diff --git a/.github/actions/launchable/setup/action.yml b/.github/actions/launchable/setup/action.yml index 155604983ebd23..6f81531ddcbdb2 100644 --- a/.github/actions/launchable/setup/action.yml +++ b/.github/actions/launchable/setup/action.yml @@ -38,6 +38,19 @@ inputs: Directory to (re-)checkout source codes. Launchable retrives the commit information from the directory. + launchable-workspace: + required: true + default: ${{ github.event.repository.name }} + description: >- + A workspace name in Launchable + + test-task: + required: true + default: ${{ matrix.test_task }} + description: >- + A test task that determine which tests are executed. + This value is used in the Launchable flavor. + runs: using: composite @@ -77,18 +90,25 @@ runs: : # The following envs are necessary in Launchable tokenless authentication. : # https://github.com/launchableinc/cli/blob/v1.80.1/launchable/utils/authentication.py#L20 echo "LAUNCHABLE_ORGANIZATION=${{ github.repository_owner }}" >> $GITHUB_ENV - echo "LAUNCHABLE_WORKSPACE=${{ github.event.repository.name }}" >> $GITHUB_ENV + echo "LAUNCHABLE_WORKSPACE=${{ inputs.launchable-workspace }}" >> $GITHUB_ENV : # https://github.com/launchableinc/cli/blob/v1.80.1/launchable/utils/authentication.py#L71 echo "GITHUB_PR_HEAD_SHA=${{ github.event.pull_request.head.sha || github.sha }}" >> $GITHUB_ENV echo "LAUNCHABLE_TOKEN=${{ inputs.launchable-token }}" >> $GITHUB_ENV if: steps.enable-launchable.outputs.enable-launchable + - name: Set up path + shell: bash + working-directory: ${{ inputs.srcdir }} + # Since updated PATH variable will be available in only subsequent actions, we need to add the path beforehand. + # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-system-path + run: echo "$(python -msite --user-base)/bin" >> $GITHUB_PATH + if: steps.enable-launchable.outputs.enable-launchable && startsWith(inputs.os, 'macos') + - name: Set up Launchable shell: bash working-directory: ${{ inputs.srcdir }} run: | set -x - echo "$(python -msite --user-base)/bin" >> $GITHUB_PATH pip install --user launchable launchable verify || true : # The build name cannot include a slash, so we replace the string here. @@ -135,7 +155,7 @@ runs: working-directory: ${{ inputs.srcdir }} post: | : # record - launchable record tests --flavor os=${{ inputs.os }} --flavor test_task=${{ matrix.test_task }} --flavor test_opts=${test_opts} raw ${report_path} + launchable record tests --flavor os=${{ inputs.os }} --flavor test_task=${{ inputs.test-task }} --flavor test_opts=${test_opts} raw ${report_path} rm -f ${report_path} if: ${{ always() && steps.enable-launchable.outputs.enable-launchable }} env: diff --git a/.github/workflows/annocheck.yml b/.github/workflows/annocheck.yml index 6b0d49d0eb51f4..bbffefbbf070c3 100644 --- a/.github/workflows/annocheck.yml +++ b/.github/workflows/annocheck.yml @@ -74,7 +74,7 @@ jobs: builddir: build makeup: true - - uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0 + - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0 with: ruby-version: '3.0' bundler: none diff --git a/.github/workflows/baseruby.yml b/.github/workflows/baseruby.yml index b3df961bd85da5..00ad9f98cd6403 100644 --- a/.github/workflows/baseruby.yml +++ b/.github/workflows/baseruby.yml @@ -51,7 +51,7 @@ jobs: - ruby-3.3 steps: - - uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0 + - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0 with: ruby-version: ${{ matrix.ruby }} bundler: none diff --git a/.github/workflows/check_dependencies.yml b/.github/workflows/check_dependencies.yml index a5b81d4896f251..cc2061272e9f98 100644 --- a/.github/workflows/check_dependencies.yml +++ b/.github/workflows/check_dependencies.yml @@ -55,7 +55,7 @@ jobs: - uses: ./.github/actions/setup/directories - - uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0 + - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0 with: ruby-version: '3.0' bundler: none diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 1480031bfa8925..745f8fd6e5e995 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -32,7 +32,7 @@ permissions: # added using https://github.com/step-security/secure-workflows jobs: analyze: name: Analyze - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest permissions: actions: read # for github/codeql-action/init to get workflow details contents: read # for actions/checkout to fetch code @@ -56,10 +56,7 @@ jobs: matrix: include: - language: cpp - os: ubuntu-latest - # ruby analysis used large memory. We need to use a larger runner. - language: ruby - os: ${{ github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'ubuntu-latest' }} steps: - name: Checkout repository @@ -80,15 +77,15 @@ jobs: run: sudo rm /usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb - name: Initialize CodeQL - uses: github/codeql-action/init@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/init@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/autobuild@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/analyze@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12 with: category: '/language:${{ matrix.language }}' upload: False @@ -118,7 +115,7 @@ jobs: continue-on-error: true - name: Upload SARIF - uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/upload-sarif@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12 with: sarif_file: sarif-results/${{ matrix.language }}.sarif continue-on-error: true diff --git a/.github/workflows/dependabot_automerge.yml b/.github/workflows/dependabot_automerge.yml index 80112a0af3500b..6b5f865f6253d9 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@5e5f99653a5b510e8555840e80cbf1514ad4af38 # v2.1.0 + uses: dependabot/fetch-metadata@dbb049abf0d677abbd7f7eee0375145b417fdd34 # v2.2.0 id: metadata - name: Wait for status checks diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 3c312829a0b901..df74904a542790 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -26,10 +26,14 @@ jobs: matrix: include: - test_task: check + os: macos-14 - test_task: test-all test_opts: --repeat-count=2 + os: macos-14 - test_task: test-bundler-parallel + os: macos-14 - test_task: test-bundled-gems + os: macos-14 - test_task: check os: macos-12 - test_task: check @@ -39,7 +43,7 @@ jobs: env: GITPULLOPTIONS: --no-tags origin ${{ github.ref }} - runs-on: ${{ matrix.os || (github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14') }} + runs-on: ${{ matrix.os }} if: >- ${{!(false @@ -94,7 +98,7 @@ jobs: - name: Set up Launchable uses: ./.github/actions/launchable/setup with: - os: ${{ matrix.os || (github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14') }} + os: ${{ matrix.os }} test-opts: ${{ matrix.test_opts }} launchable-token: ${{ secrets.LAUNCHABLE_TOKEN }} builddir: build @@ -129,7 +133,7 @@ jobs: - uses: ./.github/actions/slack with: - label: ${{ matrix.os || (github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14') }} / ${{ matrix.test_task }} + label: ${{ matrix.os }} / ${{ matrix.test_task }} SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot if: ${{ failure() }} diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index ad96de69398771..c0be674d4b3eed 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -66,7 +66,7 @@ jobs: steps: - name: Set up Ruby & MSYS2 - uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0 + uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0 with: ruby-version: ${{ matrix.baseruby }} diff --git a/.github/workflows/rjit-bindgen.yml b/.github/workflows/rjit-bindgen.yml index 25a3814c2346e4..fd1b638d9e0f6c 100644 --- a/.github/workflows/rjit-bindgen.yml +++ b/.github/workflows/rjit-bindgen.yml @@ -47,7 +47,7 @@ jobs: steps: - name: Set up Ruby - uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0 + uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0 with: ruby-version: '3.1' diff --git a/.github/workflows/rjit.yml b/.github/workflows/rjit.yml index 75c8152ce565d0..0d8b5f24ddc279 100644 --- a/.github/workflows/rjit.yml +++ b/.github/workflows/rjit.yml @@ -67,6 +67,8 @@ jobs: srcdir: src builddir: build makeup: true + # Set fetch-depth: 10 so that Launchable can receive commits information. + fetch-depth: 10 - name: Run configure env: @@ -77,19 +79,33 @@ jobs: - run: $SETARCH make + - name: Set up Launchable + uses: ./.github/actions/launchable/setup + with: + os: ubuntu-22.04 + test-task: test + launchable-token: ${{ secrets.LAUNCHABLE_TOKEN }} + builddir: build + srcdir: src + launchable-workspace: ruby-make-btest + test-opts: ${{ matrix.run_opts }} + continue-on-error: true + - name: make test run: | $SETARCH make -s test RUN_OPTS="$RUN_OPTS" timeout-minutes: 30 env: GNUMAKEFLAGS: '' - RUBY_TESTOPTS: '--tty=no' + RUBY_TESTOPTS: >- + ${{ env.TESTS }} + --tty=no RUN_OPTS: ${{ matrix.run_opts }} - name: make test-all run: | $SETARCH make -s test-all RUN_OPTS="$RUN_OPTS" - timeout-minutes: 40 + timeout-minutes: 60 env: GNUMAKEFLAGS: '' RUBY_TESTOPTS: '-q --tty=no' diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index e0104bfd123099..85dd475fc6c63d 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -67,6 +67,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: 'Upload to code-scanning' - uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v2.1.27 + uses: github/codeql-action/upload-sarif@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v2.1.27 with: sarif_file: results.sarif diff --git a/.github/workflows/spec_guards.yml b/.github/workflows/spec_guards.yml index df95ffcf2e9da7..c6f0983eeba9d4 100644 --- a/.github/workflows/spec_guards.yml +++ b/.github/workflows/spec_guards.yml @@ -47,7 +47,7 @@ jobs: steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0 + - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0 with: ruby-version: ${{ matrix.ruby }} bundler: none diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 7f65be0339421a..cd94d62b5f361b 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -34,7 +34,9 @@ jobs: - test_task: check configure: '--enable-shared --enable-load-relative' - test_task: check - configure: '--with-shared-gc' + shared_gc: true + shared_gc_dir: '/home/runner/ruby_gc' + configure: '--with-shared-gc=/home/runner/ruby_gc' - test_task: test-bundler-parallel - test_task: test-bundled-gems - test_task: check @@ -67,7 +69,7 @@ jobs: with: arch: ${{ matrix.arch }} - - uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0 + - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0 with: ruby-version: '3.0' bundler: none @@ -93,6 +95,13 @@ jobs: - run: $SETARCH make prepare-gems if: ${{ matrix.test_task == 'test-bundled-gems' }} + - name: Build shared GC + run: > + echo "RUBY_GC_LIBRARY=librubygc.default.so" >> $GITHUB_ENV && + mkdir ${{ matrix.shared_gc_dir }} && + make shared-gc SHARED_GC=default + if: ${{ matrix.shared_gc }} + - run: $SETARCH make - name: Set test options for skipped tests diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 579cae55fb6389..c43641b93658e5 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -100,7 +100,7 @@ jobs: run: | echo "WASI_SDK_PATH=/opt/wasi-sdk" >> $GITHUB_ENV - - uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0 + - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.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@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 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@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 if: ${{ github.event_name == 'pull_request' }} with: name: github-pr-info diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index b05c6d59d99365..cb492aebc335a0 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -54,6 +54,7 @@ jobs: env: GITPULLOPTIONS: --no-tags origin ${{ github.ref }} OS_VER: windows-${{ matrix.vs < 2022 && '2019' || matrix.vs }} + VCPKG_DEFAULT_TRIPLET: ${{ matrix.target || 'x64' }}-windows steps: - run: md build @@ -87,7 +88,7 @@ jobs: ${{ steps.find-tools.outputs.needs }} if: ${{ steps.find-tools.outputs.needs != '' }} - - uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0 + - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0 with: ruby-version: '3.0' bundler: none @@ -143,25 +144,16 @@ jobs: - name: Install libraries with vcpkg run: | - vcpkg install --triplet x64-windows + vcpkg install working-directory: src env: VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" - # We use OpenSSL instealled by vcpkg instead - - name: disable system OpenSSL - run: | - for %%I in (libcrypto-1_1-x64 libssl-1_1-x64) do ( - ren c:\Windows\System32\%%I.dll %%I.dll_ - ) - # windows-2019 image doesn't have OpenSSL as of 2023/9/14 - if: ${{ matrix.vs != 2019 }} - # TODO: We should use `../src` instead of `D:/a/ruby/ruby/src` - name: Configure run: >- ../src/win32/configure.bat --disable-install-doc - --with-opt-dir=D:/a/ruby/ruby/src/vcpkg_installed/x64-windows + --with-opt-dir=D:/a/ruby/ruby/src/vcpkg_installed/%VCPKG_DEFAULT_TRIPLET% - run: nmake prepare-vcpkg diff --git a/.github/workflows/yjit-macos.yml b/.github/workflows/yjit-macos.yml index 6531737120af37..7c8a0c77d0cc07 100644 --- a/.github/workflows/yjit-macos.yml +++ b/.github/workflows/yjit-macos.yml @@ -24,7 +24,7 @@ jobs: cargo: name: cargo test - runs-on: ${{ github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14' }} + runs-on: macos-14 if: >- ${{!(false @@ -68,7 +68,7 @@ jobs: RUN_OPTS: ${{ matrix.yjit_opts }} SPECOPTS: ${{ matrix.specopts }} - runs-on: ${{ github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14' }} + runs-on: macos-14 if: >- ${{!(false @@ -119,7 +119,7 @@ jobs: - name: Set up Launchable uses: ./.github/actions/launchable/setup with: - os: ${{ github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14' }} + os: macos-14 test-opts: ${{ matrix.configure }} launchable-token: ${{ secrets.LAUNCHABLE_TOKEN }} builddir: build @@ -158,7 +158,7 @@ jobs: result: if: ${{ always() }} name: ${{ github.workflow }} result - runs-on: ${{ github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14' }} + runs-on: macos-14 needs: [make] steps: - run: exit 1 diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml index 452667425269bd..6c220fc5dc0168 100644 --- a/.github/workflows/yjit-ubuntu.yml +++ b/.github/workflows/yjit-ubuntu.yml @@ -134,7 +134,7 @@ jobs: - uses: ./.github/actions/setup/ubuntu - - uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0 + - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0 with: ruby-version: '3.0' bundler: none diff --git a/NEWS.md b/NEWS.md index 314b374b72d672..6844332585a102 100644 --- a/NEWS.md +++ b/NEWS.md @@ -25,6 +25,12 @@ Note that each entry is kept to a minimum, see links for details. * Keyword arguments are no longer allowed in index assignment (e.g. `a[0, kw: 1] = 2`). [[Bug #20218]] +* `GC.config` added to allow setting configuration variables on the Garbage + Collector. [[Feature #20443]] + +* GC configuration parameter `rgengc_allow_full_mark` introduced. When `false` + GC will only mark young objects. Default is `true`. [[Feature #20443]] + ## Core classes updates Note: We're only listing outstanding class updates. @@ -54,7 +60,7 @@ The following default gems are updated. * erb 4.0.4 * fiddle 1.1.3 * io-console 0.7.2 -* irb 1.13.2 +* irb 1.14.0 * json 2.7.2 * net-http 0.4.1 * optparse 0.5.0 @@ -70,11 +76,11 @@ The following bundled gems are updated. * minitest 5.24.1 * rake 13.2.1 * test-unit 3.6.2 -* rexml 3.3.1 +* rexml 3.3.2 * net-ftp 0.3.7 * net-imap 0.4.14 * net-smtp 0.5.0 -* rbs 3.5.1 +* rbs 3.5.2 * typeprof 0.21.11 * debug 1.9.2 * racc 1.8.0 @@ -152,4 +158,5 @@ See GitHub releases like [GitHub Releases of Logger](https://github.com/ruby/log [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 +[Feature #20443]: https://bugs.ruby-lang.org/issues/20443 [Feature #20497]: https://bugs.ruby-lang.org/issues/20497 diff --git a/array.c b/array.c index 103b9cf73cbbc4..84d38c3868b174 100644 --- a/array.c +++ b/array.c @@ -934,9 +934,6 @@ ary_make_shared(VALUE ary) return ary; } else if (OBJ_FROZEN(ary)) { - if (!ARY_EMBED_P(ary)) { - ary_shrink_capa(ary); - } return ary; } else { @@ -3638,41 +3635,6 @@ rb_ary_sort_by_bang(VALUE ary) return ary; } - -/* - * call-seq: - * array.map {|element| ... } -> new_array - * array.map -> new_enumerator - * - * Calls the block, if given, with each element of +self+; - * returns a new +Array+ whose elements are the return values from the block: - * - * a = [:foo, 'bar', 2] - * a1 = a.map {|element| element.class } - * a1 # => [Symbol, String, Integer] - * - * Returns a new Enumerator if no block given: - * a = [:foo, 'bar', 2] - * a1 = a.map - * a1 # => # - * - */ - -static VALUE -rb_ary_collect(VALUE ary) -{ - long i; - VALUE collect; - - RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length); - collect = rb_ary_new2(RARRAY_LEN(ary)); - for (i = 0; i < RARRAY_LEN(ary); i++) { - rb_ary_push(collect, rb_yield(RARRAY_AREF(ary, i))); - } - return collect; -} - - /* * call-seq: * array.map! {|element| ... } -> self @@ -3815,42 +3777,6 @@ rb_ary_values_at(int argc, VALUE *argv, VALUE ary) } -/* - * call-seq: - * array.select {|element| ... } -> new_array - * array.select -> new_enumerator - * - * Calls the block, if given, with each element of +self+; - * returns a new +Array+ containing those elements of +self+ - * for which the block returns a truthy value: - * - * a = [:foo, 'bar', 2, :bam] - * a1 = a.select {|element| element.to_s.start_with?('b') } - * a1 # => ["bar", :bam] - * - * Returns a new Enumerator if no block given: - * - * a = [:foo, 'bar', 2, :bam] - * a.select # => # - * - */ - -static VALUE -rb_ary_select(VALUE ary) -{ - VALUE result; - long i; - - RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length); - result = rb_ary_new2(RARRAY_LEN(ary)); - for (i = 0; i < RARRAY_LEN(ary); i++) { - if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) { - rb_ary_push(result, rb_ary_elt(ary, i)); - } - } - return result; -} - struct select_bang_arg { VALUE ary; long len[2]; @@ -6704,6 +6630,12 @@ ary_sample(rb_execution_context_t *ec, VALUE ary, VALUE randgen, VALUE nv, VALUE return result; } +static VALUE +ary_sized_alloc(rb_execution_context_t *ec, VALUE self) +{ + return rb_ary_new2(RARRAY_LEN(self)); +} + static VALUE ary_sample0(rb_execution_context_t *ec, VALUE ary) { @@ -8706,13 +8638,9 @@ Init_Array(void) rb_define_method(rb_cArray, "sort", rb_ary_sort, 0); rb_define_method(rb_cArray, "sort!", rb_ary_sort_bang, 0); rb_define_method(rb_cArray, "sort_by!", rb_ary_sort_by_bang, 0); - rb_define_method(rb_cArray, "collect", rb_ary_collect, 0); rb_define_method(rb_cArray, "collect!", rb_ary_collect_bang, 0); - rb_define_method(rb_cArray, "map", rb_ary_collect, 0); rb_define_method(rb_cArray, "map!", rb_ary_collect_bang, 0); - rb_define_method(rb_cArray, "select", rb_ary_select, 0); rb_define_method(rb_cArray, "select!", rb_ary_select_bang, 0); - rb_define_method(rb_cArray, "filter", rb_ary_select, 0); rb_define_method(rb_cArray, "filter!", rb_ary_select_bang, 0); rb_define_method(rb_cArray, "keep_if", rb_ary_keep_if, 0); rb_define_method(rb_cArray, "values_at", rb_ary_values_at, -1); diff --git a/array.rb b/array.rb index f63ff000568186..2ebef6e62eb9d5 100644 --- a/array.rb +++ b/array.rb @@ -56,6 +56,75 @@ def each self end + # call-seq: + # array.map {|element| ... } -> new_array + # array.map -> new_enumerator + # + # Calls the block, if given, with each element of +self+; + # returns a new +Array+ whose elements are the return values from the block: + # + # a = [:foo, 'bar', 2] + # a1 = a.map {|element| element.class } + # a1 # => [Symbol, String, Integer] + # + # Returns a new Enumerator if no block given: + # a = [:foo, 'bar', 2] + # a1 = a.map + # a1 # => # + def map + Primitive.attr! :inline_block + Primitive.attr! :use_block + + unless defined?(yield) + return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, ary_enum_length)' + end + + _i = 0 + value = nil + result = Primitive.ary_sized_alloc + while Primitive.cexpr!(%q{ ary_fetch_next(self, LOCAL_PTR(_i), LOCAL_PTR(value)) }) + result << yield(value) + end + result + end + + alias collect map + + # call-seq: + # array.select {|element| ... } -> new_array + # array.select -> new_enumerator + # + # Calls the block, if given, with each element of +self+; + # returns a new +Array+ containing those elements of +self+ + # for which the block returns a truthy value: + # + # a = [:foo, 'bar', 2, :bam] + # a1 = a.select {|element| element.to_s.start_with?('b') } + # a1 # => ["bar", :bam] + # + # Returns a new Enumerator if no block given: + # + # a = [:foo, 'bar', 2, :bam] + # a.select # => # + def select + Primitive.attr! :inline_block + Primitive.attr! :use_block + + unless defined?(yield) + return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, ary_enum_length)' + end + + _i = 0 + value = nil + result = Primitive.ary_sized_alloc + while Primitive.cexpr!(%q{ ary_fetch_next(self, LOCAL_PTR(_i), LOCAL_PTR(value)) }) + result << value if yield value + end + result + end + + alias filter select + # call-seq: # array.shuffle!(random: Random) -> array # diff --git a/benchmark/hash_new.yml b/benchmark/hash_new.yml new file mode 100644 index 00000000000000..9d8e34187f6785 --- /dev/null +++ b/benchmark/hash_new.yml @@ -0,0 +1,16 @@ +prelude: | + has_hash_with_capa = Hash.instance_method(:initialize).parameters.include?([:key, :capacity]) + strings_1k = 1_000.times.map { |i| -i.to_s.freeze } + strings_100k = 100_000.times.map { |i| -i.to_s.freeze } +benchmark: + new: Hash.new + new_with_capa_1k: | + h = has_hash_with_capa ? Hash.new(capacity: strings_1k.size) : {} + strings_1k.each do |x| + h[x] = true + end + new_with_capa_100k: | + h = has_hash_with_capa ? Hash.new(capacity: strings_100k.size) : {} + strings_100k.each do |x| + h[x] = true + end diff --git a/bootstraptest/runner.rb b/bootstraptest/runner.rb index 764c4898dd5aee..2e2a3611deb7cb 100755 --- a/bootstraptest/runner.rb +++ b/bootstraptest/runner.rb @@ -78,6 +78,7 @@ def Dir.mktmpdir(prefix_suffix=nil, tmpdir=nil) :platform, :timeout, :timeout_scale, + :launchable_test_reports ) BT = Class.new(bt) do def indent=(n) @@ -229,6 +230,19 @@ def main exit true when /\A-j/ true + when /--launchable-test-reports=(.*)/ + if File.exist?($1) + # To protect files from overwritten, do nothing when the file exists. + return true + end + + require_relative '../tool/lib/launchable' + BT.launchable_test_reports = writer = Launchable::JsonStreamWriter.new($1) + writer.write_array('testCases') + at_exit { + writer.close + } + true else false end @@ -345,6 +359,45 @@ def concurrent_exec_test end end +module Launchable + def show_progress(message = '') + faildesc, t = super + + if writer = BT.launchable_test_reports + if !faildesc + status = 'TEST_PASSED' + else + status = 'TEST_FAILED' + end + repo_path = File.expand_path("#{__dir__}/../") + relative_path = self.path.delete_prefix("#{repo_path}/") + # The test path is a URL-encoded representation. + # https://github.com/launchableinc/cli/blob/v1.81.0/launchable/testpath.py#L18 + test_path = {file: relative_path, testcase: self.id}.map{|key, val| + "#{encode_test_path_component(key)}=#{encode_test_path_component(val)}" + }.join('#') + writer.write_object( + { + testPath: test_path, + status: status, + duration: t, + createdAt: Time.now.to_s, + stderr: faildesc, + stdout: nil, + data: { + lineNumber: self.lineno + } + } + ) + end + end + + private + def encode_test_path_component component + component.to_s.gsub('%', '%25').gsub('=', '%3D').gsub('#', '%23').gsub('&', '%26') + end +end + def exec_test(paths) # setup load_test paths @@ -421,6 +474,7 @@ def target_platform end class Assertion < Struct.new(:src, :path, :lineno, :proc) + prepend Launchable @count = 0 @all = Hash.new{|h, k| h[k] = []} @errbuf = [] @@ -495,9 +549,9 @@ def show_progress(message = '') $stderr.print "#{BT.progress_bs}#{BT.progress[BT_STATE.count % BT.progress.size]}" end - t = Time.now if BT.verbose + t = Time.now if BT.verbose || BT.launchable_test_reports faildesc, errout = with_stderr {yield} - t = Time.now - t if BT.verbose + t = Time.now - t if BT.verbose || BT.launchable_test_reports if !faildesc # success @@ -524,6 +578,8 @@ def show_progress(message = '') $stderr.printf("%-*s%s", BT.width, path, BT.progress[BT_STATE.count % BT.progress.size]) end end + + [faildesc, t] rescue Interrupt $stderr.puts "\##{@id} #{path}:#{lineno}" raise @@ -648,23 +704,19 @@ def assert_normal_exit(testsrc, *rest, timeout: BT.timeout, **opt) timeout_signaled = false logfile = "assert_normal_exit.#{as.path}.#{as.lineno}.log" - begin - err = open(logfile, "w") - io = IO.popen("#{BT.ruby} -W0 #{filename}", err: err) - pid = io.pid - th = Thread.new { - io.read - io.close - $? - } - if !th.join(timeout) - Process.kill :KILL, pid - timeout_signaled = true - end - status = th.value - ensure - err.close + io = IO.popen("#{BT.ruby} -W0 #{filename}", err: logfile) + pid = io.pid + th = Thread.new { + io.read + io.close + $? + } + if !th.join(timeout) + Process.kill :KILL, pid + timeout_signaled = true end + status = th.value + if status && status.signaled? signo = status.termsig signame = Signal.list.invert[signo] diff --git a/bootstraptest/test_autoload.rb b/bootstraptest/test_autoload.rb index 9e0850bc529720..de66f1f3ee15de 100644 --- a/bootstraptest/test_autoload.rb +++ b/bootstraptest/test_autoload.rb @@ -11,7 +11,7 @@ }, '[ruby-dev:43816]' assert_equal 'ok', %q{ - open('zzz2.rb', 'w') {|f| f.puts '' } + File.write('zzz2.rb', '') instance_eval do autoload :ZZZ, './zzz2.rb' begin @@ -23,7 +23,7 @@ }, '[ruby-dev:43816]' assert_equal 'ok', %q{ - open('zzz3.rb', 'w') {|f| f.puts 'class ZZZ; def self.ok;:ok;end;end'} + File.write('zzz3.rb', "class ZZZ; def self.ok;:ok;end;end\n") instance_eval do autoload :ZZZ, './zzz3.rb' ZZZ.ok @@ -31,20 +31,20 @@ }, '[ruby-dev:43816]' assert_equal 'ok', %q{ - open("zzz4.rb", "w") {|f| f.puts "class ZZZ; def self.ok;:ok;end;end"} + File.write("zzz4.rb", "class ZZZ; def self.ok;:ok;end;end\n") autoload :ZZZ, "./zzz4.rb" ZZZ.ok } assert_equal 'ok', %q{ - open("zzz5.rb", "w") {|f| f.puts "class ZZZ; def self.ok;:ok;end;end"} + File.write("zzz5.rb", "class ZZZ; def self.ok;:ok;end;end\n") autoload :ZZZ, "./zzz5.rb" require "./zzz5.rb" ZZZ.ok } assert_equal 'okok', %q{ - open("zzz6.rb", "w") {|f| f.puts "class ZZZ; def self.ok;:ok;end;end"} + File.write("zzz6.rb", "class ZZZ; def self.ok;:ok;end;end\n") autoload :ZZZ, "./zzz6.rb" t1 = Thread.new {ZZZ.ok} t2 = Thread.new {ZZZ.ok} @@ -60,7 +60,7 @@ }, '[ruby-core:21696]' assert_equal 'A::C', %q{ - open("zzz7.rb", "w") {} + File.write("zzz7.rb", "") class A autoload :C, "./zzz7" class C diff --git a/bootstraptest/test_io.rb b/bootstraptest/test_io.rb index 666e5a011b63c7..4e5d6d59c9bd36 100644 --- a/bootstraptest/test_io.rb +++ b/bootstraptest/test_io.rb @@ -91,7 +91,7 @@ at_exit { p :foo } megacontent = "abc" * 12345678 - #File.open("megasrc", "w") {|f| f << megacontent } + #File.write("megasrc", megacontent) t0 = Thread.main Thread.new { sleep 0.001 until t0.stop?; Process.kill(:INT, $$) } diff --git a/bootstraptest/test_load.rb b/bootstraptest/test_load.rb index 3253582a324995..5046d4fcea81a6 100644 --- a/bootstraptest/test_load.rb +++ b/bootstraptest/test_load.rb @@ -1,9 +1,9 @@ assert_equal 'ok', %q{ - open("require-lock-test.rb", "w") {|f| - f.puts "sleep 0.1" - f.puts "module M" - f.puts "end" - } + File.write("require-lock-test.rb", <<-END) + sleep 0.1 + module M + end + END $:.unshift Dir.pwd vs = (1..2).map {|i| Thread.start { @@ -16,7 +16,7 @@ assert_equal 'ok', %q{ %w[a a/foo b].each {|d| Dir.mkdir(d)} - open("b/foo", "w") {|f| f.puts "$ok = :ok"} + File.write("b/foo", "$ok = :ok\n") $:.replace(%w[a b]) begin load "foo" diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index 0390d38f9c3408..b6a4ef605e6233 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -1086,6 +1086,20 @@ def self.fstr = @fstr a + b + c + d + e + f } +assert_equal '["instance-variable", "instance-variable", nil]', %q{ + class C + @iv1 = "" + @iv2 = 42 + def self.iv1 = defined?(@iv1) # "instance-variable" + def self.iv2 = defined?(@iv2) # "instance-variable" + def self.iv3 = defined?(@iv3) # nil + end + + Ractor.new{ + [C.iv1, C.iv2, C.iv3] + }.take +} + # moved objects have their shape properly set to original object's shape assert_equal '1234', %q{ class Obj diff --git a/bootstraptest/test_thread.rb b/bootstraptest/test_thread.rb index a4d46e2f1018e7..4040b68a279cee 100644 --- a/bootstraptest/test_thread.rb +++ b/bootstraptest/test_thread.rb @@ -257,8 +257,7 @@ } assert_equal 'ok', %{ - open("zzz_t1.rb", "w") do |f| - f.puts <<-END + File.write("zzz_t1.rb", <<-END) begin Thread.new { fork { GC.start } }.join pid, status = Process.wait2 @@ -267,7 +266,6 @@ $result = :ok end END - end require "./zzz_t1.rb" $result } @@ -422,8 +420,7 @@ def m } assert_equal 'ok', %{ - open("zzz_t2.rb", "w") do |f| - f.puts <<-'end;' # do + File.write("zzz_t2.rb", <<-'end;') # do begin m = Thread::Mutex.new parent = Thread.current @@ -445,7 +442,6 @@ def m $result = :ok end end; - end require "./zzz_t2.rb" $result } diff --git a/common.mk b/common.mk index a5a2a133280ab0..7312a61dc182a5 100644 --- a/common.mk +++ b/common.mk @@ -131,6 +131,7 @@ COMMONOBJS = array.$(OBJEXT) \ file.$(OBJEXT) \ gc.$(OBJEXT) \ glospace.$(OBJEXT) \ + gc_impl.$(OBJEXT) \ hash.$(OBJEXT) \ inits.$(OBJEXT) \ imemo.$(OBJEXT) \ @@ -1204,6 +1205,7 @@ BUILTIN_RB_SRCS = \ $(srcdir)/trace_point.rb \ $(srcdir)/warning.rb \ $(srcdir)/array.rb \ + $(srcdir)/hash.rb \ $(srcdir)/kernel.rb \ $(srcdir)/ractor.rb \ $(srcdir)/symbol.rb \ @@ -1905,6 +1907,18 @@ rewindable: HELP_EXTRA_TASKS = "" +shared-gc: probes.h + $(Q) if test -z $(shared_gc_dir); then \ + echo "You must configure with --with-shared-gc to use shared GC"; \ + exit 1; \ + elif test -z $(SHARED_GC); then \ + echo "You must specify SHARED_GC with the GC to build"; \ + exit 1; \ + else \ + echo generating $(shared_gc_dir)librubygc.$(SHARED_GC).$(SOEXT); \ + $(LDSHARED) -I$(srcdir)/include -I$(srcdir) -I$(arch_hdrdir) $(XDLDFLAGS) $(cflags) -DBUILDING_SHARED_GC -fPIC -o $(shared_gc_dir)librubygc.$(SHARED_GC).$(SOEXT) $(srcdir)/gc/$(SHARED_GC).c; \ + fi + help: PHONY $(MESSAGE_BEGIN) \ " Makefile of Ruby" \ @@ -7225,6 +7239,8 @@ gc.$(OBJEXT): $(CCAN_DIR)/str/str.h gc.$(OBJEXT): $(hdrdir)/ruby.h gc.$(OBJEXT): $(hdrdir)/ruby/ruby.h gc.$(OBJEXT): $(hdrdir)/ruby/version.h +gc.$(OBJEXT): $(top_srcdir)/gc/gc.h +gc.$(OBJEXT): $(top_srcdir)/gc/gc_impl.h gc.$(OBJEXT): $(top_srcdir)/internal/array.h gc.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h gc.$(OBJEXT): $(top_srcdir)/internal/bignum.h @@ -7482,11 +7498,204 @@ gc.$(OBJEXT): {$(VPATH)}thread.h gc.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h gc.$(OBJEXT): {$(VPATH)}thread_native.h gc.$(OBJEXT): {$(VPATH)}util.h +gc.$(OBJEXT): {$(VPATH)}vm.h gc.$(OBJEXT): {$(VPATH)}vm_callinfo.h gc.$(OBJEXT): {$(VPATH)}vm_core.h gc.$(OBJEXT): {$(VPATH)}vm_debug.h gc.$(OBJEXT): {$(VPATH)}vm_opts.h gc.$(OBJEXT): {$(VPATH)}vm_sync.h +gc_impl.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h +gc_impl.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h +gc_impl.$(OBJEXT): $(CCAN_DIR)/list/list.h +gc_impl.$(OBJEXT): $(CCAN_DIR)/str/str.h +gc_impl.$(OBJEXT): $(hdrdir)/ruby/ruby.h +gc_impl.$(OBJEXT): $(top_srcdir)/gc/default.c +gc_impl.$(OBJEXT): $(top_srcdir)/gc/gc.h +gc_impl.$(OBJEXT): $(top_srcdir)/gc/gc_impl.h +gc_impl.$(OBJEXT): $(top_srcdir)/internal/bits.h +gc_impl.$(OBJEXT): $(top_srcdir)/internal/compilers.h +gc_impl.$(OBJEXT): $(top_srcdir)/internal/hash.h +gc_impl.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h +gc_impl.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +gc_impl.$(OBJEXT): $(top_srcdir)/internal/string.h +gc_impl.$(OBJEXT): $(top_srcdir)/internal/warnings.h +gc_impl.$(OBJEXT): {$(VPATH)}assert.h +gc_impl.$(OBJEXT): {$(VPATH)}atomic.h +gc_impl.$(OBJEXT): {$(VPATH)}backward/2/assume.h +gc_impl.$(OBJEXT): {$(VPATH)}backward/2/attributes.h +gc_impl.$(OBJEXT): {$(VPATH)}backward/2/bool.h +gc_impl.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h +gc_impl.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h +gc_impl.$(OBJEXT): {$(VPATH)}backward/2/limits.h +gc_impl.$(OBJEXT): {$(VPATH)}backward/2/long_long.h +gc_impl.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h +gc_impl.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h +gc_impl.$(OBJEXT): {$(VPATH)}config.h +gc_impl.$(OBJEXT): {$(VPATH)}darray.h +gc_impl.$(OBJEXT): {$(VPATH)}debug.h +gc_impl.$(OBJEXT): {$(VPATH)}debug_counter.h +gc_impl.$(OBJEXT): {$(VPATH)}defines.h +gc_impl.$(OBJEXT): {$(VPATH)}encoding.h +gc_impl.$(OBJEXT): {$(VPATH)}intern.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/abi.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/anyargs.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/assume.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/cold.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/const.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/error.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/format.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/pure.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/warning.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/cast.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/compiler_is.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/compiler_since.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/config.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/constant_p.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/core.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rarray.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rclass.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rdata.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rfile.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rhash.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/core/robject.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rstring.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/ctype.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/dllexport.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/dosish.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/encoding/re.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/encoding/string.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/error.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/eval.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/event.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/fl_type.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/gc.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/glob.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/globals.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/has/attribute.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/has/builtin.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/has/extension.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/has/feature.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/has/warning.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/array.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/class.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/compar.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/complex.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/cont.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/dir.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/enum.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/error.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/eval.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/file.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/hash.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/io.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/load.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/object.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/parse.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/proc.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/process.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/random.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/range.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/rational.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/re.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/select.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/signal.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/string.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/struct.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/thread.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/time.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/variable.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/vm.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/interpreter.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/iterator.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/memory.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/method.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/module.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/newobj.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/scan_args.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/special_consts.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/static_assert.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/stdalign.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/stdbool.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/stdckdint.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/symbol.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/value.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/value_type.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/variable.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/warning_push.h +gc_impl.$(OBJEXT): {$(VPATH)}internal/xmalloc.h +gc_impl.$(OBJEXT): {$(VPATH)}missing.h +gc_impl.$(OBJEXT): {$(VPATH)}onigmo.h +gc_impl.$(OBJEXT): {$(VPATH)}oniguruma.h +gc_impl.$(OBJEXT): {$(VPATH)}probes.dmyh +gc_impl.$(OBJEXT): {$(VPATH)}probes.h +gc_impl.$(OBJEXT): {$(VPATH)}st.h +gc_impl.$(OBJEXT): {$(VPATH)}subst.h +gc_impl.$(OBJEXT): {$(VPATH)}thread.h +gc_impl.$(OBJEXT): {$(VPATH)}util.h +gc_impl.$(OBJEXT): {$(VPATH)}vm.h goruby.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h goruby.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h goruby.$(OBJEXT): $(CCAN_DIR)/list/list.h @@ -7786,12 +7995,14 @@ hash.$(OBJEXT): {$(VPATH)}backward/2/limits.h hash.$(OBJEXT): {$(VPATH)}backward/2/long_long.h hash.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h hash.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h +hash.$(OBJEXT): {$(VPATH)}builtin.h hash.$(OBJEXT): {$(VPATH)}config.h hash.$(OBJEXT): {$(VPATH)}constant.h hash.$(OBJEXT): {$(VPATH)}debug_counter.h hash.$(OBJEXT): {$(VPATH)}defines.h hash.$(OBJEXT): {$(VPATH)}encoding.h hash.$(OBJEXT): {$(VPATH)}hash.c +hash.$(OBJEXT): {$(VPATH)}hash.rbinc hash.$(OBJEXT): {$(VPATH)}id.h hash.$(OBJEXT): {$(VPATH)}id_table.h hash.$(OBJEXT): {$(VPATH)}intern.h diff --git a/compile.c b/compile.c index 6ef51bb44eae89..53945d609e852e 100644 --- a/compile.c +++ b/compile.c @@ -862,6 +862,8 @@ rb_iseq_compile_callback(rb_iseq_t *iseq, const struct rb_iseq_new_with_callback return iseq_setup(iseq, ret); } +static bool drop_unreachable_return(LINK_ANCHOR *ret); + VALUE rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node) { @@ -961,7 +963,7 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node) ADD_GETLOCAL(ret, &dummy_line_node, LVAR_ERRINFO, 0); ADD_INSN1(ret, &dummy_line_node, throw, INT2FIX(0) /* continue throw */ ); } - else { + else if (!drop_unreachable_return(ret)) { ADD_SYNTHETIC_INSN(ret, ISEQ_COMPILE_DATA(iseq)->last_line, -1, leave); } @@ -4020,21 +4022,18 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal niobj = niobj->next; /* - * Eliminate array and hash allocation for f(*a, kw: 1) + * Eliminate array allocation for f(*a, kw: 1) * * splatarray true * duphash * send ARGS_SPLAT|KW_SPLAT|KW_SPLAT_MUT and not ARGS_BLOCKARG * => * splatarray false - * putobject - * send ARGS_SPLAT|KW_SPLAT + * duphash + * send */ if (optimize_args_splat_no_copy(iseq, iobj, niobj, - VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_KW_SPLAT_MUT, VM_CALL_ARGS_BLOCKARG, VM_CALL_KW_SPLAT_MUT)) { - - ((INSN*)niobj)->insn_id = BIN(putobject); - OPERAND_AT(niobj, 0) = rb_hash_freeze(rb_hash_resurrect(OPERAND_AT(niobj, 0))); + VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_KW_SPLAT_MUT, VM_CALL_ARGS_BLOCKARG, 0)) { goto optimized_splat; } @@ -4042,7 +4041,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal if (IS_NEXT_INSN_ID(niobj, getlocal) || IS_NEXT_INSN_ID(niobj, getinstancevariable) || IS_NEXT_INSN_ID(niobj, getblockparamproxy)) { /* - * Eliminate array and hash allocation for f(*a, kw: 1, &{arg,lvar,@iv}) + * Eliminate array allocation for f(*a, kw: 1, &{arg,lvar,@iv}) * * splatarray true * duphash @@ -4050,20 +4049,73 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal * send ARGS_SPLAT|KW_SPLAT|KW_SPLAT_MUT|ARGS_BLOCKARG * => * splatarray false - * putobject + * duphash * getlocal / getinstancevariable / getblockparamproxy - * send ARGS_SPLAT|KW_SPLAT|ARGS_BLOCKARG + * send */ - if (optimize_args_splat_no_copy(iseq, iobj, niobj->next, - VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_KW_SPLAT_MUT|VM_CALL_ARGS_BLOCKARG, 0, VM_CALL_KW_SPLAT_MUT)) { + optimize_args_splat_no_copy(iseq, iobj, niobj->next, + VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_KW_SPLAT_MUT|VM_CALL_ARGS_BLOCKARG, 0, 0); + } + } + } + optimized_splat: + if (IS_INSN_ID(iobj, splatarray) && OPERAND_AT(iobj, 0) == false) { + LINK_ELEMENT *niobj = &iobj->link; + if (IS_NEXT_INSN_ID(niobj, duphash)) { + niobj = niobj->next; + LINK_ELEMENT *siobj; + unsigned int set_flags = 0, unset_flags = 0; + /* + * Eliminate hash allocation for f(*a, kw: 1) + * + * splatarray false + * duphash + * send ARGS_SPLAT|KW_SPLAT|KW_SPLAT_MUT and not ARGS_BLOCKARG + * => + * splatarray false + * putobject + * send ARGS_SPLAT|KW_SPLAT + */ + if (IS_NEXT_INSN_ID(niobj, send)) { + siobj = niobj->next; + set_flags = VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_KW_SPLAT_MUT; + unset_flags = VM_CALL_ARGS_BLOCKARG; + } + /* + * Eliminate hash allocation for f(*a, kw: 1, &{arg,lvar,@iv}) + * + * splatarray false + * duphash + * getlocal / getinstancevariable / getblockparamproxy + * send ARGS_SPLAT|KW_SPLAT|KW_SPLAT_MUT|ARGS_BLOCKARG + * => + * splatarray false + * putobject + * getlocal / getinstancevariable / getblockparamproxy + * send ARGS_SPLAT|KW_SPLAT|ARGS_BLOCKARG + */ + else if ((IS_NEXT_INSN_ID(niobj, getlocal) || IS_NEXT_INSN_ID(niobj, getinstancevariable) || + IS_NEXT_INSN_ID(niobj, getblockparamproxy)) && (IS_NEXT_INSN_ID(niobj->next, send))) { + siobj = niobj->next->next; + set_flags = VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_KW_SPLAT_MUT|VM_CALL_ARGS_BLOCKARG; + } + + if (set_flags) { + const struct rb_callinfo *ci = (const struct rb_callinfo *)OPERAND_AT(siobj, 0); + unsigned int flags = vm_ci_flag(ci); + if ((flags & set_flags) == set_flags && !(flags & unset_flags)) { ((INSN*)niobj)->insn_id = BIN(putobject); OPERAND_AT(niobj, 0) = rb_hash_freeze(rb_hash_resurrect(OPERAND_AT(niobj, 0))); + + const struct rb_callinfo *nci = vm_ci_new(vm_ci_mid(ci), + flags & ~VM_CALL_KW_SPLAT_MUT, vm_ci_argc(ci), vm_ci_kwarg(ci)); + RB_OBJ_WRITTEN(iseq, ci, nci); + OPERAND_AT(siobj, 0) = (VALUE)nci; } } } } - optimized_splat: return COMPILE_OK; } @@ -6328,8 +6380,18 @@ setup_args_core(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn, return argc; } case NODE_ARGSPUSH: { - if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_SPLAT_MUT; - int argc = setup_args_core(iseq, args, RNODE_ARGSPUSH(argn)->nd_head, 1, NULL, NULL); + if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT; + int recurse_dup_rest = 1; + + if (nd_type_p(RNODE_ARGSPUSH(argn)->nd_head, NODE_SPLAT) && + nd_type_p(RNODE_ARGSPUSH(argn)->nd_body, NODE_HASH) && + !RNODE_HASH(RNODE_ARGSPUSH(argn)->nd_body)->nd_brace) { + recurse_dup_rest = 0; + } + else if (flag_ptr) { + *flag_ptr |= VM_CALL_ARGS_SPLAT_MUT; + } + int argc = setup_args_core(iseq, args, RNODE_ARGSPUSH(argn)->nd_head, recurse_dup_rest, NULL, NULL); if (nd_type_p(RNODE_ARGSPUSH(argn)->nd_body, NODE_LIST)) { int rest_len = compile_args(iseq, args, RNODE_ARGSPUSH(argn)->nd_body, &kwnode); @@ -8527,6 +8589,27 @@ compile_return(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, return COMPILE_OK; } +static bool +drop_unreachable_return(LINK_ANCHOR *ret) +{ + LINK_ELEMENT *i = ret->last, *last; + if (!i) return false; + if (IS_TRACE(i)) i = i->prev; + if (!IS_INSN(i) || !IS_INSN_ID(i, putnil)) return false; + last = i = i->prev; + if (IS_ADJUST(i)) i = i->prev; + if (!IS_INSN(i)) return false; + switch (INSN_OF(i)) { + case BIN(leave): + case BIN(jump): + break; + default: + return false; + } + (ret->last = last->prev)->next = NULL; + return true; +} + static int compile_evstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped) { diff --git a/configure.ac b/configure.ac index f8c814c22c6fcb..9f6f92cced04c1 100644 --- a/configure.ac +++ b/configure.ac @@ -489,7 +489,6 @@ AC_CACHE_CHECK([for $AR flags], [rb_cv_arflags], [ [rb_cv_arflags=rcD], [rb_cv_arflags=rcu]) ]) AC_SUBST(ARFLAGS, ["$rb_cv_arflags "]) -AC_SUBST(ASFLAGS) AS_CASE(["$target_os"], [cygwin*|msys*|mingw*], [ @@ -1007,58 +1006,6 @@ AC_SUBST(incflags, "$INCFLAGS") test -z "${ac_env_CFLAGS_set}" -a -n "${cflags+set}" && eval CFLAGS="\"$cflags $ARCH_FLAG\"" test -z "${ac_env_CXXFLAGS_set}" -a -n "${cxxflags+set}" && eval CXXFLAGS="\"$cxxflags $ARCH_FLAG\"" -# The lines above expand out the $cflags/$optflags/$debugflags/$hardenflags variables into the -# CFLAGS variable. So, at this point, we have a $CFLAGS var with the actual compiler flags we're -# going to use. -# That means this is the right time to check what branch protection flags are going to be in use -# and define appropriate macros for use in Context.S based on this -AS_CASE(["$target_cpu"], [aarch64], [ - AC_CACHE_CHECK([whether __ARM_FEATURE_BTI_DEFAULT is defined], - rb_cv_aarch64_bti_enabled, - AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM([[ - @%:@ifndef __ARM_FEATURE_BTI_DEFAULT - @%:@error "__ARM_FEATURE_BTI_DEFAULT not defined" - @%:@endif - ]])], - [rb_cv_aarch64_bti_enabled=yes], - [rb_cv_aarch64_bti_enabled=no]) - ) - AS_IF([test "$rb_cv_aarch64_bti_enabled" = yes], - AC_DEFINE(RUBY_AARCH64_BTI_ENABLED, 1)) - AC_CACHE_CHECK([whether __ARM_FEATURE_PAC_DEFAULT is defined], - rb_cv_aarch64_pac_enabled, - AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM([[ - @%:@ifndef __ARM_FEATURE_PAC_DEFAULT - @%:@error "__ARM_FEATURE_PAC_DEFAULT not defined" - @%:@endif - ]])], - [rb_cv_aarch64_pac_enabled=yes], - [rb_cv_aarch64_pac_enabled=no]) - ) - AS_IF([test "$rb_cv_aarch64_pac_enabled" = yes], - AC_DEFINE(RUBY_AARCH64_PAC_ENABLED, 1)) - # Context.S will only ever sign its return address with the A-key; it doesn't support - # the B-key at the moment. - AS_IF([test "$rb_cv_aarch64_pac_enabled" = yes], [ - AC_CACHE_CHECK([whether __ARM_FEATURE_PAC_DEFAULT specifies the b-key bit 0x02], - rb_cv_aarch64_pac_b_key, - AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM([[ - @%:@ifdef __ARM_FEATURE_PAC_DEFAULT - @%:@if __ARM_FEATURE_PAC_DEFAULT & 0x02 - @%:@error "__ARM_FEATURE_PAC_DEFAULT specifies B key" - @%:@endif - @%:@endif - ]])], - [rb_cv_aarch64_pac_b_key=no], - [rb_cv_aarch64_pac_b_key=yes]) - ) - AS_IF([test "$rb_cv_aarch64_pac_b_key" = yes], - AC_MSG_ERROR(-mbranch-protection flag specified b-key but Ruby's Context.S does not support this yet.)) - ]) -]) AC_CACHE_CHECK([whether compiler has statement and declarations in expressions], rb_cv_have_stmt_and_decl_in_expr, @@ -1125,7 +1072,6 @@ AS_CASE(["$target_os"], AS_IF([test $gcc_major -eq 4 -a $gcc_minor -lt 3], [ ac_cv_func___builtin_setjmp=no ]) - with_setjmp_type=sigsetjmp # to hijack SIGCHLD handler AC_CACHE_CHECK(for broken crypt with 8bit chars, rb_cv_broken_crypt, [AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include @@ -1717,7 +1663,7 @@ AS_IF([test "$rb_cv_func_weak" != x], [ AC_DEFINE(HAVE_FUNC_WEAK) ]) -AC_CACHE_CHECK([for __attribute__((__depreacted__(msg))) in C++], +AC_CACHE_CHECK([for __attribute__((__deprecated__(msg))) in C++], rb_cv_CentOS6_CXX_workaround, RUBY_WERROR_FLAG([ AC_LANG_PUSH([C++]) @@ -4751,7 +4697,7 @@ config_summary "target OS" "$target_os" config_summary "compiler" "$CC" config_summary "with thread" "$THREAD_MODEL" config_summary "with coroutine" "$coroutine_type" -config_summary "with shared GC" "$with_shared_gc" +config_summary "with shared GC" "$shared_gc_summary" config_summary "enable shared libs" "$ENABLE_SHARED" config_summary "dynamic library ext" "$DLEXT" config_summary "CFLAGS" "$cflags" diff --git a/coroutine/amd64/Context.S b/coroutine/amd64/Context.S index fcc4b67bdcc103..fad59ecddaddde 100644 --- a/coroutine/amd64/Context.S +++ b/coroutine/amd64/Context.S @@ -5,9 +5,9 @@ ## Copyright, 2018, by Samuel Williams. ## -#if defined(__OpenBSD__) -#include -#endif +/* Important - do _not_ include in this file; doing so will + * cause an incorrect .note.gnu.property section to be emitted. We have + * one at the bottom of this file */ #define TOKEN_PASTE(x,y) x##y #define PREFIXED_SYMBOL(prefix,name) TOKEN_PASTE(prefix,name) @@ -17,8 +17,9 @@ .globl PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer) PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer): -#if defined(__OpenBSD__) - _CET_ENDBR +#if defined(__CET__) && (__CET__ & 0x01) != 0 + /* IBT landing pad */ + endbr64 #endif # Make space on the stack for 6 registers: @@ -58,3 +59,28 @@ PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer): #if (defined(__linux__) || defined(__FreeBSD__)) && defined(__ELF__) .section .note.GNU-stack,"",%progbits #endif + +#if defined(__CET__) && (__CET__ & 0x01) != 0 +# define IBT_FLAG 0x01 +#else +# define IBT_FLAG 0x00 +#endif + +/* We do _NOT_ support CET shadow-stack. Do _not_ add the property for + * this to the Context.o object. If you require CET shadow-stack support, + * for now, consider building with --with-coroutine=ucontext */ +#define SHSTK_FLAG 0x00 + +.pushsection .note.gnu.property, "a" +.p2align 3 +.long 0x4 /* Name size ("GNU\0") */ +.long 0x10 /* Descriptor size */ +.long 0x5 /* Type: NT_GNU_PROPERTY_TYPE_0 */ +.asciz "GNU" /* Name */ +# Begin descriptor +.long 0xc0000002 /* Property type: GNU_PROPERTY_X86_FEATURE_1_AND */ +.long 0x4 /* Property size */ +.long (IBT_FLAG | SHSTK_FLAG) +.long 0x0 /* 8-byte alignment padding */ +/* End descriptor */ +.popsection diff --git a/coroutine/arm64/Context.S b/coroutine/arm64/Context.S index 54611a247e2f66..41146e80f50fad 100644 --- a/coroutine/arm64/Context.S +++ b/coroutine/arm64/Context.S @@ -5,8 +5,6 @@ ## Copyright, 2018, by Samuel Williams. ## -#include "ruby/config.h" - #define TOKEN_PASTE(x,y) x##y #define PREFIXED_SYMBOL(prefix,name) TOKEN_PASTE(prefix,name) @@ -20,6 +18,10 @@ .align 2 #endif +#if defined(__ARM_FEATURE_PAC_DEFAULT) && (__ARM_FEATURE_PAC_DEFAULT & 0x02) != 0 +# error "-mbranch-protection flag specified b-key but Context.S does not support this" +#endif + ## NOTE(PAC): Use we HINT mnemonics instead of PAC mnemonics to ## keep compatibility with those assemblers that don't support PAC. ## @@ -29,10 +31,10 @@ .global PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer) PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer): -#if defined(RUBY_AARCH64_PAC_ENABLED) +#if defined(__ARM_FEATURE_PAC_DEFAULT) && (__ARM_FEATURE_PAC_DEFAULT != 0) # paciasp (it also acts as BTI landing pad, so no need to insert BTI also) hint #25 -#elif defined(RUBY_AARCH64_BTI_ENABLED) +#elif defined(__ARM_FEATURE_BTI_DEFAULT) && (__ARM_FEATURE_BTI_DEFAULT != 0) # For the case PAC is not enabled but BTI is. # bti c hint #34 @@ -75,7 +77,7 @@ PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer): # Pop stack frame add sp, sp, 0xa0 -#if defined(RUBY_AARCH64_PAC_ENABLED) +#if defined(__ARM_FEATURE_PAC_DEFAULT) && (__ARM_FEATURE_PAC_DEFAULT != 0) # autiasp: Authenticate x30 (LR) with SP and key A hint #29 #endif @@ -87,18 +89,18 @@ PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer): .section .note.GNU-stack,"",%progbits #endif -#if defined(RUBY_AARCH64_BTI_ENABLED) || defined(RUBY_AARCH64_PAC_ENABLED) +#if (defined(__ARM_FEATURE_BTI_DEFAULT) && __ARM_FEATURE_BTI_DEFAULT != 0) || (defined(____ARM_FEATURE_PAC_DEFAULT) && __ARM_FEATURE_PAC_DEFAULT != 0) /* See "ELF for the Arm 64-bit Architecture (AArch64)" https://github.com/ARM-software/abi-aa/blob/2023Q3/aaelf64/aaelf64.rst#program-property */ # define GNU_PROPERTY_AARCH64_FEATURE_1_BTI (1<<0) # define GNU_PROPERTY_AARCH64_FEATURE_1_PAC (1<<1) -# if defined(RUBY_AARCH64_BTI_ENABLED) +# if defined(__ARM_FEATURE_BTI_DEFAULT) && __ARM_FEATURE_BTI_DEFAULT != 0 # define BTI_FLAG GNU_PROPERTY_AARCH64_FEATURE_1_BTI # else # define BTI_FLAG 0 # endif -# if defined(RUBY_AARCH64_PAC_ENABLED) +# if defined(__ARM_FEATURE_PAC_DEFAULT) && __ARM_FEATURE_PAC_DEFAULT != 0 # define PAC_FLAG GNU_PROPERTY_AARCH64_FEATURE_1_PAC # else # define PAC_FLAG 0 diff --git a/dir.c b/dir.c index 6d0ad02ee1926c..84687227a2d624 100644 --- a/dir.c +++ b/dir.c @@ -143,6 +143,50 @@ char *strchr(char*,char); # define IS_WIN32 0 #endif +#ifdef HAVE_GETATTRLIST +struct getattrlist_args { + const char *path; + int fd; + struct attrlist *list; + void *buf; + size_t size; + unsigned int options; +}; + +# define GETATTRLIST_ARGS(list_, buf_, options_) (struct getattrlist_args) \ + {.list = list_, .buf = buf_, .size = sizeof(buf_), .options = options_} + +static void * +nogvl_getattrlist(void *args) +{ + struct getattrlist_args *arg = args; + return (void *)(VALUE)getattrlist(arg->path, arg->list, arg->buf, arg->size, arg->options); +} + +static int +gvl_getattrlist(struct getattrlist_args *args, const char *path) +{ + args->path = path; + return IO_WITHOUT_GVL_INT(nogvl_getattrlist, args); +} + +# ifdef HAVE_FGETATTRLIST +static void * +nogvl_fgetattrlist(void *args) +{ + struct getattrlist_args *arg = args; + return (void *)(VALUE)fgetattrlist(arg->fd, arg->list, arg->buf, arg->size, arg->options); +} + +static int +gvl_fgetattrlist(struct getattrlist_args *args, int fd) +{ + args->fd = fd; + return IO_WITHOUT_GVL_INT(nogvl_fgetattrlist, args); +} +# endif +#endif + #if NORMALIZE_UTF8PATH # if defined HAVE_FGETATTRLIST || !defined HAVE_GETATTRLIST # define need_normalization(dirp, path) need_normalization(dirp) @@ -155,10 +199,11 @@ need_normalization(DIR *dirp, const char *path) # if defined HAVE_FGETATTRLIST || defined HAVE_GETATTRLIST u_int32_t attrbuf[SIZEUP32(fsobj_tag_t)]; struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, ATTR_CMN_OBJTAG,}; + struct getattrlist_args args = GETATTRLIST_ARGS(&al, attrbuf, 0); # if defined HAVE_FGETATTRLIST - int ret = fgetattrlist(dirfd(dirp), &al, attrbuf, sizeof(attrbuf), 0); + int ret = gvl_fgetattrlist(&args, dirfd(dirp)); # else - int ret = getattrlist(path, &al, attrbuf, sizeof(attrbuf), 0); + int ret = gvl_getattrlist(&args, path); # endif if (!ret) { const fsobj_tag_t *tag = (void *)(attrbuf+1); @@ -509,7 +554,7 @@ nogvl_opendir(void *ptr) { const char *path = ptr; - return (void *)opendir(path); + return opendir(path); } static DIR * @@ -555,7 +600,8 @@ dir_initialize(rb_execution_context_t *ec, VALUE dir, VALUE dirname, VALUE enc) else if (e == EIO) { u_int32_t attrbuf[1]; struct attrlist al = {ATTR_BIT_MAP_COUNT, 0}; - if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW) == 0) { + struct getattrlist_args args = GETATTRLIST_ARGS(&al, attrbuf, FSOPT_NOFOLLOW); + if (gvl_getattrlist(&args, path) == 0) { dp->dir = opendir_without_gvl(path); } } @@ -588,6 +634,12 @@ dir_s_close(rb_execution_context_t *ec, VALUE klass, VALUE dir) } # if defined(HAVE_FDOPENDIR) && defined(HAVE_DIRFD) +static void * +nogvl_fdopendir(void *fd) +{ + return fdopendir((int)(VALUE)fd); +} + /* * call-seq: * Dir.for_fd(fd) -> dir @@ -614,7 +666,7 @@ dir_s_for_fd(VALUE klass, VALUE fd) struct dir_data *dp; VALUE dir = TypedData_Make_Struct(klass, struct dir_data, &dir_data_type, dp); - if (!(dp->dir = fdopendir(NUM2INT(fd)))) { + if (!(dp->dir = IO_WITHOUT_GVL(nogvl_fdopendir, (void *)(VALUE)NUM2INT(fd)))) { rb_sys_fail("fdopendir"); UNREACHABLE_RETURN(Qnil); } @@ -756,8 +808,16 @@ fundamental_encoding_p(rb_encoding *enc) } } # define READDIR(dir, enc) rb_w32_readdir((dir), (enc)) +# define READDIR_NOGVL READDIR #else -# define READDIR(dir, enc) readdir((dir)) +static void * +nogvl_readdir(void *dir) +{ + return readdir(dir); +} + +# define READDIR(dir, enc) IO_WITHOUT_GVL(nogvl_readdir, (void *)(dir)) +# define READDIR_NOGVL(dir, enc) nogvl_readdir((dir)) #endif /* safe to use without GVL */ @@ -1038,7 +1098,7 @@ nogvl_chdir(void *ptr) static void dir_chdir0(VALUE path) { - if (chdir(RSTRING_PTR(path)) < 0) + if (IO_WITHOUT_GVL_INT(nogvl_chdir, (void*)RSTRING_PTR(path)) < 0) rb_sys_fail_path(path); } @@ -1240,7 +1300,7 @@ nogvl_fchdir(void *ptr) static void dir_fchdir(int fd) { - if (fchdir(fd) < 0) + if (IO_WITHOUT_GVL_INT(nogvl_fchdir, (void *)&fd) < 0) rb_sys_fail("fchdir"); } @@ -1460,6 +1520,12 @@ check_dirname(VALUE dir) } #if defined(HAVE_CHROOT) +static void * +nogvl_chroot(void *dirname) +{ + return (void *)(VALUE)chroot((const char *)dirname); +} + /* * call-seq: * Dir.chroot(dirpath) -> 0 @@ -1476,7 +1542,7 @@ static VALUE dir_s_chroot(VALUE dir, VALUE path) { path = check_dirname(path); - if (chroot(RSTRING_PTR(path)) == -1) + if (IO_WITHOUT_GVL_INT(nogvl_chroot, (void *)RSTRING_PTR(path)) == -1) rb_sys_fail_path(path); return INT2FIX(0); @@ -1653,11 +1719,11 @@ to_be_ignored(int e) } #ifdef _WIN32 -#define STAT(p, s) rb_w32_ustati128((p), (s)) -#undef lstat -#define lstat(p, s) rb_w32_ulstati128((p), (s)) +#define STAT(args) (int)(VALUE)nogvl_stat(&(args)) +#define LSTAT(args) (int)(VALUE)nogvl_lstat(&(args)) #else -#define STAT(p, s) stat((p), (s)) +#define STAT(args) IO_WITHOUT_GVL_INT(nogvl_stat, (void *)&(args)) +#define LSTAT(args) IO_WITHOUT_GVL_INT(nogvl_lstat, (void *)&(args)) #endif typedef int ruby_glob_errfunc(const char*, VALUE, const void*, int); @@ -1678,14 +1744,50 @@ at_subpath(int fd, size_t baselen, const char *path) return *path ? path : "."; } +#if USE_OPENDIR_AT +struct fstatat_args { + int fd; + int flag; + const char *path; + struct stat *pst; +}; + +static void * +nogvl_fstatat(void *args) +{ + struct fstatat_args *arg = (struct fstatat_args *)args; + return (void *)(VALUE)fstatat(arg->fd, arg->path, arg->pst, arg->flag); +} +#else +struct stat_args { + const char *path; + struct stat *pst; +}; + +static void * +nogvl_stat(void *args) +{ + struct stat_args *arg = (struct stat_args *)args; + return (void *)(VALUE)stat(arg->path, arg->pst); +} +#endif + /* System call with warning */ static int do_stat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, rb_encoding *enc) { #if USE_OPENDIR_AT - int ret = fstatat(fd, at_subpath(fd, baselen, path), pst, 0); + struct fstatat_args args; + args.fd = fd; + args.path = path; + args.pst = pst; + args.flag = 0; + int ret = IO_WITHOUT_GVL_INT(nogvl_fstatat, (void *)&args); #else - int ret = STAT(path, pst); + struct stat_args args; + args.path = path; + args.pst = pst; + int ret = STAT(args); #endif if (ret < 0 && !to_be_ignored(errno)) sys_warning(path, enc); @@ -1694,13 +1796,30 @@ do_stat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, r } #if defined HAVE_LSTAT || defined lstat || USE_OPENDIR_AT +#if !USE_OPENDIR_AT +static void * +nogvl_lstat(void *args) +{ + struct stat_args *arg = (struct stat_args *)args; + return (void *)(VALUE)lstat(arg->path, arg->pst); +} +#endif + static int do_lstat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, rb_encoding *enc) { #if USE_OPENDIR_AT - int ret = fstatat(fd, at_subpath(fd, baselen, path), pst, AT_SYMLINK_NOFOLLOW); + struct fstatat_args args; + args.fd = fd; + args.path = path; + args.pst = pst; + args.flag = AT_SYMLINK_NOFOLLOW; + int ret = IO_WITHOUT_GVL_INT(nogvl_fstatat, (void *)&args); #else - int ret = lstat(path, pst); + struct stat_args args; + args.path = path; + args.pst = pst; + int ret = LSTAT(args); #endif if (ret < 0 && !to_be_ignored(errno)) sys_warning(path, enc); @@ -2061,14 +2180,15 @@ is_case_sensitive(DIR *dirp, const char *path) const vol_capabilities_attr_t *const cap = attrbuf[0].cap; const int idx = VOL_CAPABILITIES_FORMAT; const uint32_t mask = VOL_CAP_FMT_CASE_SENSITIVE; - + struct getattrlist_args args = GETATTRLIST_ARGS(&al, attrbuf, FSOPT_NOFOLLOW); # if defined HAVE_FGETATTRLIST - if (fgetattrlist(dirfd(dirp), &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW)) - return -1; + int ret = gvl_fgetattrlist(&args, dirfd(dirp)); # else - if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW)) - return -1; + int ret = gvl_getattrlist(&args, path); # endif + if (ret) + return -1; + if (!(cap->valid[idx] & mask)) return -1; return (cap->capabilities[idx] & mask) != 0; @@ -2091,7 +2211,8 @@ replace_real_basename(char *path, long base, rb_encoding *enc, int norm_p, int f IF_NORMALIZE_UTF8PATH(VALUE utf8str = Qnil); *type = path_noent; - if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW)) { + struct getattrlist_args args = GETATTRLIST_ARGS(&al, attrbuf, FSOPT_NOFOLLOW); + if (gvl_getattrlist(&args, path)) { if (!to_be_ignored(errno)) sys_warning(path, enc); return path; @@ -3585,7 +3706,7 @@ nogvl_dir_empty_p(void *ptr) return (void *)INT2FIX(e); } } - while ((dp = READDIR(dir, NULL)) != NULL) { + while ((dp = READDIR_NOGVL(dir, NULL)) != NULL) { if (!to_be_skipped(dp)) { result = Qfalse; break; @@ -3627,12 +3748,13 @@ rb_dir_s_empty_p(VALUE obj, VALUE dirname) { u_int32_t attrbuf[SIZEUP32(fsobj_tag_t)]; struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, ATTR_CMN_OBJTAG,}; - if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), 0) != 0) + struct getattrlist_args args = GETATTRLIST_ARGS(&al, attrbuf, 0); + if (gvl_getattrlist(&args, path) != 0) rb_sys_fail_path(orig); if (*(const fsobj_tag_t *)(attrbuf+1) == VT_HFS) { al.commonattr = 0; al.dirattr = ATTR_DIR_ENTRYCOUNT; - if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), 0) == 0) { + if (gvl_getattrlist(&args, path) == 0) { if (attrbuf[0] >= 2 * sizeof(u_int32_t)) return RBOOL(attrbuf[1] == 0); if (false_on_notdir) return Qfalse; diff --git a/dln.c b/dln.c index 89f16b54f0631c..db6ea5aa0fb03d 100644 --- a/dln.c +++ b/dln.c @@ -76,12 +76,6 @@ void *xrealloc(); # include #endif -bool -dln_supported_p(void) -{ - return true; -} - #ifndef dln_loaderror static void dln_loaderror(const char *format, ...) @@ -200,6 +194,7 @@ 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) @@ -344,13 +339,16 @@ dln_disable_dlclose(void) #endif #if defined(_WIN32) || defined(USE_DLN_DLOPEN) -void * -dln_open(const char *file, char *error, size_t size) +static void * +dln_open(const char *file) { 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) { @@ -362,15 +360,15 @@ dln_open(const char *file, char *error, size_t size) free(winfile); if (!handle) { - strlcpy(error, dln_strerror(error, size), size); - return NULL; + error = dln_strerror(); + goto failed; } # if defined(RUBY_EXPORT) if (!rb_w32_check_imported(handle, rb_libruby_handle())) { FreeLibrary(handle); - strlcpy(error, incompatible, size); - return NULL; + error = incompatible; + goto failed; } # endif @@ -389,8 +387,8 @@ dln_open(const char *file, char *error, size_t size) /* Load file */ handle = dlopen(file, RTLD_LAZY|RTLD_GLOBAL); if (handle == NULL) { - strlcpy(error, dln_strerror(), size); - return NULL; + error = dln_strerror(); + goto failed; } # if defined(RUBY_EXPORT) @@ -412,15 +410,11 @@ dln_open(const char *file, char *error, size_t size) libruby_name = tmp; } dlclose(handle); - if (libruby_name) { - snprintf(error, size, "linked to incompatible %s - %s", libruby_name, file); - } - else { - strlcpy(error, incompatible, size); + dln_loaderror("linked to incompatible %s - %s", libruby_name, file); } - - return NULL; + error = incompatible; + goto failed; } } } @@ -428,9 +422,12 @@ dln_open(const char *file, char *error, size_t size) #endif return handle; + + failed: + dln_loaderror("%s - %s", error, file); } -void * +static void * dln_sym(void *handle, const char *symbol) { #if defined(_WIN32) @@ -449,7 +446,7 @@ dln_sym_func(void *handle, const char *symbol) const char *error; #if defined(_WIN32) char message[1024]; - error = dln_strerror(message, sizeof(message)); + error = dln_strerror(); #elif defined(USE_DLN_DLOPEN) const size_t errlen = strlen(error = dln_strerror()) + 1; error = memcpy(ALLOCA_N(char, errlen), error, errlen); @@ -504,12 +501,7 @@ void * dln_load(const char *file) { #if defined(_WIN32) || defined(USE_DLN_DLOPEN) - char error[1024]; - void *handle = dln_open(file, error, sizeof(error)); - - if (handle == NULL) { - dln_loaderror("%s - %s", error, file); - } + void *handle = dln_open(file); #ifdef RUBY_DLN_CHECK_ABI typedef unsigned long long abi_version_number; diff --git a/dln.h b/dln.h index 26df5266f7efdb..d624bb6611d207 100644 --- a/dln.h +++ b/dln.h @@ -22,11 +22,9 @@ 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, char *error, size_t size); void *dln_symbol(void*,const char*); RUBY_SYMBOL_EXPORT_END diff --git a/dmydln.c b/dmydln.c index 1f5b59022b1ad5..35824ebec8b832 100644 --- a/dmydln.c +++ b/dmydln.c @@ -3,12 +3,6 @@ #include "ruby/ruby.h" -bool -dln_supported_p(void) -{ - return false; -} - NORETURN(void *dln_load(const char *)); void* dln_load(const char *file) @@ -26,12 +20,3 @@ dln_symbol(void *handle, const char *symbol) UNREACHABLE_RETURN(NULL); } - -void* -dln_open(const char *library, char *error, size_t size) -{ - 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 ce844b5026bf22..2b7a06babad645 100644 --- a/doc/contributing/building_ruby.md +++ b/doc/contributing/building_ruby.md @@ -24,7 +24,7 @@ 2. Install optional, recommended dependencies: * [libffi] (to build fiddle) - * [gmp] (if you with to accelerate Bignum operations) + * [gmp] (if you wish to accelerate Bignum operations) * [rustc] - 1.58.0 or later, if you wish to build [YJIT](rdoc-ref:RubyVM::YJIT). diff --git a/doc/matchdata/bytebegin.rdoc b/doc/matchdata/bytebegin.rdoc new file mode 100644 index 00000000000000..5b40a7ef73ce4a --- /dev/null +++ b/doc/matchdata/bytebegin.rdoc @@ -0,0 +1,30 @@ +Returns the offset (in bytes) of the beginning of the specified match. + +When non-negative integer argument +n+ is given, +returns the offset of the beginning of the nth match: + + m = /(.)(.)(\d+)(\d)/.match("THX1138.") + # => # + m[0] # => "HX1138" + m.bytebegin(0) # => 1 + m[3] # => "113" + m.bytebegin(3) # => 3 + + m = /(т)(е)(с)/.match('тест') + # => # + m[0] # => "тес" + m.bytebegin(0) # => 0 + m[3] # => "с" + m.bytebegin(3) # => 4 + +When string or symbol argument +name+ is given, +returns the offset of the beginning for the named match: + + m = /(?.)(.)(?.)/.match("hoge") + # => # + m[:foo] # => "h" + m.bytebegin('foo') # => 0 + m[:bar] # => "g" + m.bytebegin(:bar) # => 2 + +Related: MatchData#byteend, MatchData#byteoffset. diff --git a/doc/matchdata/byteend.rdoc b/doc/matchdata/byteend.rdoc new file mode 100644 index 00000000000000..eb576640220b05 --- /dev/null +++ b/doc/matchdata/byteend.rdoc @@ -0,0 +1,30 @@ +Returns the offset (in bytes) of the end of the specified match. + +When non-negative integer argument +n+ is given, +returns the offset of the end of the nth match: + + m = /(.)(.)(\d+)(\d)/.match("THX1138.") + # => # + m[0] # => "HX1138" + m.byteend(0) # => 7 + m[3] # => "113" + m.byteend(3) # => 6 + + m = /(т)(е)(с)/.match('тест') + # => # + m[0] # => "тес" + m.byteend(0) # => 6 + m[3] # => "с" + m.byteend(3) # => 6 + +When string or symbol argument +name+ is given, +returns the offset of the end for the named match: + + m = /(?.)(.)(?.)/.match("hoge") + # => # + m[:foo] # => "h" + m.byteend('foo') # => 1 + m[:bar] # => "g" + m.byteend(:bar) # => 3 + +Related: MatchData#bytebegin, MatchData#byteoffset. diff --git a/doc/yjit/yjit.md b/doc/yjit/yjit.md index 8ea3409e485e4d..b2769a0f5de6f3 100644 --- a/doc/yjit/yjit.md +++ b/doc/yjit/yjit.md @@ -18,6 +18,7 @@ This project is open source and falls under the same license as CRuby.

If you wish to learn more about the approach taken, here are some conference talks and publications: + - RubyKaigi 2023 keynote: [Optimizing YJIT’s Performance, from Inception to Production](https://www.youtube.com/watch?v=X0JRhh8w_4I) - RubyKaigi 2023 keynote: [Fitting Rust YJIT into CRuby](https://www.youtube.com/watch?v=GI7vvAgP_Qs) - RubyKaigi 2022 keynote: [Stories from developing YJIT](https://www.youtube.com/watch?v=EMchdR9C8XM) @@ -160,7 +161,7 @@ You can dump statistics about compilation and execution by running YJIT with the The machine code generated for a given method can be printed by adding `puts RubyVM::YJIT.disasm(method(:method_name))` to a Ruby script. Note that no code will be generated if the method is not compiled. -### Command-Line Options +

Command-Line Options

YJIT supports all command-line options supported by upstream CRuby, but also adds a few YJIT-specific options: @@ -294,17 +295,17 @@ irb(main):001:0> RubyVM::YJIT.runtime_stats Some of the counters include: -* :yjit_insns_count - how many Ruby bytecode instructions have been executed -* :binding_allocations - number of bindings allocated -* :binding_set - number of variables set via a binding -* :code_gc_count - number of garbage collections of compiled code since process start -* :vm_insns_count - number of instructions executed by the Ruby interpreter -* :compiled_iseq_count - number of bytecode sequences compiled -* :inline_code_size - size in bytes of compiled YJIT blocks -* :outline_code_size - size in bytes of YJIT error-handling compiled code -* :side_exit_count - number of side exits taken at runtime -* :total_exit_count - number of exits, including side exits, taken at runtime -* :avg_len_in_yjit - avg. number of instructions in compiled blocks before exiting to interpreter +* `:yjit_insns_count` - how many Ruby bytecode instructions have been executed +* `:binding_allocations` - number of bindings allocated +* `:binding_set` - number of variables set via a binding +* `:code_gc_count` - number of garbage collections of compiled code since process start +* `:vm_insns_count` - number of instructions executed by the Ruby interpreter +* `:compiled_iseq_count` - number of bytecode sequences compiled +* `:inline_code_size` - size in bytes of compiled YJIT blocks +* `:outline_code_size` - size in bytes of YJIT error-handling compiled code +* `:side_exit_count` - number of side exits taken at runtime +* `:total_exit_count` - number of exits, including side exits, taken at runtime +* `:avg_len_in_yjit` - avg. number of instructions in compiled blocks before exiting to interpreter Counters starting with "exit_" show reasons for YJIT code taking a side exit (return to the interpreter.) @@ -330,6 +331,7 @@ you can contribute patches we will want to merge into YJIT. ### Source Code Organization The YJIT source code is divided between: + - `yjit.c`: code YJIT uses to interface with the rest of CRuby - `yjit.h`: C definitions YJIT exposes to the rest of the CRuby - `yjit.rb`: `YJIT` Ruby module that is exposed to Ruby @@ -342,6 +344,7 @@ The YJIT source code is divided between: - `yjit/bindgen/src/main.rs`: C bindings exposed to the Rust codebase through bindgen The core of CRuby's interpreter logic is found in: + - `insns.def`: defines Ruby's bytecode instructions (gets compiled into `vm.inc`) - `vm_insnshelper.c`: logic used by Ruby's bytecode instructions - `vm_exec.c`: Ruby interpreter loop @@ -363,6 +366,7 @@ add them to `yjit/cruby.rs` instead. ### Coding & Debugging Protips There are multiple test suites: + - `make btest` (see `/bootstraptest`) - `make test-all` - `make test-spec` @@ -381,12 +385,18 @@ Or single-threaded like this, to more easily identify which specific test is fai make test-all TESTOPTS=--verbose RUN_OPTS="--yjit-call-threshold=1" ``` -To debug a single test in `test-all`: +To run a single test file with `test-all`: ```sh make test-all TESTS='test/-ext-/marshal/test_usrmarshal.rb' RUNRUBYOPT=--debugger=lldb RUN_OPTS="--yjit-call-threshold=1" ``` +It's also possible to filter tests by name to run a single test: + +```sh +make test-all TESTS='-n /test_float_plus/' RUN_OPTS="--yjit-call-threshold=1" +``` + You can also run one specific test in `btest`: ```sh diff --git a/doc/yjit/yjit_hacking.md b/doc/yjit/yjit_hacking.md deleted file mode 100644 index 4c4d742b73e13a..00000000000000 --- a/doc/yjit/yjit_hacking.md +++ /dev/null @@ -1,75 +0,0 @@ -# YJIT Hacking - -## Code Generation and Assembly Language - -YJIT’s basic purpose is to take ISEQs and generate machine code. - -Documentation on each Ruby bytecode can be found in insns.def. - -YJIT uses those bytecodes as the “Basic Blocks” in Lazy Basic Block Versioning (LBBV.) For more deep details of LBBV, see yjit.md in this directory. - -Current YJIT has a simple assembler as a backend. Each method that generates code does it by emitting machine code: - -``` -# Excerpt of yjit_gen_exit() from yjit_codegen.c, Sept 2021 -// Generate an exit to return to the interpreter -static uint32_t -yjit_gen_exit(VALUE *exit_pc, ctx_t *ctx, codeblock_t *cb) -{ - const uint32_t code_pos = cb->write_pos; - - ADD_COMMENT(cb, "exit to interpreter"); - - // Generate the code to exit to the interpreters - // Write the adjusted SP back into the CFP - if (ctx->sp_offset != 0) { - x86opnd_t stack_pointer = ctx_sp_opnd(ctx, 0); - lea(cb, REG_SP, stack_pointer); - mov(cb, member_opnd(REG_CFP, rb_control_frame_t, sp), REG_SP); - } - - // Update CFP->PC - mov(cb, RAX, const_ptr_opnd(exit_pc)); - mov(cb, member_opnd(REG_CFP, rb_control_frame_t, pc), RAX); -``` - -Later there will be a more complex backend. - -## Code Generation vs Code Execution - -When you see lea() call above (“load effective address,”) it’s not running the LEA x86 instruction. It’s generating an LEA instruction to the codeblock pointer in the first argument. It will execute that instruction later, when the codeblock gets executed. - -This is subtle because YJIT will often wait to compile the method until you’re about to run it -- that’s when it knows the most about what types of arguments the method will receive. So it’s a compile-time instruction, but often it will defer compile-time until just barely before runtime. - -The ctx structure tracks what is known at compile time about the arguments being passed into the Ruby bytecode. Often YJIT will “peek” at an expected type before it generates machine code. - -## Inlined and Outlined Code - -When YJIT is generating code, it needs a code pointer. In many cases it needs two, usually called “cb” (codeblock) and “ocb” (out-of-line codeblock.) - -cb is for “inlined” normal code and ocb is for “outline” code such as exits. Inlined code is normal generated code for Ruby operations, while outlined code is for unusual and error conditions, such as encountering an unexpected parameter type and exiting to the interpreter. - -The purpose of the outlined code block is to keep things we believe are going to be infrequent somewhere else. That way we can keep the code in the inline block more linear and compact. Linear code, with as few branches as possible, is more easily predicted by the CPU. An exception or unsupported operation will cause YJIT to generate outlined code to handle it. - -If you search for ocb in yjit_codegen.c, you can see some places where out-of-line code is generated. - -YJIT statistics are only gathered when RUBY_DEBUG or YJIT_STATS is true. In some cases the code to increment YJIT statistics will be generated out-of-line, especially if those statistics are gathered when a side exit happens. - -## Statistics and Comments - -When RUBY_DEBUG is defined to a true value, YJIT will emit comments into the generated machine code. This can make disassemblies a lot more readable. When RUBY_DEBUG or YJIT_STATS is defined and stats are active (--yjit-stats or export YJIT_STATS=1), code will be generated to collect statistics during the run, and a report will be printed when the process exits. - -## Entering and Exiting the Interpreter - -YJIT won’t generate machine code for an ISEQ until it’s been run a certain number of times (10 by default.) Then, the next time the interpreter would call that ISEQ, it will call the generated machine code version instead. If YJIT hits an unexpected or unsupported operation, it will return to the normal interpreter. - -If YJIT returns to the interpreter, the behaviour will be correct but slower. YJIT only optimises part of some operations - for instance, YJIT will not optimise a BMETHOD call yet. - -When the interpreter calls to a YJIT-optimised method again, control will return to YJIT’s generated machine code. The more time that’s spent in YJIT-generated code (“ratio in YJIT,”) the more CPU time YJIT can save with its optimisations. - -## Side Exits - -When YJIT has compiled an ISEQ and is running it later, sometimes it will hit an unexpected condition. It might see a parameter of a different type than before, or square-brackets might be used on a hash when they were first used on an array. In those cases, the generated code will contain a call to return to the interpreter at runtime, called a “side exit.” - -Side exits are generated as out-of-line code. - diff --git a/enc/trans/ibm864-tbl.rb b/enc/trans/ibm864-tbl.rb new file mode 100644 index 00000000000000..13f8a27f1d89cf --- /dev/null +++ b/enc/trans/ibm864-tbl.rb @@ -0,0 +1,126 @@ +IBM864_TO_UCS_TBL = [ + ["80",0x00B0], + ["81",0x00B7], + ["82",0x2219], + ["83",0x221A], + ["84",0x2592], + ["85",0x2500], + ["86",0x2502], + ["87",0x253C], + ["88",0x2524], + ["89",0x252C], + ["8A",0x251C], + ["8B",0x2534], + ["8C",0x2510], + ["8D",0x250C], + ["8E",0x2514], + ["8F",0x2518], + ["90",0x03B2], + ["91",0x221E], + ["92",0x03C6], + ["93",0x00B1], + ["94",0x00BD], + ["95",0x00BC], + ["96",0x2248], + ["97",0x00AB], + ["98",0x00BB], + ["99",0xFEF7], + ["9A",0xFEF8], + ["9D",0xFEFB], + ["9E",0xFEFC], + ["9F",0xFE73], + ["A0",0x00A0], + ["A1",0x00AD], + ["A2",0xFE82], + ["A3",0x00A3], + ["A4",0x00A4], + ["A5",0xFE84], + ["A7",0x20AC], # Euro sign from CCSID 864 + ["A8",0xFE8E], + ["A9",0xFE8F], + ["AA",0xFE95], + ["AB",0xFE99], + ["AC",0x060C], + ["AD",0xFE9D], + ["AE",0xFEA1], + ["AF",0xFEA5], + ["B0",0x0660], + ["B1",0x0661], + ["B2",0x0662], + ["B3",0x0663], + ["B4",0x0664], + ["B5",0x0665], + ["B6",0x0666], + ["B7",0x0667], + ["B8",0x0668], + ["B9",0x0669], + ["BA",0xFED1], + ["BB",0x061B], + ["BC",0xFEB1], + ["BD",0xFEB5], + ["BE",0xFEB9], + ["BF",0x061F], + ["C0",0x00A2], + ["C1",0xFE80], + ["C2",0xFE81], + ["C3",0xFE83], + ["C4",0xFE85], + ["C5",0xFECA], + ["C6",0xFE8B], + ["C7",0xFE8D], + ["C8",0xFE91], + ["C9",0xFE93], + ["CA",0xFE97], + ["CB",0xFE9B], + ["CC",0xFE9F], + ["CD",0xFEA3], + ["CE",0xFEA7], + ["CF",0xFEA9], + ["D0",0xFEAB], + ["D1",0xFEAD], + ["D2",0xFEAF], + ["D3",0xFEB3], + ["D4",0xFEB7], + ["D5",0xFEBB], + ["D6",0xFEBF], + ["D7",0xFEC1], + ["D8",0xFEC5], + ["D9",0xFECB], + ["DA",0xFECF], + ["DB",0x00A6], + ["DC",0x00AC], + ["DD",0x00F7], + ["DE",0x00D7], + ["DF",0xFEC9], + ["E0",0x0640], + ["E1",0xFED3], + ["E2",0xFED7], + ["E3",0xFEDB], + ["E4",0xFEDF], + ["E5",0xFEE3], + ["E6",0xFEE7], + ["E7",0xFEEB], + ["E8",0xFEED], + ["E9",0xFEEF], + ["EA",0xFEF3], + ["EB",0xFEBD], + ["EC",0xFECC], + ["ED",0xFECE], + ["EE",0xFECD], + ["EF",0xFEE1], + ["F0",0xFE7D], + ["F1",0x0651], + ["F2",0xFEE5], + ["F3",0xFEE9], + ["F4",0xFEEC], + ["F5",0xFEF0], + ["F6",0xFEF2], + ["F7",0xFED0], + ["F8",0xFED5], + ["F9",0xFEF5], + ["FA",0xFEF6], + ["FB",0xFEDD], + ["FC",0xFED9], + ["FD",0xFEF1], + ["FE",0x25A0] +] diff --git a/enc/trans/single_byte.trans b/enc/trans/single_byte.trans index 0d5407b9188bf4..c326cbebea1143 100644 --- a/enc/trans/single_byte.trans +++ b/enc/trans/single_byte.trans @@ -61,6 +61,7 @@ transcode_tblgen_singlebyte "IBM861" transcode_tblgen_singlebyte "IBM862" transcode_tblgen_singlebyte "IBM863" + transcode_tblgen_singlebyte "IBM864" transcode_tblgen_singlebyte "IBM865" transcode_tblgen_singlebyte "IBM866" transcode_tblgen_singlebyte "IBM869" diff --git a/enum.c b/enum.c index dcb374778e469d..b4cddec9bc1bc4 100644 --- a/enum.c +++ b/enum.c @@ -322,16 +322,32 @@ enum_count(int argc, VALUE *argv, VALUE obj) return imemo_count_value(memo); } +NORETURN(static void found(VALUE i, VALUE memop)); +static void +found(VALUE i, VALUE memop) { + struct MEMO *memo = MEMO_CAST(memop); + MEMO_V1_SET(memo, i); + memo->u3.cnt = 1; + rb_iter_break(); +} + +static VALUE +find_i_fast(RB_BLOCK_CALL_FUNC_ARGLIST(i, memop)) +{ + if (RTEST(rb_yield_values2(argc, argv))) { + ENUM_WANT_SVALUE(); + found(i, memop); + } + return Qnil; +} + static VALUE find_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memop)) { ENUM_WANT_SVALUE(); if (RTEST(enum_yield(argc, i))) { - struct MEMO *memo = MEMO_CAST(memop); - MEMO_V1_SET(memo, i); - memo->u3.cnt = 1; - rb_iter_break(); + found(i, memop); } return Qnil; } @@ -366,7 +382,10 @@ enum_find(int argc, VALUE *argv, VALUE obj) if_none = rb_check_arity(argc, 0, 1) ? argv[0] : Qnil; RETURN_ENUMERATOR(obj, argc, argv); memo = MEMO_NEW(Qundef, 0, 0); - rb_block_call(obj, id_each, 0, 0, find_i, (VALUE)memo); + if (rb_block_pair_yield_optimizable()) + rb_block_call2(obj, id_each, 0, 0, find_i_fast, (VALUE)memo, RB_BLOCK_NO_USE_PACKED_ARGS); + else + rb_block_call2(obj, id_each, 0, 0, find_i, (VALUE)memo, RB_BLOCK_NO_USE_PACKED_ARGS); if (memo->u3.cnt) { return memo->v1; } @@ -1724,6 +1743,9 @@ enum_sort_by(VALUE obj) #define ENUMFUNC(name) argc ? name##_eqq : rb_block_given_p() ? name##_iter_i : name##_i +#define ENUM_BLOCK_CALL(name) \ + rb_block_call2(obj, id_each, 0, 0, ENUMFUNC(name), (VALUE)memo, rb_block_given_p() && rb_block_pair_yield_optimizable() ? RB_BLOCK_NO_USE_PACKED_ARGS : 0); + #define MEMO_ENUM_NEW(v1) (rb_check_arity(argc, 0, 1), MEMO_NEW((v1), (argc ? *argv : 0), 0)) #define DEFINE_ENUMFUNCS(name) \ @@ -1817,7 +1839,7 @@ enum_all(int argc, VALUE *argv, VALUE obj) { struct MEMO *memo = MEMO_ENUM_NEW(Qtrue); WARN_UNUSED_BLOCK(argc); - rb_block_call(obj, id_each, 0, 0, ENUMFUNC(all), (VALUE)memo); + ENUM_BLOCK_CALL(all); return memo->v1; } @@ -1879,7 +1901,7 @@ enum_any(int argc, VALUE *argv, VALUE obj) { struct MEMO *memo = MEMO_ENUM_NEW(Qfalse); WARN_UNUSED_BLOCK(argc); - rb_block_call(obj, id_each, 0, 0, ENUMFUNC(any), (VALUE)memo); + ENUM_BLOCK_CALL(any); return memo->v1; } @@ -2168,7 +2190,7 @@ enum_one(int argc, VALUE *argv, VALUE obj) VALUE result; WARN_UNUSED_BLOCK(argc); - rb_block_call(obj, id_each, 0, 0, ENUMFUNC(one), (VALUE)memo); + ENUM_BLOCK_CALL(one); result = memo->v1; if (UNDEF_P(result)) return Qfalse; return result; @@ -2229,7 +2251,7 @@ enum_none(int argc, VALUE *argv, VALUE obj) struct MEMO *memo = MEMO_ENUM_NEW(Qtrue); WARN_UNUSED_BLOCK(argc); - rb_block_call(obj, id_each, 0, 0, ENUMFUNC(none), (VALUE)memo); + ENUM_BLOCK_CALL(none); return memo->v1; } diff --git a/ext/-test-/bug-14834/bug-14384.c b/ext/-test-/bug-14834/bug-14834.c similarity index 94% rename from ext/-test-/bug-14834/bug-14384.c rename to ext/-test-/bug-14834/bug-14834.c index 3a16a2d222c832..af2070d303ff38 100644 --- a/ext/-test-/bug-14834/bug-14384.c +++ b/ext/-test-/bug-14834/bug-14834.c @@ -7,7 +7,7 @@ static NOINLINE(VALUE f(VALUE)); static NOINLINE(void g(VALUE, void*)); -extern NOINLINE(void Init_bug_14384(void)); +extern NOINLINE(void Init_bug_14834(void)); void Init_bug_14834(void) diff --git a/ext/-test-/bug-14834/depend b/ext/-test-/bug-14834/depend index 695094fa7a19e5..38429918b1f3f2 100644 --- a/ext/-test-/bug-14834/depend +++ b/ext/-test-/bug-14834/depend @@ -1,162 +1,162 @@ # AUTOGENERATED DEPENDENCIES START -bug-14384.o: $(RUBY_EXTCONF_H) -bug-14384.o: $(arch_hdrdir)/ruby/config.h -bug-14384.o: $(hdrdir)/ruby/assert.h -bug-14384.o: $(hdrdir)/ruby/backward.h -bug-14384.o: $(hdrdir)/ruby/backward/2/assume.h -bug-14384.o: $(hdrdir)/ruby/backward/2/attributes.h -bug-14384.o: $(hdrdir)/ruby/backward/2/bool.h -bug-14384.o: $(hdrdir)/ruby/backward/2/inttypes.h -bug-14384.o: $(hdrdir)/ruby/backward/2/limits.h -bug-14384.o: $(hdrdir)/ruby/backward/2/long_long.h -bug-14384.o: $(hdrdir)/ruby/backward/2/stdalign.h -bug-14384.o: $(hdrdir)/ruby/backward/2/stdarg.h -bug-14384.o: $(hdrdir)/ruby/debug.h -bug-14384.o: $(hdrdir)/ruby/defines.h -bug-14384.o: $(hdrdir)/ruby/intern.h -bug-14384.o: $(hdrdir)/ruby/internal/abi.h -bug-14384.o: $(hdrdir)/ruby/internal/anyargs.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/char.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/double.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/int.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/long.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/short.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h -bug-14384.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h -bug-14384.o: $(hdrdir)/ruby/internal/assume.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/alloc_size.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/artificial.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/cold.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/const.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/constexpr.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/deprecated.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/error.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/flag_enum.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/forceinline.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/format.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/noalias.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/nodiscard.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/noexcept.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/noinline.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/nonnull.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/noreturn.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/packed_struct.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/pure.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/restrict.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/warning.h -bug-14384.o: $(hdrdir)/ruby/internal/attr/weakref.h -bug-14384.o: $(hdrdir)/ruby/internal/cast.h -bug-14384.o: $(hdrdir)/ruby/internal/compiler_is.h -bug-14384.o: $(hdrdir)/ruby/internal/compiler_is/apple.h -bug-14384.o: $(hdrdir)/ruby/internal/compiler_is/clang.h -bug-14384.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h -bug-14384.o: $(hdrdir)/ruby/internal/compiler_is/intel.h -bug-14384.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h -bug-14384.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h -bug-14384.o: $(hdrdir)/ruby/internal/compiler_since.h -bug-14384.o: $(hdrdir)/ruby/internal/config.h -bug-14384.o: $(hdrdir)/ruby/internal/constant_p.h -bug-14384.o: $(hdrdir)/ruby/internal/core.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rarray.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rbasic.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rbignum.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rclass.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rdata.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rfile.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rhash.h -bug-14384.o: $(hdrdir)/ruby/internal/core/robject.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rregexp.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rstring.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rstruct.h -bug-14384.o: $(hdrdir)/ruby/internal/core/rtypeddata.h -bug-14384.o: $(hdrdir)/ruby/internal/ctype.h -bug-14384.o: $(hdrdir)/ruby/internal/dllexport.h -bug-14384.o: $(hdrdir)/ruby/internal/dosish.h -bug-14384.o: $(hdrdir)/ruby/internal/error.h -bug-14384.o: $(hdrdir)/ruby/internal/eval.h -bug-14384.o: $(hdrdir)/ruby/internal/event.h -bug-14384.o: $(hdrdir)/ruby/internal/fl_type.h -bug-14384.o: $(hdrdir)/ruby/internal/gc.h -bug-14384.o: $(hdrdir)/ruby/internal/glob.h -bug-14384.o: $(hdrdir)/ruby/internal/globals.h -bug-14384.o: $(hdrdir)/ruby/internal/has/attribute.h -bug-14384.o: $(hdrdir)/ruby/internal/has/builtin.h -bug-14384.o: $(hdrdir)/ruby/internal/has/c_attribute.h -bug-14384.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h -bug-14384.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h -bug-14384.o: $(hdrdir)/ruby/internal/has/extension.h -bug-14384.o: $(hdrdir)/ruby/internal/has/feature.h -bug-14384.o: $(hdrdir)/ruby/internal/has/warning.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/array.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/bignum.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/class.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/compar.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/complex.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/cont.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/dir.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/enum.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/enumerator.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/error.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/eval.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/file.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/hash.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/io.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/load.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/marshal.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/numeric.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/object.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/parse.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/proc.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/process.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/random.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/range.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/rational.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/re.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/ruby.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/select.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/select/largesize.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/signal.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/sprintf.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/string.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/struct.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/thread.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/time.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/variable.h -bug-14384.o: $(hdrdir)/ruby/internal/intern/vm.h -bug-14384.o: $(hdrdir)/ruby/internal/interpreter.h -bug-14384.o: $(hdrdir)/ruby/internal/iterator.h -bug-14384.o: $(hdrdir)/ruby/internal/memory.h -bug-14384.o: $(hdrdir)/ruby/internal/method.h -bug-14384.o: $(hdrdir)/ruby/internal/module.h -bug-14384.o: $(hdrdir)/ruby/internal/newobj.h -bug-14384.o: $(hdrdir)/ruby/internal/scan_args.h -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 -bug-14384.o: $(hdrdir)/ruby/internal/variable.h -bug-14384.o: $(hdrdir)/ruby/internal/warning_push.h -bug-14384.o: $(hdrdir)/ruby/internal/xmalloc.h -bug-14384.o: $(hdrdir)/ruby/missing.h -bug-14384.o: $(hdrdir)/ruby/ruby.h -bug-14384.o: $(hdrdir)/ruby/st.h -bug-14384.o: $(hdrdir)/ruby/subst.h -bug-14384.o: bug-14384.c +bug-14834.o: $(RUBY_EXTCONF_H) +bug-14834.o: $(arch_hdrdir)/ruby/config.h +bug-14834.o: $(hdrdir)/ruby/assert.h +bug-14834.o: $(hdrdir)/ruby/backward.h +bug-14834.o: $(hdrdir)/ruby/backward/2/assume.h +bug-14834.o: $(hdrdir)/ruby/backward/2/attributes.h +bug-14834.o: $(hdrdir)/ruby/backward/2/bool.h +bug-14834.o: $(hdrdir)/ruby/backward/2/inttypes.h +bug-14834.o: $(hdrdir)/ruby/backward/2/limits.h +bug-14834.o: $(hdrdir)/ruby/backward/2/long_long.h +bug-14834.o: $(hdrdir)/ruby/backward/2/stdalign.h +bug-14834.o: $(hdrdir)/ruby/backward/2/stdarg.h +bug-14834.o: $(hdrdir)/ruby/debug.h +bug-14834.o: $(hdrdir)/ruby/defines.h +bug-14834.o: $(hdrdir)/ruby/intern.h +bug-14834.o: $(hdrdir)/ruby/internal/abi.h +bug-14834.o: $(hdrdir)/ruby/internal/anyargs.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/char.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/double.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/int.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/long.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/short.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +bug-14834.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +bug-14834.o: $(hdrdir)/ruby/internal/assume.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/artificial.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/cold.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/const.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/constexpr.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/deprecated.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/error.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/forceinline.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/format.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/noalias.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/noexcept.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/noinline.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/nonnull.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/noreturn.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/pure.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/restrict.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/warning.h +bug-14834.o: $(hdrdir)/ruby/internal/attr/weakref.h +bug-14834.o: $(hdrdir)/ruby/internal/cast.h +bug-14834.o: $(hdrdir)/ruby/internal/compiler_is.h +bug-14834.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +bug-14834.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +bug-14834.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +bug-14834.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +bug-14834.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +bug-14834.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +bug-14834.o: $(hdrdir)/ruby/internal/compiler_since.h +bug-14834.o: $(hdrdir)/ruby/internal/config.h +bug-14834.o: $(hdrdir)/ruby/internal/constant_p.h +bug-14834.o: $(hdrdir)/ruby/internal/core.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rarray.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rbasic.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rbignum.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rclass.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rdata.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rfile.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rhash.h +bug-14834.o: $(hdrdir)/ruby/internal/core/robject.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rregexp.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rstring.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rstruct.h +bug-14834.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +bug-14834.o: $(hdrdir)/ruby/internal/ctype.h +bug-14834.o: $(hdrdir)/ruby/internal/dllexport.h +bug-14834.o: $(hdrdir)/ruby/internal/dosish.h +bug-14834.o: $(hdrdir)/ruby/internal/error.h +bug-14834.o: $(hdrdir)/ruby/internal/eval.h +bug-14834.o: $(hdrdir)/ruby/internal/event.h +bug-14834.o: $(hdrdir)/ruby/internal/fl_type.h +bug-14834.o: $(hdrdir)/ruby/internal/gc.h +bug-14834.o: $(hdrdir)/ruby/internal/glob.h +bug-14834.o: $(hdrdir)/ruby/internal/globals.h +bug-14834.o: $(hdrdir)/ruby/internal/has/attribute.h +bug-14834.o: $(hdrdir)/ruby/internal/has/builtin.h +bug-14834.o: $(hdrdir)/ruby/internal/has/c_attribute.h +bug-14834.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +bug-14834.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +bug-14834.o: $(hdrdir)/ruby/internal/has/extension.h +bug-14834.o: $(hdrdir)/ruby/internal/has/feature.h +bug-14834.o: $(hdrdir)/ruby/internal/has/warning.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/array.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/bignum.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/class.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/compar.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/complex.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/cont.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/dir.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/enum.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/enumerator.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/error.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/eval.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/file.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/hash.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/io.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/load.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/marshal.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/numeric.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/object.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/parse.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/proc.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/process.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/random.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/range.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/rational.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/re.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/ruby.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/select.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/signal.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/sprintf.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/string.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/struct.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/thread.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/time.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/variable.h +bug-14834.o: $(hdrdir)/ruby/internal/intern/vm.h +bug-14834.o: $(hdrdir)/ruby/internal/interpreter.h +bug-14834.o: $(hdrdir)/ruby/internal/iterator.h +bug-14834.o: $(hdrdir)/ruby/internal/memory.h +bug-14834.o: $(hdrdir)/ruby/internal/method.h +bug-14834.o: $(hdrdir)/ruby/internal/module.h +bug-14834.o: $(hdrdir)/ruby/internal/newobj.h +bug-14834.o: $(hdrdir)/ruby/internal/scan_args.h +bug-14834.o: $(hdrdir)/ruby/internal/special_consts.h +bug-14834.o: $(hdrdir)/ruby/internal/static_assert.h +bug-14834.o: $(hdrdir)/ruby/internal/stdalign.h +bug-14834.o: $(hdrdir)/ruby/internal/stdbool.h +bug-14834.o: $(hdrdir)/ruby/internal/stdckdint.h +bug-14834.o: $(hdrdir)/ruby/internal/symbol.h +bug-14834.o: $(hdrdir)/ruby/internal/value.h +bug-14834.o: $(hdrdir)/ruby/internal/value_type.h +bug-14834.o: $(hdrdir)/ruby/internal/variable.h +bug-14834.o: $(hdrdir)/ruby/internal/warning_push.h +bug-14834.o: $(hdrdir)/ruby/internal/xmalloc.h +bug-14834.o: $(hdrdir)/ruby/missing.h +bug-14834.o: $(hdrdir)/ruby/ruby.h +bug-14834.o: $(hdrdir)/ruby/st.h +bug-14834.o: $(hdrdir)/ruby/subst.h +bug-14834.o: bug-14834.c # AUTOGENERATED DEPENDENCIES END diff --git a/ext/objspace/objspace.c b/ext/objspace/objspace.c index 24d7bd419f3f38..e3269b5cd954b4 100644 --- a/ext/objspace/objspace.c +++ b/ext/objspace/objspace.c @@ -577,7 +577,7 @@ reachable_object_from_i(VALUE obj, void *data_ptr) VALUE key = obj; VALUE val = obj; - if (rb_objspace_markable_object_p(obj)) { + if (!rb_objspace_garbage_object_p(obj)) { if (NIL_P(rb_hash_lookup(data->refs, key))) { rb_hash_aset(data->refs, key, Qtrue); @@ -643,7 +643,7 @@ collect_values(st_data_t key, st_data_t value, st_data_t data) static VALUE reachable_objects_from(VALUE self, VALUE obj) { - if (rb_objspace_markable_object_p(obj)) { + if (!RB_SPECIAL_CONST_P(obj)) { struct rof_data data; if (rb_typeddata_is_kind_of(obj, &iow_data_type)) { @@ -690,7 +690,7 @@ reachable_object_from_root_i(const char *category, VALUE obj, void *ptr) rb_hash_aset(data->categories, category_str, category_objects); } - if (rb_objspace_markable_object_p(obj) && + if (!rb_objspace_garbage_object_p(obj) && obj != data->categories && obj != data->last_category_objects) { if (rb_objspace_internal_object_p(obj)) { diff --git a/ext/win32ole/win32ole.c b/ext/win32ole/win32ole.c index e0342d1e9dc9f2..cabbeb8431dbef 100644 --- a/ext/win32ole/win32ole.c +++ b/ext/win32ole/win32ole.c @@ -1426,9 +1426,10 @@ ole_variant2val(VARIANT *pvar) vt = V_VT(pvar); } +#define ARG_AS(type, pvar) (V_ISBYREF(pvar) ? *V_##type##REF(pvar) : V_##type(pvar)) if(V_ISARRAY(pvar)) { VARTYPE vt_base = vt & VT_TYPEMASK; - SAFEARRAY *psa = V_ISBYREF(pvar) ? *V_ARRAYREF(pvar) : V_ARRAY(pvar); + SAFEARRAY *psa = ARG_AS(ARRAY, pvar); UINT i = 0; LONG *pid, *plb, *pub; VARIANT variant; @@ -1495,109 +1496,58 @@ ole_variant2val(VARIANT *pvar) case VT_NULL: break; case VT_I1: - if(V_ISBYREF(pvar)) - obj = RB_INT2NUM((long)*V_I1REF(pvar)); - else - obj = RB_INT2NUM((long)V_I1(pvar)); + obj = RB_INT2NUM((long)ARG_AS(I1, pvar)); break; case VT_UI1: - if(V_ISBYREF(pvar)) - obj = RB_INT2NUM((long)*V_UI1REF(pvar)); - else - obj = RB_INT2NUM((long)V_UI1(pvar)); + obj = RB_INT2NUM((long)ARG_AS(UI1, pvar)); break; case VT_I2: - if(V_ISBYREF(pvar)) - obj = RB_INT2NUM((long)*V_I2REF(pvar)); - else - obj = RB_INT2NUM((long)V_I2(pvar)); + obj = RB_INT2NUM((long)ARG_AS(I2, pvar)); break; case VT_UI2: - if(V_ISBYREF(pvar)) - obj = RB_INT2NUM((long)*V_UI2REF(pvar)); - else - obj = RB_INT2NUM((long)V_UI2(pvar)); + obj = RB_INT2NUM((long)ARG_AS(UI2, pvar)); break; case VT_I4: - if(V_ISBYREF(pvar)) - obj = RB_INT2NUM((long)*V_I4REF(pvar)); - else - obj = RB_INT2NUM((long)V_I4(pvar)); + obj = RB_INT2NUM((long)ARG_AS(I4, pvar)); break; case VT_UI4: - if(V_ISBYREF(pvar)) - obj = RB_INT2NUM((long)*V_UI4REF(pvar)); - else - obj = RB_INT2NUM((long)V_UI4(pvar)); + obj = RB_INT2NUM((long)ARG_AS(UI4, pvar)); break; case VT_INT: - if(V_ISBYREF(pvar)) - obj = RB_INT2NUM((long)*V_INTREF(pvar)); - else - obj = RB_INT2NUM((long)V_INT(pvar)); + obj = RB_INT2NUM((long)ARG_AS(INT, pvar)); break; case VT_UINT: - if(V_ISBYREF(pvar)) - obj = RB_INT2NUM((long)*V_UINTREF(pvar)); - else - obj = RB_INT2NUM((long)V_UINT(pvar)); + obj = RB_INT2NUM((long)ARG_AS(UINT, pvar)); break; #if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) case VT_I8: - if(V_ISBYREF(pvar)) -#if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) -#ifdef V_I8REF - obj = I8_2_NUM(*V_I8REF(pvar)); -#endif -#else - obj = Qnil; -#endif - else - obj = I8_2_NUM(V_I8(pvar)); + obj = I8_2_NUM(ARG_AS(I8, pvar)); break; case VT_UI8: - if(V_ISBYREF(pvar)) -#if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) -#ifdef V_UI8REF - obj = UI8_2_NUM(*V_UI8REF(pvar)); -#endif -#else - obj = Qnil; -#endif - else - obj = UI8_2_NUM(V_UI8(pvar)); + obj = UI8_2_NUM(ARG_AS(UI8, pvar)); break; #endif /* (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) */ case VT_R4: - if(V_ISBYREF(pvar)) - obj = rb_float_new(*V_R4REF(pvar)); - else - obj = rb_float_new(V_R4(pvar)); + obj = rb_float_new(ARG_AS(R4, pvar)); break; case VT_R8: - if(V_ISBYREF(pvar)) - obj = rb_float_new(*V_R8REF(pvar)); - else - obj = rb_float_new(V_R8(pvar)); + obj = rb_float_new(ARG_AS(R8, pvar)); break; case VT_BSTR: { BSTR bstr; - if(V_ISBYREF(pvar)) - bstr = *V_BSTRREF(pvar); - else - bstr = V_BSTR(pvar); + bstr = ARG_AS(BSTR, pvar); obj = (SysStringLen(bstr) == 0) ? rb_str_new2("") : ole_wc2vstr(bstr, FALSE); @@ -1605,27 +1555,18 @@ ole_variant2val(VARIANT *pvar) } case VT_ERROR: - if(V_ISBYREF(pvar)) - obj = RB_INT2NUM(*V_ERRORREF(pvar)); - else - obj = RB_INT2NUM(V_ERROR(pvar)); + obj = RB_INT2NUM(ARG_AS(ERROR, pvar)); break; case VT_BOOL: - if (V_ISBYREF(pvar)) - obj = (*V_BOOLREF(pvar) ? Qtrue : Qfalse); - else - obj = (V_BOOL(pvar) ? Qtrue : Qfalse); + obj = (ARG_AS(BOOL, pvar) ? Qtrue : Qfalse); break; case VT_DISPATCH: { IDispatch *pDispatch; - if (V_ISBYREF(pvar)) - pDispatch = *V_DISPATCHREF(pvar); - else - pDispatch = V_DISPATCH(pvar); + pDispatch = ARG_AS(DISPATCH, pvar); if (pDispatch != NULL ) { OLE_ADDREF(pDispatch); @@ -1642,10 +1583,7 @@ ole_variant2val(VARIANT *pvar) void *p; HRESULT hr; - if (V_ISBYREF(pvar)) - punk = *V_UNKNOWNREF(pvar); - else - punk = V_UNKNOWN(pvar); + punk = ARG_AS(UNKNOWN, pvar); if(punk != NULL) { hr = punk->lpVtbl->QueryInterface(punk, &IID_IDispatch, &p); @@ -1660,10 +1598,7 @@ ole_variant2val(VARIANT *pvar) case VT_DATE: { DATE date; - if(V_ISBYREF(pvar)) - date = *V_DATEREF(pvar); - else - date = V_DATE(pvar); + date = ARG_AS(DATE, pvar); obj = vtdate2rbtime(date); break; @@ -1693,6 +1628,7 @@ ole_variant2val(VARIANT *pvar) } } return obj; +#undef ARG_AS } LONG diff --git a/file.c b/file.c index 835f19e541df97..97172257c09924 100644 --- a/file.c +++ b/file.c @@ -2930,7 +2930,7 @@ utime_failed(struct apply_arg *aa) # elif defined(__APPLE__) && \ (!defined(MAC_OS_X_VERSION_13_0) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_13_0)) -# if defined(__has_attribute) && __has_attribute(availability) +# if __has_attribute(availability) && __has_warning("-Wunguarded-availability-new") typedef int utimensat_func(int, const char *, const struct timespec [2], int); RBIMPL_WARNING_PUSH() @@ -2945,7 +2945,7 @@ RBIMPL_WARNING_POP() # define utimensat rb_utimensat() # else /* __API_AVAILABLE macro does nothing on gcc */ __attribute__((weak)) int utimensat(int, const char *, const struct timespec [2], int); -# endif /* defined(__has_attribute) && __has_attribute(availability) */ +# endif /* utimesat availability */ # endif /* __APPLE__ && < MAC_OS_X_VERSION_13_0 */ static int @@ -3690,6 +3690,14 @@ copy_home_path(VALUE result, const char *dir) return result; } +#ifdef HAVE_PWD_H +static void * +nogvl_getpwnam(void *login) +{ + return (void *)getpwnam((const char *)login); +} +#endif + VALUE rb_home_dir_of(VALUE user, VALUE result) { @@ -3712,7 +3720,7 @@ rb_home_dir_of(VALUE user, VALUE result) } #ifdef HAVE_PWD_H - pwPtr = getpwnam(username); + pwPtr = (struct passwd *)IO_WITHOUT_GVL(nogvl_getpwnam, (void *)username); #else if (strcasecmp(username, getlogin()) == 0) dir = pwPtr = getenv("HOME"); @@ -5326,16 +5334,12 @@ rb_thread_flock(void *data) * Returns `false` if `File::LOCK_NB` is specified and the operation would have blocked; * otherwise returns `0`. * - *
- * * | Constant | Lock | Effect - * |-----------------|--------------|------------------------------------------------------------------- - * | +File::LOCK_EX+ | Exclusive | Only one process may hold an exclusive lock for +self+ at a time. - * | +File::LOCK_NB+ | Non-blocking | No blocking; may be combined with +File::LOCK_SH+ or +File::LOCK_EX+ using the bitwise OR operator \|. - * | +File::LOCK_SH+ | Shared | Multiple processes may each hold a shared lock for +self+ at the same time. - * | +File::LOCK_UN+ | Unlock | Remove an existing lock held by this process. - * - *
+ * |-----------------|--------------|-----------------------------------------------------------------------------------------------------------------| + * | +File::LOCK_EX+ | Exclusive | Only one process may hold an exclusive lock for +self+ at a time. | + * | +File::LOCK_NB+ | Non-blocking | No blocking; may be combined with +File::LOCK_SH+ or +File::LOCK_EX+ using the bitwise OR operator \|. | + * | +File::LOCK_SH+ | Shared | Multiple processes may each hold a shared lock for +self+ at the same time. | + * | +File::LOCK_UN+ | Unlock | Remove an existing lock held by this process. | * * Example: * diff --git a/gc.c b/gc.c index df031533880c9f..cf38f740042704 100644 --- a/gc.c +++ b/gc.c @@ -19,13 +19,6 @@ # include "ruby/ruby.h" #endif -#include - -#ifndef _WIN32 -#include -#include -#endif - #if defined(__wasm__) && !defined(__EMSCRIPTEN__) # include "wasm/setjmp.h" # include "wasm/machine.h" @@ -37,16 +30,6 @@ /* MALLOC_HEADERS_BEGIN */ #ifndef HAVE_MALLOC_USABLE_SIZE -# ifdef _WIN32 -# define HAVE_MALLOC_USABLE_SIZE -# define malloc_usable_size(a) _msize(a) -# elif defined HAVE_MALLOC_SIZE -# define HAVE_MALLOC_USABLE_SIZE -# define malloc_usable_size(a) malloc_size(a) -# endif -#endif - -#ifdef HAVE_MALLOC_USABLE_SIZE # ifdef RUBY_ALTERNATIVE_MALLOC_HEADER /* Alternative malloc header is included in ruby/missing.h */ # elif defined(HAVE_MALLOC_H) @@ -56,21 +39,20 @@ # elif defined(HAVE_MALLOC_MALLOC_H) # include # endif -#endif - -#ifdef HAVE_MALLOC_TRIM -# include -# ifdef __EMSCRIPTEN__ -/* malloc_trim is defined in emscripten/emmalloc.h on emscripten. */ -# include +# ifdef _WIN32 +# define HAVE_MALLOC_USABLE_SIZE +# define malloc_usable_size(a) _msize(a) +# elif defined HAVE_MALLOC_SIZE +# define HAVE_MALLOC_USABLE_SIZE +# define malloc_usable_size(a) malloc_size(a) # endif +#else +# include #endif -#if !defined(PAGE_SIZE) && defined(HAVE_SYS_USER_H) -/* LIST_HEAD conflicts with sys/queue.h on macOS */ -# include -#endif +# define GC_ASSERT + /* MALLOC_HEADERS_END */ #ifdef HAVE_SYS_TIME_H @@ -94,11 +76,6 @@ #include #endif -#ifdef HAVE_MACH_TASK_EXCEPTION_PORTS -# include -# include -# include -#endif #undef LIST_HEAD /* ccan/list conflicts with BSD-origin sys/queue.h. */ #include "constant.h" @@ -106,6 +83,8 @@ #include "debug_counter.h" #include "eval_intern.h" #include "glospace.h" +#include "gc/gc.h" +#include "gc/gc_impl.h" #include "id_table.h" #include "internal.h" #include "internal/class.h" @@ -137,6 +116,7 @@ #include "ruby/st.h" #include "ruby/thread.h" #include "ruby/util.h" +#include "ruby/vm.h" #include "ruby_assert.h" #include "ruby_atomic.h" #include "symbol.h" @@ -148,6 +128,207 @@ #include "builtin.h" #include "shape.h" +unsigned int +rb_gc_vm_lock(void) +{ + unsigned int lev; + RB_VM_LOCK_ENTER_LEV(&lev); + return lev; +} + +void +rb_gc_vm_unlock(unsigned int lev) +{ + RB_VM_LOCK_LEAVE_LEV(&lev); +} + +unsigned int +rb_gc_cr_lock(void) +{ + unsigned int lev; + RB_VM_LOCK_ENTER_CR_LEV(GET_RACTOR(), &lev); + return lev; +} + +void +rb_gc_cr_unlock(unsigned int lev) +{ + RB_VM_LOCK_LEAVE_CR_LEV(GET_RACTOR(), &lev); +} + +unsigned int +rb_gc_vm_lock_no_barrier(void) +{ + unsigned int lev; + RB_VM_LOCK_ENTER_LEV_NB(&lev); + return lev; +} + +void +rb_gc_vm_unlock_no_barrier(unsigned int lev) +{ + RB_VM_LOCK_LEAVE_LEV(&lev); +} + +void +rb_gc_vm_barrier(void) +{ + rb_vm_barrier(); +} + +void +rb_gc_event_hook(VALUE obj, rb_event_flag_t event) +{ + if (LIKELY(!(ruby_vm_event_flags & event))) return; + + rb_execution_context_t *ec = GET_EC(); + if (!ec->cfp) return; + + EXEC_EVENT_HOOK(ec, event, ec->cfp->self, 0, 0, 0, obj); +} + +void * +rb_gc_get_objspace(void) +{ + return current_ractor_objspace(); +} + +void +rb_gc_ractor_newobj_cache_foreach(void (*func)(void *cache, void *data), void *data) +{ + rb_ractor_t *r = NULL; + ccan_list_for_each(&GET_VM()->ractor.set, r, vmlr_node) { + func(r->newobj_cache, data); + } +} + +void +rb_gc_run_obj_finalizer(VALUE objid, long count, VALUE (*callback)(long i, void *data), void *data) +{ + volatile struct { + VALUE errinfo; + VALUE final; + rb_control_frame_t *cfp; + VALUE *sp; + long finished; + } saved; + + rb_execution_context_t * volatile ec = GET_EC(); +#define RESTORE_FINALIZER() (\ + ec->cfp = saved.cfp, \ + ec->cfp->sp = saved.sp, \ + ec->errinfo = saved.errinfo) + + saved.errinfo = ec->errinfo; + saved.cfp = ec->cfp; + saved.sp = ec->cfp->sp; + saved.finished = 0; + saved.final = Qundef; + + EC_PUSH_TAG(ec); + enum ruby_tag_type state = EC_EXEC_TAG(); + if (state != TAG_NONE) { + ++saved.finished; /* skip failed finalizer */ + + VALUE failed_final = saved.final; + saved.final = Qundef; + if (!UNDEF_P(failed_final) && !NIL_P(ruby_verbose)) { + rb_warn("Exception in finalizer %+"PRIsVALUE, failed_final); + rb_ec_error_print(ec, ec->errinfo); + } + } + + for (long i = saved.finished; RESTORE_FINALIZER(), i < count; saved.finished = ++i) { + saved.final = callback(i, data); + rb_check_funcall(saved.final, idCall, 1, &objid); + } + EC_POP_TAG(); +#undef RESTORE_FINALIZER +} + +void +rb_gc_set_pending_interrupt(void) +{ + rb_execution_context_t *ec = GET_EC(); + ec->interrupt_mask |= PENDING_INTERRUPT_MASK; +} + +void +rb_gc_unset_pending_interrupt(void) +{ + rb_execution_context_t *ec = GET_EC(); + ec->interrupt_mask &= ~PENDING_INTERRUPT_MASK; +} + +bool +rb_gc_multi_ractor_p(void) +{ + return rb_multi_ractor_p(); +} + +bool rb_obj_is_main_ractor(VALUE gv); + +bool +rb_gc_shutdown_call_finalizer_p(VALUE obj) +{ + switch (BUILTIN_TYPE(obj)) { + case T_DATA: + if (!ruby_free_at_exit_p() && (!DATA_PTR(obj) || !RDATA(obj)->dfree)) return false; + if (rb_obj_is_thread(obj)) return false; + if (rb_obj_is_mutex(obj)) return false; + if (rb_obj_is_fiber(obj)) return false; + if (rb_obj_is_main_ractor(obj)) return false; + + return true; + + case T_FILE: + return true; + + case T_SYMBOL: + if (RSYMBOL(obj)->fstr && + (BUILTIN_TYPE(RSYMBOL(obj)->fstr) == T_NONE || + BUILTIN_TYPE(RSYMBOL(obj)->fstr) == T_ZOMBIE)) { + RSYMBOL(obj)->fstr = 0; + } + return true; + + case T_NONE: + return false; + + default: + return ruby_free_at_exit_p(); + } +} + +uint32_t +rb_gc_get_shape(VALUE obj) +{ + return (uint32_t)rb_shape_get_shape_id(obj); +} + +void +rb_gc_set_shape(VALUE obj, uint32_t shape_id) +{ + rb_shape_set_shape_id(obj, (uint32_t)shape_id); +} + +uint32_t +rb_gc_rebuild_shape(VALUE obj, size_t size_pool_id) +{ + rb_shape_t *orig_shape = rb_shape_get_shape(obj); + + if (rb_shape_obj_too_complex(obj)) return (uint32_t)OBJ_TOO_COMPLEX_SHAPE_ID; + + rb_shape_t *initial_shape = rb_shape_get_shape_by_id((shape_id_t)(size_pool_id + FIRST_T_OBJECT_SHAPE_ID)); + rb_shape_t *new_shape = rb_shape_traverse_from_new_root(initial_shape, orig_shape); + + if (!new_shape) return 0; + + return (uint32_t)rb_shape_id(new_shape); +} + +void rb_vm_update_references(void *ptr); + #define rb_setjmp(env) RUBY_SETJMP(env) #define rb_jmp_buf rb_jmpbuf_t #undef rb_data_object_wrap @@ -156,6 +337,39 @@ #define MAP_ANONYMOUS MAP_ANON #endif +#define unless_objspace(objspace) \ + rb_objspace_t *objspace; \ + rb_vm_t *unless_objspace_vm = GET_VM(); \ + if (unless_objspace_vm) objspace = current_ractor_objspace(); \ + else /* return; or objspace will be warned uninitialized */ + +#define RMOVED(obj) ((struct RMoved *)(obj)) + +#define TYPED_UPDATE_IF_MOVED(_objspace, _type, _thing) do { \ + if (rb_gc_impl_object_moved_p((_objspace), (VALUE)(_thing))) { \ + *(_type *)&(_thing) = (_type)rb_gc_impl_location(_objspace, (VALUE)_thing); \ + } \ +} while (0) + +#define UPDATE_IF_MOVED(_objspace, _thing) TYPED_UPDATE_IF_MOVED(_objspace, VALUE, _thing) + +#if RUBY_MARK_FREE_DEBUG +int ruby_gc_debug_indent = 0; +#endif + +#ifndef RGENGC_CHECK_MODE +# define RGENGC_CHECK_MODE 0 +#endif + +#ifndef RGENGC_OBJ_INFO +# define RGENGC_OBJ_INFO RGENGC_CHECK_MODE +#endif + +#ifndef CALC_EXACT_MALLOC_SIZE +# define CALC_EXACT_MALLOC_SIZE 0 +#endif + +VALUE rb_mGC; static size_t malloc_offset = 0; #if defined(HAVE_MALLOC_USABLE_SIZE) @@ -352,13916 +566,3283 @@ rb_gc_guarded_ptr_val(volatile VALUE *ptr, VALUE val) } #endif -#ifndef GC_HEAP_INIT_SLOTS -#define GC_HEAP_INIT_SLOTS 10000 -#endif -#ifndef GC_HEAP_FREE_SLOTS -#define GC_HEAP_FREE_SLOTS 4096 -#endif -#ifndef GC_HEAP_GROWTH_FACTOR -#define GC_HEAP_GROWTH_FACTOR 1.8 -#endif -#ifndef GC_HEAP_GROWTH_MAX_SLOTS -#define GC_HEAP_GROWTH_MAX_SLOTS 0 /* 0 is disable */ -#endif -#ifndef GC_HEAP_REMEMBERED_WB_UNPROTECTED_OBJECTS_LIMIT_RATIO -# define GC_HEAP_REMEMBERED_WB_UNPROTECTED_OBJECTS_LIMIT_RATIO 0.01 -#endif -#ifndef GC_HEAP_OLDOBJECT_LIMIT_FACTOR -#define GC_HEAP_OLDOBJECT_LIMIT_FACTOR 2.0 -#endif -#ifndef GC_HEAP_SHAREDOBJECT_LIMIT_FACTOR -#define GC_HEAP_SHAREDOBJECT_LIMIT_FACTOR 2.0 -#endif +#if USE_SHARED_GC && !defined(HAVE_DLOPEN) +# error "Shared GC requires dlopen" +#elif USE_SHARED_GC +#include -#ifndef GC_HEAP_FREE_SLOTS_MIN_RATIO -#define GC_HEAP_FREE_SLOTS_MIN_RATIO 0.20 -#endif -#ifndef GC_HEAP_FREE_SLOTS_GOAL_RATIO -#define GC_HEAP_FREE_SLOTS_GOAL_RATIO 0.40 -#endif -#ifndef GC_HEAP_FREE_SLOTS_MAX_RATIO -#define GC_HEAP_FREE_SLOTS_MAX_RATIO 0.65 -#endif +typedef struct gc_function_map { + // Bootup + void *(*objspace_alloc)(void); + void (*objspace_init)(void *objspace_ptr); + void (*objspace_free)(void *objspace_ptr); + void *(*ractor_cache_alloc)(void *objspace_ptr); + void (*ractor_cache_free)(void *objspace_ptr, void *cache); + void (*set_params)(void *objspace_ptr); + void (*init)(void); + void (*initial_stress_set)(VALUE flag); + size_t *(*size_pool_sizes)(void *objspace_ptr); + // Shutdown + void (*shutdown_free_objects)(void *objspace_ptr); + // GC + void (*start)(void *objspace_ptr, bool full_mark, bool immediate_mark, bool immediate_sweep, bool compact); + bool (*during_gc_p)(void *objspace_ptr); + void (*prepare_heap)(void *objspace_ptr); + void (*gc_enable)(void *objspace_ptr); + void (*gc_disable)(void *objspace_ptr, bool finish_current_gc); + bool (*gc_enabled_p)(void *objspace_ptr); + VALUE (*config_get)(void *objpace_ptr); + VALUE (*config_set)(void *objspace_ptr, VALUE hash); + void (*stress_set)(void *objspace_ptr, VALUE flag); + VALUE (*stress_get)(void *objspace_ptr); + // Object allocation + VALUE (*new_obj)(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, bool wb_protected, size_t alloc_size); + size_t (*obj_slot_size)(VALUE obj); + size_t (*size_pool_id_for_size)(void *objspace_ptr, size_t size); + bool (*size_allocatable_p)(size_t size); + // Malloc + void *(*malloc)(void *objspace_ptr, size_t size); + void *(*calloc)(void *objspace_ptr, size_t size); + void *(*realloc)(void *objspace_ptr, void *ptr, size_t new_size, size_t old_size); + void (*free)(void *objspace_ptr, void *ptr, size_t old_size); + void (*adjust_memory_usage)(void *objspace_ptr, ssize_t diff); + // Marking + void (*mark)(void *objspace_ptr, VALUE obj); + void (*mark_and_move)(void *objspace_ptr, VALUE *ptr); + void (*mark_and_pin)(void *objspace_ptr, VALUE obj); + void (*mark_maybe)(void *objspace_ptr, VALUE obj); + void (*mark_weak)(void *objspace_ptr, VALUE *ptr); + void (*remove_weak)(void *objspace_ptr, VALUE parent_obj, VALUE *ptr); + void (*objspace_mark)(void *objspace_ptr); + // Compaction + bool (*object_moved_p)(void *objspace_ptr, VALUE obj); + VALUE (*location)(void *objspace_ptr, VALUE value); + // Write barriers + void (*writebarrier)(void *objspace_ptr, VALUE a, VALUE b); + void (*writebarrier_unprotect)(void *objspace_ptr, VALUE obj); + void (*writebarrier_remember)(void *objspace_ptr, VALUE obj); + // Heap walking + void (*each_objects)(void *objspace_ptr, int (*callback)(void *, void *, size_t, void *), void *data); + void (*each_object)(void *objspace_ptr, void (*func)(VALUE obj, void *data), void *data); + // Finalizers + void (*make_zombie)(void *objspace_ptr, VALUE obj, void (*dfree)(void *), void *data); + VALUE (*define_finalizer)(void *objspace_ptr, VALUE obj, VALUE block); + VALUE (*undefine_finalizer)(void *objspace_ptr, VALUE obj); + void (*copy_finalizer)(void *objspace_ptr, VALUE dest, VALUE obj); + void (*shutdown_call_finalizer)(void *objspace_ptr); + // Object ID + VALUE (*object_id)(void *objspace_ptr, VALUE obj); + VALUE (*object_id_to_ref)(void *objspace_ptr, VALUE object_id); + // Statistics + VALUE (*set_measure_total_time)(void *objspace_ptr, VALUE flag); + VALUE (*get_measure_total_time)(void *objspace_ptr); + VALUE (*get_profile_total_time)(void *objspace_ptr); + size_t (*gc_count)(void *objspace_ptr); + VALUE (*latest_gc_info)(void *objspace_ptr, VALUE key); + size_t (*stat)(void *objspace_ptr, VALUE hash_or_sym); + size_t (*stat_heap)(void *objspace_ptr, VALUE heap_name, VALUE hash_or_sym); + // Miscellaneous + size_t (*obj_flags)(void *objspace_ptr, VALUE obj, ID* flags, size_t max); + bool (*pointer_to_heap_p)(void *objspace_ptr, const void *ptr); + bool (*garbage_object_p)(void *objspace_ptr, VALUE obj); + void (*set_event_hook)(void *objspace_ptr, const rb_event_flag_t event); + void (*copy_attributes)(void *objspace_ptr, VALUE dest, VALUE obj); +} rb_gc_function_map_t; -#ifndef GC_MALLOC_LIMIT_MIN -#define GC_MALLOC_LIMIT_MIN (16 * 1024 * 1024 /* 16MB */) -#endif -#ifndef GC_MALLOC_LIMIT_MAX -#define GC_MALLOC_LIMIT_MAX (32 * 1024 * 1024 /* 32MB */) -#endif -#ifndef GC_MALLOC_LIMIT_GROWTH_FACTOR -#define GC_MALLOC_LIMIT_GROWTH_FACTOR 1.4 -#endif +static rb_gc_function_map_t rb_gc_functions; -#ifndef GC_OLDMALLOC_LIMIT_MIN -#define GC_OLDMALLOC_LIMIT_MIN (16 * 1024 * 1024 /* 16MB */) -#endif -#ifndef GC_OLDMALLOC_LIMIT_GROWTH_FACTOR -#define GC_OLDMALLOC_LIMIT_GROWTH_FACTOR 1.2 -#endif -#ifndef GC_OLDMALLOC_LIMIT_MAX -#define GC_OLDMALLOC_LIMIT_MAX (128 * 1024 * 1024 /* 128MB */) -#endif +# define RUBY_GC_LIBRARY "RUBY_GC_LIBRARY" -#ifndef GC_CAN_COMPILE_COMPACTION -#if defined(__wasi__) /* WebAssembly doesn't support signals */ -# define GC_CAN_COMPILE_COMPACTION 0 -#else -# define GC_CAN_COMPILE_COMPACTION 1 -#endif -#endif +static void +ruby_external_gc_init(void) +{ + // Assert that the directory path ends with a / + GC_ASSERT(SHARED_GC_DIR[strlen(SHARED_GC_DIR) - 2] == '/'); -#ifndef PRINT_MEASURE_LINE -#define PRINT_MEASURE_LINE 0 -#endif -#ifndef PRINT_ENTER_EXIT_TICK -#define PRINT_ENTER_EXIT_TICK 0 -#endif -#ifndef PRINT_ROOT_TICKS -#define PRINT_ROOT_TICKS 0 -#endif + char *gc_so_file = getenv(RUBY_GC_LIBRARY); + char *gc_so_path = NULL; + void *handle = NULL; + if (gc_so_file) { + /* Check to make sure that gc_so_file matches /[\w-_.]+/ so that it does + * not load a shared object outside of the directory. */ + for (size_t i = 0; i < strlen(gc_so_file); i++) { + char c = gc_so_file[i]; + if (isalnum(c)) continue; + switch (c) { + case '-': + case '_': + case '.': + break; + default: + rb_bug("Only alphanumeric, dash, underscore, and period is allowed in "RUBY_GC_LIBRARY""); + } + } -#define USE_TICK_T (PRINT_ENTER_EXIT_TICK || PRINT_MEASURE_LINE || PRINT_ROOT_TICKS) - -typedef struct { - size_t size_pool_init_slots[SIZE_POOL_COUNT]; - size_t heap_free_slots; - double growth_factor; - size_t growth_max_slots; - - double heap_free_slots_min_ratio; - double heap_free_slots_goal_ratio; - double heap_free_slots_max_ratio; - double uncollectible_wb_unprotected_objects_limit_ratio; - double oldobject_limit_factor; - double sharedobject_limit_factor; - - size_t malloc_limit_min; - size_t malloc_limit_max; - double malloc_limit_growth_factor; - - size_t oldmalloc_limit_min; - size_t oldmalloc_limit_max; - double oldmalloc_limit_growth_factor; -} ruby_gc_params_t; - -static ruby_gc_params_t gc_params = { - { 0 }, - GC_HEAP_FREE_SLOTS, - GC_HEAP_GROWTH_FACTOR, - GC_HEAP_GROWTH_MAX_SLOTS, - - GC_HEAP_FREE_SLOTS_MIN_RATIO, - GC_HEAP_FREE_SLOTS_GOAL_RATIO, - GC_HEAP_FREE_SLOTS_MAX_RATIO, - GC_HEAP_REMEMBERED_WB_UNPROTECTED_OBJECTS_LIMIT_RATIO, - GC_HEAP_OLDOBJECT_LIMIT_FACTOR, - GC_HEAP_SHAREDOBJECT_LIMIT_FACTOR, - - GC_MALLOC_LIMIT_MIN, - GC_MALLOC_LIMIT_MAX, - GC_MALLOC_LIMIT_GROWTH_FACTOR, - - GC_OLDMALLOC_LIMIT_MIN, - GC_OLDMALLOC_LIMIT_MAX, - GC_OLDMALLOC_LIMIT_GROWTH_FACTOR, -}; + gc_so_path = alloca(strlen(SHARED_GC_DIR) + strlen(gc_so_file) + 1); + strcpy(gc_so_path, SHARED_GC_DIR); + strcpy(gc_so_path + strlen(SHARED_GC_DIR), gc_so_file); + gc_so_path[strlen(SHARED_GC_DIR) + strlen(gc_so_file)] = '\0'; -/* GC_DEBUG: - * enable to embed GC debugging information. - */ -#ifndef GC_DEBUG -#define GC_DEBUG 0 -#endif + handle = dlopen(gc_so_path, RTLD_LAZY | RTLD_GLOBAL); + if (!handle) { + fprintf(stderr, "%s", dlerror()); + rb_bug("ruby_external_gc_init: Shared library %s cannot be opened", gc_so_path); + } + } -/* RGENGC_DEBUG: - * 1: basic information - * 2: remember set operation - * 3: mark - * 4: - * 5: sweep - */ -#ifndef RGENGC_DEBUG -#ifdef RUBY_DEVEL -#define RGENGC_DEBUG -1 -#else -#define RGENGC_DEBUG 0 -#endif -#endif -#if RGENGC_DEBUG < 0 && !defined(_MSC_VER) -# define RGENGC_DEBUG_ENABLED(level) (-(RGENGC_DEBUG) >= (level) && ruby_rgengc_debug >= (level)) -#elif defined(HAVE_VA_ARGS_MACRO) -# define RGENGC_DEBUG_ENABLED(level) ((RGENGC_DEBUG) >= (level)) -#else -# define RGENGC_DEBUG_ENABLED(level) 0 -#endif -int ruby_rgengc_debug; - -/* RGENGC_CHECK_MODE - * 0: disable all assertions - * 1: enable assertions (to debug RGenGC) - * 2: enable internal consistency check at each GC (for debugging) - * 3: enable internal consistency check at each GC steps (for debugging) - * 4: enable liveness check - * 5: show all references - */ -#ifndef RGENGC_CHECK_MODE -#define RGENGC_CHECK_MODE 0 -#endif +# define load_external_gc_func(name) do { \ + if (handle) { \ + rb_gc_functions.name = dlsym(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) -// Note: using RUBY_ASSERT_WHEN() extend a macro in expr (info by nobu). -#define GC_ASSERT(expr) RUBY_ASSERT_MESG_WHEN(RGENGC_CHECK_MODE > 0, expr, #expr) + // Bootup + load_external_gc_func(objspace_alloc); + load_external_gc_func(objspace_init); + load_external_gc_func(objspace_free); + load_external_gc_func(ractor_cache_alloc); + load_external_gc_func(ractor_cache_free); + load_external_gc_func(set_params); + load_external_gc_func(init); + load_external_gc_func(initial_stress_set); + load_external_gc_func(size_pool_sizes); + // Shutdown + load_external_gc_func(shutdown_free_objects); + // GC + load_external_gc_func(start); + load_external_gc_func(during_gc_p); + load_external_gc_func(prepare_heap); + load_external_gc_func(gc_enable); + load_external_gc_func(gc_disable); + load_external_gc_func(gc_enabled_p); + load_external_gc_func(config_set); + load_external_gc_func(config_get); + load_external_gc_func(stress_set); + load_external_gc_func(stress_get); + // Object allocation + load_external_gc_func(new_obj); + load_external_gc_func(obj_slot_size); + load_external_gc_func(size_pool_id_for_size); + load_external_gc_func(size_allocatable_p); + // Malloc + load_external_gc_func(malloc); + load_external_gc_func(calloc); + load_external_gc_func(realloc); + load_external_gc_func(free); + load_external_gc_func(adjust_memory_usage); + // Marking + load_external_gc_func(mark); + load_external_gc_func(mark_and_move); + load_external_gc_func(mark_and_pin); + load_external_gc_func(mark_maybe); + load_external_gc_func(mark_weak); + load_external_gc_func(remove_weak); + load_external_gc_func(objspace_mark); + // Compaction + load_external_gc_func(object_moved_p); + load_external_gc_func(location); + // Write barriers + load_external_gc_func(writebarrier); + load_external_gc_func(writebarrier_unprotect); + load_external_gc_func(writebarrier_remember); + // Heap walking + load_external_gc_func(each_objects); + load_external_gc_func(each_object); + // Finalizers + load_external_gc_func(make_zombie); + load_external_gc_func(define_finalizer); + load_external_gc_func(undefine_finalizer); + load_external_gc_func(copy_finalizer); + load_external_gc_func(shutdown_call_finalizer); + // Object ID + load_external_gc_func(object_id); + load_external_gc_func(object_id_to_ref); + // Statistics + load_external_gc_func(set_measure_total_time); + load_external_gc_func(get_measure_total_time); + load_external_gc_func(get_profile_total_time); + load_external_gc_func(gc_count); + load_external_gc_func(latest_gc_info); + load_external_gc_func(stat); + load_external_gc_func(stat_heap); + // Miscellaneous + load_external_gc_func(obj_flags); + load_external_gc_func(pointer_to_heap_p); + load_external_gc_func(garbage_object_p); + load_external_gc_func(set_event_hook); + load_external_gc_func(copy_attributes); -/* RGENGC_PROFILE - * 0: disable RGenGC profiling - * 1: enable profiling for basic information - * 2: enable profiling for each types - */ -#ifndef RGENGC_PROFILE -#define RGENGC_PROFILE 0 -#endif +# undef load_external_gc_func +} -/* RGENGC_ESTIMATE_OLDMALLOC - * Enable/disable to estimate increase size of malloc'ed size by old objects. - * If estimation exceeds threshold, then will invoke full GC. - * 0: disable estimation. - * 1: enable estimation. - */ -#ifndef RGENGC_ESTIMATE_OLDMALLOC -#define RGENGC_ESTIMATE_OLDMALLOC 1 +// Bootup +# define rb_gc_impl_objspace_alloc rb_gc_functions.objspace_alloc +# define rb_gc_impl_objspace_init rb_gc_functions.objspace_init +# define rb_gc_impl_objspace_free rb_gc_functions.objspace_free +# define rb_gc_impl_ractor_cache_alloc rb_gc_functions.ractor_cache_alloc +# define rb_gc_impl_ractor_cache_free rb_gc_functions.ractor_cache_free +# define rb_gc_impl_set_params rb_gc_functions.set_params +# define rb_gc_impl_init rb_gc_functions.init +# define rb_gc_impl_initial_stress_set rb_gc_functions.initial_stress_set +# define rb_gc_impl_size_pool_sizes rb_gc_functions.size_pool_sizes +// Shutdown +# define rb_gc_impl_shutdown_free_objects rb_gc_functions.shutdown_free_objects +// GC +# define rb_gc_impl_start rb_gc_functions.start +# define rb_gc_impl_during_gc_p rb_gc_functions.during_gc_p +# define rb_gc_impl_prepare_heap rb_gc_functions.prepare_heap +# define rb_gc_impl_gc_enable rb_gc_functions.gc_enable +# define rb_gc_impl_gc_disable rb_gc_functions.gc_disable +# define rb_gc_impl_gc_enabled_p rb_gc_functions.gc_enabled_p +# define rb_gc_impl_config_get rb_gc_functions.config_get +# define rb_gc_impl_config_set rb_gc_functions.config_set +# define rb_gc_impl_stress_set rb_gc_functions.stress_set +# define rb_gc_impl_stress_get rb_gc_functions.stress_get +// Object allocation +# define rb_gc_impl_new_obj rb_gc_functions.new_obj +# define rb_gc_impl_obj_slot_size rb_gc_functions.obj_slot_size +# define rb_gc_impl_size_pool_id_for_size rb_gc_functions.size_pool_id_for_size +# define rb_gc_impl_size_allocatable_p rb_gc_functions.size_allocatable_p +// Malloc +# define rb_gc_impl_malloc rb_gc_functions.malloc +# define rb_gc_impl_calloc rb_gc_functions.calloc +# define rb_gc_impl_realloc rb_gc_functions.realloc +# define rb_gc_impl_free rb_gc_functions.free +# define rb_gc_impl_adjust_memory_usage rb_gc_functions.adjust_memory_usage +// Marking +# define rb_gc_impl_mark rb_gc_functions.mark +# define rb_gc_impl_mark_and_move rb_gc_functions.mark_and_move +# define rb_gc_impl_mark_and_pin rb_gc_functions.mark_and_pin +# define rb_gc_impl_mark_maybe rb_gc_functions.mark_maybe +# define rb_gc_impl_mark_weak rb_gc_functions.mark_weak +# define rb_gc_impl_remove_weak rb_gc_functions.remove_weak +# define rb_gc_impl_objspace_mark rb_gc_functions.objspace_mark +// Compaction +# define rb_gc_impl_object_moved_p rb_gc_functions.object_moved_p +# define rb_gc_impl_location rb_gc_functions.location +// Write barriers +# define rb_gc_impl_writebarrier rb_gc_functions.writebarrier +# define rb_gc_impl_writebarrier_unprotect rb_gc_functions.writebarrier_unprotect +# define rb_gc_impl_writebarrier_remember rb_gc_functions.writebarrier_remember +// Heap walking +# define rb_gc_impl_each_objects rb_gc_functions.each_objects +# define rb_gc_impl_each_object rb_gc_functions.each_object +// Finalizers +# define rb_gc_impl_make_zombie rb_gc_functions.make_zombie +# define rb_gc_impl_define_finalizer rb_gc_functions.define_finalizer +# define rb_gc_impl_undefine_finalizer rb_gc_functions.undefine_finalizer +# define rb_gc_impl_copy_finalizer rb_gc_functions.copy_finalizer +# define rb_gc_impl_shutdown_call_finalizer rb_gc_functions.shutdown_call_finalizer +// Object ID +# define rb_gc_impl_object_id rb_gc_functions.object_id +# define rb_gc_impl_object_id_to_ref rb_gc_functions.object_id_to_ref +// Statistics +# define rb_gc_impl_set_measure_total_time rb_gc_functions.set_measure_total_time +# define rb_gc_impl_get_measure_total_time rb_gc_functions.get_measure_total_time +# define rb_gc_impl_get_profile_total_time rb_gc_functions.get_profile_total_time +# define rb_gc_impl_gc_count rb_gc_functions.gc_count +# define rb_gc_impl_latest_gc_info rb_gc_functions.latest_gc_info +# define rb_gc_impl_stat rb_gc_functions.stat +# define rb_gc_impl_stat_heap rb_gc_functions.stat_heap +// Miscellaneous +# define rb_gc_impl_obj_flags rb_gc_functions.obj_flags +# define rb_gc_impl_pointer_to_heap_p rb_gc_functions.pointer_to_heap_p +# define rb_gc_impl_garbage_object_p rb_gc_functions.garbage_object_p +# define rb_gc_impl_set_event_hook rb_gc_functions.set_event_hook +# define rb_gc_impl_copy_attributes rb_gc_functions.copy_attributes #endif -/* RGENGC_FORCE_MAJOR_GC - * Force major/full GC if this macro is not 0. - */ -#ifndef RGENGC_FORCE_MAJOR_GC -#define RGENGC_FORCE_MAJOR_GC 0 +void * +rb_objspace_alloc(void) +{ +#if USE_SHARED_GC + ruby_external_gc_init(); #endif -#ifndef GC_PROFILE_MORE_DETAIL -#define GC_PROFILE_MORE_DETAIL 0 -#endif -#ifndef GC_PROFILE_DETAIL_MEMORY -#define GC_PROFILE_DETAIL_MEMORY 0 -#endif -#ifndef GC_ENABLE_LAZY_SWEEP -#define GC_ENABLE_LAZY_SWEEP 1 -#endif -#ifndef CALC_EXACT_MALLOC_SIZE -#define CALC_EXACT_MALLOC_SIZE USE_GC_MALLOC_OBJ_INFO_DETAILS -#endif -#if defined(HAVE_MALLOC_USABLE_SIZE) || CALC_EXACT_MALLOC_SIZE > 0 -#ifndef MALLOC_ALLOCATED_SIZE -#define MALLOC_ALLOCATED_SIZE 0 -#endif -#else -#define MALLOC_ALLOCATED_SIZE 0 -#endif -#ifndef MALLOC_ALLOCATED_SIZE_CHECK -#define MALLOC_ALLOCATED_SIZE_CHECK 0 -#endif - -#ifndef GC_DEBUG_STRESS_TO_CLASS -#define GC_DEBUG_STRESS_TO_CLASS RUBY_DEBUG -#endif - -#ifndef RGENGC_OBJ_INFO -#define RGENGC_OBJ_INFO (RGENGC_DEBUG | RGENGC_CHECK_MODE) -#endif - -typedef enum { - GPR_FLAG_NONE = 0x000, - /* major reason */ - GPR_FLAG_MAJOR_BY_NOFREE = 0x001, - GPR_FLAG_MAJOR_BY_OLDGEN = 0x002, - GPR_FLAG_MAJOR_BY_SHADY = 0x004, - GPR_FLAG_MAJOR_BY_FORCE = 0x008, - GPR_FLAG_MAJOR_BY_ABSORB = 0x010, -#if RGENGC_ESTIMATE_OLDMALLOC - GPR_FLAG_MAJOR_BY_OLDMALLOC = 0x020, -#endif - GPR_FLAG_MAJOR_MASK = 0x0ff, - - /* gc reason */ - GPR_FLAG_NEWOBJ = 0x100, - GPR_FLAG_MALLOC = 0x200, - GPR_FLAG_METHOD = 0x400, - GPR_FLAG_CAPI = 0x800, - GPR_FLAG_STRESS = 0x1000, - - /* others */ - GPR_FLAG_IMMEDIATE_SWEEP = 0x2000, - GPR_FLAG_HAVE_FINALIZE = 0x4000, - GPR_FLAG_IMMEDIATE_MARK = 0x8000, - GPR_FLAG_FULL_MARK = 0x10000, - GPR_FLAG_COMPACT = 0x20000, - GPR_FLAG_GLOBAL = 0x40000, - - GPR_DEFAULT_REASON = - (GPR_FLAG_FULL_MARK | GPR_FLAG_IMMEDIATE_MARK | - GPR_FLAG_IMMEDIATE_SWEEP | GPR_FLAG_CAPI), -} gc_profile_record_flag; - -typedef struct gc_profile_record { - unsigned int flags; - - double gc_time; - double gc_invoke_time; - - size_t heap_total_objects; - size_t heap_use_size; - size_t heap_total_size; - size_t moved_objects; - -#if GC_PROFILE_MORE_DETAIL - double gc_mark_time; - double gc_sweep_time; - - size_t heap_use_pages; - size_t heap_live_objects; - size_t heap_free_objects; - - size_t allocate_increase; - size_t allocate_limit; - - double prepare_time; - size_t removing_objects; - size_t empty_objects; -#if GC_PROFILE_DETAIL_MEMORY - long maxrss; - long minflt; - long majflt; -#endif -#endif -#if MALLOC_ALLOCATED_SIZE - size_t allocated_size; -#endif - -#if RGENGC_PROFILE > 0 - size_t old_objects; - size_t remembered_normal_objects; - size_t remembered_shady_objects; -#endif -} gc_profile_record; - -struct RMoved { - VALUE flags; - VALUE dummy; - VALUE destination; - shape_id_t original_shape_id; -}; - -#define RMOVED(obj) ((struct RMoved *)(obj)) - -typedef struct RVALUE { - union { - struct { - VALUE flags; /* always 0 for freed obj */ - struct RVALUE *next; - } free; - struct RMoved moved; - struct RBasic basic; - struct RObject object; - struct RClass klass; - struct RFloat flonum; - struct RString string; - struct RArray array; - struct RRegexp regexp; - struct RHash hash; - struct RData data; - struct RTypedData typeddata; - struct RStruct rstruct; - struct RBignum bignum; - struct RFile file; - struct RMatch match; - struct RRational rational; - struct RComplex complex; - struct RSymbol symbol; - union { - rb_cref_t cref; - struct vm_svar svar; - struct vm_throw_data throw_data; - struct vm_ifunc ifunc; - struct MEMO memo; - struct rb_method_entry_struct ment; - const rb_iseq_t iseq; - rb_env_t env; - struct rb_imemo_tmpbuf_struct alloc; - rb_ast_t ast; - } imemo; - struct { - struct RBasic basic; - VALUE v1; - VALUE v2; - VALUE v3; - } values; - } as; -} RVALUE; - -/* 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 - const char *file; - int line; -# endif -}; - -// 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)); -STATIC_ASSERT(alignof_rvalue, RUBY_ALIGNOF(RVALUE) == SIZEOF_VALUE); - -typedef uintptr_t bits_t; -enum { - BITS_SIZE = sizeof(bits_t), - BITS_BITLENGTH = ( BITS_SIZE * CHAR_BIT ) -}; - -struct heap_page_header { - struct heap_page *page; -}; - -struct heap_page_body { - struct heap_page_header header; - /* char gap[]; */ - /* RVALUE values[]; */ -}; - -#define STACK_CHUNK_SIZE 500 - -typedef struct stack_chunk { - VALUE data[STACK_CHUNK_SIZE]; - struct stack_chunk *next; -} stack_chunk_t; - -typedef struct mark_stack { - stack_chunk_t *chunk; - stack_chunk_t *cache; - int index; - int limit; - size_t cache_size; - size_t unused_cache_size; -} mark_stack_t; - -#define SIZE_POOL_EDEN_HEAP(size_pool) (&(size_pool)->eden_heap) -#define SIZE_POOL_TOMB_HEAP(size_pool) (&(size_pool)->tomb_heap) - -typedef int (*gc_compact_compare_func)(const void *l, const void *r, void *d); - -typedef struct rb_heap_struct { - struct heap_page *free_pages; - struct ccan_list_head pages; - struct heap_page *sweeping_page; /* iterator for .pages */ - struct heap_page *compact_cursor; - uintptr_t compact_cursor_index; - struct heap_page *pooled_pages; - size_t total_pages; /* total page count in a heap */ - size_t total_slots; /* total slot count (about total_pages * HEAP_PAGE_OBJ_LIMIT) */ -} rb_heap_t; - -typedef struct rb_size_pool_struct { - short slot_size; - - size_t allocatable_pages; - - /* Basic statistics */ - size_t total_allocated_pages; - size_t total_freed_pages; - size_t force_major_gc_count; - size_t force_incremental_marking_finish_count; - size_t total_allocated_objects; - size_t newly_created_by_borrowing_count; - size_t total_freed_objects; - - /* Sweeping statistics */ - size_t freed_slots; - size_t empty_slots; - - rb_heap_t eden_heap; - rb_heap_t tomb_heap; -} rb_size_pool_t; - -enum gc_mode { - gc_mode_none, - gc_mode_marking, - gc_mode_sweeping, - gc_mode_compacting, -}; - -typedef struct rb_objspace { - struct { - size_t limit; - size_t increase; -#if MALLOC_ALLOCATED_SIZE - size_t allocated_size; - size_t allocations; -#endif - - } malloc_params; - - struct { - unsigned int mode : 2; - unsigned int immediate_sweep : 1; - unsigned int dont_gc : 1; - unsigned int dont_incremental : 1; - unsigned int during_gc : 1; - unsigned int during_compacting : 1; - unsigned int during_reference_updating : 1; - unsigned int gc_stressful: 1; - unsigned int has_newobj_hook: 1; - unsigned int during_minor_gc : 1; - unsigned int during_global_gc : 1; - unsigned int during_incremental_marking : 1; - unsigned int measure_gc : 1; - } flags; - - rb_event_flag_t hook_events; - - rb_size_pool_t size_pools[SIZE_POOL_COUNT]; - - struct { - rb_atomic_t finalizing; - } atomic_flags; - - mark_stack_t mark_stack; - size_t marked_slots; - - struct { - struct heap_page **sorted; - size_t allocated_pages; - size_t allocatable_pages; - size_t sorted_length; - uintptr_t range[2]; - size_t freeable_pages; - - /* final */ - size_t final_slots; - VALUE deferred_final; - } heap_pages; - - struct { - int run; - unsigned int latest_gc_info; - gc_profile_record *records; - gc_profile_record *current_record; - size_t next_index; - size_t size; - -#if GC_PROFILE_MORE_DETAIL - double prepare_time; -#endif - double invoke_time; - - size_t minor_gc_count; - size_t major_gc_count; - size_t compact_count; - size_t read_barrier_faults; -#if RGENGC_PROFILE > 0 - size_t total_generated_normal_object_count; - size_t total_generated_shady_object_count; - size_t total_shade_operation_count; - size_t total_promoted_count; - size_t total_remembered_normal_object_count; - size_t total_remembered_shady_object_count; - -#if RGENGC_PROFILE >= 2 - size_t generated_normal_object_count_types[RUBY_T_MASK]; - size_t generated_shady_object_count_types[RUBY_T_MASK]; - size_t shade_operation_count_types[RUBY_T_MASK]; - size_t promoted_types[RUBY_T_MASK]; - size_t remembered_normal_object_count_types[RUBY_T_MASK]; - size_t remembered_shady_object_count_types[RUBY_T_MASK]; -#endif -#endif /* RGENGC_PROFILE */ - - /* temporary profiling space */ - double gc_sweep_start_time; - size_t total_allocated_objects_at_gc_start; - size_t heap_used_at_gc_start; - - /* basic statistics */ - size_t count; - uint64_t marking_time_ns; - struct timespec marking_start_time; - uint64_t sweeping_time_ns; - struct timespec sweeping_start_time; - - /* Weak references */ - size_t weak_references_count; - size_t retained_weak_references_count; - } profile; - - VALUE gc_stress_mode; - - struct { - VALUE parent_object; - int need_major_gc; - size_t last_major_gc; - size_t uncollectible_wb_unprotected_objects; - size_t uncollectible_wb_unprotected_objects_limit; - size_t old_objects; - size_t old_objects_limit; - -#if RGENGC_ESTIMATE_OLDMALLOC - size_t oldmalloc_increase; - size_t oldmalloc_increase_limit; -#endif - -#if RGENGC_CHECK_MODE >= 2 - struct st_table *allrefs_table; - size_t error_count; -#endif - } rgengc; - - struct { - size_t considered_count_table[T_MASK]; - size_t moved_count_table[T_MASK]; - size_t moved_up_count_table[T_MASK]; - size_t moved_down_count_table[T_MASK]; - size_t total_moved; - - /* This function will be used, if set, to sort the heap prior to compaction */ - gc_compact_compare_func compare_func; - } rcompactor; - - struct { - size_t pooled_slots; - size_t step_slots; - } rincgc; - -#if GC_DEBUG_STRESS_TO_CLASS - VALUE stress_to_class; -#endif - - rb_darray(VALUE *) weak_references; - rb_postponed_job_handle_t finalize_deferred_pjob; - -#ifdef RUBY_ASAN_ENABLED - const rb_execution_context_t *marking_machine_context_ec; -#endif - - bool belong_to_single_main_ractor; - - struct rb_objspace *current_parent_objspace; - - bool freeing_all; - - struct objspace_local_data local_data; -} rb_objspace_t; - -#ifndef HEAP_PAGE_ALIGN_LOG -/* default tiny heap size: 64KiB */ -#define HEAP_PAGE_ALIGN_LOG 16 -#endif - -#define BASE_SLOT_SIZE (sizeof(RVALUE) + RVALUE_OVERHEAD) - -#define CEILDIV(i, mod) roomof(i, mod) -enum { - HEAP_PAGE_ALIGN = (1UL << HEAP_PAGE_ALIGN_LOG), - HEAP_PAGE_ALIGN_MASK = (~(~0UL << HEAP_PAGE_ALIGN_LOG)), - HEAP_PAGE_SIZE = HEAP_PAGE_ALIGN, - HEAP_PAGE_OBJ_LIMIT = (unsigned int)((HEAP_PAGE_SIZE - sizeof(struct heap_page_header)) / BASE_SLOT_SIZE), - HEAP_PAGE_BITMAP_LIMIT = CEILDIV(CEILDIV(HEAP_PAGE_SIZE, BASE_SLOT_SIZE), BITS_BITLENGTH), - HEAP_PAGE_BITMAP_SIZE = (BITS_SIZE * HEAP_PAGE_BITMAP_LIMIT), -}; -#define HEAP_PAGE_ALIGN (1 << HEAP_PAGE_ALIGN_LOG) -#define HEAP_PAGE_SIZE HEAP_PAGE_ALIGN - -#if !defined(INCREMENTAL_MARK_STEP_ALLOCATIONS) -# define INCREMENTAL_MARK_STEP_ALLOCATIONS 500 -#endif - -#undef INIT_HEAP_PAGE_ALLOC_USE_MMAP -/* Must define either HEAP_PAGE_ALLOC_USE_MMAP or - * INIT_HEAP_PAGE_ALLOC_USE_MMAP. */ - -#ifndef HAVE_MMAP -/* We can't use mmap of course, if it is not available. */ -static const bool HEAP_PAGE_ALLOC_USE_MMAP = false; - -#elif defined(__wasm__) -/* wasmtime does not have proper support for mmap. - * See https://github.com/bytecodealliance/wasmtime/blob/main/docs/WASI-rationale.md#why-no-mmap-and-friends - */ -static const bool HEAP_PAGE_ALLOC_USE_MMAP = false; - -#elif HAVE_CONST_PAGE_SIZE -/* If we have the PAGE_SIZE and it is a constant, then we can directly use it. */ -static const bool HEAP_PAGE_ALLOC_USE_MMAP = (PAGE_SIZE <= HEAP_PAGE_SIZE); - -#elif defined(PAGE_MAX_SIZE) && (PAGE_MAX_SIZE <= HEAP_PAGE_SIZE) -/* If we can use the maximum page size. */ -static const bool HEAP_PAGE_ALLOC_USE_MMAP = true; - -#elif defined(PAGE_SIZE) -/* If the PAGE_SIZE macro can be used dynamically. */ -# define INIT_HEAP_PAGE_ALLOC_USE_MMAP (PAGE_SIZE <= HEAP_PAGE_SIZE) - -#elif defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE) -/* If we can use sysconf to determine the page size. */ -# define INIT_HEAP_PAGE_ALLOC_USE_MMAP (sysconf(_SC_PAGE_SIZE) <= HEAP_PAGE_SIZE) - -#else -/* Otherwise we can't determine the system page size, so don't use mmap. */ -static const bool HEAP_PAGE_ALLOC_USE_MMAP = false; -#endif - -#ifdef INIT_HEAP_PAGE_ALLOC_USE_MMAP -/* We can determine the system page size at runtime. */ -# define HEAP_PAGE_ALLOC_USE_MMAP (heap_page_alloc_use_mmap != false) - -static bool heap_page_alloc_use_mmap; -#endif - -#define RVALUE_AGE_BIT_COUNT 2 -#define RVALUE_AGE_BIT_MASK (((bits_t)1 << RVALUE_AGE_BIT_COUNT) - 1) - -struct heap_page { - short slot_size; - short total_slots; - short free_slots; - short final_slots; - short pinned_slots; - struct { - unsigned int before_sweep : 1; - unsigned int has_remembered_objects : 1; - unsigned int has_uncollectible_wb_unprotected_objects : 1; - unsigned int in_tomb : 1; - } flags; - - rb_size_pool_t *size_pool; - - struct heap_page *free_next; - uintptr_t start; - RVALUE *freelist; - struct ccan_list_node page_node; - - bits_t wb_unprotected_bits[HEAP_PAGE_BITMAP_LIMIT]; - bits_t local_immune_bits[HEAP_PAGE_BITMAP_LIMIT]; - - /* the following three bitmaps are cleared at the beginning of full GC */ - bits_t mark_bits[HEAP_PAGE_BITMAP_LIMIT]; - bits_t uncollectible_bits[HEAP_PAGE_BITMAP_LIMIT]; - bits_t marking_bits[HEAP_PAGE_BITMAP_LIMIT]; - - bits_t remembered_bits[HEAP_PAGE_BITMAP_LIMIT]; - - /* If set, the object is not movable */ - bits_t pinned_bits[HEAP_PAGE_BITMAP_LIMIT]; - bits_t age_bits[HEAP_PAGE_BITMAP_LIMIT * RVALUE_AGE_BIT_COUNT]; - - rb_ractor_t *ractor; - rb_objspace_t *objspace; - bool unlinked; -}; + void *objspace = rb_gc_impl_objspace_alloc(); + ruby_current_vm_ptr->objspace = objspace; -/* - * When asan is enabled, this will prohibit writing to the freelist until it is unlocked - */ -static void -asan_lock_freelist(struct heap_page *page) -{ - asan_poison_memory_region(&page->freelist, sizeof(RVALUE*)); -} + rb_gc_impl_objspace_init(objspace); -/* - * When asan is enabled, this will enable the ability to write to the freelist - */ -static void -asan_unlock_freelist(struct heap_page *page) -{ - asan_unpoison_memory_region(&page->freelist, sizeof(RVALUE*), false); + return objspace; } -#define GET_PAGE_BODY(x) ((struct heap_page_body *)((bits_t)(x) & ~(HEAP_PAGE_ALIGN_MASK))) -#define GET_PAGE_HEADER(x) (&GET_PAGE_BODY(x)->header) -#define GET_HEAP_PAGE(x) (GET_PAGE_HEADER(x)->page) - -rb_ractor_t * -atomic_load_ractor_of_value(VALUE obj) +void +rb_objspace_free(void *objspace) { - return (rb_ractor_t *)RUBY_ATOMIC_PTR_LOAD(GET_HEAP_PAGE(obj)->ractor); + rb_gc_impl_objspace_free(objspace); } -rb_objspace_t * -atomic_load_objspace_of_value(VALUE obj) +size_t +rb_gc_obj_slot_size(VALUE obj) { - return (rb_objspace_t *)RUBY_ATOMIC_PTR_LOAD(GET_HEAP_PAGE(obj)->objspace); + return rb_gc_impl_obj_slot_size(obj); } -rb_ractor_t * -get_ractor_of_value(VALUE obj) +static inline VALUE +newobj_of(rb_ractor_t *cr, VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, bool wb_protected, size_t size) { - if (rb_special_const_p(obj)) { - return NULL; + rb_objspace_t *objspace = rb_gc_get_objspace(); + rb_ractor_newobj_cache_t *cache; + bool borrowing = !!(objspace->local_data.alloc_target_ractor); + if (borrowing) { + objspace = objspace->local_data.alloc_target_ractor->local_objspace; + cache = &objspace->local_data.ractor->newobj_borrowing_cache; } - return GET_RACTOR_OF_VALUE(obj); -} - -bool -rb_contained_in_objspace_p(rb_objspace_t *objspace, VALUE obj) -{ - if (rb_special_const_p(obj)) { - return false; + else { + cache = &GET_RACTOR()->newobj_cache; } - return GET_OBJSPACE_OF_VALUE(obj) == objspace; -} - -#define NUM_IN_PAGE(p) (((bits_t)(p) & HEAP_PAGE_ALIGN_MASK) / BASE_SLOT_SIZE) -#define BITMAP_INDEX(p) (NUM_IN_PAGE(p) / BITS_BITLENGTH ) -#define BITMAP_OFFSET(p) (NUM_IN_PAGE(p) & (BITS_BITLENGTH-1)) -#define BITMAP_BIT(p) ((bits_t)1 << BITMAP_OFFSET(p)) -/* Bitmap Operations */ -#define MARKED_IN_BITMAP(bits, p) ((bits)[BITMAP_INDEX(p)] & BITMAP_BIT(p)) -#define MARK_IN_BITMAP(bits, p) ((bits)[BITMAP_INDEX(p)] = (bits)[BITMAP_INDEX(p)] | BITMAP_BIT(p)) -#define CLEAR_IN_BITMAP(bits, p) ((bits)[BITMAP_INDEX(p)] = (bits)[BITMAP_INDEX(p)] & ~BITMAP_BIT(p)) + VALUE obj = rb_gc_impl_new_obj(objspace, cache, klass, flags, v1, v2, v3, wb_protected, size); -/* getting bitmap */ -#define GET_HEAP_MARK_BITS(x) (&GET_HEAP_PAGE(x)->mark_bits[0]) -#define GET_HEAP_PINNED_BITS(x) (&GET_HEAP_PAGE(x)->pinned_bits[0]) -#define GET_HEAP_UNCOLLECTIBLE_BITS(x) (&GET_HEAP_PAGE(x)->uncollectible_bits[0]) -#define GET_HEAP_WB_UNPROTECTED_BITS(x) (&GET_HEAP_PAGE(x)->wb_unprotected_bits[0]) -#define GET_HEAP_LOCAL_IMMUNE_BITS(x) (&GET_HEAP_PAGE(x)->local_immune_bits[0]) -#define GET_HEAP_MARKING_BITS(x) (&GET_HEAP_PAGE(x)->marking_bits[0]) - -#define GC_SWEEP_PAGES_FREEABLE_PER_STEP 3 + if (UNLIKELY(ruby_vm_event_flags & RUBY_INTERNAL_EVENT_NEWOBJ)) { + unsigned int lev; + RB_VM_LOCK_ENTER_CR_LEV(GET_RACTOR(), &lev); + { + memset((char *)obj + RVALUE_SIZE, 0, rb_gc_obj_slot_size(obj) - RVALUE_SIZE); -#define RVALUE_AGE_BITMAP_INDEX(n) (NUM_IN_PAGE(n) / (BITS_BITLENGTH / RVALUE_AGE_BIT_COUNT)) -#define RVALUE_AGE_BITMAP_OFFSET(n) ((NUM_IN_PAGE(n) % (BITS_BITLENGTH / RVALUE_AGE_BIT_COUNT)) * RVALUE_AGE_BIT_COUNT) + rb_gc_event_hook(obj, RUBY_INTERNAL_EVENT_NEWOBJ); + } + RB_VM_LOCK_LEAVE_CR_LEV(GET_RACTOR(), &lev); + } -#define RVALUE_OLD_AGE 3 + return obj; +} -static int -RVALUE_AGE_GET(VALUE obj) +VALUE +rb_wb_unprotected_newobj_of(VALUE klass, VALUE flags, size_t size) { - bits_t *age_bits = GET_HEAP_PAGE(obj)->age_bits; - return (int)(age_bits[RVALUE_AGE_BITMAP_INDEX(obj)] >> RVALUE_AGE_BITMAP_OFFSET(obj)) & RVALUE_AGE_BIT_MASK; + GC_ASSERT((flags & FL_WB_PROTECTED) == 0); + return newobj_of(GET_RACTOR(), klass, flags, 0, 0, 0, FALSE, size); } -static void -RVALUE_AGE_SET(VALUE obj, int age) +VALUE +rb_wb_protected_newobj_of(rb_execution_context_t *ec, VALUE klass, VALUE flags, size_t size) { - RUBY_ASSERT(age <= RVALUE_OLD_AGE); - bits_t *age_bits = GET_HEAP_PAGE(obj)->age_bits; - // clear the bits - age_bits[RVALUE_AGE_BITMAP_INDEX(obj)] &= ~(RVALUE_AGE_BIT_MASK << (RVALUE_AGE_BITMAP_OFFSET(obj))); - // shift the correct value in - age_bits[RVALUE_AGE_BITMAP_INDEX(obj)] |= ((bits_t)age << RVALUE_AGE_BITMAP_OFFSET(obj)); - if (age == RVALUE_OLD_AGE) { - RB_FL_SET_RAW(obj, RUBY_FL_PROMOTED); - } - else { - RB_FL_UNSET_RAW(obj, RUBY_FL_PROMOTED); - } + GC_ASSERT((flags & FL_WB_PROTECTED) == 0); + return newobj_of(rb_ec_ractor_ptr(ec), klass, flags, 0, 0, 0, TRUE, size); } -/* Aliases */ -#define rb_objspace (*current_ractor_objspace()) -#define unless_objspace(objspace) \ - rb_objspace_t *objspace; \ - rb_vm_t *unless_objspace_vm = GET_VM(); \ - if (unless_objspace_vm) objspace = &rb_objspace; \ - else /* return; or objspace will be warned uninitialized */ - -#define malloc_limit objspace->malloc_params.limit -#define malloc_increase objspace->malloc_params.increase -#define malloc_allocated_size objspace->malloc_params.allocated_size -#define heap_pages_sorted objspace->heap_pages.sorted -#define heap_allocated_pages objspace->heap_pages.allocated_pages -#define heap_pages_sorted_length objspace->heap_pages.sorted_length -#define heap_pages_lomem objspace->heap_pages.range[0] -#define heap_pages_himem objspace->heap_pages.range[1] -#define heap_pages_freeable_pages objspace->heap_pages.freeable_pages -#define heap_pages_final_slots objspace->heap_pages.final_slots -#define heap_pages_deferred_final objspace->heap_pages.deferred_final -#define size_pools objspace->size_pools -#define during_gc objspace->flags.during_gc -#define finalizing objspace->atomic_flags.finalizing -#define finalizer_table objspace->local_data.finalizer_table -#define ruby_gc_stressful objspace->flags.gc_stressful -#define ruby_gc_stress_mode objspace->gc_stress_mode -#if GC_DEBUG_STRESS_TO_CLASS -#define stress_to_class objspace->stress_to_class -#define set_stress_to_class(c) (stress_to_class = (c)) -#else -#define stress_to_class (objspace, 0) -#define set_stress_to_class(c) (objspace, (c)) -#endif - -#if 0 -#define dont_gc_on() (fprintf(stderr, "dont_gc_on@%s:%d\n", __FILE__, __LINE__), objspace->flags.dont_gc = 1) -#define dont_gc_off() (fprintf(stderr, "dont_gc_off@%s:%d\n", __FILE__, __LINE__), objspace->flags.dont_gc = 0) -#define dont_gc_set(b) (fprintf(stderr, "dont_gc_set(%d)@%s:%d\n", __FILE__, __LINE__), (int)b), objspace->flags.dont_gc = (b)) -#define dont_gc_val() (objspace->flags.dont_gc) -#else -#define dont_gc_on() (objspace->flags.dont_gc = 1) -#define dont_gc_off() (objspace->flags.dont_gc = 0) -#define dont_gc_set(b) (((int)b), objspace->flags.dont_gc = (b)) -#define dont_gc_val() (objspace->flags.dont_gc) -#endif +#define UNEXPECTED_NODE(func) \ + rb_bug(#func"(): GC does not handle T_NODE 0x%x(%p) 0x%"PRIxVALUE, \ + BUILTIN_TYPE(obj), (void*)(obj), RBASIC(obj)->flags) -static inline enum gc_mode -gc_mode_verify(enum gc_mode mode) +static inline void +rb_data_object_check(VALUE klass) { -#if RGENGC_CHECK_MODE > 0 - switch (mode) { - case gc_mode_none: - case gc_mode_marking: - case gc_mode_sweeping: - case gc_mode_compacting: - break; - default: - rb_bug("gc_mode_verify: unreachable (%d)", (int)mode); + if (klass != rb_cObject && (rb_get_alloc_func(klass) == rb_class_allocate_instance)) { + rb_undef_alloc_func(klass); + rb_warn("undefining the allocator of T_DATA class %"PRIsVALUE, klass); } -#endif - return mode; } -static inline bool -has_sweeping_pages(rb_objspace_t *objspace) +VALUE +rb_data_object_wrap(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree) { - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - if (SIZE_POOL_EDEN_HEAP(&size_pools[i])->sweeping_page) { - return TRUE; - } - } - return FALSE; + RUBY_ASSERT_ALWAYS(dfree != (RUBY_DATA_FUNC)1); + if (klass) rb_data_object_check(klass); + return newobj_of(GET_RACTOR(), klass, T_DATA, (VALUE)dmark, (VALUE)dfree, (VALUE)datap, !dmark, sizeof(struct RTypedData)); } -static inline size_t -heap_eden_total_pages(rb_objspace_t *objspace) +VALUE +rb_data_object_zalloc(VALUE klass, size_t size, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree) { - size_t count = 0; - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - count += SIZE_POOL_EDEN_HEAP(&size_pools[i])->total_pages; - } - return count; + VALUE obj = rb_data_object_wrap(klass, 0, dmark, dfree); + DATA_PTR(obj) = xcalloc(1, size); + return obj; } -static inline size_t -heap_eden_total_slots(rb_objspace_t *objspace) +static VALUE +typed_data_alloc(VALUE klass, VALUE typed_flag, void *datap, const rb_data_type_t *type, size_t size) { - size_t count = 0; - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - count += SIZE_POOL_EDEN_HEAP(&size_pools[i])->total_slots; - } - return count; + RBIMPL_NONNULL_ARG(type); + if (klass) rb_data_object_check(klass); + bool wb_protected = (type->flags & RUBY_FL_WB_PROTECTED) || !type->function.dmark; + return newobj_of(GET_RACTOR(), klass, T_DATA, (VALUE)type, 1 | typed_flag, (VALUE)datap, wb_protected, size); } -static inline size_t -heap_tomb_total_pages(rb_objspace_t *objspace) +VALUE +rb_data_typed_object_wrap(VALUE klass, void *datap, const rb_data_type_t *type) { - size_t count = 0; - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - count += SIZE_POOL_TOMB_HEAP(&size_pools[i])->total_pages; + if (UNLIKELY(type->flags & RUBY_TYPED_EMBEDDABLE)) { + rb_raise(rb_eTypeError, "Cannot wrap an embeddable TypedData"); } - return count; -} -static inline size_t -heap_allocatable_pages(rb_objspace_t *objspace) -{ - size_t count = 0; - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - count += size_pools[i].allocatable_pages; - } - return count; + return typed_data_alloc(klass, 0, datap, type, sizeof(struct RTypedData)); } -static inline size_t -heap_allocatable_slots(rb_objspace_t *objspace) +VALUE +rb_data_typed_object_zalloc(VALUE klass, size_t size, const rb_data_type_t *type) { - size_t count = 0; - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - int slot_size_multiple = size_pool->slot_size / BASE_SLOT_SIZE; - count += size_pool->allocatable_pages * HEAP_PAGE_OBJ_LIMIT / slot_size_multiple; + if (type->flags & RUBY_TYPED_EMBEDDABLE) { + if (!(type->flags & RUBY_TYPED_FREE_IMMEDIATELY)) { + rb_raise(rb_eTypeError, "Embeddable TypedData must be freed immediately"); + } + + size_t embed_size = offsetof(struct RTypedData, data) + size; + if (rb_gc_size_allocatable_p(embed_size)) { + VALUE obj = typed_data_alloc(klass, TYPED_DATA_EMBEDDED, 0, type, embed_size); + memset((char *)obj + offsetof(struct RTypedData, data), 0, size); + return obj; + } } - return count; + + VALUE obj = typed_data_alloc(klass, 0, NULL, type, sizeof(struct RTypedData)); + DATA_PTR(obj) = xcalloc(1, size); + return obj; } -static inline size_t -total_allocated_pages(rb_objspace_t *objspace) +static size_t +rb_objspace_data_type_memsize(VALUE obj) { - size_t count = 0; - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - count += size_pool->total_allocated_pages; + size_t size = 0; + if (RTYPEDDATA_P(obj)) { + const rb_data_type_t *type = RTYPEDDATA_TYPE(obj); + const void *ptr = RTYPEDDATA_GET_DATA(obj); + + if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !RTYPEDDATA_EMBEDDED_P(obj)) { +#ifdef HAVE_MALLOC_USABLE_SIZE + size += malloc_usable_size((void *)ptr); +#endif + } + + if (ptr && type->function.dsize) { + size += type->function.dsize(ptr); + } } - return count; + + return size; } -static inline size_t -total_freed_pages(rb_objspace_t *objspace) +const char * +rb_objspace_data_type_name(VALUE obj) { - size_t count = 0; - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - count += size_pool->total_freed_pages; + if (RTYPEDDATA_P(obj)) { + return RTYPEDDATA_TYPE(obj)->wrap_struct_name; + } + else { + return 0; } - return count; } -static inline size_t -total_allocated_objects(rb_objspace_t *objspace) +static enum rb_id_table_iterator_result +cvar_table_free_i(VALUE value, void *ctx) { - rb_ractor_t *r = objspace->local_data.ractor; - if (!during_gc) rb_borrowing_sync_lock(r); - size_t count = 0; - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - size_pool->total_allocated_objects += size_pool->newly_created_by_borrowing_count; - size_pool->newly_created_by_borrowing_count = 0; - count += size_pool->total_allocated_objects; - } - if (!during_gc) rb_borrowing_sync_unlock(r); - return count; + xfree((void *)value); + return ID_TABLE_CONTINUE; } -static inline size_t -total_freed_objects(rb_objspace_t *objspace) +static inline void +make_io_zombie(void *objspace, VALUE obj) { - size_t count = 0; - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - count += size_pool->total_freed_objects; - } - return count; + rb_io_t *fptr = RFILE(obj)->fptr; + rb_gc_impl_make_zombie(objspace, obj, rb_io_fptr_finalize_internal, fptr); } -#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) -#define is_full_marking(objspace) ((objspace)->flags.during_minor_gc == FALSE) -#define is_incremental_marking(objspace) ((objspace)->flags.during_incremental_marking != FALSE) -#define will_be_incremental_marking(objspace) ((objspace)->rgengc.need_major_gc != GPR_FLAG_NONE) -#define GC_INCREMENTAL_SWEEP_SLOT_COUNT 2048 -#define GC_INCREMENTAL_SWEEP_POOL_SLOT_COUNT 1024 -#define is_lazy_sweeping(objspace) (GC_ENABLE_LAZY_SWEEP && has_sweeping_pages(objspace)) -#define using_local_limits(objspace) (objspace != ruby_single_main_objspace && !objspace->flags.during_global_gc) - -#if SIZEOF_LONG == SIZEOF_VOIDP -# define obj_id_to_ref(objid) ((objid) ^ FIXNUM_FLAG) /* unset FIXNUM_FLAG */ -#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP -# define obj_id_to_ref(objid) (FIXNUM_P(objid) ? \ - ((objid) ^ FIXNUM_FLAG) : (NUM2PTR(objid) << 1)) -#else -# error not supported -#endif +static bool +rb_data_free(void *objspace, VALUE obj) +{ + void *data = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj); + if (data) { + int free_immediately = false; + void (*dfree)(void *); -#define RANY(o) ((RVALUE*)(o)) + if (RTYPEDDATA_P(obj)) { + free_immediately = (RTYPEDDATA(obj)->type->flags & RUBY_TYPED_FREE_IMMEDIATELY) != 0; + dfree = RTYPEDDATA(obj)->type->function.dfree; + } + else { + dfree = RDATA(obj)->dfree; + } -struct RZombie { - struct RBasic basic; - VALUE next; - void (*dfree)(void *); - void *data; -}; - -#define RZOMBIE(o) ((struct RZombie *)(o)) - -#define nomem_error GET_VM()->special_exceptions[ruby_error_nomemory] - -#if RUBY_MARK_FREE_DEBUG -int ruby_gc_debug_indent = 0; -#endif -VALUE rb_mGC; -int ruby_disable_gc = 0; -int ruby_enable_autocompact = 0; -#if RGENGC_CHECK_MODE -gc_compact_compare_func ruby_autocompact_compare_func; -#endif - -void rb_vm_update_references(void *ptr); - -void rb_gcdebug_print_obj_condition(VALUE obj); - -NORETURN(static void *gc_vraise(void *ptr)); -NORETURN(static void gc_raise(VALUE exc, const char *fmt, ...)); -NORETURN(static void negative_size_allocation_error(const char *)); - -static void init_mark_stack(mark_stack_t *stack); -static int garbage_collect_global(rb_objspace_t *, unsigned int reason, bool need_finalize_deferred); -static int garbage_collect_local(rb_objspace_t *, unsigned int reason, bool need_finalize_deferred); -static int garbage_collect(rb_objspace_t *, unsigned int reason, bool need_finalize_deferred); - -static int gc_start(rb_objspace_t *objspace, unsigned int reason); -static void gc_rest(rb_objspace_t *objspace); - -enum gc_enter_event { - gc_enter_event_start, - gc_enter_event_continue, - gc_enter_event_rest, - gc_enter_event_finalizer, - gc_enter_event_rb_memerror, -}; - -static inline void gc_enter(rb_objspace_t *objspace, enum gc_enter_event event); -static inline void gc_global_enter(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_lev); -static inline void gc_exit(rb_objspace_t *objspace, enum gc_enter_event event); -static inline void gc_global_exit(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_lev); -static void gc_marking_enter(rb_objspace_t *objspace); -static void gc_marking_exit(rb_objspace_t *objspace); -static void gc_sweeping_enter(rb_objspace_t *objspace); -static void gc_sweeping_exit(rb_objspace_t *objspace); -static bool gc_marks_continue(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap); - -static void gc_sweep(rb_objspace_t *objspace); -static void gc_sweep_finish_size_pool(rb_objspace_t *objspace, rb_size_pool_t *size_pool); -static void gc_sweep_continue(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap); - -static inline void gc_mark(rb_objspace_t *objspace, VALUE ptr); -static inline void gc_pin(rb_objspace_t *objspace, VALUE ptr); -static inline void gc_mark_and_pin(rb_objspace_t *objspace, VALUE ptr); -NO_SANITIZE("memory", static void gc_mark_maybe(rb_objspace_t *objspace, VALUE ptr)); -NO_SANITIZE("memory", static void gc_stack_location_mark_maybe(rb_objspace_t *objspace, VALUE ptr)); - -static int gc_mark_stacked_objects_incremental(rb_objspace_t *, size_t count); -NO_SANITIZE("memory", static inline int is_pointer_to_heap(rb_objspace_t *objspace, const void *ptr)); - -static size_t obj_memsize_of(VALUE obj, int use_all_types); -static void gc_verify_internal_consistency(rb_objspace_t *objspace); - -static VALUE gc_disable_no_rest(rb_objspace_t *); - -static double getrusage_time(void); -static inline void gc_prof_setup_new_record(rb_objspace_t *objspace, unsigned int reason); -static inline void gc_prof_timer_start(rb_objspace_t *); -static inline void gc_prof_timer_stop(rb_objspace_t *); -static inline void gc_prof_mark_timer_start(rb_objspace_t *); -static inline void gc_prof_mark_timer_stop(rb_objspace_t *); -static inline void gc_prof_sweep_timer_start(rb_objspace_t *); -static inline void gc_prof_sweep_timer_stop(rb_objspace_t *); -static inline void gc_prof_set_malloc_info(rb_objspace_t *); -static inline void gc_prof_set_heap_info(rb_objspace_t *); - -#define TYPED_UPDATE_IF_MOVED(_objspace, _type, _thing) do { \ - if (gc_object_moved_p((_objspace), (VALUE)(_thing))) { \ - *(_type *)&(_thing) = (_type)RMOVED(_thing)->destination; \ - } \ -} while (0) - -#define UPDATE_IF_MOVED(_objspace, _thing) TYPED_UPDATE_IF_MOVED(_objspace, VALUE, _thing) - -#define gc_prof_record(objspace) (objspace)->profile.current_record -#define gc_prof_enabled(objspace) ((objspace)->profile.run && (objspace)->profile.current_record) - -#ifdef HAVE_VA_ARGS_MACRO -# define gc_report(level, objspace, ...) \ - if (!RGENGC_DEBUG_ENABLED(level)) {} else gc_report_body(level, objspace, __VA_ARGS__) -#else -# define gc_report if (!RGENGC_DEBUG_ENABLED(0)) {} else gc_report_body -#endif -PRINTF_ARGS(static void gc_report_body(int level, rb_objspace_t *objspace, const char *fmt, ...), 3, 4); -static const char *obj_info(VALUE obj); -static const char *obj_info_basic(VALUE obj); -static const char *obj_type_name(VALUE obj); - -static void gc_finalize_deferred(void *dmy); - -#if USE_TICK_T -/* the following code is only for internal tuning. */ - -/* Source code to use RDTSC is quoted and modified from - * https://www.mcs.anl.gov/~kazutomo/rdtsc.html - * written by Kazutomo Yoshii - */ - -#if defined(__GNUC__) && defined(__i386__) -typedef unsigned long long tick_t; -#define PRItick "llu" -static inline tick_t -tick(void) -{ - unsigned long long int x; - __asm__ __volatile__ ("rdtsc" : "=A" (x)); - return x; -} - -#elif defined(__GNUC__) && defined(__x86_64__) -typedef unsigned long long tick_t; -#define PRItick "llu" - -static __inline__ tick_t -tick(void) -{ - unsigned long hi, lo; - __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); - return ((unsigned long long)lo)|( ((unsigned long long)hi)<<32); -} - -#elif defined(__powerpc64__) && (GCC_VERSION_SINCE(4,8,0) || defined(__clang__)) -typedef unsigned long long tick_t; -#define PRItick "llu" - -static __inline__ tick_t -tick(void) -{ - unsigned long long val = __builtin_ppc_get_timebase(); - return val; -} - -/* Implementation for macOS PPC by @nobu - * See: https://github.com/ruby/ruby/pull/5975#discussion_r890045558 - */ -#elif defined(__POWERPC__) && defined(__APPLE__) -typedef unsigned long long tick_t; -#define PRItick "llu" - -static __inline__ tick_t -tick(void) -{ - unsigned long int upper, lower, tmp; - # define mftbu(r) __asm__ volatile("mftbu %0" : "=r"(r)) - # define mftb(r) __asm__ volatile("mftb %0" : "=r"(r)) - do { - mftbu(upper); - mftb(lower); - mftbu(tmp); - } while (tmp != upper); - return ((tick_t)upper << 32) | lower; -} - -#elif defined(__aarch64__) && defined(__GNUC__) -typedef unsigned long tick_t; -#define PRItick "lu" - -static __inline__ tick_t -tick(void) -{ - unsigned long val; - __asm__ __volatile__ ("mrs %0, cntvct_el0" : "=r" (val)); - return val; -} - - -#elif defined(_WIN32) && defined(_MSC_VER) -#include -typedef unsigned __int64 tick_t; -#define PRItick "llu" - -static inline tick_t -tick(void) -{ - return __rdtsc(); -} - -#else /* use clock */ -typedef clock_t tick_t; -#define PRItick "llu" - -static inline tick_t -tick(void) -{ - return clock(); -} -#endif /* TSC */ -#else /* USE_TICK_T */ -#define MEASURE_LINE(expr) expr -#endif /* USE_TICK_T */ - -#define asan_unpoisoning_object(obj) \ - for (void *poisoned = asan_unpoison_object_temporary(obj), \ - *unpoisoning = &poisoned; /* flag to loop just once */ \ - unpoisoning; \ - unpoisoning = asan_poison_object_restore(obj, poisoned)) - -#define FL_CHECK2(name, x, pred) \ - ((RGENGC_CHECK_MODE && SPECIAL_CONST_P(x)) ? \ - (rb_bug(name": SPECIAL_CONST (%p)", (void *)(x)), 0) : (pred)) -#define FL_TEST2(x,f) FL_CHECK2("FL_TEST2", x, FL_TEST_RAW((x),(f)) != 0) -#define FL_SET2(x,f) FL_CHECK2("FL_SET2", x, RBASIC(x)->flags |= (f)) -#define FL_UNSET2(x,f) FL_CHECK2("FL_UNSET2", x, RBASIC(x)->flags &= ~(f)) - -static inline VALUE check_rvalue_consistency(const VALUE obj); - -#define RVALUE_MARKED_BITMAP(obj) MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(obj), (obj)) -#define RVALUE_WB_UNPROTECTED_BITMAP(obj) MARKED_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(obj), (obj)) -#define RVALUE_LOCAL_IMMUNE_BITMAP(obj) MARKED_IN_BITMAP(GET_HEAP_LOCAL_IMMUNE_BITS(obj), (obj)) -#define RVALUE_MARKING_BITMAP(obj) MARKED_IN_BITMAP(GET_HEAP_MARKING_BITS(obj), (obj)) -#define RVALUE_UNCOLLECTIBLE_BITMAP(obj) MARKED_IN_BITMAP(GET_HEAP_UNCOLLECTIBLE_BITS(obj), (obj)) -#define RVALUE_PINNED_BITMAP(obj) MARKED_IN_BITMAP(GET_HEAP_PINNED_BITS(obj), (obj)) - -static inline int -RVALUE_MARKED(VALUE obj) -{ - check_rvalue_consistency(obj); - return RVALUE_MARKED_BITMAP(obj) != 0; -} - -static inline int -RVALUE_PINNED(VALUE obj) -{ - check_rvalue_consistency(obj); - return RVALUE_PINNED_BITMAP(obj) != 0; -} - -static inline int -RVALUE_WB_UNPROTECTED(VALUE obj) -{ - check_rvalue_consistency(obj); - return RVALUE_WB_UNPROTECTED_BITMAP(obj) != 0; -} - -static inline int -RVALUE_LOCAL_IMMUNE(VALUE obj) -{ - check_rvalue_consistency(obj); - return RVALUE_LOCAL_IMMUNE_BITMAP(obj) != 0; -} - -static inline int -RVALUE_MARKING(VALUE obj) -{ - check_rvalue_consistency(obj); - return RVALUE_MARKING_BITMAP(obj) != 0; -} - -static inline int -RVALUE_REMEMBERED(VALUE obj) -{ - check_rvalue_consistency(obj); - return MARKED_IN_BITMAP(GET_HEAP_PAGE(obj)->remembered_bits, obj) != 0; -} - -static inline int -RVALUE_UNCOLLECTIBLE(VALUE obj) -{ - check_rvalue_consistency(obj); - return RVALUE_UNCOLLECTIBLE_BITMAP(obj) != 0; -} - -#define RVALUE_PAGE_MARKED(page, obj) MARKED_IN_BITMAP((page)->mark_bits, (obj)) -#define RVALUE_PAGE_WB_UNPROTECTED(page, obj) MARKED_IN_BITMAP((page)->wb_unprotected_bits, (obj)) -#define RVALUE_PAGE_UNCOLLECTIBLE(page, obj) MARKED_IN_BITMAP((page)->uncollectible_bits, (obj)) -#define RVALUE_PAGE_MARKING(page, obj) MARKED_IN_BITMAP((page)->marking_bits, (obj)) -#define RVALUE_PAGE_LOCAL_IMMUNE(page, obj) MARKED_IN_BITMAP((page)->local_immune_bits, (obj)) - -static int rgengc_remember(rb_objspace_t *objspace, VALUE obj); -static void rgengc_mark_and_rememberset_clear(rb_objspace_t *objspace, rb_heap_t *heap); -static void rgengc_rememberset_mark(rb_objspace_t *objspace, rb_heap_t *heap); - -static int -check_rvalue_consistency_force(const VALUE obj, int terminate) -{ - int err = 0; - - WITH_OBJSPACE_OF_VALUE_ENTER(obj, objspace); - { - HEAP_LOCK_ENTER(objspace); - { - if (SPECIAL_CONST_P(obj)) { - fprintf(stderr, "check_rvalue_consistency: %p is a special const.\n", (void *)obj); - err++; - } - else if (!is_pointer_to_heap(objspace, (void *)obj)) { - /* check if it is in tomb_pages */ - struct heap_page *page = NULL; - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - ccan_list_for_each(&size_pool->tomb_heap.pages, page, page_node) { - if (page->start <= (uintptr_t)obj && - (uintptr_t)obj < (page->start + (page->total_slots * size_pool->slot_size))) { - fprintf(stderr, "check_rvalue_consistency: %p is in a tomb_heap (%p).\n", - (void *)obj, (void *)page); - err++; - goto skip; - } - } - } - bp(); - fprintf(stderr, "check_rvalue_consistency: %p is not a Ruby object.\n", (void *)obj); - err++; -skip: - ; - } - else { - const int wb_unprotected_bit = RVALUE_WB_UNPROTECTED_BITMAP(obj) != 0; - const int uncollectible_bit = RVALUE_UNCOLLECTIBLE_BITMAP(obj) != 0; - const int mark_bit = RVALUE_MARKED_BITMAP(obj) != 0; - const int marking_bit = RVALUE_MARKING_BITMAP(obj) != 0; - const int remembered_bit = MARKED_IN_BITMAP(GET_HEAP_PAGE(obj)->remembered_bits, obj) != 0; - const int age = RVALUE_AGE_GET((VALUE)obj); - - if (GET_HEAP_PAGE(obj)->flags.in_tomb) { - fprintf(stderr, "check_rvalue_consistency: %s is in tomb page.\n", obj_info(obj)); - err++; - } - if (BUILTIN_TYPE(obj) == T_NONE) { - fprintf(stderr, "check_rvalue_consistency: %s is T_NONE.\n", obj_info(obj)); - err++; - } - if (BUILTIN_TYPE(obj) == T_ZOMBIE) { - fprintf(stderr, "check_rvalue_consistency: %s is T_ZOMBIE.\n", obj_info(obj)); - err++; - } - - obj_memsize_of((VALUE)obj, FALSE); - - /* check generation - * - * OLD == age == 3 && old-bitmap && mark-bit (except incremental marking) - */ - if (age > 0 && wb_unprotected_bit) { - fprintf(stderr, "check_rvalue_consistency: %s is not WB protected, but age is %d > 0.\n", obj_info(obj), age); - err++; - } - - if (!is_marking(objspace) && uncollectible_bit && !mark_bit) { - fprintf(stderr, "check_rvalue_consistency: %s is uncollectible, but is not marked while !gc.\n", obj_info(obj)); - err++; - } - - if (!is_full_marking(objspace)) { - if (uncollectible_bit && age != RVALUE_OLD_AGE && !wb_unprotected_bit) { - fprintf(stderr, "check_rvalue_consistency: %s is uncollectible, but not old (age: %d) and not WB unprotected.\n", - obj_info(obj), age); - err++; - } - if (remembered_bit && age != RVALUE_OLD_AGE) { - fprintf(stderr, "check_rvalue_consistency: %s is remembered, but not old (age: %d).\n", - obj_info(obj), age); - err++; - } - } - - /* - * check coloring - * - * marking:false marking:true - * marked:false white *invalid* - * marked:true black grey - */ - if (is_incremental_marking(objspace) && marking_bit) { - if (!is_marking(objspace) && !mark_bit) { - fprintf(stderr, "check_rvalue_consistency: %s is marking, but not marked.\n", obj_info(obj)); - err++; - } - } - } - } - HEAP_LOCK_LEAVE(objspace); - } - WITH_OBJSPACE_OF_VALUE_LEAVE(objspace); - - if (err > 0 && terminate) { - rb_bug("check_rvalue_consistency_force: there is %d errors.", err); - } - return err; -} - -#if RGENGC_CHECK_MODE == 0 -static inline VALUE -check_rvalue_consistency(const VALUE obj) -{ - return obj; -} -#else -static VALUE -check_rvalue_consistency(const VALUE obj) -{ - check_rvalue_consistency_force(obj, TRUE); - return obj; -} -#endif - -static inline int -gc_object_moved_p(rb_objspace_t * objspace, VALUE obj) -{ - if (RB_SPECIAL_CONST_P(obj)) { - return FALSE; - } - else { - void *poisoned = asan_unpoison_object_temporary(obj); - - int ret = BUILTIN_TYPE(obj) == T_MOVED; - /* Re-poison slot if it's not the one we want */ - if (poisoned) { - GC_ASSERT(BUILTIN_TYPE(obj) == T_NONE); - asan_poison_object(obj); - } - return ret; - } -} - -static inline int -RVALUE_OLD_P(VALUE obj) -{ - GC_ASSERT(!RB_SPECIAL_CONST_P(obj)); - check_rvalue_consistency(obj); - // Because this will only ever be called on GC controlled objects, - // we can use the faster _RAW function here - return RB_OBJ_PROMOTED_RAW(obj); -} - -static inline void -RVALUE_PAGE_OLD_UNCOLLECTIBLE_SET(rb_objspace_t *objspace, struct heap_page *page, VALUE obj) -{ - MARK_IN_BITMAP(&page->uncollectible_bits[0], obj); - objspace->rgengc.old_objects++; - -#if RGENGC_PROFILE >= 2 - objspace->profile.total_promoted_count++; - objspace->profile.promoted_types[BUILTIN_TYPE(obj)]++; -#endif -} - -static inline void -RVALUE_OLD_UNCOLLECTIBLE_SET(rb_objspace_t *objspace, VALUE obj) -{ - RB_DEBUG_COUNTER_INC(obj_promote); - RVALUE_PAGE_OLD_UNCOLLECTIBLE_SET(objspace, GET_HEAP_PAGE(obj), obj); -} - -/* set age to age+1 */ -static inline void -RVALUE_AGE_INC(rb_objspace_t *objspace, VALUE obj) -{ - int age = RVALUE_AGE_GET((VALUE)obj); - - if (RGENGC_CHECK_MODE && age == RVALUE_OLD_AGE) { - rb_bug("RVALUE_AGE_INC: can not increment age of OLD object %s.", obj_info(obj)); - } - - age++; - RVALUE_AGE_SET(obj, age); - - if (age == RVALUE_OLD_AGE) { - RVALUE_OLD_UNCOLLECTIBLE_SET(objspace, obj); - } - - check_rvalue_consistency(obj); -} - -static inline void -RVALUE_AGE_SET_CANDIDATE(rb_objspace_t *objspace, VALUE obj) -{ - check_rvalue_consistency(obj); - GC_ASSERT(!RVALUE_OLD_P(obj)); - RVALUE_AGE_SET(obj, RVALUE_OLD_AGE - 1); - check_rvalue_consistency(obj); -} - -static inline void -RVALUE_AGE_RESET(VALUE obj) -{ - RVALUE_AGE_SET(obj, 0); -} - -static inline void -RVALUE_DEMOTE(rb_objspace_t *objspace, VALUE obj) -{ - check_rvalue_consistency(obj); - GC_ASSERT(RVALUE_OLD_P(obj)); - - if (!is_incremental_marking(objspace) && RVALUE_REMEMBERED(obj)) { - CLEAR_IN_BITMAP(GET_HEAP_PAGE(obj)->remembered_bits, obj); - } - - CLEAR_IN_BITMAP(GET_HEAP_UNCOLLECTIBLE_BITS(obj), obj); - RVALUE_AGE_RESET(obj); - - if (RVALUE_MARKED(obj)) { - objspace->rgengc.old_objects--; - } - - check_rvalue_consistency(obj); -} - -static inline int -RVALUE_BLACK_P(VALUE obj) -{ - return RVALUE_MARKED(obj) && !RVALUE_MARKING(obj); -} - -#if 0 -static inline int -RVALUE_GREY_P(VALUE obj) -{ - return RVALUE_MARKED(obj) && RVALUE_MARKING(obj); -} -#endif - -static inline int -RVALUE_WHITE_P(VALUE obj) -{ - return RVALUE_MARKED(obj) == FALSE; -} - -/* - --------------------------- ObjectSpace ----------------------------- -*/ - -static inline void * -calloc1(size_t n) -{ - return calloc(1, n); -} - -static VALUE initial_stress = Qfalse; - -void -rb_gc_initial_stress_set(VALUE flag) -{ - initial_stress = flag; -} - -static void *rb_gc_impl_objspace_alloc(void); - -#if USE_SHARED_GC -# include "dln.h" - -typedef struct gc_function_map { - void *(*objspace_alloc)(void); -} rb_gc_function_map_t; - -static rb_gc_function_map_t rb_gc_functions; - -# define RUBY_GC_LIBRARY_ARG "--gc-library=" - -void -ruby_load_external_gc_from_argv(int argc, char **argv) -{ - char *gc_so_path = NULL; - - for (int i = 0; i < argc; i++) { - if (strncmp(argv[i], RUBY_GC_LIBRARY_ARG, sizeof(RUBY_GC_LIBRARY_ARG) - 1) == 0) { - gc_so_path = argv[i] + sizeof(RUBY_GC_LIBRARY_ARG) - 1; - } - } - - 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_load_external_gc_from_argv: 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_load_external_gc_from_argv: " #name " func not exported by library %s", gc_so_path); \ - } \ - } \ - else { \ - rb_gc_functions.name = rb_gc_impl_##name; \ - } \ -} while (0) - - load_external_gc_func(objspace_alloc); - -# undef load_external_gc_func -} - -# define rb_gc_impl_objspace_alloc rb_gc_functions.objspace_alloc -#endif - -rb_objspace_t * -rb_objspace_alloc(void) -{ - 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, bool global_pages_locked); - -void -rb_objspace_free(rb_objspace_t *objspace) -{ - if (is_lazy_sweeping(objspace)) - rb_bug("lazy sweeping underway when freeing object space"); - - free(objspace->profile.records); - objspace->profile.records = NULL; - - bool pages_to_free = false; - if (heap_pages_sorted) { - if (objspace->local_data.objspace_closed) { - free(heap_pages_sorted); - } - else { - pages_to_free = true; - } - } - - if (pages_to_free) { - size_t i; - size_t total_heap_pages = heap_allocated_pages; - for (i = 0; i < total_heap_pages; ++i) { - heap_page_free(objspace, heap_pages_sorted[i], false); - } - free(heap_pages_sorted); - heap_allocated_pages = 0; - heap_pages_sorted_length = 0; - heap_pages_lomem = 0; - heap_pages_himem = 0; - - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - SIZE_POOL_EDEN_HEAP(size_pool)->total_pages = 0; - SIZE_POOL_EDEN_HEAP(size_pool)->total_slots = 0; - } - } - free_local_data_contents(&objspace->local_data); - - free_stack_chunks(&objspace->mark_stack); - mark_stack_free_cache(&objspace->mark_stack); - - rb_darray_free(objspace->weak_references); - - if (objspace == GET_VM()->objspace) rb_global_space_free(GET_VM()->global_space); - - free(objspace); -} - -static struct heap_page ** -page_list_expand_sorted_to_size(struct heap_page **sorted_list, size_t sorted_list_length, size_t target_size) -{ - struct heap_page **sorted; - if (sorted_list_length > 0) { - sorted = (struct heap_page **)realloc(sorted_list, target_size); - } - else { - sorted = (struct heap_page **)malloc(target_size); - } - - if (sorted == 0) { - rb_memerror(); - } - return sorted; -} - -static void -heap_pages_expand_sorted_to(rb_objspace_t *objspace, size_t next_length) -{ - size_t length_diff = next_length - heap_pages_sorted_length; - size_t size = size_mul_or_raise(next_length, sizeof(struct heap_page *), rb_eRuntimeError); - - gc_report(3, objspace, "heap_pages_expand_sorted: next_length: %"PRIdSIZE", size: %"PRIdSIZE"\n", - next_length, size); - - heap_pages_sorted = page_list_expand_sorted_to_size(heap_pages_sorted, heap_pages_sorted_length, size); - heap_pages_sorted_length = next_length; - - rb_global_space_t *global_space = &rb_global_space; - rb_native_mutex_lock(&global_space->all_pages.global_pages_lock); - - size_t next_length_global = length_diff + all_pages_sorted_length_global; - size_t global_size = size_mul_or_raise(next_length_global, sizeof(struct heap_page *), rb_eRuntimeError); - all_pages_sorted_global = page_list_expand_sorted_to_size(all_pages_sorted_global, all_pages_sorted_length_global, global_size); - all_pages_sorted_length_global = next_length_global; - - rb_native_mutex_unlock(&global_space->all_pages.global_pages_lock); - -} - -static void -heap_pages_expand_sorted(rb_objspace_t *objspace) -{ - /* usually heap_allocatable_pages + heap_eden->total_pages == heap_pages_sorted_length - * because heap_allocatable_pages contains heap_tomb->total_pages (recycle heap_tomb pages). - * however, if there are pages which do not have empty slots, then try to create new pages - * so that the additional allocatable_pages counts (heap_tomb->total_pages) are added. - */ - size_t next_length = heap_allocatable_pages(objspace); - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - next_length += SIZE_POOL_EDEN_HEAP(size_pool)->total_pages; - next_length += SIZE_POOL_TOMB_HEAP(size_pool)->total_pages; - } - - if (next_length > heap_pages_sorted_length) { - heap_pages_expand_sorted_to(objspace, next_length); - } - - GC_ASSERT(heap_allocatable_pages(objspace) + heap_eden_total_pages(objspace) <= heap_pages_sorted_length); - GC_ASSERT(heap_allocated_pages <= heap_pages_sorted_length); -} - -static void -size_pool_allocatable_pages_set(rb_objspace_t *objspace, rb_size_pool_t *size_pool, size_t s) -{ - size_pool->allocatable_pages = s; - heap_pages_expand_sorted(objspace); -} - -static inline void -heap_page_add_freeobj(rb_objspace_t *objspace, struct heap_page *page, VALUE obj) -{ - RVALUE *p = (RVALUE *)obj; - - asan_unpoison_object(obj, false); - - asan_unlock_freelist(page); - - p->as.free.flags = 0; - p->as.free.next = page->freelist; - page->freelist = p; - asan_lock_freelist(page); - - RVALUE_AGE_RESET(obj); - - if (RGENGC_CHECK_MODE && - /* obj should belong to page */ - !(page->start <= (uintptr_t)obj && - (uintptr_t)obj < ((uintptr_t)page->start + (page->total_slots * page->slot_size)) && - obj % BASE_SLOT_SIZE == 0)) { - rb_bug("heap_page_add_freeobj: %p is not rvalue.", (void *)p); - } - - asan_poison_object(obj); - gc_report(3, objspace, "heap_page_add_freeobj: add %p to freelist\n", (void *)obj); -} - -static inline void -heap_add_freepage(rb_heap_t *heap, struct heap_page *page) -{ - asan_unlock_freelist(page); - GC_ASSERT(page->free_slots != 0); - GC_ASSERT(page->freelist != NULL); - - page->free_next = heap->free_pages; - heap->free_pages = page; - - RUBY_DEBUG_LOG("page:%p freelist:%p", (void *)page, (void *)page->freelist); - - asan_lock_freelist(page); -} - -static inline void -heap_add_poolpage(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *page) -{ - asan_unlock_freelist(page); - GC_ASSERT(page->free_slots != 0); - GC_ASSERT(page->freelist != NULL); - - page->free_next = heap->pooled_pages; - heap->pooled_pages = page; - objspace->rincgc.pooled_slots += page->free_slots; - - asan_lock_freelist(page); -} - -static void -heap_unlink_page(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *page) -{ - ccan_list_del(&page->page_node); - heap->total_pages--; - heap->total_slots -= page->total_slots; -} - -static void rb_aligned_free(void *ptr, size_t size); - -static void -heap_page_body_free(struct heap_page_body *page_body) -{ - GC_ASSERT((uintptr_t)page_body % HEAP_PAGE_ALIGN == 0); - - if (HEAP_PAGE_ALLOC_USE_MMAP) { -#ifdef HAVE_MMAP - GC_ASSERT(HEAP_PAGE_SIZE % sysconf(_SC_PAGE_SIZE) == 0); - if (munmap(page_body, HEAP_PAGE_SIZE)) { - rb_bug("heap_page_body_free: munmap failed"); - } -#endif - } - else { - rb_aligned_free(page_body, HEAP_PAGE_SIZE); - } -} - -static void -heap_page_free(rb_objspace_t *objspace, struct heap_page *page, bool global_pages_locked) -{ - heap_allocated_pages--; - - decrement_global_allocated_pages(global_pages_locked); - - page->size_pool->total_freed_pages++; - heap_page_body_free(GET_PAGE_BODY(page->start)); - free(page); -} - -static void * -rb_aligned_malloc(size_t alignment, size_t size) -{ - /* alignment must be a power of 2 */ - GC_ASSERT(((alignment - 1) & alignment) == 0); - GC_ASSERT(alignment % sizeof(void*) == 0); - - void *res; - -#if defined __MINGW32__ - res = __mingw_aligned_malloc(size, alignment); -#elif defined _WIN32 - void *_aligned_malloc(size_t, size_t); - res = _aligned_malloc(size, alignment); -#elif defined(HAVE_POSIX_MEMALIGN) - if (posix_memalign(&res, alignment, size) != 0) { - return NULL; - } -#elif defined(HAVE_MEMALIGN) - res = memalign(alignment, size); -#else - char* aligned; - res = malloc(alignment + size + sizeof(void*)); - aligned = (char*)res + alignment + sizeof(void*); - aligned -= ((VALUE)aligned & (alignment - 1)); - ((void**)aligned)[-1] = res; - res = (void*)aligned; -#endif - - GC_ASSERT((uintptr_t)res % alignment == 0); - - return res; -} - -static void -set_sorted_page_list_range(struct heap_page **sorted_page_list, size_t list_allocated_pages, uintptr_t *lo, uintptr_t *hi) -{ - struct heap_page *hipage = sorted_page_list[list_allocated_pages - 1]; - uintptr_t himem = (uintptr_t)hipage->start + (hipage->total_slots * hipage->slot_size); - GC_ASSERT(himem <= *hi); - *hi = himem; - - struct heap_page *lopage = sorted_page_list[0]; - uintptr_t lomem = (uintptr_t)lopage->start; - GC_ASSERT(lomem >= *lo); - *lo = lomem; -} - -static void -heap_pages_free_unused_pages(rb_objspace_t *objspace) -{ - size_t i, j; - - bool has_pages_in_tomb_heap = FALSE; - for (i = 0; i < SIZE_POOL_COUNT; i++) { - if (!ccan_list_empty(&SIZE_POOL_TOMB_HEAP(&size_pools[i])->pages)) { - has_pages_in_tomb_heap = TRUE; - break; - } - } - - if (has_pages_in_tomb_heap) { - int unlinked_pages = 0; - for (i = j = 0; j < heap_allocated_pages - unlinked_pages; i++) { - struct heap_page *page = heap_pages_sorted[i]; - - if (page->flags.in_tomb && page->free_slots == page->total_slots) { - heap_unlink_page(objspace, SIZE_POOL_TOMB_HEAP(page->size_pool), page); - page->unlinked = true; - unlinked_pages++; - } - else { - if (i != j) { - heap_pages_sorted[j] = page; - } - j++; - } - } - - set_sorted_page_list_range(heap_pages_sorted, heap_allocated_pages - unlinked_pages, &heap_pages_lomem, &heap_pages_himem); - - GC_ASSERT(j == heap_allocated_pages - unlinked_pages); - - rb_global_space_t *global_space = &rb_global_space; - rb_native_mutex_lock(&global_space->all_pages.global_pages_lock); - - for (i = j = 0; j < all_allocated_pages_global; i++) { - struct heap_page *page = all_pages_sorted_global[i]; - - if (page->unlinked) { - heap_page_free(objspace, page, true); - } - else { - if (i != j) { - all_pages_sorted_global[j] = page; - } - j++; - } - } - - set_sorted_page_list_range(all_pages_sorted_global, all_allocated_pages_global, &all_pages_lomem_global, &all_pages_himem_global); - - GC_ASSERT(j == all_allocated_pages_global); - - rb_native_mutex_unlock(&global_space->all_pages.global_pages_lock); - } -} - -static struct heap_page_body * -heap_page_body_allocate(void) -{ - struct heap_page_body *page_body; - - if (HEAP_PAGE_ALLOC_USE_MMAP) { -#ifdef HAVE_MMAP - GC_ASSERT(HEAP_PAGE_ALIGN % sysconf(_SC_PAGE_SIZE) == 0); - - char *ptr = mmap(NULL, HEAP_PAGE_ALIGN + HEAP_PAGE_SIZE, - PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (ptr == MAP_FAILED) { - return NULL; - } - - char *aligned = ptr + HEAP_PAGE_ALIGN; - aligned -= ((VALUE)aligned & (HEAP_PAGE_ALIGN - 1)); - GC_ASSERT(aligned > ptr); - GC_ASSERT(aligned <= ptr + HEAP_PAGE_ALIGN); - - size_t start_out_of_range_size = aligned - ptr; - GC_ASSERT(start_out_of_range_size % sysconf(_SC_PAGE_SIZE) == 0); - if (start_out_of_range_size > 0) { - if (munmap(ptr, start_out_of_range_size)) { - rb_bug("heap_page_body_allocate: munmap failed for start"); - } - } - - size_t end_out_of_range_size = HEAP_PAGE_ALIGN - start_out_of_range_size; - GC_ASSERT(end_out_of_range_size % sysconf(_SC_PAGE_SIZE) == 0); - if (end_out_of_range_size > 0) { - if (munmap(aligned + HEAP_PAGE_SIZE, end_out_of_range_size)) { - rb_bug("heap_page_body_allocate: munmap failed for end"); - } - } - - page_body = (struct heap_page_body *)aligned; -#endif - } - else { - page_body = rb_aligned_malloc(HEAP_PAGE_ALIGN, HEAP_PAGE_SIZE); - } - - GC_ASSERT((uintptr_t)page_body % HEAP_PAGE_ALIGN == 0); - - return page_body; -} - -static void -insert_into_sorted_page_list(rb_objspace_t *objspace, struct heap_page **sorted_list, size_t allocated_pages, struct heap_page *page, uintptr_t start) -{ - uintptr_t hi, lo, mid; - lo = 0; - hi = (uintptr_t)allocated_pages; - while (lo < hi) { - struct heap_page *mid_page; - - mid = (lo + hi) / 2; - mid_page = sorted_list[mid]; - if ((uintptr_t)mid_page->start < start) { - lo = mid + 1; - } - else if ((uintptr_t)mid_page->start > start) { - hi = mid; - } - else { - rb_bug("same heap page is allocated: %p at %"PRIuVALUE, (void *)GET_PAGE_BODY(start), (VALUE)mid); - } - } - - if (hi < (uintptr_t)allocated_pages) { - MEMMOVE(&sorted_list[hi+1], &sorted_list[hi], struct heap_page_header*, allocated_pages - hi); - } - - sorted_list[hi] = page; -} - -static struct heap_page * -heap_page_allocate(rb_objspace_t *objspace, rb_size_pool_t *size_pool) -{ - uintptr_t start, end, p; - struct heap_page *page; - size_t stride = size_pool->slot_size; - unsigned int limit = (unsigned int)((HEAP_PAGE_SIZE - sizeof(struct heap_page_header)))/(int)stride; - - /* assign heap_page body (contains heap_page_header and RVALUEs) */ - struct heap_page_body *page_body = heap_page_body_allocate(); - if (page_body == 0) { - rb_memerror(); - } - - /* assign heap_page entry */ - page = calloc1(sizeof(struct heap_page)); - if (page == 0) { - heap_page_body_free(page_body); - rb_memerror(); - } - - /* adjust obj_limit (object number available in this page) */ - start = (uintptr_t)((VALUE)page_body + sizeof(struct heap_page_header)); - - if (start % BASE_SLOT_SIZE != 0) { - int delta = BASE_SLOT_SIZE - (start % BASE_SLOT_SIZE); - start = start + delta; - GC_ASSERT(NUM_IN_PAGE(start) == 0 || NUM_IN_PAGE(start) == 1); - - /* Find a num in page that is evenly divisible by `stride`. - * This is to ensure that objects are aligned with bit planes. - * In other words, ensure there are an even number of objects - * per bit plane. */ - if (NUM_IN_PAGE(start) == 1) { - start += stride - BASE_SLOT_SIZE; - } - - GC_ASSERT(NUM_IN_PAGE(start) * BASE_SLOT_SIZE % stride == 0); - - limit = (HEAP_PAGE_SIZE - (int)(start - (uintptr_t)page_body))/(int)stride; - } - end = start + (limit * (int)stride); - - /* setup heap_pages_sorted */ - insert_into_sorted_page_list(objspace, heap_pages_sorted, heap_allocated_pages, page, start); - - heap_allocated_pages++; - - rb_global_space_t *global_space = &rb_global_space; - rb_native_mutex_lock(&global_space->all_pages.global_pages_lock); - - insert_into_sorted_page_list(objspace, all_pages_sorted_global, all_allocated_pages_global, page, start); - all_allocated_pages_global++; - - rb_native_mutex_unlock(&global_space->all_pages.global_pages_lock); - - GC_ASSERT(heap_eden_total_pages(objspace) + heap_allocatable_pages(objspace) <= heap_pages_sorted_length); - GC_ASSERT(heap_eden_total_pages(objspace) + heap_tomb_total_pages(objspace) == heap_allocated_pages - 1); - GC_ASSERT(heap_allocated_pages <= heap_pages_sorted_length); - - size_pool->total_allocated_pages++; - - if (heap_allocated_pages > heap_pages_sorted_length) { - rb_bug("heap_page_allocate: allocated(%"PRIdSIZE") > sorted(%"PRIdSIZE")", - heap_allocated_pages, heap_pages_sorted_length); - } - - if (heap_pages_lomem == 0 || heap_pages_lomem > start) heap_pages_lomem = start; - if (heap_pages_himem < end) heap_pages_himem = end; - - rb_native_mutex_lock(&global_space->all_pages.global_pages_lock); - - if (all_pages_lomem_global == 0 || all_pages_lomem_global > start) all_pages_lomem_global = start; - if (all_pages_himem_global < end) all_pages_himem_global = end; - - rb_native_mutex_unlock(&global_space->all_pages.global_pages_lock); - - page->start = start; - page->total_slots = limit; - page->slot_size = size_pool->slot_size; - page->size_pool = size_pool; - page_body->header.page = page; - - page->ractor = objspace->local_data.ractor; - page->objspace = objspace; - - for (p = start; p != end; p += stride) { - gc_report(3, objspace, "assign_heap_page: %p is added to freelist\n", (void *)p); - heap_page_add_freeobj(objspace, page, (VALUE)p); - } - page->free_slots = limit; - - asan_lock_freelist(page); - return page; -} - -static struct heap_page * -heap_page_resurrect(rb_objspace_t *objspace, rb_size_pool_t *size_pool) -{ - struct heap_page *page = 0, *next; - - ccan_list_for_each_safe(&SIZE_POOL_TOMB_HEAP(size_pool)->pages, page, next, page_node) { - asan_unlock_freelist(page); - if (page->freelist != NULL) { - heap_unlink_page(objspace, &size_pool->tomb_heap, page); - asan_lock_freelist(page); - return page; - } - } - - return NULL; -} - -static struct heap_page * -heap_page_create(rb_objspace_t *objspace, rb_size_pool_t *size_pool) -{ - struct heap_page *page; - const char *method = "recycle"; - - size_pool->allocatable_pages--; - - page = heap_page_resurrect(objspace, size_pool); - - if (page == NULL) { - page = heap_page_allocate(objspace, size_pool); - method = "allocate"; - } - if (0) fprintf(stderr, "heap_page_create: %s - %p, " - "heap_allocated_pages: %"PRIdSIZE", " - "heap_allocated_pages: %"PRIdSIZE", " - "tomb->total_pages: %"PRIdSIZE"\n", - method, (void *)page, heap_pages_sorted_length, heap_allocated_pages, SIZE_POOL_TOMB_HEAP(size_pool)->total_pages); - return page; -} - -static void -heap_add_page(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap, struct heap_page *page) -{ - /* Adding to eden heap during incremental sweeping is forbidden */ - GC_ASSERT(!(heap == SIZE_POOL_EDEN_HEAP(size_pool) && heap->sweeping_page)); - page->flags.in_tomb = (heap == SIZE_POOL_TOMB_HEAP(size_pool)); - ccan_list_add_tail(&heap->pages, &page->page_node); - heap->total_pages++; - heap->total_slots += page->total_slots; -} - -rb_heap_t * -select_heap(rb_objspace_t *objspace, int size_pool_idx, bool eden) -{ - return eden ? SIZE_POOL_EDEN_HEAP(&size_pools[size_pool_idx]) : SIZE_POOL_TOMB_HEAP(&size_pools[size_pool_idx]); -} - -void -absorb_page_into_objspace(rb_objspace_t *objspace, struct heap_page *page, int size_pool_idx, bool eden) -{ - VM_ASSERT (page->objspace != objspace); - VM_ASSERT(objspace->local_data.currently_absorbing); - - ccan_list_del_init(&page->page_node); - - rb_heap_t *heap = select_heap(objspace, size_pool_idx, eden); - - rb_size_pool_t *size_pool = &size_pools[size_pool_idx]; - - heap_add_page(objspace, size_pool, heap, page); - insert_into_sorted_page_list(objspace, heap_pages_sorted, heap_allocated_pages, page, page->start); - size_pool->allocatable_pages--; - heap_allocated_pages++; - - struct heap_page_body *page_body = GET_PAGE_BODY(page->start); - size_t stride = (size_pool)->slot_size; - unsigned int limit = (HEAP_PAGE_SIZE - (int)(page->start - (uintptr_t)page_body))/(int)stride; - uintptr_t end = page->start + (limit * (int)stride); - if (heap_pages_lomem == 0 || heap_pages_lomem > page->start) heap_pages_lomem = page->start; - if (heap_pages_himem < end) heap_pages_himem = end; - - RUBY_ATOMIC_PTR_EXCHANGE(page->ractor, objspace->local_data.ractor); - RUBY_ATOMIC_PTR_EXCHANGE(page->objspace, objspace); - - //TODO: What if another Ractor tries to access this pointer at this exact moment? - page->size_pool = size_pool; -} - -void -heap_append_free_page(rb_heap_t *heap, struct heap_page *page) -{ - struct heap_page *end_page = heap->free_pages; - if (end_page) { - while (end_page->free_next) end_page = end_page->free_next; - end_page->free_next = page; - } - else { - heap->free_pages = page; - } -} - -struct heap_page * -get_freepages(rb_heap_t *heap) -{ - return heap->free_pages; -} - -struct heap_page * -heap_get_top_page(rb_heap_t *heap) -{ - return ccan_list_top(&heap->pages, struct heap_page, page_node); -} - -size_t -total_pages_in_heap(rb_heap_t *heap) -{ - return heap->total_pages; -} - -void -size_pool_allocatable_pages_update(rb_objspace_t *objspace, size_t additional_pages[SIZE_POOL_COUNT]) -{ - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - size_pools[i].allocatable_pages += additional_pages[i]; - } - heap_pages_expand_sorted(objspace); -} - -void -absorption_finished(rb_objspace_t *objspace) -{ - objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_ABSORB; -} - -void -rb_disconnect_ractor_from_unabsorbed_objspace(rb_ractor_t *r) -{ - r->local_objspace->local_data.ractor = NULL; -} - -struct objspace_local_data * -objspace_get_local_data(rb_objspace_t *objspace) -{ - return &objspace->local_data; -} - -static void -heap_assign_page(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap) -{ - struct heap_page *page = heap_page_create(objspace, size_pool); - heap_add_page(objspace, size_pool, heap, page); - heap_add_freepage(heap, page); -} - -#if GC_CAN_COMPILE_COMPACTION -static void -heap_add_pages(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap, size_t add) -{ - size_t i; - - size_pool_allocatable_pages_set(objspace, size_pool, add); - - for (i = 0; i < add; i++) { - heap_assign_page(objspace, size_pool, heap); - } - - GC_ASSERT(size_pool->allocatable_pages == 0); -} -#endif - -static size_t -slots_to_pages_for_size_pool(rb_objspace_t *objspace, rb_size_pool_t *size_pool, size_t slots) -{ - size_t multiple = size_pool->slot_size / BASE_SLOT_SIZE; - /* Due to alignment, heap pages may have one less slot. We should - * ensure there is enough pages to guarantee that we will have at - * least the required number of slots after allocating all the pages. */ - size_t slots_per_page = (HEAP_PAGE_OBJ_LIMIT / multiple) - 1; - return CEILDIV(slots, slots_per_page); -} - -static size_t -minimum_pages_for_size_pool(rb_objspace_t *objspace, rb_size_pool_t *size_pool) -{ - size_t size_pool_idx = size_pool - size_pools; - size_t init_slots = gc_params.size_pool_init_slots[size_pool_idx]; - return slots_to_pages_for_size_pool(objspace, size_pool, init_slots); -} - -static size_t -heap_extend_pages(rb_objspace_t *objspace, rb_size_pool_t *size_pool, size_t free_slots, size_t total_slots, size_t used) -{ - double goal_ratio = gc_params.heap_free_slots_goal_ratio; - size_t next_used; - - if (goal_ratio == 0.0) { - next_used = (size_t)(used * gc_params.growth_factor); - } - else if (total_slots == 0) { - next_used = minimum_pages_for_size_pool(objspace, size_pool); - } - else { - /* Find `f' where free_slots = f * total_slots * goal_ratio - * => f = (total_slots - free_slots) / ((1 - goal_ratio) * total_slots) - */ - double f = (double)(total_slots - free_slots) / ((1 - goal_ratio) * total_slots); - - if (f > gc_params.growth_factor) f = gc_params.growth_factor; - if (f < 1.0) f = 1.1; - - next_used = (size_t)(f * used); - - if (0) { - fprintf(stderr, - "free_slots(%8"PRIuSIZE")/total_slots(%8"PRIuSIZE")=%1.2f," - " G(%1.2f), f(%1.2f)," - " used(%8"PRIuSIZE") => next_used(%8"PRIuSIZE")\n", - free_slots, total_slots, free_slots/(double)total_slots, - goal_ratio, f, used, next_used); - } - } - - if (gc_params.growth_max_slots > 0) { - size_t max_used = (size_t)(used + gc_params.growth_max_slots/HEAP_PAGE_OBJ_LIMIT); - if (next_used > max_used) next_used = max_used; - } - - size_t extend_page_count = next_used - used; - /* Extend by at least 1 page. */ - if (extend_page_count == 0) extend_page_count = 1; - - return extend_page_count; -} - -static int -heap_increment(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap) -{ - if (size_pool->allocatable_pages > 0) { - gc_report(1, objspace, "heap_increment: heap_pages_sorted_length: %"PRIdSIZE", " - "heap_pages_inc: %"PRIdSIZE", heap->total_pages: %"PRIdSIZE"\n", - heap_pages_sorted_length, size_pool->allocatable_pages, heap->total_pages); - - GC_ASSERT(heap_allocatable_pages(objspace) + heap_eden_total_pages(objspace) <= heap_pages_sorted_length); - GC_ASSERT(heap_allocated_pages <= heap_pages_sorted_length); - - heap_assign_page(objspace, size_pool, heap); - return TRUE; - } - return FALSE; -} - -static void -gc_continue(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap) -{ - LOCAL_GC_BEGIN(objspace); - { - gc_enter(objspace, gc_enter_event_continue); - - /* Continue marking if in incremental marking. */ - if (is_incremental_marking(objspace)) { - if (gc_marks_continue(objspace, size_pool, heap)) { - gc_sweep(objspace); - } - } - - /* Continue sweeping if in lazy sweeping or the previous incremental - * marking finished and did not yield a free page. */ - if (heap->free_pages == NULL && is_lazy_sweeping(objspace)) { - gc_sweep_continue(objspace, size_pool, heap); - } - - gc_exit(objspace, gc_enter_event_continue); - } - LOCAL_GC_END(objspace); -} - -static void -heap_prepare(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap, bool borrowing) -{ - GC_ASSERT(heap->free_pages == NULL); - - if (!borrowing) { - /* Continue incremental marking or lazy sweeping, if in any of those steps. */ - gc_continue(objspace, size_pool, heap); - } - - /* If we still don't have a free page and not allowed to create a new page, - * we should start a new GC cycle. */ - if (heap->free_pages == NULL && - (will_be_incremental_marking(objspace) || - (heap_increment(objspace, size_pool, heap) == FALSE))) { - if (borrowing) { - size_pool_allocatable_pages_set(objspace, size_pool, 1); - if (!heap_increment(objspace, size_pool, heap)) { - rb_bug("cannot create a new borrowing page in target Ractor"); - } - } - else if (garbage_collect(objspace, GPR_FLAG_NEWOBJ, false) == FALSE) { - rb_memerror(); - } - else { - /* Do steps of incremental marking or lazy sweeping if the GC run permits. */ - gc_continue(objspace, size_pool, heap); - - /* If we're not incremental marking (e.g. a minor GC) or finished - * 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 (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 - if (garbage_collect(objspace, GPR_FLAG_NEWOBJ, false) == FALSE) { - rb_memerror(); - } - else { - /* Do steps of incremental marking or lazy sweeping. */ - gc_continue(objspace, size_pool, heap); - - if (heap->free_pages == NULL && - !heap_increment(objspace, size_pool, heap)) { - rb_bug("cannot create a new page after major GC"); - } - } - } - } - } - } - - GC_ASSERT(heap->free_pages != NULL); -} - -void -rb_objspace_set_event_hook(const rb_event_flag_t event) -{ - rb_objspace_t *objspace = &rb_objspace; - objspace->hook_events = event & RUBY_INTERNAL_EVENT_OBJSPACE_MASK; - objspace->flags.has_newobj_hook = !!(objspace->hook_events & RUBY_INTERNAL_EVENT_NEWOBJ); -} - -static void -gc_event_hook_body(rb_execution_context_t *ec, rb_objspace_t *objspace, const rb_event_flag_t event, VALUE data) -{ - if (UNLIKELY(!ec->cfp)) return; - EXEC_EVENT_HOOK(ec, event, ec->cfp->self, 0, 0, 0, data); -} - -#define gc_event_newobj_hook_needed_p(objspace) ((objspace)->flags.has_newobj_hook) -#define gc_event_hook_needed_p(objspace, event) ((objspace)->hook_events & (event)) - -#define gc_event_hook_prep(objspace, event, data, prep) do { \ - if (UNLIKELY(gc_event_hook_needed_p(objspace, event))) { \ - prep; \ - gc_event_hook_body(GET_EC(), (objspace), (event), (data)); \ - } \ -} while (0) - -#define gc_event_hook(objspace, event, data) gc_event_hook_prep(objspace, event, data, (void)0) - -static inline VALUE -newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace, VALUE obj) -{ -#if !__has_feature(memory_sanitizer) - GC_ASSERT(BUILTIN_TYPE(obj) == T_NONE); - GC_ASSERT((flags & FL_WB_PROTECTED) == 0); -#endif - RVALUE *p = RANY(obj); - p->as.basic.flags = flags; - *((VALUE *)&p->as.basic.klass) = klass; - - int t = flags & RUBY_T_MASK; - if (t == T_CLASS || t == T_MODULE || t == T_ICLASS) { - RVALUE_AGE_SET_CANDIDATE(objspace, obj); - } - -#if RACTOR_CHECK_MODE - rb_ractor_setup_belonging(obj); -#endif - -#if RGENGC_CHECK_MODE - p->as.values.v1 = p->as.values.v2 = p->as.values.v3 = 0; - - HEAP_LOCK_ENTER(objspace); - { - check_rvalue_consistency(obj); - - GC_ASSERT(RVALUE_MARKED(obj) == FALSE); - GC_ASSERT(RVALUE_MARKING(obj) == FALSE); - GC_ASSERT(RVALUE_OLD_P(obj) == FALSE); - GC_ASSERT(RVALUE_WB_UNPROTECTED(obj) == FALSE); - GC_ASSERT(RVALUE_LOCAL_IMMUNE(obj) == FALSE); - - if (RVALUE_REMEMBERED((VALUE)obj)) rb_bug("newobj: %s is remembered.", obj_info(obj)); - } - HEAP_LOCK_LEAVE(objspace); -#endif - - if (UNLIKELY(wb_protected == FALSE)) { - VM_ASSERT(heap_locked(objspace)); - MARK_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(obj), obj); - } - -#if RGENGC_PROFILE - if (wb_protected) { - objspace->profile.total_generated_normal_object_count++; -#if RGENGC_PROFILE >= 2 - objspace->profile.generated_normal_object_count_types[BUILTIN_TYPE(obj)]++; -#endif - } - else { - objspace->profile.total_generated_shady_object_count++; -#if RGENGC_PROFILE >= 2 - objspace->profile.generated_shady_object_count_types[BUILTIN_TYPE(obj)]++; -#endif - } -#endif - -#if GC_DEBUG - GET_RVALUE_OVERHEAD(obj)->file = rb_source_location_cstr(&GET_RVALUE_OVERHEAD(obj)->line); - GC_ASSERT(!SPECIAL_CONST_P(obj)); /* check alignment */ -#endif - - gc_report(5, objspace, "newobj: %s\n", obj_info_basic(obj)); - - // RUBY_DEBUG_LOG("obj:%p (%s)", (void *)obj, obj_type_name(obj)); - return obj; -} - -size_t -rb_gc_obj_slot_size(VALUE obj) -{ - return GET_HEAP_PAGE(obj)->slot_size - RVALUE_OVERHEAD; -} - -static inline size_t -size_pool_slot_size(unsigned char pool_id) -{ - GC_ASSERT(pool_id < SIZE_POOL_COUNT); - - size_t slot_size = (1 << pool_id) * BASE_SLOT_SIZE; - -#if RGENGC_CHECK_MODE - rb_objspace_t *objspace = &rb_objspace; - GC_ASSERT(size_pools[pool_id].slot_size == (short)slot_size); -#endif - - slot_size -= RVALUE_OVERHEAD; - - return slot_size; -} - -bool -rb_gc_size_allocatable_p(size_t size) -{ - return size <= size_pool_slot_size(SIZE_POOL_COUNT - 1); -} - -static size_t size_pool_sizes[SIZE_POOL_COUNT + 1] = { 0 }; - -size_t * -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] = size_pool_slot_size(i); - } - } - - return size_pool_sizes; -} - -size_t -rb_gc_size_pool_id_for_size(size_t size) -{ - size += RVALUE_OVERHEAD; - - size_t slot_count = CEILDIV(size, BASE_SLOT_SIZE); - - /* size_pool_idx is ceil(log2(slot_count)) */ - size_t size_pool_idx = 64 - nlz_int64(slot_count - 1); - - if (size_pool_idx >= SIZE_POOL_COUNT) { - rb_bug("rb_gc_size_pool_id_for_size: allocation size too large " - "(size=%"PRIuSIZE"u, size_pool_idx=%"PRIuSIZE"u)", size, size_pool_idx); - } - -#if RGENGC_CHECK_MODE - rb_objspace_t *objspace = &rb_objspace; - GC_ASSERT(size <= (size_t)size_pools[size_pool_idx].slot_size); - if (size_pool_idx > 0) GC_ASSERT(size > (size_t)size_pools[size_pool_idx - 1].slot_size); -#endif - - return size_pool_idx; -} - -static inline VALUE -ractor_cache_allocate_slot(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, - size_t size_pool_idx, bool borrowing) -{ - rb_ractor_newobj_size_pool_cache_t *size_pool_cache = &cache->size_pool_caches[size_pool_idx]; - RVALUE *p = size_pool_cache->freelist; - - if (!borrowing && is_incremental_marking(objspace)) { - // Not allowed to allocate without running an incremental marking step - if (cache->incremental_mark_step_allocated_slots >= INCREMENTAL_MARK_STEP_ALLOCATIONS) { - return Qfalse; - } - - if (p) { - cache->incremental_mark_step_allocated_slots++; - } - } - - if (p) { - VALUE obj = (VALUE)p; - MAYBE_UNUSED(const size_t) stride = size_pool_slot_size(size_pool_idx); - size_pool_cache->freelist = p->as.free.next; - asan_unpoison_memory_region(p, stride, true); -#if RGENGC_CHECK_MODE - GC_ASSERT(rb_gc_obj_slot_size(obj) == stride); - // zero clear - MEMZERO((char *)obj, char, stride); -#endif - return obj; - } - else { - return Qfalse; - } -} - -static struct heap_page * -heap_next_free_page(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap, bool borrowing) -{ - VM_ASSERT(heap_locked(objspace)); - - struct heap_page *page; - - if (heap->free_pages == NULL) { - heap_prepare(objspace, size_pool, heap, borrowing); - } - - page = heap->free_pages; - heap->free_pages = page->free_next; - - GC_ASSERT(page->free_slots != 0); - RUBY_DEBUG_LOG("page:%p freelist:%p cnt:%d", (void *)page, (void *)page->freelist, page->free_slots); - - asan_unlock_freelist(page); - - return page; -} - -static inline void -ractor_cache_set_page(rb_ractor_newobj_cache_t *cache, size_t size_pool_idx, - struct heap_page *page) -{ - gc_report(3, &rb_objspace, "ractor_set_cache: Using page %p\n", (void *)GET_PAGE_BODY(page->start)); - - rb_ractor_newobj_size_pool_cache_t *size_pool_cache = &cache->size_pool_caches[size_pool_idx]; - - GC_ASSERT(size_pool_cache->freelist == NULL); - GC_ASSERT(page->free_slots != 0); - GC_ASSERT(page->freelist != NULL); - - size_pool_cache->using_page = page; - size_pool_cache->freelist = page->freelist; - page->free_slots = 0; - page->freelist = NULL; - - asan_unpoison_object((VALUE)size_pool_cache->freelist, false); - GC_ASSERT(RB_TYPE_P((VALUE)size_pool_cache->freelist, T_NONE)); - asan_poison_object((VALUE)size_pool_cache->freelist); -} - -static inline VALUE -newobj_fill(VALUE obj, VALUE v1, VALUE v2, VALUE v3) -{ - RVALUE *p = (RVALUE *)obj; - p->as.values.v1 = v1; - p->as.values.v2 = v2; - p->as.values.v3 = v3; - return obj; -} - -static void gc_ractor_newobj_size_pool_cache_clear(rb_ractor_newobj_size_pool_cache_t *cache); - -static VALUE -newobj_alloc_borrowing(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t size_pool_idx) -{ - rb_ractor_t *alloc_target_ractor = objspace->local_data.ractor; - rb_ractor_t *cr = GET_RACTOR(); - - VM_ASSERT(alloc_target_ractor->borrowing_sync.lock_owner == cr); - - rb_size_pool_t *size_pool = &size_pools[size_pool_idx]; - rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); - - bool borrowable_page_locked = false; - borrowable_page_locked = !rb_native_mutex_trylock(&alloc_target_ractor->borrowing_sync.page_lock[size_pool_idx]); - if (borrowable_page_locked) { - alloc_target_ractor->borrowing_sync.page_lock_owner[size_pool_idx] = cr; - } - - bool need_new_borrowing_page = !borrowable_page_locked && alloc_target_ractor->borrowing_sync.page_recently_locked[size_pool_idx]; - - VALUE obj; - if (need_new_borrowing_page) { - obj = Qfalse; - } - else { - obj = ractor_cache_allocate_slot(objspace, cache, size_pool_idx, true); - } - - if (UNLIKELY(obj == Qfalse)) { - - HEAP_LOCK_ENTER(objspace); - { - if (obj == Qfalse) { - // Get next free page (possibly running GC) - struct heap_page *page = heap_next_free_page(objspace, size_pool, heap, true); - if (need_new_borrowing_page) - { - rb_ractor_newobj_size_pool_cache_t *size_pool_cache = &cache->size_pool_caches[size_pool_idx]; - gc_ractor_newobj_size_pool_cache_clear(size_pool_cache); - } - - ractor_cache_set_page(cache, size_pool_idx, page); - - // Retry allocation after moving to new page - obj = ractor_cache_allocate_slot(objspace, cache, size_pool_idx, true); - } - } - HEAP_LOCK_LEAVE(objspace); - } - - if (UNLIKELY(obj == Qfalse)) { - rb_memerror(); - } - - size_pool->newly_created_by_borrowing_count++; - - if (borrowable_page_locked) { - alloc_target_ractor->borrowing_sync.page_lock_owner[size_pool_idx] = NULL; - rb_native_mutex_unlock(&alloc_target_ractor->borrowing_sync.page_lock[size_pool_idx]); - } - alloc_target_ractor->borrowing_sync.page_recently_locked[size_pool_idx] = false; - - return obj; -} - -static VALUE -newobj_alloc(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t size_pool_idx) -{ - rb_size_pool_t *size_pool = &size_pools[size_pool_idx]; - rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); - - VALUE obj = ractor_cache_allocate_slot(objspace, cache, size_pool_idx, false); - - if (UNLIKELY(obj == Qfalse)) { - - HEAP_LOCK_ENTER(objspace); - { - if (is_incremental_marking(objspace)) { - gc_continue(objspace, size_pool, heap); - cache->incremental_mark_step_allocated_slots = 0; - - // Retry allocation after resetting incremental_mark_step_allocated_slots - obj = ractor_cache_allocate_slot(objspace, cache, size_pool_idx, false); - } - - if (obj == Qfalse) { - // Get next free page (possibly running GC) - struct heap_page *page = heap_next_free_page(objspace, size_pool, heap, false); - - ractor_cache_set_page(cache, size_pool_idx, page); - - // Retry allocation after moving to new page - obj = ractor_cache_allocate_slot(objspace, cache, size_pool_idx, false); - } - } - HEAP_LOCK_LEAVE(objspace); - - } - - if (UNLIKELY(obj == Qfalse)) { - rb_memerror(); - } - - size_pool->total_allocated_objects++; - - return obj; -} - -static void -newobj_zero_slot(VALUE obj) -{ - memset((char *)obj + sizeof(struct RBasic), 0, rb_gc_obj_slot_size(obj) - sizeof(struct RBasic)); -} - -ALWAYS_INLINE(static VALUE newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, int wb_protected, size_t size_pool_idx, bool borrowing)); - -static inline VALUE -newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, int wb_protected, size_t size_pool_idx, bool borrowing) -{ - VALUE obj; - unsigned int lev; - if (UNLIKELY((during_gc) || ruby_gc_stressful)) { - if (during_gc && !(borrowing && objspace->local_data.waiting_for_object_graph_safety)) { - dont_gc_on(); - during_gc = 0; - rb_bug("object allocation during garbage collection phase"); - } - - if (ruby_gc_stressful) { - if (!garbage_collect(objspace, GPR_FLAG_NEWOBJ, false)) { - rb_memerror(); - } - } - } - - obj = UNLIKELY(borrowing) ? newobj_alloc_borrowing(objspace, cache, size_pool_idx) : newobj_alloc(objspace, cache, size_pool_idx); - - HEAP_LOCK_ENTER(objspace); - { - newobj_init(klass, flags, wb_protected, objspace, obj); - - gc_event_hook_prep(objspace, RUBY_INTERNAL_EVENT_NEWOBJ, obj, newobj_zero_slot(obj)); - } - HEAP_LOCK_LEAVE(objspace); - - return obj; -} - -NOINLINE(static VALUE newobj_slowpath_wb_protected(VALUE klass, VALUE flags, - rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t size_pool_idx, bool borrowing)); -NOINLINE(static VALUE newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, - rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t size_pool_idx, bool borrowing)); - -static VALUE -newobj_slowpath_wb_protected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t size_pool_idx, bool borrowing) -{ - return newobj_slowpath(klass, flags, objspace, cache, TRUE, size_pool_idx, borrowing); -} - -static VALUE -newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t size_pool_idx, bool borrowing) -{ - return newobj_slowpath(klass, flags, objspace, cache, FALSE, size_pool_idx, borrowing); -} - -static inline int gc_mark_set(rb_objspace_t *objspace, VALUE obj); -static void gc_grey(rb_objspace_t *objspace, VALUE obj); -static void gc_aging(VALUE obj); - -static inline VALUE -newobj_of(rb_ractor_t *cr, VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, int wb_protected, size_t alloc_size) -{ - VALUE obj; - rb_objspace_t *objspace = &rb_objspace; - - RB_DEBUG_COUNTER_INC(obj_newobj); - (void)RB_DEBUG_COUNTER_INC_IF(obj_newobj_wb_unprotected, !wb_protected); - - if (UNLIKELY(stress_to_class)) { - long i, cnt = RARRAY_LEN(stress_to_class); - for (i = 0; i < cnt; ++i) { - if (klass == RARRAY_AREF(stress_to_class, i)) rb_memerror(); - } - } - - size_t size_pool_idx = rb_gc_size_pool_id_for_size(alloc_size); - - - rb_ractor_newobj_cache_t *cache; - bool borrowing = !!(objspace->local_data.alloc_target_ractor); - if (borrowing) { - objspace = objspace->local_data.alloc_target_ractor->local_objspace; - cache = &objspace->local_data.ractor->newobj_borrowing_cache; - } - else { - cache = &cr->newobj_cache; - } - - if (borrowing) rb_borrowing_sync_lock(objspace->local_data.ractor); - - if (!UNLIKELY(during_gc || - ruby_gc_stressful || - gc_event_newobj_hook_needed_p(objspace)) && - wb_protected) { - obj = UNLIKELY(borrowing) ? newobj_alloc_borrowing(objspace, cache, size_pool_idx) : newobj_alloc(objspace, cache, size_pool_idx); - newobj_init(klass, flags, wb_protected, objspace, obj); - } - else { - RB_DEBUG_COUNTER_INC(obj_newobj_slowpath); - - obj = wb_protected ? - newobj_slowpath_wb_protected(klass, flags, objspace, cache, size_pool_idx, borrowing) : - newobj_slowpath_wb_unprotected(klass, flags, objspace, cache, size_pool_idx, borrowing); - } - - if (borrowing && (is_incremental_marking(objspace) || objspace->local_data.waiting_for_object_graph_safety)) { - if (gc_mark_set(objspace, obj)) { - gc_aging(obj); - gc_grey(objspace, obj); - } - } - if (borrowing) { - register_received_obj(objspace, GET_RACTOR()->borrowing_sync.borrowing_id, obj); - rb_borrowing_sync_unlock(objspace->local_data.ractor); - } - - return newobj_fill(obj, v1, v2, v3); -} - -VALUE -rb_wb_unprotected_newobj_of(VALUE klass, VALUE flags, size_t size) -{ - GC_ASSERT((flags & FL_WB_PROTECTED) == 0); - return newobj_of(GET_RACTOR(), klass, flags, 0, 0, 0, FALSE, size); -} - -VALUE -rb_wb_protected_newobj_of(rb_execution_context_t *ec, VALUE klass, VALUE flags, size_t size) -{ - GC_ASSERT((flags & FL_WB_PROTECTED) == 0); - return newobj_of(rb_ec_ractor_ptr(ec), klass, flags, 0, 0, 0, TRUE, size); -} - -#define UNEXPECTED_NODE(func) \ - rb_bug(#func"(): GC does not handle T_NODE 0x%x(%p) 0x%"PRIxVALUE, \ - BUILTIN_TYPE(obj), (void*)(obj), RBASIC(obj)->flags) - -static inline void -rb_data_object_check(VALUE klass) -{ - if (klass != rb_cObject && (rb_get_alloc_func(klass) == rb_class_allocate_instance)) { - rb_undef_alloc_func(klass); - rb_warn("undefining the allocator of T_DATA class %"PRIsVALUE, klass); - } -} - -VALUE -rb_data_object_wrap(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree) -{ - RUBY_ASSERT_ALWAYS(dfree != (RUBY_DATA_FUNC)1); - if (klass) rb_data_object_check(klass); - return newobj_of(GET_RACTOR(), klass, T_DATA, (VALUE)dmark, (VALUE)dfree, (VALUE)datap, !dmark, sizeof(struct RTypedData)); -} - -VALUE -rb_data_object_zalloc(VALUE klass, size_t size, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree) -{ - VALUE obj = rb_data_object_wrap(klass, 0, dmark, dfree); - DATA_PTR(obj) = xcalloc(1, size); - return obj; -} - -static VALUE -typed_data_alloc(VALUE klass, VALUE typed_flag, void *datap, const rb_data_type_t *type, size_t size) -{ - RBIMPL_NONNULL_ARG(type); - if (klass) rb_data_object_check(klass); - bool wb_protected = (type->flags & RUBY_FL_WB_PROTECTED) || !type->function.dmark; - return newobj_of(GET_RACTOR(), klass, T_DATA, (VALUE)type, 1 | typed_flag, (VALUE)datap, wb_protected, size); -} - -VALUE -rb_data_typed_object_wrap(VALUE klass, void *datap, const rb_data_type_t *type) -{ - if (UNLIKELY(type->flags & RUBY_TYPED_EMBEDDABLE)) { - rb_raise(rb_eTypeError, "Cannot wrap an embeddable TypedData"); - } - - return typed_data_alloc(klass, 0, datap, type, sizeof(struct RTypedData)); -} - -VALUE -rb_data_typed_object_zalloc(VALUE klass, size_t size, const rb_data_type_t *type) -{ - if (type->flags & RUBY_TYPED_EMBEDDABLE) { - if (!(type->flags & RUBY_TYPED_FREE_IMMEDIATELY)) { - rb_raise(rb_eTypeError, "Embeddable TypedData must be freed immediately"); - } - - size_t embed_size = offsetof(struct RTypedData, data) + size; - if (rb_gc_size_allocatable_p(embed_size)) { - VALUE obj = typed_data_alloc(klass, TYPED_DATA_EMBEDDED, 0, type, embed_size); - memset((char *)obj + offsetof(struct RTypedData, data), 0, size); - return obj; - } - } - - VALUE obj = typed_data_alloc(klass, 0, NULL, type, sizeof(struct RTypedData)); - DATA_PTR(obj) = xcalloc(1, size); - return obj; -} - -static size_t -rb_objspace_data_type_memsize(VALUE obj) -{ - size_t size = 0; - if (RTYPEDDATA_P(obj)) { - const rb_data_type_t *type = RTYPEDDATA_TYPE(obj); - const void *ptr = RTYPEDDATA_GET_DATA(obj); - - if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !RTYPEDDATA_EMBEDDED_P(obj)) { -#ifdef HAVE_MALLOC_USABLE_SIZE - size += malloc_usable_size((void *)ptr); -#endif - } - - if (ptr && type->function.dsize) { - size += type->function.dsize(ptr); - } - } - - return size; -} - -const char * -rb_objspace_data_type_name(VALUE obj) -{ - if (RTYPEDDATA_P(obj)) { - return RTYPEDDATA_TYPE(obj)->wrap_struct_name; - } - else { - return 0; - } -} - -static int -ptr_in_page_body_p(const void *ptr, const void *memb) -{ - struct heap_page *page = *(struct heap_page **)memb; - uintptr_t p_body = (uintptr_t)GET_PAGE_BODY(page->start); - - if ((uintptr_t)ptr >= p_body) { - return (uintptr_t)ptr < (p_body + HEAP_PAGE_SIZE) ? 0 : 1; - } - else { - return -1; - } -} - -PUREFUNC(static inline struct heap_page * heap_page_for_ptr(rb_objspace_t *objspace, uintptr_t ptr);) -static inline struct heap_page * -heap_page_for_ptr(rb_objspace_t *objspace, uintptr_t ptr) -{ - struct heap_page **res; - - if (ptr < (uintptr_t)heap_pages_lomem || - ptr > (uintptr_t)heap_pages_himem) { - return NULL; - } - - res = bsearch((void *)ptr, heap_pages_sorted, - (size_t)heap_allocated_pages, sizeof(struct heap_page *), - ptr_in_page_body_p); - - if (res) { - return *res; - } - else { - return NULL; - } -} - -PUREFUNC(static inline struct heap_page * global_heap_page_for_ptr(rb_objspace_t *objspace, uintptr_t ptr);) -static inline struct heap_page * -global_heap_page_for_ptr(rb_objspace_t *objspace, uintptr_t ptr) -{ - rb_global_space_t *global_space = &rb_global_space; - struct heap_page **res; - - if (ptr < (uintptr_t)all_pages_lomem_global || - ptr > (uintptr_t)all_pages_himem_global) { - return NULL; - } - - res = bsearch((void *)ptr, all_pages_sorted_global, - (size_t)all_allocated_pages_global, sizeof(struct heap_page *), - ptr_in_page_body_p); - - if (res) { - return *res; - } - else { - return NULL; - } -} - -PUREFUNC(static inline int pointer_is_on_page(register uintptr_t p, register struct heap_page *page);) -static inline int -pointer_is_on_page(register uintptr_t p, register struct heap_page *page) -{ - if (page) { - RB_DEBUG_COUNTER_INC(gc_isptr_maybe); - if (page->flags.in_tomb) { - return FALSE; - } - else { - if (p < page->start) return FALSE; - if (p >= page->start + (page->total_slots * page->slot_size)) return FALSE; - if ((NUM_IN_PAGE(p) * BASE_SLOT_SIZE) % page->slot_size != 0) return FALSE; - - return TRUE; - } - } - return FALSE; -} - -PUREFUNC(static inline int heap_page_possible(rb_objspace_t *objspace, register uintptr_t p, uintptr_t lomem, uintptr_t himem);) -static inline int -heap_page_possible(rb_objspace_t *objspace, register uintptr_t p, uintptr_t lomem, uintptr_t himem) -{ - RB_DEBUG_COUNTER_INC(gc_isptr_trial); - - if (p < lomem || p > himem) return FALSE; - RB_DEBUG_COUNTER_INC(gc_isptr_range); - - if (p % BASE_SLOT_SIZE != 0) return FALSE; - RB_DEBUG_COUNTER_INC(gc_isptr_align); - - return TRUE; -} - -PUREFUNC(static inline int is_pointer_to_local_heap(rb_objspace_t *objspace, const void *ptr);) -static inline int -is_pointer_to_local_heap(rb_objspace_t *objspace, const void *ptr) -{ - register uintptr_t p = (uintptr_t)ptr; - register struct heap_page *page = NULL; - - if (during_gc) { - if (heap_page_possible(objspace, p, heap_pages_lomem, heap_pages_himem) == TRUE) { - page = heap_page_for_ptr(objspace, (uintptr_t)ptr); - } - return pointer_is_on_page(p, page); - } - else { - int ret; - HEAP_LOCK_ENTER(objspace); - { - if (heap_page_possible(objspace, p, heap_pages_lomem, heap_pages_himem) == TRUE) { - page = heap_page_for_ptr(objspace, (uintptr_t)ptr); - } - ret = pointer_is_on_page(p, page); - } - HEAP_LOCK_LEAVE(objspace); - return ret; - } -} - -PUREFUNC(static inline int is_pointer_to_global_heap(rb_objspace_t *objspace, const void *ptr);) -static inline int -is_pointer_to_global_heap(rb_objspace_t *objspace, const void *ptr) -{ - rb_global_space_t *global_space = &rb_global_space; - register uintptr_t p = (uintptr_t)ptr; - register struct heap_page *page = NULL; - - if (objspace->flags.during_global_gc) { - if (heap_page_possible(global_space, p, all_pages_lomem_global, all_pages_himem_global) == TRUE) { - page = global_heap_page_for_ptr(objspace, (uintptr_t)ptr); - } - return pointer_is_on_page(p, page); - } - else { - rb_objspace_t *page_objspace; - int ret; - - rb_native_mutex_lock(&global_space->all_pages.global_pages_lock); - if (heap_page_possible(global_space, p, all_pages_lomem_global, all_pages_himem_global) == TRUE) { - page = global_heap_page_for_ptr(objspace, (uintptr_t)ptr); - if (page) { - page_objspace = page->objspace; - rb_native_mutex_unlock(&global_space->all_pages.global_pages_lock); - HEAP_LOCK_ENTER(page_objspace); - { - ret = pointer_is_on_page(p, page); - } - HEAP_LOCK_LEAVE(page_objspace); - return ret; - } - } - rb_native_mutex_unlock(&global_space->all_pages.global_pages_lock); - return FALSE; - } -} - -PUREFUNC(static inline int is_pointer_to_heap(rb_objspace_t *objspace, const void *ptr);) -static inline int -is_pointer_to_heap(rb_objspace_t *objspace, const void *ptr) -{ - if (is_pointer_to_local_heap(objspace, ptr)) { - return TRUE; - } - else if (!objspace->belong_to_single_main_ractor) { - int ret; - SUSPEND_HEAP_LOCK_BEGIN(objspace); - { - ret = is_pointer_to_global_heap(objspace, ptr); - } - SUSPEND_HEAP_LOCK_END(objspace); - return ret; - } - else { - return FALSE; - } -} - -static enum rb_id_table_iterator_result -cvar_table_free_i(VALUE value, void *ctx) -{ - xfree((void *)value); - return ID_TABLE_CONTINUE; -} - -#define ZOMBIE_OBJ_KEPT_FLAGS (FL_SEEN_OBJ_ID | FL_FINALIZE) - -static inline void -make_zombie(rb_objspace_t *objspace, VALUE obj, void (*dfree)(void *), void *data) -{ - struct RZombie *zombie = RZOMBIE(obj); - zombie->basic.flags = T_ZOMBIE | (zombie->basic.flags & ZOMBIE_OBJ_KEPT_FLAGS); - zombie->dfree = dfree; - zombie->data = data; - VALUE prev, next = heap_pages_deferred_final; - do { - zombie->next = prev = next; - next = RUBY_ATOMIC_VALUE_CAS(heap_pages_deferred_final, prev, obj); - } while (next != prev); - - struct heap_page *page = GET_HEAP_PAGE(obj); - page->final_slots++; - heap_pages_final_slots++; -} - -static inline void -make_io_zombie(rb_objspace_t *objspace, VALUE obj) -{ - rb_io_t *fptr = RANY(obj)->as.file.fptr; - make_zombie(objspace, obj, rb_io_fptr_finalize_internal, fptr); -} - -static void -obj_free_object_id(rb_objspace_t *objspace, VALUE obj) -{ - ASSERT_ractor_safe_gc_state(); - st_data_t o = (st_data_t)obj, id; - - GC_ASSERT(FL_TEST(obj, FL_SEEN_OBJ_ID)); - FL_UNSET(obj, FL_SEEN_OBJ_ID); - if (!delete_from_obj_id_tables(obj, &o, &id)) { - rb_bug("Object ID seen, but not in mapping table: %s", obj_info(obj)); - } -} - -static bool -rb_data_free(rb_objspace_t *objspace, VALUE obj) -{ - void *data = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj); - if (data) { - int free_immediately = false; - void (*dfree)(void *); - - if (RTYPEDDATA_P(obj)) { - free_immediately = (RANY(obj)->as.typeddata.type->flags & RUBY_TYPED_FREE_IMMEDIATELY) != 0; - dfree = RANY(obj)->as.typeddata.type->function.dfree; - } - else { - dfree = RANY(obj)->as.data.dfree; - } - - if (dfree) { - if (dfree == RUBY_DEFAULT_FREE) { - if (!RTYPEDDATA_EMBEDDED_P(obj)) { - xfree(data); - RB_DEBUG_COUNTER_INC(obj_data_xfree); - } - } - else if (free_immediately) { - (*dfree)(data); - if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !RTYPEDDATA_EMBEDDED_P(obj)) { - xfree(data); - } - - RB_DEBUG_COUNTER_INC(obj_data_imm_free); - } - else { - make_zombie(objspace, obj, dfree, data); - RB_DEBUG_COUNTER_INC(obj_data_zombie); - return FALSE; - } - } - else { - RB_DEBUG_COUNTER_INC(obj_data_empty); - } - } - - return true; -} - -static int -obj_free(rb_objspace_t *objspace, VALUE obj) -{ - RB_DEBUG_COUNTER_INC(obj_free); - // RUBY_DEBUG_LOG("obj:%p (%s)", (void *)obj, obj_type_name(obj)); - - gc_event_hook(objspace, RUBY_INTERNAL_EVENT_FREEOBJ, obj); - - switch (BUILTIN_TYPE(obj)) { - case T_NIL: - case T_FIXNUM: - case T_TRUE: - case T_FALSE: - rb_bug("obj_free() called for broken object"); - break; - default: - break; - } - - if (FL_TEST(obj, FL_EXIVAR)) { - rb_free_generic_ivar((VALUE)obj); - FL_UNSET(obj, FL_EXIVAR); - } - - if (FL_TEST(obj, FL_SEEN_OBJ_ID) && !FL_TEST(obj, FL_FINALIZE)) { - obj_free_object_id(objspace, obj); - } - - if (RVALUE_WB_UNPROTECTED(obj)) CLEAR_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(obj), obj); - if (RVALUE_LOCAL_IMMUNE(obj)) { - VM_ASSERT(!using_local_limits(objspace)); - CLEAR_IN_BITMAP(GET_HEAP_LOCAL_IMMUNE_BITS(obj), obj); - } - -#if RGENGC_CHECK_MODE -#define CHECK(x) if (x(obj) != FALSE) rb_bug("obj_free: " #x "(%s) != FALSE", obj_info(obj)) - CHECK(RVALUE_WB_UNPROTECTED); - CHECK(RVALUE_LOCAL_IMMUNE); - CHECK(RVALUE_MARKED); - CHECK(RVALUE_MARKING); - CHECK(RVALUE_UNCOLLECTIBLE); -#undef CHECK -#endif - - switch (BUILTIN_TYPE(obj)) { - case T_OBJECT: - if (rb_shape_obj_too_complex(obj)) { - RB_DEBUG_COUNTER_INC(obj_obj_too_complex); - st_free_table(ROBJECT_IV_HASH(obj)); - } - else if (RANY(obj)->as.basic.flags & ROBJECT_EMBED) { - RB_DEBUG_COUNTER_INC(obj_obj_embed); - } - else { - xfree(RANY(obj)->as.object.as.heap.ivptr); - RB_DEBUG_COUNTER_INC(obj_obj_ptr); - } - break; - case T_MODULE: - case T_CLASS: - rb_id_table_free(RCLASS_M_TBL(obj)); - rb_cc_table_free(obj); - if (rb_shape_obj_too_complex(obj)) { - st_free_table((st_table *)RCLASS_IVPTR(obj)); - } - else { - xfree(RCLASS_IVPTR(obj)); - } - - if (RCLASS_CONST_TBL(obj)) { - rb_free_const_table(RCLASS_CONST_TBL(obj)); - } - if (RCLASS_CVC_TBL(obj)) { - rb_id_table_foreach_values(RCLASS_CVC_TBL(obj), cvar_table_free_i, NULL); - rb_id_table_free(RCLASS_CVC_TBL(obj)); - } - rb_class_remove_subclass_head(obj); - rb_class_remove_from_module_subclasses(obj); - rb_class_remove_from_super_subclasses(obj); - if (FL_TEST_RAW(obj, RCLASS_SUPERCLASSES_INCLUDE_SELF)) { - xfree(RCLASS_SUPERCLASSES(obj)); - } - - (void)RB_DEBUG_COUNTER_INC_IF(obj_module_ptr, BUILTIN_TYPE(obj) == T_MODULE); - (void)RB_DEBUG_COUNTER_INC_IF(obj_class_ptr, BUILTIN_TYPE(obj) == T_CLASS); - break; - case T_STRING: - rb_str_free(obj); - break; - case T_ARRAY: - rb_ary_free(obj); - break; - case T_HASH: -#if USE_DEBUG_COUNTER - switch (RHASH_SIZE(obj)) { - case 0: - RB_DEBUG_COUNTER_INC(obj_hash_empty); - break; - case 1: - RB_DEBUG_COUNTER_INC(obj_hash_1); - break; - case 2: - RB_DEBUG_COUNTER_INC(obj_hash_2); - break; - case 3: - RB_DEBUG_COUNTER_INC(obj_hash_3); - break; - case 4: - RB_DEBUG_COUNTER_INC(obj_hash_4); - break; - case 5: - case 6: - case 7: - case 8: - RB_DEBUG_COUNTER_INC(obj_hash_5_8); - break; - default: - GC_ASSERT(RHASH_SIZE(obj) > 8); - RB_DEBUG_COUNTER_INC(obj_hash_g8); - } - - if (RHASH_AR_TABLE_P(obj)) { - if (RHASH_AR_TABLE(obj) == NULL) { - RB_DEBUG_COUNTER_INC(obj_hash_null); - } - else { - RB_DEBUG_COUNTER_INC(obj_hash_ar); - } - } - else { - RB_DEBUG_COUNTER_INC(obj_hash_st); - } -#endif - - rb_hash_free(obj); - break; - case T_REGEXP: - if (RANY(obj)->as.regexp.ptr) { - onig_free(RANY(obj)->as.regexp.ptr); - RB_DEBUG_COUNTER_INC(obj_regexp_ptr); - } - break; - case T_DATA: - if (!rb_data_free(objspace, obj)) return false; - break; - case T_MATCH: - { - rb_matchext_t *rm = RMATCH_EXT(obj); -#if USE_DEBUG_COUNTER - if (rm->regs.num_regs >= 8) { - RB_DEBUG_COUNTER_INC(obj_match_ge8); - } - else if (rm->regs.num_regs >= 4) { - RB_DEBUG_COUNTER_INC(obj_match_ge4); - } - else if (rm->regs.num_regs >= 1) { - RB_DEBUG_COUNTER_INC(obj_match_under4); - } -#endif - onig_region_free(&rm->regs, 0); - xfree(rm->char_offset); - - RB_DEBUG_COUNTER_INC(obj_match_ptr); - } - break; - case T_FILE: - if (RANY(obj)->as.file.fptr) { - make_io_zombie(objspace, obj); - RB_DEBUG_COUNTER_INC(obj_file_ptr); - return FALSE; - } - break; - case T_RATIONAL: - RB_DEBUG_COUNTER_INC(obj_rational); - break; - case T_COMPLEX: - RB_DEBUG_COUNTER_INC(obj_complex); - break; - case T_MOVED: - break; - case T_ICLASS: - /* Basically , T_ICLASS shares table with the module */ - if (RICLASS_OWNS_M_TBL_P(obj)) { - /* Method table is not shared for origin iclasses of classes */ - rb_id_table_free(RCLASS_M_TBL(obj)); - } - if (RCLASS_CALLABLE_M_TBL(obj) != NULL) { - rb_id_table_free(RCLASS_CALLABLE_M_TBL(obj)); - } - rb_class_remove_subclass_head(obj); - rb_cc_table_free(obj); - rb_class_remove_from_module_subclasses(obj); - rb_class_remove_from_super_subclasses(obj); - - RB_DEBUG_COUNTER_INC(obj_iclass_ptr); - break; - - case T_FLOAT: - RB_DEBUG_COUNTER_INC(obj_float); - break; - - case T_BIGNUM: - if (!BIGNUM_EMBED_P(obj) && BIGNUM_DIGITS(obj)) { - xfree(BIGNUM_DIGITS(obj)); - RB_DEBUG_COUNTER_INC(obj_bignum_ptr); - } - else { - RB_DEBUG_COUNTER_INC(obj_bignum_embed); - } - break; - - case T_NODE: - UNEXPECTED_NODE(obj_free); - break; - - case T_STRUCT: - if ((RBASIC(obj)->flags & RSTRUCT_EMBED_LEN_MASK) || - RANY(obj)->as.rstruct.as.heap.ptr == NULL) { - RB_DEBUG_COUNTER_INC(obj_struct_embed); - } - else { - xfree((void *)RANY(obj)->as.rstruct.as.heap.ptr); - RB_DEBUG_COUNTER_INC(obj_struct_ptr); - } - break; - - case T_SYMBOL: - { - rb_gc_free_dsymbol(obj); - RB_DEBUG_COUNTER_INC(obj_symbol); - } - break; - - case T_IMEMO: - rb_imemo_free((VALUE)obj); - break; - - default: - rb_bug("gc_sweep(): unknown data type 0x%x(%p) 0x%"PRIxVALUE, - BUILTIN_TYPE(obj), (void*)obj, RBASIC(obj)->flags); - } - - if (FL_TEST(obj, FL_FINALIZE)) { - make_zombie(objspace, obj, 0, 0); - return FALSE; - } - else { - RBASIC(obj)->flags = 0; - return TRUE; - } -} - - -#define OBJ_ID_INCREMENT (BASE_SLOT_SIZE) -#define OBJ_ID_INITIAL (OBJ_ID_INCREMENT) - -unsigned long long -rb_get_obj_id_initial(void) -{ - return OBJ_ID_INITIAL; -} - -static int -object_id_cmp(st_data_t x, st_data_t y) -{ - if (RB_BIGNUM_TYPE_P(x)) { - return !rb_big_eql(x, y); - } - else { - return x != y; - } -} - -static st_index_t -object_id_hash(st_data_t n) -{ - if (RB_BIGNUM_TYPE_P(n)) { - return FIX2LONG(rb_big_hash(n)); - } - else { - return st_numhash(n); - } -} - -const struct st_hash_type object_id_hash_type = { - object_id_cmp, - object_id_hash, -}; - -#define LOCAL_SIZE_POOL_STAT_COUNT 7 -void -size_pool_local_stats_init(rb_objspace_t *objspace, int size_pool_idx) -{ - objspace->local_data.size_pool_stats[size_pool_idx].stats = malloc(LOCAL_SIZE_POOL_STAT_COUNT * sizeof(size_t *)); - objspace->local_data.size_pool_stats[size_pool_idx].field_count = LOCAL_SIZE_POOL_STAT_COUNT; - - objspace->local_data.size_pool_stats[size_pool_idx].stats[0] = &size_pools[size_pool_idx].total_allocated_pages; - objspace->local_data.size_pool_stats[size_pool_idx].stats[1] = &size_pools[size_pool_idx].total_freed_pages; - objspace->local_data.size_pool_stats[size_pool_idx].stats[2] = &size_pools[size_pool_idx].force_major_gc_count; - objspace->local_data.size_pool_stats[size_pool_idx].stats[3] = &size_pools[size_pool_idx].force_incremental_marking_finish_count; - objspace->local_data.size_pool_stats[size_pool_idx].stats[4] = &size_pools[size_pool_idx].total_allocated_objects; - objspace->local_data.size_pool_stats[size_pool_idx].stats[5] = &size_pools[size_pool_idx].newly_created_by_borrowing_count; - objspace->local_data.size_pool_stats[size_pool_idx].stats[6] = &size_pools[size_pool_idx].total_freed_objects; -} - -#define LOCAL_GC_STAT_COUNT 7 -void -objspace_local_stats_init(rb_objspace_t *objspace) -{ - objspace->local_data.gc_stats.stats = malloc(LOCAL_GC_STAT_COUNT * sizeof(size_t *)); - objspace->local_data.gc_stats.field_count = LOCAL_GC_STAT_COUNT; - objspace->local_data.gc_stats.stats[0] = &objspace->heap_pages.allocatable_pages; - objspace->local_data.gc_stats.stats[1] = &heap_pages_freeable_pages; - objspace->local_data.gc_stats.stats[2] = &heap_pages_final_slots; - objspace->local_data.gc_stats.stats[3] = &objspace->rgengc.uncollectible_wb_unprotected_objects; - objspace->local_data.gc_stats.stats[4] = &objspace->rgengc.uncollectible_wb_unprotected_objects_limit; - objspace->local_data.gc_stats.stats[5] = &objspace->rgengc.old_objects; - objspace->local_data.gc_stats.stats[6] = &objspace->rgengc.old_objects_limit; - - - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - size_pool_local_stats_init(objspace, i); - } -} - -rb_objspace_t * -objspace_setup(rb_objspace_t *objspace, rb_ractor_t *ractor) -{ - ractor->local_objspace = objspace; - objspace->local_data.ractor = ractor; - - objspace->flags.gc_stressful = RTEST(initial_stress); - objspace->gc_stress_mode = initial_stress; - - objspace->flags.measure_gc = 1; - malloc_limit = gc_params.malloc_limit_min; - objspace->finalize_deferred_pjob = rb_postponed_job_preregister(0, gc_finalize_deferred, NULL); - if (objspace->finalize_deferred_pjob == POSTPONED_JOB_HANDLE_INVALID) { - rb_bug("Could not preregister postponed job for GC"); - } - - // TODO: debug why on Windows Ruby crashes on boot when GC is on. -#ifdef _WIN32 - dont_gc_on(); -#endif - - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - - size_pool->slot_size = (1 << i) * BASE_SLOT_SIZE; - - ccan_list_head_init(&SIZE_POOL_EDEN_HEAP(size_pool)->pages); - ccan_list_head_init(&SIZE_POOL_TOMB_HEAP(size_pool)->pages); - - gc_params.size_pool_init_slots[i] = GC_HEAP_INIT_SLOTS; - - size_pool->allocatable_pages = minimum_pages_for_size_pool(objspace, size_pool); - } - - rb_darray_make(&objspace->weak_references, 0); - -#if defined(INIT_HEAP_PAGE_ALLOC_USE_MMAP) - /* Need to determine if we can use mmap at runtime. */ - heap_page_alloc_use_mmap = INIT_HEAP_PAGE_ALLOC_USE_MMAP; -#endif - -#if RGENGC_ESTIMATE_OLDMALLOC - objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_min; -#endif - - heap_pages_expand_sorted(objspace); - - init_mark_stack(&objspace->mark_stack); - - objspace->profile.invoke_time = getrusage_time(); - - objspace_local_data_init(objspace, &objspace->local_data); - objspace_local_stats_init(objspace); - - return objspace; -} - -static void * -rb_gc_impl_objspace_alloc(void) -{ - rb_objspace_t *objspace = calloc1(sizeof(rb_objspace_t)); - ruby_current_vm_ptr->objspace = objspace; - ruby_single_main_objspace = objspace; - ruby_current_vm_ptr->ractor.main_ractor = rb_ractor_main_alloc(); - return objspace_setup(objspace, ruby_current_vm_ptr->ractor.main_ractor); -} - -void -rb_assign_main_ractor_objspace(rb_ractor_t *ractor) -{ - VM_ASSERT(ractor == ruby_single_main_ractor); - - rb_vm_t *vm = GET_VM(); - vm->objspace->belong_to_single_main_ractor = true; - ractor->local_objspace = vm->objspace; - vm->objspace->local_data.ractor = ractor; -} - -void -rb_create_ractor_local_objspace(rb_ractor_t *ractor) -{ - rb_objspace_t *objspace = calloc1(sizeof(rb_objspace_t)); - objspace_setup(objspace, ractor); - rb_vm_t *vm = GET_VM(); - if (ractor != vm->ractor.main_ractor) { - ruby_single_main_objspace = NULL; - vm->objspace->belong_to_single_main_ractor = false; - } - rb_objspace_gc_enable(ractor->local_objspace); -} - -typedef int each_obj_callback(void *, void *, size_t, void *); -typedef int each_page_callback(struct heap_page *, void *); - -static void objspace_each_objects(rb_objspace_t *objspace, each_obj_callback *callback, void *data, bool protected); -static void objspace_reachable_objects_from_root(rb_objspace_t *, void (func)(const char *, VALUE, void *), void *); - -struct each_obj_data { - rb_objspace_t *objspace; - bool reenable_incremental; - - each_obj_callback *each_obj_callback; - each_page_callback *each_page_callback; - void *data; - - struct heap_page **pages[SIZE_POOL_COUNT]; - size_t pages_counts[SIZE_POOL_COUNT]; - - bool using_borrowable_page[SIZE_POOL_COUNT]; -}; - -static VALUE -objspace_each_objects_ensure(VALUE arg) -{ - struct each_obj_data *data = (struct each_obj_data *)arg; - rb_objspace_t *objspace = data->objspace; - - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - if (data->using_borrowable_page[i]) { - unlock_own_borrowable_page(GET_RACTOR(), i); - } - } - - /* Reenable incremental GC */ - if (data->reenable_incremental) { - objspace->flags.dont_incremental = FALSE; - } - - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - struct heap_page **pages = data->pages[i]; - free(pages); - } - - return Qnil; -} - -static VALUE -objspace_each_objects_try(VALUE arg) -{ - struct each_obj_data *data = (struct each_obj_data *)arg; - rb_objspace_t *objspace = data->objspace; - - /* Copy pages from all size_pools to their respective buffers. */ - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - size_t size = size_mul_or_raise(SIZE_POOL_EDEN_HEAP(size_pool)->total_pages, sizeof(struct heap_page *), rb_eRuntimeError); - - struct heap_page **pages = malloc(size); - if (!pages) rb_memerror(); - - /* Set up pages buffer by iterating over all pages in the current eden - * heap. This will be a snapshot of the state of the heap before we - * call the callback over each page that exists in this buffer. Thus it - * is safe for the callback to allocate objects without possibly entering - * an infinite loop. */ - struct heap_page *page = 0; - size_t pages_count = 0; - ccan_list_for_each(&SIZE_POOL_EDEN_HEAP(size_pool)->pages, page, page_node) { - pages[pages_count] = page; - pages_count++; - } - data->pages[i] = pages; - data->pages_counts[i] = pages_count; - GC_ASSERT(pages_count == SIZE_POOL_EDEN_HEAP(size_pool)->total_pages); - } - - rb_ractor_t *r = GET_RACTOR(); - for (int size_pool_idx = 0; size_pool_idx < SIZE_POOL_COUNT; size_pool_idx++) { - rb_size_pool_t *size_pool = &size_pools[size_pool_idx]; - size_t pages_count = data->pages_counts[size_pool_idx]; - struct heap_page **pages = data->pages[size_pool_idx]; - - struct heap_page *page = ccan_list_top(&SIZE_POOL_EDEN_HEAP(size_pool)->pages, struct heap_page, page_node); - for (size_t i = 0; i < pages_count; i++) { - /* If we have reached the end of the linked list then there are no - * more pages, so break. */ - if (page == NULL) break; - - /* If this page does not match the one in the buffer, then move to - * the next page in the buffer. */ - if (pages[i] != page) continue; - - rb_borrowing_sync_lock(r); - if (current_borrowable_page(r, size_pool_idx) == page) { - lock_own_borrowable_page(r, size_pool_idx); - data->using_borrowable_page[size_pool_idx] = true; - } - rb_borrowing_sync_unlock(r); - - uintptr_t pstart = (uintptr_t)page->start; - uintptr_t pend = pstart + (page->total_slots * size_pool->slot_size); - - if (data->each_obj_callback && - (*data->each_obj_callback)((void *)pstart, (void *)pend, size_pool->slot_size, data->data)) { - break; - } - if (data->each_page_callback && - (*data->each_page_callback)(page, data->data)) { - break; - } - - if (data->using_borrowable_page[size_pool_idx]) { - unlock_own_borrowable_page(r, size_pool_idx); - data->using_borrowable_page[size_pool_idx] = false; - } - - page = ccan_list_next(&SIZE_POOL_EDEN_HEAP(size_pool)->pages, page, page_node); - } - - if (data->using_borrowable_page[size_pool_idx]) { - unlock_own_borrowable_page(r, size_pool_idx); - data->using_borrowable_page[size_pool_idx] = false; - } - } - - return Qnil; -} - -/* - * rb_objspace_each_objects() is special C API to walk through - * Ruby object space. This C API is too difficult to use it. - * To be frank, you should not use it. Or you need to read the - * source code of this function and understand what this function does. - * - * 'callback' will be called several times (the number of heap page, - * at current implementation) with: - * vstart: a pointer to the first living object of the heap_page. - * vend: a pointer to next to the valid heap_page area. - * stride: a distance to next VALUE. - * - * If callback() returns non-zero, the iteration will be stopped. - * - * This is a sample callback code to iterate liveness objects: - * - * static int - * sample_callback(void *vstart, void *vend, int stride, void *data) - * { - * VALUE v = (VALUE)vstart; - * for (; v != (VALUE)vend; v += stride) { - * if (!rb_objspace_internal_object_p(v)) { // liveness check - * // do something with live object 'v' - * } - * } - * return 0; // continue to iteration - * } - * - * Note: 'vstart' is not a top of heap_page. This point the first - * living object to grasp at least one object to avoid GC issue. - * This means that you can not walk through all Ruby object page - * including freed object page. - * - * Note: On this implementation, 'stride' is the same as sizeof(RVALUE). - * However, there are possibilities to pass variable values with - * 'stride' with some reasons. You must use stride instead of - * use some constant value in the iteration. - */ -void -rb_objspace_each_objects(each_obj_callback *callback, void *data) -{ - objspace_each_objects(&rb_objspace, callback, data, TRUE); -} - -static void -objspace_each_exec(bool protected, struct each_obj_data *each_obj_data) -{ - /* Disable incremental GC */ - rb_objspace_t *objspace = each_obj_data->objspace; - bool reenable_incremental = FALSE; - if (protected) { - reenable_incremental = !objspace->flags.dont_incremental; - - gc_rest(objspace); - objspace->flags.dont_incremental = TRUE; - } - - each_obj_data->reenable_incremental = reenable_incremental; - memset(&each_obj_data->pages, 0, sizeof(each_obj_data->pages)); - memset(&each_obj_data->pages_counts, 0, sizeof(each_obj_data->pages_counts)); - rb_ensure(objspace_each_objects_try, (VALUE)each_obj_data, - objspace_each_objects_ensure, (VALUE)each_obj_data); -} - -static void -objspace_each_objects(rb_objspace_t *objspace, each_obj_callback *callback, void *data, bool protected) -{ - struct each_obj_data each_obj_data = { - .objspace = objspace, - .each_obj_callback = callback, - .each_page_callback = NULL, - .data = data, - }; - objspace_each_exec(protected, &each_obj_data); -} - -#if GC_CAN_COMPILE_COMPACTION -static void -objspace_each_pages(rb_objspace_t *objspace, each_page_callback *callback, void *data, bool protected) -{ - struct each_obj_data each_obj_data = { - .objspace = objspace, - .each_obj_callback = NULL, - .each_page_callback = callback, - .data = data, - }; - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - each_obj_data.using_borrowable_page[i] = false; - } - objspace_each_exec(protected, &each_obj_data); -} -#endif - -struct os_each_struct { - size_t num; - VALUE of; -}; - -static int -internal_object_p(VALUE obj) -{ - RVALUE *p = (RVALUE *)obj; - void *ptr = asan_unpoison_object_temporary(obj); - bool used_p = p->as.basic.flags; - - if (used_p) { - switch (BUILTIN_TYPE(obj)) { - case T_NODE: - UNEXPECTED_NODE(internal_object_p); - break; - case T_NONE: - case T_MOVED: - case T_IMEMO: - case T_ICLASS: - case T_ZOMBIE: - break; - case T_CLASS: - if (!p->as.basic.klass) break; - if (RCLASS_SINGLETON_P(obj)) { - return rb_singleton_class_internal_p(obj); - } - return 0; - default: - if (!p->as.basic.klass) break; - return 0; - } - } - if (ptr || ! used_p) { - asan_poison_object(obj); - } - return 1; -} - -int -rb_objspace_internal_object_p(VALUE obj) -{ - return internal_object_p(obj); -} - -static int -os_obj_of_i(void *vstart, void *vend, size_t stride, void *data) -{ - struct os_each_struct *oes = (struct os_each_struct *)data; - - VALUE v = (VALUE)vstart; - for (; v != (VALUE)vend; v += stride) { - if (!internal_object_p(v)) { - if (!oes->of || rb_obj_is_kind_of(v, oes->of)) { - rb_yield(v); - oes->num++; - } - } - } - - return 0; -} - -static VALUE -os_obj_of(VALUE of) -{ - struct os_each_struct oes; - - oes.num = 0; - oes.of = of; - rb_objspace_each_objects(os_obj_of_i, &oes); - return SIZET2NUM(oes.num); -} - -/* - * call-seq: - * ObjectSpace.each_object([module]) {|obj| ... } -> integer - * ObjectSpace.each_object([module]) -> an_enumerator - * - * Calls the block once for each living, nonimmediate object in this - * Ruby process. If module is specified, calls the block - * for only those classes or modules that match (or are a subclass of) - * module. Returns the number of objects found. Immediate - * objects (Fixnums, Symbols - * true, false, and nil) are - * never returned. In the example below, #each_object returns both - * the numbers we defined and several constants defined in the Math - * module. - * - * If no block is given, an enumerator is returned instead. - * - * a = 102.7 - * b = 95 # Won't be returned - * c = 12345678987654321 - * count = ObjectSpace.each_object(Numeric) {|x| p x } - * puts "Total count: #{count}" - * - * produces: - * - * 12345678987654321 - * 102.7 - * 2.71828182845905 - * 3.14159265358979 - * 2.22044604925031e-16 - * 1.7976931348623157e+308 - * 2.2250738585072e-308 - * Total count: 7 - * - */ - -static VALUE -os_each_obj(int argc, VALUE *argv, VALUE os) -{ - VALUE of; - - of = (!rb_check_arity(argc, 0, 1) ? 0 : argv[0]); - RETURN_ENUMERATOR(os, 1, &of); - return os_obj_of(of); -} - -static void -should_belong_to_current_objspace(VALUE obj) -{ - if (GET_OBJSPACE_OF_VALUE(obj) != &rb_objspace) { - rb_raise(rb_eRactorIsolationError, "cannot define or undefine finalizers for objects belonging to another Ractor"); - } -} - -/* - * call-seq: - * ObjectSpace.undefine_finalizer(obj) - * - * Removes all finalizers for obj. - * - */ - -static VALUE -undefine_final(VALUE os, VALUE obj) -{ - return rb_undefine_finalizer(obj); -} - -VALUE -rb_undefine_finalizer(VALUE obj) -{ - should_belong_to_current_objspace(obj); - rb_objspace_t *objspace = &rb_objspace; - st_data_t data = obj; - rb_check_frozen(obj); - st_delete(finalizer_table, &data, 0); - FL_UNSET(obj, FL_FINALIZE); - return obj; -} - -static void -should_be_callable(VALUE block) -{ - if (!rb_obj_respond_to(block, idCall, TRUE)) { - rb_raise(rb_eArgError, "wrong type argument %"PRIsVALUE" (should be callable)", - rb_obj_class(block)); - } -} - -static void -should_be_finalizable(VALUE obj) -{ - if (!FL_ABLE(obj)) { - rb_raise(rb_eArgError, "cannot define finalizer for %s", - rb_obj_classname(obj)); - } - rb_check_frozen(obj); -} - -static VALUE -rb_define_finalizer_no_check(VALUE obj, VALUE block) -{ - rb_objspace_t *objspace = &rb_objspace; - VALUE table; - st_data_t data; - - RBASIC(obj)->flags |= FL_FINALIZE; - - if (st_lookup(finalizer_table, obj, &data)) { - table = (VALUE)data; - - /* avoid duplicate block, table is usually small */ - { - long len = RARRAY_LEN(table); - long i; - - for (i = 0; i < len; i++) { - VALUE recv = RARRAY_AREF(table, i); - if (rb_equal(recv, block)) { - block = recv; - goto end; - } - } - } - - rb_ary_push(table, block); - } - else { - table = rb_ary_new3(1, block); - FL_SET_RAW(table, RUBY_FL_SHAREABLE); //TODO: Protect table from data races - RBASIC_CLEAR_CLASS(table); - st_add_direct(finalizer_table, obj, table); - } - end: - block = rb_ary_new3(2, INT2FIX(0), block); - OBJ_FREEZE(block); - return block; -} - -/* - * call-seq: - * ObjectSpace.define_finalizer(obj, aProc=proc()) - * - * Adds aProc as a finalizer, to be called after obj - * was destroyed. The object ID of the obj will be passed - * as an argument to aProc. If aProc is a lambda or - * method, make sure it can be called with a single argument. - * - * The return value is an array [0, aProc]. - * - * The two recommended patterns are to either create the finaliser proc - * in a non-instance method where it can safely capture the needed state, - * or to use a custom callable object that stores the needed state - * explicitly as instance variables. - * - * class Foo - * def initialize(data_needed_for_finalization) - * ObjectSpace.define_finalizer(self, self.class.create_finalizer(data_needed_for_finalization)) - * end - * - * def self.create_finalizer(data_needed_for_finalization) - * proc { - * puts "finalizing #{data_needed_for_finalization}" - * } - * end - * end - * - * class Bar - * class Remover - * def initialize(data_needed_for_finalization) - * @data_needed_for_finalization = data_needed_for_finalization - * end - * - * def call(id) - * puts "finalizing #{@data_needed_for_finalization}" - * end - * end - * - * def initialize(data_needed_for_finalization) - * ObjectSpace.define_finalizer(self, Remover.new(data_needed_for_finalization)) - * end - * end - * - * Note that if your finalizer references the object to be - * finalized it will never be run on GC, although it will still be - * run at exit. You will get a warning if you capture the object - * to be finalized as the receiver of the finalizer. - * - * class CapturesSelf - * def initialize(name) - * ObjectSpace.define_finalizer(self, proc { - * # this finalizer will only be run on exit - * puts "finalizing #{name}" - * }) - * end - * end - * - * Also note that finalization can be unpredictable and is never guaranteed - * to be run except on exit. - */ - -static VALUE -define_final(int argc, VALUE *argv, VALUE os) -{ - VALUE obj, block; - - rb_scan_args(argc, argv, "11", &obj, &block); - should_be_finalizable(obj); - should_belong_to_current_objspace(obj); - if (argc == 1) { - block = rb_block_proc(); - } - else { - should_be_callable(block); - } - - if (rb_callable_receiver(block) == obj) { - rb_warn("finalizer references object to be finalized"); - } - - return rb_define_finalizer_no_check(obj, block); -} - -VALUE -rb_define_finalizer(VALUE obj, VALUE block) -{ - should_be_finalizable(obj); - should_be_callable(block); - should_belong_to_current_objspace(obj); - return rb_define_finalizer_no_check(obj, block); -} - -void -rb_gc_copy_finalizer(VALUE dest, VALUE obj) -{ - rb_objspace_t *objspace = &rb_objspace; - VALUE table; - st_data_t data; - - if (!FL_TEST(obj, FL_FINALIZE)) return; - - if (RB_LIKELY(st_lookup(finalizer_table, obj, &data))) { - table = (VALUE)data; - WITH_OBJSPACE_OF_VALUE_ENTER(dest, objspace); - { - st_insert(finalizer_table, dest, table); - FL_SET(dest, FL_FINALIZE); - } - WITH_OBJSPACE_OF_VALUE_LEAVE(objspace); - } - else { - rb_bug("rb_gc_copy_finalizer: FL_FINALIZE set but not found in finalizer_table: %s", obj_info(obj)); - } -} - -static VALUE -run_single_final(VALUE cmd, VALUE objid) -{ - return rb_check_funcall(cmd, idCall, 1, &objid); -} - -static void -warn_exception_in_finalizer(rb_execution_context_t *ec, VALUE final) -{ - if (!UNDEF_P(final) && !NIL_P(ruby_verbose)) { - VALUE errinfo = ec->errinfo; - rb_warn("Exception in finalizer %+"PRIsVALUE, final); - rb_ec_error_print(ec, errinfo); - } -} - -static void -run_finalizer(rb_objspace_t *objspace, VALUE obj, VALUE table) -{ - long i; - enum ruby_tag_type state; - volatile struct { - VALUE errinfo; - VALUE objid; - VALUE final; - rb_control_frame_t *cfp; - VALUE *sp; - long finished; - } saved; - - rb_execution_context_t * volatile ec = GET_EC(); -#define RESTORE_FINALIZER() (\ - ec->cfp = saved.cfp, \ - ec->cfp->sp = saved.sp, \ - ec->errinfo = saved.errinfo) - - saved.errinfo = ec->errinfo; - saved.objid = rb_obj_id(obj); - saved.cfp = ec->cfp; - saved.sp = ec->cfp->sp; - saved.finished = 0; - saved.final = Qundef; - - EC_PUSH_TAG(ec); - state = EC_EXEC_TAG(); - if (state != TAG_NONE) { - ++saved.finished; /* skip failed finalizer */ - warn_exception_in_finalizer(ec, ATOMIC_VALUE_EXCHANGE(saved.final, Qundef)); - } - for (i = saved.finished; - RESTORE_FINALIZER(), idfree) { - RZOMBIE(zombie)->dfree(RZOMBIE(zombie)->data); - } - - st_data_t key = (st_data_t)zombie; - if (FL_TEST_RAW(zombie, FL_FINALIZE)) { - FL_UNSET(zombie, FL_FINALIZE); - st_data_t table; - if (st_delete(finalizer_table, &key, &table)) { - run_finalizer(objspace, zombie, (VALUE)table); - } - else { - rb_bug("FL_FINALIZE flag is set, but finalizers are not found"); - } - } - else { - GC_ASSERT(!st_lookup(finalizer_table, key, NULL)); - } -} - -static void -finalize_list(rb_objspace_t *objspace, VALUE zombie) -{ - while (zombie) { - VALUE next_zombie; - struct heap_page *page; - asan_unpoison_object(zombie, false); - next_zombie = RZOMBIE(zombie)->next; - page = GET_HEAP_PAGE(zombie); - - run_final(objspace, zombie); - - HEAP_LOCK_ENTER(objspace); - { - GC_ASSERT(BUILTIN_TYPE(zombie) == T_ZOMBIE); - if (FL_TEST(zombie, FL_SEEN_OBJ_ID)) { - obj_free_object_id(objspace, zombie); - } - - GC_ASSERT(heap_pages_final_slots > 0); - GC_ASSERT(page->final_slots > 0); - - heap_pages_final_slots--; - page->final_slots--; - page->free_slots++; - heap_page_add_freeobj(objspace, page, zombie); - page->size_pool->total_freed_objects++; - } - HEAP_LOCK_LEAVE(objspace); - - zombie = next_zombie; - } -} - -static void -finalize_deferred_heap_pages(rb_objspace_t *objspace) -{ - SUSPEND_HEAP_LOCK_BEGIN(objspace); - { - VALUE zombie; - while ((zombie = ATOMIC_VALUE_EXCHANGE(heap_pages_deferred_final, 0)) != 0) { - finalize_list(objspace, zombie); - } - } - SUSPEND_HEAP_LOCK_END(objspace); -} - -static void -finalize_deferred(rb_objspace_t *objspace) -{ - rb_execution_context_t *ec = GET_EC(); - ec->interrupt_mask |= PENDING_INTERRUPT_MASK; - finalize_deferred_heap_pages(objspace); - ec->interrupt_mask &= ~PENDING_INTERRUPT_MASK; -} - -static void -gc_finalize_deferred(void *dmy) -{ - rb_objspace_t *objspace = &rb_objspace; - if (!objspace || ATOMIC_EXCHANGE(finalizing, 1)) return; - - finalize_deferred(objspace); - ATOMIC_SET(finalizing, 0); -} - -static void -gc_finalize_deferred_register(rb_objspace_t *objspace) -{ - /* will enqueue a call to gc_finalize_deferred */ - rb_postponed_job_trigger(objspace->finalize_deferred_pjob); -} - -static int pop_mark_stack(mark_stack_t *stack, VALUE *data); - -static void -gc_abort(rb_objspace_t *objspace) -{ - if (is_incremental_marking(objspace)) { - /* Remove all objects from the mark stack. */ - VALUE obj; - while (pop_mark_stack(&objspace->mark_stack, &obj)); - - objspace->flags.during_incremental_marking = FALSE; - } - - if (is_lazy_sweeping(objspace)) { - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); - - heap->sweeping_page = NULL; - struct heap_page *page = NULL; - - ccan_list_for_each(&heap->pages, page, page_node) { - page->flags.before_sweep = false; - } - } - } - - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); - rgengc_mark_and_rememberset_clear(objspace, heap); - } - - gc_mode_set(objspace, gc_mode_none); -} - -struct force_finalize_list { - VALUE obj; - VALUE table; - struct force_finalize_list *next; -}; - -static int -force_chain_object(st_data_t key, st_data_t val, st_data_t arg) -{ - struct force_finalize_list **prev = (struct force_finalize_list **)arg; - struct force_finalize_list *curr = ALLOC(struct force_finalize_list); - curr->obj = key; - curr->table = val; - curr->next = *prev; - *prev = curr; - return ST_CONTINUE; -} - -static int -get_size_pool_idx(rb_objspace_t *objspace, rb_size_pool_t *size_pool) -{ - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - if(&size_pools[i] == size_pool) { - return i; - } - } - return -1; -} - -static void -gc_each_object(rb_objspace_t *objspace, void (*func)(VALUE obj, void *data), void *data) -{ - rb_ractor_t *r = GET_RACTOR(); - for (size_t i = 0; i < heap_allocated_pages; i++) { - struct heap_page *page = heap_pages_sorted[i]; - - int size_pool_idx = get_size_pool_idx(objspace, page->size_pool); - bool using_borrowable_page = false; - if (!objspace->freeing_all) { - rb_borrowing_sync_lock(r); - if (current_borrowable_page(r, size_pool_idx) == page) { - lock_own_borrowable_page(r, size_pool_idx); - using_borrowable_page = true; - } - rb_borrowing_sync_unlock(r); - } - - short stride = page->slot_size; - - uintptr_t p = (uintptr_t)page->start; - uintptr_t pend = p + page->total_slots * stride; - for (; p < pend; p += stride) { - VALUE obj = (VALUE)p; - - void *poisoned = asan_unpoison_object_temporary(obj); - - func(obj, data); - - if (poisoned) { - GC_ASSERT(BUILTIN_TYPE(obj) == T_NONE); - asan_poison_object(obj); - } - } - - if (using_borrowable_page) { - unlock_own_borrowable_page(r, size_pool_idx); - } - } -} - -bool rb_obj_is_main_ractor(VALUE gv); - -static void -rb_objspace_free_objects_i(VALUE obj, void *data) -{ - rb_objspace_t *objspace = (rb_objspace_t *)data; - - if (BUILTIN_TYPE(obj) != T_NONE) { - obj_free(objspace, obj); - } -} - -void -rb_objspace_free_objects(rb_objspace_t *objspace) -{ - objspace->freeing_all = true; - gc_each_object(objspace, rb_objspace_free_objects_i, objspace); - objspace->freeing_all = false; -} - -static void -rb_objspace_call_finalizer_i(VALUE obj, void *data) -{ - rb_objspace_t *objspace = (rb_objspace_t *)data; - - switch (BUILTIN_TYPE(obj)) { - case T_DATA: - if (!rb_free_at_exit && (!DATA_PTR(obj) || !RANY(obj)->as.data.dfree)) break; - if (rb_obj_is_thread(obj)) break; - if (rb_obj_is_mutex(obj)) break; - if (rb_obj_is_fiber(obj)) break; - if (rb_obj_is_main_ractor(obj)) break; - - obj_free(objspace, obj); - break; - case T_FILE: - obj_free(objspace, obj); - break; - case T_SYMBOL: - if (rb_free_at_exit) { - if (RSYMBOL(obj)->fstr && - (BUILTIN_TYPE(RSYMBOL(obj)->fstr) == T_NONE || - BUILTIN_TYPE(RSYMBOL(obj)->fstr) == T_ZOMBIE)) { - RSYMBOL(obj)->fstr = 0; - } - - obj_free(objspace, obj); - } - break; - case T_NONE: - break; - default: - if (rb_free_at_exit) { - obj_free(objspace, obj); - } - break; - } -} - -void -rb_objspace_call_finalizer(rb_objspace_t *objspace) -{ -#if RGENGC_CHECK_MODE >= 2 - gc_verify_internal_consistency(objspace); -#endif - if (ATOMIC_EXCHANGE(finalizing, 1)) return; - - /* run finalizers */ - finalize_deferred(objspace); - GC_ASSERT(heap_pages_deferred_final == 0); - - /* prohibit incremental GC */ - objspace->flags.dont_incremental = 1; - - /* force to run finalizer */ - while (finalizer_table->num_entries) { - struct force_finalize_list *list = 0; - st_foreach(finalizer_table, force_chain_object, (st_data_t)&list); - while (list) { - struct force_finalize_list *curr = list; - - st_data_t obj = (st_data_t)curr->obj; - st_delete(finalizer_table, &obj, 0); - FL_UNSET(curr->obj, FL_FINALIZE); - - run_finalizer(objspace, curr->obj, curr->table); - - list = curr->next; - xfree(curr); - } - } - - /* Abort incremental marking and lazy sweeping to speed up shutdown. */ - gc_abort(objspace); - - /* prohibit GC because force T_DATA finalizers can break an object graph consistency */ - dont_gc_on(); - - /* running data/file finalizers are part of garbage collection */ - LOCAL_GC_BEGIN(objspace); - { - gc_enter(objspace, gc_enter_event_finalizer); - - gc_each_object(objspace, rb_objspace_call_finalizer_i, objspace); - - gc_exit(objspace, gc_enter_event_finalizer); - } - LOCAL_GC_END(objspace); - - finalize_deferred_heap_pages(objspace); - - st_free_table(finalizer_table); - finalizer_table = 0; - ATOMIC_SET(finalizing, 0); -} - -/* garbage objects will be collected soon. */ -static inline bool -is_garbage_object(rb_objspace_t *objspace, VALUE ptr) -{ - return is_lazy_sweeping(objspace) && GET_HEAP_PAGE(ptr)->flags.before_sweep && - !RVALUE_MARKED(ptr); -} - -static inline bool -is_live_object(rb_objspace_t *objspace, VALUE ptr) -{ - switch (BUILTIN_TYPE(ptr)) { - case T_NONE: - case T_MOVED: - case T_ZOMBIE: - return FALSE; - default: - break; - } - - return !is_garbage_object(objspace, ptr); -} - -static inline int -is_markable_object(VALUE obj) -{ - return !RB_SPECIAL_CONST_P(obj); -} - -int -rb_objspace_markable_object_p(VALUE obj) -{ - rb_objspace_t *objspace = &rb_objspace; - return is_markable_object(obj) && is_live_object(objspace, obj); -} - -int -rb_objspace_garbage_object_p(VALUE obj) -{ - rb_objspace_t *objspace = &rb_objspace; - return is_garbage_object(objspace, obj); -} - -bool -rb_gc_is_ptr_to_obj(const void *ptr) -{ - bool ret; - WITH_OBJSPACE_OF_VALUE_ENTER((VALUE)ptr, objspace); - { - ret = is_pointer_to_heap(objspace, ptr); - } - WITH_OBJSPACE_OF_VALUE_LEAVE(objspace); - return ret; -} - -/* - * call-seq: - * ObjectSpace._id2ref(object_id) -> an_object - * - * Converts an object id to a reference to the object. May not be - * called on an object id passed as a parameter to a finalizer. - * - * s = "I am a string" #=> "I am a string" - * r = ObjectSpace._id2ref(s.object_id) #=> "I am a string" - * r == s #=> true - * - * On multi-ractor mode, if the object is not shareable, it raises - * RangeError. - */ - -static VALUE -id2ref(VALUE objid) -{ -#if SIZEOF_LONG == SIZEOF_VOIDP -#define NUM2PTR(x) NUM2ULONG(x) -#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP -#define NUM2PTR(x) NUM2ULL(x) -#endif - rb_objspace_t *objspace = &rb_objspace; - - objid = rb_to_int(objid); - if (FIXNUM_P(objid) || rb_big_size(objid) <= SIZEOF_VOIDP) { - VALUE ptr = NUM2PTR(objid); - if (SPECIAL_CONST_P(ptr)) { - if (ptr == Qtrue) return Qtrue; - if (ptr == Qfalse) return Qfalse; - if (NIL_P(ptr)) return Qnil; - if (FIXNUM_P(ptr)) return ptr; - if (FLONUM_P(ptr)) return ptr; - - if (SYMBOL_P(ptr)) { - // Check that the symbol is valid - if (rb_static_id_valid_p(SYM2ID(ptr))) { - return ptr; - } - else { - rb_raise(rb_eRangeError, "%p is not symbol id value", (void *)ptr); - } - } - - rb_raise(rb_eRangeError, "%+"PRIsVALUE" is not id value", rb_int2str(objid, 10)); - } - } - - VALUE orig; - if (!UNDEF_P(orig = rb_gc_id2ref_obj_tbl(objid)) && - is_live_object(objspace, orig)) { - if (GET_OBJSPACE_OF_VALUE(orig) == objspace || rb_ractor_shareable_p(orig)) { - return orig; - } - else { - rb_raise(rb_eRangeError, "%+"PRIsVALUE" is the id of an unshareable object belonging to another Ractor", rb_int2str(objid, 10)); - } - } - - if (rb_nonexistent_id(objid)) { - rb_raise(rb_eRangeError, "%+"PRIsVALUE" is not id value", rb_int2str(objid, 10)); - } - else { - rb_raise(rb_eRangeError, "%+"PRIsVALUE" is recycled object", rb_int2str(objid, 10)); - } -} - -/* :nodoc: */ -static VALUE -os_id2ref(VALUE os, VALUE objid) -{ - return id2ref(objid); -} - -static VALUE -rb_find_object_id(VALUE obj, VALUE (*get_heap_object_id)(VALUE)) -{ - if (SPECIAL_CONST_P(obj)) { -#if SIZEOF_LONG == SIZEOF_VOIDP - return LONG2NUM((SIGNED_VALUE)obj); -#else - return LL2NUM((SIGNED_VALUE)obj); -#endif - } - - return get_heap_object_id(obj); -} - -static VALUE -cached_object_id(VALUE obj) -{ - VALUE id; - - WITH_OBJSPACE_OF_VALUE_ENTER(obj, objspace); - rb_native_mutex_lock(&objspace->local_data.obj_id_lock); - if (st_lookup(objspace->local_data.obj_to_id_tbl, (st_data_t)obj, &id)) { - GC_ASSERT(FL_TEST(obj, FL_SEEN_OBJ_ID)); - } - else { - GC_ASSERT(!FL_TEST(obj, FL_SEEN_OBJ_ID)); - id = retrieve_next_obj_id(OBJ_ID_INCREMENT); - - VALUE already_disabled = gc_disable_no_rest(objspace); - st_insert(objspace->local_data.obj_to_id_tbl, (st_data_t)obj, (st_data_t)id); - st_insert(objspace->local_data.id_to_obj_tbl, (st_data_t)id, (st_data_t)obj); - if (already_disabled == Qfalse) rb_objspace_gc_enable(objspace); - FL_SET(obj, FL_SEEN_OBJ_ID); - } - rb_native_mutex_unlock(&objspace->local_data.obj_id_lock); - WITH_OBJSPACE_OF_VALUE_LEAVE(objspace); - - return id; -} - -static VALUE -nonspecial_obj_id(VALUE obj) -{ -#if SIZEOF_LONG == SIZEOF_VOIDP - return (VALUE)((SIGNED_VALUE)(obj)|FIXNUM_FLAG); -#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP - return LL2NUM((SIGNED_VALUE)(obj) / 2); -#else -# error not supported -#endif -} - -VALUE -rb_memory_id(VALUE obj) -{ - return rb_find_object_id(obj, nonspecial_obj_id); -} - -/* - * Document-method: __id__ - * Document-method: object_id - * - * call-seq: - * obj.__id__ -> integer - * obj.object_id -> integer - * - * Returns an integer identifier for +obj+. - * - * The same number will be returned on all calls to +object_id+ for a given - * object, and no two active objects will share an id. - * - * Note: that some objects of builtin classes are reused for optimization. - * This is the case for immediate values and frozen string literals. - * - * BasicObject implements +__id__+, Kernel implements +object_id+. - * - * Immediate values are not passed by reference but are passed by value: - * +nil+, +true+, +false+, Fixnums, Symbols, and some Floats. - * - * Object.new.object_id == Object.new.object_id # => false - * (21 * 2).object_id == (21 * 2).object_id # => true - * "hello".object_id == "hello".object_id # => false - * "hi".freeze.object_id == "hi".freeze.object_id # => true - */ - -VALUE -rb_obj_id(VALUE obj) -{ - /* - * 32-bit VALUE space - * MSB ------------------------ LSB - * false 00000000000000000000000000000000 - * true 00000000000000000000000000000010 - * nil 00000000000000000000000000000100 - * undef 00000000000000000000000000000110 - * symbol ssssssssssssssssssssssss00001110 - * object oooooooooooooooooooooooooooooo00 = 0 (mod sizeof(RVALUE)) - * fixnum fffffffffffffffffffffffffffffff1 - * - * object_id space - * LSB - * false 00000000000000000000000000000000 - * true 00000000000000000000000000000010 - * nil 00000000000000000000000000000100 - * undef 00000000000000000000000000000110 - * symbol 000SSSSSSSSSSSSSSSSSSSSSSSSSSS0 S...S % A = 4 (S...S = s...s * A + 4) - * object oooooooooooooooooooooooooooooo0 o...o % A = 0 - * fixnum fffffffffffffffffffffffffffffff1 bignum if required - * - * where A = sizeof(RVALUE)/4 - * - * sizeof(RVALUE) is - * 20 if 32-bit, double is 4-byte aligned - * 24 if 32-bit, double is 8-byte aligned - * 40 if 64-bit - */ - - return rb_find_object_id(obj, cached_object_id); -} - -static enum rb_id_table_iterator_result -cc_table_memsize_i(VALUE ccs_ptr, void *data_ptr) -{ - size_t *total_size = data_ptr; - struct rb_class_cc_entries *ccs = (struct rb_class_cc_entries *)ccs_ptr; - *total_size += sizeof(*ccs); - *total_size += sizeof(ccs->entries[0]) * RUBY_ATOMIC_LOAD(ccs->capa); - return ID_TABLE_CONTINUE; -} - -static size_t -cc_table_memsize(struct rb_id_table *cc_table) -{ - size_t total = rb_id_table_memsize(cc_table); - rb_id_table_foreach_values(cc_table, cc_table_memsize_i, &total); - return total; -} - -static size_t -obj_memsize_of(VALUE obj, int use_all_types) -{ - size_t size = 0; - - if (SPECIAL_CONST_P(obj)) { - return 0; - } - - if (FL_TEST(obj, FL_EXIVAR)) { - size += rb_generic_ivar_memsize(obj); - } - - switch (BUILTIN_TYPE(obj)) { - case T_OBJECT: - if (rb_shape_obj_too_complex(obj)) { - size += rb_st_memsize(ROBJECT_IV_HASH(obj)); - } - else if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) { - size += ROBJECT_IV_CAPACITY(obj) * sizeof(VALUE); - } - break; - case T_MODULE: - case T_CLASS: - if (RCLASS_M_TBL(obj)) { - size += rb_id_table_memsize(RCLASS_M_TBL(obj)); - } - // class IV sizes are allocated as powers of two - size += SIZEOF_VALUE << bit_length(RCLASS_IV_COUNT(obj)); - if (RCLASS_CVC_TBL(obj)) { - size += rb_id_table_memsize(RCLASS_CVC_TBL(obj)); - } - if (RCLASS_EXT(obj)->const_tbl) { - size += rb_id_table_memsize(RCLASS_EXT(obj)->const_tbl); - } - if (RCLASS_CC_TBL(obj)) { - size += cc_table_memsize(RCLASS_CC_TBL(obj)); - } - if (FL_TEST_RAW(obj, RCLASS_SUPERCLASSES_INCLUDE_SELF)) { - size += (RCLASS_SUPERCLASS_DEPTH(obj) + 1) * sizeof(VALUE); - } - break; - case T_ICLASS: - if (RICLASS_OWNS_M_TBL_P(obj)) { - if (RCLASS_M_TBL(obj)) { - size += rb_id_table_memsize(RCLASS_M_TBL(obj)); - } - } - if (RCLASS_CC_TBL(obj)) { - size += cc_table_memsize(RCLASS_CC_TBL(obj)); - } - break; - case T_STRING: - size += rb_str_memsize(obj); - break; - case T_ARRAY: - size += rb_ary_memsize(obj); - break; - case T_HASH: - if (RHASH_ST_TABLE_P(obj)) { - VM_ASSERT(RHASH_ST_TABLE(obj) != NULL); - /* st_table is in the slot */ - size += st_memsize(RHASH_ST_TABLE(obj)) - sizeof(st_table); - } - break; - case T_REGEXP: - if (RREGEXP_PTR(obj)) { - size += onig_memsize(RREGEXP_PTR(obj)); - } - break; - case T_DATA: - if (use_all_types) size += rb_objspace_data_type_memsize(obj); - break; - case T_MATCH: - { - rb_matchext_t *rm = RMATCH_EXT(obj); - size += onig_region_memsize(&rm->regs); - size += sizeof(struct rmatch_offset) * rm->char_offset_num_allocated; - } - break; - case T_FILE: - if (RFILE(obj)->fptr) { - size += rb_io_memsize(RFILE(obj)->fptr); - } - break; - case T_RATIONAL: - case T_COMPLEX: - break; - case T_IMEMO: - size += rb_imemo_memsize(obj); - break; - - case T_FLOAT: - case T_SYMBOL: - break; - - case T_BIGNUM: - if (!(RBASIC(obj)->flags & BIGNUM_EMBED_FLAG) && BIGNUM_DIGITS(obj)) { - size += BIGNUM_LEN(obj) * sizeof(BDIGIT); - } - break; - - case T_NODE: - UNEXPECTED_NODE(obj_memsize_of); - break; - - case T_STRUCT: - if ((RBASIC(obj)->flags & RSTRUCT_EMBED_LEN_MASK) == 0 && - RSTRUCT(obj)->as.heap.ptr) { - size += sizeof(VALUE) * RSTRUCT_LEN(obj); - } - break; - - case T_ZOMBIE: - case T_MOVED: - break; - - default: - rb_bug("objspace/memsize_of(): unknown data type 0x%x(%p)", - BUILTIN_TYPE(obj), (void*)obj); - } - - return size + rb_gc_obj_slot_size(obj); -} - -size_t -rb_obj_memsize_of(VALUE obj) -{ - return obj_memsize_of(obj, TRUE); -} - -static int -set_zero(st_data_t key, st_data_t val, st_data_t arg) -{ - VALUE k = (VALUE)key; - VALUE hash = (VALUE)arg; - rb_hash_aset(hash, k, INT2FIX(0)); - return ST_CONTINUE; -} - -static VALUE -type_sym(size_t type) -{ - switch (type) { -#define COUNT_TYPE(t) case (t): return ID2SYM(rb_intern(#t)); break; - COUNT_TYPE(T_NONE); - COUNT_TYPE(T_OBJECT); - COUNT_TYPE(T_CLASS); - COUNT_TYPE(T_MODULE); - COUNT_TYPE(T_FLOAT); - COUNT_TYPE(T_STRING); - COUNT_TYPE(T_REGEXP); - COUNT_TYPE(T_ARRAY); - COUNT_TYPE(T_HASH); - COUNT_TYPE(T_STRUCT); - COUNT_TYPE(T_BIGNUM); - COUNT_TYPE(T_FILE); - COUNT_TYPE(T_DATA); - COUNT_TYPE(T_MATCH); - COUNT_TYPE(T_COMPLEX); - COUNT_TYPE(T_RATIONAL); - COUNT_TYPE(T_NIL); - COUNT_TYPE(T_TRUE); - COUNT_TYPE(T_FALSE); - COUNT_TYPE(T_SYMBOL); - COUNT_TYPE(T_FIXNUM); - COUNT_TYPE(T_IMEMO); - COUNT_TYPE(T_UNDEF); - COUNT_TYPE(T_NODE); - COUNT_TYPE(T_ICLASS); - COUNT_TYPE(T_ZOMBIE); - COUNT_TYPE(T_MOVED); -#undef COUNT_TYPE - default: return SIZET2NUM(type); break; - } -} - -struct count_objects_data { - size_t counts[T_MASK+1]; - size_t freed; - size_t total; -}; - -static void -count_objects_i(VALUE obj, void *d) -{ - struct count_objects_data *data = (struct count_objects_data *)d; - - if (RANY(obj)->as.basic.flags) { - data->counts[BUILTIN_TYPE(obj)]++; - } - else { - data->freed++; - } - - data->total++; -} - -/* - * call-seq: - * ObjectSpace.count_objects([result_hash]) -> hash - * - * Counts all objects grouped by type. - * - * It returns a hash, such as: - * { - * :TOTAL=>10000, - * :FREE=>3011, - * :T_OBJECT=>6, - * :T_CLASS=>404, - * # ... - * } - * - * The contents of the returned hash are implementation specific. - * It may be changed in future. - * - * The keys starting with +:T_+ means live objects. - * For example, +:T_ARRAY+ is the number of arrays. - * +:FREE+ means object slots which is not used now. - * +:TOTAL+ means sum of above. - * - * If the optional argument +result_hash+ is given, - * it is overwritten and returned. This is intended to avoid probe effect. - * - * h = {} - * ObjectSpace.count_objects(h) - * puts h - * # => { :TOTAL=>10000, :T_CLASS=>158280, :T_MODULE=>20672, :T_STRING=>527249 } - * - * This method is only expected to work on C Ruby. - * - */ - -static VALUE -count_objects(int argc, VALUE *argv, VALUE os) -{ - rb_objspace_t *objspace = &rb_objspace; - struct count_objects_data data = { 0 }; - VALUE hash = Qnil; - - if (rb_check_arity(argc, 0, 1) == 1) { - hash = argv[0]; - if (!RB_TYPE_P(hash, T_HASH)) - rb_raise(rb_eTypeError, "non-hash given"); - } - - gc_each_object(objspace, count_objects_i, &data); - - if (NIL_P(hash)) { - hash = rb_hash_new(); - } - else if (!RHASH_EMPTY_P(hash)) { - rb_hash_stlike_foreach(hash, set_zero, hash); - } - rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(data.total)); - rb_hash_aset(hash, ID2SYM(rb_intern("FREE")), SIZET2NUM(data.freed)); - - for (size_t i = 0; i <= T_MASK; i++) { - VALUE type = type_sym(i); - if (data.counts[i]) - rb_hash_aset(hash, type, SIZET2NUM(data.counts[i])); - } - - return hash; -} - -/* - ------------------------ Garbage Collection ------------------------ -*/ - -/* Sweeping */ - -static size_t -objspace_available_slots(rb_objspace_t *objspace) -{ - size_t total_slots = 0; - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - total_slots += SIZE_POOL_EDEN_HEAP(size_pool)->total_slots; - total_slots += SIZE_POOL_TOMB_HEAP(size_pool)->total_slots; - } - return total_slots; -} - -static size_t -objspace_live_slots(rb_objspace_t *objspace) -{ - return total_allocated_objects(objspace) - total_freed_objects(objspace) - heap_pages_final_slots; -} - -static size_t -objspace_free_slots(rb_objspace_t *objspace) -{ - return objspace_available_slots(objspace) - objspace_live_slots(objspace) - heap_pages_final_slots; -} - -static void -gc_setup_mark_bits(struct heap_page *page) -{ - /* copy oldgen bitmap to mark bitmap */ - memcpy(&page->mark_bits[0], &page->uncollectible_bits[0], HEAP_PAGE_BITMAP_SIZE); -} - -static int gc_is_moveable_obj(rb_objspace_t *objspace, VALUE obj); -static VALUE gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, size_t slot_size); - -#if defined(_WIN32) -enum {HEAP_PAGE_LOCK = PAGE_NOACCESS, HEAP_PAGE_UNLOCK = PAGE_READWRITE}; - -static BOOL -protect_page_body(struct heap_page_body *body, DWORD protect) -{ - DWORD old_protect; - return VirtualProtect(body, HEAP_PAGE_SIZE, protect, &old_protect) != 0; -} -#else -enum {HEAP_PAGE_LOCK = PROT_NONE, HEAP_PAGE_UNLOCK = PROT_READ | PROT_WRITE}; -#define protect_page_body(body, protect) !mprotect((body), HEAP_PAGE_SIZE, (protect)) -#endif - -static void -lock_page_body(rb_objspace_t *objspace, struct heap_page_body *body) -{ - if (!protect_page_body(body, HEAP_PAGE_LOCK)) { - rb_bug("Couldn't protect page %p, errno: %s", (void *)body, strerror(errno)); - } - else { - gc_report(5, objspace, "Protecting page in move %p\n", (void *)body); - } -} - -static void -unlock_page_body(rb_objspace_t *objspace, struct heap_page_body *body) -{ - if (!protect_page_body(body, HEAP_PAGE_UNLOCK)) { - rb_bug("Couldn't unprotect page %p, errno: %s", (void *)body, strerror(errno)); - } - else { - gc_report(5, objspace, "Unprotecting page in move %p\n", (void *)body); - } -} - -static bool -try_move(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *free_page, VALUE src) -{ - GC_ASSERT(gc_is_moveable_obj(objspace, src)); - - struct heap_page *src_page = GET_HEAP_PAGE(src); - if (!free_page) { - return false; - } - - /* We should return true if either src is successfully moved, or src is - * unmoveable. A false return will cause the sweeping cursor to be - * incremented to the next page, and src will attempt to move again */ - GC_ASSERT(RVALUE_MARKED(src)); - - asan_unlock_freelist(free_page); - VALUE dest = (VALUE)free_page->freelist; - asan_lock_freelist(free_page); - asan_unpoison_object(dest, false); - if (!dest) { - /* if we can't get something from the freelist then the page must be - * full */ - return false; - } - asan_unlock_freelist(free_page); - free_page->freelist = RANY(dest)->as.free.next; - asan_lock_freelist(free_page); - - GC_ASSERT(RB_BUILTIN_TYPE(dest) == T_NONE); - - if (src_page->slot_size > free_page->slot_size) { - objspace->rcompactor.moved_down_count_table[BUILTIN_TYPE(src)]++; - } - else if (free_page->slot_size > src_page->slot_size) { - objspace->rcompactor.moved_up_count_table[BUILTIN_TYPE(src)]++; - } - objspace->rcompactor.moved_count_table[BUILTIN_TYPE(src)]++; - objspace->rcompactor.total_moved++; - - gc_move(objspace, src, dest, src_page->slot_size, free_page->slot_size); - gc_pin(objspace, src); - free_page->free_slots--; - - return true; -} - -static void -gc_unprotect_pages(rb_objspace_t *objspace, rb_heap_t *heap) -{ - struct heap_page *cursor = heap->compact_cursor; - - while (cursor) { - unlock_page_body(objspace, GET_PAGE_BODY(cursor->start)); - cursor = ccan_list_next(&heap->pages, cursor, page_node); - } -} - -static void gc_update_references(rb_objspace_t * objspace); -#if GC_CAN_COMPILE_COMPACTION -static void invalidate_moved_page(rb_objspace_t *objspace, struct heap_page *page); -#endif - -#if defined(__MINGW32__) || defined(_WIN32) -# define GC_COMPACTION_SUPPORTED 1 -#else -/* If not MinGW, Windows, or does not have mmap, we cannot use mprotect for - * the read barrier, so we must disable compaction. */ -# define GC_COMPACTION_SUPPORTED (GC_CAN_COMPILE_COMPACTION && HEAP_PAGE_ALLOC_USE_MMAP) -#endif - -#if GC_CAN_COMPILE_COMPACTION -static void -read_barrier_handler(uintptr_t original_address) -{ - VALUE obj; - rb_objspace_t * objspace = &rb_objspace; - - /* Calculate address aligned to slots. */ - uintptr_t address = original_address - (original_address % BASE_SLOT_SIZE); - - obj = (VALUE)address; - - struct heap_page_body *page_body = GET_PAGE_BODY(obj); - - /* If the page_body is NULL, then mprotect cannot handle it and will crash - * with "Cannot allocate memory". */ - if (page_body == NULL) { - rb_bug("read_barrier_handler: segmentation fault at %p", (void *)original_address); - } - - RB_VM_LOCK_ENTER_NO_BARRIER(); - { - HEAP_LOCK_ENTER(objspace); - { - unlock_page_body(objspace, page_body); - - objspace->profile.read_barrier_faults++; - - invalidate_moved_page(objspace, GET_HEAP_PAGE(obj)); - } - HEAP_LOCK_LEAVE(objspace); - } - RB_VM_LOCK_LEAVE_NO_BARRIER(); -} -#endif - -#if !GC_CAN_COMPILE_COMPACTION -static void -uninstall_handlers(void) -{ - /* no-op */ -} - -static void -install_handlers(void) -{ - /* no-op */ -} -#elif defined(_WIN32) -static LPTOP_LEVEL_EXCEPTION_FILTER old_handler; -typedef void (*signal_handler)(int); -static signal_handler old_sigsegv_handler; - -static LONG WINAPI -read_barrier_signal(EXCEPTION_POINTERS * info) -{ - /* EXCEPTION_ACCESS_VIOLATION is what's raised by access to protected pages */ - if (info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { - /* > The second array element specifies the virtual address of the inaccessible data. - * https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-exception_record - * - * Use this address to invalidate the page */ - read_barrier_handler((uintptr_t)info->ExceptionRecord->ExceptionInformation[1]); - return EXCEPTION_CONTINUE_EXECUTION; - } - else { - return EXCEPTION_CONTINUE_SEARCH; - } -} - -static void -uninstall_handlers(void) -{ - signal(SIGSEGV, old_sigsegv_handler); - SetUnhandledExceptionFilter(old_handler); -} - -static void -install_handlers(void) -{ - /* Remove SEGV handler so that the Unhandled Exception Filter handles it */ - old_sigsegv_handler = signal(SIGSEGV, NULL); - /* Unhandled Exception Filter has access to the violation address similar - * to si_addr from sigaction */ - old_handler = SetUnhandledExceptionFilter(read_barrier_signal); -} -#else -static struct sigaction old_sigbus_handler; -static struct sigaction old_sigsegv_handler; - -#ifdef HAVE_MACH_TASK_EXCEPTION_PORTS -static exception_mask_t old_exception_masks[32]; -static mach_port_t old_exception_ports[32]; -static exception_behavior_t old_exception_behaviors[32]; -static thread_state_flavor_t old_exception_flavors[32]; -static mach_msg_type_number_t old_exception_count; - -static void -disable_mach_bad_access_exc(void) -{ - old_exception_count = sizeof(old_exception_masks) / sizeof(old_exception_masks[0]); - task_swap_exception_ports( - mach_task_self(), EXC_MASK_BAD_ACCESS, - MACH_PORT_NULL, EXCEPTION_DEFAULT, 0, - old_exception_masks, &old_exception_count, - old_exception_ports, old_exception_behaviors, old_exception_flavors - ); -} - -static void -restore_mach_bad_access_exc(void) -{ - for (mach_msg_type_number_t i = 0; i < old_exception_count; i++) { - task_set_exception_ports( - mach_task_self(), - old_exception_masks[i], old_exception_ports[i], - old_exception_behaviors[i], old_exception_flavors[i] - ); - } -} -#endif - -static void -read_barrier_signal(int sig, siginfo_t * info, void * data) -{ - // setup SEGV/BUS handlers for errors - struct sigaction prev_sigbus, prev_sigsegv; - sigaction(SIGBUS, &old_sigbus_handler, &prev_sigbus); - sigaction(SIGSEGV, &old_sigsegv_handler, &prev_sigsegv); - - // enable SIGBUS/SEGV - sigset_t set, prev_set; - sigemptyset(&set); - sigaddset(&set, SIGBUS); - sigaddset(&set, SIGSEGV); - sigprocmask(SIG_UNBLOCK, &set, &prev_set); -#ifdef HAVE_MACH_TASK_EXCEPTION_PORTS - disable_mach_bad_access_exc(); -#endif - // run handler - read_barrier_handler((uintptr_t)info->si_addr); - - // reset SEGV/BUS handlers -#ifdef HAVE_MACH_TASK_EXCEPTION_PORTS - restore_mach_bad_access_exc(); -#endif - sigaction(SIGBUS, &prev_sigbus, NULL); - sigaction(SIGSEGV, &prev_sigsegv, NULL); - sigprocmask(SIG_SETMASK, &prev_set, NULL); -} - -static void -uninstall_handlers(void) -{ -#ifdef HAVE_MACH_TASK_EXCEPTION_PORTS - restore_mach_bad_access_exc(); -#endif - sigaction(SIGBUS, &old_sigbus_handler, NULL); - sigaction(SIGSEGV, &old_sigsegv_handler, NULL); -} - -static void -install_handlers(void) -{ - struct sigaction action; - memset(&action, 0, sizeof(struct sigaction)); - sigemptyset(&action.sa_mask); - action.sa_sigaction = read_barrier_signal; - action.sa_flags = SA_SIGINFO | SA_ONSTACK; - - sigaction(SIGBUS, &action, &old_sigbus_handler); - sigaction(SIGSEGV, &action, &old_sigsegv_handler); -#ifdef HAVE_MACH_TASK_EXCEPTION_PORTS - disable_mach_bad_access_exc(); -#endif -} -#endif - -static void -gc_compact_finish(rb_objspace_t *objspace) -{ - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); - gc_unprotect_pages(objspace, heap); - } - - uninstall_handlers(); - - gc_update_references(objspace); - objspace->profile.compact_count++; - - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); - heap->compact_cursor = NULL; - heap->free_pages = NULL; - heap->compact_cursor_index = 0; - } - - if (gc_prof_enabled(objspace)) { - gc_profile_record *record = gc_prof_record(objspace); - record->moved_objects = objspace->rcompactor.total_moved - record->moved_objects; - } - objspace->flags.during_compacting = FALSE; -} - -struct gc_sweep_context { - struct heap_page *page; - int final_slots; - int freed_slots; - int empty_slots; -}; - -static inline void -gc_sweep_plane(rb_objspace_t *objspace, rb_heap_t *heap, uintptr_t p, bits_t bitset, struct gc_sweep_context *ctx) -{ - struct heap_page * sweep_page = ctx->page; - short slot_size = sweep_page->slot_size; - short slot_bits = slot_size / BASE_SLOT_SIZE; - GC_ASSERT(slot_bits > 0); - - do { - VALUE vp = (VALUE)p; - GC_ASSERT(vp % BASE_SLOT_SIZE == 0); - - asan_unpoison_object(vp, false); - if (bitset & 1) { - switch (BUILTIN_TYPE(vp)) { - default: /* majority case */ - gc_report(2, objspace, "page_sweep: free %p\n", (void *)p); -#if RGENGC_CHECK_MODE - if (!is_full_marking(objspace)) { - if (RVALUE_OLD_P(vp)) rb_bug("page_sweep: %p - old while minor GC.", (void *)p); - if (RVALUE_REMEMBERED(vp)) rb_bug("page_sweep: %p - remembered.", (void *)p); - } -#endif - if (obj_free(objspace, vp)) { - // always add free slots back to the swept pages freelist, - // so that if we're comapacting, we can re-use the slots - (void)VALGRIND_MAKE_MEM_UNDEFINED((void*)p, BASE_SLOT_SIZE); - heap_page_add_freeobj(objspace, sweep_page, vp); - gc_report(3, objspace, "page_sweep: %s is added to freelist\n", obj_info(vp)); - ctx->freed_slots++; - } - else { - ctx->final_slots++; - } - break; - - case T_MOVED: - if (objspace->flags.during_compacting) { - /* The sweep cursor shouldn't have made it to any - * T_MOVED slots while the compact flag is enabled. - * The sweep cursor and compact cursor move in - * opposite directions, and when they meet references will - * get updated and "during_compacting" should get disabled */ - rb_bug("T_MOVED shouldn't be seen until compaction is finished"); - } - gc_report(3, objspace, "page_sweep: %s is added to freelist\n", obj_info(vp)); - ctx->empty_slots++; - heap_page_add_freeobj(objspace, sweep_page, vp); - break; - case T_ZOMBIE: - /* already counted */ - break; - case T_NONE: - ctx->empty_slots++; /* already freed */ - break; - } - } - p += slot_size; - bitset >>= slot_bits; - } while (bitset); -} - -static inline void -gc_sweep_page(rb_objspace_t *objspace, rb_heap_t *heap, struct gc_sweep_context *ctx) -{ - struct heap_page *sweep_page = ctx->page; - GC_ASSERT(SIZE_POOL_EDEN_HEAP(sweep_page->size_pool) == heap); - - uintptr_t p; - bits_t *bits, bitset; - - gc_report(2, objspace, "page_sweep: start.\n"); - -#if RGENGC_CHECK_MODE - if (!objspace->flags.immediate_sweep) { - GC_ASSERT(sweep_page->flags.before_sweep == TRUE); - } -#endif - sweep_page->flags.before_sweep = FALSE; - sweep_page->free_slots = 0; - - p = (uintptr_t)sweep_page->start; - bits = sweep_page->mark_bits; - - int page_rvalue_count = sweep_page->total_slots * (sweep_page->slot_size / BASE_SLOT_SIZE); - int out_of_range_bits = (NUM_IN_PAGE(p) + page_rvalue_count) % BITS_BITLENGTH; - if (out_of_range_bits != 0) { // sizeof(RVALUE) == 64 - bits[BITMAP_INDEX(p) + page_rvalue_count / BITS_BITLENGTH] |= ~(((bits_t)1 << out_of_range_bits) - 1); - } - - /* The last bitmap plane may not be used if the last plane does not - * have enough space for the slot_size. In that case, the last plane must - * be skipped since none of the bits will be set. */ - int bitmap_plane_count = CEILDIV(NUM_IN_PAGE(p) + page_rvalue_count, BITS_BITLENGTH); - GC_ASSERT(bitmap_plane_count == HEAP_PAGE_BITMAP_LIMIT - 1 || - bitmap_plane_count == HEAP_PAGE_BITMAP_LIMIT); - - bool local_immunity_active = using_local_limits(objspace); - - // Skip out of range slots at the head of the page - bitset = ~bits[0]; - if (local_immunity_active) bitset &= ~sweep_page->local_immune_bits[0]; - bitset >>= NUM_IN_PAGE(p); - if (bitset) { - gc_sweep_plane(objspace, heap, p, bitset, ctx); - } - p += (BITS_BITLENGTH - NUM_IN_PAGE(p)) * BASE_SLOT_SIZE; - - for (int i = 1; i < bitmap_plane_count; i++) { - bitset = ~bits[i]; - if (local_immunity_active) bitset &= ~sweep_page->local_immune_bits[i]; - if (bitset) { - gc_sweep_plane(objspace, heap, p, bitset, ctx); - } - p += BITS_BITLENGTH * BASE_SLOT_SIZE; - } - - if (!heap->compact_cursor) { - gc_setup_mark_bits(sweep_page); - } - -#if GC_PROFILE_MORE_DETAIL - if (gc_prof_enabled(objspace)) { - gc_profile_record *record = gc_prof_record(objspace); - record->removing_objects += ctx->final_slots + ctx->freed_slots; - record->empty_objects += ctx->empty_slots; - } -#endif - if (0) fprintf(stderr, "gc_sweep_page(%"PRIdSIZE"): total_slots: %d, freed_slots: %d, empty_slots: %d, final_slots: %d\n", - rb_gc_count(), - sweep_page->total_slots, - ctx->freed_slots, ctx->empty_slots, ctx->final_slots); - - sweep_page->free_slots += ctx->freed_slots + ctx->empty_slots; - sweep_page->size_pool->total_freed_objects += ctx->freed_slots; - - if (heap_pages_deferred_final && !finalizing) { - gc_finalize_deferred_register(objspace); - } - -#if RGENGC_CHECK_MODE - short freelist_len = 0; - asan_unlock_freelist(sweep_page); - RVALUE *ptr = sweep_page->freelist; - while (ptr) { - freelist_len++; - ptr = ptr->as.free.next; - } - asan_lock_freelist(sweep_page); - if (freelist_len != sweep_page->free_slots) { - rb_bug("inconsistent freelist length: expected %d but was %d", sweep_page->free_slots, freelist_len); - } -#endif - - gc_report(2, objspace, "page_sweep: end.\n"); -} - -static const char * -gc_mode_name(enum gc_mode mode) -{ - switch (mode) { - case gc_mode_none: return "none"; - case gc_mode_marking: return "marking"; - case gc_mode_sweeping: return "sweeping"; - case gc_mode_compacting: return "compacting"; - default: rb_bug("gc_mode_name: unknown mode: %d", (int)mode); - } -} - -static void -gc_mode_transition(rb_objspace_t *objspace, enum gc_mode mode) -{ -#if RGENGC_CHECK_MODE - enum gc_mode prev_mode = gc_mode(objspace); - switch (prev_mode) { - case gc_mode_none: GC_ASSERT(mode == gc_mode_marking); break; - case gc_mode_marking: GC_ASSERT(mode == gc_mode_sweeping); break; - case gc_mode_sweeping: GC_ASSERT(mode == gc_mode_none || mode == gc_mode_compacting); break; - case gc_mode_compacting: GC_ASSERT(mode == gc_mode_none); break; - } -#endif - if (0) fprintf(stderr, "gc_mode_transition: %s->%s\n", gc_mode_name(gc_mode(objspace)), gc_mode_name(mode)); - gc_mode_set(objspace, mode); -} - -static void -heap_page_freelist_append(struct heap_page *page, RVALUE *freelist) -{ - if (freelist) { - asan_unlock_freelist(page); - if (page->freelist) { - RVALUE *p = page->freelist; - asan_unpoison_object((VALUE)p, false); - while (p->as.free.next) { - RVALUE *prev = p; - p = p->as.free.next; - asan_poison_object((VALUE)prev); - asan_unpoison_object((VALUE)p, false); - } - p->as.free.next = freelist; - asan_poison_object((VALUE)p); - } - else { - page->freelist = freelist; - } - asan_lock_freelist(page); - } -} - -static void -gc_sweep_start_heap(rb_objspace_t *objspace, rb_heap_t *heap) -{ - heap->sweeping_page = ccan_list_top(&heap->pages, struct heap_page, page_node); - heap->free_pages = NULL; - heap->pooled_pages = NULL; - if (!objspace->flags.immediate_sweep) { - struct heap_page *page = NULL; - - ccan_list_for_each(&heap->pages, page, page_node) { - page->flags.before_sweep = TRUE; - } - } -} - -#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 4 -__attribute__((noinline)) -#endif - -#if GC_CAN_COMPILE_COMPACTION -static void gc_sort_heap_by_compare_func(rb_objspace_t *objspace, gc_compact_compare_func compare_func); -static int compare_pinned_slots(const void *left, const void *right, void *d); -#endif - -static void -gc_sweep_start(rb_objspace_t *objspace) -{ - gc_mode_transition(objspace, gc_mode_sweeping); - objspace->rincgc.pooled_slots = 0; - -#if GC_CAN_COMPILE_COMPACTION - if (objspace->flags.during_compacting) { - gc_sort_heap_by_compare_func( - objspace, - objspace->rcompactor.compare_func ? objspace->rcompactor.compare_func : compare_pinned_slots - ); - } -#endif - - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); - - gc_sweep_start_heap(objspace, heap); - - /* We should call gc_sweep_finish_size_pool for size pools with no pages. */ - if (heap->sweeping_page == NULL) { - GC_ASSERT(heap->total_pages == 0); - GC_ASSERT(heap->total_slots == 0); - gc_sweep_finish_size_pool(objspace, size_pool); - } - } - - rb_gc_ractor_newobj_cache_clear(&objspace->local_data.ractor->newobj_cache); - rb_gc_ractor_newobj_cache_clear(&objspace->local_data.ractor->newobj_borrowing_cache); -} - -static void -gc_sweep_finish_size_pool(rb_objspace_t *objspace, rb_size_pool_t *size_pool) -{ - rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); - size_t total_slots = heap->total_slots + SIZE_POOL_TOMB_HEAP(size_pool)->total_slots; - size_t total_pages = heap->total_pages + SIZE_POOL_TOMB_HEAP(size_pool)->total_pages; - size_t swept_slots = size_pool->freed_slots + size_pool->empty_slots; - - size_t init_slots = gc_params.size_pool_init_slots[size_pool - size_pools]; - size_t min_free_slots = (size_t)(MAX(total_slots, init_slots) * gc_params.heap_free_slots_min_ratio); - - /* If we don't have enough slots and we have pages on the tomb heap, move - * pages from the tomb heap to the eden heap. This may prevent page - * creation thrashing (frequently allocating and deallocting pages) and - * GC thrashing (running GC more frequently than required). */ - struct heap_page *resurrected_page; - while (swept_slots < min_free_slots && - (resurrected_page = heap_page_resurrect(objspace, size_pool))) { - swept_slots += resurrected_page->free_slots; - - heap_add_page(objspace, size_pool, heap, resurrected_page); - heap_add_freepage(heap, resurrected_page); - } - - if (swept_slots < min_free_slots) { - bool grow_heap = is_full_marking(objspace); - - /* Consider growing or starting a major GC if we are not currently in a - * major GC and we can't allocate any more pages. */ - if (!is_full_marking(objspace) && size_pool->allocatable_pages == 0) { - /* The heap is a growth heap if it freed more slots than had empty slots. */ - bool is_growth_heap = size_pool->empty_slots == 0 || size_pool->freed_slots > size_pool->empty_slots; - - /* Grow this heap if we haven't run at least RVALUE_OLD_AGE minor - * GC since the last major GC or if this heap is smaller than the - * the configured initial size. */ - if (objspace->profile.count - objspace->rgengc.last_major_gc < RVALUE_OLD_AGE || - total_slots < init_slots) { - grow_heap = TRUE; - } - else if (is_growth_heap) { /* Only growth heaps are allowed to start a major GC. */ - gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_NOFREE; - size_pool->force_major_gc_count++; - } - } - - if (grow_heap) { - size_t extend_page_count = heap_extend_pages(objspace, size_pool, swept_slots, total_slots, total_pages); - - if (extend_page_count > size_pool->allocatable_pages) { - size_pool_allocatable_pages_set(objspace, size_pool, extend_page_count); - } - } - } -} - -static void -gc_sweep_finish(rb_objspace_t *objspace) -{ - gc_report(1, objspace, "gc_sweep_finish\n"); - - gc_prof_set_heap_info(objspace); - heap_pages_free_unused_pages(objspace); - - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - - /* if heap_pages has unused pages, then assign them to increment */ - size_t tomb_pages = SIZE_POOL_TOMB_HEAP(size_pool)->total_pages; - if (size_pool->allocatable_pages < tomb_pages) { - size_pool->allocatable_pages = tomb_pages; - } - - size_pool->freed_slots = 0; - size_pool->empty_slots = 0; - - if (!will_be_incremental_marking(objspace)) { - rb_heap_t *eden_heap = SIZE_POOL_EDEN_HEAP(size_pool); - struct heap_page *end_page = eden_heap->free_pages; - if (end_page) { - while (end_page->free_next) end_page = end_page->free_next; - end_page->free_next = eden_heap->pooled_pages; - } - else { - eden_heap->free_pages = eden_heap->pooled_pages; - } - eden_heap->pooled_pages = NULL; - objspace->rincgc.pooled_slots = 0; - } - } - heap_pages_expand_sorted(objspace); - - gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_END_SWEEP, 0); - gc_mode_transition(objspace, gc_mode_none); - -#if RGENGC_CHECK_MODE >= 2 - gc_verify_internal_consistency(objspace); -#endif -} - -static int -gc_sweep_step(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap) -{ - struct heap_page *sweep_page = heap->sweeping_page; - int unlink_limit = GC_SWEEP_PAGES_FREEABLE_PER_STEP; - int swept_slots = 0; - int pooled_slots = 0; - - if (sweep_page == NULL) return FALSE; - -#if GC_ENABLE_LAZY_SWEEP - gc_prof_sweep_timer_start(objspace); -#endif - - do { - RUBY_DEBUG_LOG("sweep_page:%p", (void *)sweep_page); - - struct gc_sweep_context ctx = { - .page = sweep_page, - .final_slots = 0, - .freed_slots = 0, - .empty_slots = 0, - }; - gc_sweep_page(objspace, heap, &ctx); - int free_slots = ctx.freed_slots + ctx.empty_slots; - - heap->sweeping_page = ccan_list_next(&heap->pages, sweep_page, page_node); - - if (sweep_page->final_slots + free_slots == sweep_page->total_slots && - heap_pages_freeable_pages > 0 && - unlink_limit > 0) { - heap_pages_freeable_pages--; - unlink_limit--; - /* there are no living objects -> move this page to tomb heap */ - heap_unlink_page(objspace, heap, sweep_page); - heap_add_page(objspace, size_pool, SIZE_POOL_TOMB_HEAP(size_pool), sweep_page); - } - else if (free_slots > 0) { - size_pool->freed_slots += ctx.freed_slots; - size_pool->empty_slots += ctx.empty_slots; - - if (pooled_slots < GC_INCREMENTAL_SWEEP_POOL_SLOT_COUNT) { - heap_add_poolpage(objspace, heap, sweep_page); - pooled_slots += free_slots; - } - else { - heap_add_freepage(heap, sweep_page); - swept_slots += free_slots; - if (swept_slots > GC_INCREMENTAL_SWEEP_SLOT_COUNT) { - break; - } - } - } - else { - sweep_page->free_next = NULL; - } - } while ((sweep_page = heap->sweeping_page)); - - if (!heap->sweeping_page) { - gc_sweep_finish_size_pool(objspace, size_pool); - - if (!has_sweeping_pages(objspace)) { - gc_sweep_finish(objspace); - } - } - -#if GC_ENABLE_LAZY_SWEEP - gc_prof_sweep_timer_stop(objspace); -#endif - - return heap->free_pages != NULL; -} - -static void -gc_sweep_rest(rb_objspace_t *objspace) -{ - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - - while (SIZE_POOL_EDEN_HEAP(size_pool)->sweeping_page) { - gc_sweep_step(objspace, size_pool, SIZE_POOL_EDEN_HEAP(size_pool)); - } - } -} - -static void -gc_sweep_continue(rb_objspace_t *objspace, rb_size_pool_t *sweep_size_pool, rb_heap_t *heap) -{ - GC_ASSERT(dont_gc_val() == FALSE); - if (!GC_ENABLE_LAZY_SWEEP) return; - - gc_sweeping_enter(objspace); - - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - if (!gc_sweep_step(objspace, size_pool, SIZE_POOL_EDEN_HEAP(size_pool))) { - /* sweep_size_pool requires a free slot but sweeping did not yield any. */ - if (size_pool == sweep_size_pool) { - if (size_pool->allocatable_pages > 0) { - heap_increment(objspace, size_pool, heap); - } - else { - /* Not allowed to create a new page so finish sweeping. */ - gc_sweep_rest(objspace); - break; - } - } - } - } - - gc_sweeping_exit(objspace); -} - -#if GC_CAN_COMPILE_COMPACTION -static void -invalidate_moved_plane(rb_objspace_t *objspace, struct heap_page *page, uintptr_t p, bits_t bitset) -{ - if (bitset) { - do { - if (bitset & 1) { - VALUE forwarding_object = (VALUE)p; - VALUE object; - - if (BUILTIN_TYPE(forwarding_object) == T_MOVED) { - GC_ASSERT(RVALUE_PINNED(forwarding_object)); - GC_ASSERT(!RVALUE_MARKED(forwarding_object)); - - CLEAR_IN_BITMAP(GET_HEAP_PINNED_BITS(forwarding_object), forwarding_object); - - object = rb_gc_location(forwarding_object); - - shape_id_t original_shape_id = 0; - if (RB_TYPE_P(object, T_OBJECT)) { - original_shape_id = RMOVED(forwarding_object)->original_shape_id; - } - - gc_move(objspace, object, forwarding_object, GET_HEAP_PAGE(object)->slot_size, page->slot_size); - /* forwarding_object is now our actual object, and "object" - * is the free slot for the original page */ - - if (original_shape_id) { - ROBJECT_SET_SHAPE_ID(forwarding_object, original_shape_id); - } - - struct heap_page *orig_page = GET_HEAP_PAGE(object); - orig_page->free_slots++; - heap_page_add_freeobj(objspace, orig_page, object); - - GC_ASSERT(RVALUE_MARKED(forwarding_object)); - GC_ASSERT(BUILTIN_TYPE(forwarding_object) != T_MOVED); - GC_ASSERT(BUILTIN_TYPE(forwarding_object) != T_NONE); - } - } - p += BASE_SLOT_SIZE; - bitset >>= 1; - } while (bitset); - } -} - -static void -invalidate_moved_page(rb_objspace_t *objspace, struct heap_page *page) -{ - int i; - bits_t *mark_bits, *pin_bits; - bits_t bitset; - - mark_bits = page->mark_bits; - pin_bits = page->pinned_bits; - - uintptr_t p = page->start; - - // Skip out of range slots at the head of the page - bitset = pin_bits[0] & ~mark_bits[0]; - bitset >>= NUM_IN_PAGE(p); - invalidate_moved_plane(objspace, page, p, bitset); - p += (BITS_BITLENGTH - NUM_IN_PAGE(p)) * BASE_SLOT_SIZE; - - for (i=1; i < HEAP_PAGE_BITMAP_LIMIT; i++) { - /* Moved objects are pinned but never marked. We reuse the pin bits - * to indicate there is a moved object in this slot. */ - bitset = pin_bits[i] & ~mark_bits[i]; - - invalidate_moved_plane(objspace, page, p, bitset); - p += BITS_BITLENGTH * BASE_SLOT_SIZE; - } -} -#endif - -static void -gc_compact_start(rb_objspace_t *objspace) -{ - struct heap_page *page = NULL; - gc_mode_transition(objspace, gc_mode_compacting); - - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(&size_pools[i]); - ccan_list_for_each(&heap->pages, page, page_node) { - page->flags.before_sweep = TRUE; - } - - heap->compact_cursor = ccan_list_tail(&heap->pages, struct heap_page, page_node); - heap->compact_cursor_index = 0; - } - - if (gc_prof_enabled(objspace)) { - gc_profile_record *record = gc_prof_record(objspace); - record->moved_objects = objspace->rcompactor.total_moved; - } - - memset(objspace->rcompactor.considered_count_table, 0, T_MASK * sizeof(size_t)); - memset(objspace->rcompactor.moved_count_table, 0, T_MASK * sizeof(size_t)); - memset(objspace->rcompactor.moved_up_count_table, 0, T_MASK * sizeof(size_t)); - memset(objspace->rcompactor.moved_down_count_table, 0, T_MASK * sizeof(size_t)); - - /* Set up read barrier for pages containing MOVED objects */ - install_handlers(); -} - -static void gc_sweep_compact(rb_objspace_t *objspace); - -static void -gc_sweep(rb_objspace_t *objspace) -{ - gc_sweeping_enter(objspace); - - const unsigned int immediate_sweep = objspace->flags.immediate_sweep; - - gc_report(1, objspace, "gc_sweep: immediate: %d\n", immediate_sweep); - - gc_sweep_start(objspace); - if (objspace->flags.during_compacting) { - gc_sweep_compact(objspace); - } - - if (immediate_sweep) { -#if !GC_ENABLE_LAZY_SWEEP - gc_prof_sweep_timer_start(objspace); -#endif - gc_sweep_rest(objspace); -#if !GC_ENABLE_LAZY_SWEEP - gc_prof_sweep_timer_stop(objspace); -#endif - } - else { - - /* Sweep every size pool. */ - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - gc_sweep_step(objspace, size_pool, SIZE_POOL_EDEN_HEAP(size_pool)); - } - } - - gc_sweeping_exit(objspace); -} - -/* Marking - Marking stack */ - -static stack_chunk_t * -stack_chunk_alloc(void) -{ - stack_chunk_t *res; - - res = malloc(sizeof(stack_chunk_t)); - if (!res) - rb_memerror(); - - return res; -} - -static inline int -is_mark_stack_empty(mark_stack_t *stack) -{ - return stack->chunk == NULL; -} - -static size_t -mark_stack_size(mark_stack_t *stack) -{ - size_t size = stack->index; - stack_chunk_t *chunk = stack->chunk ? stack->chunk->next : NULL; - - while (chunk) { - size += stack->limit; - chunk = chunk->next; - } - return size; -} - -static void -add_stack_chunk_cache(mark_stack_t *stack, stack_chunk_t *chunk) -{ - chunk->next = stack->cache; - stack->cache = chunk; - stack->cache_size++; -} - -static void -shrink_stack_chunk_cache(mark_stack_t *stack) -{ - stack_chunk_t *chunk; - - if (stack->unused_cache_size > (stack->cache_size/2)) { - chunk = stack->cache; - stack->cache = stack->cache->next; - stack->cache_size--; - free(chunk); - } - stack->unused_cache_size = stack->cache_size; -} - -static void -push_mark_stack_chunk(mark_stack_t *stack) -{ - stack_chunk_t *next; - - GC_ASSERT(stack->index == stack->limit); - - if (stack->cache_size > 0) { - next = stack->cache; - stack->cache = stack->cache->next; - stack->cache_size--; - if (stack->unused_cache_size > stack->cache_size) - stack->unused_cache_size = stack->cache_size; - } - else { - next = stack_chunk_alloc(); - } - next->next = stack->chunk; - stack->chunk = next; - stack->index = 0; -} - -static void -pop_mark_stack_chunk(mark_stack_t *stack) -{ - stack_chunk_t *prev; - - prev = stack->chunk->next; - GC_ASSERT(stack->index == 0); - add_stack_chunk_cache(stack, stack->chunk); - stack->chunk = prev; - stack->index = stack->limit; -} - -static void -mark_stack_chunk_list_free(stack_chunk_t *chunk) -{ - stack_chunk_t *next = NULL; - - while (chunk != NULL) { - next = chunk->next; - free(chunk); - chunk = next; - } -} - -static void -free_stack_chunks(mark_stack_t *stack) -{ - mark_stack_chunk_list_free(stack->chunk); -} - -static void -mark_stack_free_cache(mark_stack_t *stack) -{ - mark_stack_chunk_list_free(stack->cache); - stack->cache_size = 0; - stack->unused_cache_size = 0; -} - -static void -push_mark_stack(mark_stack_t *stack, VALUE obj) -{ - switch (BUILTIN_TYPE(obj)) { - case T_OBJECT: - case T_CLASS: - case T_MODULE: - case T_FLOAT: - case T_STRING: - case T_REGEXP: - case T_ARRAY: - case T_HASH: - case T_STRUCT: - case T_BIGNUM: - case T_FILE: - case T_DATA: - case T_MATCH: - case T_COMPLEX: - case T_RATIONAL: - case T_TRUE: - case T_FALSE: - case T_SYMBOL: - case T_IMEMO: - case T_ICLASS: - if (stack->index == stack->limit) { - push_mark_stack_chunk(stack); - } - stack->chunk->data[stack->index++] = obj; - return; - - case T_NONE: - case T_NIL: - case T_FIXNUM: - case T_MOVED: - case T_ZOMBIE: - case T_UNDEF: - case T_MASK: - rb_bug("push_mark_stack() called for broken object"); - break; - - case T_NODE: - UNEXPECTED_NODE(push_mark_stack); - break; - } - - rb_bug("rb_gc_mark(): unknown data type 0x%x(%p) %s", - BUILTIN_TYPE(obj), (void *)obj, - is_pointer_to_heap(&rb_objspace, (void *)obj) ? "corrupted object" : "non object"); -} - -static int -pop_mark_stack(mark_stack_t *stack, VALUE *data) -{ - if (is_mark_stack_empty(stack)) { - return FALSE; - } - if (stack->index == 1) { - *data = stack->chunk->data[--stack->index]; - pop_mark_stack_chunk(stack); - } - else { - *data = stack->chunk->data[--stack->index]; - } - return TRUE; -} - -static void -init_mark_stack(mark_stack_t *stack) -{ - int i; - - MEMZERO(stack, mark_stack_t, 1); - stack->index = stack->limit = STACK_CHUNK_SIZE; - - for (i=0; i < 4; i++) { - add_stack_chunk_cache(stack, stack_chunk_alloc()); - } - stack->unused_cache_size = stack->cache_size; -} - -/* Marking */ - -#define SET_STACK_END SET_MACHINE_STACK_END(&ec->machine.stack_end) - -#define STACK_START (ec->machine.stack_start) -#define STACK_END (ec->machine.stack_end) -#define STACK_LEVEL_MAX (ec->machine.stack_maxsize/sizeof(VALUE)) - -#if STACK_GROW_DIRECTION < 0 -# define STACK_LENGTH (size_t)(STACK_START - STACK_END) -#elif STACK_GROW_DIRECTION > 0 -# define STACK_LENGTH (size_t)(STACK_END - STACK_START + 1) -#else -# define STACK_LENGTH ((STACK_END < STACK_START) ? (size_t)(STACK_START - STACK_END) \ - : (size_t)(STACK_END - STACK_START + 1)) -#endif -#if !STACK_GROW_DIRECTION -int ruby_stack_grow_direction; -int -ruby_get_stack_grow_direction(volatile VALUE *addr) -{ - VALUE *end; - SET_MACHINE_STACK_END(&end); - - if (end > addr) return ruby_stack_grow_direction = 1; - return ruby_stack_grow_direction = -1; -} -#endif - -size_t -ruby_stack_length(VALUE **p) -{ - rb_execution_context_t *ec = GET_EC(); - SET_STACK_END; - if (p) *p = STACK_UPPER(STACK_END, STACK_START, STACK_END); - return STACK_LENGTH; -} - -#define PREVENT_STACK_OVERFLOW 1 -#ifndef PREVENT_STACK_OVERFLOW -#if !(defined(POSIX_SIGNAL) && defined(SIGSEGV) && defined(HAVE_SIGALTSTACK)) -# define PREVENT_STACK_OVERFLOW 1 -#else -# define PREVENT_STACK_OVERFLOW 0 -#endif -#endif -#if PREVENT_STACK_OVERFLOW && !defined(__EMSCRIPTEN__) -static int -stack_check(rb_execution_context_t *ec, int water_mark) -{ - SET_STACK_END; - - size_t length = STACK_LENGTH; - size_t maximum_length = STACK_LEVEL_MAX - water_mark; - - return length > maximum_length; -} -#else -#define stack_check(ec, water_mark) FALSE -#endif - -#define STACKFRAME_FOR_CALL_CFUNC 2048 - -int -rb_ec_stack_check(rb_execution_context_t *ec) -{ - return stack_check(ec, STACKFRAME_FOR_CALL_CFUNC); -} - -int -ruby_stack_check(void) -{ - return stack_check(GET_EC(), STACKFRAME_FOR_CALL_CFUNC); -} - -ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(static void each_location(rb_objspace_t *objspace, register const VALUE *x, register long n, void (*cb)(rb_objspace_t *, VALUE))); -static void -each_location(rb_objspace_t *objspace, register const VALUE *x, register long n, void (*cb)(rb_objspace_t *, VALUE)) -{ - VALUE v; - while (n--) { - v = *x; - cb(objspace, v); - x++; - } -} - -static void -gc_mark_locations(rb_objspace_t *objspace, const VALUE *start, const VALUE *end, void (*cb)(rb_objspace_t *, VALUE)) -{ - long n; - - if (end <= start) return; - n = end - start; - each_location(objspace, start, n, cb); -} - -void -rb_gc_mark_locations(const VALUE *start, const VALUE *end) -{ - gc_mark_locations(&rb_objspace, start, end, gc_stack_location_mark_maybe); -} - -void -rb_gc_mark_values(long n, const VALUE *values) -{ - long i; - rb_objspace_t *objspace = &rb_objspace; - - for (i=0; inum_entries == 0) return; - st_foreach(tbl, mark_value, (st_data_t)objspace); -} - -static void -mark_tbl(rb_objspace_t *objspace, st_table *tbl) -{ - if (!tbl || tbl->num_entries == 0) return; - st_foreach(tbl, mark_value_pin, (st_data_t)objspace); -} - -static int -mark_key_pin(st_data_t key, st_data_t value, st_data_t data) -{ - rb_objspace_t *objspace = (rb_objspace_t *)data; - gc_mark_and_pin(objspace, (VALUE)key); - return ST_CONTINUE; -} - -static void -mark_set(rb_objspace_t *objspace, st_table *tbl) -{ - if (!tbl) return; - st_foreach(tbl, mark_key_pin, (st_data_t)objspace); -} - -static int -pin_value(st_data_t key, st_data_t value, st_data_t data) -{ - rb_objspace_t *objspace = (rb_objspace_t *)data; - gc_mark_and_pin(objspace, (VALUE)value); - return ST_CONTINUE; -} - -static void -mark_finalizer_tbl(rb_objspace_t *objspace, st_table *tbl) -{ - if (!tbl) return; - st_foreach(tbl, pin_value, (st_data_t)objspace); -} - -void -rb_mark_set(st_table *tbl) -{ - mark_set(&rb_objspace, tbl); -} - -static int -mark_keyvalue(st_data_t key, st_data_t value, st_data_t data) -{ - rb_objspace_t *objspace = (rb_objspace_t *)data; - - gc_mark(objspace, (VALUE)key); - gc_mark(objspace, (VALUE)value); - return ST_CONTINUE; -} - -static int -pin_key_pin_value(st_data_t key, st_data_t value, st_data_t data) -{ - rb_objspace_t *objspace = (rb_objspace_t *)data; - - gc_mark_and_pin(objspace, (VALUE)key); - gc_mark_and_pin(objspace, (VALUE)value); - return ST_CONTINUE; -} - -static int -pin_key_mark_value(st_data_t key, st_data_t value, st_data_t data) -{ - rb_objspace_t *objspace = (rb_objspace_t *)data; - - gc_mark_and_pin(objspace, (VALUE)key); - gc_mark(objspace, (VALUE)value); - return ST_CONTINUE; -} - -static void -mark_hash(rb_objspace_t *objspace, VALUE hash) -{ - if (rb_hash_compare_by_id_p(hash)) { - rb_hash_stlike_foreach(hash, pin_key_mark_value, (st_data_t)objspace); - } - else { - rb_hash_stlike_foreach(hash, mark_keyvalue, (st_data_t)objspace); - } - - gc_mark(objspace, RHASH(hash)->ifnone); -} - -static void -mark_st(rb_objspace_t *objspace, st_table *tbl) -{ - if (!tbl) return; - st_foreach(tbl, pin_key_pin_value, (st_data_t)objspace); -} - -void -rb_mark_hash(st_table *tbl) -{ - mark_st(&rb_objspace, tbl); -} - -static enum rb_id_table_iterator_result -mark_method_entry_i(VALUE me, void *data) -{ - rb_objspace_t *objspace = (rb_objspace_t *)data; - - gc_mark(objspace, me); - return ID_TABLE_CONTINUE; -} - -static void -mark_m_tbl(rb_objspace_t *objspace, struct rb_id_table *tbl) -{ - if (tbl) { - rb_id_table_foreach_values(tbl, mark_method_entry_i, objspace); - } -} - -static enum rb_id_table_iterator_result -mark_const_entry_i(VALUE value, void *data) -{ - const rb_const_entry_t *ce = (const rb_const_entry_t *)value; - rb_objspace_t *objspace = data; - - gc_mark(objspace, ce->value); - gc_mark(objspace, ce->file); - return ID_TABLE_CONTINUE; -} - -static void -mark_const_tbl(rb_objspace_t *objspace, struct rb_id_table *tbl) -{ - if (!tbl) return; - rb_id_table_foreach_values(tbl, mark_const_entry_i, objspace); -} - -#if STACK_GROW_DIRECTION < 0 -#define GET_STACK_BOUNDS(start, end, appendix) ((start) = STACK_END, (end) = STACK_START) -#elif STACK_GROW_DIRECTION > 0 -#define GET_STACK_BOUNDS(start, end, appendix) ((start) = STACK_START, (end) = STACK_END+(appendix)) -#else -#define GET_STACK_BOUNDS(start, end, appendix) \ - ((STACK_END < STACK_START) ? \ - ((start) = STACK_END, (end) = STACK_START) : ((start) = STACK_START, (end) = STACK_END+(appendix))) -#endif - -static void each_stack_location(rb_objspace_t *objspace, const rb_execution_context_t *ec, - const VALUE *stack_start, const VALUE *stack_end, void (*cb)(rb_objspace_t *, VALUE)); - -static void -gc_mark_machine_stack_location_maybe(rb_objspace_t *objspace, VALUE obj) -{ - gc_stack_location_mark_maybe(objspace, obj); - -#ifdef RUBY_ASAN_ENABLED - const rb_execution_context_t *ec = objspace->marking_machine_context_ec; - void *fake_frame_start; - void *fake_frame_end; - bool is_fake_frame = asan_get_fake_stack_extents( - ec->machine.asan_fake_stack_handle, obj, - ec->machine.stack_start, ec->machine.stack_end, - &fake_frame_start, &fake_frame_end - ); - if (is_fake_frame) { - each_stack_location(objspace, ec, fake_frame_start, fake_frame_end, gc_stack_location_mark_maybe); - } -#endif -} - -#if defined(__wasm__) - - -static VALUE *rb_stack_range_tmp[2]; - -static void -rb_mark_locations(void *begin, void *end) -{ - rb_stack_range_tmp[0] = begin; - rb_stack_range_tmp[1] = end; -} - -# if defined(__EMSCRIPTEN__) - -static void -mark_current_machine_context(rb_objspace_t *objspace, rb_execution_context_t *ec) -{ - emscripten_scan_stack(rb_mark_locations); - each_stack_location(objspace, ec, rb_stack_range_tmp[0], rb_stack_range_tmp[1], gc_stack_location_mark_maybe); - - emscripten_scan_registers(rb_mark_locations); - each_stack_location(objspace, ec, rb_stack_range_tmp[0], rb_stack_range_tmp[1], gc_stack_location_mark_maybe); -} -# else // use Asyncify version - -static void -mark_current_machine_context(rb_objspace_t *objspace, rb_execution_context_t *ec) -{ - VALUE *stack_start, *stack_end; - SET_STACK_END; - GET_STACK_BOUNDS(stack_start, stack_end, 1); - each_stack_location(objspace, ec, stack_start, stack_end, gc_stack_location_mark_maybe); - - rb_wasm_scan_locals(rb_mark_locations); - each_stack_location(objspace, ec, rb_stack_range_tmp[0], rb_stack_range_tmp[1], gc_stack_location_mark_maybe); -} - -# endif - -#else // !defined(__wasm__) - -static void -mark_current_machine_context(rb_objspace_t *objspace, rb_execution_context_t *ec) -{ - union { - rb_jmp_buf j; - VALUE v[sizeof(rb_jmp_buf) / (sizeof(VALUE))]; - } save_regs_gc_mark; - VALUE *stack_start, *stack_end; - - FLUSH_REGISTER_WINDOWS; - memset(&save_regs_gc_mark, 0, sizeof(save_regs_gc_mark)); - /* This assumes that all registers are saved into the jmp_buf (and stack) */ - rb_setjmp(save_regs_gc_mark.j); - - /* SET_STACK_END must be called in this function because - * the stack frame of this function may contain - * callee save registers and they should be marked. */ - SET_STACK_END; - GET_STACK_BOUNDS(stack_start, stack_end, 1); - -#ifdef RUBY_ASAN_ENABLED - objspace->marking_machine_context_ec = ec; -#endif - - each_location(objspace, save_regs_gc_mark.v, numberof(save_regs_gc_mark.v), gc_mark_machine_stack_location_maybe); - each_stack_location(objspace, ec, stack_start, stack_end, gc_mark_machine_stack_location_maybe); - -#ifdef RUBY_ASAN_ENABLED - objspace->marking_machine_context_ec = NULL; -#endif -} -#endif - -void -rb_gc_mark_machine_context(const rb_execution_context_t *ec) -{ - rb_objspace_t *objspace = &rb_objspace; -#ifdef RUBY_ASAN_ENABLED - objspace->marking_machine_context_ec = ec; -#endif - - VALUE *stack_start, *stack_end; - - GET_STACK_BOUNDS(stack_start, stack_end, 0); - RUBY_DEBUG_LOG("ec->th:%u stack_start:%p stack_end:%p", rb_ec_thread_ptr(ec)->serial, stack_start, stack_end); - - each_stack_location(objspace, ec, stack_start, stack_end, gc_mark_machine_stack_location_maybe); - int num_regs = sizeof(ec->machine.regs)/(sizeof(VALUE)); - each_location(objspace, (VALUE*)&ec->machine.regs, num_regs, gc_mark_machine_stack_location_maybe); - -#ifdef RUBY_ASAN_ENABLED - objspace->marking_machine_context_ec = NULL; -#endif -} - -static void -each_stack_location(rb_objspace_t *objspace, const rb_execution_context_t *ec, - const VALUE *stack_start, const VALUE *stack_end, void (*cb)(rb_objspace_t *, VALUE)) -{ - - gc_mark_locations(objspace, stack_start, stack_end, cb); - -#if defined(__mc68000__) - gc_mark_locations(objspace, - (VALUE*)((char*)stack_start + 2), - (VALUE*)((char*)stack_end - 2), cb); -#endif -} - -void -rb_mark_tbl(st_table *tbl) -{ - mark_tbl(&rb_objspace, tbl); -} - -void -rb_mark_tbl_no_pin(st_table *tbl) -{ - mark_tbl_no_pin(&rb_objspace, tbl); -} - -static void -gc_mark_maybe(rb_objspace_t *objspace, VALUE obj) -{ - (void)VALGRIND_MAKE_MEM_DEFINED(&obj, sizeof(obj)); - - if (is_pointer_to_heap(objspace, (void *)obj)) { - void *ptr = asan_unpoison_object_temporary(obj); - - /* Garbage can live on the stack, so do not mark or pin */ - switch (BUILTIN_TYPE(obj)) { - case T_ZOMBIE: - case T_NONE: - break; - default: - gc_mark_and_pin(objspace, obj); - } - - if (ptr) { - GC_ASSERT(BUILTIN_TYPE(obj) == T_NONE); - asan_poison_object(obj); - } - } -} - -static void -gc_stack_location_mark_maybe(rb_objspace_t *objspace, VALUE obj) -{ - (void)VALGRIND_MAKE_MEM_DEFINED(&obj, sizeof(obj)); - - if (is_pointer_to_heap(objspace, (void *)obj)) { - void *ptr = asan_unpoison_object_temporary(obj); - - /* Garbage can live on the stack, so do not mark or pin */ - switch (BUILTIN_TYPE(obj)) { - case T_ZOMBIE: - case T_NONE: - break; - default: - if (GET_OBJSPACE_OF_VALUE(obj) == objspace || FL_TEST(obj, FL_SHAREABLE) || !using_local_limits(objspace)) { - gc_mark_and_pin(objspace, obj); - } - } - - if (ptr) { - GC_ASSERT(BUILTIN_TYPE(obj) == T_NONE); - asan_poison_object(obj); - } - } -} - -void -rb_gc_mark_maybe(VALUE obj) -{ - gc_mark_maybe(&rb_objspace, obj); -} - -static inline int -gc_mark_set(rb_objspace_t *objspace, VALUE obj) -{ - ASSERT_ractor_safe_gc_state(); - //TODO assertion needed? - //VM_ASSERT(during_gc || heap_locked(objspace)); - if (RVALUE_MARKED(obj)) return 0; - MARK_IN_BITMAP(GET_HEAP_MARK_BITS(obj), obj); - return 1; -} - -static int -gc_remember_unprotected(rb_objspace_t *objspace, VALUE obj) -{ - struct heap_page *page = GET_HEAP_PAGE(obj); - bits_t *uncollectible_bits = &page->uncollectible_bits[0]; - - if (!MARKED_IN_BITMAP(uncollectible_bits, obj)) { - page->flags.has_uncollectible_wb_unprotected_objects = TRUE; - MARK_IN_BITMAP(uncollectible_bits, obj); - objspace->rgengc.uncollectible_wb_unprotected_objects++; - -#if RGENGC_PROFILE > 0 - objspace->profile.total_remembered_shady_object_count++; -#if RGENGC_PROFILE >= 2 - objspace->profile.remembered_shady_object_count_types[BUILTIN_TYPE(obj)]++; -#endif -#endif - return TRUE; - } - else { - return FALSE; - } -} - -static void -rgengc_check_relation(rb_objspace_t *objspace, VALUE obj) -{ - const VALUE old_parent = objspace->rgengc.parent_object; - - if (old_parent) { /* parent object is old */ - if (RVALUE_WB_UNPROTECTED(obj) || !RVALUE_OLD_P(obj)) { - rgengc_remember(objspace, old_parent); - } - } - - GC_ASSERT(old_parent == objspace->rgengc.parent_object); -} - -static void -gc_grey(rb_objspace_t *objspace, VALUE obj) -{ -#if RGENGC_CHECK_MODE - if (RVALUE_MARKED(obj) == FALSE) rb_bug("gc_grey: %s is not marked.", obj_info(obj)); - if (RVALUE_MARKING(obj) == TRUE) rb_bug("gc_grey: %s is marking/remembered.", obj_info(obj)); -#endif - - if (is_incremental_marking(objspace)) { - MARK_IN_BITMAP(GET_HEAP_MARKING_BITS(obj), obj); - } - - push_mark_stack(&objspace->mark_stack, obj); -} - -static void -gc_aging(VALUE obj) -{ - struct heap_page *page = GET_HEAP_PAGE(obj); - rb_objspace_t *objspace = page->objspace; - - GC_ASSERT(RVALUE_MARKING(obj) == FALSE); - check_rvalue_consistency(obj); - - if (!RVALUE_PAGE_WB_UNPROTECTED(page, obj)) { - if (!RVALUE_OLD_P(obj)) { - gc_report(3, objspace, "gc_aging: YOUNG: %s\n", obj_info(obj)); - RVALUE_AGE_INC(objspace, obj); - } - else if (is_full_marking(objspace)) { - GC_ASSERT(RVALUE_PAGE_UNCOLLECTIBLE(page, obj) == FALSE); - RVALUE_PAGE_OLD_UNCOLLECTIBLE_SET(objspace, page, obj); - } - } - check_rvalue_consistency(obj); - - objspace->marked_slots++; -} - -static bool -in_marking_range(rb_objspace_t *objspace, VALUE obj) -{ - return !FL_TEST_RAW(obj, FL_SHAREABLE) || GET_OBJSPACE_OF_VALUE(obj) == objspace; -} - -static void -check_not_tnone(VALUE obj) -{ - if (UNLIKELY(RB_TYPE_P(obj, T_NONE))) { /* check here will help debugging */ - rp(obj); - if (rb_multi_ractor_p()) { - rb_objspace_t *objspace = &rb_objspace; - if (objspace->flags.during_global_gc) { - rb_bug("try to mark T_NONE object (belonging to Ractor #%d) from global GC", GET_RACTOR_OF_VALUE(obj)->pub.id); - } - else { - rb_bug("try to mark T_NONE object (belonging to Ractor #%d) from local GC of Ractor #%d", GET_RACTOR_OF_VALUE(obj)->pub.id, objspace->local_data.ractor->pub.id); - } - } - else { - rb_bug("try to mark T_NONE object"); - } - } -} - -NOINLINE(static void gc_mark_ptr(rb_objspace_t *objspace, VALUE obj)); -static void reachable_objects_from_callback(VALUE obj); - -static void -gc_mark_ptr(rb_objspace_t *objspace, VALUE obj) -{ - VM_ASSERT(GET_OBJSPACE_OF_VALUE(obj) == objspace || FL_TEST(obj, FL_SHAREABLE) || !using_local_limits(objspace)); - - if (LIKELY(during_gc)) { - - if (!ruby_single_main_objspace) { - if (objspace->flags.during_global_gc) { - VM_ASSERT(is_full_marking(objspace)); - if (objspace->current_parent_objspace != GET_OBJSPACE_OF_VALUE(obj)) { - check_not_tnone(obj); - mark_in_external_reference_tbl(objspace->current_parent_objspace, obj); - } - } - else { - if (!in_marking_range(objspace, obj)) { - if (is_full_marking(objspace)) { - check_not_tnone(obj); - mark_in_external_reference_tbl(objspace, obj); - } - return; - } - } - } - - rgengc_check_relation(objspace, obj); - if (!gc_mark_set(objspace, obj)) return; /* already marked */ - - if (0) { // for debug GC marking miss - if (objspace->rgengc.parent_object) { - RUBY_DEBUG_LOG("%p (%s) parent:%p (%s)", - (void *)obj, obj_type_name(obj), - (void *)objspace->rgengc.parent_object, obj_type_name(objspace->rgengc.parent_object)); - } - else { - RUBY_DEBUG_LOG("%p (%s)", (void *)obj, obj_type_name(obj)); - } - } - - check_not_tnone(obj); - gc_aging(obj); - gc_grey(objspace, obj); - } - else { - VM_ASSERT(dont_gc_val() == TRUE); - if (GET_OBJSPACE_OF_VALUE(obj) == objspace) { - reachable_objects_from_callback(obj); - } - } -} - -static inline void -gc_pin(rb_objspace_t *objspace, VALUE obj) -{ - GC_ASSERT(is_markable_object(obj)); - if (UNLIKELY(objspace->flags.during_compacting)) { - if (LIKELY(during_gc)) { - if (!RVALUE_PINNED(obj)) { - GC_ASSERT(GET_HEAP_PAGE(obj)->pinned_slots <= GET_HEAP_PAGE(obj)->total_slots); - GET_HEAP_PAGE(obj)->pinned_slots++; - MARK_IN_BITMAP(GET_HEAP_PINNED_BITS(obj), obj); - } - } - } -} - -static inline void -gc_mark_and_pin(rb_objspace_t *objspace, VALUE obj) -{ - if (!is_markable_object(obj)) return; - gc_pin(objspace, obj); - gc_mark_ptr(objspace, obj); -} - -static inline void -gc_mark(rb_objspace_t *objspace, VALUE obj) -{ - if (!is_markable_object(obj)) return; - gc_mark_ptr(objspace, obj); -} - -void -rb_gc_mark_movable(VALUE ptr) -{ - gc_mark(&rb_objspace, ptr); -} - -void -rb_gc_mark(VALUE ptr) -{ - gc_mark_and_pin(&rb_objspace, ptr); -} - -void -rb_gc_mark_and_move(VALUE *ptr) -{ - rb_objspace_t *objspace = &rb_objspace; - if (RB_SPECIAL_CONST_P(*ptr)) return; - - if (UNLIKELY(objspace->flags.during_reference_updating)) { - GC_ASSERT(objspace->flags.during_compacting); - GC_ASSERT(during_gc); - - *ptr = rb_gc_location(*ptr); - } - else { - gc_mark_ptr(objspace, *ptr); - } -} - -void -rb_gc_mark_weak(VALUE *ptr) -{ - rb_objspace_t *objspace = &rb_objspace; - - if (UNLIKELY(!during_gc)) return; - - VALUE obj = *ptr; - if (RB_SPECIAL_CONST_P(obj) || (using_local_limits(objspace) && GET_OBJSPACE_OF_VALUE(obj) != objspace)) return; - - GC_ASSERT(objspace->rgengc.parent_object == 0 || FL_TEST(objspace->rgengc.parent_object, FL_WB_PROTECTED)); - - if (UNLIKELY(RB_TYPE_P(obj, T_NONE))) { - rp(obj); - rb_bug("try to mark T_NONE object"); - } - - /* If we are in a minor GC and the other object is old, then obj should - * already be marked and cannot be reclaimed in this GC cycle so we don't - * need to add it to the weak refences list. */ - if (!is_full_marking(objspace) && RVALUE_OLD_P(obj)) { - GC_ASSERT(RVALUE_MARKED(obj)); - GC_ASSERT(!objspace->flags.during_compacting); - - return; - } - - rgengc_check_relation(objspace, obj); - - DURING_GC_COULD_MALLOC_REGION_START(); - { - rb_darray_append(&objspace->weak_references, ptr); - } - DURING_GC_COULD_MALLOC_REGION_END(); - - objspace->profile.weak_references_count++; -} - -void -rb_gc_remove_weak(VALUE parent_obj, VALUE *ptr) -{ - rb_objspace_t *objspace = &rb_objspace; - - /* If we're not incremental marking, then the state of the objects can't - * change so we don't need to do anything. */ - if (!is_incremental_marking(objspace)) return; - /* If parent_obj has not been marked, then ptr has not yet been marked - * weak, so we don't need to do anything. */ - if (!RVALUE_MARKED(parent_obj)) return; - - VALUE **ptr_ptr; - rb_darray_foreach(objspace->weak_references, i, ptr_ptr) { - if (*ptr_ptr == ptr) { - *ptr_ptr = NULL; - break; - } - } -} - -static inline void -gc_mark_set_parent(rb_objspace_t *objspace, VALUE obj) -{ - if (RVALUE_OLD_P(obj)) { - objspace->rgengc.parent_object = obj; - } - else { - objspace->rgengc.parent_object = Qfalse; - } - -#if VM_CHECK_MODE > 0 - objspace->current_parent_objspace = GET_OBJSPACE_OF_VALUE(obj); -#else - if (!using_local_limits(objspace)) { - objspace->current_parent_objspace = GET_OBJSPACE_OF_VALUE(obj); - } -#endif -} - -static inline void -gc_mark_reset_parent(rb_objspace_t *objspace) -{ - objspace->rgengc.parent_object = Qfalse; - objspace->current_parent_objspace = objspace; -} - -static bool -gc_declarative_marking_p(const rb_data_type_t *type) -{ - return (type->flags & RUBY_TYPED_DECL_MARKING) != 0; -} - -static void mark_cvc_tbl(rb_objspace_t *objspace, VALUE klass); - -static void -gc_mark_children(rb_objspace_t *objspace, VALUE obj) -{ - register RVALUE *any = RANY(obj); - gc_mark_set_parent(objspace, obj); - - if (FL_TEST(obj, FL_EXIVAR)) { - rb_mark_generic_ivar(obj); - } - - switch (BUILTIN_TYPE(obj)) { - case T_FLOAT: - case T_BIGNUM: - case T_SYMBOL: - /* Not immediates, but does not have references and singleton class. - * - * RSYMBOL(obj)->fstr intentionally not marked. See log for 96815f1e - * ("symbol.c: remove rb_gc_mark_symbols()") */ - return; - - case T_NIL: - case T_FIXNUM: - rb_bug("rb_gc_mark() called for broken object"); - break; - - case T_NODE: - UNEXPECTED_NODE(rb_gc_mark); - break; - - case T_IMEMO: - rb_imemo_mark_and_move(obj, false); - return; - - default: - break; - } - - gc_mark(objspace, any->as.basic.klass); - - rb_vm_t *vm = GET_VM(); - - switch (BUILTIN_TYPE(obj)) { - case T_CLASS: - if (FL_TEST(obj, FL_SINGLETON)) { - gc_mark(objspace, RCLASS_ATTACHED_OBJECT(obj)); - } - // Continue to the shared T_CLASS/T_MODULE - case T_MODULE: - if (RCLASS_SUPER(obj)) { - gc_mark(objspace, RCLASS_SUPER(obj)); - } - - mark_m_tbl(objspace, RCLASS_M_TBL(obj)); - mark_cvc_tbl(objspace, obj); - rb_cc_table_mark(obj); - if (rb_shape_obj_too_complex(obj)) { - mark_tbl_no_pin(objspace, (st_table *)RCLASS_IVPTR(obj)); - } - else { - for (attr_index_t i = 0; i < RCLASS_IV_COUNT(obj); i++) { - gc_mark(objspace, RCLASS_IVPTR(obj)[i]); - } - } - if (objspace == vm->objspace || objspace->flags.during_global_gc) mark_const_tbl(objspace, RCLASS_CONST_TBL(obj)); - rb_native_mutex_lock(&vm->classpath_lock); - gc_mark(objspace, RCLASS_EXT(obj)->classpath); - rb_native_mutex_unlock(&vm->classpath_lock); - break; - - case T_ICLASS: - if (RICLASS_OWNS_M_TBL_P(obj)) { - mark_m_tbl(objspace, RCLASS_M_TBL(obj)); - } - if (RCLASS_SUPER(obj)) { - gc_mark(objspace, RCLASS_SUPER(obj)); - } - - if (RCLASS_INCLUDER(obj)) { - gc_mark(objspace, RCLASS_INCLUDER(obj)); - } - mark_m_tbl(objspace, RCLASS_CALLABLE_M_TBL(obj)); - rb_cc_table_mark(obj); - break; - - case T_ARRAY: - if (ARY_SHARED_P(obj)) { - VALUE root = ARY_SHARED_ROOT(obj); - gc_mark(objspace, root); - } - else { - long i, len = RARRAY_LEN(obj); - const VALUE *ptr = RARRAY_CONST_PTR(obj); - for (i=0; i < len; i++) { - gc_mark(objspace, ptr[i]); - } - } - break; - - case T_HASH: - mark_hash(objspace, obj); - break; - - case T_STRING: - if (STR_SHARED_P(obj)) { - if (STR_EMBED_P(any->as.string.as.heap.aux.shared)) { - /* Embedded shared strings cannot be moved because this string - * points into the slot of the shared string. There may be code - * using the RSTRING_PTR on the stack, which would pin this - * string but not pin the shared string, causing it to move. */ - gc_mark_and_pin(objspace, any->as.string.as.heap.aux.shared); - } - else { - gc_mark(objspace, any->as.string.as.heap.aux.shared); - } - } - break; - - case T_DATA: - { - void *const ptr = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj); - - if (ptr) { - if (RTYPEDDATA_P(obj) && gc_declarative_marking_p(any->as.typeddata.type)) { - size_t *offset_list = (size_t *)RANY(obj)->as.typeddata.type->function.dmark; - - for (size_t offset = *offset_list; offset != RUBY_REF_END; offset = *offset_list++) { - rb_gc_mark_movable(*(VALUE *)((char *)ptr + offset)); - } - } - else { - RUBY_DATA_FUNC mark_func = RTYPEDDATA_P(obj) ? - any->as.typeddata.type->function.dmark : - any->as.data.dmark; - if (mark_func) (*mark_func)(ptr); - } - } - } - break; - - case T_OBJECT: - { - rb_shape_t *shape = rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj)); - if (rb_shape_obj_too_complex(obj)) { - mark_tbl_no_pin(objspace, ROBJECT_IV_HASH(obj)); - } - else { - const VALUE * const ptr = ROBJECT_IVPTR(obj); - - uint32_t i, len = ROBJECT_IV_COUNT(obj); - for (i = 0; i < len; i++) { - gc_mark(objspace, ptr[i]); - } - } - if (shape) { - VALUE klass = RBASIC_CLASS(obj); - - // Increment max_iv_count if applicable, used to determine size pool allocation - attr_index_t num_of_ivs = shape->next_iv_index; - if (RCLASS_EXT(klass)->max_iv_count < num_of_ivs) { - RCLASS_EXT(klass)->max_iv_count = num_of_ivs; - } - } - } - break; - - case T_FILE: - if (any->as.file.fptr) { - gc_mark(objspace, any->as.file.fptr->self); - gc_mark(objspace, any->as.file.fptr->pathv); - gc_mark(objspace, any->as.file.fptr->tied_io_for_writing); - gc_mark(objspace, any->as.file.fptr->writeconv_asciicompat); - gc_mark(objspace, any->as.file.fptr->writeconv_pre_ecopts); - gc_mark(objspace, any->as.file.fptr->encs.ecopts); - gc_mark(objspace, any->as.file.fptr->write_lock); - gc_mark(objspace, any->as.file.fptr->timeout); - } - break; - - case T_REGEXP: - gc_mark(objspace, any->as.regexp.src); - break; - - case T_MATCH: - gc_mark(objspace, any->as.match.regexp); - if (any->as.match.str) { - gc_mark(objspace, any->as.match.str); - } - break; - - case T_RATIONAL: - gc_mark(objspace, any->as.rational.num); - gc_mark(objspace, any->as.rational.den); - break; - - case T_COMPLEX: - gc_mark(objspace, any->as.complex.real); - gc_mark(objspace, any->as.complex.imag); - break; - - case T_STRUCT: - { - long i; - const long len = RSTRUCT_LEN(obj); - const VALUE * const ptr = RSTRUCT_CONST_PTR(obj); - - for (i=0; i not incremental (do all) - * incremental: n -> mark at most `n' objects - */ -static inline int -gc_mark_stacked_objects(rb_objspace_t *objspace, int incremental, size_t count) -{ - mark_stack_t *mstack = &objspace->mark_stack; - VALUE obj; - size_t marked_slots_at_the_beginning = objspace->marked_slots; - size_t popped_count = 0; - - while (pop_mark_stack(mstack, &obj)) { - if (UNDEF_P(obj)) continue; /* skip */ - - if (RGENGC_CHECK_MODE && !RVALUE_MARKED(obj)) { - rb_bug("gc_mark_stacked_objects: %s is not marked.", obj_info(obj)); - } - gc_mark_children(objspace, obj); - - if (incremental) { - if (RGENGC_CHECK_MODE && !RVALUE_MARKING(obj)) { - rb_bug("gc_mark_stacked_objects: incremental, but marking bit is 0"); - } - CLEAR_IN_BITMAP(GET_HEAP_MARKING_BITS(obj), obj); - popped_count++; - - if (popped_count + (objspace->marked_slots - marked_slots_at_the_beginning) > count) { - break; - } - } - else { - /* just ignore marking bits */ - } - } - - if (RGENGC_CHECK_MODE >= 3) gc_verify_internal_consistency(objspace); - - if (is_mark_stack_empty(mstack)) { - shrink_stack_chunk_cache(mstack); - gc_mark_reset_parent(objspace); - return TRUE; - } - else { - return FALSE; - } -} - -static int -gc_mark_stacked_objects_incremental(rb_objspace_t *objspace, size_t count) -{ - if (gc_mark_stacked_objects(objspace, TRUE, count) == TRUE && mark_externally_modifiable_tables(objspace)) { - return TRUE; - } - else - { - return FALSE; - } -} - -static int -gc_mark_stacked_objects_all(rb_objspace_t *objspace) -{ - while (true) { - gc_mark_stacked_objects(objspace, FALSE, 0); - if (mark_externally_modifiable_tables(objspace)) return TRUE; - } -} - - -#if PRINT_ROOT_TICKS -#define MAX_TICKS 0x100 -static tick_t mark_ticks[MAX_TICKS]; -static const char *mark_ticks_categories[MAX_TICKS]; - -static void -show_mark_ticks(void) -{ - int i; - fprintf(stderr, "mark ticks result:\n"); - for (i=0; iobjspace) { - rb_vm_mark(vm); - if (vm->self) gc_mark(objspace, vm->self); - } - mark_zombie_threads(objspace); - mark_absorbed_threads_tbl(objspace); - - MARK_CHECKPOINT("cache_table"); - mark_global_cc_cache_table(objspace); - - MARK_CHECKPOINT("ractor"); - rb_ractor_related_objects_mark(objspace->local_data.ractor); - mark_contained_ractor_tbl(objspace); - - MARK_CHECKPOINT("finalizers"); - mark_finalizer_tbl(objspace, finalizer_table); - - MARK_CHECKPOINT("machine_context"); - mark_current_machine_context(objspace, ec); - - /* mark protected global variables */ - - MARK_CHECKPOINT("end_proc"); - rb_mark_end_proc(objspace); - - if (objspace == vm->objspace) { - MARK_CHECKPOINT("global_tbl"); - rb_gc_mark_global_tbl(); - } - - rb_global_space_t *global_space = &rb_global_space; - - MARK_CHECKPOINT("object_id"); - rb_native_mutex_lock(&objspace->local_data.obj_id_lock); - mark_tbl_no_pin(objspace, objspace->local_data.obj_to_id_tbl); /* Only mark ids */ - rb_native_mutex_unlock(&objspace->local_data.obj_id_lock); - - if (using_local_limits(objspace) || !during_gc) { - MARK_CHECKPOINT("shared_reference_tbl"); - mark_shared_reference_tbl(objspace); - mark_received_received_obj_tbl(objspace); - } - - if (stress_to_class) rb_gc_mark(stress_to_class); - - MARK_CHECKPOINT("finish"); -#undef MARK_CHECKPOINT -} - -#if RGENGC_CHECK_MODE >= 4 - -#define MAKE_ROOTSIG(obj) (((VALUE)(obj) << 1) | 0x01) -#define IS_ROOTSIG(obj) ((VALUE)(obj) & 0x01) -#define GET_ROOTSIG(obj) ((const char *)((VALUE)(obj) >> 1)) - -struct reflist { - VALUE *list; - int pos; - int size; -}; - -static struct reflist * -reflist_create(VALUE obj) -{ - struct reflist *refs = xmalloc(sizeof(struct reflist)); - refs->size = 1; - refs->list = ALLOC_N(VALUE, refs->size); - refs->list[0] = obj; - refs->pos = 1; - return refs; -} - -static void -reflist_destruct(struct reflist *refs) -{ - xfree(refs->list); - xfree(refs); -} - -static void -reflist_add(struct reflist *refs, VALUE obj) -{ - if (refs->pos == refs->size) { - refs->size *= 2; - SIZED_REALLOC_N(refs->list, VALUE, refs->size, refs->size/2); - } - - refs->list[refs->pos++] = obj; -} - -static void -reflist_dump(struct reflist *refs) -{ - int i; - for (i=0; ipos; i++) { - VALUE obj = refs->list[i]; - if (IS_ROOTSIG(obj)) { /* root */ - fprintf(stderr, "", GET_ROOTSIG(obj)); - } - else { - fprintf(stderr, "<%s>", obj_info(obj)); - } - if (i+1 < refs->pos) fprintf(stderr, ", "); - } -} - -static int -reflist_referred_from_machine_context(struct reflist *refs) -{ - int i; - for (i=0; ipos; i++) { - VALUE obj = refs->list[i]; - if (IS_ROOTSIG(obj) && strcmp(GET_ROOTSIG(obj), "machine_context") == 0) return 1; - } - return 0; -} - -struct allrefs { - rb_objspace_t *objspace; - /* a -> obj1 - * b -> obj1 - * c -> obj1 - * c -> obj2 - * d -> obj3 - * #=> {obj1 => [a, b, c], obj2 => [c, d]} - */ - struct st_table *references; - const char *category; - VALUE root_obj; - mark_stack_t mark_stack; -}; - -static int -allrefs_add(struct allrefs *data, VALUE obj) -{ - struct reflist *refs; - st_data_t r; - - if (st_lookup(data->references, obj, &r)) { - refs = (struct reflist *)r; - reflist_add(refs, data->root_obj); - return 0; - } - else { - refs = reflist_create(data->root_obj); - st_insert(data->references, obj, (st_data_t)refs); - return 1; - } -} - -static void -allrefs_i(VALUE obj, void *ptr) -{ - struct allrefs *data = (struct allrefs *)ptr; - - if (allrefs_add(data, obj)) { - push_mark_stack(&data->mark_stack, obj); - } -} - -static void -allrefs_roots_i(VALUE obj, void *ptr) -{ - struct allrefs *data = (struct allrefs *)ptr; - if (strlen(data->category) == 0) rb_bug("!!!"); - data->root_obj = MAKE_ROOTSIG(data->category); - - if (allrefs_add(data, obj)) { - push_mark_stack(&data->mark_stack, obj); - } -} -#define PUSH_MARK_FUNC_DATA(v) do { \ - struct gc_mark_func_data_struct *prev_mark_func_data = GET_RACTOR()->mfd; \ - GET_RACTOR()->mfd = (v); - -#define POP_MARK_FUNC_DATA() GET_RACTOR()->mfd = prev_mark_func_data;} while (0) - -static st_table * -objspace_allrefs(rb_objspace_t *objspace) -{ - struct allrefs data; - struct gc_mark_func_data_struct mfd; - VALUE obj; - int prev_dont_gc = dont_gc_val(); - dont_gc_on(); - - data.objspace = objspace; - data.references = st_init_numtable(); - init_mark_stack(&data.mark_stack); - - mfd.mark_func = allrefs_roots_i; - mfd.data = &data; - - /* traverse root objects */ - PUSH_MARK_FUNC_DATA(&mfd); - GET_RACTOR()->mfd = &mfd; - gc_mark_roots(objspace, &data.category); - POP_MARK_FUNC_DATA(); - - /* traverse rest objects reachable from root objects */ - while (pop_mark_stack(&data.mark_stack, &obj)) { - rb_objspace_reachable_objects_from(data.root_obj = obj, allrefs_i, &data); - } - free_stack_chunks(&data.mark_stack); - - dont_gc_set(prev_dont_gc); - return data.references; -} - -static int -objspace_allrefs_destruct_i(st_data_t key, st_data_t value, st_data_t ptr) -{ - struct reflist *refs = (struct reflist *)value; - reflist_destruct(refs); - return ST_CONTINUE; -} - -static void -objspace_allrefs_destruct(struct st_table *refs) -{ - st_foreach(refs, objspace_allrefs_destruct_i, 0); - st_free_table(refs); -} - -#if RGENGC_CHECK_MODE >= 5 -static int -allrefs_dump_i(st_data_t k, st_data_t v, st_data_t ptr) -{ - VALUE obj = (VALUE)k; - struct reflist *refs = (struct reflist *)v; - fprintf(stderr, "[allrefs_dump_i] %s <- ", obj_info(obj)); - reflist_dump(refs); - fprintf(stderr, "\n"); - return ST_CONTINUE; -} - -static void -allrefs_dump(rb_objspace_t *objspace) -{ - VALUE size = objspace->rgengc.allrefs_table->num_entries; - fprintf(stderr, "[all refs] (size: %"PRIuVALUE")\n", size); - st_foreach(objspace->rgengc.allrefs_table, allrefs_dump_i, 0); -} -#endif - -static int -gc_check_after_marks_i(st_data_t k, st_data_t v, st_data_t ptr) -{ - VALUE obj = k; - struct reflist *refs = (struct reflist *)v; - rb_objspace_t *objspace = (rb_objspace_t *)ptr; - - /* object should be marked or oldgen */ - if (!RVALUE_MARKED(obj)) { - fprintf(stderr, "gc_check_after_marks_i: %s is not marked and not oldgen.\n", obj_info(obj)); - fprintf(stderr, "gc_check_after_marks_i: %p is referred from ", (void *)obj); - reflist_dump(refs); - - if (reflist_referred_from_machine_context(refs)) { - fprintf(stderr, " (marked from machine stack).\n"); - /* marked from machine context can be false positive */ - } - else { - objspace->rgengc.error_count++; - fprintf(stderr, "\n"); - } - } - return ST_CONTINUE; -} - -static void -gc_marks_check(rb_objspace_t *objspace, st_foreach_callback_func *checker_func, const char *checker_name) -{ - size_t saved_malloc_increase = objspace->malloc_params.increase; -#if RGENGC_ESTIMATE_OLDMALLOC - size_t saved_oldmalloc_increase = objspace->rgengc.oldmalloc_increase; -#endif - VALUE already_disabled = rb_objspace_gc_disable(objspace); - - objspace->rgengc.allrefs_table = objspace_allrefs(objspace); - - if (checker_func) { - st_foreach(objspace->rgengc.allrefs_table, checker_func, (st_data_t)objspace); - } - - if (objspace->rgengc.error_count > 0) { -#if RGENGC_CHECK_MODE >= 5 - allrefs_dump(objspace); -#endif - if (checker_name) rb_bug("%s: GC has problem.", checker_name); - } - - objspace_allrefs_destruct(objspace->rgengc.allrefs_table); - objspace->rgengc.allrefs_table = 0; - - if (already_disabled == Qfalse) rb_objspace_gc_enable(objspace); - objspace->malloc_params.increase = saved_malloc_increase; -#if RGENGC_ESTIMATE_OLDMALLOC - objspace->rgengc.oldmalloc_increase = saved_oldmalloc_increase; -#endif -} -#endif /* RGENGC_CHECK_MODE >= 4 */ - -struct verify_internal_consistency_struct { - rb_objspace_t *objspace; - int err_count; - size_t live_object_count; - size_t zombie_object_count; - - VALUE parent; - size_t old_object_count; - size_t remembered_shady_count; -}; - -static void -check_generation_i(const VALUE child, void *ptr) -{ - struct verify_internal_consistency_struct *data = (struct verify_internal_consistency_struct *)ptr; - const VALUE parent = data->parent; - - if (RGENGC_CHECK_MODE) GC_ASSERT(RVALUE_OLD_P(parent)); - - rb_objspace_t *objspace = &rb_objspace; - bool absorption_correction_needed = objspace->rgengc.need_major_gc & GPR_FLAG_MAJOR_BY_ABSORB; - if (absorption_correction_needed) return; - if (!RVALUE_OLD_P(child)) { - if (!RVALUE_REMEMBERED(parent) && - !RVALUE_REMEMBERED(child) && - !RVALUE_UNCOLLECTIBLE(child)) { - fprintf(stderr, "verify_internal_consistency_reachable_i: WB miss (O->Y) %s -> %s\n", obj_info(parent), obj_info(child)); - data->err_count++; - } - } -} - -static void -check_color_i(const VALUE child, void *ptr) -{ - struct verify_internal_consistency_struct *data = (struct verify_internal_consistency_struct *)ptr; - const VALUE parent = data->parent; - - if (!RVALUE_WB_UNPROTECTED(parent) && RVALUE_WHITE_P(child)) { - fprintf(stderr, "verify_internal_consistency_reachable_i: WB miss (B->W) - %s -> %s\n", - obj_info(parent), obj_info(child)); - data->err_count++; - } -} - -static void -check_limmune_i(const VALUE child, void *ptr) -{ - struct verify_internal_consistency_struct *data = (struct verify_internal_consistency_struct *)ptr; - const VALUE parent = data->parent; - - if (false && MUTABLE_SHAREABLE(parent)) { - if (!RVALUE_LOCAL_IMMUNE(child)) { - fprintf(stderr, "check_limmune_i: (mutable-shareable -> non-local-immune) - %s -> %s\n", - obj_info(parent), obj_info(child)); - } - data->err_count++; - } - if (RVALUE_LOCAL_IMMUNE(parent)) { - if (BUILTIN_TYPE(parent) != T_CLASS) { - if (!RVALUE_LOCAL_IMMUNE(child)) { - fprintf(stderr, "check_limmune_i: (local-immune -> non-local-immune) - %s -> %s\n", - obj_info(parent), obj_info(child)); - data->err_count++; - rb_bug("sigh"); - } - } - } -} - -static void -check_children_i(const VALUE child, void *ptr) -{ - struct verify_internal_consistency_struct *data = (struct verify_internal_consistency_struct *)ptr; - if (check_rvalue_consistency_force(child, FALSE) != 0) { - fprintf(stderr, "check_children_i: %s has error (referenced from %s)", - obj_info(child), obj_info(data->parent)); - rb_print_backtrace(stderr); /* C backtrace will help to debug */ - - data->err_count++; - } -} - -static int -verify_internal_consistency_i(void *page_start, void *page_end, size_t stride, - struct verify_internal_consistency_struct *data) -{ - VALUE obj; - rb_objspace_t *objspace = data->objspace; - - for (obj = (VALUE)page_start; obj != (VALUE)page_end; obj += stride) { - void *poisoned = asan_unpoison_object_temporary(obj); - - if (is_live_object(objspace, obj)) { - /* count objects */ - data->live_object_count++; - data->parent = obj; - - /* Normally, we don't expect T_MOVED objects to be in the heap. - * But they can stay alive on the stack, */ - if (!gc_object_moved_p(objspace, obj)) { - /* moved slots don't have children */ - rb_objspace_reachable_objects_from(obj, check_children_i, (void *)data); - } - - /* check health of children */ - if (RVALUE_OLD_P(obj)) { - if (RVALUE_MARKED(obj)) { - data->old_object_count++; - } - else { - VM_ASSERT(RVALUE_LOCAL_IMMUNE(obj)); - } - } - if (RVALUE_WB_UNPROTECTED(obj) && RVALUE_UNCOLLECTIBLE(obj)) data->remembered_shady_count++; - - if (!is_marking(objspace) && RVALUE_OLD_P(obj)) { - /* reachable objects from an oldgen object should be old or (young with remember) */ - data->parent = obj; - rb_objspace_reachable_objects_from(obj, check_generation_i, (void *)data); - } - - if (is_incremental_marking(objspace)) { - if (RVALUE_BLACK_P(obj)) { - /* reachable objects from black objects should be black or grey objects */ - data->parent = obj; - rb_objspace_reachable_objects_from(obj, check_color_i, (void *)data); - } - } - rb_objspace_reachable_objects_from(obj, check_limmune_i, (void *)data); - } - else { - if (BUILTIN_TYPE(obj) == T_ZOMBIE) { - data->zombie_object_count++; - - if ((RBASIC(obj)->flags & ~ZOMBIE_OBJ_KEPT_FLAGS) != T_ZOMBIE) { - fprintf(stderr, "verify_internal_consistency_i: T_ZOMBIE has extra flags set: %s\n", - obj_info(obj)); - data->err_count++; - } - - if (!!FL_TEST(obj, FL_FINALIZE) != !!st_is_member(finalizer_table, obj)) { - fprintf(stderr, "verify_internal_consistency_i: FL_FINALIZE %s but %s finalizer_table: %s\n", - FL_TEST(obj, FL_FINALIZE) ? "set" : "not set", st_is_member(finalizer_table, obj) ? "in" : "not in", - obj_info(obj)); - data->err_count++; - } - } - } - if (poisoned) { - GC_ASSERT(BUILTIN_TYPE(obj) == T_NONE); - asan_poison_object(obj); - } - } - - return 0; -} - -static int -gc_verify_heap_page(rb_objspace_t *objspace, struct heap_page *page, VALUE obj) -{ - unsigned int has_remembered_shady = FALSE; - unsigned int has_remembered_old = FALSE; - int remembered_old_objects = 0; - int free_objects = 0; - int zombie_objects = 0; - - short slot_size = page->slot_size; - uintptr_t start = (uintptr_t)page->start; - uintptr_t end = start + page->total_slots * slot_size; - - for (uintptr_t ptr = start; ptr < end; ptr += slot_size) { - VALUE val = (VALUE)ptr; - void *poisoned = asan_unpoison_object_temporary(val); - enum ruby_value_type type = BUILTIN_TYPE(val); - - if (type == T_NONE) free_objects++; - if (type == T_ZOMBIE) zombie_objects++; - if (RVALUE_PAGE_UNCOLLECTIBLE(page, val) && RVALUE_PAGE_WB_UNPROTECTED(page, val)) { - has_remembered_shady = TRUE; - } - if (RVALUE_PAGE_MARKING(page, val)) { - has_remembered_old = TRUE; - remembered_old_objects++; - } - - if (poisoned) { - GC_ASSERT(BUILTIN_TYPE(val) == T_NONE); - asan_poison_object(val); - } - } - - if (!is_incremental_marking(objspace) && - page->flags.has_remembered_objects == FALSE && has_remembered_old == TRUE) { - - for (uintptr_t ptr = start; ptr < end; ptr += slot_size) { - VALUE val = (VALUE)ptr; - if (RVALUE_PAGE_MARKING(page, val)) { - fprintf(stderr, "marking -> %s\n", obj_info(val)); - } - } - rb_bug("page %p's has_remembered_objects should be false, but there are remembered old objects (%d). %s", - (void *)page, remembered_old_objects, obj ? obj_info(obj) : ""); - } - - if (page->flags.has_uncollectible_wb_unprotected_objects == FALSE && has_remembered_shady == TRUE) { - rb_bug("page %p's has_remembered_shady should be false, but there are remembered shady objects. %s", - (void *)page, obj ? obj_info(obj) : ""); - } - - if (0) { - /* free_slots may not equal to free_objects */ - if (page->free_slots != free_objects) { - rb_bug("page %p's free_slots should be %d, but %d", (void *)page, page->free_slots, free_objects); - } - } - if (page->final_slots != zombie_objects) { - rb_bug("page %p's final_slots should be %d, but %d", (void *)page, page->final_slots, zombie_objects); - } - - return remembered_old_objects; -} - -static int -gc_verify_heap_pages_(rb_objspace_t *objspace, struct ccan_list_head *head) -{ - int remembered_old_objects = 0; - struct heap_page *page = 0; - - ccan_list_for_each(head, page, page_node) { - asan_unlock_freelist(page); - RVALUE *p = page->freelist; - while (p) { - VALUE vp = (VALUE)p; - VALUE prev = vp; - asan_unpoison_object(vp, false); - if (BUILTIN_TYPE(vp) != T_NONE) { - fprintf(stderr, "freelist slot expected to be T_NONE but was: %s\n", obj_info(vp)); - } - p = p->as.free.next; - asan_poison_object(prev); - } - asan_lock_freelist(page); - - if (page->flags.has_remembered_objects == FALSE) { - remembered_old_objects += gc_verify_heap_page(objspace, page, Qfalse); - } - } - - return remembered_old_objects; -} - -static int -gc_verify_heap_pages(rb_objspace_t *objspace) -{ - int remembered_old_objects = 0; - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - remembered_old_objects += gc_verify_heap_pages_(objspace, &(SIZE_POOL_EDEN_HEAP(&size_pools[i])->pages)); - remembered_old_objects += gc_verify_heap_pages_(objspace, &(SIZE_POOL_TOMB_HEAP(&size_pools[i])->pages)); - } - return remembered_old_objects; -} - -/* - * call-seq: - * GC.verify_internal_consistency -> nil - * - * Verify internal consistency. - * - * This method is implementation specific. - * Now this method checks generational consistency - * if RGenGC is supported. - */ -static VALUE -gc_verify_internal_consistency_m(VALUE dummy) -{ - gc_verify_internal_consistency(&rb_objspace); - return Qnil; -} - -static void -gc_verify_internal_consistency_(rb_objspace_t *objspace) -{ - struct verify_internal_consistency_struct data = {0}; - - data.objspace = objspace; - gc_report(5, objspace, "gc_verify_internal_consistency: start\n"); - - /* check relations */ - for (size_t i = 0; i < heap_allocated_pages; i++) { - struct heap_page *page = heap_pages_sorted[i]; - short slot_size = page->slot_size; - - uintptr_t start = (uintptr_t)page->start; - uintptr_t end = start + page->total_slots * slot_size; - - verify_internal_consistency_i((void *)start, (void *)end, slot_size, &data); - } - - if (data.err_count != 0) { -#if RGENGC_CHECK_MODE >= 5 - objspace->rgengc.error_count = data.err_count; - gc_marks_check(objspace, NULL, NULL); - allrefs_dump(objspace); -#endif - rb_bug("gc_verify_internal_consistency: found internal inconsistency."); - } - - /* check heap_page status */ - gc_verify_heap_pages(objspace); - - /* check counters */ - - if (!is_lazy_sweeping(objspace) && - !finalizing && - ruby_single_main_ractor != NULL) { - if (objspace_live_slots(objspace) != data.live_object_count) { - fprintf(stderr, "heap_pages_final_slots: %"PRIdSIZE", total_freed_objects: %"PRIdSIZE"\n", - heap_pages_final_slots, total_freed_objects(objspace)); - rb_bug("inconsistent live slot number: expect %"PRIuSIZE", but %"PRIuSIZE".", - objspace_live_slots(objspace), data.live_object_count); - } - } - - if (!is_marking(objspace)) { - if (objspace->rgengc.old_objects != data.old_object_count) { - rb_bug("inconsistent old slot number: expect %"PRIuSIZE", but %"PRIuSIZE".", - objspace->rgengc.old_objects, data.old_object_count); - } - if (objspace->rgengc.uncollectible_wb_unprotected_objects != data.remembered_shady_count) { - rb_bug("inconsistent number of wb unprotected objects: expect %"PRIuSIZE", but %"PRIuSIZE".", - objspace->rgengc.uncollectible_wb_unprotected_objects, data.remembered_shady_count); - } - } - - if (!finalizing) { - size_t list_count = 0; - - { - VALUE z = heap_pages_deferred_final; - while (z) { - list_count++; - z = RZOMBIE(z)->next; - } - } - - if (heap_pages_final_slots != data.zombie_object_count || - heap_pages_final_slots != list_count) { - - rb_bug("inconsistent finalizing object count:\n" - " expect %"PRIuSIZE"\n" - " but %"PRIuSIZE" zombies\n" - " heap_pages_deferred_final list has %"PRIuSIZE" items.", - heap_pages_final_slots, - data.zombie_object_count, - list_count); - } - } - - gc_report(5, objspace, "gc_verify_internal_consistency: OK\n"); -} - -static void -gc_verify_internal_consistency(rb_objspace_t *objspace) -{ - RB_VM_LOCK_ENTER(); - { - rb_vm_barrier(); // stop other ractors - - unsigned int prev_during_gc = during_gc; - during_gc = FALSE; // stop gc here - HEAP_LOCK_ENTER(objspace); - { - gc_verify_internal_consistency_(objspace); - } - HEAP_LOCK_LEAVE(objspace); - during_gc = prev_during_gc; - } - RB_VM_LOCK_LEAVE(); -} - -void -rb_gc_verify_internal_consistency(void) -{ - gc_verify_internal_consistency(&rb_objspace); -} - -static void -heap_move_pooled_pages_to_free_pages(rb_heap_t *heap) -{ - if (heap->pooled_pages) { - if (heap->free_pages) { - struct heap_page *free_pages_tail = heap->free_pages; - while (free_pages_tail->free_next) { - free_pages_tail = free_pages_tail->free_next; - } - free_pages_tail->free_next = heap->pooled_pages; - } - else { - heap->free_pages = heap->pooled_pages; - } - - heap->pooled_pages = NULL; - } -} - -/* marks */ - -static void -gc_marks_prepare(rb_objspace_t *objspace, int full_mark) -{ - /* start marking */ - gc_report(1, objspace, "gc_marks_start: (%s)\n", full_mark ? "full" : "minor"); - gc_mode_transition(objspace, gc_mode_marking); - - VM_ASSERT(count_objspaces(GET_VM()) > 1 || (shared_reference_tbl_empty(objspace) && external_reference_tbl_empty(objspace))); - confirm_externally_added_external_references(objspace); - - if (full_mark) { - size_t incremental_marking_steps = (objspace->rincgc.pooled_slots / INCREMENTAL_MARK_STEP_ALLOCATIONS) + 1; - objspace->rincgc.step_slots = (objspace->marked_slots * 2) / incremental_marking_steps; - - if (0) fprintf(stderr, "objspace->marked_slots: %"PRIdSIZE", " - "objspace->rincgc.pooled_page_num: %"PRIdSIZE", " - "objspace->rincgc.step_slots: %"PRIdSIZE", \n", - objspace->marked_slots, objspace->rincgc.pooled_slots, objspace->rincgc.step_slots); - objspace->flags.during_minor_gc = FALSE; - if (ruby_enable_autocompact) { - objspace->flags.during_compacting |= TRUE; - } - objspace->profile.major_gc_count++; - objspace->rgengc.uncollectible_wb_unprotected_objects = 0; - objspace->rgengc.old_objects = 0; - objspace->rgengc.last_major_gc = objspace->profile.count; - objspace->marked_slots = 0; - - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); - rgengc_mark_and_rememberset_clear(objspace, heap); - heap_move_pooled_pages_to_free_pages(heap); - - if (objspace->flags.during_compacting) { - struct heap_page *page = NULL; - - ccan_list_for_each(&heap->pages, page, page_node) { - page->pinned_slots = 0; - } - } - } - } - else { - objspace->flags.during_minor_gc = TRUE; - objspace->marked_slots = - objspace->rgengc.old_objects + objspace->rgengc.uncollectible_wb_unprotected_objects; /* uncollectible objects are marked already */ - objspace->profile.minor_gc_count++; - - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rgengc_rememberset_mark(objspace, SIZE_POOL_EDEN_HEAP(&size_pools[i])); - } - } - -} - -static void -gc_marks_start(rb_objspace_t *objspace, int full_mark) -{ - gc_mark_roots(objspace, NULL); - - gc_report(1, objspace, "gc_marks_start: (%s) end, stack in %"PRIdSIZE"\n", - full_mark ? "full" : "minor", mark_stack_size(&objspace->mark_stack)); -} - -static inline void -gc_marks_wb_unprotected_objects_plane(rb_objspace_t *objspace, uintptr_t p, bits_t bits) -{ - if (bits) { - do { - if (bits & 1) { - gc_report(2, objspace, "gc_marks_wb_unprotected_objects: marked shady: %s\n", obj_info((VALUE)p)); - GC_ASSERT(RVALUE_WB_UNPROTECTED((VALUE)p)); - GC_ASSERT(RVALUE_MARKED((VALUE)p)); - gc_mark_children(objspace, (VALUE)p); - } - p += BASE_SLOT_SIZE; - bits >>= 1; - } while (bits); - } -} - -static void -gc_marks_wb_unprotected_objects(rb_objspace_t *objspace, rb_heap_t *heap) -{ - struct heap_page *page = 0; - - ccan_list_for_each(&heap->pages, page, page_node) { - bits_t *mark_bits = page->mark_bits; - bits_t *wbun_bits = page->wb_unprotected_bits; - uintptr_t p = page->start; - size_t j; - - bits_t bits = mark_bits[0] & wbun_bits[0]; - bits >>= NUM_IN_PAGE(p); - gc_marks_wb_unprotected_objects_plane(objspace, p, bits); - p += (BITS_BITLENGTH - NUM_IN_PAGE(p)) * BASE_SLOT_SIZE; - - for (j=1; jweak_references, i, ptr_ptr) { - if (!*ptr_ptr) continue; - - VALUE obj = **ptr_ptr; - - if (RB_SPECIAL_CONST_P(obj)) continue; - - if (!RVALUE_MARKED(obj) && !(using_local_limits(objspace) && RVALUE_LOCAL_IMMUNE(obj))) { - **ptr_ptr = Qundef; - } - else { - retained_weak_references_count++; - } - } - - objspace->profile.retained_weak_references_count = retained_weak_references_count; - - rb_darray_clear(objspace->weak_references); - - DURING_GC_COULD_MALLOC_REGION_START(); - { - rb_darray_resize_capa(&objspace->weak_references, retained_weak_references_count); - } - DURING_GC_COULD_MALLOC_REGION_END(); -} - -static void -gc_marks_finish(rb_objspace_t *objspace) -{ - /* finish incremental GC */ - if (is_incremental_marking(objspace)) { - if (RGENGC_CHECK_MODE && is_mark_stack_empty(&objspace->mark_stack) == 0) { - rb_bug("gc_marks_finish: mark stack is not empty (%"PRIdSIZE").", - mark_stack_size(&objspace->mark_stack)); - } - - gc_mark_roots(objspace, 0); - while (gc_mark_stacked_objects_incremental(objspace, INT_MAX) == false); - -#if RGENGC_CHECK_MODE >= 2 - if (gc_verify_heap_pages(objspace) != 0) { - rb_bug("gc_marks_finish (incremental): there are remembered old objects."); - } -#endif - - objspace->flags.during_incremental_marking = FALSE; - /* check children of all marked wb-unprotected objects */ - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - gc_marks_wb_unprotected_objects(objspace, SIZE_POOL_EDEN_HEAP(&size_pools[i])); - } - } - - gc_update_weak_references(objspace); - gc_update_external_weak_references(objspace); - if (is_full_marking(objspace)) update_shared_object_references(objspace); - -#if RGENGC_CHECK_MODE >= 2 - gc_verify_internal_consistency(objspace); -#endif - -#if RGENGC_CHECK_MODE >= 4 - during_gc = FALSE; - gc_marks_check(objspace, gc_check_after_marks_i, "after_marks"); - during_gc = TRUE; -#endif - - { - /* decide full GC is needed or not */ - size_t total_slots = heap_allocatable_slots(objspace) + heap_eden_total_slots(objspace); - size_t sweep_slots = total_slots - objspace->marked_slots; /* will be swept slots */ - size_t max_free_slots = (size_t)(total_slots * gc_params.heap_free_slots_max_ratio); - size_t min_free_slots = (size_t)(total_slots * gc_params.heap_free_slots_min_ratio); - int full_marking = is_full_marking(objspace); - const int r_cnt = GET_VM()->ractor.cnt; - const int r_mul = r_cnt > 8 ? 8 : r_cnt; // upto 8 - - GC_ASSERT(heap_eden_total_slots(objspace) >= objspace->marked_slots); - - /* Setup freeable slots. */ - size_t total_init_slots = 0; - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - total_init_slots += gc_params.size_pool_init_slots[i] * r_mul; - } - - if (max_free_slots < total_init_slots) { - max_free_slots = total_init_slots; - } - - if (sweep_slots > max_free_slots) { - heap_pages_freeable_pages = (sweep_slots - max_free_slots) / HEAP_PAGE_OBJ_LIMIT; - } - else { - heap_pages_freeable_pages = 0; - } - - /* check free_min */ - if (min_free_slots < gc_params.heap_free_slots * r_mul) { - min_free_slots = gc_params.heap_free_slots * r_mul; - } - - if (sweep_slots < min_free_slots) { - if (!full_marking) { - if (objspace->profile.count - objspace->rgengc.last_major_gc < RVALUE_OLD_AGE) { - full_marking = TRUE; - /* do not update last_major_gc, because full marking is not done. */ - /* goto increment; */ - } - else { - gc_report(1, objspace, "gc_marks_finish: next is full GC!!)\n"); - gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_NOFREE; - } - } - } - - if (full_marking) { - /* See the comment about RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR */ - const double r = gc_params.oldobject_limit_factor; - objspace->rgengc.uncollectible_wb_unprotected_objects_limit = MAX( - (size_t)(objspace->rgengc.uncollectible_wb_unprotected_objects * r), - (size_t)(objspace->rgengc.old_objects * gc_params.uncollectible_wb_unprotected_objects_limit_ratio) - ); - objspace->rgengc.old_objects_limit = (size_t)(objspace->rgengc.old_objects * r); - - arrange_next_gc_global_status(gc_params.sharedobject_limit_factor); - } - - if (objspace->rgengc.uncollectible_wb_unprotected_objects > objspace->rgengc.uncollectible_wb_unprotected_objects_limit) { - gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_SHADY; - } - if (objspace->rgengc.old_objects > objspace->rgengc.old_objects_limit) { - gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_OLDGEN; - } - if (RGENGC_FORCE_MAJOR_GC) { - 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), - global_gc_needed() ? "global" : (gc_needs_major_flags ? "major" : "minor")); - } - - rb_ractor_finish_marking(); - - gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_END_MARK, 0); -} - -static bool -gc_compact_heap_cursors_met_p(rb_heap_t *heap) -{ - return heap->sweeping_page == heap->compact_cursor; -} - -static rb_size_pool_t * -gc_compact_destination_pool(rb_objspace_t *objspace, rb_size_pool_t *src_pool, VALUE src) -{ - size_t obj_size; - size_t idx = 0; - - switch (BUILTIN_TYPE(src)) { - case T_ARRAY: - obj_size = rb_ary_size_as_embedded(src); - break; - - case T_OBJECT: - if (rb_shape_obj_too_complex(src)) { - return &size_pools[0]; - } - else { - obj_size = rb_obj_embedded_size(ROBJECT_IV_CAPACITY(src)); - } - break; - - case T_STRING: - obj_size = rb_str_size_as_embedded(src); - break; - - case T_HASH: - obj_size = sizeof(struct RHash) + (RHASH_ST_TABLE_P(src) ? sizeof(st_table) : sizeof(ar_table)); - break; - - default: - return src_pool; - } - - if (rb_gc_size_allocatable_p(obj_size)){ - idx = rb_gc_size_pool_id_for_size(obj_size); - } - return &size_pools[idx]; -} - -static bool -gc_compact_move(rb_objspace_t *objspace, rb_heap_t *heap, rb_size_pool_t *size_pool, VALUE src) -{ - GC_ASSERT(BUILTIN_TYPE(src) != T_MOVED); - GC_ASSERT(gc_is_moveable_obj(objspace, src)); - - rb_size_pool_t *dest_pool = gc_compact_destination_pool(objspace, size_pool, src); - rb_heap_t *dheap = SIZE_POOL_EDEN_HEAP(dest_pool); - rb_shape_t *new_shape = NULL; - rb_shape_t *orig_shape = NULL; - - if (gc_compact_heap_cursors_met_p(dheap)) { - return dheap != heap; - } - - if (RB_TYPE_P(src, T_OBJECT)) { - orig_shape = rb_shape_get_shape(src); - if (dheap != heap && !rb_shape_obj_too_complex(src)) { - rb_shape_t *initial_shape = rb_shape_get_shape_by_id((shape_id_t)((dest_pool - size_pools) + FIRST_T_OBJECT_SHAPE_ID)); - new_shape = rb_shape_traverse_from_new_root(initial_shape, orig_shape); - - if (!new_shape) { - dest_pool = size_pool; - dheap = heap; - } - } - } - - while (!try_move(objspace, dheap, dheap->free_pages, src)) { - struct gc_sweep_context ctx = { - .page = dheap->sweeping_page, - .final_slots = 0, - .freed_slots = 0, - .empty_slots = 0, - }; - - /* The page of src could be partially compacted, so it may contain - * T_MOVED. Sweeping a page may read objects on this page, so we - * need to lock the page. */ - lock_page_body(objspace, GET_PAGE_BODY(src)); - gc_sweep_page(objspace, dheap, &ctx); - unlock_page_body(objspace, GET_PAGE_BODY(src)); - - if (dheap->sweeping_page->free_slots > 0) { - heap_add_freepage(dheap, dheap->sweeping_page); - } - - dheap->sweeping_page = ccan_list_next(&dheap->pages, dheap->sweeping_page, page_node); - if (gc_compact_heap_cursors_met_p(dheap)) { - return dheap != heap; - } - } - - if (orig_shape) { - if (new_shape) { - VALUE dest = rb_gc_location(src); - rb_shape_set_shape(dest, new_shape); - } - RMOVED(src)->original_shape_id = rb_shape_id(orig_shape); - } - - return true; -} - -static bool -gc_compact_plane(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap, uintptr_t p, bits_t bitset, struct heap_page *page) -{ - short slot_size = page->slot_size; - short slot_bits = slot_size / BASE_SLOT_SIZE; - GC_ASSERT(slot_bits > 0); - - do { - VALUE vp = (VALUE)p; - GC_ASSERT(vp % BASE_SLOT_SIZE == 0); - - if (bitset & 1) { - objspace->rcompactor.considered_count_table[BUILTIN_TYPE(vp)]++; - - if (gc_is_moveable_obj(objspace, vp)) { - if (!gc_compact_move(objspace, heap, size_pool, vp)) { - //the cursors met. bubble up - return false; - } - } - } - p += slot_size; - bitset >>= slot_bits; - } while (bitset); - - return true; -} - -// Iterate up all the objects in page, moving them to where they want to go -static bool -gc_compact_page(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap, struct heap_page *page) -{ - GC_ASSERT(page == heap->compact_cursor); - - bits_t *mark_bits, *pin_bits; - bits_t bitset; - uintptr_t p = page->start; - - mark_bits = page->mark_bits; - pin_bits = page->pinned_bits; - - // objects that can be moved are marked and not pinned - bitset = (mark_bits[0] & ~pin_bits[0]); - bitset >>= NUM_IN_PAGE(p); - if (bitset) { - if (!gc_compact_plane(objspace, size_pool, heap, (uintptr_t)p, bitset, page)) - return false; - } - p += (BITS_BITLENGTH - NUM_IN_PAGE(p)) * BASE_SLOT_SIZE; - - for (int j = 1; j < HEAP_PAGE_BITMAP_LIMIT; j++) { - bitset = (mark_bits[j] & ~pin_bits[j]); - if (bitset) { - if (!gc_compact_plane(objspace, size_pool, heap, (uintptr_t)p, bitset, page)) - return false; - } - p += BITS_BITLENGTH * BASE_SLOT_SIZE; - } - - return true; -} - -static bool -gc_compact_all_compacted_p(rb_objspace_t *objspace) -{ - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); - - if (heap->total_pages > 0 && - !gc_compact_heap_cursors_met_p(heap)) { - return false; - } - } - - return true; -} - -static void -gc_sweep_compact(rb_objspace_t *objspace) -{ - gc_compact_start(objspace); -#if RGENGC_CHECK_MODE >= 2 - gc_verify_internal_consistency(objspace); -#endif - - while (!gc_compact_all_compacted_p(objspace)) { - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); - - if (gc_compact_heap_cursors_met_p(heap)) { - continue; - } - - struct heap_page *start_page = heap->compact_cursor; - - if (!gc_compact_page(objspace, size_pool, heap, start_page)) { - lock_page_body(objspace, GET_PAGE_BODY(start_page->start)); - - continue; - } - - // If we get here, we've finished moving all objects on the compact_cursor page - // So we can lock it and move the cursor on to the next one. - lock_page_body(objspace, GET_PAGE_BODY(start_page->start)); - heap->compact_cursor = ccan_list_prev(&heap->pages, heap->compact_cursor, page_node); - } - } - - gc_compact_finish(objspace); - -#if RGENGC_CHECK_MODE >= 2 - gc_verify_internal_consistency(objspace); -#endif -} - -static void -gc_marks_rest_no_finish(rb_objspace_t *objspace) -{ - gc_report(1, objspace, "gc_marks_rest\n"); - - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - SIZE_POOL_EDEN_HEAP(&size_pools[i])->pooled_pages = NULL; - } - - if (is_incremental_marking(objspace)) { - while (gc_mark_stacked_objects_incremental(objspace, INT_MAX) == FALSE); - } - else { - gc_mark_stacked_objects_all(objspace); - } -} - -static void -gc_marks_rest(rb_objspace_t *objspace) -{ - gc_marks_rest_no_finish(objspace); - gc_marks_finish(objspace); -} - -static bool -gc_marks_step(rb_objspace_t *objspace, size_t slots) -{ - bool marking_finished = false; - - GC_ASSERT(is_marking(objspace)); - if (gc_mark_stacked_objects_incremental(objspace, slots)) { - gc_marks_finish(objspace); - - marking_finished = true; - } - - return marking_finished; -} - -static bool -gc_marks_continue(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap) -{ - GC_ASSERT(dont_gc_val() == FALSE); - bool marking_finished = true; - - gc_marking_enter(objspace); - - if (heap->free_pages) { - gc_report(2, objspace, "gc_marks_continue: has pooled pages"); - - marking_finished = gc_marks_step(objspace, objspace->rincgc.step_slots); - } - else { - gc_report(2, objspace, "gc_marks_continue: no more pooled pages (stack depth: %"PRIdSIZE").\n", - mark_stack_size(&objspace->mark_stack)); - size_pool->force_incremental_marking_finish_count++; - gc_marks_rest(objspace); - } - - gc_marking_exit(objspace); - - return marking_finished; -} - -#if RGENGC_PROFILE > 0 -static void -gc_record_old_objects(rb_objspace_t *objspace) -{ - if (gc_prof_record(objspace)) { - gc_profile_record *record = gc_prof_record(objspace); - record->old_objects = objspace->rgengc.old_objects; - } -} -#endif - -static bool -gc_marks(rb_objspace_t *objspace, int full_mark) -{ - gc_prof_mark_timer_start(objspace); - gc_marking_enter(objspace); - - bool marking_finished = false; - - /* setup marking */ - - gc_marks_prepare(objspace, full_mark); - gc_marks_start(objspace, full_mark); - if (!is_incremental_marking(objspace)) { - gc_marks_rest(objspace); - marking_finished = true; - } - -#if RGENGC_PROFILE > 0 - gc_record_old_objects(objspace); -#endif - - gc_marking_exit(objspace); - gc_prof_mark_timer_stop(objspace); - - return marking_finished; -} - -static void -gc_marks_prepare_full(rb_objspace_t *objspace) -{ - gc_marks_prepare(objspace, true); -} - -static void -gc_marks_start_full(rb_objspace_t *objspace) -{ - gc_marks_start(objspace, true); -} - -static void -gc_marks_global(rb_objspace_t *objspace) -{ - rb_vm_t *vm = GET_VM(); - - global_gc_for_each_objspace(vm, objspace, gc_prof_mark_timer_start); - global_gc_for_each_objspace(vm, objspace, gc_marks_prepare_full); - global_gc_for_each_objspace(vm, objspace, gc_marks_start_full); - - rb_vm_ractor_mark(vm); - - global_gc_for_each_objspace(vm, objspace, gc_marks_rest_no_finish); - global_gc_for_each_objspace(vm, objspace, gc_marks_finish); - -#if RGENGC_PROFILE > 0 - global_gc_for_each_objspace(vm, objspace, gc_record_old_objects); -#endif - - global_gc_for_each_objspace(vm, objspace, gc_prof_mark_timer_stop); - global_gc_for_each_objspace(vm, objspace, gc_sweep); -} - -/* RGENGC */ - -static void -gc_report_body(int level, rb_objspace_t *objspace, const char *fmt, ...) -{ - if (level <= RGENGC_DEBUG) { - char buf[1024]; - FILE *out = stderr; - va_list args; - const char *status = " "; - - if (during_gc) { - status = is_full_marking(objspace) ? "+" : "-"; - } - else { - if (is_lazy_sweeping(objspace)) { - status = "S"; - } - if (is_incremental_marking(objspace)) { - status = "M"; - } - } - - va_start(args, fmt); - vsnprintf(buf, 1024, fmt, args); - va_end(args); - - fprintf(out, "%s|", status); - fputs(buf, out); - } -} - -/* bit operations */ - -static int -rgengc_remembersetbits_set(rb_objspace_t *objspace, VALUE obj) -{ - struct heap_page *page = GET_HEAP_PAGE(obj); - bits_t *bits = &page->remembered_bits[0]; - - if (MARKED_IN_BITMAP(bits, obj)) { - return FALSE; - } - else { - page->flags.has_remembered_objects = TRUE; - MARK_IN_BITMAP(bits, obj); - return TRUE; - } -} - -/* wb, etc */ - -/* return FALSE if already remembered */ -static int -rgengc_remember(rb_objspace_t *objspace, VALUE obj) -{ - gc_report(6, objspace, "rgengc_remember: %s %s\n", obj_info(obj), - RVALUE_REMEMBERED(obj) ? "was already remembered" : "is remembered now"); - - check_rvalue_consistency(obj); - - if (RGENGC_CHECK_MODE) { - if (RVALUE_WB_UNPROTECTED(obj)) rb_bug("rgengc_remember: %s is not wb protected.", obj_info(obj)); - } - -#if RGENGC_PROFILE > 0 - if (!RVALUE_REMEMBERED(obj)) { - if (RVALUE_WB_UNPROTECTED(obj) == 0) { - objspace->profile.total_remembered_normal_object_count++; -#if RGENGC_PROFILE >= 2 - objspace->profile.remembered_normal_object_count_types[BUILTIN_TYPE(obj)]++; -#endif - } - } -#endif /* RGENGC_PROFILE > 0 */ - - return rgengc_remembersetbits_set(objspace, obj); -} - -#ifndef PROFILE_REMEMBERSET_MARK -#define PROFILE_REMEMBERSET_MARK 0 -#endif - -static inline void -rgengc_rememberset_mark_plane(rb_objspace_t *objspace, uintptr_t p, bits_t bitset) -{ - if (bitset) { - do { - if (bitset & 1) { - VALUE obj = (VALUE)p; - gc_report(2, objspace, "rgengc_rememberset_mark: mark %s\n", obj_info(obj)); - GC_ASSERT(RVALUE_UNCOLLECTIBLE(obj)); - GC_ASSERT(RVALUE_OLD_P(obj) || RVALUE_WB_UNPROTECTED(obj)); - - gc_mark_children(objspace, obj); - } - p += BASE_SLOT_SIZE; - bitset >>= 1; - } while (bitset); - } -} - -static void -rgengc_rememberset_mark(rb_objspace_t *objspace, rb_heap_t *heap) -{ - size_t j; - struct heap_page *page = 0; -#if PROFILE_REMEMBERSET_MARK - int has_old = 0, has_shady = 0, has_both = 0, skip = 0; -#endif - gc_report(1, objspace, "rgengc_rememberset_mark: start\n"); - - ccan_list_for_each(&heap->pages, page, page_node) { - if (page->flags.has_remembered_objects | page->flags.has_uncollectible_wb_unprotected_objects) { - uintptr_t p = page->start; - bits_t bitset, bits[HEAP_PAGE_BITMAP_LIMIT]; - bits_t *remembered_bits = page->remembered_bits; - bits_t *uncollectible_bits = page->uncollectible_bits; - bits_t *wb_unprotected_bits = page->wb_unprotected_bits; -#if PROFILE_REMEMBERSET_MARK - if (page->flags.has_remembered_objects && page->flags.has_uncollectible_wb_unprotected_objects) has_both++; - else if (page->flags.has_remembered_objects) has_old++; - else if (page->flags.has_uncollectible_wb_unprotected_objects) has_shady++; -#endif - for (j=0; jflags.has_remembered_objects = FALSE; - - bitset = bits[0]; - bitset >>= NUM_IN_PAGE(p); - rgengc_rememberset_mark_plane(objspace, p, bitset); - p += (BITS_BITLENGTH - NUM_IN_PAGE(p)) * BASE_SLOT_SIZE; - - for (j=1; j < HEAP_PAGE_BITMAP_LIMIT; j++) { - bitset = bits[j]; - rgengc_rememberset_mark_plane(objspace, p, bitset); - p += BITS_BITLENGTH * BASE_SLOT_SIZE; - } - } -#if PROFILE_REMEMBERSET_MARK - else { - skip++; - } -#endif - } - -#if PROFILE_REMEMBERSET_MARK - fprintf(stderr, "%d\t%d\t%d\t%d\n", has_both, has_old, has_shady, skip); -#endif - gc_report(1, objspace, "rgengc_rememberset_mark: finished\n"); -} - -static void -rgengc_mark_and_rememberset_clear(rb_objspace_t *objspace, rb_heap_t *heap) -{ - struct heap_page *page = 0; - - ccan_list_for_each(&heap->pages, page, page_node) { - memset(&page->mark_bits[0], 0, HEAP_PAGE_BITMAP_SIZE); - memset(&page->uncollectible_bits[0], 0, HEAP_PAGE_BITMAP_SIZE); - memset(&page->marking_bits[0], 0, HEAP_PAGE_BITMAP_SIZE); - memset(&page->remembered_bits[0], 0, HEAP_PAGE_BITMAP_SIZE); - memset(&page->pinned_bits[0], 0, HEAP_PAGE_BITMAP_SIZE); - page->flags.has_uncollectible_wb_unprotected_objects = FALSE; - page->flags.has_remembered_objects = FALSE; - } -} - -/* RGENGC: APIs */ - -NOINLINE(static void gc_writebarrier_generational(VALUE a, VALUE b, rb_objspace_t *objspace)); - -static void -gc_writebarrier_generational(VALUE a, VALUE b, rb_objspace_t *objspace) -{ - VM_ASSERT(GET_OBJSPACE_OF_VALUE(b) == objspace); - - if (GET_OBJSPACE_OF_VALUE(a) != objspace || !RVALUE_OLD_P(a) || RVALUE_OLD_P(b)) return; - - if (RGENGC_CHECK_MODE) { - if (!RVALUE_OLD_P(a)) rb_bug("gc_writebarrier_generational: %s is not an old object.", obj_info(a)); - if ( RVALUE_OLD_P(b)) rb_bug("gc_writebarrier_generational: %s is an old object.", obj_info(b)); - if (is_incremental_marking(objspace)) rb_bug("gc_writebarrier_generational: called while incremental marking: %s -> %s", obj_info(a), obj_info(b)); - } - - /* mark `a' and remember (default behavior) */ - if (!RVALUE_REMEMBERED(a)) { - HEAP_LOCK_ENTER(objspace); - { - rgengc_remember(objspace, a); - } - HEAP_LOCK_LEAVE(objspace); - gc_report(1, objspace, "gc_writebarrier_generational: %s (remembered) -> %s\n", obj_info(a), obj_info(b)); - } - - check_rvalue_consistency(a); - check_rvalue_consistency(b); -} - -static void -gc_mark_from(rb_objspace_t *objspace, VALUE obj, VALUE parent) -{ - VM_ASSERT(GET_OBJSPACE_OF_VALUE(obj) == objspace); - gc_mark_set_parent(objspace, parent); - rgengc_check_relation(objspace, obj); - if (gc_mark_set(objspace, obj) == FALSE) return; - gc_aging(obj); - gc_grey(objspace, obj); -} - -NOINLINE(static void gc_writebarrier_incremental(VALUE a, VALUE b, rb_objspace_t *objspace)); - -static void -gc_writebarrier_incremental(VALUE a, VALUE b, rb_objspace_t *objspace) -{ - gc_report(2, objspace, "gc_writebarrier_incremental: [LG] %p -> %s\n", (void *)a, obj_info(b)); - - VM_ASSERT(GET_OBJSPACE_OF_VALUE(b) == objspace); - VM_ASSERT(is_incremental_marking(objspace)); - - if (LIKELY(GET_OBJSPACE_OF_VALUE(a) == objspace)) { - if (RVALUE_BLACK_P(a)) { - if (RVALUE_WHITE_P(b)) { - if (!RVALUE_WB_UNPROTECTED(a)) { - gc_report(2, objspace, "gc_writebarrier_incremental: [IN] %p -> %s\n", (void *)a, obj_info(b)); - gc_mark_from(objspace, b, a); - } - } - else if (RVALUE_OLD_P(a) && !RVALUE_OLD_P(b)) { - rgengc_remember(objspace, a); - } - - if (UNLIKELY(objspace->flags.during_compacting)) { - MARK_IN_BITMAP(GET_HEAP_PINNED_BITS(b), b); - } - } - } - else { - if (RVALUE_WHITE_P(b)) { - if (gc_mark_set(objspace, b)) { - gc_aging(b); - gc_grey(objspace, b); - } - } - } -} - -void -rb_gc_writebarrier_safe_objspace(VALUE a, VALUE b, rb_objspace_t *objspace) -{ - VM_ASSERT(GET_OBJSPACE_OF_VALUE(b) == objspace); - - if (is_incremental_marking(objspace)) { - gc_writebarrier_incremental(a, b, objspace); - } - else { - gc_writebarrier_generational(a, b, objspace); - } -} - -static VALUE -limmune(VALUE os, VALUE obj) -{ - rb_gc_give_local_immunity_traversal(obj); - return Qnil; -} - -void -rb_gc_writebarrier(VALUE a, VALUE b) -{ - if (RGENGC_CHECK_MODE) { - if (SPECIAL_CONST_P(a)) rb_bug("rb_gc_writebarrier: a is special const: %"PRIxVALUE, a); - if (SPECIAL_CONST_P(b)) rb_bug("rb_gc_writebarrier: b is special const: %"PRIxVALUE, b); - } - - rb_objspace_t *current_objspace = &rb_objspace; - - //VM_ASSERT(!NEEDS_LOCAL_IMMUNE_CHILDREN(a) || RVALUE_LOCAL_IMMUNE(b)); - - if (ruby_single_main_objspace) { - rb_gc_writebarrier_safe_objspace(a, b, current_objspace); - } - else { - rb_gc_writebarrier_multi_objspace(a, b, current_objspace); - } -} - -void -rb_gc_writebarrier_unprotect(VALUE obj) -{ - if (RVALUE_WB_UNPROTECTED(obj)) { - return; - } - else { - rb_objspace_t *objspace = GET_OBJSPACE_OF_VALUE(obj); - - gc_report(2, objspace, "rb_gc_writebarrier_unprotect: %s %s\n", obj_info(obj), - RVALUE_REMEMBERED(obj) ? " (already remembered)" : ""); - - HEAP_LOCK_ENTER(objspace); - { - if (RVALUE_OLD_P(obj)) { - gc_report(1, objspace, "rb_gc_writebarrier_unprotect: %s\n", obj_info(obj)); - RVALUE_DEMOTE(objspace, obj); - gc_mark_set(objspace, obj); - gc_remember_unprotected(objspace, obj); - -#if RGENGC_PROFILE - objspace->profile.total_shade_operation_count++; -#if RGENGC_PROFILE >= 2 - objspace->profile.shade_operation_count_types[BUILTIN_TYPE(obj)]++; -#endif /* RGENGC_PROFILE >= 2 */ -#endif /* RGENGC_PROFILE */ - } - else { - RVALUE_AGE_RESET(obj); - } - - RB_DEBUG_COUNTER_INC(obj_wb_unprotect); - MARK_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(obj), obj); - } - HEAP_LOCK_LEAVE(objspace); - } -} - -/* - * remember `obj' if needed. - */ -void -rb_gc_writebarrier_remember(VALUE obj) -{ - rb_objspace_t *objspace = &rb_objspace; - - gc_report(1, objspace, "rb_gc_writebarrier_remember: %s\n", obj_info(obj)); - - if (is_incremental_marking(objspace)) { - if (RVALUE_BLACK_P(obj)) { - gc_grey(objspace, obj); - } - } - else { - if (RVALUE_OLD_P(obj)) { - rgengc_remember(objspace, obj); - } - } -} - -void -rb_gc_copy_attributes(VALUE dest, VALUE obj) -{ - if (RVALUE_WB_UNPROTECTED(obj)) { - rb_gc_writebarrier_unprotect(dest); - } - if (RVALUE_LOCAL_IMMUNE(obj)) { - MARK_IN_BITMAP(GET_HEAP_LOCAL_IMMUNE_BITS(obj), obj); - } - rb_gc_copy_finalizer(dest, obj); -} - -size_t -rb_obj_gc_flags(VALUE obj, ID* flags, size_t max) -{ - size_t n = 0; - static ID ID_marked; - static ID ID_wb_protected, ID_old, ID_marking, ID_uncollectible, ID_pinned, ID_local_immune; - - if (!ID_marked) { -#define I(s) ID_##s = rb_intern(#s); - I(marked); - I(wb_protected); - I(old); - I(marking); - I(uncollectible); - I(pinned); - I(local_immune); -#undef I - } - - if (RVALUE_WB_UNPROTECTED(obj) == 0 && nusing_page; - RVALUE *freelist = cache->freelist; - RUBY_DEBUG_LOG("ractor using_page:%p freelist:%p", (void *)page, (void *)freelist); - heap_page_freelist_append(page, freelist); - - cache->using_page = NULL; - cache->freelist = NULL; -} - -void -rb_gc_ractor_newobj_cache_clear(rb_ractor_newobj_cache_t *newobj_cache) -{ - newobj_cache->incremental_mark_step_allocated_slots = 0; - - for (size_t size_pool_idx = 0; size_pool_idx < SIZE_POOL_COUNT; size_pool_idx++) { - rb_ractor_newobj_size_pool_cache_t *cache = &newobj_cache->size_pool_caches[size_pool_idx]; - gc_ractor_newobj_size_pool_cache_clear(cache); - } -} - -static VALUE -register_mark_object_no_redirection(VALUE obj) -{ - rb_ractor_t *r = rb_current_allocating_ractor(); - rb_objspace_t *objspace = r->local_objspace; - if (!is_pointer_to_heap(objspace, (void *)obj)) - return Qnil; - - rb_vm_register_global_object(obj); - return Qnil; -} - -void -rb_gc_register_mark_object(VALUE obj) -{ - VALUE ret; - if (rb_special_const_p(obj)) { - ret = register_mark_object_no_redirection(obj); - } - else { - WITH_OBJSPACE_OF_VALUE_ENTER(obj, objspace); - { - ret = rb_run_with_redirected_allocation(objspace->local_data.ractor, register_mark_object_no_redirection, obj); - } - WITH_OBJSPACE_OF_VALUE_LEAVE(objspace); - } - return ret; -} - -void -rb_gc_register_address(VALUE *addr) -{ - VALUE obj = *addr; - - rb_ractor_t *r = GET_RACTOR(); - VM_ASSERT(SPECIAL_CONST_P(obj) || GET_RACTOR_OF_VALUE(obj) == r || FL_TEST_RAW(obj, FL_SHAREABLE)); - struct global_object_list *tmp = ALLOC(struct global_object_list); - tmp->next = r->global_object_list; - tmp->varptr = addr; - r->global_object_list = tmp; - - /* - * Because some C extensions have assignment-then-register bugs, - * we guard `obj` here so that it would not get swept defensively. - */ - RB_GC_GUARD(obj); - if (0 && !SPECIAL_CONST_P(obj)) { - rb_warn("Object is assigned to registering address already: %"PRIsVALUE, - rb_obj_class(obj)); - rb_print_backtrace(stderr); - } -} - -void -rb_gc_unregister_address(VALUE *addr) -{ - rb_ractor_t *r = GET_RACTOR(); - VM_ASSERT(SPECIAL_CONST_P(*addr) || GET_RACTOR_OF_VALUE(*addr) == r || FL_TEST_RAW(*addr, FL_SHAREABLE)); - struct global_object_list *tmp = r->global_object_list; - - if (tmp->varptr == addr) { - r->global_object_list = tmp->next; - xfree(tmp); - return; - } - while (tmp->next) { - if (tmp->next->varptr == addr) { - struct global_object_list *t = tmp->next; - - tmp->next = tmp->next->next; - xfree(t); - break; - } - tmp = tmp->next; - } -} - -void -rb_gc_register_in_mark_object_ary(VALUE obj) -{ - WITH_OBJSPACE_OF_VALUE_ENTER(obj, objspace); - { - rb_ractor_t *r = objspace->local_data.ractor; - - VALUE already_disabled = rb_objspace_gc_disable(objspace); - rb_native_mutex_lock(&r->mark_object_ary_lock); - - VALUE list = r->mark_object_ary; - VALUE head = rb_pin_array_list_append(list, obj); - if (head != list) { - r->mark_object_ary = head; - } - RB_GC_GUARD(obj); - - rb_native_mutex_unlock(&r->mark_object_ary_lock); - if (already_disabled == Qfalse) rb_objspace_gc_enable(objspace); - } - WITH_OBJSPACE_OF_VALUE_LEAVE(objspace); -} - -void -rb_global_variable(VALUE *var) -{ - rb_gc_register_address(var); -} - -enum { - gc_stress_no_major, - gc_stress_no_immediate_sweep, - gc_stress_full_mark_after_malloc, - gc_stress_max -}; - -#define gc_stress_full_mark_after_malloc_p() \ - (FIXNUM_P(ruby_gc_stress_mode) && (FIX2LONG(ruby_gc_stress_mode) & (1<free_pages) { - if (!heap_increment(objspace, size_pool, heap)) { - size_pool_allocatable_pages_set(objspace, size_pool, 1); - heap_increment(objspace, size_pool, heap); - } - } -} - -static int -ready_to_gc(rb_objspace_t *objspace) -{ - if (dont_gc_val() || during_gc || ruby_disable_gc) { - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - heap_ready_to_gc(objspace, size_pool, SIZE_POOL_EDEN_HEAP(size_pool)); - } - return FALSE; - } - else { - return TRUE; - } -} - -static void -gc_reset_malloc_info(rb_objspace_t *objspace, bool full_mark) -{ - gc_prof_set_malloc_info(objspace); - { - size_t inc = ATOMIC_SIZE_EXCHANGE(malloc_increase, 0); - size_t old_limit = malloc_limit; - - if (inc > malloc_limit) { - malloc_limit = (size_t)(inc * gc_params.malloc_limit_growth_factor); - if (malloc_limit > gc_params.malloc_limit_max) { - malloc_limit = gc_params.malloc_limit_max; - } - } - else { - malloc_limit = (size_t)(malloc_limit * 0.98); /* magic number */ - if (malloc_limit < gc_params.malloc_limit_min) { - malloc_limit = gc_params.malloc_limit_min; - } - } - - if (0) { - if (old_limit != malloc_limit) { - fprintf(stderr, "[%"PRIuSIZE"] malloc_limit: %"PRIuSIZE" -> %"PRIuSIZE"\n", - rb_gc_count(), old_limit, malloc_limit); - } - else { - fprintf(stderr, "[%"PRIuSIZE"] malloc_limit: not changed (%"PRIuSIZE")\n", - rb_gc_count(), malloc_limit); - } - } - } - - /* reset oldmalloc info */ -#if RGENGC_ESTIMATE_OLDMALLOC - if (!full_mark) { - if (objspace->rgengc.oldmalloc_increase > objspace->rgengc.oldmalloc_increase_limit) { - 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); - - if (objspace->rgengc.oldmalloc_increase_limit > gc_params.oldmalloc_limit_max) { - objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_max; - } - } - - if (0) fprintf(stderr, "%"PRIdSIZE"\t%d\t%"PRIuSIZE"\t%"PRIuSIZE"\t%"PRIdSIZE"\n", - rb_gc_count(), - gc_needs_major_flags, - objspace->rgengc.oldmalloc_increase, - objspace->rgengc.oldmalloc_increase_limit, - gc_params.oldmalloc_limit_max); - } - else { - /* major GC */ - objspace->rgengc.oldmalloc_increase = 0; - - if ((objspace->profile.latest_gc_info & GPR_FLAG_MAJOR_BY_OLDMALLOC) == 0) { - objspace->rgengc.oldmalloc_increase_limit = - (size_t)(objspace->rgengc.oldmalloc_increase_limit / ((gc_params.oldmalloc_limit_growth_factor - 1)/10 + 1)); - if (objspace->rgengc.oldmalloc_increase_limit < gc_params.oldmalloc_limit_min) { - objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_min; - } - } - } -#endif -} - -static int -garbage_collect(rb_objspace_t *objspace, unsigned int reason, bool need_finalize_deferred) -{ - if (global_gc_needed()) { - reason |= GPR_FLAG_GLOBAL; - } - VM_ASSERT(!(reason & GPR_FLAG_COMPACT) || !(reason & GPR_FLAG_GLOBAL)); - - if (reason & GPR_FLAG_GLOBAL) { - return garbage_collect_global(objspace, reason, need_finalize_deferred); - } - else { - return garbage_collect_local(objspace, reason, need_finalize_deferred); - } -} - -static int -garbage_collect_local(rb_objspace_t *objspace, unsigned int reason, bool need_finalize_deferred) -{ - int ret; - - if (rb_redirecting_allocation()) { - return TRUE; - } - - LOCAL_GC_BEGIN(objspace); - { -#if GC_PROFILE_MORE_DETAIL - objspace->profile.prepare_time = getrusage_time(); -#endif - - gc_rest(objspace); - -#if GC_PROFILE_MORE_DETAIL - objspace->profile.prepare_time = getrusage_time() - objspace->profile.prepare_time; -#endif - - ret = gc_start(objspace, reason); - - if (need_finalize_deferred) { - gc_finalize_deferred(objspace); - } - } - LOCAL_GC_END(objspace); - - return ret; -} - -void -gc_rest_global(rb_objspace_t *objspace) -{ - global_gc_for_each_objspace(GET_VM(), objspace, gc_rest); -} - -static int -garbage_collect_global(rb_objspace_t *objspace, unsigned int reason, bool need_finalize_deferred) -{ - int ret; - rb_vm_t *vm = GET_VM(); - - bool global_gc_possible = true; - GLOBAL_GC_BEGIN(vm, objspace); - { -#if GC_PROFILE_MORE_DETAIL - objspace->profile.prepare_time = getrusage_time(); -#endif - - gc_rest_global(objspace); - -#if GC_PROFILE_MORE_DETAIL - objspace->profile.prepare_time = getrusage_time() - objspace->profile.prepare_time; -#endif - - struct objspace_local_data *local_data = NULL; - ccan_list_for_each(&vm->objspace_set, local_data, objspace_node) { - rb_objspace_t *os = local_data->objspace; - if (os == objspace) continue; - - global_gc_possible = global_gc_possible && (os->heap_pages.allocated_pages && ((reason & GPR_FLAG_METHOD) || ready_to_gc(os))); - - GC_ASSERT(gc_mode(os) == gc_mode_none); - GC_ASSERT(!is_lazy_sweeping(os)); - GC_ASSERT(!is_incremental_marking(os)); - } - - if (global_gc_possible) { - ret = gc_start(objspace, reason); - - if (need_finalize_deferred) { - global_gc_for_each_objspace(GET_VM(), objspace, gc_finalize_deferred); - } - } - } - GLOBAL_GC_END(vm, objspace); - if (!global_gc_possible) { - reason &= ~GPR_FLAG_GLOBAL; - return garbage_collect_local(objspace, reason, need_finalize_deferred); - } - - return ret; -} - -static void -gc_set_flags_start(rb_objspace_t *objspace, unsigned int reason, unsigned int *do_full_mark) -{ - /* reason may be clobbered, later, so keep set immediate_sweep here */ - objspace->flags.immediate_sweep = !!(reason & GPR_FLAG_IMMEDIATE_SWEEP); - - objspace->flags.during_global_gc = !!(reason & GPR_FLAG_GLOBAL); -} - -static void -gc_set_flags_finish(rb_objspace_t *objspace, unsigned int reason, unsigned int *do_full_mark, unsigned int *immediate_mark) -{ - if (ruby_gc_stressful) { - int flag = FIXNUM_P(ruby_gc_stress_mode) ? FIX2INT(ruby_gc_stress_mode) : 0; - - if ((flag & (1<flags.immediate_sweep = !(flag & (1<flags.dont_incremental || - *immediate_mark || - ruby_gc_stressful) { - objspace->flags.during_incremental_marking = FALSE; - } - else { - objspace->flags.during_incremental_marking = *do_full_mark; - } - - /* Explicitly enable compaction (GC.compact) */ - if (do_full_mark && ruby_enable_autocompact) { - objspace->flags.during_compacting = TRUE; -#if RGENGC_CHECK_MODE - objspace->rcompactor.compare_func = ruby_autocompact_compare_func; -#endif - } - else { - objspace->flags.during_compacting = !!(reason & GPR_FLAG_COMPACT); - } - - if (!GC_ENABLE_LAZY_SWEEP || objspace->flags.dont_incremental) { - objspace->flags.immediate_sweep = TRUE; - } - - if (objspace->flags.immediate_sweep) reason |= GPR_FLAG_IMMEDIATE_SWEEP; - - gc_report(1, objspace, "gc_start(reason: %x) => %u, %d, %d\n", - reason, - *do_full_mark, !is_incremental_marking(objspace), objspace->flags.immediate_sweep); - -#if USE_DEBUG_COUNTER - RB_DEBUG_COUNTER_INC(gc_count); - - if (reason & GPR_FLAG_MAJOR_MASK) { - (void)RB_DEBUG_COUNTER_INC_IF(gc_major_nofree, reason & GPR_FLAG_MAJOR_BY_NOFREE); - (void)RB_DEBUG_COUNTER_INC_IF(gc_major_oldgen, reason & GPR_FLAG_MAJOR_BY_OLDGEN); - (void)RB_DEBUG_COUNTER_INC_IF(gc_major_shady, reason & GPR_FLAG_MAJOR_BY_SHADY); - (void)RB_DEBUG_COUNTER_INC_IF(gc_major_force, reason & GPR_FLAG_MAJOR_BY_FORCE); - (void)RB_DEBUG_COUNTER_INC_IF(gc_major_absorb, reason & GPR_FLAG_MAJOR_BY_ABSORB); -#if RGENGC_ESTIMATE_OLDMALLOC - (void)RB_DEBUG_COUNTER_INC_IF(gc_major_oldmalloc, reason & GPR_FLAG_MAJOR_BY_OLDMALLOC); -#endif - } - else { - (void)RB_DEBUG_COUNTER_INC_IF(gc_minor_newobj, reason & GPR_FLAG_NEWOBJ); - (void)RB_DEBUG_COUNTER_INC_IF(gc_minor_malloc, reason & GPR_FLAG_MALLOC); - (void)RB_DEBUG_COUNTER_INC_IF(gc_minor_method, reason & GPR_FLAG_METHOD); - (void)RB_DEBUG_COUNTER_INC_IF(gc_minor_capi, reason & GPR_FLAG_CAPI); - (void)RB_DEBUG_COUNTER_INC_IF(gc_minor_stress, reason & GPR_FLAG_STRESS); - } -#endif - - objspace->profile.count++; - objspace->profile.latest_gc_info = reason; - objspace->profile.total_allocated_objects_at_gc_start = total_allocated_objects(objspace); - objspace->profile.heap_used_at_gc_start = heap_allocated_pages; - objspace->profile.weak_references_count = 0; - objspace->profile.retained_weak_references_count = 0; - gc_prof_setup_new_record(objspace, reason); - gc_reset_malloc_info(objspace, *do_full_mark); - - gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_START, 0 /* TODO: pass minor/immediate flag? */); - GC_ASSERT(during_gc); -} - -static int -gc_start(rb_objspace_t *objspace, unsigned int reason) -{ - VM_ASSERT(objspace->local_data.local_gc_level > 0 || objspace->local_data.running_global_gc); - VM_ASSERT(!GET_RACTOR()->teardown_cleanup_done); - - rb_vm_t *vm = GET_VM(); - - unsigned int global_gc = !!(reason & GPR_FLAG_GLOBAL); - if (global_gc) { - reason |= GPR_FLAG_FULL_MARK; - reason |= GPR_FLAG_IMMEDIATE_MARK; - reason |= GPR_FLAG_IMMEDIATE_SWEEP; - } - unsigned int do_full_mark = !!(reason & GPR_FLAG_FULL_MARK); - unsigned int immediate_mark = reason & GPR_FLAG_IMMEDIATE_MARK; - gc_set_flags_start(objspace, reason, &do_full_mark); - - if (!heap_allocated_pages) return TRUE; /* heap is not ready */ - if (!(reason & GPR_FLAG_METHOD) && !ready_to_gc(objspace)) return TRUE; /* GC is not allowed */ - - GC_ASSERT(gc_mode(objspace) == gc_mode_none); - GC_ASSERT(!is_lazy_sweeping(objspace)); - GC_ASSERT(!is_incremental_marking(objspace)); - - if (global_gc) { - struct objspace_local_data *local_data = NULL; - ccan_list_for_each(&vm->objspace_set, local_data, objspace_node) { - rb_objspace_t *os = local_data->objspace; - if (os == objspace) continue; - gc_set_flags_start(os, reason, &do_full_mark); - } - } - - rb_global_space_t *global_space = &rb_global_space; - - unsigned int lock_lev; - if (global_gc) { - VM_ASSERT(objspace->local_data.running_global_gc); - ASSERT_vm_locking(); - ASSERT_ractor_safe_gc_state(); - gc_global_enter(objspace, gc_enter_event_start, &lock_lev); -#if RGENGC_CHECK_MODE >= 2 - gc_verify_internal_consistency(objspace); -#endif - - struct objspace_local_data *local_data = NULL; - ccan_list_for_each(&vm->objspace_set, local_data, objspace_node) { - rb_objspace_t *os = local_data->objspace; - if (os->local_data.ractor && os->local_data.ractor->gc_chain_node) { - os->local_data.ractor->gc_chain_node->value = 0; - } - gc_set_flags_finish(os, reason, &do_full_mark, &immediate_mark); - } - rb_native_mutex_lock(&global_space->rglobalgc.shared_tracking_lock); - global_space->rglobalgc.need_global_gc = false; - rb_native_mutex_unlock(&global_space->rglobalgc.shared_tracking_lock); - gc_marks_global(objspace); - gc_global_exit(objspace, gc_enter_event_start, &lock_lev); - } - else { - VM_ASSERT(objspace->local_data.local_gc_level > 0); - gc_enter(objspace, gc_enter_event_start); -#if RGENGC_CHECK_MODE >= 2 - gc_verify_internal_consistency(objspace); -#endif - - gc_set_flags_finish(objspace, reason, &do_full_mark, &immediate_mark); - gc_prof_timer_start(objspace); - { - if (gc_marks(objspace, do_full_mark)) { - gc_sweep(objspace); - } - } - gc_prof_timer_stop(objspace); - gc_exit(objspace, gc_enter_event_start); - } - - return TRUE; -} - -static void -gc_rest(rb_objspace_t *objspace) -{ - if (is_incremental_marking(objspace) || is_lazy_sweeping(objspace)) { - unsigned int lock_lev; - LOCAL_GC_BEGIN(objspace); - { - gc_enter(objspace, gc_enter_event_rest); - - if (RGENGC_CHECK_MODE >= 2) gc_verify_internal_consistency(objspace); - - if (is_incremental_marking(objspace)) { - gc_marking_enter(objspace); - gc_marks_rest(objspace); - gc_marking_exit(objspace); - - gc_sweep(objspace); - } - - if (is_lazy_sweeping(objspace)) { - gc_sweeping_enter(objspace); - gc_sweep_rest(objspace); - gc_sweeping_exit(objspace); - } - - gc_exit(objspace, gc_enter_event_rest); - } - LOCAL_GC_END(objspace); - } -} - -struct objspace_and_reason { - rb_objspace_t *objspace; - unsigned int reason; -}; - -static void -gc_current_status_fill(rb_objspace_t *objspace, char *buff) -{ - int i = 0; - if (is_marking(objspace)) { - buff[i++] = 'M'; - if (is_full_marking(objspace)) buff[i++] = 'F'; - if (is_incremental_marking(objspace)) buff[i++] = 'I'; - } - else if (is_sweeping(objspace)) { - buff[i++] = 'S'; - if (is_lazy_sweeping(objspace)) buff[i++] = 'L'; - } - else { - buff[i++] = 'N'; - } - buff[i] = '\0'; -} - -static const char * -gc_current_status(rb_objspace_t *objspace) -{ - static char buff[0x10]; - gc_current_status_fill(objspace, buff); - return buff; -} - -#if PRINT_ENTER_EXIT_TICK - -static tick_t last_exit_tick; -static tick_t enter_tick; -static int enter_count = 0; -static char last_gc_status[0x10]; - -static inline void -gc_record(rb_objspace_t *objspace, int direction, const char *event) -{ - if (direction == 0) { /* enter */ - enter_count++; - enter_tick = tick(); - gc_current_status_fill(objspace, last_gc_status); - } - else { /* exit */ - tick_t exit_tick = tick(); - char current_gc_status[0x10]; - gc_current_status_fill(objspace, current_gc_status); -#if 1 - /* [last mutator time] [gc time] [event] */ - fprintf(stderr, "%"PRItick"\t%"PRItick"\t%s\t[%s->%s|%c]\n", - enter_tick - last_exit_tick, - exit_tick - enter_tick, - event, - last_gc_status, current_gc_status, - (objspace->profile.latest_gc_info & GPR_FLAG_MAJOR_MASK) ? '+' : '-'); - last_exit_tick = exit_tick; -#else - /* [enter_tick] [gc time] [event] */ - fprintf(stderr, "%"PRItick"\t%"PRItick"\t%s\t[%s->%s|%c]\n", - enter_tick, - exit_tick - enter_tick, - event, - last_gc_status, current_gc_status, - (objspace->profile.latest_gc_info & GPR_FLAG_MAJOR_MASK) ? '+' : '-'); -#endif - } -} -#else /* PRINT_ENTER_EXIT_TICK */ -static inline void -gc_record(rb_objspace_t *objspace, int direction, const char *event) -{ - /* null */ -} -#endif /* PRINT_ENTER_EXIT_TICK */ - -static const char * -gc_enter_event_cstr(enum gc_enter_event event) -{ - switch (event) { - case gc_enter_event_start: return "start"; - case gc_enter_event_continue: return "continue"; - case gc_enter_event_rest: return "rest"; - case gc_enter_event_finalizer: return "finalizer"; - case gc_enter_event_rb_memerror: return "rb_memerror"; - } - return NULL; -} - -static void -gc_enter_count(enum gc_enter_event event) -{ - switch (event) { - case gc_enter_event_start: RB_DEBUG_COUNTER_INC(gc_enter_start); break; - case gc_enter_event_continue: RB_DEBUG_COUNTER_INC(gc_enter_continue); break; - case gc_enter_event_rest: RB_DEBUG_COUNTER_INC(gc_enter_rest); break; - case gc_enter_event_finalizer: RB_DEBUG_COUNTER_INC(gc_enter_finalizer); break; - case gc_enter_event_rb_memerror: /* nothing */ break; - } -} - -static bool current_process_time(struct timespec *ts); - -static void -gc_clock_start(struct timespec *ts) -{ - if (!current_process_time(ts)) { - ts->tv_sec = 0; - ts->tv_nsec = 0; - } -} - -static uint64_t -gc_clock_end(struct timespec *ts) -{ - struct timespec end_time; - - if ((ts->tv_sec > 0 || ts->tv_nsec > 0) && - current_process_time(&end_time) && - end_time.tv_sec >= ts->tv_sec) { - return (uint64_t)(end_time.tv_sec - ts->tv_sec) * (1000 * 1000 * 1000) + - (end_time.tv_nsec - ts->tv_nsec); - } - - return 0; -} - -static inline void -gc_enter(rb_objspace_t *objspace, enum gc_enter_event event) -{ - gc_enter_count(event); - if (UNLIKELY(during_gc != 0)) rb_bug("during_gc != 0"); - if (RGENGC_CHECK_MODE >= 3 && (dont_gc_val() == 0)) gc_verify_internal_consistency(objspace); - - VM_ASSERT(!rb_redirecting_allocation()); - - during_gc = TRUE; - RUBY_DEBUG_LOG("%s (%s)",gc_enter_event_cstr(event), gc_current_status(objspace)); - gc_report(1, objspace, "gc_enter: %s [%s]\n", gc_enter_event_cstr(event), gc_current_status(objspace)); - gc_record(objspace, 0, gc_enter_event_cstr(event)); - gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_ENTER, 0); /* TODO: which parameter should be passed? */ -} - -static inline void -gc_global_enter(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_lev) -{ - ASSERT_vm_locking(); - gc_enter(objspace, event); - - struct objspace_local_data *local_data = NULL; - ccan_list_for_each(&GET_VM()->objspace_set, local_data, objspace_node) { - rb_objspace_t *os = local_data->objspace; - if (os == objspace) - continue; - gc_enter(os, event); - } -} - -static inline void -gc_exit(rb_objspace_t *objspace, enum gc_enter_event event) -{ - GC_ASSERT(during_gc != 0); - - gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_EXIT, 0); /* TODO: which parameter should be passed? */ - gc_record(objspace, 1, gc_enter_event_cstr(event)); - RUBY_DEBUG_LOG("%s (%s)", gc_enter_event_cstr(event), gc_current_status(objspace)); - gc_report(1, objspace, "gc_exit: %s [%s]\n", gc_enter_event_cstr(event), gc_current_status(objspace)); - during_gc = FALSE; - objspace->flags.during_global_gc = false; - - -#if RGENGC_CHECK_MODE >= 2 - if (event == gc_enter_event_sweep_continue && gc_mode(objspace) == gc_mode_none) { - GC_ASSERT(!during_gc); - // sweep finished - gc_verify_internal_consistency(objspace); - } -#endif -} + if (dfree) { + if (dfree == RUBY_DEFAULT_FREE) { + if (!RTYPEDDATA_EMBEDDED_P(obj)) { + xfree(data); + RB_DEBUG_COUNTER_INC(obj_data_xfree); + } + } + else if (free_immediately) { + (*dfree)(data); + if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !RTYPEDDATA_EMBEDDED_P(obj)) { + xfree(data); + } -static inline void -gc_global_exit(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_lev) -{ - struct objspace_local_data *local_data = NULL; - ccan_list_for_each(&GET_VM()->objspace_set, local_data, objspace_node) { - rb_objspace_t *os = local_data->objspace; - if (os == objspace) - continue; - gc_exit(os, event); + RB_DEBUG_COUNTER_INC(obj_data_imm_free); + } + else { + rb_gc_impl_make_zombie(rb_gc_get_objspace(), obj, dfree, data); + RB_DEBUG_COUNTER_INC(obj_data_zombie); + return FALSE; + } + } + else { + RB_DEBUG_COUNTER_INC(obj_data_empty); + } } - gc_exit(objspace, event); -} - -#ifndef MEASURE_GC -#define MEASURE_GC (objspace->flags.measure_gc) -#endif - -static void -gc_marking_enter(rb_objspace_t *objspace) -{ - GC_ASSERT(during_gc != 0); - - if (MEASURE_GC) { - gc_clock_start(&objspace->profile.marking_start_time); - } + return true; } -static void -gc_marking_exit(rb_objspace_t *objspace) +bool +rb_gc_obj_free(void *objspace, VALUE obj) { - GC_ASSERT(during_gc != 0); - - if (MEASURE_GC) { - objspace->profile.marking_time_ns += gc_clock_end(&objspace->profile.marking_start_time); - } -} + RB_DEBUG_COUNTER_INC(obj_free); -static void -gc_sweeping_enter(rb_objspace_t *objspace) -{ - GC_ASSERT(during_gc != 0); + rb_gc_event_hook(obj, RUBY_INTERNAL_EVENT_FREEOBJ); - if (MEASURE_GC) { - gc_clock_start(&objspace->profile.sweeping_start_time); + switch (BUILTIN_TYPE(obj)) { + case T_NIL: + case T_FIXNUM: + case T_TRUE: + case T_FALSE: + rb_bug("obj_free() called for broken object"); + break; + default: + break; } -} - -static void -gc_sweeping_exit(rb_objspace_t *objspace) -{ - GC_ASSERT(during_gc != 0); - if (MEASURE_GC) { - objspace->profile.sweeping_time_ns += gc_clock_end(&objspace->profile.sweeping_start_time); + if (FL_TEST(obj, FL_EXIVAR)) { + rb_free_generic_ivar((VALUE)obj); + FL_UNSET(obj, FL_EXIVAR); } -} - -static void * -gc_with_gvl(void *ptr) -{ - struct objspace_and_reason *oar = (struct objspace_and_reason *)ptr; - return (void *)(VALUE)garbage_collect(oar->objspace, oar->reason, false); -} -static int -garbage_collect_with_gvl(rb_objspace_t *objspace, unsigned int reason) -{ - if (dont_gc_val()) return TRUE; - if (ruby_thread_has_gvl_p()) { - return garbage_collect(objspace, reason, false); - } - else { - if (ruby_native_thread_p()) { - struct objspace_and_reason oar; - oar.objspace = objspace; - oar.reason = reason; - return (int)(VALUE)rb_thread_call_with_gvl(gc_with_gvl, (void *)&oar); + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + if (rb_shape_obj_too_complex(obj)) { + RB_DEBUG_COUNTER_INC(obj_obj_too_complex); + st_free_table(ROBJECT_IV_HASH(obj)); + } + else if (RBASIC(obj)->flags & ROBJECT_EMBED) { + RB_DEBUG_COUNTER_INC(obj_obj_embed); } else { - /* no ruby thread */ - fprintf(stderr, "[FATAL] failed to allocate memory\n"); - exit(EXIT_FAILURE); + xfree(ROBJECT(obj)->as.heap.ivptr); + RB_DEBUG_COUNTER_INC(obj_obj_ptr); } - } -} - -static int -gc_set_candidate_object_i(void *vstart, void *vend, size_t stride, void *data) -{ - rb_objspace_t *objspace = &rb_objspace; - VALUE v = (VALUE)vstart; - for (; v != (VALUE)vend; v += stride) { - asan_unpoisoning_object(v) { - switch (BUILTIN_TYPE(v)) { - case T_NONE: - case T_ZOMBIE: - break; - case T_STRING: - // precompute the string coderange. This both save time for when it will be - // eventually needed, and avoid mutating heap pages after a potential fork. - rb_enc_str_coderange(v); - // fall through - default: - if (!RVALUE_OLD_P(v) && !RVALUE_WB_UNPROTECTED(v)) { - RVALUE_AGE_SET_CANDIDATE(objspace, v); - } - } + break; + case T_MODULE: + case T_CLASS: + rb_id_table_free(RCLASS_M_TBL(obj)); + rb_cc_table_free(obj); + if (rb_shape_obj_too_complex(obj)) { + st_free_table((st_table *)RCLASS_IVPTR(obj)); + } + else { + xfree(RCLASS_IVPTR(obj)); } - } - - return 0; -} - -static VALUE -gc_start_internal(rb_execution_context_t *ec, VALUE self, VALUE full_mark, VALUE immediate_mark, VALUE immediate_sweep, VALUE global, VALUE compact) -{ - bool running_full_mark = RTEST(full_mark); - bool running_immediate_mark = RTEST(immediate_mark); - bool running_immediate_sweep = RTEST(immediate_sweep); - bool running_global = RTEST(global); - bool running_compact = RTEST(compact); - - if (running_global) { - if (!running_full_mark) rb_raise(rb_eArgError, "`full_mark' must be true if `global' is true"); - if (!running_immediate_mark) rb_raise(rb_eArgError, "`immediate_mark' must be true if `global' is true"); - if (!running_immediate_sweep) rb_raise(rb_eArgError, "`immediate_sweep' must be true if `global' is true"); - if (running_compact) rb_raise(rb_eArgError, "global compaction is not yet implemented"); - } - - rb_objspace_t *objspace = &rb_objspace; - unsigned int reason = (GPR_FLAG_FULL_MARK | - GPR_FLAG_IMMEDIATE_MARK | - GPR_FLAG_IMMEDIATE_SWEEP | - GPR_FLAG_GLOBAL | - GPR_FLAG_METHOD); - - /* For now, compact implies full mark / sweep, so ignore other flags */ - if (running_compact) { - GC_ASSERT(GC_COMPACTION_SUPPORTED); - - reason |= GPR_FLAG_COMPACT; - } - else { - if (!running_full_mark) reason &= ~GPR_FLAG_FULL_MARK; - if (!running_immediate_mark) reason &= ~GPR_FLAG_IMMEDIATE_MARK; - if (!running_immediate_sweep) reason &= ~GPR_FLAG_IMMEDIATE_SWEEP; - } - if (!running_global) reason &= ~GPR_FLAG_GLOBAL; - - if(!GET_VM()->gc_deactivated) { - if (reason & GPR_FLAG_COMPACT) { //TODO: Implement global compaction - reason &= ~GPR_FLAG_COMPACT; - while (global_gc_needed()) { - garbage_collect(objspace, reason, true); - } - reason |= GPR_FLAG_COMPACT; - } - garbage_collect(objspace, reason, true); - } - - return Qnil; -} - -static void -free_empty_pages(void) -{ - rb_objspace_t *objspace = &rb_objspace; - - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - /* Move all empty pages to the tomb heap for freeing. */ - rb_size_pool_t *size_pool = &size_pools[i]; - rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); - rb_heap_t *tomb_heap = SIZE_POOL_TOMB_HEAP(size_pool); - - size_t freed_pages = 0; - struct heap_page **next_page_ptr = &heap->free_pages; - struct heap_page *page = heap->free_pages; - while (page) { - /* All finalizers should have been ran in gc_start_internal, so there - * should be no objects that require finalization. */ - GC_ASSERT(page->final_slots == 0); + if (RCLASS_CONST_TBL(obj)) { + rb_free_const_table(RCLASS_CONST_TBL(obj)); + } + if (RCLASS_CVC_TBL(obj)) { + rb_id_table_foreach_values(RCLASS_CVC_TBL(obj), cvar_table_free_i, NULL); + rb_id_table_free(RCLASS_CVC_TBL(obj)); + } + rb_class_remove_subclass_head(obj); + rb_class_remove_from_module_subclasses(obj); + rb_class_remove_from_super_subclasses(obj); + if (FL_TEST_RAW(obj, RCLASS_SUPERCLASSES_INCLUDE_SELF)) { + xfree(RCLASS_SUPERCLASSES(obj)); + } - struct heap_page *next_page = page->free_next; + (void)RB_DEBUG_COUNTER_INC_IF(obj_module_ptr, BUILTIN_TYPE(obj) == T_MODULE); + (void)RB_DEBUG_COUNTER_INC_IF(obj_class_ptr, BUILTIN_TYPE(obj) == T_CLASS); + break; + case T_STRING: + rb_str_free(obj); + break; + case T_ARRAY: + rb_ary_free(obj); + break; + case T_HASH: +#if USE_DEBUG_COUNTER + switch (RHASH_SIZE(obj)) { + case 0: + RB_DEBUG_COUNTER_INC(obj_hash_empty); + break; + case 1: + RB_DEBUG_COUNTER_INC(obj_hash_1); + break; + case 2: + RB_DEBUG_COUNTER_INC(obj_hash_2); + break; + case 3: + RB_DEBUG_COUNTER_INC(obj_hash_3); + break; + case 4: + RB_DEBUG_COUNTER_INC(obj_hash_4); + break; + case 5: + case 6: + case 7: + case 8: + RB_DEBUG_COUNTER_INC(obj_hash_5_8); + break; + default: + GC_ASSERT(RHASH_SIZE(obj) > 8); + RB_DEBUG_COUNTER_INC(obj_hash_g8); + } - if (page->free_slots == page->total_slots) { - heap_unlink_page(objspace, heap, page); - heap_add_page(objspace, size_pool, tomb_heap, page); - freed_pages++; + if (RHASH_AR_TABLE_P(obj)) { + if (RHASH_AR_TABLE(obj) == NULL) { + RB_DEBUG_COUNTER_INC(obj_hash_null); } else { - *next_page_ptr = page; - next_page_ptr = &page->free_next; + RB_DEBUG_COUNTER_INC(obj_hash_ar); } - - page = next_page; } - - *next_page_ptr = NULL; - - size_pool_allocatable_pages_set(objspace, size_pool, size_pool->allocatable_pages + freed_pages); - } - - heap_pages_free_unused_pages(objspace); -} - -void -rb_gc_prepare_heap(void) -{ - rb_objspace_each_objects(gc_set_candidate_object_i, NULL); - gc_start_internal(NULL, Qtrue, Qtrue, Qtrue, Qtrue, Qfalse, Qtrue); - free_empty_pages(); - -#if defined(HAVE_MALLOC_TRIM) && !defined(RUBY_ALTERNATIVE_MALLOC_HEADER) - malloc_trim(0); -#endif -} - -static int -gc_is_moveable_obj(rb_objspace_t *objspace, VALUE obj) -{ - GC_ASSERT(!SPECIAL_CONST_P(obj)); - - if (using_local_limits(objspace) && (FL_TEST_RAW(obj, FL_SHAREABLE) || RVALUE_LOCAL_IMMUNE(obj))) { - return FALSE; - } - - switch (BUILTIN_TYPE(obj)) { - case T_NONE: - case T_MOVED: - case T_ZOMBIE: - return FALSE; - case T_SYMBOL: - if (RSYMBOL(obj)->id & ~ID_SCOPE_MASK) { - return FALSE; + else { + RB_DEBUG_COUNTER_INC(obj_hash_st); } - /* fall through */ - case T_STRING: - case T_OBJECT: - case T_FLOAT: - case T_IMEMO: - case T_ARRAY: - case T_BIGNUM: - case T_ICLASS: - case T_MODULE: +#endif + + rb_hash_free(obj); + break; case T_REGEXP: + if (RREGEXP(obj)->ptr) { + onig_free(RREGEXP(obj)->ptr); + RB_DEBUG_COUNTER_INC(obj_regexp_ptr); + } + break; case T_DATA: + if (!rb_data_free(objspace, obj)) return false; + break; case T_MATCH: - case T_STRUCT: - case T_HASH: - case T_FILE: - case T_COMPLEX: - case T_RATIONAL: - case T_NODE: - case T_CLASS: - if (FL_TEST(obj, FL_FINALIZE)) { - /* The finalizer table is a numtable. It looks up objects by address. - * We can't mark the keys in the finalizer table because that would - * prevent the objects from being collected. This check prevents - * objects that are keys in the finalizer table from being moved - * without directly pinning them. */ - GC_ASSERT(st_is_member(finalizer_table, obj)); + { + rb_matchext_t *rm = RMATCH_EXT(obj); +#if USE_DEBUG_COUNTER + if (rm->regs.num_regs >= 8) { + RB_DEBUG_COUNTER_INC(obj_match_ge8); + } + else if (rm->regs.num_regs >= 4) { + RB_DEBUG_COUNTER_INC(obj_match_ge4); + } + else if (rm->regs.num_regs >= 1) { + RB_DEBUG_COUNTER_INC(obj_match_under4); + } +#endif + onig_region_free(&rm->regs, 0); + xfree(rm->char_offset); + RB_DEBUG_COUNTER_INC(obj_match_ptr); + } + break; + case T_FILE: + if (RFILE(obj)->fptr) { + make_io_zombie(objspace, obj); + RB_DEBUG_COUNTER_INC(obj_file_ptr); return FALSE; } - GC_ASSERT(RVALUE_MARKED(obj)); - GC_ASSERT(!RVALUE_PINNED(obj)); - - return TRUE; - - default: - rb_bug("gc_is_moveable_obj: unreachable (%d)", (int)BUILTIN_TYPE(obj)); break; - } - - return FALSE; -} - -static void -update_obj_id_mapping(rb_objspace_t *objspace, RVALUE *dest, RVALUE *src) -{ - rb_native_mutex_lock(&objspace->local_data.obj_id_lock); - if (FL_TEST((VALUE)src, FL_SEEN_OBJ_ID)) { - /* If the source object's object_id has been seen, we need to update - * the object to object id mapping. */ - st_data_t srcid = (st_data_t)src, id; - - gc_report(4, objspace, "Moving object with seen id: %p -> %p\n", (void *)src, (void *)dest); - /* Resizing the st table could cause a malloc */ - DURING_GC_COULD_MALLOC_REGION_START(); - { - if (!st_delete(objspace->local_data.obj_to_id_tbl, &srcid, &id)) { - rb_bug("gc_move: object ID seen, but not in mapping table: %s", obj_info((VALUE)src)); - } - - st_insert(objspace->local_data.obj_to_id_tbl, (st_data_t)dest, id); + case T_RATIONAL: + RB_DEBUG_COUNTER_INC(obj_rational); + break; + case T_COMPLEX: + RB_DEBUG_COUNTER_INC(obj_complex); + break; + case T_MOVED: + break; + case T_ICLASS: + /* Basically , T_ICLASS shares table with the module */ + if (RICLASS_OWNS_M_TBL_P(obj)) { + /* Method table is not shared for origin iclasses of classes */ + rb_id_table_free(RCLASS_M_TBL(obj)); } - DURING_GC_COULD_MALLOC_REGION_END(); - } - else { - GC_ASSERT(!st_lookup(objspace->local_data.obj_to_id_tbl, (st_data_t)src, NULL)); - } - rb_native_mutex_unlock(&objspace->local_data.obj_id_lock); -} - -static VALUE -gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, size_t slot_size) -{ - int marked; - int wb_unprotected; - int uncollectible; - int age; - RVALUE *dest = (RVALUE *)free; - RVALUE *src = (RVALUE *)scan; - - gc_report(4, objspace, "Moving object: %p -> %p\n", (void*)scan, (void *)free); + if (RCLASS_CALLABLE_M_TBL(obj) != NULL) { + rb_id_table_free(RCLASS_CALLABLE_M_TBL(obj)); + } + rb_class_remove_subclass_head(obj); + rb_cc_table_free(obj); + rb_class_remove_from_module_subclasses(obj); + rb_class_remove_from_super_subclasses(obj); - GC_ASSERT(BUILTIN_TYPE(scan) != T_NONE); - GC_ASSERT(!MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(free), free)); + RB_DEBUG_COUNTER_INC(obj_iclass_ptr); + break; - GC_ASSERT(!RVALUE_MARKING((VALUE)src)); + case T_FLOAT: + RB_DEBUG_COUNTER_INC(obj_float); + break; - VM_ASSERT(!RVALUE_LOCAL_IMMUNE((VALUE)src)); + case T_BIGNUM: + if (!BIGNUM_EMBED_P(obj) && BIGNUM_DIGITS(obj)) { + xfree(BIGNUM_DIGITS(obj)); + RB_DEBUG_COUNTER_INC(obj_bignum_ptr); + } + else { + RB_DEBUG_COUNTER_INC(obj_bignum_embed); + } + break; - /* Save off bits for current object. */ - marked = RVALUE_MARKED((VALUE)src); - wb_unprotected = RVALUE_WB_UNPROTECTED((VALUE)src); - uncollectible = RVALUE_UNCOLLECTIBLE((VALUE)src); - bool remembered = RVALUE_REMEMBERED((VALUE)src); - age = RVALUE_AGE_GET((VALUE)src); + case T_NODE: + UNEXPECTED_NODE(obj_free); + break; - /* Clear bits for eventual T_MOVED */ - CLEAR_IN_BITMAP(GET_HEAP_MARK_BITS((VALUE)src), (VALUE)src); - CLEAR_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS((VALUE)src), (VALUE)src); - CLEAR_IN_BITMAP(GET_HEAP_UNCOLLECTIBLE_BITS((VALUE)src), (VALUE)src); - CLEAR_IN_BITMAP(GET_HEAP_PAGE((VALUE)src)->remembered_bits, (VALUE)src); + case T_STRUCT: + if ((RBASIC(obj)->flags & RSTRUCT_EMBED_LEN_MASK) || + RSTRUCT(obj)->as.heap.ptr == NULL) { + RB_DEBUG_COUNTER_INC(obj_struct_embed); + } + else { + xfree((void *)RSTRUCT(obj)->as.heap.ptr); + RB_DEBUG_COUNTER_INC(obj_struct_ptr); + } + break; - if (FL_TEST((VALUE)src, FL_EXIVAR)) { - /* Resizing the st table could cause a malloc */ - DURING_GC_COULD_MALLOC_REGION_START(); + case T_SYMBOL: { - rb_mv_generic_ivar((VALUE)src, (VALUE)dest); + rb_gc_free_dsymbol(obj); + RB_DEBUG_COUNTER_INC(obj_symbol); } - DURING_GC_COULD_MALLOC_REGION_END(); - } - - update_obj_id_mapping(objspace, dest, src); - -#if VM_CHECK_MODE > 0 - rb_native_mutex_lock(&objspace->local_data.shared_reference_tbl_lock); - VM_ASSERT(!st_lookup(objspace->local_data.shared_reference_tbl, (st_data_t)src, NULL)); - rb_native_mutex_unlock(&objspace->local_data.shared_reference_tbl_lock); -#endif - - /* Move the object */ - memcpy(dest, src, MIN(src_slot_size, slot_size)); - - if (RVALUE_OVERHEAD > 0) { - void *dest_overhead = (void *)(((uintptr_t)dest) + slot_size - RVALUE_OVERHEAD); - void *src_overhead = (void *)(((uintptr_t)src) + src_slot_size - RVALUE_OVERHEAD); - - memcpy(dest_overhead, src_overhead, RVALUE_OVERHEAD); - } - - memset(src, 0, src_slot_size); - RVALUE_AGE_RESET((VALUE)src); - - /* Set bits for object in new location */ - if (remembered) { - MARK_IN_BITMAP(GET_HEAP_PAGE(dest)->remembered_bits, (VALUE)dest); - } - else { - CLEAR_IN_BITMAP(GET_HEAP_PAGE(dest)->remembered_bits, (VALUE)dest); - } + break; - if (marked) { - MARK_IN_BITMAP(GET_HEAP_MARK_BITS((VALUE)dest), (VALUE)dest); - } - else { - CLEAR_IN_BITMAP(GET_HEAP_MARK_BITS((VALUE)dest), (VALUE)dest); - } + case T_IMEMO: + rb_imemo_free((VALUE)obj); + break; - if (wb_unprotected) { - MARK_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS((VALUE)dest), (VALUE)dest); - } - else { - CLEAR_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS((VALUE)dest), (VALUE)dest); + default: + rb_bug("gc_sweep(): unknown data type 0x%x(%p) 0x%"PRIxVALUE, + BUILTIN_TYPE(obj), (void*)obj, RBASIC(obj)->flags); } - if (uncollectible) { - MARK_IN_BITMAP(GET_HEAP_UNCOLLECTIBLE_BITS((VALUE)dest), (VALUE)dest); + if (FL_TEST(obj, FL_FINALIZE)) { + rb_gc_impl_make_zombie(rb_gc_get_objspace(), obj, 0, 0); + return FALSE; } else { - CLEAR_IN_BITMAP(GET_HEAP_UNCOLLECTIBLE_BITS((VALUE)dest), (VALUE)dest); + RBASIC(obj)->flags = 0; + return TRUE; } - - RVALUE_AGE_SET((VALUE)dest, age); - /* Assign forwarding address */ - src->as.moved.flags = T_MOVED; - src->as.moved.dummy = Qundef; - src->as.moved.destination = (VALUE)dest; - GC_ASSERT(BUILTIN_TYPE((VALUE)dest) != T_NONE); - - return (VALUE)src; } -#if GC_CAN_COMPILE_COMPACTION -static int -compare_pinned_slots(const void *left, const void *right, void *dummy) +void +rb_objspace_set_event_hook(const rb_event_flag_t event) { - struct heap_page *left_page; - struct heap_page *right_page; - - left_page = *(struct heap_page * const *)left; - right_page = *(struct heap_page * const *)right; - - return left_page->pinned_slots - right_page->pinned_slots; + rb_gc_impl_set_event_hook(rb_gc_get_objspace(), event); } static int -compare_free_slots(const void *left, const void *right, void *dummy) -{ - struct heap_page *left_page; - struct heap_page *right_page; - - left_page = *(struct heap_page * const *)left; - right_page = *(struct heap_page * const *)right; - - return left_page->free_slots - right_page->free_slots; -} - -static void -gc_sort_heap_by_compare_func(rb_objspace_t *objspace, gc_compact_compare_func compare_func) +internal_object_p(VALUE obj) { - for (int j = 0; j < SIZE_POOL_COUNT; j++) { - rb_size_pool_t *size_pool = &size_pools[j]; - - size_t total_pages = SIZE_POOL_EDEN_HEAP(size_pool)->total_pages; - size_t size = size_mul_or_raise(total_pages, sizeof(struct heap_page *), rb_eRuntimeError); - struct heap_page *page = 0, **page_list = malloc(size); - size_t i = 0; - - SIZE_POOL_EDEN_HEAP(size_pool)->free_pages = NULL; - ccan_list_for_each(&SIZE_POOL_EDEN_HEAP(size_pool)->pages, page, page_node) { - page_list[i++] = page; - GC_ASSERT(page); - } - - GC_ASSERT((size_t)i == total_pages); - - /* Sort the heap so "filled pages" are first. `heap_add_page` adds to the - * head of the list, so empty pages will end up at the start of the heap */ - ruby_qsort(page_list, total_pages, sizeof(struct heap_page *), compare_func, NULL); - - /* Reset the eden heap */ - ccan_list_head_init(&SIZE_POOL_EDEN_HEAP(size_pool)->pages); + void *ptr = asan_unpoison_object_temporary(obj); - for (i = 0; i < total_pages; i++) { - ccan_list_add(&SIZE_POOL_EDEN_HEAP(size_pool)->pages, &page_list[i]->page_node); - if (page_list[i]->free_slots != 0) { - heap_add_freepage(SIZE_POOL_EDEN_HEAP(size_pool), page_list[i]); + if (RBASIC(obj)->flags) { + switch (BUILTIN_TYPE(obj)) { + case T_NODE: + UNEXPECTED_NODE(internal_object_p); + break; + case T_NONE: + case T_MOVED: + case T_IMEMO: + case T_ICLASS: + case T_ZOMBIE: + break; + case T_CLASS: + if (!RBASIC(obj)->klass) break; + if (RCLASS_SINGLETON_P(obj)) { + return rb_singleton_class_internal_p(obj); } + return 0; + default: + if (!RBASIC(obj)->klass) break; + return 0; } - - free(page_list); } + if (ptr || !RBASIC(obj)->flags) { + asan_poison_object(obj); + } + return 1; } -#endif -static void -gc_ref_update_array(rb_objspace_t * objspace, VALUE v) +int +rb_objspace_internal_object_p(VALUE obj) { - if (ARY_SHARED_P(v)) { - VALUE old_root = RARRAY(v)->as.heap.aux.shared_root; - - UPDATE_IF_MOVED(objspace, RARRAY(v)->as.heap.aux.shared_root); - - VALUE new_root = RARRAY(v)->as.heap.aux.shared_root; - // If the root is embedded and its location has changed - if (ARY_EMBED_P(new_root) && new_root != old_root) { - size_t offset = (size_t)(RARRAY(v)->as.heap.ptr - RARRAY(old_root)->as.ary); - GC_ASSERT(RARRAY(v)->as.heap.ptr >= RARRAY(old_root)->as.ary); - RARRAY(v)->as.heap.ptr = RARRAY(new_root)->as.ary + offset; - } - } - else { - long len = RARRAY_LEN(v); - - if (len > 0) { - VALUE *ptr = (VALUE *)RARRAY_CONST_PTR(v); - for (long i = 0; i < len; i++) { - UPDATE_IF_MOVED(objspace, ptr[i]); - } - } - - if (rb_gc_obj_slot_size(v) >= rb_ary_size_as_embedded(v)) { - if (rb_ary_embeddable_p(v)) { - rb_ary_make_embedded(v); - } - } - } + return internal_object_p(obj); } -static void gc_ref_update_table_values_only(rb_objspace_t *objspace, st_table *tbl); +struct os_each_struct { + size_t num; + VALUE of; +}; -static void -gc_ref_update_object(rb_objspace_t *objspace, VALUE v) +static int +os_obj_of_i(void *vstart, void *vend, size_t stride, void *data) { - VALUE *ptr = ROBJECT_IVPTR(v); - - if (rb_shape_obj_too_complex(v)) { - gc_ref_update_table_values_only(objspace, ROBJECT_IV_HASH(v)); - return; - } - - size_t slot_size = rb_gc_obj_slot_size(v); - size_t embed_size = rb_obj_embedded_size(ROBJECT_IV_CAPACITY(v)); - if (slot_size >= embed_size && !RB_FL_TEST_RAW(v, ROBJECT_EMBED)) { - // Object can be re-embedded - memcpy(ROBJECT(v)->as.ary, ptr, sizeof(VALUE) * ROBJECT_IV_COUNT(v)); - RB_FL_SET_RAW(v, ROBJECT_EMBED); - xfree(ptr); - ptr = ROBJECT(v)->as.ary; - } + struct os_each_struct *oes = (struct os_each_struct *)data; - for (uint32_t i = 0; i < ROBJECT_IV_COUNT(v); i++) { - UPDATE_IF_MOVED(objspace, ptr[i]); + VALUE v = (VALUE)vstart; + for (; v != (VALUE)vend; v += stride) { + if (!internal_object_p(v)) { + if (!oes->of || rb_obj_is_kind_of(v, oes->of)) { + rb_yield(v); + oes->num++; + } + } } + + return 0; } -static int -hash_replace_ref(st_data_t *key, st_data_t *value, st_data_t argp, int existing) +static VALUE +os_obj_of(VALUE of) { - rb_objspace_t *objspace = (rb_objspace_t *)argp; - - if (gc_object_moved_p(objspace, (VALUE)*key)) { - *key = rb_gc_location((VALUE)*key); - } - - if (gc_object_moved_p(objspace, (VALUE)*value)) { - *value = rb_gc_location((VALUE)*value); - } + struct os_each_struct oes; - return ST_CONTINUE; + oes.num = 0; + oes.of = of; + rb_objspace_each_objects(os_obj_of_i, &oes); + return SIZET2NUM(oes.num); } -static int -hash_foreach_replace(st_data_t key, st_data_t value, st_data_t argp, int error) -{ - rb_objspace_t *objspace; - - objspace = (rb_objspace_t *)argp; +/* + * call-seq: + * ObjectSpace.each_object([module]) {|obj| ... } -> integer + * ObjectSpace.each_object([module]) -> an_enumerator + * + * Calls the block once for each living, nonimmediate object in this + * Ruby process. If module is specified, calls the block + * for only those classes or modules that match (or are a subclass of) + * module. Returns the number of objects found. Immediate + * objects (Fixnums, Symbols + * true, false, and nil) are + * never returned. In the example below, #each_object returns both + * the numbers we defined and several constants defined in the Math + * module. + * + * If no block is given, an enumerator is returned instead. + * + * a = 102.7 + * b = 95 # Won't be returned + * c = 12345678987654321 + * count = ObjectSpace.each_object(Numeric) {|x| p x } + * puts "Total count: #{count}" + * + * produces: + * + * 12345678987654321 + * 102.7 + * 2.71828182845905 + * 3.14159265358979 + * 2.22044604925031e-16 + * 1.7976931348623157e+308 + * 2.2250738585072e-308 + * Total count: 7 + * + */ - if (gc_object_moved_p(objspace, (VALUE)key)) { - return ST_REPLACE; - } +static VALUE +os_each_obj(int argc, VALUE *argv, VALUE os) +{ + VALUE of; - if (gc_object_moved_p(objspace, (VALUE)value)) { - return ST_REPLACE; - } - return ST_CONTINUE; + of = (!rb_check_arity(argc, 0, 1) ? 0 : argv[0]); + RETURN_ENUMERATOR(os, 1, &of); + return os_obj_of(of); } -static int -hash_replace_ref_value(st_data_t *key, st_data_t *value, st_data_t argp, int existing) +static void +should_belong_to_current_objspace(VALUE obj) { - rb_objspace_t *objspace = (rb_objspace_t *)argp; - - if (gc_object_moved_p(objspace, (VALUE)*value)) { - *value = rb_gc_location((VALUE)*value); + if (GET_OBJSPACE_OF_VALUE(obj) != &rb_objspace) { + rb_raise(rb_eRactorIsolationError, "cannot define or undefine finalizers for objects belonging to another Ractor"); } - - return ST_CONTINUE; } -static int -hash_foreach_replace_value(st_data_t key, st_data_t value, st_data_t argp, int error) -{ - rb_objspace_t *objspace; +/* + * call-seq: + * ObjectSpace.undefine_finalizer(obj) + * + * Removes all finalizers for obj. + * + */ - objspace = (rb_objspace_t *)argp; +static VALUE +undefine_final(VALUE os, VALUE obj) +{ + should_belong_to_current_objspace(obj); + return rb_gc_impl_undefine_finalizer(rb_gc_get_objspace(), obj); +} - if (gc_object_moved_p(objspace, (VALUE)value)) { - return ST_REPLACE; +static void +should_be_callable(VALUE block) +{ + if (!rb_obj_respond_to(block, idCall, TRUE)) { + rb_raise(rb_eArgError, "wrong type argument %"PRIsVALUE" (should be callable)", + rb_obj_class(block)); } - return ST_CONTINUE; } static void -gc_ref_update_table_values_only(rb_objspace_t *objspace, st_table *tbl) +should_be_finalizable(VALUE obj) { - if (!tbl || tbl->num_entries == 0) return; - - if (st_foreach_with_replace(tbl, hash_foreach_replace_value, hash_replace_ref_value, (st_data_t)objspace)) { - rb_raise(rb_eRuntimeError, "hash modified during iteration"); + if (!FL_ABLE(obj)) { + rb_raise(rb_eArgError, "cannot define finalizer for %s", + rb_obj_classname(obj)); } + rb_check_frozen(obj); } void -rb_gc_ref_update_table_values_only(st_table *tbl) +rb_gc_copy_finalizer(VALUE dest, VALUE obj) { - gc_ref_update_table_values_only(&rb_objspace, tbl); + rb_gc_impl_copy_finalizer(rb_gc_get_objspace(), dest, obj); } -static void -gc_update_table_refs(rb_objspace_t * objspace, st_table *tbl) +/* + * call-seq: + * ObjectSpace.define_finalizer(obj, aProc=proc()) + * + * Adds aProc as a finalizer, to be called after obj + * was destroyed. The object ID of the obj will be passed + * as an argument to aProc. If aProc is a lambda or + * method, make sure it can be called with a single argument. + * + * The return value is an array [0, aProc]. + * + * The two recommended patterns are to either create the finaliser proc + * in a non-instance method where it can safely capture the needed state, + * or to use a custom callable object that stores the needed state + * explicitly as instance variables. + * + * class Foo + * def initialize(data_needed_for_finalization) + * ObjectSpace.define_finalizer(self, self.class.create_finalizer(data_needed_for_finalization)) + * end + * + * def self.create_finalizer(data_needed_for_finalization) + * proc { + * puts "finalizing #{data_needed_for_finalization}" + * } + * end + * end + * + * class Bar + * class Remover + * def initialize(data_needed_for_finalization) + * @data_needed_for_finalization = data_needed_for_finalization + * end + * + * def call(id) + * puts "finalizing #{@data_needed_for_finalization}" + * end + * end + * + * def initialize(data_needed_for_finalization) + * ObjectSpace.define_finalizer(self, Remover.new(data_needed_for_finalization)) + * end + * end + * + * Note that if your finalizer references the object to be + * finalized it will never be run on GC, although it will still be + * run at exit. You will get a warning if you capture the object + * to be finalized as the receiver of the finalizer. + * + * class CapturesSelf + * def initialize(name) + * ObjectSpace.define_finalizer(self, proc { + * # this finalizer will only be run on exit + * puts "finalizing #{name}" + * }) + * end + * end + * + * Also note that finalization can be unpredictable and is never guaranteed + * to be run except on exit. + */ + +static VALUE +define_final(int argc, VALUE *argv, VALUE os) { - if (!tbl || tbl->num_entries == 0) return; + VALUE obj, block; - if (st_foreach_with_replace(tbl, hash_foreach_replace, hash_replace_ref, (st_data_t)objspace)) { - rb_raise(rb_eRuntimeError, "hash modified during iteration"); + rb_scan_args(argc, argv, "11", &obj, &block); + should_be_finalizable(obj); + should_belong_to_current_objspace(obj); + if (argc == 1) { + block = rb_block_proc(); + } + else { + should_be_callable(block); } -} -/* Update MOVED references in a VALUE=>VALUE st_table */ -void -rb_gc_update_tbl_refs(st_table *ptr) -{ - rb_objspace_t *objspace = &rb_objspace; - gc_update_table_refs(objspace, ptr); + if (rb_callable_receiver(block) == obj) { + rb_warn("finalizer references object to be finalized"); + } + + return rb_gc_impl_define_finalizer(rb_gc_get_objspace(), obj, block); } -static void -gc_ref_update_hash(rb_objspace_t * objspace, VALUE v) +VALUE +rb_define_finalizer(VALUE obj, VALUE block) { - rb_hash_stlike_foreach_with_replace(v, hash_foreach_replace, hash_replace_ref, (st_data_t)objspace); + should_be_finalizable(obj); + should_belong_to_current_objspace(obj); + should_be_callable(block); + return rb_gc_impl_define_finalizer(rb_gc_get_objspace(), obj, block); } -static void -gc_update_values(rb_objspace_t *objspace, long n, VALUE *values) +void +rb_objspace_call_finalizer(rb_objspace_t *objspace) { - long i; - - for (i=0; i an_object + * + * Converts an object id to a reference to the object. May not be + * called on an object id passed as a parameter to a finalizer. + * + * s = "I am a string" #=> "I am a string" + * r = ObjectSpace._id2ref(s.object_id) #=> "I am a string" + * r == s #=> true + * + * On multi-ractor mode, if the object is not shareable, it raises + * RangeError. + */ - VALUE destination; +static VALUE +id2ref(VALUE objid) +{ +#if SIZEOF_LONG == SIZEOF_VOIDP +#define NUM2PTR(x) NUM2ULONG(x) +#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP +#define NUM2PTR(x) NUM2ULL(x) +#endif + objid = rb_to_int(objid); + if (FIXNUM_P(objid) || rb_big_size(objid) <= SIZEOF_VOIDP) { + VALUE ptr = NUM2PTR(objid); + if (SPECIAL_CONST_P(ptr)) { + if (ptr == Qtrue) return Qtrue; + if (ptr == Qfalse) return Qfalse; + if (NIL_P(ptr)) return Qnil; + if (FIXNUM_P(ptr)) return ptr; + if (FLONUM_P(ptr)) return ptr; - if (!SPECIAL_CONST_P(value)) { - void *poisoned = asan_unpoison_object_temporary(value); + if (SYMBOL_P(ptr)) { + // Check that the symbol is valid + if (rb_static_id_valid_p(SYM2ID(ptr))) { + return ptr; + } + else { + rb_raise(rb_eRangeError, "%p is not symbol id value", (void *)ptr); + } + } - if (BUILTIN_TYPE(value) == T_MOVED) { - destination = (VALUE)RMOVED(value)->destination; - GC_ASSERT(BUILTIN_TYPE(destination) != T_NONE); - } - else { - destination = value; + rb_raise(rb_eRangeError, "%+"PRIsVALUE" is not id value", rb_int2str(objid, 10)); } + } - /* Re-poison slot if it's not the one we want */ - if (poisoned) { - GC_ASSERT(BUILTIN_TYPE(value) == T_NONE); - asan_poison_object(value); - } + VALUE obj = rb_gc_impl_object_id_to_ref(rb_gc_get_objspace(), objid); + if (GET_OBJSPACE_OF_VALUE(obj) == objspace || rb_ractor_shareable_p(obj)) { + return obj; } else { - destination = value; - } - - return destination; -} - -static enum rb_id_table_iterator_result -update_id_table(VALUE *value, void *data, int existing) -{ - rb_objspace_t *objspace = (rb_objspace_t *)data; - - if (gc_object_moved_p(objspace, (VALUE)*value)) { - *value = rb_gc_location((VALUE)*value); + rb_raise(rb_eRangeError, "%+"PRIsVALUE" is id of the unshareable object on multi-ractor", rb_int2str(objid, 10)); } - - return ID_TABLE_CONTINUE; } -static void -update_m_tbl(rb_objspace_t *objspace, struct rb_id_table *tbl) +/* :nodoc: */ +static VALUE +os_id2ref(VALUE os, VALUE objid) { - if (tbl) { - rb_id_table_foreach_values_with_replace(tbl, check_id_table_move, update_id_table, objspace); - } + return id2ref(objid); } -static enum rb_id_table_iterator_result -update_cc_tbl_i(VALUE ccs_ptr, void *data) +static VALUE +rb_find_object_id(void *objspace, VALUE obj, VALUE (*get_heap_object_id)(void *, VALUE)) { - rb_objspace_t *objspace = (rb_objspace_t *)data; - struct rb_class_cc_entries *ccs = (struct rb_class_cc_entries *)ccs_ptr; - VM_ASSERT(vm_ccs_p(ccs)); - - if (gc_object_moved_p(objspace, (VALUE)ccs->cme)) { - ccs->cme = (const rb_callable_method_entry_t *)rb_gc_location((VALUE)ccs->cme); - } - - for (int i=0; ilen); i++) { - 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); - } + if (SPECIAL_CONST_P(obj)) { +#if SIZEOF_LONG == SIZEOF_VOIDP + return LONG2NUM((SIGNED_VALUE)obj); +#else + return LL2NUM((SIGNED_VALUE)obj); +#endif } - // do not replace - return ID_TABLE_CONTINUE; + return get_heap_object_id(objspace, obj); } -static void -update_cc_tbl(rb_objspace_t *objspace, VALUE klass) +static VALUE +nonspecial_obj_id(void *_objspace, VALUE obj) { - struct rb_id_table *tbl = RCLASS_CC_TBL(klass); - if (tbl) { - rb_id_table_foreach_values(tbl, update_cc_tbl_i, objspace); - } +#if SIZEOF_LONG == SIZEOF_VOIDP + return (VALUE)((SIGNED_VALUE)(obj)|FIXNUM_FLAG); +#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP + return LL2NUM((SIGNED_VALUE)(obj) / 2); +#else +# error not supported +#endif } -static enum rb_id_table_iterator_result -update_cvc_tbl_i(VALUE cvc_entry, void *data) +VALUE +rb_memory_id(VALUE obj) { - struct rb_cvar_class_tbl_entry *entry; - rb_objspace_t * objspace = (rb_objspace_t *)data; - - entry = (struct rb_cvar_class_tbl_entry *)cvc_entry; - - if (entry->cref) { - TYPED_UPDATE_IF_MOVED(objspace, rb_cref_t *, entry->cref); - } - - entry->class_value = rb_gc_location(entry->class_value); - - return ID_TABLE_CONTINUE; + return rb_find_object_id(NULL, obj, nonspecial_obj_id); } -static void -update_cvc_tbl(rb_objspace_t *objspace, VALUE klass) -{ - struct rb_id_table *tbl = RCLASS_CVC_TBL(klass); - if (tbl) { - rb_id_table_foreach_values(tbl, update_cvc_tbl_i, objspace); - } -} +/* + * Document-method: __id__ + * Document-method: object_id + * + * call-seq: + * obj.__id__ -> integer + * obj.object_id -> integer + * + * Returns an integer identifier for +obj+. + * + * The same number will be returned on all calls to +object_id+ for a given + * object, and no two active objects will share an id. + * + * Note: that some objects of builtin classes are reused for optimization. + * This is the case for immediate values and frozen string literals. + * + * BasicObject implements +__id__+, Kernel implements +object_id+. + * + * Immediate values are not passed by reference but are passed by value: + * +nil+, +true+, +false+, Fixnums, Symbols, and some Floats. + * + * Object.new.object_id == Object.new.object_id # => false + * (21 * 2).object_id == (21 * 2).object_id # => true + * "hello".object_id == "hello".object_id # => false + * "hi".freeze.object_id == "hi".freeze.object_id # => true + */ -static enum rb_id_table_iterator_result -mark_cvc_tbl_i(VALUE cvc_entry, void *data) +VALUE +rb_obj_id(VALUE obj) { - rb_objspace_t *objspace = (rb_objspace_t *)data; - struct rb_cvar_class_tbl_entry *entry; - - entry = (struct rb_cvar_class_tbl_entry *)cvc_entry; - - RUBY_ASSERT(entry->cref == 0 || (BUILTIN_TYPE((VALUE)entry->cref) == T_IMEMO && IMEMO_TYPE_P(entry->cref, imemo_cref))); - gc_mark(objspace, (VALUE) entry->cref); - - return ID_TABLE_CONTINUE; -} + /* + * 32-bit VALUE space + * MSB ------------------------ LSB + * false 00000000000000000000000000000000 + * true 00000000000000000000000000000010 + * nil 00000000000000000000000000000100 + * undef 00000000000000000000000000000110 + * symbol ssssssssssssssssssssssss00001110 + * object oooooooooooooooooooooooooooooo00 = 0 (mod sizeof(RVALUE)) + * fixnum fffffffffffffffffffffffffffffff1 + * + * object_id space + * LSB + * false 00000000000000000000000000000000 + * true 00000000000000000000000000000010 + * nil 00000000000000000000000000000100 + * undef 00000000000000000000000000000110 + * symbol 000SSSSSSSSSSSSSSSSSSSSSSSSSSS0 S...S % A = 4 (S...S = s...s * A + 4) + * object oooooooooooooooooooooooooooooo0 o...o % A = 0 + * fixnum fffffffffffffffffffffffffffffff1 bignum if required + * + * where A = sizeof(RVALUE)/4 + * + * sizeof(RVALUE) is + * 20 if 32-bit, double is 4-byte aligned + * 24 if 32-bit, double is 8-byte aligned + * 40 if 64-bit + */ -static void -mark_cvc_tbl(rb_objspace_t *objspace, VALUE klass) -{ - struct rb_id_table *tbl = RCLASS_CVC_TBL(klass); - if (tbl) { - rb_id_table_foreach_values(tbl, mark_cvc_tbl_i, objspace); - } + return rb_find_object_id(rb_gc_get_objspace(), obj, rb_gc_impl_object_id); } static enum rb_id_table_iterator_result -update_const_table(VALUE value, void *data) +cc_table_memsize_i(VALUE ccs_ptr, void *data_ptr) { - rb_const_entry_t *ce = (rb_const_entry_t *)value; - rb_objspace_t * objspace = (rb_objspace_t *)data; - - if (gc_object_moved_p(objspace, ce->value)) { - ce->value = rb_gc_location(ce->value); - } - - if (gc_object_moved_p(objspace, ce->file)) { - ce->file = rb_gc_location(ce->file); - } - + size_t *total_size = data_ptr; + struct rb_class_cc_entries *ccs = (struct rb_class_cc_entries *)ccs_ptr; + *total_size += sizeof(*ccs); + *total_size += sizeof(ccs->entries[0]) * RUBY_ATOMIC_LOAD(ccs->capa); return ID_TABLE_CONTINUE; } -static void -update_const_tbl(rb_objspace_t *objspace, struct rb_id_table *tbl) -{ - if (!tbl) return; - rb_id_table_foreach_values(tbl, update_const_table, objspace); -} - -static void -update_subclass_entries(rb_objspace_t *objspace, rb_subclass_entry_t *entry) +static size_t +cc_table_memsize(struct rb_id_table *cc_table) { - while (entry) { - UPDATE_IF_MOVED(objspace, entry->klass); - entry = entry->next; - } + size_t total = rb_id_table_memsize(cc_table); + rb_id_table_foreach_values(cc_table, cc_table_memsize_i, &total); + return total; } -static void -update_class_ext(rb_objspace_t *objspace, rb_classext_t *ext) +size_t +rb_obj_memsize_of(VALUE obj) { - UPDATE_IF_MOVED(objspace, ext->origin_); - UPDATE_IF_MOVED(objspace, ext->includer); - UPDATE_IF_MOVED(objspace, ext->refined_class); - update_subclass_entries(objspace, ext->subclasses); -} + size_t size = 0; -static void -update_superclasses(rb_objspace_t *objspace, VALUE obj) -{ - if (FL_TEST_RAW(obj, RCLASS_SUPERCLASSES_INCLUDE_SELF)) { - for (size_t i = 0; i < RCLASS_SUPERCLASS_DEPTH(obj) + 1; i++) { - UPDATE_IF_MOVED(objspace, RCLASS_SUPERCLASSES(obj)[i]); - } + if (SPECIAL_CONST_P(obj)) { + return 0; } -} - -static void -gc_update_object_references(rb_objspace_t *objspace, VALUE obj) -{ - RVALUE *any = RANY(obj); - - gc_report(4, objspace, "update-refs: %p ->\n", (void *)obj); if (FL_TEST(obj, FL_EXIVAR)) { - rb_ref_update_generic_ivar(obj); + size += rb_generic_ivar_memsize(obj); } switch (BUILTIN_TYPE(obj)) { - case T_CLASS: - if (FL_TEST(obj, FL_SINGLETON)) { - UPDATE_IF_MOVED(objspace, RCLASS_ATTACHED_OBJECT(obj)); + case T_OBJECT: + if (rb_shape_obj_too_complex(obj)) { + size += rb_st_memsize(ROBJECT_IV_HASH(obj)); } - // Continue to the shared T_CLASS/T_MODULE + else if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) { + size += ROBJECT_IV_CAPACITY(obj) * sizeof(VALUE); + } + break; case T_MODULE: - if (RCLASS_SUPER((VALUE)obj)) { - UPDATE_IF_MOVED(objspace, RCLASS(obj)->super); + case T_CLASS: + if (RCLASS_M_TBL(obj)) { + size += rb_id_table_memsize(RCLASS_M_TBL(obj)); } - update_m_tbl(objspace, RCLASS_M_TBL(obj)); - update_cc_tbl(objspace, obj); - update_cvc_tbl(objspace, obj); - update_superclasses(objspace, obj); - - if (rb_shape_obj_too_complex(obj)) { - gc_ref_update_table_values_only(objspace, RCLASS_IV_HASH(obj)); + // class IV sizes are allocated as powers of two + size += SIZEOF_VALUE << bit_length(RCLASS_IV_COUNT(obj)); + if (RCLASS_CVC_TBL(obj)) { + size += rb_id_table_memsize(RCLASS_CVC_TBL(obj)); } - else { - for (attr_index_t i = 0; i < RCLASS_IV_COUNT(obj); i++) { - UPDATE_IF_MOVED(objspace, RCLASS_IVPTR(obj)[i]); - } + if (RCLASS_EXT(obj)->const_tbl) { + size += rb_id_table_memsize(RCLASS_EXT(obj)->const_tbl); + } + if (RCLASS_CC_TBL(obj)) { + size += cc_table_memsize(RCLASS_CC_TBL(obj)); + } + if (FL_TEST_RAW(obj, RCLASS_SUPERCLASSES_INCLUDE_SELF)) { + size += (RCLASS_SUPERCLASS_DEPTH(obj) + 1) * sizeof(VALUE); } - - update_class_ext(objspace, RCLASS_EXT(obj)); - update_const_tbl(objspace, RCLASS_CONST_TBL(obj)); - - UPDATE_IF_MOVED(objspace, RCLASS_EXT(obj)->classpath); break; - case T_ICLASS: if (RICLASS_OWNS_M_TBL_P(obj)) { - update_m_tbl(objspace, RCLASS_M_TBL(obj)); + if (RCLASS_M_TBL(obj)) { + size += rb_id_table_memsize(RCLASS_M_TBL(obj)); + } } - if (RCLASS_SUPER((VALUE)obj)) { - UPDATE_IF_MOVED(objspace, RCLASS(obj)->super); + if (RCLASS_CC_TBL(obj)) { + size += cc_table_memsize(RCLASS_CC_TBL(obj)); } - update_class_ext(objspace, RCLASS_EXT(obj)); - update_m_tbl(objspace, RCLASS_CALLABLE_M_TBL(obj)); - update_cc_tbl(objspace, obj); break; - - case T_IMEMO: - rb_imemo_mark_and_move(obj, true); - return; - - case T_NIL: - case T_FIXNUM: - case T_NODE: - case T_MOVED: - case T_NONE: - /* These can't move */ - return; - + case T_STRING: + size += rb_str_memsize(obj); + break; case T_ARRAY: - gc_ref_update_array(objspace, obj); + size += rb_ary_memsize(obj); break; - case T_HASH: - gc_ref_update_hash(objspace, obj); - UPDATE_IF_MOVED(objspace, any->as.hash.ifnone); + if (RHASH_ST_TABLE_P(obj)) { + VM_ASSERT(RHASH_ST_TABLE(obj) != NULL); + /* st_table is in the slot */ + size += st_memsize(RHASH_ST_TABLE(obj)) - sizeof(st_table); + } break; - - case T_STRING: - { - if (STR_SHARED_P(obj)) { - UPDATE_IF_MOVED(objspace, any->as.string.as.heap.aux.shared); - } - - /* If, after move the string is not embedded, and can fit in the - * slot it's been placed in, then re-embed it. */ - if (rb_gc_obj_slot_size(obj) >= rb_str_size_as_embedded(obj)) { - if (!STR_EMBED_P(obj) && rb_str_reembeddable_p(obj)) { - rb_str_make_embedded(obj); - } - } - - break; + case T_REGEXP: + if (RREGEXP_PTR(obj)) { + size += onig_memsize(RREGEXP_PTR(obj)); } + break; case T_DATA: - /* Call the compaction callback, if it exists */ + size += rb_objspace_data_type_memsize(obj); + break; + case T_MATCH: { - void *const ptr = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj); - if (ptr) { - if (RTYPEDDATA_P(obj) && gc_declarative_marking_p(any->as.typeddata.type)) { - size_t *offset_list = (size_t *)RANY(obj)->as.typeddata.type->function.dmark; - - for (size_t offset = *offset_list; offset != RUBY_REF_END; offset = *offset_list++) { - VALUE *ref = (VALUE *)((char *)ptr + offset); - if (SPECIAL_CONST_P(*ref)) continue; - *ref = rb_gc_location(*ref); - } - } - else if (RTYPEDDATA_P(obj)) { - RUBY_DATA_FUNC compact_func = any->as.typeddata.type->function.dcompact; - if (compact_func) (*compact_func)(ptr); - } - } + rb_matchext_t *rm = RMATCH_EXT(obj); + size += onig_region_memsize(&rm->regs); + size += sizeof(struct rmatch_offset) * rm->char_offset_num_allocated; } break; - - case T_OBJECT: - gc_ref_update_object(objspace, obj); - break; - case T_FILE: - if (any->as.file.fptr) { - UPDATE_IF_MOVED(objspace, any->as.file.fptr->self); - UPDATE_IF_MOVED(objspace, any->as.file.fptr->pathv); - UPDATE_IF_MOVED(objspace, any->as.file.fptr->tied_io_for_writing); - UPDATE_IF_MOVED(objspace, any->as.file.fptr->writeconv_asciicompat); - UPDATE_IF_MOVED(objspace, any->as.file.fptr->writeconv_pre_ecopts); - UPDATE_IF_MOVED(objspace, any->as.file.fptr->encs.ecopts); - UPDATE_IF_MOVED(objspace, any->as.file.fptr->write_lock); + if (RFILE(obj)->fptr) { + size += rb_io_memsize(RFILE(obj)->fptr); } break; - case T_REGEXP: - UPDATE_IF_MOVED(objspace, any->as.regexp.src); + case T_RATIONAL: + case T_COMPLEX: break; - - case T_SYMBOL: - UPDATE_IF_MOVED(objspace, RSYMBOL(any)->fstr); + case T_IMEMO: + size += rb_imemo_memsize(obj); break; case T_FLOAT: - case T_BIGNUM: + case T_SYMBOL: break; - case T_MATCH: - UPDATE_IF_MOVED(objspace, any->as.match.regexp); - - if (any->as.match.str) { - UPDATE_IF_MOVED(objspace, any->as.match.str); + case T_BIGNUM: + if (!(RBASIC(obj)->flags & BIGNUM_EMBED_FLAG) && BIGNUM_DIGITS(obj)) { + size += BIGNUM_LEN(obj) * sizeof(BDIGIT); } break; - case T_RATIONAL: - UPDATE_IF_MOVED(objspace, any->as.rational.num); - UPDATE_IF_MOVED(objspace, any->as.rational.den); - break; - - case T_COMPLEX: - UPDATE_IF_MOVED(objspace, any->as.complex.real); - UPDATE_IF_MOVED(objspace, any->as.complex.imag); - + case T_NODE: + UNEXPECTED_NODE(obj_memsize_of); break; case T_STRUCT: - { - long i, len = RSTRUCT_LEN(obj); - VALUE *ptr = (VALUE *)RSTRUCT_CONST_PTR(obj); - - for (i = 0; i < len; i++) { - UPDATE_IF_MOVED(objspace, ptr[i]); - } + if ((RBASIC(obj)->flags & RSTRUCT_EMBED_LEN_MASK) == 0 && + RSTRUCT(obj)->as.heap.ptr) { + size += sizeof(VALUE) * RSTRUCT_LEN(obj); } break; - default: -#if GC_DEBUG - rb_gcdebug_print_obj_condition((VALUE)obj); - rb_obj_info_dump(obj); - rb_bug("unreachable"); -#endif + + case T_ZOMBIE: + case T_MOVED: break; + default: + rb_bug("objspace/memsize_of(): unknown data type 0x%x(%p)", + BUILTIN_TYPE(obj), (void*)obj); } - UPDATE_IF_MOVED(objspace, RBASIC(obj)->klass); - - gc_report(4, objspace, "update-refs: %p <-\n", (void *)obj); + return size + rb_gc_obj_slot_size(obj); } static int -gc_ref_update(void *vstart, void *vend, size_t stride, rb_objspace_t * objspace, struct heap_page *page) -{ - VALUE v = (VALUE)vstart; - asan_unlock_freelist(page); - asan_lock_freelist(page); - page->flags.has_uncollectible_wb_unprotected_objects = FALSE; - page->flags.has_remembered_objects = FALSE; - - /* For each object on the page */ - for (; v != (VALUE)vend; v += stride) { - void *poisoned = asan_unpoison_object_temporary(v); - - switch (BUILTIN_TYPE(v)) { - case T_NONE: - case T_MOVED: - case T_ZOMBIE: - break; - default: - if (RVALUE_WB_UNPROTECTED(v)) { - page->flags.has_uncollectible_wb_unprotected_objects = TRUE; - } - if (RVALUE_REMEMBERED(v)) { - page->flags.has_remembered_objects = TRUE; - } - if (page->flags.before_sweep) { - if (RVALUE_MARKED(v)) { - gc_update_object_references(objspace, v); - } - } - else { - gc_update_object_references(objspace, v); - } - } - - if (poisoned) { - asan_poison_object(v); - } - } - - return 0; -} - -extern rb_symbols_t ruby_global_symbols; - -static void -gc_update_references(rb_objspace_t *objspace) +set_zero(st_data_t key, st_data_t val, st_data_t arg) { - objspace->flags.during_reference_updating = true; - - rb_execution_context_t *ec = GET_EC(); - rb_vm_t *vm = rb_ec_vm_ptr(ec); - - struct heap_page *page = NULL; - - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - bool should_set_mark_bits = TRUE; - rb_size_pool_t *size_pool = &size_pools[i]; - rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); - - ccan_list_for_each(&heap->pages, page, page_node) { - uintptr_t start = (uintptr_t)page->start; - uintptr_t end = start + (page->total_slots * size_pool->slot_size); - - gc_ref_update((void *)start, (void *)end, size_pool->slot_size, objspace, page); - if (page == heap->sweeping_page) { - should_set_mark_bits = FALSE; - } - if (should_set_mark_bits) { - gc_setup_mark_bits(page); - } - } - } - if (objspace == vm->objspace) { - rb_vm_update_references(vm); - rb_gc_update_global_tbl(); - } - rb_ractor_update_references(objspace->local_data.ractor); - - GLOBAL_SYMBOLS_ENTER(global_symbols); - { - global_symbols->ids = rb_gc_location(global_symbols->ids); - global_symbols->dsymbol_fstr_hash = rb_gc_location(global_symbols->dsymbol_fstr_hash); - gc_update_table_refs(objspace, global_symbols->str_sym); - } - GLOBAL_SYMBOLS_LEAVE(global_symbols); - - rb_native_mutex_lock(&objspace->local_data.obj_id_lock); - gc_ref_update_table_values_only(objspace, objspace->local_data.obj_to_id_tbl); - gc_update_table_refs(objspace, objspace->local_data.id_to_obj_tbl); - rb_native_mutex_unlock(&objspace->local_data.obj_id_lock); - gc_update_table_refs(objspace, finalizer_table); - - objspace->flags.during_reference_updating = false; + VALUE k = (VALUE)key; + VALUE hash = (VALUE)arg; + rb_hash_aset(hash, k, INT2FIX(0)); + return ST_CONTINUE; } -#if GC_CAN_COMPILE_COMPACTION -/* - * call-seq: - * GC.latest_compact_info -> hash - * - * Returns information about object moved in the most recent \GC compaction. - * - * The returned +hash+ contains the following keys: - * - * [considered] - * Hash containing the type of the object as the key and the number of - * objects of that type that were considered for movement. - * [moved] - * Hash containing the type of the object as the key and the number of - * objects of that type that were actually moved. - * [moved_up] - * Hash containing the type of the object as the key and the number of - * objects of that type that were increased in size. - * [moved_down] - * Hash containing the type of the object as the key and the number of - * objects of that type that were decreased in size. - * - * Some objects can't be moved (due to pinning) so these numbers can be used to - * calculate compaction efficiency. - */ static VALUE -gc_compact_stats(VALUE self) -{ - size_t i; - rb_objspace_t *objspace = &rb_objspace; - VALUE h = rb_hash_new(); - VALUE considered = rb_hash_new(); - VALUE moved = rb_hash_new(); - VALUE moved_up = rb_hash_new(); - VALUE moved_down = rb_hash_new(); - - for (i=0; ircompactor.considered_count_table[i]) { - rb_hash_aset(considered, type_sym(i), SIZET2NUM(objspace->rcompactor.considered_count_table[i])); - } - - if (objspace->rcompactor.moved_count_table[i]) { - rb_hash_aset(moved, type_sym(i), SIZET2NUM(objspace->rcompactor.moved_count_table[i])); - } - - if (objspace->rcompactor.moved_up_count_table[i]) { - rb_hash_aset(moved_up, type_sym(i), SIZET2NUM(objspace->rcompactor.moved_up_count_table[i])); - } - - if (objspace->rcompactor.moved_down_count_table[i]) { - rb_hash_aset(moved_down, type_sym(i), SIZET2NUM(objspace->rcompactor.moved_down_count_table[i])); - } - } - - rb_hash_aset(h, ID2SYM(rb_intern("considered")), considered); - rb_hash_aset(h, ID2SYM(rb_intern("moved")), moved); - rb_hash_aset(h, ID2SYM(rb_intern("moved_up")), moved_up); - rb_hash_aset(h, ID2SYM(rb_intern("moved_down")), moved_down); - - return h; -} -#else -# define gc_compact_stats rb_f_notimplement -#endif - -#if GC_CAN_COMPILE_COMPACTION -static void -root_obj_check_moved_i(const char *category, VALUE obj, void *data) +type_sym(size_t type) { - rb_objspace_t *objspace = data; - - if (gc_object_moved_p(objspace, obj)) { - rb_bug("ROOT %s points to MOVED: %p -> %s", category, (void *)obj, obj_info(rb_gc_location(obj))); + switch (type) { +#define COUNT_TYPE(t) case (t): return ID2SYM(rb_intern(#t)); break; + COUNT_TYPE(T_NONE); + COUNT_TYPE(T_OBJECT); + COUNT_TYPE(T_CLASS); + COUNT_TYPE(T_MODULE); + COUNT_TYPE(T_FLOAT); + COUNT_TYPE(T_STRING); + COUNT_TYPE(T_REGEXP); + COUNT_TYPE(T_ARRAY); + COUNT_TYPE(T_HASH); + COUNT_TYPE(T_STRUCT); + COUNT_TYPE(T_BIGNUM); + COUNT_TYPE(T_FILE); + COUNT_TYPE(T_DATA); + COUNT_TYPE(T_MATCH); + COUNT_TYPE(T_COMPLEX); + COUNT_TYPE(T_RATIONAL); + COUNT_TYPE(T_NIL); + COUNT_TYPE(T_TRUE); + COUNT_TYPE(T_FALSE); + COUNT_TYPE(T_SYMBOL); + COUNT_TYPE(T_FIXNUM); + COUNT_TYPE(T_IMEMO); + COUNT_TYPE(T_UNDEF); + COUNT_TYPE(T_NODE); + COUNT_TYPE(T_ICLASS); + COUNT_TYPE(T_ZOMBIE); + COUNT_TYPE(T_MOVED); +#undef COUNT_TYPE + default: return SIZET2NUM(type); break; } } -static void -reachable_object_check_moved_i(VALUE ref, void *data) -{ - VALUE parent = (VALUE)data; - if (gc_object_moved_p(&rb_objspace, ref)) { - rb_bug("Object %s points to MOVED: %p -> %s", obj_info(parent), (void *)ref, obj_info(rb_gc_location(ref))); - } -} +struct count_objects_data { + size_t counts[T_MASK+1]; + size_t freed; + size_t total; +}; -static int -heap_check_moved_i(void *vstart, void *vend, size_t stride, void *data) +static void +count_objects_i(VALUE obj, void *d) { - rb_objspace_t *objspace = data; - - VALUE v = (VALUE)vstart; - for (; v != (VALUE)vend; v += stride) { - if (gc_object_moved_p(objspace, v)) { - /* Moved object still on the heap, something may have a reference. */ - } - else { - void *poisoned = asan_unpoison_object_temporary(v); - - switch (BUILTIN_TYPE(v)) { - case T_NONE: - case T_ZOMBIE: - break; - default: - if (!rb_objspace_garbage_object_p(v)) { - rb_objspace_reachable_objects_from(v, reachable_object_check_moved_i, (void *)v); - } - } + struct count_objects_data *data = (struct count_objects_data *)d; - if (poisoned) { - GC_ASSERT(BUILTIN_TYPE(v) == T_NONE); - asan_poison_object(v); - } - } + if (RBASIC(obj)->flags) { + data->counts[BUILTIN_TYPE(obj)]++; + } + else { + data->freed++; } - return 0; + data->total++; } /* * call-seq: - * GC.compact -> hash + * ObjectSpace.count_objects([result_hash]) -> hash + * + * Counts all objects grouped by type. + * + * It returns a hash, such as: + * { + * :TOTAL=>10000, + * :FREE=>3011, + * :T_OBJECT=>6, + * :T_CLASS=>404, + * # ... + * } + * + * The contents of the returned hash are implementation specific. + * It may be changed in future. * - * This function compacts objects together in Ruby's heap. It eliminates - * unused space (or fragmentation) in the heap by moving objects in to that - * unused space. + * The keys starting with +:T_+ means live objects. + * For example, +:T_ARRAY+ is the number of arrays. + * +:FREE+ means object slots which is not used now. + * +:TOTAL+ means sum of above. * - * The returned +hash+ contains statistics about the objects that were moved; - * see GC.latest_compact_info. + * If the optional argument +result_hash+ is given, + * it is overwritten and returned. This is intended to avoid probe effect. * - * This method is only expected to work on CRuby. + * h = {} + * ObjectSpace.count_objects(h) + * puts h + * # => { :TOTAL=>10000, :T_CLASS=>158280, :T_MODULE=>20672, :T_STRING=>527249 } * - * To test whether \GC compaction is supported, use the idiom: + * This method is only expected to work on C Ruby. * - * GC.respond_to?(:compact) */ + static VALUE -gc_compact(VALUE self) +count_objects(int argc, VALUE *argv, VALUE os) { - /* Run GC with compaction enabled */ - gc_start_internal(NULL, self, Qtrue, Qtrue, Qtrue, Qfalse, Qtrue); - - return gc_compact_stats(self); -} -#else -# define gc_compact rb_f_notimplement -#endif - -#if GC_CAN_COMPILE_COMPACTION - -struct desired_compaction_pages_i_data { - rb_objspace_t *objspace; - size_t required_slots[SIZE_POOL_COUNT]; -}; + struct count_objects_data data = { 0 }; + VALUE hash = Qnil; -static int -desired_compaction_pages_i(struct heap_page *page, void *data) -{ - struct desired_compaction_pages_i_data *tdata = data; - rb_objspace_t *objspace = tdata->objspace; - VALUE vstart = (VALUE)page->start; - VALUE vend = vstart + (VALUE)(page->total_slots * page->size_pool->slot_size); + if (rb_check_arity(argc, 0, 1) == 1) { + hash = argv[0]; + if (!RB_TYPE_P(hash, T_HASH)) + rb_raise(rb_eTypeError, "non-hash given"); + } + rb_gc_impl_each_object(rb_gc_get_objspace(), count_objects_i, &data); - for (VALUE v = vstart; v != vend; v += page->size_pool->slot_size) { - /* skip T_NONEs; they won't be moved */ - void *poisoned = asan_unpoison_object_temporary(v); - if (BUILTIN_TYPE(v) == T_NONE) { - if (poisoned) { - asan_poison_object(v); - } - continue; - } + if (NIL_P(hash)) { + hash = rb_hash_new(); + } + else if (!RHASH_EMPTY_P(hash)) { + rb_hash_stlike_foreach(hash, set_zero, hash); + } + rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(data.total)); + rb_hash_aset(hash, ID2SYM(rb_intern("FREE")), SIZET2NUM(data.freed)); - rb_size_pool_t *dest_pool = gc_compact_destination_pool(objspace, page->size_pool, v); - size_t dest_pool_idx = dest_pool - size_pools; - tdata->required_slots[dest_pool_idx]++; + for (size_t i = 0; i <= T_MASK; i++) { + VALUE type = type_sym(i); + if (data.counts[i]) + rb_hash_aset(hash, type, SIZET2NUM(data.counts[i])); } - return 0; + return hash; } -static VALUE -gc_verify_compaction_references(rb_execution_context_t *ec, VALUE self, VALUE double_heap, VALUE expand_heap, VALUE toward_empty) -{ - rb_objspace_t *objspace = &rb_objspace; - - /* Clear the heap. */ - gc_start_internal(NULL, self, Qtrue, Qtrue, Qtrue, Qfalse, Qfalse); +#define SET_STACK_END SET_MACHINE_STACK_END(&ec->machine.stack_end) - if (RTEST(double_heap)) { - rb_warn("double_heap is deprecated, please use expand_heap instead"); - } +#define STACK_START (ec->machine.stack_start) +#define STACK_END (ec->machine.stack_end) +#define STACK_LEVEL_MAX (ec->machine.stack_maxsize/sizeof(VALUE)) - HEAP_LOCK_ENTER(objspace); - { - gc_rest(objspace); - - /* if both double_heap and expand_heap are set, expand_heap takes precedence */ - if (RTEST(expand_heap)) { - struct desired_compaction_pages_i_data desired_compaction = { - .objspace = objspace, - .required_slots = {0}, - }; - /* Work out how many objects want to be in each size pool, taking account of moves */ - objspace_each_pages(objspace, desired_compaction_pages_i, &desired_compaction, TRUE); - - /* Find out which pool has the most pages */ - size_t max_existing_pages = 0; - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); - max_existing_pages = MAX(max_existing_pages, heap->total_pages); - } - /* Add pages to each size pool so that compaction is guaranteed to move every object */ - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); - - size_t pages_to_add = 0; - /* - * Step 1: Make sure every pool has the same number of pages, by adding empty pages - * to smaller pools. This is required to make sure the compact cursor can advance - * through all of the pools in `gc_sweep_compact` without hitting the "sweep & - * compact cursors met" condition on some pools before fully compacting others - */ - pages_to_add += max_existing_pages - heap->total_pages; - /* - * Step 2: Now add additional free pages to each size pool sufficient to hold all objects - * that want to be in that size pool, whether moved into it or moved within it - */ - pages_to_add += slots_to_pages_for_size_pool(objspace, size_pool, desired_compaction.required_slots[i]); - /* - * Step 3: Add two more pages so that the compact & sweep cursors will meet _after_ all objects - * have been moved, and not on the last iteration of the `gc_sweep_compact` loop - */ - pages_to_add += 2; - - heap_add_pages(objspace, size_pool, heap, pages_to_add); - } - } - else if (RTEST(double_heap)) { - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); - heap_add_pages(objspace, size_pool, heap, heap->total_pages); - } +#if STACK_GROW_DIRECTION < 0 +# define STACK_LENGTH (size_t)(STACK_START - STACK_END) +#elif STACK_GROW_DIRECTION > 0 +# define STACK_LENGTH (size_t)(STACK_END - STACK_START + 1) +#else +# define STACK_LENGTH ((STACK_END < STACK_START) ? (size_t)(STACK_START - STACK_END) \ + : (size_t)(STACK_END - STACK_START + 1)) +#endif +#if !STACK_GROW_DIRECTION +int ruby_stack_grow_direction; +int +ruby_get_stack_grow_direction(volatile VALUE *addr) +{ + VALUE *end; + SET_MACHINE_STACK_END(&end); - } + if (end > addr) return ruby_stack_grow_direction = 1; + return ruby_stack_grow_direction = -1; +} +#endif - if (RTEST(toward_empty)) { - objspace->rcompactor.compare_func = compare_free_slots; - } - } - HEAP_LOCK_LEAVE(objspace); +size_t +ruby_stack_length(VALUE **p) +{ + rb_execution_context_t *ec = GET_EC(); + SET_STACK_END; + if (p) *p = STACK_UPPER(STACK_END, STACK_START, STACK_END); + return STACK_LENGTH; +} - gc_start_internal(NULL, self, Qtrue, Qtrue, Qtrue, Qfalse, Qtrue); +#define PREVENT_STACK_OVERFLOW 1 +#ifndef PREVENT_STACK_OVERFLOW +#if !(defined(POSIX_SIGNAL) && defined(SIGSEGV) && defined(HAVE_SIGALTSTACK)) +# define PREVENT_STACK_OVERFLOW 1 +#else +# define PREVENT_STACK_OVERFLOW 0 +#endif +#endif +#if PREVENT_STACK_OVERFLOW && !defined(__EMSCRIPTEN__) +static int +stack_check(rb_execution_context_t *ec, int water_mark) +{ + SET_STACK_END; - objspace_reachable_objects_from_root(objspace, root_obj_check_moved_i, objspace); - objspace_each_objects(objspace, heap_check_moved_i, objspace, TRUE); + size_t length = STACK_LENGTH; + size_t maximum_length = STACK_LEVEL_MAX - water_mark; - objspace->rcompactor.compare_func = NULL; - return gc_compact_stats(self); + return length > maximum_length; } #else -# define gc_verify_compaction_references (rb_builtin_arity3_function_type)rb_f_notimplement +#define stack_check(ec, water_mark) FALSE #endif -VALUE -rb_gc_start(void) +#define STACKFRAME_FOR_CALL_CFUNC 2048 + +int +rb_ec_stack_check(rb_execution_context_t *ec) { - rb_gc(); - return Qnil; + return stack_check(ec, STACKFRAME_FOR_CALL_CFUNC); } -VALUE -rb_gc_ractor_teardown_cleanup(void) +int +ruby_stack_check(void) { - rb_ractor_t *cr = GET_RACTOR(); - rb_objspace_t *objspace = cr->local_objspace; - if (!GET_VM()->gc_deactivated) { - cr->during_teardown_cleanup = true; - unsigned int reason = GPR_DEFAULT_REASON; - garbage_collect(objspace, reason, true); - cr->during_teardown_cleanup = false; - } - - VM_ASSERT(heap_pages_deferred_final == NULL); - return Qnil; + return stack_check(GET_EC(), STACKFRAME_FOR_CALL_CFUNC); } -void -rb_gc(void) +ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(static void each_location(void *objspace, register const VALUE *x, register long n, void (*cb)(void *objspace, VALUE))); +static void +each_location(void *objspace, register const VALUE *x, register long n, void (*cb)(void *objspace, VALUE)) { - unless_objspace(objspace) { return; } - unsigned int reason = GPR_DEFAULT_REASON; - garbage_collect(objspace, reason, false); + VALUE v; + while (n--) { + v = *x; + cb(objspace, v); + x++; + } } -int -rb_during_gc(void) +static void +gc_mark_locations(void *objspace, const VALUE *start, const VALUE *end, void (*cb)(void *, VALUE)) { - unless_objspace(objspace) { return FALSE; } - return during_gc; + long n; + + if (end <= start) return; + n = end - start; + each_location(objspace, start, n, cb); } -int -rb_during_local_gc(void) +void +rb_gc_mark_locations(const VALUE *start, const VALUE *end) { - unless_objspace(objspace) { return FALSE; } - return during_gc && !objspace->flags.during_global_gc; + gc_mark_locations(&rb_objspace, start, end, rb_gc_impl_stack_location_mark_maybe); } -int -rb_during_global_gc(void) +void +rb_gc_mark_values(long n, const VALUE *values) { - unless_objspace(objspace) { return FALSE; } - return during_gc && objspace->flags.during_global_gc; + for (long i = 0; i < n; i++) { + rb_gc_impl_mark(rb_gc_get_objspace(), values[i]); + } } -#if RGENGC_PROFILE >= 2 - -static const char *type_name(int type, VALUE obj); - -static void -gc_count_add_each_types(VALUE hash, const char *name, const size_t *types) +void +rb_gc_mark_vm_stack_values(long n, const VALUE *values) { - VALUE result = rb_hash_new_with_size(T_MASK); - int i; - for (i=0; iprofile.latest_gc_info; + void *objspace = (void *)data; - if (SYMBOL_P(hash_or_key)) { - key = hash_or_key; - } - else if (RB_TYPE_P(hash_or_key, T_HASH)) { - hash = hash_or_key; - } - else { - rb_raise(rb_eTypeError, "non-hash or symbol given"); - } + rb_gc_impl_mark(objspace, (VALUE)key); + rb_gc_impl_mark(objspace, (VALUE)value); - if (NIL_P(sym_major_by)) { -#define S(s) sym_##s = ID2SYM(rb_intern_const(#s)) - S(major_by); - S(gc_by); - S(immediate_sweep); - S(have_finalizer); - S(state); - S(need_major_by); - - S(stress); - S(nofree); - S(oldgen); - S(shady); - S(force); -#if RGENGC_ESTIMATE_OLDMALLOC - S(oldmalloc); -#endif - S(newobj); - S(malloc); - S(method); - S(capi); - - S(none); - S(marking); - S(sweeping); - - S(weak_references_count); - S(retained_weak_references_count); -#undef S - } - -#define SET(name, attr) \ - if (key == sym_##name) \ - return (attr); \ - else if (hash != Qnil) \ - rb_hash_aset(hash, sym_##name, (attr)); - - major_by = - (flags & GPR_FLAG_MAJOR_BY_NOFREE) ? sym_nofree : - (flags & GPR_FLAG_MAJOR_BY_OLDGEN) ? sym_oldgen : - (flags & GPR_FLAG_MAJOR_BY_SHADY) ? sym_shady : - (flags & GPR_FLAG_MAJOR_BY_FORCE) ? sym_force : - (flags & GPR_FLAG_MAJOR_BY_ABSORB) ? sym_absorb : -#if RGENGC_ESTIMATE_OLDMALLOC - (flags & GPR_FLAG_MAJOR_BY_OLDMALLOC) ? sym_oldmalloc : -#endif - Qnil; - SET(major_by, major_by); - - if (orig_flags == 0) { /* set need_major_by only if flags not set explicitly */ - 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 : - (need_major_flags & GPR_FLAG_MAJOR_BY_SHADY) ? sym_shady : - (need_major_flags & GPR_FLAG_MAJOR_BY_FORCE) ? sym_force : - (need_major_flags & GPR_FLAG_MAJOR_BY_ABSORB) ? sym_absorb : -#if RGENGC_ESTIMATE_OLDMALLOC - (need_major_flags & GPR_FLAG_MAJOR_BY_OLDMALLOC) ? sym_oldmalloc : -#endif - Qnil; - SET(need_major_by, need_major_by); - } + return ST_CONTINUE; +} - SET(gc_by, - (flags & GPR_FLAG_NEWOBJ) ? sym_newobj : - (flags & GPR_FLAG_MALLOC) ? sym_malloc : - (flags & GPR_FLAG_METHOD) ? sym_method : - (flags & GPR_FLAG_CAPI) ? sym_capi : - (flags & GPR_FLAG_STRESS) ? sym_stress : - Qnil - ); +static int +pin_key_pin_value(st_data_t key, st_data_t value, st_data_t data) +{ + void *objspace = (void *)data; - SET(have_finalizer, RBOOL(flags & GPR_FLAG_HAVE_FINALIZE)); - SET(immediate_sweep, RBOOL(flags & GPR_FLAG_IMMEDIATE_SWEEP)); + rb_gc_impl_mark_and_pin(objspace, (VALUE)key); + rb_gc_impl_mark_and_pin(objspace, (VALUE)value); - if (orig_flags == 0) { - SET(state, gc_mode(objspace) == gc_mode_none ? sym_none : - gc_mode(objspace) == gc_mode_marking ? sym_marking : sym_sweeping); - } + return ST_CONTINUE; +} - SET(weak_references_count, LONG2FIX(objspace->profile.weak_references_count)); - SET(retained_weak_references_count, LONG2FIX(objspace->profile.retained_weak_references_count)); -#undef SET +static int +pin_key_mark_value(st_data_t key, st_data_t value, st_data_t data) +{ + void *objspace = (void *)data; + + rb_gc_impl_mark_and_pin(objspace, (VALUE)key); + rb_gc_impl_mark(objspace, (VALUE)value); - if (!NIL_P(key)) {/* matched key should return above */ - rb_raise(rb_eArgError, "unknown key: %"PRIsVALUE, rb_sym2str(key)); + return ST_CONTINUE; +} + +static void +mark_hash(void *objspace, VALUE hash) +{ + if (rb_hash_compare_by_id_p(hash)) { + rb_hash_stlike_foreach(hash, pin_key_mark_value, (st_data_t)objspace); + } + else { + rb_hash_stlike_foreach(hash, mark_keyvalue, (st_data_t)objspace); } - return hash; + rb_gc_impl_mark(objspace, RHASH(hash)->ifnone); } VALUE -rb_gc_latest_gc_info(VALUE key) +rb_gc_ractor_teardown_cleanup(void) { - rb_objspace_t *objspace = &rb_objspace; - return gc_info_decode(objspace, key, 0); + rb_ractor_t *cr = GET_RACTOR(); + rb_objspace_t *objspace = cr->local_objspace; + if (!GET_VM()->gc_deactivated) { + cr->during_teardown_cleanup = true; + unsigned int reason = GPR_DEFAULT_REASON; + garbage_collect(objspace, reason, true); + cr->during_teardown_cleanup = false; + } + + VM_ASSERT(heap_pages_deferred_final == NULL); + return Qnil; } -static VALUE -gc_latest_gc_info(rb_execution_context_t *ec, VALUE self, VALUE arg) +void +rb_mark_hash(st_table *tbl) { - rb_objspace_t *objspace = &rb_objspace; + if (!tbl) return; - if (NIL_P(arg)) { - arg = rb_hash_new(); - } - else if (!SYMBOL_P(arg) && !RB_TYPE_P(arg, T_HASH)) { - rb_raise(rb_eTypeError, "non-hash or symbol given"); - } + st_foreach(tbl, pin_key_pin_value, (st_data_t)rb_gc_get_objspace()); +} - return gc_info_decode(objspace, arg, 0); -} - -enum gc_stat_sym { - gc_stat_sym_count, - gc_stat_sym_time, - gc_stat_sym_marking_time, - gc_stat_sym_sweeping_time, - gc_stat_sym_heap_allocated_pages, - gc_stat_sym_heap_sorted_length, - gc_stat_sym_heap_allocatable_pages, - gc_stat_sym_heap_available_slots, - gc_stat_sym_heap_live_slots, - gc_stat_sym_heap_free_slots, - gc_stat_sym_heap_final_slots, - gc_stat_sym_heap_marked_slots, - gc_stat_sym_heap_eden_pages, - gc_stat_sym_heap_tomb_pages, - gc_stat_sym_total_allocated_pages, - gc_stat_sym_total_freed_pages, - gc_stat_sym_total_allocated_objects, - gc_stat_sym_total_freed_objects, - gc_stat_sym_malloc_increase_bytes, - gc_stat_sym_malloc_increase_bytes_limit, - gc_stat_sym_minor_gc_count, - gc_stat_sym_major_gc_count, - gc_stat_sym_compact_count, - gc_stat_sym_read_barrier_faults, - gc_stat_sym_total_moved_objects, - gc_stat_sym_remembered_wb_unprotected_objects, - gc_stat_sym_remembered_wb_unprotected_objects_limit, - gc_stat_sym_old_objects, - gc_stat_sym_old_objects_limit, - gc_stat_sym_shared_objects, - gc_stat_sym_shared_objects_limit, -#if RGENGC_ESTIMATE_OLDMALLOC - gc_stat_sym_oldmalloc_increase_bytes, - gc_stat_sym_oldmalloc_increase_bytes_limit, -#endif - gc_stat_sym_weak_references_count, -#if RGENGC_PROFILE - gc_stat_sym_total_generated_normal_object_count, - gc_stat_sym_total_generated_shady_object_count, - gc_stat_sym_total_shade_operation_count, - gc_stat_sym_total_promoted_count, - gc_stat_sym_total_remembered_normal_object_count, - gc_stat_sym_total_remembered_shady_object_count, -#endif - gc_stat_sym_last -}; +static enum rb_id_table_iterator_result +mark_method_entry_i(VALUE me, void *objspace) +{ + rb_gc_impl_mark(objspace, me); -static VALUE gc_stat_symbols[gc_stat_sym_last]; + return ID_TABLE_CONTINUE; +} static void -setup_gc_stat_symbols(void) -{ - if (gc_stat_symbols[0] == 0) { -#define S(s) gc_stat_symbols[gc_stat_sym_##s] = ID2SYM(rb_intern_const(#s)) - S(count); - S(time); - S(marking_time), - S(sweeping_time), - S(heap_allocated_pages); - S(heap_sorted_length); - S(heap_allocatable_pages); - S(heap_available_slots); - S(heap_live_slots); - S(heap_free_slots); - S(heap_final_slots); - S(heap_marked_slots); - S(heap_eden_pages); - S(heap_tomb_pages); - S(total_allocated_pages); - S(total_freed_pages); - S(total_allocated_objects); - S(total_freed_objects); - S(malloc_increase_bytes); - S(malloc_increase_bytes_limit); - S(minor_gc_count); - S(major_gc_count); - S(compact_count); - S(read_barrier_faults); - S(total_moved_objects); - S(remembered_wb_unprotected_objects); - S(remembered_wb_unprotected_objects_limit); - S(old_objects); - S(old_objects_limit); - if (rb_multi_ractor_p()) { - S(shared_objects); - S(shared_objects_limit); - } -#if RGENGC_ESTIMATE_OLDMALLOC - S(oldmalloc_increase_bytes); - S(oldmalloc_increase_bytes_limit); -#endif - S(weak_references_count); -#if RGENGC_PROFILE - S(total_generated_normal_object_count); - S(total_generated_shady_object_count); - S(total_shade_operation_count); - S(total_promoted_count); - S(total_remembered_normal_object_count); - S(total_remembered_shady_object_count); -#endif /* RGENGC_PROFILE */ -#undef S +mark_m_tbl(void *objspace, struct rb_id_table *tbl) +{ + if (tbl) { + rb_id_table_foreach_values(tbl, mark_method_entry_i, objspace); } } -static uint64_t -ns_to_ms(uint64_t ns) -{ - return ns / (1000 * 1000); -} +#if STACK_GROW_DIRECTION < 0 +#define GET_STACK_BOUNDS(start, end, appendix) ((start) = STACK_END, (end) = STACK_START) +#elif STACK_GROW_DIRECTION > 0 +#define GET_STACK_BOUNDS(start, end, appendix) ((start) = STACK_START, (end) = STACK_END+(appendix)) +#else +#define GET_STACK_BOUNDS(start, end, appendix) \ + ((STACK_END < STACK_START) ? \ + ((start) = STACK_END, (end) = STACK_START) : ((start) = STACK_START, (end) = STACK_END+(appendix))) +#endif -static size_t -gc_stat_internal(VALUE hash_or_sym) +static void +each_stack_location(void *objspace, const rb_execution_context_t *ec, + const VALUE *stack_start, const VALUE *stack_end, void (*cb)(void *objspace, VALUE obj)) { - rb_objspace_t *objspace = &rb_objspace; - VALUE hash = Qnil, key = Qnil; - - setup_gc_stat_symbols(); + gc_mark_locations(objspace, stack_start, stack_end, cb); - if (RB_TYPE_P(hash_or_sym, T_HASH)) { - hash = hash_or_sym; - } - else if (SYMBOL_P(hash_or_sym)) { - key = hash_or_sym; - } - else { - rb_raise(rb_eTypeError, "non-hash or symbol argument"); - } - - rb_global_space_t *global_space = &rb_global_space; - rb_native_mutex_lock(&global_space->rglobalgc.shared_tracking_lock); - size_t shared_objects_total_value = global_space->rglobalgc.shared_objects_total; - size_t shared_objects_limit_value = global_space->rglobalgc.shared_objects_limit; - rb_native_mutex_unlock(&global_space->rglobalgc.shared_tracking_lock); - -#define SET(name, attr) \ - if (key == gc_stat_symbols[gc_stat_sym_##name]) \ - return attr; \ - else if (hash != Qnil) \ - rb_hash_aset(hash, gc_stat_symbols[gc_stat_sym_##name], SIZET2NUM(attr)); - - SET(count, objspace->profile.count); - SET(time, (size_t)ns_to_ms(objspace->profile.marking_time_ns + objspace->profile.sweeping_time_ns)); // TODO: UINT64T2NUM - SET(marking_time, (size_t)ns_to_ms(objspace->profile.marking_time_ns)); - SET(sweeping_time, (size_t)ns_to_ms(objspace->profile.sweeping_time_ns)); - - /* implementation dependent counters */ - SET(heap_allocated_pages, heap_allocated_pages); - SET(heap_sorted_length, heap_pages_sorted_length); - SET(heap_allocatable_pages, heap_allocatable_pages(objspace)); - SET(heap_available_slots, objspace_available_slots(objspace)); - SET(heap_live_slots, objspace_live_slots(objspace)); - SET(heap_free_slots, objspace_free_slots(objspace)); - SET(heap_final_slots, heap_pages_final_slots); - SET(heap_marked_slots, objspace->marked_slots); - SET(heap_eden_pages, heap_eden_total_pages(objspace)); - SET(heap_tomb_pages, heap_tomb_total_pages(objspace)); - SET(total_allocated_pages, total_allocated_pages(objspace)); - SET(total_freed_pages, total_freed_pages(objspace)); - SET(total_allocated_objects, total_allocated_objects(objspace)); - SET(total_freed_objects, total_freed_objects(objspace)); - SET(malloc_increase_bytes, malloc_increase); - SET(malloc_increase_bytes_limit, malloc_limit); - SET(minor_gc_count, objspace->profile.minor_gc_count); - SET(major_gc_count, objspace->profile.major_gc_count); - SET(compact_count, objspace->profile.compact_count); - SET(read_barrier_faults, objspace->profile.read_barrier_faults); - SET(total_moved_objects, objspace->rcompactor.total_moved); - SET(remembered_wb_unprotected_objects, objspace->rgengc.uncollectible_wb_unprotected_objects); - SET(remembered_wb_unprotected_objects_limit, objspace->rgengc.uncollectible_wb_unprotected_objects_limit); - SET(old_objects, objspace->rgengc.old_objects); - SET(old_objects_limit, objspace->rgengc.old_objects_limit); - if (rb_multi_ractor_p()) { - SET(shared_objects, shared_objects_total_value); - SET(shared_objects_limit, shared_objects_limit_value); - } -#if RGENGC_ESTIMATE_OLDMALLOC - SET(oldmalloc_increase_bytes, objspace->rgengc.oldmalloc_increase); - SET(oldmalloc_increase_bytes_limit, objspace->rgengc.oldmalloc_increase_limit); +#if defined(__mc68000__) + gc_mark_locations(objspace, + (VALUE*)((char*)stack_start + 2), + (VALUE*)((char*)stack_end - 2), cb); #endif +} -#if RGENGC_PROFILE - SET(total_generated_normal_object_count, objspace->profile.total_generated_normal_object_count); - SET(total_generated_shady_object_count, objspace->profile.total_generated_shady_object_count); - SET(total_shade_operation_count, objspace->profile.total_shade_operation_count); - SET(total_promoted_count, objspace->profile.total_promoted_count); - SET(total_remembered_normal_object_count, objspace->profile.total_remembered_normal_object_count); - SET(total_remembered_shady_object_count, objspace->profile.total_remembered_shady_object_count); -#endif /* RGENGC_PROFILE */ -#undef SET - - if (!NIL_P(key)) { /* matched key should return above */ - rb_raise(rb_eArgError, "unknown key: %"PRIsVALUE, rb_sym2str(key)); - } - -#if defined(RGENGC_PROFILE) && RGENGC_PROFILE >= 2 - if (hash != Qnil) { - gc_count_add_each_types(hash, "generated_normal_object_count_types", objspace->profile.generated_normal_object_count_types); - gc_count_add_each_types(hash, "generated_shady_object_count_types", objspace->profile.generated_shady_object_count_types); - gc_count_add_each_types(hash, "shade_operation_count_types", objspace->profile.shade_operation_count_types); - gc_count_add_each_types(hash, "promoted_types", objspace->profile.promoted_types); - gc_count_add_each_types(hash, "remembered_normal_object_count_types", objspace->profile.remembered_normal_object_count_types); - gc_count_add_each_types(hash, "remembered_shady_object_count_types", objspace->profile.remembered_shady_object_count_types); - } +struct mark_machine_stack_location_maybe_data { + void *objspace; +#ifdef RUBY_ASAN_ENABLED + const rb_execution_context_t *ec; #endif +}; - return 0; -} - -static VALUE -gc_stat(rb_execution_context_t *ec, VALUE self, VALUE arg) // arg is (nil || hash || symbol) +static void +gc_mark_machine_stack_location_maybe(void *data, VALUE obj) { - if (NIL_P(arg)) { - arg = rb_hash_new(); - } - else if (SYMBOL_P(arg)) { - size_t value = gc_stat_internal(arg); - return SIZET2NUM(value); - } - else if (RB_TYPE_P(arg, T_HASH)) { - // ok - } - else { - rb_raise(rb_eTypeError, "non-hash or symbol given"); - } + void *objspace = ((struct mark_machine_stack_location_maybe_data *)data)->objspace; - gc_stat_internal(arg); - return arg; -} + rb_gc_impl_stack_location_mark_maybe(objspace, obj); -size_t -rb_gc_stat(VALUE key) -{ - if (SYMBOL_P(key)) { - size_t value = gc_stat_internal(key); - return value; - } - else { - gc_stat_internal(key); - return 0; +#ifdef RUBY_ASAN_ENABLED + const rb_execution_context_t *ec = ((struct mark_machine_stack_location_maybe_data *)data)->ec; + void *fake_frame_start; + void *fake_frame_end; + bool is_fake_frame = asan_get_fake_stack_extents( + ec->machine.asan_fake_stack_handle, obj, + ec->machine.stack_start, ec->machine.stack_end, + &fake_frame_start, &fake_frame_end + ); + if (is_fake_frame) { + each_stack_location(objspace, ec, fake_frame_start, fake_frame_end, rb_gc_impl_stack_location_mark_maybe); } +#endif } +#if defined(__wasm__) -enum gc_stat_heap_sym { - gc_stat_heap_sym_slot_size, - gc_stat_heap_sym_heap_allocatable_pages, - gc_stat_heap_sym_heap_eden_pages, - gc_stat_heap_sym_heap_eden_slots, - gc_stat_heap_sym_heap_tomb_pages, - gc_stat_heap_sym_heap_tomb_slots, - gc_stat_heap_sym_total_allocated_pages, - gc_stat_heap_sym_total_freed_pages, - gc_stat_heap_sym_force_major_gc_count, - gc_stat_heap_sym_force_incremental_marking_finish_count, - gc_stat_heap_sym_total_allocated_objects, - gc_stat_heap_sym_total_freed_objects, - gc_stat_heap_sym_last -}; -static VALUE gc_stat_heap_symbols[gc_stat_heap_sym_last]; +static VALUE *rb_stack_range_tmp[2]; static void -setup_gc_stat_heap_symbols(void) -{ - if (gc_stat_heap_symbols[0] == 0) { -#define S(s) gc_stat_heap_symbols[gc_stat_heap_sym_##s] = ID2SYM(rb_intern_const(#s)) - S(slot_size); - S(heap_allocatable_pages); - S(heap_eden_pages); - S(heap_eden_slots); - S(heap_tomb_pages); - S(heap_tomb_slots); - S(total_allocated_pages); - S(total_freed_pages); - S(force_major_gc_count); - S(force_incremental_marking_finish_count); - S(total_allocated_objects); - S(total_freed_objects); -#undef S - } -} - -static size_t -gc_stat_heap_internal(int size_pool_idx, VALUE hash_or_sym) +rb_mark_locations(void *begin, void *end) { - rb_objspace_t *objspace = &rb_objspace; - VALUE hash = Qnil, key = Qnil; - - setup_gc_stat_heap_symbols(); - - if (RB_TYPE_P(hash_or_sym, T_HASH)) { - hash = hash_or_sym; - } - else if (SYMBOL_P(hash_or_sym)) { - key = hash_or_sym; - } - else { - rb_raise(rb_eTypeError, "non-hash or symbol argument"); - } + rb_stack_range_tmp[0] = begin; + rb_stack_range_tmp[1] = end; +} - if (size_pool_idx < 0 || size_pool_idx >= SIZE_POOL_COUNT) { - rb_raise(rb_eArgError, "size pool index out of range"); - } +# if defined(__EMSCRIPTEN__) - rb_size_pool_t *size_pool = &size_pools[size_pool_idx]; +static void +mark_current_machine_context(void *objspace, rb_execution_context_t *ec) +{ + emscripten_scan_stack(rb_mark_locations); + each_stack_location(objspace, ec, rb_stack_range_tmp[0], rb_stack_range_tmp[1], rb_gc_impl_stack_location_mark_maybe); - if (!during_gc) rb_borrowing_sync_lock(objspace->local_data.ractor); - size_pool->total_allocated_objects += size_pool->newly_created_by_borrowing_count; - size_pool->newly_created_by_borrowing_count = 0; - if (!during_gc) rb_borrowing_sync_unlock(objspace->local_data.ractor); + emscripten_scan_registers(rb_mark_locations); + each_stack_location(objspace, ec, rb_stack_range_tmp[0], rb_stack_range_tmp[1], rb_gc_impl_stack_location_mark_maybe); +} +# else // use Asyncify version -#define SET(name, attr) \ - if (key == gc_stat_heap_symbols[gc_stat_heap_sym_##name]) \ - return attr; \ - else if (hash != Qnil) \ - rb_hash_aset(hash, gc_stat_heap_symbols[gc_stat_heap_sym_##name], SIZET2NUM(attr)); +static void +mark_current_machine_context(void *objspace, rb_execution_context_t *ec) +{ + VALUE *stack_start, *stack_end; + SET_STACK_END; + GET_STACK_BOUNDS(stack_start, stack_end, 1); + each_stack_location(objspace, ec, stack_start, stack_end, rb_gc_impl_stack_location_mark_maybe); - SET(slot_size, size_pool->slot_size); - SET(heap_allocatable_pages, size_pool->allocatable_pages); - SET(heap_eden_pages, SIZE_POOL_EDEN_HEAP(size_pool)->total_pages); - SET(heap_eden_slots, SIZE_POOL_EDEN_HEAP(size_pool)->total_slots); - SET(heap_tomb_pages, SIZE_POOL_TOMB_HEAP(size_pool)->total_pages); - SET(heap_tomb_slots, SIZE_POOL_TOMB_HEAP(size_pool)->total_slots); - SET(total_allocated_pages, size_pool->total_allocated_pages); - SET(total_freed_pages, size_pool->total_freed_pages); - SET(force_major_gc_count, size_pool->force_major_gc_count); - SET(force_incremental_marking_finish_count, size_pool->force_incremental_marking_finish_count); - SET(total_allocated_objects, size_pool->total_allocated_objects); - SET(total_freed_objects, size_pool->total_freed_objects); -#undef SET + rb_wasm_scan_locals(rb_mark_locations); + each_stack_location(objspace, ec, rb_stack_range_tmp[0], rb_stack_range_tmp[1], rb_gc_impl_stack_location_mark_maybe); +} - if (!NIL_P(key)) { /* matched key should return above */ - rb_raise(rb_eArgError, "unknown key: %"PRIsVALUE, rb_sym2str(key)); - } +# endif - return 0; -} +#else // !defined(__wasm__) -static VALUE -gc_stat_heap(rb_execution_context_t *ec, VALUE self, VALUE heap_name, VALUE arg) +static void +mark_current_machine_context(void *objspace, rb_execution_context_t *ec) { - if (NIL_P(heap_name)) { - if (NIL_P(arg)) { - arg = rb_hash_new(); - } - else if (RB_TYPE_P(arg, T_HASH)) { - // ok - } - else { - rb_raise(rb_eTypeError, "non-hash given"); - } + union { + rb_jmp_buf j; + VALUE v[sizeof(rb_jmp_buf) / (sizeof(VALUE))]; + } save_regs_gc_mark; + VALUE *stack_start, *stack_end; - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - VALUE hash = rb_hash_aref(arg, INT2FIX(i)); - if (NIL_P(hash)) { - hash = rb_hash_new(); - rb_hash_aset(arg, INT2FIX(i), hash); - } - gc_stat_heap_internal(i, hash); - } - } - else if (FIXNUM_P(heap_name)) { - int size_pool_idx = FIX2INT(heap_name); + FLUSH_REGISTER_WINDOWS; + memset(&save_regs_gc_mark, 0, sizeof(save_regs_gc_mark)); + /* This assumes that all registers are saved into the jmp_buf (and stack) */ + rb_setjmp(save_regs_gc_mark.j); - if (NIL_P(arg)) { - arg = rb_hash_new(); - } - else if (SYMBOL_P(arg)) { - size_t value = gc_stat_heap_internal(size_pool_idx, arg); - return SIZET2NUM(value); - } - else if (RB_TYPE_P(arg, T_HASH)) { - // ok - } - else { - rb_raise(rb_eTypeError, "non-hash or symbol given"); - } + /* SET_STACK_END must be called in this function because + * the stack frame of this function may contain + * callee save registers and they should be marked. */ + SET_STACK_END; + GET_STACK_BOUNDS(stack_start, stack_end, 1); - gc_stat_heap_internal(size_pool_idx, arg); - } - else { - rb_raise(rb_eTypeError, "heap_name must be nil or an Integer"); - } + struct mark_machine_stack_location_maybe_data data = { + .objspace = objspace, +#ifdef RUBY_ASAN_ENABLED + .ec = ec +#endif + }; - return arg; + each_location((void *)&data, save_regs_gc_mark.v, numberof(save_regs_gc_mark.v), gc_mark_machine_stack_location_maybe); + each_stack_location((void *)&data, ec, stack_start, stack_end, gc_mark_machine_stack_location_maybe); } +#endif -static VALUE -gc_stress_get(rb_execution_context_t *ec, VALUE self) +void +rb_gc_mark_machine_context(const rb_execution_context_t *ec) { - rb_objspace_t *objspace = &rb_objspace; - return ruby_gc_stress_mode; -} + VALUE *stack_start, *stack_end; -static VALUE -gc_stress_set_m(rb_execution_context_t *ec, VALUE self, VALUE flag) -{ - rb_objspace_t *objspace = &rb_objspace; + GET_STACK_BOUNDS(stack_start, stack_end, 0); + RUBY_DEBUG_LOG("ec->th:%u stack_start:%p stack_end:%p", rb_ec_thread_ptr(ec)->serial, stack_start, stack_end); - objspace->flags.gc_stressful = RTEST(flag); - objspace->gc_stress_mode = flag; + struct mark_machine_stack_location_maybe_data data = { + .objspace = rb_gc_get_objspace(), +#ifdef RUBY_ASAN_ENABLED + .ec = ec +#endif + }; - return flag; + each_stack_location((void *)&data, ec, stack_start, stack_end, gc_mark_machine_stack_location_maybe); + int num_regs = sizeof(ec->machine.regs)/(sizeof(VALUE)); + each_location((void *)&data, (VALUE*)&ec->machine.regs, num_regs, gc_mark_machine_stack_location_maybe); } -VALUE -rb_gc_enable(void) +static int +rb_mark_tbl_i(st_data_t key, st_data_t value, st_data_t data) { - rb_objspace_t *objspace = &rb_objspace; - return rb_objspace_gc_enable(objspace); -} + void *objspace = (void *)data; -VALUE -rb_objspace_gc_enable(rb_objspace_t *objspace) -{ - int old = dont_gc_val(); + rb_gc_impl_mark_and_pin(objspace, (VALUE)value); - dont_gc_off(); - return RBOOL(old); + return ST_CONTINUE; } -static VALUE -gc_enable(rb_execution_context_t *ec, VALUE _) +void +rb_mark_tbl(st_table *tbl) { - return rb_gc_enable(); -} + if (!tbl || tbl->num_entries == 0) return; -VALUE -rb_gc_disable_no_rest(void) -{ - rb_objspace_t *objspace = &rb_objspace; - return gc_disable_no_rest(objspace); + st_foreach(tbl, rb_mark_tbl_i, (st_data_t)rb_gc_get_objspace()); } -static VALUE -gc_disable_no_rest(rb_objspace_t *objspace) +static int +gc_mark_tbl_no_pin_i(st_data_t key, st_data_t value, st_data_t data) { - int old = dont_gc_val(); - dont_gc_on(); - return RBOOL(old); + void *objspace = (void *)data; + + rb_gc_impl_mark(objspace, (VALUE)value); + + return ST_CONTINUE; } -VALUE -rb_gc_disable(void) +static void +gc_mark_tbl_no_pin(void *objspace, st_table *tbl) { - rb_objspace_t *objspace = &rb_objspace; - return rb_objspace_gc_disable(objspace); + if (!tbl || tbl->num_entries == 0) return; + + st_foreach(tbl, gc_mark_tbl_no_pin_i, (st_data_t)objspace); } -VALUE -rb_objspace_gc_disable(rb_objspace_t *objspace) +void +rb_mark_tbl_no_pin(st_table *tbl) { - gc_rest(objspace); - return gc_disable_no_rest(objspace); + gc_mark_tbl_no_pin(rb_gc_get_objspace(), tbl); } -static VALUE -gc_disable(rb_execution_context_t *ec, VALUE _) +void +rb_gc_mark_maybe(VALUE obj) { - return rb_gc_disable(); + rb_gc_impl_mark_maybe(rb_gc_get_objspace(), obj); } -#if GC_CAN_COMPILE_COMPACTION -/* - * call-seq: - * GC.auto_compact = flag - * - * Updates automatic compaction mode. - * - * When enabled, the compactor will execute on every major collection. - * - * Enabling compaction will degrade performance on major collections. - */ -static VALUE -gc_set_auto_compact(VALUE _, VALUE v) +static enum rb_id_table_iterator_result +mark_cvc_tbl_i(VALUE cvc_entry, void *objspace) { - GC_ASSERT(GC_COMPACTION_SUPPORTED); - - ruby_enable_autocompact = RTEST(v); + struct rb_cvar_class_tbl_entry *entry; -#if RGENGC_CHECK_MODE - ruby_autocompact_compare_func = NULL; + entry = (struct rb_cvar_class_tbl_entry *)cvc_entry; - if (SYMBOL_P(v)) { - ID id = RB_SYM2ID(v); - if (id == rb_intern("empty")) { - ruby_autocompact_compare_func = compare_free_slots; - } - } -#endif + RUBY_ASSERT(entry->cref == 0 || (BUILTIN_TYPE((VALUE)entry->cref) == T_IMEMO && IMEMO_TYPE_P(entry->cref, imemo_cref))); + rb_gc_impl_mark(objspace, (VALUE)entry->cref); - return v; + return ID_TABLE_CONTINUE; } -#else -# define gc_set_auto_compact rb_f_notimplement -#endif -#if GC_CAN_COMPILE_COMPACTION -/* - * call-seq: - * GC.auto_compact -> true or false - * - * Returns whether or not automatic compaction has been enabled. - */ -static VALUE -gc_get_auto_compact(VALUE _) +static void +mark_cvc_tbl(void *objspace, VALUE klass) { - return RBOOL(ruby_enable_autocompact); + struct rb_id_table *tbl = RCLASS_CVC_TBL(klass); + if (tbl) { + rb_id_table_foreach_values(tbl, mark_cvc_tbl_i, objspace); + } } -#else -# define gc_get_auto_compact rb_f_notimplement -#endif - -static int -get_envparam_size(const char *name, size_t *default_value, size_t lower_bound) -{ - const char *ptr = getenv(name); - ssize_t val; - if (ptr != NULL && *ptr) { - size_t unit = 0; - char *end; -#if SIZEOF_SIZE_T == SIZEOF_LONG_LONG - val = strtoll(ptr, &end, 0); -#else - val = strtol(ptr, &end, 0); -#endif - switch (*end) { - case 'k': case 'K': - unit = 1024; - ++end; - break; - case 'm': case 'M': - unit = 1024*1024; - ++end; - break; - case 'g': case 'G': - unit = 1024*1024*1024; - ++end; - break; - } - while (*end && isspace((unsigned char)*end)) end++; - if (*end) { - if (RTEST(ruby_verbose)) fprintf(stderr, "invalid string for %s: %s\n", name, ptr); - return 0; - } - if (unit > 0) { - if (val < -(ssize_t)(SIZE_MAX / 2 / unit) || (ssize_t)(SIZE_MAX / 2 / unit) < val) { - if (RTEST(ruby_verbose)) fprintf(stderr, "%s=%s is ignored because it overflows\n", name, ptr); - return 0; - } - val *= unit; - } - if (val > 0 && (size_t)val > lower_bound) { - if (RTEST(ruby_verbose)) { - fprintf(stderr, "%s=%"PRIdSIZE" (default value: %"PRIuSIZE")\n", name, val, *default_value); - } - *default_value = (size_t)val; - return 1; - } - else { - if (RTEST(ruby_verbose)) { - fprintf(stderr, "%s=%"PRIdSIZE" (default value: %"PRIuSIZE") is ignored because it must be greater than %"PRIuSIZE".\n", - name, val, *default_value, lower_bound); - } - return 0; - } - } - return 0; +void +rb_gc_mark_movable(VALUE obj) +{ + rb_gc_impl_mark(rb_gc_get_objspace(), obj); } -static int -get_envparam_double(const char *name, double *default_value, double lower_bound, double upper_bound, int accept_zero) +void +rb_gc_mark(VALUE obj) { - const char *ptr = getenv(name); - double val; + rb_gc_impl_mark_and_pin(rb_gc_get_objspace(), obj); +} - if (ptr != NULL && *ptr) { - char *end; - val = strtod(ptr, &end); - if (!*ptr || *end) { - if (RTEST(ruby_verbose)) fprintf(stderr, "invalid string for %s: %s\n", name, ptr); - return 0; - } +void +rb_gc_mark_and_move(VALUE *ptr) +{ + rb_gc_impl_mark_and_move(rb_gc_get_objspace(), ptr); +} - if (accept_zero && val == 0.0) { - goto accept; - } - else if (val <= lower_bound) { - if (RTEST(ruby_verbose)) { - fprintf(stderr, "%s=%f (default value: %f) is ignored because it must be greater than %f.\n", - name, val, *default_value, lower_bound); - } - } - else if (upper_bound != 0.0 && /* ignore upper_bound if it is 0.0 */ - val > upper_bound) { - if (RTEST(ruby_verbose)) { - fprintf(stderr, "%s=%f (default value: %f) is ignored because it must be lower than %f.\n", - name, val, *default_value, upper_bound); - } - } - else { - goto accept; - } - } - return 0; +void +rb_gc_mark_weak(VALUE *ptr) +{ + rb_gc_impl_mark_weak(rb_gc_get_objspace(), ptr); +} - accept: - if (RTEST(ruby_verbose)) fprintf(stderr, "%s=%f (default value: %f)\n", name, val, *default_value); - *default_value = val; - return 1; +void +rb_gc_remove_weak(VALUE parent_obj, VALUE *ptr) +{ + rb_gc_impl_remove_weak(rb_gc_get_objspace(), parent_obj, ptr); } -static void -gc_set_initial_pages(rb_objspace_t *objspace) +static bool +gc_declarative_marking_p(const rb_data_type_t *type) { - gc_rest(objspace); + return (type->flags & RUBY_TYPED_DECL_MARKING) != 0; +} - for (int i = 0; i < SIZE_POOL_COUNT; i++) { - rb_size_pool_t *size_pool = &size_pools[i]; - char env_key[sizeof("RUBY_GC_HEAP_" "_INIT_SLOTS") + DECIMAL_SIZE_OF_BITS(sizeof(int) * CHAR_BIT)]; - snprintf(env_key, sizeof(env_key), "RUBY_GC_HEAP_%d_INIT_SLOTS", i); +static enum rb_id_table_iterator_result +mark_const_table_i(VALUE value, void *objspace) +{ + const rb_const_entry_t *ce = (const rb_const_entry_t *)value; - size_t size_pool_init_slots = gc_params.size_pool_init_slots[i]; - if (get_envparam_size(env_key, &size_pool_init_slots, 0)) { - gc_params.size_pool_init_slots[i] = size_pool_init_slots; - } + rb_gc_impl_mark(objspace, ce->value); + rb_gc_impl_mark(objspace, ce->file); - if (size_pool_init_slots > size_pool->eden_heap.total_slots) { - size_t slots = size_pool_init_slots - size_pool->eden_heap.total_slots; - size_pool->allocatable_pages = slots_to_pages_for_size_pool(objspace, size_pool, slots); - } - else { - /* We already have more slots than size_pool_init_slots allows, so - * prevent creating more pages. */ - size_pool->allocatable_pages = 0; - } - } - heap_pages_expand_sorted(objspace); + return ID_TABLE_CONTINUE; } -/* - * GC tuning environment variables - * - * * RUBY_GC_HEAP_FREE_SLOTS - * - Prepare at least this amount of slots after GC. - * - Allocate slots if there are not enough slots. - * * RUBY_GC_HEAP_GROWTH_FACTOR (new from 2.1) - * - Allocate slots by this factor. - * - (next slots number) = (current slots number) * (this factor) - * * RUBY_GC_HEAP_GROWTH_MAX_SLOTS (new from 2.1) - * - Allocation rate is limited to this number of slots. - * * RUBY_GC_HEAP_FREE_SLOTS_MIN_RATIO (new from 2.4) - * - Allocate additional pages when the number of free slots is - * lower than the value (total_slots * (this ratio)). - * * RUBY_GC_HEAP_FREE_SLOTS_GOAL_RATIO (new from 2.4) - * - Allocate slots to satisfy this formula: - * free_slots = total_slots * goal_ratio - * - In other words, prepare (total_slots * goal_ratio) free slots. - * - if this value is 0.0, then use RUBY_GC_HEAP_GROWTH_FACTOR directly. - * * RUBY_GC_HEAP_FREE_SLOTS_MAX_RATIO (new from 2.4) - * - Allow to free pages when the number of free slots is - * greater than the value (total_slots * (this ratio)). - * * RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR (new from 2.1.1) - * - Do full GC when the number of old objects is more than R * N - * where R is this factor and - * N is the number of old objects just after last full GC. - * - * * obsolete - * * RUBY_FREE_MIN -> RUBY_GC_HEAP_FREE_SLOTS (from 2.1) - * * RUBY_HEAP_MIN_SLOTS -> RUBY_GC_HEAP_INIT_SLOTS (from 2.1) - * - * * RUBY_GC_MALLOC_LIMIT - * * RUBY_GC_MALLOC_LIMIT_MAX (new from 2.1) - * * RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR (new from 2.1) - * - * * RUBY_GC_OLDMALLOC_LIMIT (new from 2.1) - * * RUBY_GC_OLDMALLOC_LIMIT_MAX (new from 2.1) - * * RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR (new from 2.1) - */ - void -ruby_gc_set_params(void) +rb_gc_mark_roots(void *objspace, const char **categoryp) { - rb_objspace_t *objspace = &rb_objspace; - /* RUBY_GC_HEAP_FREE_SLOTS */ - if (get_envparam_size("RUBY_GC_HEAP_FREE_SLOTS", &gc_params.heap_free_slots, 0)) { - /* ok */ - } + rb_execution_context_t *ec = GET_EC(); + rb_vm_t *vm = rb_ec_vm_ptr(ec); + +#define MARK_CHECKPOINT(category) do { \ + if (categoryp) *categoryp = category; \ +} while (0) - gc_set_initial_pages(objspace); + gc_mark_reset_parent(objspace); - get_envparam_double("RUBY_GC_HEAP_GROWTH_FACTOR", &gc_params.growth_factor, 1.0, 0.0, FALSE); - get_envparam_size ("RUBY_GC_HEAP_GROWTH_MAX_SLOTS", &gc_params.growth_max_slots, 0); - get_envparam_double("RUBY_GC_HEAP_FREE_SLOTS_MIN_RATIO", &gc_params.heap_free_slots_min_ratio, - 0.0, 1.0, FALSE); - get_envparam_double("RUBY_GC_HEAP_FREE_SLOTS_MAX_RATIO", &gc_params.heap_free_slots_max_ratio, - gc_params.heap_free_slots_min_ratio, 1.0, FALSE); - get_envparam_double("RUBY_GC_HEAP_FREE_SLOTS_GOAL_RATIO", &gc_params.heap_free_slots_goal_ratio, - gc_params.heap_free_slots_min_ratio, gc_params.heap_free_slots_max_ratio, TRUE); - get_envparam_double("RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR", &gc_params.oldobject_limit_factor, 0.0, 0.0, TRUE); - get_envparam_double("RUBY_GC_HEAP_SHAREDOBJECT_LIMIT_FACTOR", &gc_params.sharedobject_limit_factor, 0.0, 0.0, TRUE); - get_envparam_double("RUBY_GC_HEAP_REMEMBERED_WB_UNPROTECTED_OBJECTS_LIMIT_RATIO", &gc_params.uncollectible_wb_unprotected_objects_limit_ratio, 0.0, 0.0, TRUE); + MARK_CHECKPOINT("objspace"); + rb_gc_impl_objspace_mark(objspace); - if (get_envparam_size("RUBY_GC_MALLOC_LIMIT", &gc_params.malloc_limit_min, 0)) { - malloc_limit = gc_params.malloc_limit_min; + MARK_CHECKPOINT("vm"); + SET_STACK_END; + if (objspace == vm->objspace) { + rb_vm_mark(vm); + if (vm->self) rb_gc_impl_mark(objspace, vm->self); } - get_envparam_size ("RUBY_GC_MALLOC_LIMIT_MAX", &gc_params.malloc_limit_max, 0); - if (!gc_params.malloc_limit_max) { /* ignore max-check if 0 */ - gc_params.malloc_limit_max = SIZE_MAX; + mark_zombie_threads(objspace); + mark_absorbed_threads_tbl(objspace); + + MARK_CHECKPOINT("cache_table"); + mark_global_cc_cache_table(objspace); + + MARK_CHECKPOINT("ractor"); + rb_ractor_related_objects_mark(objspace->local_data.ractor); + mark_contained_ractor_tbl(objspace); + + MARK_CHECKPOINT("machine_context"); + mark_current_machine_context(objspace, ec); + + MARK_CHECKPOINT("end_proc"); + rb_mark_end_proc(objspace); + + if (objspace == vm->objspace) { + MARK_CHECKPOINT("global_tbl"); + rb_gc_mark_global_tbl(); } - get_envparam_double("RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR", &gc_params.malloc_limit_growth_factor, 1.0, 0.0, FALSE); -#if RGENGC_ESTIMATE_OLDMALLOC - if (get_envparam_size("RUBY_GC_OLDMALLOC_LIMIT", &gc_params.oldmalloc_limit_min, 0)) { - objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_min; + if (rb_during_local_gc()) { + MARK_CHECKPOINT("shared_reference_tbl"); + mark_shared_reference_tbl(objspace); + mark_received_received_obj_tbl(objspace); } - get_envparam_size ("RUBY_GC_OLDMALLOC_LIMIT_MAX", &gc_params.oldmalloc_limit_max, 0); - get_envparam_double("RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR", &gc_params.oldmalloc_limit_growth_factor, 1.0, 0.0, FALSE); -#endif -} -static void -reachable_objects_from_callback(VALUE obj) -{ - rb_ractor_t *cr = GET_RACTOR(); - cr->mfd->mark_func(obj, cr->mfd->data); + MARK_CHECKPOINT("finish"); +#undef MARK_CHECKPOINT } void -rb_objspace_reachable_objects_from(VALUE obj, void (func)(VALUE, void *), void *data) +rb_gc_mark_children(void *objspace, VALUE obj) { - rb_objspace_t *objspace = &rb_objspace; + if (FL_TEST(obj, FL_EXIVAR)) { + rb_mark_generic_ivar(obj); + } - RB_VM_LOCK_ENTER(); - { - if (during_gc) rb_bug("rb_objspace_reachable_objects_from() is not supported while during_gc == true"); + switch (BUILTIN_TYPE(obj)) { + case T_FLOAT: + case T_BIGNUM: + case T_SYMBOL: + /* Not immediates, but does not have references and singleton class. + * + * RSYMBOL(obj)->fstr intentionally not marked. See log for 96815f1e + * ("symbol.c: remove rb_gc_mark_symbols()") */ + return; - if (is_markable_object(obj)) { - rb_ractor_t *cr = GET_RACTOR(); - struct gc_mark_func_data_struct mfd = { - .mark_func = func, - .data = data, - }, *prev_mfd = cr->mfd; + case T_NIL: + case T_FIXNUM: + rb_bug("rb_gc_mark() called for broken object"); + break; - cr->mfd = &mfd; + case T_NODE: + UNEXPECTED_NODE(rb_gc_mark); + break; - VALUE already_disabled = rb_objspace_gc_disable(objspace); - gc_mark_children(objspace, obj); - if (already_disabled == Qfalse) rb_objspace_gc_enable(objspace); + case T_IMEMO: + rb_imemo_mark_and_move(obj, false); + return; - cr->mfd = prev_mfd; - } + default: + break; } - RB_VM_LOCK_LEAVE(); -} -struct root_objects_data { - const char *category; - void (*func)(const char *category, VALUE, void *); - void *data; -}; + rb_gc_impl_mark(objspace, RBASIC(obj)->klass); -static void -root_objects_from(VALUE obj, void *ptr) -{ - const struct root_objects_data *data = (struct root_objects_data *)ptr; - (*data->func)(data->category, obj, data->data); -} + rb_vm_t *vm = GET_VM(); -void -rb_objspace_reachable_objects_from_root(void (func)(const char *category, VALUE, void *), void *passing_data) -{ - rb_objspace_t *objspace = &rb_objspace; - objspace_reachable_objects_from_root(objspace, func, passing_data); -} + switch (BUILTIN_TYPE(obj)) { + case T_CLASS: + if (FL_TEST(obj, FL_SINGLETON)) { + rb_gc_impl_mark(objspace, RCLASS_ATTACHED_OBJECT(obj)); + } + // Continue to the shared T_CLASS/T_MODULE + case T_MODULE: + if (RCLASS_SUPER(obj)) { + rb_gc_impl_mark(objspace, RCLASS_SUPER(obj)); + } -static void -objspace_reachable_objects_from_root(rb_objspace_t *objspace, void (func)(const char *category, VALUE, void *), void *passing_data) -{ - if (during_gc) rb_bug("objspace_reachable_objects_from_root() is not supported while during_gc == true"); + mark_m_tbl(objspace, RCLASS_M_TBL(obj)); + mark_cvc_tbl(objspace, obj); + rb_cc_table_mark(obj); + if (rb_shape_obj_too_complex(obj)) { + gc_mark_tbl_no_pin(objspace, (st_table *)RCLASS_IVPTR(obj)); + } + else { + for (attr_index_t i = 0; i < RCLASS_IV_COUNT(obj); i++) { + rb_gc_impl_mark(objspace, RCLASS_IVPTR(obj)[i]); + } + } + if (objspace == vm->objspace || objspace->flags.during_global_gc) { + if (RCLASS_CONST_TBL(obj)) { + rb_id_table_foreach_values(RCLASS_CONST_TBL(obj), mark_const_table_i, objspace); + } + } - rb_ractor_t *cr = GET_RACTOR(); - struct root_objects_data data = { - .func = func, - .data = passing_data, - }; - struct gc_mark_func_data_struct mfd = { - .mark_func = root_objects_from, - .data = &data, - }, *prev_mfd = cr->mfd; + rb_native_mutex_lock(&vm->classpath_lock); + rb_gc_impl_mark(objspace, RCLASS_EXT(obj)->classpath); + rb_native_mutex_unlock(&vm->classpath_lock); + break; - cr->mfd = &mfd; + case T_ICLASS: + if (RICLASS_OWNS_M_TBL_P(obj)) { + mark_m_tbl(objspace, RCLASS_M_TBL(obj)); + } + if (RCLASS_SUPER(obj)) { + rb_gc_impl_mark(objspace, RCLASS_SUPER(obj)); + } - HEAP_LOCK_ENTER(objspace); - { - VALUE already_disabled = rb_objspace_gc_disable(objspace); - gc_mark_roots(objspace, &data.category); - if (already_disabled == Qfalse) rb_objspace_gc_enable(objspace); - } - HEAP_LOCK_LEAVE(objspace); + if (RCLASS_INCLUDER(obj)) { + rb_gc_impl_mark(objspace, RCLASS_INCLUDER(obj)); + } + mark_m_tbl(objspace, RCLASS_CALLABLE_M_TBL(obj)); + rb_cc_table_mark(obj); + break; - cr->mfd = prev_mfd; -} + case T_ARRAY: + if (ARY_SHARED_P(obj)) { + VALUE root = ARY_SHARED_ROOT(obj); + rb_gc_impl_mark(objspace, root); + } + else { + long len = RARRAY_LEN(obj); + const VALUE *ptr = RARRAY_CONST_PTR(obj); + for (long i = 0; i < len; i++) { + rb_gc_impl_mark(objspace, ptr[i]); + } + } + break; -/* - ------------------------ Extended allocator ------------------------ -*/ + case T_HASH: + mark_hash(objspace, obj); + break; + + case T_STRING: + if (STR_SHARED_P(obj)) { + if (STR_EMBED_P(RSTRING(obj)->as.heap.aux.shared)) { + /* Embedded shared strings cannot be moved because this string + * points into the slot of the shared string. There may be code + * using the RSTRING_PTR on the stack, which would pin this + * string but not pin the shared string, causing it to move. */ + rb_gc_impl_mark_and_pin(objspace, RSTRING(obj)->as.heap.aux.shared); + } + else { + rb_gc_impl_mark(objspace, RSTRING(obj)->as.heap.aux.shared); + } + } + break; + + case T_DATA: { + void *const ptr = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj); + + if (ptr) { + if (RTYPEDDATA_P(obj) && gc_declarative_marking_p(RTYPEDDATA(obj)->type)) { + size_t *offset_list = (size_t *)RTYPEDDATA(obj)->type->function.dmark; + + for (size_t offset = *offset_list; offset != RUBY_REF_END; offset = *offset_list++) { + rb_gc_impl_mark(objspace, *(VALUE *)((char *)ptr + offset)); + } + } + else { + RUBY_DATA_FUNC mark_func = RTYPEDDATA_P(obj) ? + RTYPEDDATA(obj)->type->function.dmark : + RDATA(obj)->dmark; + if (mark_func) (*mark_func)(ptr); + } + } + + break; + } + + case T_OBJECT: { + rb_shape_t *shape = rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj)); + + if (rb_shape_obj_too_complex(obj)) { + gc_mark_tbl_no_pin(objspace, ROBJECT_IV_HASH(obj)); + } + else { + const VALUE * const ptr = ROBJECT_IVPTR(obj); + + uint32_t len = ROBJECT_IV_COUNT(obj); + for (uint32_t i = 0; i < len; i++) { + rb_gc_impl_mark(objspace, ptr[i]); + } + } + + if (shape) { + VALUE klass = RBASIC_CLASS(obj); + + // Increment max_iv_count if applicable, used to determine size pool allocation + attr_index_t num_of_ivs = shape->next_iv_index; + if (RCLASS_EXT(klass)->max_iv_count < num_of_ivs) { + RCLASS_EXT(klass)->max_iv_count = num_of_ivs; + } + } + + break; + } + + case T_FILE: + if (RFILE(obj)->fptr) { + rb_gc_impl_mark(objspace, RFILE(obj)->fptr->self); + rb_gc_impl_mark(objspace, RFILE(obj)->fptr->pathv); + rb_gc_impl_mark(objspace, RFILE(obj)->fptr->tied_io_for_writing); + rb_gc_impl_mark(objspace, RFILE(obj)->fptr->writeconv_asciicompat); + rb_gc_impl_mark(objspace, RFILE(obj)->fptr->writeconv_pre_ecopts); + rb_gc_impl_mark(objspace, RFILE(obj)->fptr->encs.ecopts); + rb_gc_impl_mark(objspace, RFILE(obj)->fptr->write_lock); + rb_gc_impl_mark(objspace, RFILE(obj)->fptr->timeout); + } + break; -struct gc_raise_tag { - VALUE exc; - const char *fmt; - va_list *ap; -}; + case T_REGEXP: + rb_gc_impl_mark(objspace, RREGEXP(obj)->src); + break; -static void * -gc_vraise(void *ptr) -{ - struct gc_raise_tag *argv = ptr; - rb_vraise(argv->exc, argv->fmt, *argv->ap); - UNREACHABLE_RETURN(NULL); -} + case T_MATCH: + rb_gc_impl_mark(objspace, RMATCH(obj)->regexp); + if (RMATCH(obj)->str) { + rb_gc_impl_mark(objspace, RMATCH(obj)->str); + } + break; -static void -gc_raise(VALUE exc, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - struct gc_raise_tag argv = { - exc, fmt, &ap, - }; + case T_RATIONAL: + rb_gc_impl_mark(objspace, RRATIONAL(obj)->num); + rb_gc_impl_mark(objspace, RRATIONAL(obj)->den); + break; - if (ruby_thread_has_gvl_p()) { - gc_vraise(&argv); - UNREACHABLE; - } - else if (ruby_native_thread_p()) { - rb_thread_call_with_gvl(gc_vraise, &argv); - UNREACHABLE; - } - else { - /* Not in a ruby thread */ - fprintf(stderr, "%s", "[FATAL] "); - vfprintf(stderr, fmt, ap); - } + case T_COMPLEX: + rb_gc_impl_mark(objspace, RCOMPLEX(obj)->real); + rb_gc_impl_mark(objspace, RCOMPLEX(obj)->imag); + break; - va_end(ap); - abort(); -} + case T_STRUCT: { + const long len = RSTRUCT_LEN(obj); + const VALUE * const ptr = RSTRUCT_CONST_PTR(obj); -static void objspace_xfree(rb_objspace_t *objspace, void *ptr, size_t size); + for (long i = 0; i < len; i++) { + rb_gc_impl_mark(objspace, ptr[i]); + } -static void -negative_size_allocation_error(const char *msg) -{ - gc_raise(rb_eNoMemError, "%s", msg); -} + break; + } -static void * -ruby_memerror_body(void *dummy) -{ - rb_memerror(); - return 0; + default: + if (BUILTIN_TYPE(obj) == T_MOVED) rb_bug("rb_gc_mark(): %p is T_MOVED", (void *)obj); + if (BUILTIN_TYPE(obj) == T_NONE) rb_bug("rb_gc_mark(): %p is T_NONE", (void *)obj); + if (BUILTIN_TYPE(obj) == T_ZOMBIE) rb_bug("rb_gc_mark(): %p is T_ZOMBIE", (void *)obj); + rb_bug("rb_gc_mark(): unknown data type 0x%x(%p) %s", + BUILTIN_TYPE(obj), (void *)obj, + rb_gc_impl_pointer_to_heap_p(rb_gc_get_objspace(), (void *)obj) ? "corrupted object" : "non object"); + } } -NORETURN(static void ruby_memerror(void)); -RBIMPL_ATTR_MAYBE_UNUSED() -static void -ruby_memerror(void) +size_t +rb_gc_obj_optimal_size(VALUE obj) { - if (ruby_thread_has_gvl_p()) { - rb_memerror(); - } - else { - if (ruby_native_thread_p()) { - rb_thread_call_with_gvl(ruby_memerror_body, 0); + switch (BUILTIN_TYPE(obj)) { + case T_ARRAY: + return rb_ary_size_as_embedded(obj); + + case T_OBJECT: + if (rb_shape_obj_too_complex(obj)) { + return sizeof(struct RObject); } else { - /* no ruby thread */ - fprintf(stderr, "[FATAL] failed to allocate memory\n"); + return rb_obj_embedded_size(ROBJECT_IV_CAPACITY(obj)); } + + case T_STRING: + return rb_str_size_as_embedded(obj); + + case T_HASH: + return sizeof(struct RHash) + (RHASH_ST_TABLE_P(obj) ? sizeof(st_table) : sizeof(ar_table)); + + default: + return 0; } - exit(EXIT_FAILURE); } void -rb_memerror(void) +rb_gc_writebarrier(VALUE a, VALUE b) { - rb_execution_context_t *ec = GET_EC(); - rb_objspace_t *objspace = &rb_objspace; - VALUE exc; - - if (0) { - // Print out pid, sleep, so you can attach debugger to see what went wrong: - fprintf(stderr, "rb_memerror pid=%"PRI_PIDT_PREFIX"d\n", getpid()); - sleep(60); - } + rb_gc_impl_writebarrier(rb_gc_get_objspace(), a, b); +} - if (during_gc) { - // TODO: OMG!! How to implement it? - gc_exit(objspace, gc_enter_event_rb_memerror); - } +void +rb_gc_writebarrier_unprotect(VALUE obj) +{ + rb_gc_impl_writebarrier_unprotect(rb_gc_get_objspace(), obj); +} - exc = nomem_error; - if (!exc || - rb_ec_raised_p(ec, RAISED_NOMEMORY)) { - fprintf(stderr, "[FATAL] failed to allocate memory\n"); - exit(EXIT_FAILURE); - } - if (rb_ec_raised_p(ec, RAISED_NOMEMORY)) { - rb_ec_raised_clear(ec); - } - else { - rb_ec_raised_set(ec, RAISED_NOMEMORY); - exc = ruby_vm_special_exception_copy(exc); - } - ec->errinfo = exc; - EC_JUMP_TAG(ec, TAG_RAISE); +/* + * remember `obj' if needed. + */ +void +rb_gc_writebarrier_remember(VALUE obj) +{ + rb_gc_impl_writebarrier_remember(rb_gc_get_objspace(), obj); } -static void -rb_aligned_free(void *ptr, size_t size) +void +rb_gc_copy_attributes(VALUE dest, VALUE obj) { -#if defined __MINGW32__ - __mingw_aligned_free(ptr); -#elif defined _WIN32 - _aligned_free(ptr); -#elif defined(HAVE_POSIX_MEMALIGN) || defined(HAVE_MEMALIGN) - free(ptr); -#else - free(((void**)ptr)[-1]); -#endif + rb_gc_impl_copy_attributes(rb_gc_get_objspace(), dest, obj); } -static inline size_t -objspace_malloc_size(rb_objspace_t *objspace, void *ptr, size_t hint) +// TODO: rearchitect this function to work for a generic GC +size_t +rb_obj_gc_flags(VALUE obj, ID* flags, size_t max) { -#ifdef HAVE_MALLOC_USABLE_SIZE - return malloc_usable_size(ptr); -#else - return hint; -#endif + return rb_gc_impl_obj_flags(rb_gc_get_objspace(), obj, flags, max); } -enum memop_type { - MEMOP_TYPE_MALLOC = 0, - MEMOP_TYPE_FREE, - MEMOP_TYPE_REALLOC -}; +/* GC */ -static inline void -atomic_sub_nounderflow(size_t *var, size_t sub) +void * +rb_gc_ractor_cache_alloc(void) { - if (sub == 0) return; - - while (1) { - size_t val = *var; - if (val < sub) sub = val; - if (ATOMIC_SIZE_CAS(*var, val, val-sub) == val) break; - } + return rb_gc_impl_ractor_cache_alloc(rb_gc_get_objspace()); } -static void -objspace_malloc_gc_stress(rb_objspace_t *objspace) +void +rb_gc_ractor_cache_free(void *cache) { - if (ruby_gc_stressful && ruby_native_thread_p()) { - unsigned int reason = (GPR_FLAG_IMMEDIATE_MARK | GPR_FLAG_IMMEDIATE_SWEEP | - GPR_FLAG_STRESS | GPR_FLAG_MALLOC); - - if (gc_stress_full_mark_after_malloc_p()) { - reason |= GPR_FLAG_FULL_MARK; - } - garbage_collect_with_gvl(objspace, reason); - } + rb_gc_impl_ractor_cache_free(rb_gc_get_objspace(), cache); } -static inline bool -objspace_malloc_increase_report(rb_objspace_t *objspace, void *mem, size_t new_size, size_t old_size, enum memop_type type) +static VALUE +register_mark_object_no_redirection(VALUE obj) { - if (0) fprintf(stderr, "increase - ptr: %p, type: %s, new_size: %"PRIdSIZE", old_size: %"PRIdSIZE"\n", - mem, - type == MEMOP_TYPE_MALLOC ? "malloc" : - type == MEMOP_TYPE_FREE ? "free " : - type == MEMOP_TYPE_REALLOC ? "realloc": "error", - new_size, old_size); - return false; + rb_ractor_t *r = rb_current_allocating_ractor(); + rb_objspace_t *objspace = r->local_objspace; + if (!rb_gc_impl_pointer_to_heap_p(objspace, (void *)obj)) + return Qnil; + + rb_vm_register_global_object(obj); + return Qnil; } -static bool -objspace_malloc_increase_body(rb_objspace_t *objspace, void *mem, size_t new_size, size_t old_size, enum memop_type type) +void +rb_gc_register_mark_object(VALUE obj) { - if (new_size > old_size) { - ATOMIC_SIZE_ADD(malloc_increase, new_size - old_size); -#if RGENGC_ESTIMATE_OLDMALLOC - ATOMIC_SIZE_ADD(objspace->rgengc.oldmalloc_increase, new_size - old_size); -#endif + VALUE ret; + if (rb_special_const_p(obj)) { + ret = register_mark_object_no_redirection(obj); } else { - atomic_sub_nounderflow(&malloc_increase, old_size - new_size); -#if RGENGC_ESTIMATE_OLDMALLOC - atomic_sub_nounderflow(&objspace->rgengc.oldmalloc_increase, old_size - new_size); -#endif + WITH_OBJSPACE_OF_VALUE_ENTER(obj, objspace); + { + ret = rb_run_with_redirected_allocation(objspace->local_data.ractor, register_mark_object_no_redirection, obj); + } + WITH_OBJSPACE_OF_VALUE_LEAVE(objspace); } + return ret; +} - if (type == MEMOP_TYPE_MALLOC) { - retry: - if (malloc_increase > malloc_limit && ruby_native_thread_p() && !dont_gc_val()) { - if (ruby_thread_has_gvl_p() && is_lazy_sweeping(objspace)) { - gc_rest(objspace); /* gc_rest can reduce malloc_increase */ - goto retry; - } - garbage_collect_with_gvl(objspace, GPR_FLAG_MALLOC); - } - } +void +rb_gc_register_address(VALUE *addr) +{ + rb_vm_t *vm = GET_VM(); -#if MALLOC_ALLOCATED_SIZE - if (new_size >= old_size) { - ATOMIC_SIZE_ADD(objspace->malloc_params.allocated_size, new_size - old_size); - } - else { - size_t dec_size = old_size - new_size; - size_t allocated_size = objspace->malloc_params.allocated_size; + VALUE obj = *addr; -#if MALLOC_ALLOCATED_SIZE_CHECK - if (allocated_size < dec_size) { - rb_bug("objspace_malloc_increase: underflow malloc_params.allocated_size."); - } -#endif - atomic_sub_nounderflow(&objspace->malloc_params.allocated_size, dec_size); - } + rb_ractor_t *r = GET_RACTOR(); + VM_ASSERT(SPECIAL_CONST_P(obj) || GET_RACTOR_OF_VALUE(obj) == r || FL_TEST_RAW(obj, FL_SHAREABLE)); + struct global_object_list *tmp = ALLOC(struct global_object_list); + tmp->next = r->global_object_list; + tmp->varptr = addr; + r->global_object_list = tmp; - switch (type) { - case MEMOP_TYPE_MALLOC: - ATOMIC_SIZE_INC(objspace->malloc_params.allocations); - break; - case MEMOP_TYPE_FREE: - { - size_t allocations = objspace->malloc_params.allocations; - if (allocations > 0) { - atomic_sub_nounderflow(&objspace->malloc_params.allocations, 1); - } -#if MALLOC_ALLOCATED_SIZE_CHECK - else { - GC_ASSERT(objspace->malloc_params.allocations > 0); - } -#endif - } - break; - case MEMOP_TYPE_REALLOC: /* ignore */ break; + /* + * Because some C extensions have assignment-then-register bugs, + * we guard `obj` here so that it would not get swept defensively. + */ + RB_GC_GUARD(obj); + if (0 && !SPECIAL_CONST_P(obj)) { + rb_warn("Object is assigned to registering address already: %"PRIsVALUE, + rb_obj_class(obj)); + rb_print_backtrace(stderr); } -#endif - return true; } -#define objspace_malloc_increase(...) \ - for (bool malloc_increase_done = objspace_malloc_increase_report(__VA_ARGS__); \ - !malloc_increase_done; \ - malloc_increase_done = objspace_malloc_increase_body(__VA_ARGS__)) - -struct malloc_obj_info { /* 4 words */ - size_t size; -#if USE_GC_MALLOC_OBJ_INFO_DETAILS - size_t gen; - const char *file; - size_t line; -#endif -}; - -#if USE_GC_MALLOC_OBJ_INFO_DETAILS -const char *ruby_malloc_info_file; -int ruby_malloc_info_line; -#endif - -static inline size_t -objspace_malloc_prepare(rb_objspace_t *objspace, size_t size) +void +rb_gc_unregister_address(VALUE *addr) { - if (size == 0) size = 1; + rb_ractor_t *r = GET_RACTOR(); + VM_ASSERT(SPECIAL_CONST_P(*addr) || GET_RACTOR_OF_VALUE(*addr) == r || FL_TEST_RAW(*addr, FL_SHAREABLE)); + struct global_object_list *tmp = r->global_object_list; -#if CALC_EXACT_MALLOC_SIZE - size += sizeof(struct malloc_obj_info); -#endif + if (tmp->varptr == addr) { + r->global_object_list = tmp->next; + xfree(tmp); + return; + } + while (tmp->next) { + if (tmp->next->varptr == addr) { + struct global_object_list *t = tmp->next; - return size; + tmp->next = tmp->next->next; + xfree(t); + break; + } + tmp = tmp->next; + } } -static bool -malloc_during_gc_p(rb_objspace_t *objspace) +void +rb_global_variable(VALUE *var) { - /* malloc is not allowed during GC when we're not using multiple ractors - * (since ractors can run while another thread is sweeping) and when we - * have the GVL (since if we don't have the GVL, we'll try to acquire the - * GVL which will block and ensure the other thread finishes GC). */ - return during_gc && !dont_gc_val() && !rb_multi_ractor_p() && ruby_thread_has_gvl_p(); + rb_gc_register_address(var); } -static inline void * -objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size) +void +rb_gc_register_in_mark_object_ary(VALUE obj) { - size = objspace_malloc_size(objspace, mem, size); - objspace_malloc_increase(objspace, mem, size, 0, MEMOP_TYPE_MALLOC) {} - -#if CALC_EXACT_MALLOC_SIZE - { - struct malloc_obj_info *info = mem; - info->size = size; -#if USE_GC_MALLOC_OBJ_INFO_DETAILS - info->gen = objspace->profile.count; - info->file = ruby_malloc_info_file; - info->line = info->file ? ruby_malloc_info_line : 0; -#endif - mem = info + 1; - } -#endif - - return mem; -} - -#if defined(__GNUC__) && RUBY_DEBUG -#define RB_BUG_INSTEAD_OF_RB_MEMERROR 1 -#endif + WITH_OBJSPACE_OF_VALUE_ENTER(obj, objspace); + { + rb_ractor_t *r = objspace->local_data.ractor; -#ifndef RB_BUG_INSTEAD_OF_RB_MEMERROR -# define RB_BUG_INSTEAD_OF_RB_MEMERROR 0 -#endif + VALUE already_disabled = rb_objspace_gc_disable(objspace); + rb_native_mutex_lock(&r->mark_object_ary_lock); -#define GC_MEMERROR(...) \ - ((RB_BUG_INSTEAD_OF_RB_MEMERROR+0) ? rb_bug("" __VA_ARGS__) : rb_memerror()) - -#define TRY_WITH_GC(siz, expr) do { \ - const gc_profile_record_flag gpr = \ - GPR_FLAG_FULL_MARK | \ - GPR_FLAG_IMMEDIATE_MARK | \ - GPR_FLAG_IMMEDIATE_SWEEP | \ - GPR_FLAG_MALLOC; \ - objspace_malloc_gc_stress(objspace); \ - \ - if (LIKELY((expr))) { \ - /* Success on 1st try */ \ - } \ - else if (!garbage_collect_with_gvl(objspace, gpr)) { \ - /* @shyouhei thinks this doesn't happen */ \ - GC_MEMERROR("TRY_WITH_GC: could not GC"); \ - } \ - else if ((expr)) { \ - /* Success on 2nd try */ \ - } \ - else { \ - GC_MEMERROR("TRY_WITH_GC: could not allocate:" \ - "%"PRIdSIZE" bytes for %s", \ - siz, # expr); \ - } \ - } while (0) + VALUE list = r->mark_object_ary; + VALUE head = rb_pin_array_list_append(list, obj); + if (head != list) { + r->mark_object_ary = head; + } + RB_GC_GUARD(obj); -static void -check_malloc_not_in_gc(rb_objspace_t *objspace, const char *msg) -{ - if (UNLIKELY(malloc_during_gc_p(objspace))) { - dont_gc_on(); - during_gc = false; - rb_bug("Cannot %s during GC", msg); + rb_native_mutex_unlock(&r->mark_object_ary_lock); + if (already_disabled == Qfalse) rb_objspace_gc_enable(objspace); } + WITH_OBJSPACE_OF_VALUE_LEAVE(objspace); } -/* these shouldn't be called directly. - * objspace_* functions do not check allocation size. - */ -static void * -objspace_xmalloc0(rb_objspace_t *objspace, size_t size) +static VALUE +gc_start_internal(rb_execution_context_t *ec, VALUE self, VALUE full_mark, VALUE immediate_mark, VALUE immediate_sweep, VALUE global, VALUE compact) { - check_malloc_not_in_gc(objspace, "malloc"); - - void *mem; + rb_gc_impl_start(rb_gc_get_objspace(), RTEST(full_mark), RTEST(immediate_mark), RTEST(immediate_sweep), RTEST(global), RTEST(compact)); - size = objspace_malloc_prepare(objspace, size); - TRY_WITH_GC(size, mem = malloc(size)); - RB_DEBUG_COUNTER_INC(heap_xmalloc); - return objspace_malloc_fixup(objspace, mem, size); + return Qnil; } -static inline size_t -xmalloc2_size(const size_t count, const size_t elsize) +/* + * rb_objspace_each_objects() is special C API to walk through + * Ruby object space. This C API is too difficult to use it. + * To be frank, you should not use it. Or you need to read the + * source code of this function and understand what this function does. + * + * 'callback' will be called several times (the number of heap page, + * at current implementation) with: + * vstart: a pointer to the first living object of the heap_page. + * vend: a pointer to next to the valid heap_page area. + * stride: a distance to next VALUE. + * + * If callback() returns non-zero, the iteration will be stopped. + * + * This is a sample callback code to iterate liveness objects: + * + * static int + * sample_callback(void *vstart, void *vend, int stride, void *data) + * { + * VALUE v = (VALUE)vstart; + * for (; v != (VALUE)vend; v += stride) { + * if (!rb_objspace_internal_object_p(v)) { // liveness check + * // do something with live object 'v' + * } + * } + * return 0; // continue to iteration + * } + * + * Note: 'vstart' is not a top of heap_page. This point the first + * living object to grasp at least one object to avoid GC issue. + * This means that you can not walk through all Ruby object page + * including freed object page. + * + * Note: On this implementation, 'stride' is the same as sizeof(RVALUE). + * However, there are possibilities to pass variable values with + * 'stride' with some reasons. You must use stride instead of + * use some constant value in the iteration. + */ +void +rb_objspace_each_objects(int (*callback)(void *, void *, size_t, void *), void *data) { - return size_mul_or_raise(count, elsize, rb_eArgError); + rb_gc_impl_each_objects(rb_gc_get_objspace(), callback, data); } -static void * -objspace_xrealloc(rb_objspace_t *objspace, void *ptr, size_t new_size, size_t old_size) +static void +gc_ref_update_array(void *objspace, VALUE v) { - check_malloc_not_in_gc(objspace, "realloc"); + if (ARY_SHARED_P(v)) { + VALUE old_root = RARRAY(v)->as.heap.aux.shared_root; - void *mem; + UPDATE_IF_MOVED(objspace, RARRAY(v)->as.heap.aux.shared_root); - if (!ptr) return objspace_xmalloc0(objspace, new_size); + VALUE new_root = RARRAY(v)->as.heap.aux.shared_root; + // If the root is embedded and its location has changed + if (ARY_EMBED_P(new_root) && new_root != old_root) { + size_t offset = (size_t)(RARRAY(v)->as.heap.ptr - RARRAY(old_root)->as.ary); + GC_ASSERT(RARRAY(v)->as.heap.ptr >= RARRAY(old_root)->as.ary); + RARRAY(v)->as.heap.ptr = RARRAY(new_root)->as.ary + offset; + } + } + else { + long len = RARRAY_LEN(v); - /* - * The behavior of realloc(ptr, 0) is implementation defined. - * Therefore we don't use realloc(ptr, 0) for portability reason. - * see http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_400.htm - */ - if (new_size == 0) { - if ((mem = objspace_xmalloc0(objspace, 0)) != NULL) { - /* - * - OpenBSD's malloc(3) man page says that when 0 is passed, it - * returns a non-NULL pointer to an access-protected memory page. - * The returned pointer cannot be read / written at all, but - * still be a valid argument of free(). - * - * https://man.openbsd.org/malloc.3 - * - * - Linux's malloc(3) man page says that it _might_ perhaps return - * a non-NULL pointer when its argument is 0. That return value - * is safe (and is expected) to be passed to free(). - * - * https://man7.org/linux/man-pages/man3/malloc.3.html - * - * - As I read the implementation jemalloc's malloc() returns fully - * normal 16 bytes memory region when its argument is 0. - * - * - As I read the implementation musl libc's malloc() returns - * fully normal 32 bytes memory region when its argument is 0. - * - * - Other malloc implementations can also return non-NULL. - */ - objspace_xfree(objspace, ptr, old_size); - return mem; + if (len > 0) { + VALUE *ptr = (VALUE *)RARRAY_CONST_PTR(v); + for (long i = 0; i < len; i++) { + UPDATE_IF_MOVED(objspace, ptr[i]); + } } - else { - /* - * It is dangerous to return NULL here, because that could lead to - * RCE. Fallback to 1 byte instead of zero. - * - * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11932 - */ - new_size = 1; + + if (rb_gc_obj_slot_size(v) >= rb_ary_size_as_embedded(v)) { + if (rb_ary_embeddable_p(v)) { + rb_ary_make_embedded(v); + } } } +} -#if CALC_EXACT_MALLOC_SIZE - { - struct malloc_obj_info *info = (struct malloc_obj_info *)ptr - 1; - new_size += sizeof(struct malloc_obj_info); - ptr = info; - old_size = info->size; - } -#endif +static void gc_ref_update_table_values_only(void *objspace, st_table *tbl); - old_size = objspace_malloc_size(objspace, ptr, old_size); - TRY_WITH_GC(new_size, mem = RB_GNUC_EXTENSION_BLOCK(realloc(ptr, new_size))); - new_size = objspace_malloc_size(objspace, mem, new_size); +static void +gc_ref_update_object(void *objspace, VALUE v) +{ + VALUE *ptr = ROBJECT_IVPTR(v); -#if CALC_EXACT_MALLOC_SIZE - { - struct malloc_obj_info *info = mem; - info->size = new_size; - mem = info + 1; + if (rb_shape_obj_too_complex(v)) { + gc_ref_update_table_values_only(objspace, ROBJECT_IV_HASH(v)); + return; } -#endif - objspace_malloc_increase(objspace, mem, new_size, old_size, MEMOP_TYPE_REALLOC); + size_t slot_size = rb_gc_obj_slot_size(v); + size_t embed_size = rb_obj_embedded_size(ROBJECT_IV_CAPACITY(v)); + if (slot_size >= embed_size && !RB_FL_TEST_RAW(v, ROBJECT_EMBED)) { + // Object can be re-embedded + memcpy(ROBJECT(v)->as.ary, ptr, sizeof(VALUE) * ROBJECT_IV_COUNT(v)); + RB_FL_SET_RAW(v, ROBJECT_EMBED); + xfree(ptr); + ptr = ROBJECT(v)->as.ary; + } - RB_DEBUG_COUNTER_INC(heap_xrealloc); - return mem; + for (uint32_t i = 0; i < ROBJECT_IV_COUNT(v); i++) { + UPDATE_IF_MOVED(objspace, ptr[i]); + } } -#if CALC_EXACT_MALLOC_SIZE && USE_GC_MALLOC_OBJ_INFO_DETAILS - -#define MALLOC_INFO_GEN_SIZE 100 -#define MALLOC_INFO_SIZE_SIZE 10 -static size_t malloc_info_gen_cnt[MALLOC_INFO_GEN_SIZE]; -static size_t malloc_info_gen_size[MALLOC_INFO_GEN_SIZE]; -static size_t malloc_info_size[MALLOC_INFO_SIZE_SIZE+1]; -static st_table *malloc_info_file_table; - static int -mmalloc_info_file_i(st_data_t key, st_data_t val, st_data_t dmy) +hash_replace_ref(st_data_t *key, st_data_t *value, st_data_t argp, int existing) { - const char *file = (void *)key; - const size_t *data = (void *)val; + void *objspace = (void *)argp; - fprintf(stderr, "%s\t%"PRIdSIZE"\t%"PRIdSIZE"\n", file, data[0], data[1]); + if (rb_gc_impl_object_moved_p(objspace, (VALUE)*key)) { + *key = rb_gc_impl_location(objspace, (VALUE)*key); + } + + if (rb_gc_impl_object_moved_p(objspace, (VALUE)*value)) { + *value = rb_gc_impl_location(objspace, (VALUE)*value); + } return ST_CONTINUE; } -__attribute__((destructor)) -void -rb_malloc_info_show_results(void) +static int +hash_foreach_replace(st_data_t key, st_data_t value, st_data_t argp, int error) { - int i; + void *objspace; - fprintf(stderr, "* malloc_info gen statistics\n"); - for (i=0; isize; -#if USE_GC_MALLOC_OBJ_INFO_DETAILS - { - int gen = (int)(objspace->profile.count - info->gen); - int gen_index = gen >= MALLOC_INFO_GEN_SIZE ? MALLOC_INFO_GEN_SIZE-1 : gen; - int i; - - malloc_info_gen_cnt[gen_index]++; - malloc_info_gen_size[gen_index] += info->size; - - for (i=0; isize <= s) { - malloc_info_size[i]++; - goto found; - } - } - malloc_info_size[i]++; - found:; + return ST_CONTINUE; +} - { - st_data_t key = (st_data_t)info->file, d; - size_t *data; +static int +hash_foreach_replace_value(st_data_t key, st_data_t value, st_data_t argp, int error) +{ + void *objspace; - if (malloc_info_file_table == NULL) { - malloc_info_file_table = st_init_numtable_with_size(1024); - } - if (st_lookup(malloc_info_file_table, key, &d)) { - /* hit */ - data = (size_t *)d; - } - else { - data = malloc(xmalloc2_size(2, sizeof(size_t))); - if (data == NULL) rb_bug("objspace_xfree: can not allocate memory"); - data[0] = data[1] = 0; - st_insert(malloc_info_file_table, key, (st_data_t)data); - } - data[0] ++; - data[1] += info->size; - }; - if (0 && gen >= 2) { /* verbose output */ - if (info->file) { - fprintf(stderr, "free - size:%"PRIdSIZE", gen:%d, pos: %s:%"PRIdSIZE"\n", - info->size, gen, info->file, info->line); - } - else { - fprintf(stderr, "free - size:%"PRIdSIZE", gen:%d\n", - info->size, gen); - } - } - } -#endif -#endif - old_size = objspace_malloc_size(objspace, ptr, old_size); + objspace = (void *)argp; - objspace_malloc_increase(objspace, ptr, 0, old_size, MEMOP_TYPE_FREE) { - free(ptr); - ptr = NULL; - RB_DEBUG_COUNTER_INC(heap_xfree); + if (rb_gc_impl_object_moved_p(objspace, (VALUE)value)) { + return ST_REPLACE; } + return ST_CONTINUE; } -static void * -ruby_xmalloc0(size_t size) +static void +gc_ref_update_table_values_only(void *objspace, st_table *tbl) { - return objspace_xmalloc0(&rb_objspace, size); -} + if (!tbl || tbl->num_entries == 0) return; -void * -ruby_xmalloc_body(size_t size) -{ - if ((ssize_t)size < 0) { - negative_size_allocation_error("too large allocation size"); + if (st_foreach_with_replace(tbl, hash_foreach_replace_value, hash_replace_ref_value, (st_data_t)objspace)) { + rb_raise(rb_eRuntimeError, "hash modified during iteration"); } - return ruby_xmalloc0(size); } void -ruby_malloc_size_overflow(size_t count, size_t elsize) +rb_gc_ref_update_table_values_only(st_table *tbl) { - rb_raise(rb_eArgError, - "malloc: possible integer overflow (%"PRIuSIZE"*%"PRIuSIZE")", - count, elsize); + gc_ref_update_table_values_only(rb_gc_get_objspace(), tbl); } -void * -ruby_xmalloc2_body(size_t n, size_t size) +static void +gc_update_table_refs(void *objspace, st_table *tbl) { - return objspace_xmalloc0(&rb_objspace, xmalloc2_size(n, size)); -} + if (!tbl || tbl->num_entries == 0) return; -static void * -objspace_xcalloc(rb_objspace_t *objspace, size_t size) -{ - if (UNLIKELY(malloc_during_gc_p(objspace))) { - rb_warn("calloc during GC detected, this could cause crashes if it triggers another GC"); -#if RGENGC_CHECK_MODE || RUBY_DEBUG - rb_bug("Cannot calloc during GC"); -#endif + if (st_foreach_with_replace(tbl, hash_foreach_replace, hash_replace_ref, (st_data_t)objspace)) { + rb_raise(rb_eRuntimeError, "hash modified during iteration"); } - - void *mem; - - size = objspace_malloc_prepare(objspace, size); - TRY_WITH_GC(size, mem = calloc1(size)); - return objspace_malloc_fixup(objspace, mem, size); } -void * -ruby_xcalloc_body(size_t n, size_t size) +/* Update MOVED references in a VALUE=>VALUE st_table */ +void +rb_gc_update_tbl_refs(st_table *ptr) { - return objspace_xcalloc(&rb_objspace, xmalloc2_size(n, size)); + gc_update_table_refs(rb_gc_get_objspace(), ptr); } -#ifdef ruby_sized_xrealloc -#undef ruby_sized_xrealloc -#endif -void * -ruby_sized_xrealloc(void *ptr, size_t new_size, size_t old_size) +static void +gc_ref_update_hash(void *objspace, VALUE v) { - if ((ssize_t)new_size < 0) { - negative_size_allocation_error("too large allocation size"); - } - - return objspace_xrealloc(&rb_objspace, ptr, new_size, old_size); + rb_hash_stlike_foreach_with_replace(v, hash_foreach_replace, hash_replace_ref, (st_data_t)objspace); } -void * -ruby_xrealloc_body(void *ptr, size_t new_size) +static void +gc_update_values(void *objspace, long n, VALUE *values) { - return ruby_sized_xrealloc(ptr, new_size, 0); + for (long i = 0; i < n; i++) { + UPDATE_IF_MOVED(objspace, values[i]); + } } -#ifdef ruby_sized_xrealloc2 -#undef ruby_sized_xrealloc2 -#endif -void * -ruby_sized_xrealloc2(void *ptr, size_t n, size_t size, size_t old_n) +void +rb_gc_update_values(long n, VALUE *values) { - size_t len = xmalloc2_size(n, size); - return objspace_xrealloc(&rb_objspace, ptr, len, old_n * size); + gc_update_values(rb_gc_get_objspace(), n, values); } -void * -ruby_xrealloc2_body(void *ptr, size_t n, size_t size) +static enum rb_id_table_iterator_result +check_id_table_move(VALUE value, void *data) { - return ruby_sized_xrealloc2(ptr, n, size, 0); -} + void *objspace = (void *)data; -#ifdef ruby_sized_xfree -#undef ruby_sized_xfree -#endif -void -ruby_sized_xfree(void *x, size_t size) -{ - if (LIKELY(x)) { - /* It's possible for a C extension's pthread destructor function set by pthread_key_create - * to be called after ruby_vm_destruct and attempt to free memory. Fall back to mimfree in - * that case. */ - if (LIKELY(GET_VM())) { - objspace_xfree(&rb_objspace, x, size); - } - else { - ruby_mimfree(x); - } + if (rb_gc_impl_object_moved_p(objspace, (VALUE)value)) { + return ID_TABLE_REPLACE; } + + return ID_TABLE_CONTINUE; } -void -ruby_xfree(void *x) +VALUE +rb_gc_location(VALUE value) { - ruby_sized_xfree(x, 0); + return rb_gc_impl_location(rb_gc_get_objspace(), value); } -void * -rb_xmalloc_mul_add(size_t x, size_t y, size_t z) /* x * y + z */ +void +rb_gc_prepare_heap(void) { - size_t w = size_mul_add_or_raise(x, y, z, rb_eArgError); - return ruby_xmalloc(w); + rb_gc_impl_prepare_heap(rb_gc_get_objspace()); } -void * -rb_xcalloc_mul_add(size_t x, size_t y, size_t z) /* x * y + z */ +size_t +rb_gc_size_pool_id_for_size(size_t size) { - size_t w = size_mul_add_or_raise(x, y, z, rb_eArgError); - return ruby_xcalloc(w, 1); + return rb_gc_impl_size_pool_id_for_size(rb_gc_get_objspace(), size); } -void * -rb_xrealloc_mul_add(const void *p, size_t x, size_t y, size_t z) /* x * y + z */ +bool +rb_gc_size_allocatable_p(size_t size) { - size_t w = size_mul_add_or_raise(x, y, z, rb_eArgError); - return ruby_xrealloc((void *)p, w); + return rb_gc_impl_size_allocatable_p(size); } -void * -rb_xmalloc_mul_add_mul(size_t x, size_t y, size_t z, size_t w) /* x * y + z * w */ +static enum rb_id_table_iterator_result +update_id_table(VALUE *value, void *data, int existing) { - size_t u = size_mul_add_mul_or_raise(x, y, z, w, rb_eArgError); - return ruby_xmalloc(u); + void *objspace = (void *)data; + + if (rb_gc_impl_object_moved_p(objspace, (VALUE)*value)) { + *value = rb_gc_impl_location(objspace, (VALUE)*value); + } + + return ID_TABLE_CONTINUE; } -void * -rb_xcalloc_mul_add_mul(size_t x, size_t y, size_t z, size_t w) /* x * y + z * w */ +static void +update_m_tbl(void *objspace, struct rb_id_table *tbl) { - size_t u = size_mul_add_mul_or_raise(x, y, z, w, rb_eArgError); - return ruby_xcalloc(u, 1); + if (tbl) { + rb_id_table_foreach_values_with_replace(tbl, check_id_table_move, update_id_table, objspace); + } } -/* Mimic ruby_xmalloc, but need not rb_objspace. - * should return pointer suitable for ruby_xfree - */ -void * -ruby_mimmalloc(size_t size) +static enum rb_id_table_iterator_result +update_cc_tbl_i(VALUE ccs_ptr, void *objspace) { - void *mem; -#if CALC_EXACT_MALLOC_SIZE - size += sizeof(struct malloc_obj_info); -#endif - mem = malloc(size); -#if CALC_EXACT_MALLOC_SIZE - if (!mem) { - return NULL; + struct rb_class_cc_entries *ccs = (struct rb_class_cc_entries *)ccs_ptr; + VM_ASSERT(vm_ccs_p(ccs)); + + if (rb_gc_impl_object_moved_p(objspace, (VALUE)ccs->cme)) { + ccs->cme = (const rb_callable_method_entry_t *)rb_gc_impl_location(objspace, (VALUE)ccs->cme); } - 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; + + for (int i=0; ilen); i++) { + if (rb_gc_impl_object_moved_p(objspace, (VALUE)ccs->entries[i].cc)) { + ccs->entries[i].cc = (struct rb_callcache *)rb_gc_location((VALUE)ccs->entries[i].cc); + } } -#endif - return mem; + + // do not replace + return ID_TABLE_CONTINUE; } -void * -ruby_mimcalloc(size_t num, size_t size) +static void +update_cc_tbl(void *objspace, VALUE klass) { - 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; + struct rb_id_table *tbl = RCLASS_CC_TBL(klass); + if (tbl) { + rb_id_table_foreach_values(tbl, update_cc_tbl_i, objspace); } - size = t.right + sizeof(struct malloc_obj_info); - mem = calloc1(size); - if (!mem) { - return NULL; +} + +static enum rb_id_table_iterator_result +update_cvc_tbl_i(VALUE cvc_entry, void *objspace) +{ + struct rb_cvar_class_tbl_entry *entry; + + entry = (struct rb_cvar_class_tbl_entry *)cvc_entry; + + if (entry->cref) { + TYPED_UPDATE_IF_MOVED(objspace, rb_cref_t *, entry->cref); } - 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; + + entry->class_value = rb_gc_impl_location(objspace, entry->class_value); + + return ID_TABLE_CONTINUE; +} + +static void +update_cvc_tbl(void *objspace, VALUE klass) +{ + struct rb_id_table *tbl = RCLASS_CVC_TBL(klass); + if (tbl) { + rb_id_table_foreach_values(tbl, update_cvc_tbl_i, objspace); } -#else - mem = calloc(num, size); -#endif - return mem; } -void -ruby_mimfree(void *ptr) +static enum rb_id_table_iterator_result +update_const_table(VALUE value, void *objspace) { -#if CALC_EXACT_MALLOC_SIZE - struct malloc_obj_info *info = (struct malloc_obj_info *)ptr - 1; - ptr = info; -#endif - free(ptr); + rb_const_entry_t *ce = (rb_const_entry_t *)value; + + if (rb_gc_impl_object_moved_p(objspace, ce->value)) { + ce->value = rb_gc_impl_location(objspace, ce->value); + } + + if (rb_gc_impl_object_moved_p(objspace, ce->file)) { + ce->file = rb_gc_impl_location(objspace, ce->file); + } + + return ID_TABLE_CONTINUE; } -#if MALLOC_ALLOCATED_SIZE -/* - * call-seq: - * GC.malloc_allocated_size -> Integer - * - * Returns the size of memory allocated by malloc(). - * - * Only available if ruby was built with +CALC_EXACT_MALLOC_SIZE+. - */ +static void +update_const_tbl(void *objspace, struct rb_id_table *tbl) +{ + if (!tbl) return; + rb_id_table_foreach_values(tbl, update_const_table, objspace); +} -static VALUE -gc_malloc_allocated_size(VALUE self) +static void +update_subclass_entries(void *objspace, rb_subclass_entry_t *entry) { - return UINT2NUM(rb_objspace.malloc_params.allocated_size); + while (entry) { + UPDATE_IF_MOVED(objspace, entry->klass); + entry = entry->next; + } } -/* - * call-seq: - * GC.malloc_allocations -> Integer - * - * Returns the number of malloc() allocations. - * - * Only available if ruby was built with +CALC_EXACT_MALLOC_SIZE+. - */ +static void +update_class_ext(void *objspace, rb_classext_t *ext) +{ + UPDATE_IF_MOVED(objspace, ext->origin_); + UPDATE_IF_MOVED(objspace, ext->includer); + UPDATE_IF_MOVED(objspace, ext->refined_class); + update_subclass_entries(objspace, ext->subclasses); +} -static VALUE -gc_malloc_allocations(VALUE self) +static void +update_superclasses(void *objspace, VALUE obj) { - return UINT2NUM(rb_objspace.malloc_params.allocations); + if (FL_TEST_RAW(obj, RCLASS_SUPERCLASSES_INCLUDE_SELF)) { + for (size_t i = 0; i < RCLASS_SUPERCLASS_DEPTH(obj) + 1; i++) { + UPDATE_IF_MOVED(objspace, RCLASS_SUPERCLASSES(obj)[i]); + } + } } -#endif + +extern rb_symbols_t ruby_global_symbols; void -rb_gc_adjust_memory_usage(ssize_t diff) +rb_gc_update_vm_references(void *objspace) { - unless_objspace(objspace) { return; } + rb_execution_context_t *ec = GET_EC(); + rb_vm_t *vm = rb_ec_vm_ptr(ec); - if (diff > 0) { - objspace_malloc_increase(objspace, 0, diff, 0, MEMOP_TYPE_REALLOC); + if (objspace == vm->objspace) { + rb_vm_update_references(vm); + rb_gc_update_global_tbl(); } - else if (diff < 0) { - objspace_malloc_increase(objspace, 0, 0, -diff, MEMOP_TYPE_REALLOC); + rb_ractor_update_references(objspace->local_data.ractor); + + GLOBAL_SYMBOLS_ENTER(global_symbols); + { + global_symbols->ids = rb_gc_impl_location(global_symbols->ids); + global_symbols->dsymbol_fstr_hash = rb_gc_impl_location(global_symbols->dsymbol_fstr_hash); + gc_update_table_refs(objspace, global_symbols->str_sym); } + GLOBAL_SYMBOLS_LEAVE(global_symbols); + + rb_native_mutex_lock(&objspace->local_data.obj_id_lock); + gc_ref_update_table_values_only(objspace, objspace->local_data.obj_to_id_tbl); + gc_update_table_refs(objspace, objspace->local_data.id_to_obj_tbl); + rb_native_mutex_unlock(&objspace->local_data.obj_id_lock); + gc_update_table_refs(objspace, finalizer_table); } -/* - ------------------------------ GC profiler ------------------------------ -*/ +void +rb_gc_update_object_references(void *objspace, VALUE obj) +{ + if (FL_TEST(obj, FL_EXIVAR)) { + rb_ref_update_generic_ivar(obj); + } + + switch (BUILTIN_TYPE(obj)) { + case T_CLASS: + if (FL_TEST(obj, FL_SINGLETON)) { + UPDATE_IF_MOVED(objspace, RCLASS_ATTACHED_OBJECT(obj)); + } + // Continue to the shared T_CLASS/T_MODULE + case T_MODULE: + if (RCLASS_SUPER((VALUE)obj)) { + UPDATE_IF_MOVED(objspace, RCLASS(obj)->super); + } + update_m_tbl(objspace, RCLASS_M_TBL(obj)); + update_cc_tbl(objspace, obj); + update_cvc_tbl(objspace, obj); + update_superclasses(objspace, obj); + + if (rb_shape_obj_too_complex(obj)) { + gc_ref_update_table_values_only(objspace, RCLASS_IV_HASH(obj)); + } + else { + for (attr_index_t i = 0; i < RCLASS_IV_COUNT(obj); i++) { + UPDATE_IF_MOVED(objspace, RCLASS_IVPTR(obj)[i]); + } + } + + update_class_ext(objspace, RCLASS_EXT(obj)); + update_const_tbl(objspace, RCLASS_CONST_TBL(obj)); + + UPDATE_IF_MOVED(objspace, RCLASS_EXT(obj)->classpath); + break; + + case T_ICLASS: + if (RICLASS_OWNS_M_TBL_P(obj)) { + update_m_tbl(objspace, RCLASS_M_TBL(obj)); + } + if (RCLASS_SUPER((VALUE)obj)) { + UPDATE_IF_MOVED(objspace, RCLASS(obj)->super); + } + update_class_ext(objspace, RCLASS_EXT(obj)); + update_m_tbl(objspace, RCLASS_CALLABLE_M_TBL(obj)); + update_cc_tbl(objspace, obj); + break; + + case T_IMEMO: + rb_imemo_mark_and_move(obj, true); + return; + + case T_NIL: + case T_FIXNUM: + case T_NODE: + case T_MOVED: + case T_NONE: + /* These can't move */ + return; + + case T_ARRAY: + gc_ref_update_array(objspace, obj); + break; + + case T_HASH: + gc_ref_update_hash(objspace, obj); + UPDATE_IF_MOVED(objspace, RHASH(obj)->ifnone); + break; -#define GC_PROFILE_RECORD_DEFAULT_SIZE 100 + case T_STRING: + { + if (STR_SHARED_P(obj)) { + UPDATE_IF_MOVED(objspace, RSTRING(obj)->as.heap.aux.shared); + } -static bool -current_process_time(struct timespec *ts) -{ -#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID) - { - static int try_clock_gettime = 1; - if (try_clock_gettime && clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ts) == 0) { - return true; - } - else { - try_clock_gettime = 0; - } - } -#endif + /* If, after move the string is not embedded, and can fit in the + * slot it's been placed in, then re-embed it. */ + if (rb_gc_obj_slot_size(obj) >= rb_str_size_as_embedded(obj)) { + if (!STR_EMBED_P(obj) && rb_str_reembeddable_p(obj)) { + rb_str_make_embedded(obj); + } + } -#ifdef RUSAGE_SELF - { - struct rusage usage; - struct timeval time; - if (getrusage(RUSAGE_SELF, &usage) == 0) { - time = usage.ru_utime; - ts->tv_sec = time.tv_sec; - ts->tv_nsec = (int32_t)time.tv_usec * 1000; - return true; + break; } - } -#endif + case T_DATA: + /* Call the compaction callback, if it exists */ + { + void *const ptr = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj); + if (ptr) { + if (RTYPEDDATA_P(obj) && gc_declarative_marking_p(RTYPEDDATA(obj)->type)) { + size_t *offset_list = (size_t *)RTYPEDDATA(obj)->type->function.dmark; -#ifdef _WIN32 - { - FILETIME creation_time, exit_time, kernel_time, user_time; - ULARGE_INTEGER ui; - - if (GetProcessTimes(GetCurrentProcess(), - &creation_time, &exit_time, &kernel_time, &user_time) != 0) { - memcpy(&ui, &user_time, sizeof(FILETIME)); -#define PER100NSEC (uint64_t)(1000 * 1000 * 10) - ts->tv_nsec = (long)(ui.QuadPart % PER100NSEC); - ts->tv_sec = (time_t)(ui.QuadPart / PER100NSEC); - return true; + for (size_t offset = *offset_list; offset != RUBY_REF_END; offset = *offset_list++) { + VALUE *ref = (VALUE *)((char *)ptr + offset); + if (SPECIAL_CONST_P(*ref)) continue; + *ref = rb_gc_impl_location(objspace, *ref); + } + } + else if (RTYPEDDATA_P(obj)) { + RUBY_DATA_FUNC compact_func = RTYPEDDATA(obj)->type->function.dcompact; + if (compact_func) (*compact_func)(ptr); + } + } } - } -#endif + break; - return false; -} + case T_OBJECT: + gc_ref_update_object(objspace, obj); + break; -static double -getrusage_time(void) -{ - struct timespec ts; - if (current_process_time(&ts)) { - return ts.tv_sec + ts.tv_nsec * 1e-9; - } - else { - return 0.0; - } -} + case T_FILE: + if (RFILE(obj)->fptr) { + UPDATE_IF_MOVED(objspace, RFILE(obj)->fptr->self); + UPDATE_IF_MOVED(objspace, RFILE(obj)->fptr->pathv); + UPDATE_IF_MOVED(objspace, RFILE(obj)->fptr->tied_io_for_writing); + UPDATE_IF_MOVED(objspace, RFILE(obj)->fptr->writeconv_asciicompat); + UPDATE_IF_MOVED(objspace, RFILE(obj)->fptr->writeconv_pre_ecopts); + UPDATE_IF_MOVED(objspace, RFILE(obj)->fptr->encs.ecopts); + UPDATE_IF_MOVED(objspace, RFILE(obj)->fptr->write_lock); + } + break; + case T_REGEXP: + UPDATE_IF_MOVED(objspace, RREGEXP(obj)->src); + break; + case T_SYMBOL: + UPDATE_IF_MOVED(objspace, RSYMBOL(obj)->fstr); + break; -static inline void -gc_prof_setup_new_record(rb_objspace_t *objspace, unsigned int reason) -{ - if (objspace->profile.run) { - size_t index = objspace->profile.next_index; - gc_profile_record *record; + case T_FLOAT: + case T_BIGNUM: + break; - /* create new record */ - objspace->profile.next_index++; + case T_MATCH: + UPDATE_IF_MOVED(objspace, RMATCH(obj)->regexp); - if (!objspace->profile.records) { - objspace->profile.size = GC_PROFILE_RECORD_DEFAULT_SIZE; - objspace->profile.records = malloc(xmalloc2_size(sizeof(gc_profile_record), objspace->profile.size)); - } - if (index >= objspace->profile.size) { - void *ptr; - objspace->profile.size += 1000; - ptr = realloc(objspace->profile.records, xmalloc2_size(sizeof(gc_profile_record), objspace->profile.size)); - if (!ptr) rb_memerror(); - objspace->profile.records = ptr; + if (RMATCH(obj)->str) { + UPDATE_IF_MOVED(objspace, RMATCH(obj)->str); } - if (!objspace->profile.records) { - rb_bug("gc_profile malloc or realloc miss"); - } - record = objspace->profile.current_record = &objspace->profile.records[objspace->profile.next_index - 1]; - MEMZERO(record, gc_profile_record, 1); + break; - /* setup before-GC parameter */ - record->flags = reason | (ruby_gc_stressful ? GPR_FLAG_STRESS : 0); -#if MALLOC_ALLOCATED_SIZE - record->allocated_size = malloc_allocated_size; -#endif -#if GC_PROFILE_MORE_DETAIL && GC_PROFILE_DETAIL_MEMORY -#ifdef RUSAGE_SELF + case T_RATIONAL: + UPDATE_IF_MOVED(objspace, RRATIONAL(obj)->num); + UPDATE_IF_MOVED(objspace, RRATIONAL(obj)->den); + break; + + case T_COMPLEX: + UPDATE_IF_MOVED(objspace, RCOMPLEX(obj)->real); + UPDATE_IF_MOVED(objspace, RCOMPLEX(obj)->imag); + + break; + + case T_STRUCT: { - struct rusage usage; - if (getrusage(RUSAGE_SELF, &usage) == 0) { - record->maxrss = usage.ru_maxrss; - record->minflt = usage.ru_minflt; - record->majflt = usage.ru_majflt; + long i, len = RSTRUCT_LEN(obj); + VALUE *ptr = (VALUE *)RSTRUCT_CONST_PTR(obj); + + for (i = 0; i < len; i++) { + UPDATE_IF_MOVED(objspace, ptr[i]); } } -#endif -#endif + break; + default: + rb_bug("unreachable"); + break; } + + UPDATE_IF_MOVED(objspace, RBASIC(obj)->klass); } -static inline void -gc_prof_timer_start(rb_objspace_t *objspace) +VALUE +rb_gc_start(void) { - if (gc_prof_enabled(objspace)) { - gc_profile_record *record = gc_prof_record(objspace); -#if GC_PROFILE_MORE_DETAIL - record->prepare_time = objspace->profile.prepare_time; -#endif - record->gc_time = 0; - record->gc_invoke_time = getrusage_time(); - } + rb_gc(); + return Qnil; } -static double -elapsed_time_from(double time) +void +rb_gc(void) { - double now = getrusage_time(); - if (now > time) { - return now - time; - } - else { - return 0; - } + unless_objspace(objspace) { return; } + + rb_gc_impl_start(objspace, true, true, true, false, false); } -static inline void -gc_prof_timer_stop(rb_objspace_t *objspace) +int +rb_during_gc(void) { - if (gc_prof_enabled(objspace)) { - gc_profile_record *record = gc_prof_record(objspace); - record->gc_time = elapsed_time_from(record->gc_invoke_time); - record->gc_invoke_time -= objspace->profile.invoke_time; - } + unless_objspace(objspace) { return FALSE; } + + return rb_gc_impl_during_gc_p(objspace); } -#define RUBY_DTRACE_GC_HOOK(name) \ - do {if (RUBY_DTRACE_GC_##name##_ENABLED()) RUBY_DTRACE_GC_##name();} while (0) -static inline void -gc_prof_mark_timer_start(rb_objspace_t *objspace) +size_t +rb_gc_count(void) { - RUBY_DTRACE_GC_HOOK(MARK_BEGIN); -#if GC_PROFILE_MORE_DETAIL - if (gc_prof_enabled(objspace)) { - gc_prof_record(objspace)->gc_mark_time = getrusage_time(); - } -#endif + return rb_gc_impl_gc_count(rb_gc_get_objspace()); } -static inline void -gc_prof_mark_timer_stop(rb_objspace_t *objspace) +static VALUE +gc_count(rb_execution_context_t *ec, VALUE self) { - RUBY_DTRACE_GC_HOOK(MARK_END); -#if GC_PROFILE_MORE_DETAIL - if (gc_prof_enabled(objspace)) { - gc_profile_record *record = gc_prof_record(objspace); - record->gc_mark_time = elapsed_time_from(record->gc_mark_time); - } -#endif + return SIZET2NUM(rb_gc_count()); } -static inline void -gc_prof_sweep_timer_start(rb_objspace_t *objspace) +VALUE +rb_gc_latest_gc_info(VALUE key) { - RUBY_DTRACE_GC_HOOK(SWEEP_BEGIN); - if (gc_prof_enabled(objspace)) { - gc_profile_record *record = gc_prof_record(objspace); + return rb_gc_impl_latest_gc_info(rb_gc_get_objspace(), key); +} - if (record->gc_time > 0 || GC_PROFILE_MORE_DETAIL) { - objspace->profile.gc_sweep_start_time = getrusage_time(); - } +static VALUE +gc_latest_gc_info(rb_execution_context_t *ec, VALUE self, VALUE arg) +{ + if (NIL_P(arg)) { + arg = rb_hash_new(); + } + else if (!SYMBOL_P(arg) && !RB_TYPE_P(arg, T_HASH)) { + rb_raise(rb_eTypeError, "non-hash or symbol given"); } + + return rb_gc_latest_gc_info(arg); } -static inline void -gc_prof_sweep_timer_stop(rb_objspace_t *objspace) +static VALUE +gc_stat(rb_execution_context_t *ec, VALUE self, VALUE arg) // arg is (nil || hash || symbol) { - RUBY_DTRACE_GC_HOOK(SWEEP_END); - - if (gc_prof_enabled(objspace)) { - double sweep_time; - gc_profile_record *record = gc_prof_record(objspace); + if (NIL_P(arg)) { + arg = rb_hash_new(); + } - if (record->gc_time > 0) { - sweep_time = elapsed_time_from(objspace->profile.gc_sweep_start_time); - /* need to accumulate GC time for lazy sweep after gc() */ - record->gc_time += sweep_time; - } - else if (GC_PROFILE_MORE_DETAIL) { - sweep_time = elapsed_time_from(objspace->profile.gc_sweep_start_time); - } + size_t val = rb_gc_impl_stat(rb_gc_get_objspace(), arg); -#if GC_PROFILE_MORE_DETAIL - record->gc_sweep_time += sweep_time; - if (heap_pages_deferred_final) record->flags |= GPR_FLAG_HAVE_FINALIZE; -#endif - if (heap_pages_deferred_final) objspace->profile.latest_gc_info |= GPR_FLAG_HAVE_FINALIZE; + if (SYMBOL_P(arg)) { + return SIZET2NUM(val); + } + else { + return arg; } } -static inline void -gc_prof_set_malloc_info(rb_objspace_t *objspace) +size_t +rb_gc_stat(VALUE key) { -#if GC_PROFILE_MORE_DETAIL - if (gc_prof_enabled(objspace)) { - gc_profile_record *record = gc_prof_record(objspace); - record->allocate_increase = malloc_increase; - record->allocate_limit = malloc_limit; + if (SYMBOL_P(key)) { + return rb_gc_impl_stat(rb_gc_get_objspace(), key); + } + else { + rb_gc_impl_stat(rb_gc_get_objspace(), key); + return 0; } -#endif } -static inline void -gc_prof_set_heap_info(rb_objspace_t *objspace) +static VALUE +gc_stat_heap(rb_execution_context_t *ec, VALUE self, VALUE heap_name, VALUE arg) { - if (gc_prof_enabled(objspace)) { - gc_profile_record *record = gc_prof_record(objspace); - size_t live = objspace->profile.total_allocated_objects_at_gc_start - total_freed_objects(objspace); - size_t total = objspace->profile.heap_used_at_gc_start * HEAP_PAGE_OBJ_LIMIT; + if (NIL_P(arg)) { + arg = rb_hash_new(); + } -#if GC_PROFILE_MORE_DETAIL - record->heap_use_pages = objspace->profile.heap_used_at_gc_start; - record->heap_live_objects = live; - record->heap_free_objects = total - live; -#endif + size_t val = rb_gc_impl_stat_heap(rb_gc_get_objspace(), heap_name, arg); - record->heap_total_objects = total; - record->heap_use_size = live * sizeof(RVALUE); - record->heap_total_size = total * sizeof(RVALUE); + if (SYMBOL_P(arg)) { + return SIZET2NUM(val); + } + else { + return arg; } } -/* - * call-seq: - * GC::Profiler.clear -> nil - * - * Clears the \GC profiler data. - * - */ - static VALUE -gc_profile_clear(VALUE _) -{ - rb_objspace_t *objspace = &rb_objspace; - void *p = objspace->profile.records; - objspace->profile.records = NULL; - objspace->profile.size = 0; - objspace->profile.next_index = 0; - objspace->profile.current_record = 0; - free(p); - return Qnil; +gc_config_get(rb_execution_context_t *ec, VALUE self) +{ + return rb_gc_impl_config_get(rb_gc_get_objspace()); } -/* - * call-seq: - * GC::Profiler.raw_data -> [Hash, ...] - * - * Returns an Array of individual raw profile data Hashes ordered - * from earliest to latest by +:GC_INVOKE_TIME+. - * - * For example: - * - * [ - * { - * :GC_TIME=>1.3000000000000858e-05, - * :GC_INVOKE_TIME=>0.010634999999999999, - * :HEAP_USE_SIZE=>289640, - * :HEAP_TOTAL_SIZE=>588960, - * :HEAP_TOTAL_OBJECTS=>14724, - * :GC_IS_MARKED=>false - * }, - * # ... - * ] - * - * The keys mean: - * - * +:GC_TIME+:: - * Time elapsed in seconds for this GC run - * +:GC_INVOKE_TIME+:: - * Time elapsed in seconds from startup to when the GC was invoked - * +:HEAP_USE_SIZE+:: - * Total bytes of heap used - * +:HEAP_TOTAL_SIZE+:: - * Total size of heap in bytes - * +:HEAP_TOTAL_OBJECTS+:: - * Total number of objects - * +:GC_IS_MARKED+:: - * Returns +true+ if the GC is in mark phase - * - * If ruby was built with +GC_PROFILE_MORE_DETAIL+, you will also have access - * to the following hash keys: - * - * +:GC_MARK_TIME+:: - * +:GC_SWEEP_TIME+:: - * +:ALLOCATE_INCREASE+:: - * +:ALLOCATE_LIMIT+:: - * +:HEAP_USE_PAGES+:: - * +:HEAP_LIVE_OBJECTS+:: - * +:HEAP_FREE_OBJECTS+:: - * +:HAVE_FINALIZE+:: - * - */ - static VALUE -gc_profile_record_get(VALUE _) +gc_config_set(rb_execution_context_t *ec, VALUE self, VALUE hash) { - VALUE prof; - VALUE gc_profile = rb_ary_new(); - size_t i; - rb_objspace_t *objspace = (&rb_objspace); - - if (!objspace->profile.run) { - return Qnil; - } - - for (i =0; i < objspace->profile.next_index; i++) { - gc_profile_record *record = &objspace->profile.records[i]; - - prof = rb_hash_new(); - rb_hash_aset(prof, ID2SYM(rb_intern("GC_FLAGS")), gc_info_decode(objspace, rb_hash_new(), record->flags)); - rb_hash_aset(prof, ID2SYM(rb_intern("GC_TIME")), DBL2NUM(record->gc_time)); - rb_hash_aset(prof, ID2SYM(rb_intern("GC_INVOKE_TIME")), DBL2NUM(record->gc_invoke_time)); - rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_USE_SIZE")), SIZET2NUM(record->heap_use_size)); - rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_TOTAL_SIZE")), SIZET2NUM(record->heap_total_size)); - rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_TOTAL_OBJECTS")), SIZET2NUM(record->heap_total_objects)); - rb_hash_aset(prof, ID2SYM(rb_intern("MOVED_OBJECTS")), SIZET2NUM(record->moved_objects)); - rb_hash_aset(prof, ID2SYM(rb_intern("GC_IS_MARKED")), Qtrue); -#if GC_PROFILE_MORE_DETAIL - rb_hash_aset(prof, ID2SYM(rb_intern("GC_MARK_TIME")), DBL2NUM(record->gc_mark_time)); - rb_hash_aset(prof, ID2SYM(rb_intern("GC_SWEEP_TIME")), DBL2NUM(record->gc_sweep_time)); - rb_hash_aset(prof, ID2SYM(rb_intern("ALLOCATE_INCREASE")), SIZET2NUM(record->allocate_increase)); - rb_hash_aset(prof, ID2SYM(rb_intern("ALLOCATE_LIMIT")), SIZET2NUM(record->allocate_limit)); - rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_USE_PAGES")), SIZET2NUM(record->heap_use_pages)); - rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_LIVE_OBJECTS")), SIZET2NUM(record->heap_live_objects)); - rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_FREE_OBJECTS")), SIZET2NUM(record->heap_free_objects)); - - rb_hash_aset(prof, ID2SYM(rb_intern("REMOVING_OBJECTS")), SIZET2NUM(record->removing_objects)); - rb_hash_aset(prof, ID2SYM(rb_intern("EMPTY_OBJECTS")), SIZET2NUM(record->empty_objects)); - - rb_hash_aset(prof, ID2SYM(rb_intern("HAVE_FINALIZE")), RBOOL(record->flags & GPR_FLAG_HAVE_FINALIZE)); -#endif - -#if RGENGC_PROFILE > 0 - rb_hash_aset(prof, ID2SYM(rb_intern("OLD_OBJECTS")), SIZET2NUM(record->old_objects)); - rb_hash_aset(prof, ID2SYM(rb_intern("REMEMBERED_NORMAL_OBJECTS")), SIZET2NUM(record->remembered_normal_objects)); - rb_hash_aset(prof, ID2SYM(rb_intern("REMEMBERED_SHADY_OBJECTS")), SIZET2NUM(record->remembered_shady_objects)); -#endif - rb_ary_push(gc_profile, prof); - } - - return gc_profile; + return rb_gc_impl_config_set(rb_gc_get_objspace(), hash); } -#if GC_PROFILE_MORE_DETAIL -#define MAJOR_REASON_MAX 0x10 - -static char * -gc_profile_dump_major_reason(unsigned int flags, char *buff) +static VALUE +gc_stress_get(rb_execution_context_t *ec, VALUE self) { - unsigned int reason = flags & GPR_FLAG_MAJOR_MASK; - int i = 0; - - if (reason == GPR_FLAG_NONE) { - buff[0] = '-'; - buff[1] = 0; - } - else { -#define C(x, s) \ - if (reason & GPR_FLAG_MAJOR_BY_##x) { \ - buff[i++] = #x[0]; \ - if (i >= MAJOR_REASON_MAX) rb_bug("gc_profile_dump_major_reason: overflow"); \ - buff[i] = 0; \ - } - C(NOFREE, N); - C(OLDGEN, O); - C(SHADY, S); -#if RGENGC_ESTIMATE_OLDMALLOC - C(OLDMALLOC, M); -#endif -#undef C - } - return buff; + return rb_gc_impl_stress_get(rb_gc_get_objspace()); } -#endif -static void -gc_profile_dump_on(VALUE out, VALUE (*append)(VALUE, VALUE)) +static VALUE +gc_stress_set_m(rb_execution_context_t *ec, VALUE self, VALUE flag) { - rb_objspace_t *objspace = &rb_objspace; - size_t count = objspace->profile.next_index; -#ifdef MAJOR_REASON_MAX - char reason_str[MAJOR_REASON_MAX]; -#endif + rb_gc_impl_stress_set(rb_gc_get_objspace(), flag); - if (objspace->profile.run && count /* > 1 */) { - size_t i; - const gc_profile_record *record; + return flag; +} - append(out, rb_sprintf("GC %"PRIuSIZE" invokes.\n", objspace->profile.count)); - append(out, rb_str_new_cstr("Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC Time(ms)\n")); +void +rb_gc_initial_stress_set(VALUE flag) +{ + rb_gc_impl_initial_stress_set(flag); +} - for (i = 0; i < count; i++) { - record = &objspace->profile.records[i]; - append(out, rb_sprintf("%5"PRIuSIZE" %19.3f %20"PRIuSIZE" %20"PRIuSIZE" %20"PRIuSIZE" %30.20f\n", - i+1, record->gc_invoke_time, record->heap_use_size, - record->heap_total_size, record->heap_total_objects, record->gc_time*1000)); - } +size_t * +rb_gc_size_pool_sizes(void) +{ + return rb_gc_impl_size_pool_sizes(rb_gc_get_objspace()); +} -#if GC_PROFILE_MORE_DETAIL - const char *str = "\n\n" \ - "More detail.\n" \ - "Prepare Time = Previously GC's rest sweep time\n" - "Index Flags Allocate Inc. Allocate Limit" -#if CALC_EXACT_MALLOC_SIZE - " Allocated Size" -#endif - " Use Page Mark Time(ms) Sweep Time(ms) Prepare Time(ms) LivingObj FreeObj RemovedObj EmptyObj" -#if RGENGC_PROFILE - " OldgenObj RemNormObj RemShadObj" -#endif -#if GC_PROFILE_DETAIL_MEMORY - " MaxRSS(KB) MinorFLT MajorFLT" -#endif - "\n"; - append(out, rb_str_new_cstr(str)); +VALUE +rb_gc_enable(void) +{ + return rb_objspace_gc_enable(rb_gc_get_objspace()); +} - for (i = 0; i < count; i++) { - record = &objspace->profile.records[i]; - append(out, rb_sprintf("%5"PRIuSIZE" %4s/%c/%6s%c %13"PRIuSIZE" %15"PRIuSIZE -#if CALC_EXACT_MALLOC_SIZE - " %15"PRIuSIZE -#endif - " %9"PRIuSIZE" %17.12f %17.12f %17.12f %10"PRIuSIZE" %10"PRIuSIZE" %10"PRIuSIZE" %10"PRIuSIZE -#if RGENGC_PROFILE - "%10"PRIuSIZE" %10"PRIuSIZE" %10"PRIuSIZE -#endif -#if GC_PROFILE_DETAIL_MEMORY - "%11ld %8ld %8ld" -#endif +VALUE +rb_objspace_gc_enable(void *objspace) +{ + bool disabled = !rb_gc_impl_gc_enabled_p(objspace); + rb_gc_impl_gc_enable(objspace); + return RBOOL(disabled); +} - "\n", - i+1, - gc_profile_dump_major_reason(record->flags, reason_str), - (record->flags & GPR_FLAG_HAVE_FINALIZE) ? 'F' : '.', - (record->flags & GPR_FLAG_NEWOBJ) ? "NEWOBJ" : - (record->flags & GPR_FLAG_MALLOC) ? "MALLOC" : - (record->flags & GPR_FLAG_METHOD) ? "METHOD" : - (record->flags & GPR_FLAG_CAPI) ? "CAPI__" : "??????", - (record->flags & GPR_FLAG_STRESS) ? '!' : ' ', - record->allocate_increase, record->allocate_limit, -#if CALC_EXACT_MALLOC_SIZE - record->allocated_size, -#endif - record->heap_use_pages, - record->gc_mark_time*1000, - record->gc_sweep_time*1000, - record->prepare_time*1000, - - record->heap_live_objects, - record->heap_free_objects, - record->removing_objects, - record->empty_objects -#if RGENGC_PROFILE - , - record->old_objects, - record->remembered_normal_objects, - record->remembered_shady_objects -#endif -#if GC_PROFILE_DETAIL_MEMORY - , - record->maxrss / 1024, - record->minflt, - record->majflt -#endif +static VALUE +gc_enable(rb_execution_context_t *ec, VALUE _) +{ + return rb_gc_enable(); +} - )); - } -#endif - } +static VALUE +gc_disable_no_rest(void *objspace) +{ + bool disabled = !rb_gc_impl_gc_enabled_p(objspace); + rb_gc_impl_gc_disable(objspace, false); + return RBOOL(disabled); } -/* - * call-seq: - * GC::Profiler.result -> String - * - * Returns a profile data report such as: - * - * GC 1 invokes. - * Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC time(ms) - * 1 0.012 159240 212940 10647 0.00000000000001530000 - */ +VALUE +rb_gc_disable_no_rest(void) +{ + return gc_disable_no_rest(rb_gc_get_objspace()); +} -static VALUE -gc_profile_result(VALUE _) +VALUE +rb_gc_disable(void) { - VALUE str = rb_str_buf_new(0); - gc_profile_dump_on(str, rb_str_buf_append); - return str; + return rb_objspace_gc_disable(rb_gc_get_objspace()); } -/* - * call-seq: - * GC::Profiler.report - * GC::Profiler.report(io) - * - * Writes the GC::Profiler.result to $stdout or the given IO object. - * - */ +VALUE +rb_objspace_gc_disable(void *objspace) +{ + bool disabled = !rb_gc_impl_gc_enabled_p(objspace); + rb_gc_impl_gc_disable(objspace, true); + return RBOOL(disabled); +} static VALUE -gc_profile_report(int argc, VALUE *argv, VALUE self) +gc_disable(rb_execution_context_t *ec, VALUE _) { - VALUE out; + return rb_gc_disable(); +} - out = (!rb_check_arity(argc, 0, 1) ? rb_stdout : argv[0]); - gc_profile_dump_on(out, rb_io_write); - return Qnil; + +// TODO: think about moving ruby_gc_set_params into Init_heap or Init_gc +void +ruby_gc_set_params(void) +{ + rb_gc_impl_set_params(rb_gc_get_objspace()); } -/* - * call-seq: - * GC::Profiler.total_time -> float - * - * The total time used for garbage collection in seconds - */ +void +rb_gc_reachable_objects_from_callback(VALUE obj) +{ + rb_ractor_t *cr = GET_RACTOR(); + cr->mfd->mark_func(obj, cr->mfd->data); +} -static VALUE -gc_profile_total_time(VALUE self) +void +rb_objspace_reachable_objects_from(VALUE obj, void (func)(VALUE, void *), void *data) { - double time = 0; - rb_objspace_t *objspace = &rb_objspace; + RB_VM_LOCK_ENTER(); + { + if (rb_gc_impl_during_gc_p(rb_gc_get_objspace())) rb_bug("rb_objspace_reachable_objects_from() is not supported while during GC"); + + if (!RB_SPECIAL_CONST_P(obj)) { + rb_ractor_t *cr = GET_RACTOR(); + struct gc_mark_func_data_struct mfd = { + .mark_func = func, + .data = data, + }, *prev_mfd = cr->mfd; - if (objspace->profile.run && objspace->profile.next_index > 0) { - size_t i; - size_t count = objspace->profile.next_index; + rb_objspace_t *objspace = rb_gc_get_objspace(); - for (i = 0; i < count; i++) { - time += objspace->profile.records[i].gc_time; + cr->mfd = &mfd; + + VALUE already_disabled = rb_objspace_gc_disable(objspace); + rb_gc_mark_children(objspace, obj); + if (already_disabled == Qfalse) rb_objspace_gc_enable(objspace); + + cr->mfd = prev_mfd; } } - return DBL2NUM(time); + RB_VM_LOCK_LEAVE(); } -/* - * call-seq: - * GC::Profiler.enabled? -> true or false - * - * The current status of \GC profile mode. - */ +struct root_objects_data { + const char *category; + void (*func)(const char *category, VALUE, void *); + void *data; +}; -static VALUE -gc_profile_enable_get(VALUE self) +static void +root_objects_from(VALUE obj, void *ptr) { - rb_objspace_t *objspace = &rb_objspace; - return RBOOL(objspace->profile.run); + const struct root_objects_data *data = (struct root_objects_data *)ptr; + (*data->func)(data->category, obj, data->data); } -/* - * call-seq: - * GC::Profiler.enable -> nil - * - * Starts the \GC profiler. - * - */ - -static VALUE -gc_profile_enable(VALUE _) +void +rb_objspace_reachable_objects_from_root(void (func)(const char *category, VALUE, void *), void *passing_data) { - rb_objspace_t *objspace = &rb_objspace; - objspace->profile.run = TRUE; - objspace->profile.current_record = 0; - return Qnil; -} + if (rb_gc_impl_during_gc_p(rb_gc_get_objspace())) rb_bug("rb_gc_impl_objspace_reachable_objects_from_root() is not supported while during GC"); -/* - * call-seq: - * GC::Profiler.disable -> nil - * - * Stops the \GC profiler. - * - */ + rb_ractor_t *cr = GET_RACTOR(); + struct root_objects_data data = { + .func = func, + .data = passing_data, + }; + struct gc_mark_func_data_struct mfd = { + .mark_func = root_objects_from, + .data = &data, + }, *prev_mfd = cr->mfd; -static VALUE -gc_profile_disable(VALUE _) -{ - rb_objspace_t *objspace = &rb_objspace; + cr->mfd = &mfd; - objspace->profile.run = FALSE; - objspace->profile.current_record = 0; - return Qnil; + rb_objspace_t *objspace = rb_gc_get_objspace(); + + HEAP_LOCK_ENTER(objspace); + { + VALUE already_disabled = rb_objspace_gc_disable(objspace); + rb_gc_mark_roots(objspace, &data.category); + if (already_disabled == Qfalse) rb_objspace_gc_enable(objspace); + } + HEAP_LOCK_LEAVE(objspace); + + cr->mfd = prev_mfd; } /* @@ -14383,25 +3964,26 @@ rb_raw_obj_info_common(char *const buff, const size_t buff_size, const VALUE obj } } else { - const int age = RVALUE_AGE_GET(obj); - - if (is_pointer_to_heap(&rb_objspace, (void *)obj)) { - APPEND_F("%p [%d%s%s%s%s%s%s] %s ", - (void *)obj, age, - C(RVALUE_UNCOLLECTIBLE_BITMAP(obj), "L"), - C(RVALUE_MARKED_BITMAP(obj), "M"), - C(RVALUE_PINNED_BITMAP(obj), "P"), - C(RVALUE_MARKING_BITMAP(obj), "R"), - C(RVALUE_WB_UNPROTECTED_BITMAP(obj), "U"), - C(RVALUE_LOCAL_IMMUNE_BITMAP(obj), "I"), - C(rb_objspace_garbage_object_p(obj), "G"), - obj_type_name(obj)); + // const int age = RVALUE_AGE_GET(obj); + + if (rb_gc_impl_pointer_to_heap_p(rb_gc_get_objspace(), (void *)obj)) { + // TODO: fixme + // APPEND_F("%p [%d%s%s%s%s%s%s] %s ", + // (void *)obj, age, + // C(RVALUE_UNCOLLECTIBLE_BITMAP(obj), "L"), + // C(RVALUE_MARK_BITMAP(obj), "M"), + // C(RVALUE_PIN_BITMAP(obj), "P"), + // C(RVALUE_MARKING_BITMAP(obj), "R"), + // C(RVALUE_WB_UNPROTECTED_BITMAP(obj), "U"), + // C(RVALUE_LOCAL_IMMUNE_BITMAP(obj), "I"), + // C(rb_objspace_garbage_object_p(obj), "G"), + // obj_type_name(obj)); } else { /* fake */ - APPEND_F("%p [%dXXXX] %s", - (void *)obj, age, - obj_type_name(obj)); + // APPEND_F("%p [%dXXXX] %s", + // (void *)obj, age, + // obj_type_name(obj)); } if (internal_object_p(obj)) { @@ -14416,16 +3998,14 @@ rb_raw_obj_info_common(char *const buff, const size_t buff_size, const VALUE obj APPEND_F("(%s)", RSTRING_PTR(class_path)); } } - -#if GC_DEBUG - APPEND_F("@%s:%d", GET_RVALUE_OVERHEAD(obj)->file, GET_RVALUE_OVERHEAD(obj)->line); -#endif } end: return pos; } +const char *rb_raw_obj_info(char *const buff, const size_t buff_size, VALUE obj); + static size_t rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALUE obj, size_t pos) { @@ -14480,7 +4060,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU break; } case T_MOVED: { - APPEND_F("-> %p", (void*)rb_gc_location(obj)); + APPEND_F("-> %p", (void*)rb_gc_impl_location(rb_gc_get_objspace(), obj)); break; } case T_HASH: { @@ -14518,7 +4098,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU else { uint32_t len = ROBJECT_IV_CAPACITY(obj); - if (RANY(obj)->as.basic.flags & ROBJECT_EMBED) { + if (RBASIC(obj)->flags & ROBJECT_EMBED) { APPEND_F("(embed) len:%d", len); } else { @@ -14557,7 +4137,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU switch (imemo_type(obj)) { case imemo_ment: { - const rb_method_entry_t *me = &RANY(obj)->as.imemo.ment; + const rb_method_entry_t *me = (const rb_method_entry_t *)obj; APPEND_F(":%s (%s%s%s%s) type:%s aliased:%d owner:%p defined_class:%p", rb_id2name(me->called_id), @@ -14629,6 +4209,12 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU #undef C +#define asan_unpoisoning_object(obj) \ + for (void *poisoned = asan_unpoison_object_temporary(obj), \ + *unpoisoning = &poisoned; /* flag to loop just once */ \ + unpoisoning; \ + unpoisoning = asan_poison_object_restore(obj, poisoned)) + const char * rb_raw_obj_info(char *const buff, const size_t buff_size, VALUE obj) { @@ -14657,7 +4243,7 @@ static rb_atomic_t atomic_inc_wraparound(rb_atomic_t *var, const rb_atomic_t maxval) { rb_atomic_t oldval = RUBY_ATOMIC_FETCH_ADD(*var, 1); - if (UNLIKELY(oldval >= maxval - 1)) { // wraparound *var + if (RB_UNLIKELY(oldval >= maxval - 1)) { // wraparound *var const rb_atomic_t newval = oldval + 1; RUBY_ATOMIC_CAS(*var, newval, newval % maxval); oldval %= maxval; @@ -14665,171 +4251,352 @@ atomic_inc_wraparound(rb_atomic_t *var, const rb_atomic_t maxval) return oldval; } -static const char * -obj_info(VALUE obj) +static const char * +obj_info(VALUE obj) +{ + RB_VM_LOCK_ENTER(); + { + rb_atomic_t index = atomic_inc_wraparound(&obj_info_buffers_index, OBJ_INFO_BUFFERS_NUM); + char *const buff = obj_info_buffers[index]; + const char *ret = rb_raw_obj_info(buff, OBJ_INFO_BUFFERS_SIZE, obj); + } + RB_VM_LOCK_LEAVE(); + return ret; +} +#else +static const char * +obj_info(VALUE obj) +{ + return obj_type_name(obj); +} +#endif + +/* + ------------------------ Extended allocator ------------------------ +*/ + +struct gc_raise_tag { + VALUE exc; + const char *fmt; + va_list *ap; +}; + +static void * +gc_vraise(void *ptr) +{ + struct gc_raise_tag *argv = ptr; + rb_vraise(argv->exc, argv->fmt, *argv->ap); + UNREACHABLE_RETURN(NULL); +} + +static void +gc_raise(VALUE exc, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + struct gc_raise_tag argv = { + exc, fmt, &ap, + }; + + if (ruby_thread_has_gvl_p()) { + gc_vraise(&argv); + UNREACHABLE; + } + else if (ruby_native_thread_p()) { + rb_thread_call_with_gvl(gc_vraise, &argv); + UNREACHABLE; + } + else { + /* Not in a ruby thread */ + fprintf(stderr, "%s", "[FATAL] "); + vfprintf(stderr, fmt, ap); + } + + va_end(ap); + abort(); +} + +NORETURN(static void negative_size_allocation_error(const char *)); +static void +negative_size_allocation_error(const char *msg) +{ + gc_raise(rb_eNoMemError, "%s", msg); +} + +static void * +ruby_memerror_body(void *dummy) +{ + rb_memerror(); + return 0; +} + +NORETURN(static void ruby_memerror(void)); +RBIMPL_ATTR_MAYBE_UNUSED() +static void +ruby_memerror(void) +{ + if (ruby_thread_has_gvl_p()) { + rb_memerror(); + } + else { + if (ruby_native_thread_p()) { + rb_thread_call_with_gvl(ruby_memerror_body, 0); + } + else { + /* no ruby thread */ + fprintf(stderr, "[FATAL] failed to allocate memory\n"); + } + } + exit(EXIT_FAILURE); +} + +void +rb_memerror(void) +{ + rb_execution_context_t *ec = GET_EC(); + VALUE exc = GET_VM()->special_exceptions[ruby_error_nomemory]; + + if (!exc || + rb_ec_raised_p(ec, RAISED_NOMEMORY)) { + fprintf(stderr, "[FATAL] failed to allocate memory\n"); + exit(EXIT_FAILURE); + } + if (rb_ec_raised_p(ec, RAISED_NOMEMORY)) { + rb_ec_raised_clear(ec); + } + else { + rb_ec_raised_set(ec, RAISED_NOMEMORY); + exc = ruby_vm_special_exception_copy(exc); + } + ec->errinfo = exc; + EC_JUMP_TAG(ec, TAG_RAISE); +} + +void +rb_malloc_info_show_results(void) +{ +} + +void * +ruby_xmalloc(size_t size) +{ + if ((ssize_t)size < 0) { + negative_size_allocation_error("too large allocation size"); + } + + return rb_gc_impl_malloc(rb_gc_get_objspace(), size); +} + +void +ruby_malloc_size_overflow(size_t count, size_t elsize) +{ + rb_raise(rb_eArgError, + "malloc: possible integer overflow (%"PRIuSIZE"*%"PRIuSIZE")", + count, elsize); +} + +static inline size_t +xmalloc2_size(const size_t count, const size_t elsize) { - RB_VM_LOCK_ENTER(); - { - rb_atomic_t index = atomic_inc_wraparound(&obj_info_buffers_index, OBJ_INFO_BUFFERS_NUM); - char *const buff = obj_info_buffers[index]; - const char *ret = rb_raw_obj_info(buff, OBJ_INFO_BUFFERS_SIZE, obj); - } - RB_VM_LOCK_LEAVE(); - return ret; + return size_mul_or_raise(count, elsize, rb_eArgError); } -static const char * -obj_info_basic(VALUE obj) +void * +ruby_xmalloc2(size_t n, size_t size) { - const char *ret; - RB_VM_LOCK_ENTER(); - { - rb_atomic_t index = atomic_inc_wraparound(&obj_info_buffers_index, OBJ_INFO_BUFFERS_NUM); - char *const buff = obj_info_buffers[index]; - - asan_unpoisoning_object(obj) { - rb_raw_obj_info_common(buff, OBJ_INFO_BUFFERS_SIZE, obj); - } + return rb_gc_impl_malloc(rb_gc_get_objspace(), xmalloc2_size(n, size)); +} - ret = buff; - } - RB_VM_LOCK_LEAVE(); - return ret; +void * +ruby_xcalloc(size_t n, size_t size) +{ + return rb_gc_impl_calloc(rb_gc_get_objspace(), xmalloc2_size(n, size)); } -#else -static const char * -obj_info(VALUE obj) + +#ifdef ruby_sized_xrealloc +#undef ruby_sized_xrealloc +#endif +void * +ruby_sized_xrealloc(void *ptr, size_t new_size, size_t old_size) { - return obj_type_name(obj); + if ((ssize_t)new_size < 0) { + negative_size_allocation_error("too large allocation size"); + } + + return rb_gc_impl_realloc(rb_gc_get_objspace(), ptr, new_size, old_size); } -static const char * -obj_info_basic(VALUE obj) +void * +ruby_xrealloc(void *ptr, size_t new_size) { - return obj_type_name(obj); + return ruby_sized_xrealloc(ptr, new_size, 0); } +#ifdef ruby_sized_xrealloc2 +#undef ruby_sized_xrealloc2 #endif +void * +ruby_sized_xrealloc2(void *ptr, size_t n, size_t size, size_t old_n) +{ + size_t len = xmalloc2_size(n, size); + return rb_gc_impl_realloc(rb_gc_get_objspace(), ptr, len, old_n * size); +} -const char * -rb_obj_info(VALUE obj) +void * +ruby_xrealloc2(void *ptr, size_t n, size_t size) { - return obj_info(obj); + return ruby_sized_xrealloc2(ptr, n, size, 0); } +#ifdef ruby_sized_xfree +#undef ruby_sized_xfree +#endif void -rb_obj_info_dump(VALUE obj) +ruby_sized_xfree(void *x, size_t size) { - char buff[0x100]; - fprintf(stderr, "rb_obj_info_dump: %s\n", rb_raw_obj_info(buff, 0x100, obj)); + if (LIKELY(x)) { + /* It's possible for a C extension's pthread destructor function set by pthread_key_create + * to be called after ruby_vm_destruct and attempt to free memory. Fall back to mimfree in + * that case. */ + if (LIKELY(GET_VM())) { + rb_gc_impl_free(rb_gc_get_objspace(), x, size); + } + else { + ruby_mimfree(x); + } + } } void -rb_obj_info_dump_loc(VALUE obj, const char *file, int line, const char *func) +ruby_xfree(void *x) { - char buff[0x100]; - fprintf(stderr, " %s\n", func, file, line, rb_raw_obj_info(buff, 0x100, obj)); + ruby_sized_xfree(x, 0); } -#if GC_DEBUG +void * +rb_xmalloc_mul_add(size_t x, size_t y, size_t z) /* x * y + z */ +{ + size_t w = size_mul_add_or_raise(x, y, z, rb_eArgError); + return ruby_xmalloc(w); +} -void -rb_gcdebug_print_obj_condition(VALUE obj) +void * +rb_xcalloc_mul_add(size_t x, size_t y, size_t z) /* x * y + z */ { - rb_objspace_t *objspace = &rb_objspace; + size_t w = size_mul_add_or_raise(x, y, z, rb_eArgError); + return ruby_xcalloc(w, 1); +} - fprintf(stderr, "created at: %s:%d\n", GET_RVALUE_OVERHEAD(obj)->file, GET_RVALUE_OVERHEAD(obj)->line); +void * +rb_xrealloc_mul_add(const void *p, size_t x, size_t y, size_t z) /* x * y + z */ +{ + size_t w = size_mul_add_or_raise(x, y, z, rb_eArgError); + return ruby_xrealloc((void *)p, w); +} - if (BUILTIN_TYPE(obj) == T_MOVED) { - fprintf(stderr, "moved?: true\n"); - } - else { - fprintf(stderr, "moved?: false\n"); - } - if (is_pointer_to_heap(objspace, (void *)obj)) { - fprintf(stderr, "pointer to heap?: true\n"); - } - else { - fprintf(stderr, "pointer to heap?: false\n"); - return; - } +void * +rb_xmalloc_mul_add_mul(size_t x, size_t y, size_t z, size_t w) /* x * y + z * w */ +{ + size_t u = size_mul_add_mul_or_raise(x, y, z, w, rb_eArgError); + return ruby_xmalloc(u); +} - fprintf(stderr, "marked? : %s\n", RVALUE_MARKED(obj) ? "true" : "false"); - fprintf(stderr, "pinned? : %s\n", RVALUE_PINNED(obj) ? "true" : "false"); - fprintf(stderr, "age? : %d\n", RVALUE_AGE_GET(obj)); - fprintf(stderr, "old? : %s\n", RVALUE_OLD_P(obj) ? "true" : "false"); - fprintf(stderr, "WB-protected? : %s\n", RVALUE_WB_UNPROTECTED(obj) ? "false" : "true"); - fprintf(stderr, "local-immune? : %s\n", RVALUE_LOCAL_IMMUNE(obj) ? "false" : "true"); - fprintf(stderr, "remembered? : %s\n", RVALUE_REMEMBERED(obj) ? "true" : "false"); +void * +rb_xcalloc_mul_add_mul(size_t x, size_t y, size_t z, size_t w) /* x * y + z * w */ +{ + size_t u = size_mul_add_mul_or_raise(x, y, z, w, rb_eArgError); + return ruby_xcalloc(u, 1); +} - if (is_lazy_sweeping(objspace)) { - fprintf(stderr, "lazy sweeping?: true\n"); - fprintf(stderr, "page swept?: %s\n", GET_HEAP_PAGE(obj)->flags.before_sweep ? "false" : "true"); +/* Mimic ruby_xmalloc, but need not rb_objspace. + * should return pointer suitable for ruby_xfree + */ +void * +ruby_mimmalloc(size_t size) +{ + void *mem; +#if CALC_EXACT_MALLOC_SIZE + size += sizeof(struct malloc_obj_info); +#endif + mem = malloc(size); +#if CALC_EXACT_MALLOC_SIZE + if (!mem) { + return NULL; } - else { - fprintf(stderr, "lazy sweeping?: false\n"); + else + /* set 0 for consistency of allocated_size/allocations */ + { + struct malloc_obj_info *info = mem; + info->size = 0; + mem = info + 1; } +#endif + return mem; } -static VALUE -gcdebug_sentinel(RB_BLOCK_CALL_FUNC_ARGLIST(obj, name)) +void * +ruby_mimcalloc(size_t num, size_t size) { - fprintf(stderr, "WARNING: object %s(%p) is inadvertently collected\n", (char *)name, (void *)obj); - return Qnil; + 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; + mem = info + 1; + } +#else + mem = calloc(num, size); +#endif + return mem; } void -rb_gcdebug_sentinel(VALUE obj, const char *name) +ruby_mimfree(void *ptr) { - rb_define_finalizer(obj, rb_proc_new(gcdebug_sentinel, (VALUE)name)); +#if CALC_EXACT_MALLOC_SIZE + struct malloc_obj_info *info = (struct malloc_obj_info *)ptr - 1; + ptr = info; +#endif + free(ptr); } -#endif /* GC_DEBUG */ - -/* :nodoc: - * - * call-seq: - * GC.add_stress_to_class(class[, ...]) - * - * Raises NoMemoryError when allocating an instance of the given classes. - * - */ -static VALUE -rb_gcdebug_add_stress_to_class(int argc, VALUE *argv, VALUE self) +void +rb_gc_adjust_memory_usage(ssize_t diff) { - rb_objspace_t *objspace = &rb_objspace; + unless_objspace(objspace) { return; } - if (!stress_to_class) { - set_stress_to_class(rb_ary_hidden_new(argc)); - } - rb_ary_cat(stress_to_class, argv, argc); - return self; + rb_gc_impl_adjust_memory_usage(objspace, diff); } -/* :nodoc: - * - * call-seq: - * GC.remove_stress_to_class(class[, ...]) - * - * No longer raises NoMemoryError when allocating an instance of the - * given classes. - * - */ -static VALUE -rb_gcdebug_remove_stress_to_class(int argc, VALUE *argv, VALUE self) +const char * +rb_obj_info(VALUE obj) { - rb_objspace_t *objspace = &rb_objspace; - int i; + return obj_info(obj); +} - if (stress_to_class) { - for (i = 0; i < argc; ++i) { - rb_ary_delete_same(stress_to_class, argv[i]); - } - if (RARRAY_LEN(stress_to_class) == 0) { - set_stress_to_class(0); - } - } - return Qnil; +void +rb_obj_info_dump(VALUE obj) +{ + char buff[0x100]; + fprintf(stderr, "rb_obj_info_dump: %s\n", rb_raw_obj_info(buff, 0x100, obj)); +} + +void +rb_obj_info_dump_loc(VALUE obj, const char *file, int line, const char *func) +{ + char buff[0x100]; + fprintf(stderr, " %s\n", func, file, line, rb_raw_obj_info(buff, 0x100, obj)); } /* @@ -14885,41 +4652,9 @@ Init_GC(void) #undef rb_intern malloc_offset = gc_compute_malloc_offset(); - VALUE rb_mObjSpace; - VALUE rb_mProfiler; - VALUE gc_constants; - rb_mGC = rb_define_module("GC"); - gc_constants = rb_hash_new(); - 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(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)); - rb_hash_aset(gc_constants, ID2SYM(rb_intern("SIZE_POOL_COUNT")), LONG2FIX(SIZE_POOL_COUNT)); - rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVARGC_MAX_ALLOCATE_SIZE")), LONG2FIX(size_pool_slot_size(SIZE_POOL_COUNT - 1))); - rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_OLD_AGE")), LONG2FIX(RVALUE_OLD_AGE)); - if (RB_BUG_INSTEAD_OF_RB_MEMERROR+0) { - rb_hash_aset(gc_constants, ID2SYM(rb_intern("RB_BUG_INSTEAD_OF_RB_MEMERROR")), Qtrue); - } - OBJ_FREEZE(gc_constants); - /* Internal constants in the garbage collector. */ - rb_define_const(rb_mGC, "INTERNAL_CONSTANTS", gc_constants); - - rb_mProfiler = rb_define_module_under(rb_mGC, "Profiler"); - rb_define_singleton_method(rb_mProfiler, "enabled?", gc_profile_enable_get, 0); - rb_define_singleton_method(rb_mProfiler, "enable", gc_profile_enable, 0); - rb_define_singleton_method(rb_mProfiler, "raw_data", gc_profile_record_get, 0); - rb_define_singleton_method(rb_mProfiler, "disable", gc_profile_disable, 0); - rb_define_singleton_method(rb_mProfiler, "clear", gc_profile_clear, 0); - rb_define_singleton_method(rb_mProfiler, "result", gc_profile_result, 0); - rb_define_singleton_method(rb_mProfiler, "report", gc_profile_report, -1); - rb_define_singleton_method(rb_mProfiler, "total_time", gc_profile_total_time, 0); - - rb_mObjSpace = rb_define_module("ObjectSpace"); + VALUE rb_mObjSpace = rb_define_module("ObjectSpace"); rb_define_module_function(rb_mObjSpace, "each_object", os_each_obj, -1); @@ -14938,118 +4673,5 @@ Init_GC(void) rb_define_module_function(rb_mObjSpace, "count_objects", count_objects, -1); - /* internal methods */ - rb_define_singleton_method(rb_mGC, "verify_internal_consistency", gc_verify_internal_consistency_m, 0); -#if MALLOC_ALLOCATED_SIZE - rb_define_singleton_method(rb_mGC, "malloc_allocated_size", gc_malloc_allocated_size, 0); - rb_define_singleton_method(rb_mGC, "malloc_allocations", gc_malloc_allocations, 0); -#endif - - if (GC_COMPACTION_SUPPORTED) { - rb_define_singleton_method(rb_mGC, "compact", gc_compact, 0); - rb_define_singleton_method(rb_mGC, "auto_compact", gc_get_auto_compact, 0); - rb_define_singleton_method(rb_mGC, "auto_compact=", gc_set_auto_compact, 1); - rb_define_singleton_method(rb_mGC, "latest_compact_info", gc_compact_stats, 0); - } - else { - rb_define_singleton_method(rb_mGC, "compact", rb_f_notimplement, 0); - rb_define_singleton_method(rb_mGC, "auto_compact", rb_f_notimplement, 0); - rb_define_singleton_method(rb_mGC, "auto_compact=", rb_f_notimplement, 1); - rb_define_singleton_method(rb_mGC, "latest_compact_info", rb_f_notimplement, 0); - /* When !GC_COMPACTION_SUPPORTED, this method is not defined in gc.rb */ - rb_define_singleton_method(rb_mGC, "verify_compaction_references", rb_f_notimplement, -1); - } - - if (GC_DEBUG_STRESS_TO_CLASS) { - rb_define_singleton_method(rb_mGC, "add_stress_to_class", rb_gcdebug_add_stress_to_class, -1); - rb_define_singleton_method(rb_mGC, "remove_stress_to_class", rb_gcdebug_remove_stress_to_class, -1); - } - - { - VALUE opts; - /* \GC build options */ - rb_define_const(rb_mGC, "OPTS", opts = rb_ary_new()); -#define OPT(o) if (o) rb_ary_push(opts, rb_fstring_lit(#o)) - OPT(GC_DEBUG); - OPT(USE_RGENGC); - OPT(RGENGC_DEBUG); - OPT(RGENGC_CHECK_MODE); - OPT(RGENGC_PROFILE); - OPT(RGENGC_ESTIMATE_OLDMALLOC); - OPT(GC_PROFILE_MORE_DETAIL); - OPT(GC_ENABLE_LAZY_SWEEP); - OPT(CALC_EXACT_MALLOC_SIZE); - OPT(MALLOC_ALLOCATED_SIZE); - OPT(MALLOC_ALLOCATED_SIZE_CHECK); - OPT(GC_PROFILE_DETAIL_MEMORY); - OPT(GC_COMPACTION_SUPPORTED); -#undef OPT - OBJ_FREEZE(opts); - } -} - -#ifdef ruby_xmalloc -#undef ruby_xmalloc -#endif -#ifdef ruby_xmalloc2 -#undef ruby_xmalloc2 -#endif -#ifdef ruby_xcalloc -#undef ruby_xcalloc -#endif -#ifdef ruby_xrealloc -#undef ruby_xrealloc -#endif -#ifdef ruby_xrealloc2 -#undef ruby_xrealloc2 -#endif - -void * -ruby_xmalloc(size_t size) -{ -#if USE_GC_MALLOC_OBJ_INFO_DETAILS - ruby_malloc_info_file = __FILE__; - ruby_malloc_info_line = __LINE__; -#endif - return ruby_xmalloc_body(size); -} - -void * -ruby_xmalloc2(size_t n, size_t size) -{ -#if USE_GC_MALLOC_OBJ_INFO_DETAILS - ruby_malloc_info_file = __FILE__; - ruby_malloc_info_line = __LINE__; -#endif - return ruby_xmalloc2_body(n, size); -} - -void * -ruby_xcalloc(size_t n, size_t size) -{ -#if USE_GC_MALLOC_OBJ_INFO_DETAILS - ruby_malloc_info_file = __FILE__; - ruby_malloc_info_line = __LINE__; -#endif - return ruby_xcalloc_body(n, size); -} - -void * -ruby_xrealloc(void *ptr, size_t new_size) -{ -#if USE_GC_MALLOC_OBJ_INFO_DETAILS - ruby_malloc_info_file = __FILE__; - ruby_malloc_info_line = __LINE__; -#endif - return ruby_xrealloc_body(ptr, new_size); -} - -void * -ruby_xrealloc2(void *ptr, size_t n, size_t new_size) -{ -#if USE_GC_MALLOC_OBJ_INFO_DETAILS - ruby_malloc_info_file = __FILE__; - ruby_malloc_info_line = __LINE__; -#endif - return ruby_xrealloc2_body(ptr, n, new_size); + rb_gc_impl_init(); } diff --git a/gc.rb b/gc.rb index eb98e0fb787260..9d0a1647cc7210 100644 --- a/gc.rb +++ b/gc.rb @@ -252,6 +252,62 @@ def self.stat_heap heap_name = nil, hash_or_key = nil Primitive.gc_stat_heap heap_name, hash_or_key end + # call-seq: + # GC.config -> hash + # GC.config(hash) -> hash + # + # Sets or gets information about the current GC config. + # + # Configuration parameters are GC implementation specific and may change + # without notice. + # + # This method can be called without parameters to retrieve the current config. + # + # This method can also be called with a +Hash+ argument to assign values to + # valid config keys. Config keys missing from the passed +Hash+ will be left + # unmodified. + # + # If a key/value pair is passed to this function that does not correspond to + # a valid config key for the GC implementation being used, no config will be + # updated, the key will be present in the returned Hash, and it's value will + # be +nil+. This is to facilitate easy migration between GC implementations. + # + # In both call-seqs the return value of GC.config will be a +Hash+ + # containing the most recent full configuration. ie. All keys and values + # defined by the specific GC implementation being used. In the case of a + # config update, the return value will include the new values being updated. + # + # This method is only expected to work on CRuby. + # + # Valid config keys for Ruby's default GC implementation are: + # + # [rgengc_allow_full_mark] + # Control whether the GC is allowed to run a full mark (young & old objects). + # + # When +true+ GC interleaves major and minor collections. This is default. GC + # will function as intended. + # + # When +false+, the GC will never trigger a full marking cycle unless + # explicitly requested by user code. Instead only a minor mark will run - + # only young objects will be marked. When the heap space is exhausted, new + # pages will be allocated immediately instead of running a full mark. + # + # A flag will be set to notify that a full mark has been + # requested. This flag is accessible using + # GC.latest_gc_info(:needs_major_by) + # + # The user can trigger a major collection at any time using + # GC.start(full_mark: true) + # + # When +false+. Young to Old object promotion is disabled. For performance + # reasons it is recommended to warmup an application using +Process.warmup+ + # before setting this parameter to +false+. + def self.config hash = nil + return Primitive.gc_config_get unless hash + + Primitive.gc_config_set hash + end + # call-seq: # GC.latest_gc_info -> hash # GC.latest_gc_info(hash) -> hash @@ -266,26 +322,6 @@ def self.latest_gc_info hash_or_key = nil Primitive.gc_latest_gc_info hash_or_key end - if respond_to?(:compact) - # call-seq: - # GC.verify_compaction_references(toward: nil, double_heap: false) -> hash - # - # Verify compaction reference consistency. - # - # This method is implementation specific. During compaction, objects that - # were moved are replaced with T_MOVED objects. No object should have a - # reference to a T_MOVED object after compaction. - # - # This function expands the heap to ensure room to move all objects, - # compacts the heap to make sure everything moves, updates all references, - # then performs a full \GC. If any object contains a reference to a T_MOVED - # object, that object should be pushed on the mark stack, and will - # make a SEGV. - def self.verify_compaction_references(toward: nil, double_heap: false, expand_heap: false) - Primitive.gc_verify_compaction_references(double_heap, expand_heap, toward == :empty) - end - end - # call-seq: # GC.measure_total_time = true/false # @@ -294,8 +330,7 @@ def self.verify_compaction_references(toward: nil, double_heap: false, expand_he # Note that \GC time measurement can cause some performance overhead. def self.measure_total_time=(flag) Primitive.cstmt! %{ - rb_objspace.flags.measure_gc = RTEST(flag) ? TRUE : FALSE; - return flag; + return rb_gc_impl_set_measure_total_time(rb_gc_get_objspace(), flag); } end @@ -306,7 +341,7 @@ def self.measure_total_time=(flag) # Note that measurement can affect the application performance. def self.measure_total_time Primitive.cexpr! %{ - RBOOL(rb_objspace.flags.measure_gc) + rb_gc_impl_get_measure_total_time(rb_gc_get_objspace()) } end @@ -316,7 +351,7 @@ def self.measure_total_time # Return measured \GC total time in nano seconds. def self.total_time Primitive.cexpr! %{ - ULL2NUM(rb_objspace.profile.marking_time_ns + rb_objspace.profile.sweeping_time_ns) + rb_gc_impl_get_profile_total_time(rb_gc_get_objspace()) } end end diff --git a/gc/default.c b/gc/default.c new file mode 100644 index 00000000000000..9492985d55997c --- /dev/null +++ b/gc/default.c @@ -0,0 +1,10943 @@ +#include "ruby/internal/config.h" + +#include + +#ifndef _WIN32 +# include +# include +#endif + +#if !defined(PAGE_SIZE) && defined(HAVE_SYS_USER_H) +/* LIST_HEAD conflicts with sys/queue.h on macOS */ +# include +#endif + +#include "internal/hash.h" + +#include "ruby/ruby.h" +#include "ruby/atomic.h" +#include "ruby/debug.h" +#include "ruby/thread.h" +#include "ruby/util.h" +#include "ruby/vm.h" +#include "ruby/internal/encoding/string.h" +#include "ccan/list/list.h" +#include "darray.h" +#include "gc/gc.h" +#include "gc/gc_impl.h" +#include "glospace.h" + +#ifndef BUILDING_SHARED_GC +# include "probes.h" +#endif + +#include "debug_counter.h" +#include "internal/sanitizers.h" + +/* MALLOC_HEADERS_BEGIN */ +#ifndef HAVE_MALLOC_USABLE_SIZE +# ifdef _WIN32 +# define HAVE_MALLOC_USABLE_SIZE +# define malloc_usable_size(a) _msize(a) +# elif defined HAVE_MALLOC_SIZE +# define HAVE_MALLOC_USABLE_SIZE +# define malloc_usable_size(a) malloc_size(a) +# endif +#endif + +#ifdef HAVE_MALLOC_USABLE_SIZE +# ifdef RUBY_ALTERNATIVE_MALLOC_HEADER +/* Alternative malloc header is included in ruby/missing.h */ +# elif defined(HAVE_MALLOC_H) +# include +# elif defined(HAVE_MALLOC_NP_H) +# include +# elif defined(HAVE_MALLOC_MALLOC_H) +# include +# endif +#endif + +#ifdef HAVE_MALLOC_TRIM +# include + +# ifdef __EMSCRIPTEN__ +/* malloc_trim is defined in emscripten/emmalloc.h on emscripten. */ +# include +# endif +#endif + +#ifdef HAVE_MACH_TASK_EXCEPTION_PORTS +# include +# include +# include +#endif + +#ifndef VM_CHECK_MODE +# define VM_CHECK_MODE RUBY_DEBUG +#endif + +// From ractor_core.h +#ifndef RACTOR_CHECK_MODE +# define RACTOR_CHECK_MODE (VM_CHECK_MODE || RUBY_DEBUG) && (SIZEOF_UINT64_T == SIZEOF_VALUE) +#endif + +#ifndef RUBY_DEBUG_LOG +# define RUBY_DEBUG_LOG(...) +#endif + +#ifndef GC_HEAP_INIT_SLOTS +#define GC_HEAP_INIT_SLOTS 10000 +#endif +#ifndef GC_HEAP_FREE_SLOTS +#define GC_HEAP_FREE_SLOTS 4096 +#endif +#ifndef GC_HEAP_GROWTH_FACTOR +#define GC_HEAP_GROWTH_FACTOR 1.8 +#endif +#ifndef GC_HEAP_GROWTH_MAX_SLOTS +#define GC_HEAP_GROWTH_MAX_SLOTS 0 /* 0 is disable */ +#endif +#ifndef GC_HEAP_REMEMBERED_WB_UNPROTECTED_OBJECTS_LIMIT_RATIO +# define GC_HEAP_REMEMBERED_WB_UNPROTECTED_OBJECTS_LIMIT_RATIO 0.01 +#endif +#ifndef GC_HEAP_OLDOBJECT_LIMIT_FACTOR +#define GC_HEAP_OLDOBJECT_LIMIT_FACTOR 2.0 +#endif +#ifndef GC_HEAP_SHAREDOBJECT_LIMIT_FACTOR +#define GC_HEAP_SHAREDOBJECT_LIMIT_FACTOR 2.0 +#endif + +#ifndef GC_HEAP_FREE_SLOTS_MIN_RATIO +#define GC_HEAP_FREE_SLOTS_MIN_RATIO 0.20 +#endif +#ifndef GC_HEAP_FREE_SLOTS_GOAL_RATIO +#define GC_HEAP_FREE_SLOTS_GOAL_RATIO 0.40 +#endif +#ifndef GC_HEAP_FREE_SLOTS_MAX_RATIO +#define GC_HEAP_FREE_SLOTS_MAX_RATIO 0.65 +#endif + +#ifndef GC_MALLOC_LIMIT_MIN +#define GC_MALLOC_LIMIT_MIN (16 * 1024 * 1024 /* 16MB */) +#endif +#ifndef GC_MALLOC_LIMIT_MAX +#define GC_MALLOC_LIMIT_MAX (32 * 1024 * 1024 /* 32MB */) +#endif +#ifndef GC_MALLOC_LIMIT_GROWTH_FACTOR +#define GC_MALLOC_LIMIT_GROWTH_FACTOR 1.4 +#endif + +#ifndef GC_OLDMALLOC_LIMIT_MIN +#define GC_OLDMALLOC_LIMIT_MIN (16 * 1024 * 1024 /* 16MB */) +#endif +#ifndef GC_OLDMALLOC_LIMIT_GROWTH_FACTOR +#define GC_OLDMALLOC_LIMIT_GROWTH_FACTOR 1.2 +#endif +#ifndef GC_OLDMALLOC_LIMIT_MAX +#define GC_OLDMALLOC_LIMIT_MAX (128 * 1024 * 1024 /* 128MB */) +#endif + +#ifndef GC_CAN_COMPILE_COMPACTION +#if defined(__wasi__) /* WebAssembly doesn't support signals */ +# define GC_CAN_COMPILE_COMPACTION 0 +#else +# define GC_CAN_COMPILE_COMPACTION 1 +#endif +#endif + +#ifndef PRINT_ENTER_EXIT_TICK +# define PRINT_ENTER_EXIT_TICK 0 +#endif +#ifndef PRINT_ROOT_TICKS +#define PRINT_ROOT_TICKS 0 +#endif + +#define USE_TICK_T (PRINT_ENTER_EXIT_TICK || PRINT_MEASURE_LINE || PRINT_ROOT_TICKS) + +#ifndef SIZE_POOL_COUNT +# define SIZE_POOL_COUNT 5 +#endif + +typedef struct ractor_newobj_size_pool_cache { + struct free_slot *freelist; + struct heap_page *using_page; +} rb_ractor_newobj_size_pool_cache_t; + +typedef struct ractor_newobj_cache { + size_t incremental_mark_step_allocated_slots; + rb_ractor_newobj_size_pool_cache_t size_pool_caches[SIZE_POOL_COUNT]; +} rb_ractor_newobj_cache_t; + +typedef struct { + size_t size_pool_init_slots[SIZE_POOL_COUNT]; + size_t heap_free_slots; + double growth_factor; + size_t growth_max_slots; + + double heap_free_slots_min_ratio; + double heap_free_slots_goal_ratio; + double heap_free_slots_max_ratio; + double uncollectible_wb_unprotected_objects_limit_ratio; + double oldobject_limit_factor; + double sharedobject_limit_factor; + + size_t malloc_limit_min; + size_t malloc_limit_max; + double malloc_limit_growth_factor; + + size_t oldmalloc_limit_min; + size_t oldmalloc_limit_max; + double oldmalloc_limit_growth_factor; +} ruby_gc_params_t; + +static ruby_gc_params_t gc_params = { + { 0 }, + GC_HEAP_FREE_SLOTS, + GC_HEAP_GROWTH_FACTOR, + GC_HEAP_GROWTH_MAX_SLOTS, + + GC_HEAP_FREE_SLOTS_MIN_RATIO, + GC_HEAP_FREE_SLOTS_GOAL_RATIO, + GC_HEAP_FREE_SLOTS_MAX_RATIO, + GC_HEAP_REMEMBERED_WB_UNPROTECTED_OBJECTS_LIMIT_RATIO, + GC_HEAP_OLDOBJECT_LIMIT_FACTOR, + GC_HEAP_SHAREDOBJECT_LIMIT_FACTOR, + + GC_MALLOC_LIMIT_MIN, + GC_MALLOC_LIMIT_MAX, + GC_MALLOC_LIMIT_GROWTH_FACTOR, + + GC_OLDMALLOC_LIMIT_MIN, + GC_OLDMALLOC_LIMIT_MAX, + GC_OLDMALLOC_LIMIT_GROWTH_FACTOR, +}; + +/* GC_DEBUG: + * enable to embed GC debugging information. + */ +#ifndef GC_DEBUG +#define GC_DEBUG 0 +#endif + +/* RGENGC_DEBUG: + * 1: basic information + * 2: remember set operation + * 3: mark + * 4: + * 5: sweep + */ +#ifndef RGENGC_DEBUG +#ifdef RUBY_DEVEL +#define RGENGC_DEBUG -1 +#else +#define RGENGC_DEBUG 0 +#endif +#endif +#if RGENGC_DEBUG < 0 && !defined(_MSC_VER) +# define RGENGC_DEBUG_ENABLED(level) (-(RGENGC_DEBUG) >= (level) && ruby_rgengc_debug >= (level)) +#elif defined(HAVE_VA_ARGS_MACRO) +# define RGENGC_DEBUG_ENABLED(level) ((RGENGC_DEBUG) >= (level)) +#else +# define RGENGC_DEBUG_ENABLED(level) 0 +#endif +int ruby_rgengc_debug; + +/* RGENGC_CHECK_MODE + * 0: disable all assertions + * 1: enable assertions (to debug RGenGC) + * 2: enable internal consistency check at each GC (for debugging) + * 3: enable internal consistency check at each GC steps (for debugging) + * 4: enable liveness check + * 5: show all references + */ +#ifndef RGENGC_CHECK_MODE +# define RGENGC_CHECK_MODE 0 +#endif + +// Note: using RUBY_ASSERT_WHEN() extend a macro in expr (info by nobu). +#define GC_ASSERT(expr) RUBY_ASSERT_MESG_WHEN(RGENGC_CHECK_MODE > 0, expr, #expr) + +/* RGENGC_PROFILE + * 0: disable RGenGC profiling + * 1: enable profiling for basic information + * 2: enable profiling for each types + */ +#ifndef RGENGC_PROFILE +# define RGENGC_PROFILE 0 +#endif + +/* RGENGC_ESTIMATE_OLDMALLOC + * Enable/disable to estimate increase size of malloc'ed size by old objects. + * If estimation exceeds threshold, then will invoke full GC. + * 0: disable estimation. + * 1: enable estimation. + */ +#ifndef RGENGC_ESTIMATE_OLDMALLOC +# define RGENGC_ESTIMATE_OLDMALLOC 1 +#endif + +/* RGENGC_FORCE_MAJOR_GC + * Force major/full GC if this macro is not 0. + */ +#ifndef RGENGC_FORCE_MAJOR_GC +# define RGENGC_FORCE_MAJOR_GC 0 +#endif + +#ifndef GC_PROFILE_MORE_DETAIL +# define GC_PROFILE_MORE_DETAIL 0 +#endif +#ifndef GC_PROFILE_DETAIL_MEMORY +# define GC_PROFILE_DETAIL_MEMORY 0 +#endif +#ifndef GC_ENABLE_LAZY_SWEEP +# define GC_ENABLE_LAZY_SWEEP 1 +#endif +#ifndef CALC_EXACT_MALLOC_SIZE +# define CALC_EXACT_MALLOC_SIZE 0 +#endif +#if defined(HAVE_MALLOC_USABLE_SIZE) || CALC_EXACT_MALLOC_SIZE > 0 +# ifndef MALLOC_ALLOCATED_SIZE +# define MALLOC_ALLOCATED_SIZE 0 +# endif +#else +# define MALLOC_ALLOCATED_SIZE 0 +#endif +#ifndef MALLOC_ALLOCATED_SIZE_CHECK +# define MALLOC_ALLOCATED_SIZE_CHECK 0 +#endif + +#ifndef GC_DEBUG_STRESS_TO_CLASS +# define GC_DEBUG_STRESS_TO_CLASS RUBY_DEBUG +#endif + +typedef enum { + GPR_FLAG_NONE = 0x000, + /* major reason */ + GPR_FLAG_MAJOR_BY_NOFREE = 0x001, + GPR_FLAG_MAJOR_BY_OLDGEN = 0x002, + GPR_FLAG_MAJOR_BY_SHADY = 0x004, + GPR_FLAG_MAJOR_BY_FORCE = 0x008, + GPR_FLAG_MAJOR_BY_ABSORB = 0x010, +#if RGENGC_ESTIMATE_OLDMALLOC + GPR_FLAG_MAJOR_BY_OLDMALLOC = 0x020, +#endif + GPR_FLAG_MAJOR_MASK = 0x0ff, + + /* gc reason */ + GPR_FLAG_NEWOBJ = 0x100, + GPR_FLAG_MALLOC = 0x200, + GPR_FLAG_METHOD = 0x400, + GPR_FLAG_CAPI = 0x800, + GPR_FLAG_STRESS = 0x1000, + + /* others */ + GPR_FLAG_IMMEDIATE_SWEEP = 0x2000, + GPR_FLAG_HAVE_FINALIZE = 0x4000, + GPR_FLAG_IMMEDIATE_MARK = 0x8000, + GPR_FLAG_FULL_MARK = 0x10000, + GPR_FLAG_COMPACT = 0x20000, + GPR_FLAG_GLOBAL = 0x40000, + + GPR_DEFAULT_REASON = + (GPR_FLAG_FULL_MARK | GPR_FLAG_IMMEDIATE_MARK | + GPR_FLAG_IMMEDIATE_SWEEP | GPR_FLAG_CAPI), +} gc_profile_record_flag; + +typedef struct gc_profile_record { + unsigned int flags; + + double gc_time; + double gc_invoke_time; + + size_t heap_total_objects; + size_t heap_use_size; + size_t heap_total_size; + size_t moved_objects; + +#if GC_PROFILE_MORE_DETAIL + double gc_mark_time; + double gc_sweep_time; + + size_t heap_use_pages; + size_t heap_live_objects; + size_t heap_free_objects; + + size_t allocate_increase; + size_t allocate_limit; + + double prepare_time; + size_t removing_objects; + size_t empty_objects; +#if GC_PROFILE_DETAIL_MEMORY + long maxrss; + long minflt; + long majflt; +#endif +#endif +#if MALLOC_ALLOCATED_SIZE + size_t allocated_size; +#endif + +#if RGENGC_PROFILE > 0 + size_t old_objects; + size_t remembered_normal_objects; + size_t remembered_shady_objects; +#endif +} gc_profile_record; + +struct RMoved { + VALUE flags; + VALUE dummy; + VALUE destination; + uint32_t original_shape_id; +}; + +#define RMOVED(obj) ((struct RMoved *)(obj)) + +typedef uintptr_t bits_t; +enum { + BITS_SIZE = sizeof(bits_t), + BITS_BITLENGTH = ( BITS_SIZE * CHAR_BIT ) +}; + +struct heap_page_header { + struct heap_page *page; +}; + +struct heap_page_body { + struct heap_page_header header; + /* char gap[]; */ + /* RVALUE values[]; */ +}; + +#define STACK_CHUNK_SIZE 500 + +typedef struct stack_chunk { + VALUE data[STACK_CHUNK_SIZE]; + struct stack_chunk *next; +} stack_chunk_t; + +typedef struct mark_stack { + stack_chunk_t *chunk; + stack_chunk_t *cache; + int index; + int limit; + size_t cache_size; + size_t unused_cache_size; +} mark_stack_t; + +#define SIZE_POOL_EDEN_HEAP(size_pool) (&(size_pool)->eden_heap) +#define SIZE_POOL_TOMB_HEAP(size_pool) (&(size_pool)->tomb_heap) + +typedef int (*gc_compact_compare_func)(const void *l, const void *r, void *d); + +typedef struct rb_heap_struct { + struct heap_page *free_pages; + struct ccan_list_head pages; + struct heap_page *sweeping_page; /* iterator for .pages */ + struct heap_page *compact_cursor; + uintptr_t compact_cursor_index; + struct heap_page *pooled_pages; + size_t total_pages; /* total page count in a heap */ + size_t total_slots; /* total slot count (about total_pages * HEAP_PAGE_OBJ_LIMIT) */ +} rb_heap_t; + +typedef struct rb_size_pool_struct { + short slot_size; + + size_t allocatable_pages; + + /* Basic statistics */ + size_t total_allocated_pages; + size_t total_freed_pages; + size_t force_major_gc_count; + size_t force_incremental_marking_finish_count; + size_t total_allocated_objects; + size_t newly_created_by_borrowing_count; + size_t total_freed_objects; + + /* Sweeping statistics */ + size_t freed_slots; + size_t empty_slots; + + rb_heap_t eden_heap; + rb_heap_t tomb_heap; +} rb_size_pool_t; + +enum { + gc_stress_no_major, + gc_stress_no_immediate_sweep, + gc_stress_full_mark_after_malloc, + gc_stress_max +}; + +enum gc_mode { + gc_mode_none, + gc_mode_marking, + gc_mode_sweeping, + gc_mode_compacting, +}; + +typedef struct rb_objspace { + struct { + size_t limit; + size_t increase; +#if MALLOC_ALLOCATED_SIZE + size_t allocated_size; + size_t allocations; +#endif + } malloc_params; + + struct rb_gc_config { + bool full_mark; + } gc_config; + + struct { + unsigned int mode : 2; + unsigned int immediate_sweep : 1; + unsigned int dont_gc : 1; + unsigned int dont_incremental : 1; + unsigned int during_gc : 1; + unsigned int during_compacting : 1; + unsigned int during_reference_updating : 1; + unsigned int gc_stressful: 1; + unsigned int has_newobj_hook: 1; + unsigned int during_minor_gc : 1; + unsigned int during_global_gc : 1; + unsigned int during_incremental_marking : 1; + unsigned int measure_gc : 1; + } flags; + + rb_event_flag_t hook_events; + + rb_size_pool_t size_pools[SIZE_POOL_COUNT]; + + struct { + rb_atomic_t finalizing; + } atomic_flags; + + mark_stack_t mark_stack; + size_t marked_slots; + + struct { + struct heap_page **sorted; + size_t allocated_pages; + size_t allocatable_pages; + size_t sorted_length; + uintptr_t range[2]; + size_t freeable_pages; + + /* final */ + size_t final_slots; + VALUE deferred_final; + } heap_pages; + + struct { + int run; + unsigned int latest_gc_info; + gc_profile_record *records; + gc_profile_record *current_record; + size_t next_index; + size_t size; + +#if GC_PROFILE_MORE_DETAIL + double prepare_time; +#endif + double invoke_time; + + size_t minor_gc_count; + size_t major_gc_count; + size_t compact_count; + size_t read_barrier_faults; +#if RGENGC_PROFILE > 0 + size_t total_generated_normal_object_count; + size_t total_generated_shady_object_count; + size_t total_shade_operation_count; + size_t total_promoted_count; + size_t total_remembered_normal_object_count; + size_t total_remembered_shady_object_count; + +#if RGENGC_PROFILE >= 2 + size_t generated_normal_object_count_types[RUBY_T_MASK]; + size_t generated_shady_object_count_types[RUBY_T_MASK]; + size_t shade_operation_count_types[RUBY_T_MASK]; + size_t promoted_types[RUBY_T_MASK]; + size_t remembered_normal_object_count_types[RUBY_T_MASK]; + size_t remembered_shady_object_count_types[RUBY_T_MASK]; +#endif +#endif /* RGENGC_PROFILE */ + + /* temporary profiling space */ + double gc_sweep_start_time; + size_t total_allocated_objects_at_gc_start; + size_t heap_used_at_gc_start; + + /* basic statistics */ + size_t count; + uint64_t marking_time_ns; + struct timespec marking_start_time; + uint64_t sweeping_time_ns; + struct timespec sweeping_start_time; + + /* Weak references */ + size_t weak_references_count; + size_t retained_weak_references_count; + } profile; + + VALUE gc_stress_mode; + + struct { + VALUE parent_object; + int need_major_gc; + size_t last_major_gc; + size_t uncollectible_wb_unprotected_objects; + size_t uncollectible_wb_unprotected_objects_limit; + size_t old_objects; + size_t old_objects_limit; + +#if RGENGC_ESTIMATE_OLDMALLOC + size_t oldmalloc_increase; + size_t oldmalloc_increase_limit; +#endif + +#if RGENGC_CHECK_MODE >= 2 + struct st_table *allrefs_table; + size_t error_count; +#endif + } rgengc; + + struct { + size_t considered_count_table[T_MASK]; + size_t moved_count_table[T_MASK]; + size_t moved_up_count_table[T_MASK]; + size_t moved_down_count_table[T_MASK]; + size_t total_moved; + + /* This function will be used, if set, to sort the heap prior to compaction */ + gc_compact_compare_func compare_func; + } rcompactor; + + struct { + size_t pooled_slots; + size_t step_slots; + } rincgc; + +#if GC_DEBUG_STRESS_TO_CLASS + VALUE stress_to_class; +#endif + + rb_darray(VALUE *) weak_references; + rb_postponed_job_handle_t finalize_deferred_pjob; + + unsigned long live_ractor_cache_count; +} rb_objspace_t; + +#ifndef HEAP_PAGE_ALIGN_LOG +/* default tiny heap size: 64KiB */ +#define HEAP_PAGE_ALIGN_LOG 16 +#endif + +#if RACTOR_CHECK_MODE || GC_DEBUG +struct rvalue_overhead { +# if RACTOR_CHECK_MODE + uint32_t _ractor_belonging_id; +# endif +# if GC_DEBUG + const char *file; + int line; +# endif +}; + +// Make sure that RVALUE_OVERHEAD aligns to sizeof(VALUE) +# define RVALUE_OVERHEAD (sizeof(struct { \ + union { \ + struct rvalue_overhead overhead; \ + VALUE value; \ + }; \ +})) +size_t rb_gc_impl_obj_slot_size(VALUE obj); +# define GET_RVALUE_OVERHEAD(obj) ((struct rvalue_overhead *)((uintptr_t)obj + rb_gc_impl_obj_slot_size(obj))) +#else +# define RVALUE_OVERHEAD 0 +#endif + +#define BASE_SLOT_SIZE (sizeof(struct RBasic) + sizeof(VALUE[RBIMPL_RVALUE_EMBED_LEN_MAX]) + RVALUE_OVERHEAD) + +#ifndef MAX +# define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef MIN +# define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif +#define roomof(x, y) (((x) + (y) - 1) / (y)) +#define CEILDIV(i, mod) roomof(i, mod) +enum { + HEAP_PAGE_ALIGN = (1UL << HEAP_PAGE_ALIGN_LOG), + HEAP_PAGE_ALIGN_MASK = (~(~0UL << HEAP_PAGE_ALIGN_LOG)), + HEAP_PAGE_SIZE = HEAP_PAGE_ALIGN, + HEAP_PAGE_OBJ_LIMIT = (unsigned int)((HEAP_PAGE_SIZE - sizeof(struct heap_page_header)) / BASE_SLOT_SIZE), + HEAP_PAGE_BITMAP_LIMIT = CEILDIV(CEILDIV(HEAP_PAGE_SIZE, BASE_SLOT_SIZE), BITS_BITLENGTH), + HEAP_PAGE_BITMAP_SIZE = (BITS_SIZE * HEAP_PAGE_BITMAP_LIMIT), +}; +#define HEAP_PAGE_ALIGN (1 << HEAP_PAGE_ALIGN_LOG) +#define HEAP_PAGE_SIZE HEAP_PAGE_ALIGN + +#if !defined(INCREMENTAL_MARK_STEP_ALLOCATIONS) +# define INCREMENTAL_MARK_STEP_ALLOCATIONS 500 +#endif + +#undef INIT_HEAP_PAGE_ALLOC_USE_MMAP +/* Must define either HEAP_PAGE_ALLOC_USE_MMAP or + * INIT_HEAP_PAGE_ALLOC_USE_MMAP. */ + +#ifndef HAVE_MMAP +/* We can't use mmap of course, if it is not available. */ +static const bool HEAP_PAGE_ALLOC_USE_MMAP = false; + +#elif defined(__wasm__) +/* wasmtime does not have proper support for mmap. + * See https://github.com/bytecodealliance/wasmtime/blob/main/docs/WASI-rationale.md#why-no-mmap-and-friends + */ +static const bool HEAP_PAGE_ALLOC_USE_MMAP = false; + +#elif HAVE_CONST_PAGE_SIZE +/* If we have the PAGE_SIZE and it is a constant, then we can directly use it. */ +static const bool HEAP_PAGE_ALLOC_USE_MMAP = (PAGE_SIZE <= HEAP_PAGE_SIZE); + +#elif defined(PAGE_MAX_SIZE) && (PAGE_MAX_SIZE <= HEAP_PAGE_SIZE) +/* If we can use the maximum page size. */ +static const bool HEAP_PAGE_ALLOC_USE_MMAP = true; + +#elif defined(PAGE_SIZE) +/* If the PAGE_SIZE macro can be used dynamically. */ +# define INIT_HEAP_PAGE_ALLOC_USE_MMAP (PAGE_SIZE <= HEAP_PAGE_SIZE) + +#elif defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE) +/* If we can use sysconf to determine the page size. */ +# define INIT_HEAP_PAGE_ALLOC_USE_MMAP (sysconf(_SC_PAGE_SIZE) <= HEAP_PAGE_SIZE) + +#else +/* Otherwise we can't determine the system page size, so don't use mmap. */ +static const bool HEAP_PAGE_ALLOC_USE_MMAP = false; +#endif + +#ifdef INIT_HEAP_PAGE_ALLOC_USE_MMAP +/* We can determine the system page size at runtime. */ +# define HEAP_PAGE_ALLOC_USE_MMAP (heap_page_alloc_use_mmap != false) + +static bool heap_page_alloc_use_mmap; +#endif + +#define RVALUE_AGE_BIT_COUNT 2 +#define RVALUE_AGE_BIT_MASK (((bits_t)1 << RVALUE_AGE_BIT_COUNT) - 1) +#define RVALUE_OLD_AGE 3 + +struct free_slot { + VALUE flags; /* always 0 for freed obj */ + struct free_slot *next; +}; + +struct heap_page { + short slot_size; + short total_slots; + short free_slots; + short final_slots; + short pinned_slots; + struct { + unsigned int before_sweep : 1; + unsigned int has_remembered_objects : 1; + unsigned int has_uncollectible_wb_unprotected_objects : 1; + unsigned int in_tomb : 1; + } flags; + + rb_size_pool_t *size_pool; + + struct heap_page *free_next; + uintptr_t start; + struct free_slot *freelist; + struct ccan_list_node page_node; + + bits_t wb_unprotected_bits[HEAP_PAGE_BITMAP_LIMIT]; + bits_t local_immune_bits[HEAP_PAGE_BITMAP_LIMIT]; + + /* the following three bitmaps are cleared at the beginning of full GC */ + bits_t mark_bits[HEAP_PAGE_BITMAP_LIMIT]; + bits_t uncollectible_bits[HEAP_PAGE_BITMAP_LIMIT]; + bits_t marking_bits[HEAP_PAGE_BITMAP_LIMIT]; + + bits_t remembered_bits[HEAP_PAGE_BITMAP_LIMIT]; + + /* If set, the object is not movable */ + bits_t pinned_bits[HEAP_PAGE_BITMAP_LIMIT]; + bits_t age_bits[HEAP_PAGE_BITMAP_LIMIT * RVALUE_AGE_BIT_COUNT]; + + rb_ractor_t *ractor; + rb_objspace_t *objspace; + bool unlinked; +}; + +/* + * When asan is enabled, this will prohibit writing to the freelist until it is unlocked + */ +static void +asan_lock_freelist(struct heap_page *page) +{ + asan_poison_memory_region(&page->freelist, sizeof(struct free_list *)); +} + +/* + * When asan is enabled, this will enable the ability to write to the freelist + */ +static void +asan_unlock_freelist(struct heap_page *page) +{ + asan_unpoison_memory_region(&page->freelist, sizeof(struct free_list *), false); +} + +#define GET_PAGE_BODY(x) ((struct heap_page_body *)((bits_t)(x) & ~(HEAP_PAGE_ALIGN_MASK))) +#define GET_PAGE_HEADER(x) (&GET_PAGE_BODY(x)->header) +#define GET_HEAP_PAGE(x) (GET_PAGE_HEADER(x)->page) + +rb_ractor_t * +atomic_load_ractor_of_value(VALUE obj) +{ + return (rb_ractor_t *)RUBY_ATOMIC_PTR_LOAD(GET_HEAP_PAGE(obj)->ractor); +} + +rb_objspace_t * +atomic_load_objspace_of_value(VALUE obj) +{ + return (rb_objspace_t *)RUBY_ATOMIC_PTR_LOAD(GET_HEAP_PAGE(obj)->objspace); +} + +rb_ractor_t * +get_ractor_of_value(VALUE obj) +{ + if (rb_special_const_p(obj)) { + return NULL; + } + return GET_RACTOR_OF_VALUE(obj); +} + +bool +rb_contained_in_objspace_p(rb_objspace_t *objspace, VALUE obj) +{ + if (rb_special_const_p(obj)) { + return false; + } + return GET_OBJSPACE_OF_VALUE(obj) == objspace; +} + +#define NUM_IN_PAGE(p) (((bits_t)(p) & HEAP_PAGE_ALIGN_MASK) / BASE_SLOT_SIZE) +#define BITMAP_INDEX(p) (NUM_IN_PAGE(p) / BITS_BITLENGTH ) +#define BITMAP_OFFSET(p) (NUM_IN_PAGE(p) & (BITS_BITLENGTH-1)) +#define BITMAP_BIT(p) ((bits_t)1 << BITMAP_OFFSET(p)) + +/* Bitmap Operations */ +#define MARKED_IN_BITMAP(bits, p) ((bits)[BITMAP_INDEX(p)] & BITMAP_BIT(p)) +#define MARK_IN_BITMAP(bits, p) ((bits)[BITMAP_INDEX(p)] = (bits)[BITMAP_INDEX(p)] | BITMAP_BIT(p)) +#define CLEAR_IN_BITMAP(bits, p) ((bits)[BITMAP_INDEX(p)] = (bits)[BITMAP_INDEX(p)] & ~BITMAP_BIT(p)) + +/* getting bitmap */ +#define GET_HEAP_MARK_BITS(x) (&GET_HEAP_PAGE(x)->mark_bits[0]) +#define GET_HEAP_PINNED_BITS(x) (&GET_HEAP_PAGE(x)->pinned_bits[0]) +#define GET_HEAP_UNCOLLECTIBLE_BITS(x) (&GET_HEAP_PAGE(x)->uncollectible_bits[0]) +#define GET_HEAP_WB_UNPROTECTED_BITS(x) (&GET_HEAP_PAGE(x)->wb_unprotected_bits[0]) +#define GET_HEAP_LOCAL_IMMUNE_BITS(x) (&GET_HEAP_PAGE(x)->local_immune_bits[0]) +#define GET_HEAP_MARKING_BITS(x) (&GET_HEAP_PAGE(x)->marking_bits[0]) + +#define GC_SWEEP_PAGES_FREEABLE_PER_STEP 3 + +#define RVALUE_AGE_BITMAP_INDEX(n) (NUM_IN_PAGE(n) / (BITS_BITLENGTH / RVALUE_AGE_BIT_COUNT)) +#define RVALUE_AGE_BITMAP_OFFSET(n) ((NUM_IN_PAGE(n) % (BITS_BITLENGTH / RVALUE_AGE_BIT_COUNT)) * RVALUE_AGE_BIT_COUNT) + +static int +RVALUE_AGE_GET(VALUE obj) +{ + bits_t *age_bits = GET_HEAP_PAGE(obj)->age_bits; + return (int)(age_bits[RVALUE_AGE_BITMAP_INDEX(obj)] >> RVALUE_AGE_BITMAP_OFFSET(obj)) & RVALUE_AGE_BIT_MASK; +} + +static void +RVALUE_AGE_SET(VALUE obj, int age) +{ + RUBY_ASSERT(age <= RVALUE_OLD_AGE); + bits_t *age_bits = GET_HEAP_PAGE(obj)->age_bits; + // clear the bits + age_bits[RVALUE_AGE_BITMAP_INDEX(obj)] &= ~(RVALUE_AGE_BIT_MASK << (RVALUE_AGE_BITMAP_OFFSET(obj))); + // shift the correct value in + age_bits[RVALUE_AGE_BITMAP_INDEX(obj)] |= ((bits_t)age << RVALUE_AGE_BITMAP_OFFSET(obj)); + if (age == RVALUE_OLD_AGE) { + RB_FL_SET_RAW(obj, RUBY_FL_PROMOTED); + } + else { + RB_FL_UNSET_RAW(obj, RUBY_FL_PROMOTED); + } +} + +#define malloc_limit objspace->malloc_params.limit +#define malloc_increase objspace->malloc_params.increase +#define malloc_allocated_size objspace->malloc_params.allocated_size +#define heap_pages_sorted objspace->heap_pages.sorted +#define heap_allocated_pages objspace->heap_pages.allocated_pages +#define heap_pages_sorted_length objspace->heap_pages.sorted_length +#define heap_pages_lomem objspace->heap_pages.range[0] +#define heap_pages_himem objspace->heap_pages.range[1] +#define heap_pages_freeable_pages objspace->heap_pages.freeable_pages +#define heap_pages_final_slots objspace->heap_pages.final_slots +#define heap_pages_deferred_final objspace->heap_pages.deferred_final +#define size_pools objspace->size_pools +#define during_gc objspace->flags.during_gc +#define finalizing objspace->atomic_flags.finalizing +#define finalizer_table objspace->local_data.finalizer_table +#define ruby_gc_stressful objspace->flags.gc_stressful +#define ruby_gc_stress_mode objspace->gc_stress_mode +#if GC_DEBUG_STRESS_TO_CLASS +#define stress_to_class objspace->stress_to_class +#define set_stress_to_class(c) (stress_to_class = (c)) +#else +#define stress_to_class (objspace, 0) +#define set_stress_to_class(c) (objspace, (c)) +#endif + +#if 0 +#define dont_gc_on() (fprintf(stderr, "dont_gc_on@%s:%d\n", __FILE__, __LINE__), objspace->flags.dont_gc = 1) +#define dont_gc_off() (fprintf(stderr, "dont_gc_off@%s:%d\n", __FILE__, __LINE__), objspace->flags.dont_gc = 0) +#define dont_gc_set(b) (fprintf(stderr, "dont_gc_set(%d)@%s:%d\n", __FILE__, __LINE__), (int)b), objspace->flags.dont_gc = (b)) +#define dont_gc_val() (objspace->flags.dont_gc) +#else +#define dont_gc_on() (objspace->flags.dont_gc = 1) +#define dont_gc_off() (objspace->flags.dont_gc = 0) +#define dont_gc_set(b) (((int)b), objspace->flags.dont_gc = (b)) +#define dont_gc_val() (objspace->flags.dont_gc) +#endif + +#define gc_config_full_mark_set(b) (((int)b), objspace->gc_config.full_mark = (b)) +#define gc_config_full_mark_val (objspace->gc_config.full_mark) + +#define DURING_GC_COULD_MALLOC_REGION_START() \ + assert(rb_during_gc()); \ + bool _prev_enabled = rb_gc_impl_gc_enabled_p(objspace); \ + rb_gc_impl_gc_disable(objspace, false) + +#define DURING_GC_COULD_MALLOC_REGION_END() \ + if (_prev_enabled) rb_gc_impl_gc_enable(objspace) + +static inline enum gc_mode +gc_mode_verify(enum gc_mode mode) +{ +#if RGENGC_CHECK_MODE > 0 + switch (mode) { + case gc_mode_none: + case gc_mode_marking: + case gc_mode_sweeping: + case gc_mode_compacting: + break; + default: + rb_bug("gc_mode_verify: unreachable (%d)", (int)mode); + } +#endif + return mode; +} + +static inline bool +has_sweeping_pages(rb_objspace_t *objspace) +{ + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + if (SIZE_POOL_EDEN_HEAP(&size_pools[i])->sweeping_page) { + return TRUE; + } + } + return FALSE; +} + +static inline size_t +heap_eden_total_pages(rb_objspace_t *objspace) +{ + size_t count = 0; + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + count += SIZE_POOL_EDEN_HEAP(&size_pools[i])->total_pages; + } + return count; +} + +static inline size_t +heap_eden_total_slots(rb_objspace_t *objspace) +{ + size_t count = 0; + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + count += SIZE_POOL_EDEN_HEAP(&size_pools[i])->total_slots; + } + return count; +} + +static inline size_t +heap_tomb_total_pages(rb_objspace_t *objspace) +{ + size_t count = 0; + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + count += SIZE_POOL_TOMB_HEAP(&size_pools[i])->total_pages; + } + return count; +} + +static inline size_t +heap_allocatable_pages(rb_objspace_t *objspace) +{ + size_t count = 0; + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + count += size_pools[i].allocatable_pages; + } + return count; +} + +static inline size_t +heap_allocatable_slots(rb_objspace_t *objspace) +{ + size_t count = 0; + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + int slot_size_multiple = size_pool->slot_size / BASE_SLOT_SIZE; + count += size_pool->allocatable_pages * HEAP_PAGE_OBJ_LIMIT / slot_size_multiple; + } + return count; +} + +static inline size_t +total_allocated_pages(rb_objspace_t *objspace) +{ + size_t count = 0; + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + count += size_pool->total_allocated_pages; + } + return count; +} + +static inline size_t +total_freed_pages(rb_objspace_t *objspace) +{ + size_t count = 0; + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + count += size_pool->total_freed_pages; + } + return count; +} + +static inline size_t +total_allocated_objects(rb_objspace_t *objspace) +{ + rb_ractor_t *r = objspace->local_data.ractor; + if (!during_gc) rb_borrowing_sync_lock(r); + size_t count = 0; + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + size_pool->total_allocated_objects += size_pool->newly_created_by_borrowing_count; + size_pool->newly_created_by_borrowing_count = 0; + count += size_pool->total_allocated_objects; + } + if (!during_gc) rb_borrowing_sync_unlock(r); + return count; +} + +static inline size_t +total_freed_objects(rb_objspace_t *objspace) +{ + size_t count = 0; + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + count += size_pool->total_freed_objects; + } + return count; +} + +#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) +#define is_full_marking(objspace) ((objspace)->flags.during_minor_gc == FALSE) +#define is_incremental_marking(objspace) ((objspace)->flags.during_incremental_marking != FALSE) +#define will_be_incremental_marking(objspace) ((objspace)->rgengc.need_major_gc != GPR_FLAG_NONE) +#define GC_INCREMENTAL_SWEEP_SLOT_COUNT 2048 +#define GC_INCREMENTAL_SWEEP_POOL_SLOT_COUNT 1024 +#define is_lazy_sweeping(objspace) (GC_ENABLE_LAZY_SWEEP && has_sweeping_pages(objspace)) +#define using_local_limits(objspace) (objspace != ruby_single_main_objspace && !objspace->flags.during_global_gc) + +#if SIZEOF_LONG == SIZEOF_VOIDP +# define obj_id_to_ref(objid) ((objid) ^ FIXNUM_FLAG) /* unset FIXNUM_FLAG */ +#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP +# define obj_id_to_ref(objid) (FIXNUM_P(objid) ? \ + ((objid) ^ FIXNUM_FLAG) : (NUM2PTR(objid) << 1)) +#else +# error not supported +#endif + +struct RZombie { + struct RBasic basic; + VALUE next; + void (*dfree)(void *); + void *data; +}; + +#define RZOMBIE(o) ((struct RZombie *)(o)) + +int ruby_disable_gc = 0; +int ruby_enable_autocompact = 0; +#if RGENGC_CHECK_MODE +gc_compact_compare_func ruby_autocompact_compare_func; +#endif + +static void init_mark_stack(mark_stack_t *stack); +static int garbage_collect_global(rb_objspace_t *, unsigned int reason, bool need_finalize_deferred); +static int garbage_collect_local(rb_objspace_t *, unsigned int reason, bool need_finalize_deferred); +static int garbage_collect(rb_objspace_t *, unsigned int reason, bool need_finalize_deferred); + +static int gc_start(rb_objspace_t *objspace, unsigned int reason); +static void gc_rest(rb_objspace_t *objspace); + +enum gc_enter_event { + gc_enter_event_start, + gc_enter_event_continue, + gc_enter_event_rest, + gc_enter_event_finalizer, +}; + +static inline void gc_enter(rb_objspace_t *objspace, enum gc_enter_event event); +static inline void gc_global_enter(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_lev); +static inline void gc_exit(rb_objspace_t *objspace, enum gc_enter_event event); +static inline void gc_global_exit(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_lev); +static void gc_marking_enter(rb_objspace_t *objspace); +static void gc_marking_exit(rb_objspace_t *objspace); +static void gc_sweeping_enter(rb_objspace_t *objspace); +static void gc_sweeping_exit(rb_objspace_t *objspace); +static bool gc_marks_continue(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap); + +static void gc_sweep(rb_objspace_t *objspace); +static void gc_sweep_finish_size_pool(rb_objspace_t *objspace, rb_size_pool_t *size_pool); +static void gc_sweep_continue(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap); + +static inline void gc_mark(rb_objspace_t *objspace, VALUE ptr); +static inline void gc_pin(rb_objspace_t *objspace, VALUE ptr); +static inline void gc_mark_and_pin(rb_objspace_t *objspace, VALUE ptr); +NO_SANITIZE("memory", static void gc_stack_location_mark_maybe(rb_objspace_t *objspace, VALUE ptr)); + +static int gc_mark_stacked_objects_incremental(rb_objspace_t *, size_t count); +NO_SANITIZE("memory", static inline bool is_pointer_to_heap(rb_objspace_t *objspace, const void *ptr)); + +static void gc_verify_internal_consistency(void *objspace_ptr); + +static double getrusage_time(void); +static inline void gc_prof_setup_new_record(rb_objspace_t *objspace, unsigned int reason); +static inline void gc_prof_timer_start(rb_objspace_t *); +static inline void gc_prof_timer_stop(rb_objspace_t *); +static inline void gc_prof_mark_timer_start(rb_objspace_t *); +static inline void gc_prof_mark_timer_stop(rb_objspace_t *); +static inline void gc_prof_sweep_timer_start(rb_objspace_t *); +static inline void gc_prof_sweep_timer_stop(rb_objspace_t *); +static inline void gc_prof_set_malloc_info(rb_objspace_t *); +static inline void gc_prof_set_heap_info(rb_objspace_t *); + +#define gc_prof_record(objspace) (objspace)->profile.current_record +#define gc_prof_enabled(objspace) ((objspace)->profile.run && (objspace)->profile.current_record) + +#ifdef HAVE_VA_ARGS_MACRO +# define gc_report(level, objspace, ...) \ + if (!RGENGC_DEBUG_ENABLED(level)) {} else gc_report_body(level, objspace, __VA_ARGS__) +#else +# define gc_report if (!RGENGC_DEBUG_ENABLED(0)) {} else gc_report_body +#endif +PRINTF_ARGS(static void gc_report_body(int level, rb_objspace_t *objspace, const char *fmt, ...), 3, 4); + +static void gc_finalize_deferred(void *dmy); + +#if USE_TICK_T + +/* the following code is only for internal tuning. */ + +/* Source code to use RDTSC is quoted and modified from + * https://www.mcs.anl.gov/~kazutomo/rdtsc.html + * written by Kazutomo Yoshii + */ + +#if defined(__GNUC__) && defined(__i386__) +typedef unsigned long long tick_t; +#define PRItick "llu" +static inline tick_t +tick(void) +{ + unsigned long long int x; + __asm__ __volatile__ ("rdtsc" : "=A" (x)); + return x; +} + +#elif defined(__GNUC__) && defined(__x86_64__) +typedef unsigned long long tick_t; +#define PRItick "llu" + +static __inline__ tick_t +tick(void) +{ + unsigned long hi, lo; + __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); + return ((unsigned long long)lo)|( ((unsigned long long)hi)<<32); +} + +#elif defined(__powerpc64__) && (GCC_VERSION_SINCE(4,8,0) || defined(__clang__)) +typedef unsigned long long tick_t; +#define PRItick "llu" + +static __inline__ tick_t +tick(void) +{ + unsigned long long val = __builtin_ppc_get_timebase(); + return val; +} + +/* Implementation for macOS PPC by @nobu + * See: https://github.com/ruby/ruby/pull/5975#discussion_r890045558 + */ +#elif defined(__POWERPC__) && defined(__APPLE__) +typedef unsigned long long tick_t; +#define PRItick "llu" + +static __inline__ tick_t +tick(void) +{ + unsigned long int upper, lower, tmp; + # define mftbu(r) __asm__ volatile("mftbu %0" : "=r"(r)) + # define mftb(r) __asm__ volatile("mftb %0" : "=r"(r)) + do { + mftbu(upper); + mftb(lower); + mftbu(tmp); + } while (tmp != upper); + return ((tick_t)upper << 32) | lower; +} + +#elif defined(__aarch64__) && defined(__GNUC__) +typedef unsigned long tick_t; +#define PRItick "lu" + +static __inline__ tick_t +tick(void) +{ + unsigned long val; + __asm__ __volatile__ ("mrs %0, cntvct_el0" : "=r" (val)); + return val; +} + + +#elif defined(_WIN32) && defined(_MSC_VER) +#include +typedef unsigned __int64 tick_t; +#define PRItick "llu" + +static inline tick_t +tick(void) +{ + return __rdtsc(); +} + +#else /* use clock */ +typedef clock_t tick_t; +#define PRItick "llu" + +static inline tick_t +tick(void) +{ + return clock(); +} +#endif /* TSC */ +#else /* USE_TICK_T */ +#define MEASURE_LINE(expr) expr +#endif /* USE_TICK_T */ + +#define asan_unpoisoning_object(obj) \ + for (void *poisoned = asan_unpoison_object_temporary(obj), \ + *unpoisoning = &poisoned; /* flag to loop just once */ \ + unpoisoning; \ + unpoisoning = asan_poison_object_restore(obj, poisoned)) + +#define FL_CHECK2(name, x, pred) \ + ((RGENGC_CHECK_MODE && SPECIAL_CONST_P(x)) ? \ + (rb_bug(name": SPECIAL_CONST (%p)", (void *)(x)), 0) : (pred)) +#define FL_TEST2(x,f) FL_CHECK2("FL_TEST2", x, FL_TEST_RAW((x),(f)) != 0) +#define FL_SET2(x,f) FL_CHECK2("FL_SET2", x, RBASIC(x)->flags |= (f)) +#define FL_UNSET2(x,f) FL_CHECK2("FL_UNSET2", x, RBASIC(x)->flags &= ~(f)) + +static inline VALUE check_rvalue_consistency(rb_objspace_t *objspace, const VALUE obj); + +#define RVALUE_MARKED_BITMAP(obj) MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(obj), (obj)) +#define RVALUE_WB_UNPROTECTED_BITMAP(obj) MARKED_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(obj), (obj)) +#define RVALUE_LOCAL_IMMUNE_BITMAP(obj) MARKED_IN_BITMAP(GET_HEAP_LOCAL_IMMUNE_BITS(obj), (obj)) +#define RVALUE_MARKING_BITMAP(obj) MARKED_IN_BITMAP(GET_HEAP_MARKING_BITS(obj), (obj)) +#define RVALUE_UNCOLLECTIBLE_BITMAP(obj) MARKED_IN_BITMAP(GET_HEAP_UNCOLLECTIBLE_BITS(obj), (obj)) +#define RVALUE_PINNED_BITMAP(obj) MARKED_IN_BITMAP(GET_HEAP_PINNED_BITS(obj), (obj)) + +static inline int +RVALUE_MARKED(rb_objspace_t *objspace, VALUE obj) +{ + check_rvalue_consistency(objspace, obj); + return RVALUE_MARKED_BITMAP(obj) != 0; +} + +static inline int +RVALUE_PINNED(rb_objspace_t *objspace, VALUE obj) +{ + check_rvalue_consistency(objspace, obj); + return RVALUE_PINNED_BITMAP(obj) != 0; +} + +static inline int +RVALUE_WB_UNPROTECTED(rb_objspace_t *objspace, VALUE obj) +{ + check_rvalue_consistency(objspace, obj); + return RVALUE_WB_UNPROTECTED_BITMAP(obj) != 0; +} + +static inline int +RVALUE_MARKING(rb_objspace_t *objspace, VALUE obj) +{ + check_rvalue_consistency(objspace, obj); + return RVALUE_MARKING_BITMAP(obj) != 0; +} + +static inline int +RVALUE_REMEMBERED(rb_objspace_t *objspace, VALUE obj) +{ + check_rvalue_consistency(objspace, obj); + return MARKED_IN_BITMAP(GET_HEAP_PAGE(obj)->remembered_bits, obj) != 0; +} + +static inline int +RVALUE_UNCOLLECTIBLE(rb_objspace_t *objspace, VALUE obj) +{ + check_rvalue_consistency(objspace, obj); + return RVALUE_UNCOLLECTIBLE_BITMAP(obj) != 0; +} + +bool +rb_gc_object_marked(VALUE obj) +{ + return RVALUE_MARKED(GET_OBJSPACE_OF_VALUE(obj), obj); +} + +bool +rb_gc_object_local_immune(VALUE obj) +{ + return RVALUE_LOCAL_IMMUNE(GET_OBJSPACE_OF_VALUE(obj), obj); +} + +void +gc_give_local_immunity_no_check(VALUE obj) +{ + MARK_IN_BITMAP(GET_HEAP_LOCAL_IMMUNE_BITS(obj), obj); +} + +#define RVALUE_PAGE_MARKED(page, obj) MARKED_IN_BITMAP((page)->mark_bits, (obj)) +#define RVALUE_PAGE_WB_UNPROTECTED(page, obj) MARKED_IN_BITMAP((page)->wb_unprotected_bits, (obj)) +#define RVALUE_PAGE_UNCOLLECTIBLE(page, obj) MARKED_IN_BITMAP((page)->uncollectible_bits, (obj)) +#define RVALUE_PAGE_MARKING(page, obj) MARKED_IN_BITMAP((page)->marking_bits, (obj)) + +static int rgengc_remember(rb_objspace_t *objspace, VALUE obj); +static void rgengc_mark_and_rememberset_clear(rb_objspace_t *objspace, rb_heap_t *heap); +static void rgengc_rememberset_mark(rb_objspace_t *objspace, rb_heap_t *heap); + +static int +check_rvalue_consistency_force(rb_objspace_t *objspace, const VALUE obj, int terminate) +{ + int err = 0; + + if (objspace != GET_OBJSPACE_OF_VALUE(obj)) { + int result; + WITH_OBJSPACE_OF_VALUE_ENTER(obj, objspace); + { + result = check_rvalue_consistency_force(objspace, obj, terminate); + } + WITH_OBJSPACE_OF_VALUE_LEAVE(objspace); + return result; + } + + HEAP_LOCK_ENTER(objspace); + { + if (SPECIAL_CONST_P(obj)) { + fprintf(stderr, "check_rvalue_consistency: %p is a special const.\n", (void *)obj); + err++; + } + else if (!is_pointer_to_heap(objspace, (void *)obj)) { + /* check if it is in tomb_pages */ + struct heap_page *page = NULL; + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + ccan_list_for_each(&size_pool->tomb_heap.pages, page, page_node) { + if (page->start <= (uintptr_t)obj && + (uintptr_t)obj < (page->start + (page->total_slots * size_pool->slot_size))) { + fprintf(stderr, "check_rvalue_consistency: %p is in a tomb_heap (%p).\n", + (void *)obj, (void *)page); + err++; + goto skip; + } + } + } + fprintf(stderr, "check_rvalue_consistency: %p is not a Ruby object.\n", (void *)obj); + err++; + skip: + ; + } + else { + const int wb_unprotected_bit = RVALUE_WB_UNPROTECTED_BITMAP(obj) != 0; + const int uncollectible_bit = RVALUE_UNCOLLECTIBLE_BITMAP(obj) != 0; + const int mark_bit = RVALUE_MARKED_BITMAP(obj) != 0; + const int marking_bit = RVALUE_MARKING_BITMAP(obj) != 0; + const int remembered_bit = MARKED_IN_BITMAP(GET_HEAP_PAGE(obj)->remembered_bits, obj) != 0; + const int age = RVALUE_AGE_GET((VALUE)obj); + + if (GET_HEAP_PAGE(obj)->flags.in_tomb) { + fprintf(stderr, "check_rvalue_consistency: %s is in tomb page.\n", rb_obj_info(obj)); + err++; + } + if (BUILTIN_TYPE(obj) == T_NONE) { + fprintf(stderr, "check_rvalue_consistency: %s is T_NONE.\n", rb_obj_info(obj)); + err++; + } + if (BUILTIN_TYPE(obj) == T_ZOMBIE) { + fprintf(stderr, "check_rvalue_consistency: %s is T_ZOMBIE.\n", rb_obj_info(obj)); + err++; + } + + if (BUILTIN_TYPE(obj) != T_DATA) { + rb_obj_memsize_of((VALUE)obj); + } + + /* check generation + * + * OLD == age == 3 && old-bitmap && mark-bit (except incremental marking) + */ + if (age > 0 && wb_unprotected_bit) { + fprintf(stderr, "check_rvalue_consistency: %s is not WB protected, but age is %d > 0.\n", rb_obj_info(obj), age); + err++; + } + + if (!is_marking(objspace) && uncollectible_bit && !mark_bit) { + fprintf(stderr, "check_rvalue_consistency: %s is uncollectible, but is not marked while !gc.\n", rb_obj_info(obj)); + err++; + } + + if (!is_full_marking(objspace)) { + if (uncollectible_bit && age != RVALUE_OLD_AGE && !wb_unprotected_bit) { + fprintf(stderr, "check_rvalue_consistency: %s is uncollectible, but not old (age: %d) and not WB unprotected.\n", + rb_obj_info(obj), age); + err++; + } + if (remembered_bit && age != RVALUE_OLD_AGE) { + fprintf(stderr, "check_rvalue_consistency: %s is remembered, but not old (age: %d).\n", + rb_obj_info(obj), age); + err++; + } + } + + /* + * check coloring + * + * marking:false marking:true + * marked:false white *invalid* + * marked:true black grey + */ + if (is_incremental_marking(objspace) && marking_bit) { + if (!is_marking(objspace) && !mark_bit) { + fprintf(stderr, "check_rvalue_consistency: %s is marking, but not marked.\n", rb_obj_info(obj)); + err++; + } + } + } + } + HEAP_LOCK_LEAVE(objspace); + + if (err > 0 && terminate) { + rb_bug("check_rvalue_consistency_force: there is %d errors.", err); + } + return err; +} + +#if RGENGC_CHECK_MODE == 0 +static inline VALUE +check_rvalue_consistency(rb_objspace_t *objspace, const VALUE obj) +{ + return obj; +} +#else +static VALUE +check_rvalue_consistency(rb_objspace_t *objspace, const VALUE obj) +{ + check_rvalue_consistency_force(objspace, obj, TRUE); + return obj; +} +#endif + +static inline bool +gc_object_moved_p(rb_objspace_t *objspace, VALUE obj) +{ + if (RB_SPECIAL_CONST_P(obj)) { + return FALSE; + } + else { + void *poisoned = asan_unpoison_object_temporary(obj); + + int ret = BUILTIN_TYPE(obj) == T_MOVED; + /* Re-poison slot if it's not the one we want */ + if (poisoned) { + GC_ASSERT(BUILTIN_TYPE(obj) == T_NONE); + asan_poison_object(obj); + } + return ret; + } +} + +static inline int +RVALUE_OLD_P(rb_objspace_t *objspace, VALUE obj) +{ + GC_ASSERT(!RB_SPECIAL_CONST_P(obj)); + check_rvalue_consistency(objspace, obj); + // Because this will only ever be called on GC controlled objects, + // we can use the faster _RAW function here + return RB_OBJ_PROMOTED_RAW(obj); +} + +static inline void +RVALUE_PAGE_OLD_UNCOLLECTIBLE_SET(rb_objspace_t *objspace, struct heap_page *page, VALUE obj) +{ + MARK_IN_BITMAP(&page->uncollectible_bits[0], obj); + objspace->rgengc.old_objects++; + +#if RGENGC_PROFILE >= 2 + objspace->profile.total_promoted_count++; + objspace->profile.promoted_types[BUILTIN_TYPE(obj)]++; +#endif +} + +static inline void +RVALUE_OLD_UNCOLLECTIBLE_SET(rb_objspace_t *objspace, VALUE obj) +{ + RB_DEBUG_COUNTER_INC(obj_promote); + RVALUE_PAGE_OLD_UNCOLLECTIBLE_SET(objspace, GET_HEAP_PAGE(obj), obj); +} + +/* set age to age+1 */ +static inline void +RVALUE_AGE_INC(rb_objspace_t *objspace, VALUE obj) +{ + int age = RVALUE_AGE_GET((VALUE)obj); + + if (RGENGC_CHECK_MODE && age == RVALUE_OLD_AGE) { + rb_bug("RVALUE_AGE_INC: can not increment age of OLD object %s.", rb_obj_info(obj)); + } + + age++; + RVALUE_AGE_SET(obj, age); + + if (age == RVALUE_OLD_AGE) { + RVALUE_OLD_UNCOLLECTIBLE_SET(objspace, obj); + } + + check_rvalue_consistency(objspace, obj); +} + +static inline void +RVALUE_AGE_SET_CANDIDATE(rb_objspace_t *objspace, VALUE obj) +{ + check_rvalue_consistency(objspace, obj); + GC_ASSERT(!RVALUE_OLD_P(objspace, obj)); + RVALUE_AGE_SET(obj, RVALUE_OLD_AGE - 1); + check_rvalue_consistency(objspace, obj); +} + +static inline void +RVALUE_AGE_RESET(VALUE obj) +{ + RVALUE_AGE_SET(obj, 0); +} + +static inline void +RVALUE_DEMOTE(rb_objspace_t *objspace, VALUE obj) +{ + check_rvalue_consistency(objspace, obj); + GC_ASSERT(RVALUE_OLD_P(objspace, obj)); + + if (!is_incremental_marking(objspace) && RVALUE_REMEMBERED(objspace, obj)) { + CLEAR_IN_BITMAP(GET_HEAP_PAGE(obj)->remembered_bits, obj); + } + + CLEAR_IN_BITMAP(GET_HEAP_UNCOLLECTIBLE_BITS(obj), obj); + RVALUE_AGE_RESET(obj); + + if (RVALUE_MARKED(objspace, obj)) { + objspace->rgengc.old_objects--; + } + + check_rvalue_consistency(objspace, obj); +} + +static inline int +RVALUE_BLACK_P(rb_objspace_t *objspace, VALUE obj) +{ + return RVALUE_MARKED(objspace, obj) && !RVALUE_MARKING(objspace, obj); +} + +static inline int +RVALUE_WHITE_P(rb_objspace_t *objspace, VALUE obj) +{ + return !RVALUE_MARKED(objspace, obj); +} + +bool +rb_gc_impl_gc_enabled_p(void *objspace_ptr) +{ + rb_objspace_t *objspace = objspace_ptr; + return !dont_gc_val(); +} + +void +rb_gc_impl_gc_enable(void *objspace_ptr) +{ + rb_objspace_t *objspace = objspace_ptr; + + dont_gc_off(); +} + +void +rb_gc_impl_gc_disable(void *objspace_ptr, bool finish_current_gc) +{ + rb_objspace_t *objspace = objspace_ptr; + + if (finish_current_gc) { + gc_rest(objspace); + } + + dont_gc_on(); +} + +/* + --------------------------- ObjectSpace ----------------------------- +*/ + +static inline void * +calloc1(size_t n) +{ + return calloc(1, n); +} + +void +rb_gc_impl_set_event_hook(void *objspace_ptr, const rb_event_flag_t event) +{ + rb_objspace_t *objspace = objspace_ptr; + objspace->hook_events = event & RUBY_INTERNAL_EVENT_OBJSPACE_MASK; + objspace->flags.has_newobj_hook = !!(objspace->hook_events & RUBY_INTERNAL_EVENT_NEWOBJ); +} + +VALUE +rb_gc_impl_get_profile_total_time(void *objspace_ptr) +{ + rb_objspace_t *objspace = objspace_ptr; + + uint64_t marking_time = objspace->profile.marking_time_ns; + uint64_t sweeping_time = objspace->profile.sweeping_time_ns; + + return ULL2NUM(marking_time + sweeping_time); +} + +VALUE +rb_gc_impl_set_measure_total_time(void *objspace_ptr, VALUE flag) +{ + rb_objspace_t *objspace = objspace_ptr; + + objspace->flags.measure_gc = RTEST(flag) ? TRUE : FALSE; + + return flag; +} + +VALUE +rb_gc_impl_get_measure_total_time(void *objspace_ptr) +{ + rb_objspace_t *objspace = objspace_ptr; + + return objspace->flags.measure_gc ? Qtrue : Qfalse; +} + +static size_t +slots_to_pages_for_size_pool(rb_objspace_t *objspace, rb_size_pool_t *size_pool, size_t slots) +{ + size_t multiple = size_pool->slot_size / BASE_SLOT_SIZE; + /* Due to alignment, heap pages may have one less slot. We should + * ensure there is enough pages to guarantee that we will have at + * least the required number of slots after allocating all the pages. */ + size_t slots_per_page = (HEAP_PAGE_OBJ_LIMIT / multiple) - 1; + return CEILDIV(slots, slots_per_page); +} + +static size_t +minimum_pages_for_size_pool(rb_objspace_t *objspace, rb_size_pool_t *size_pool) +{ + size_t size_pool_idx = size_pool - size_pools; + size_t init_slots = gc_params.size_pool_init_slots[size_pool_idx]; + return slots_to_pages_for_size_pool(objspace, size_pool, init_slots); +} + +static VALUE initial_stress = Qfalse; + +void +rb_gc_impl_initial_stress_set(VALUE flag) +{ + initial_stress = flag; +} + +static int +object_id_cmp(st_data_t x, st_data_t y) +{ + if (RB_TYPE_P(x, T_BIGNUM)) { + return !rb_big_eql(x, y); + } + else { + return x != y; + } +} + +static st_index_t +object_id_hash(st_data_t n) +{ + return FIX2LONG(rb_hash((VALUE)n)); +} + +#define OBJ_ID_INCREMENT (RUBY_IMMEDIATE_MASK + 1) +#define OBJ_ID_INITIAL (OBJ_ID_INCREMENT) + +unsigned long long +rb_get_obj_id_initial(void) +{ + return OBJ_ID_INITIAL; +} + +const struct st_hash_type object_id_hash_type = { + object_id_cmp, + object_id_hash, +}; + +/* garbage objects will be collected soon. */ +bool +rb_gc_impl_garbage_object_p(void *objspace_ptr, VALUE ptr) +{ + rb_objspace_t *objspace = objspace_ptr; + + switch (BUILTIN_TYPE(ptr)) { + case T_NONE: + case T_MOVED: + case T_ZOMBIE: + return true; + default: + break; + } + + return is_lazy_sweeping(objspace) && GET_HEAP_PAGE(ptr)->flags.before_sweep && + !RVALUE_MARKED(objspace, ptr); +} + +VALUE +rb_gc_impl_object_id_to_ref(void *objspace_ptr, VALUE object_id) +{ + rb_objspace_t *objspace = objspace_ptr; + + VALUE obj; + if (st_lookup(objspace->id_to_obj_tbl, object_id, &obj) && + !rb_gc_impl_garbage_object_p(objspace, obj)) { + return obj; + } + + if (rb_funcall(object_id, rb_intern(">="), 1, ULL2NUM(objspace->next_object_id))) { + rb_raise(rb_eRangeError, "%+"PRIsVALUE" is not id value", rb_funcall(object_id, rb_intern("to_s"), 1, INT2FIX(10))); + } + else { + rb_raise(rb_eRangeError, "%+"PRIsVALUE" is recycled object", rb_funcall(object_id, rb_intern("to_s"), 1, INT2FIX(10))); + } +} + +VALUE +rb_gc_impl_object_id(void *objspace_ptr, VALUE obj) +{ + VALUE id; + + WITH_OBJSPACE_OF_VALUE_ENTER(obj, objspace); + rb_native_mutex_lock(&objspace->local_data.obj_id_lock); + if (st_lookup(objspace->local_data.obj_to_id_tbl, (st_data_t)obj, &id)) { + GC_ASSERT(FL_TEST(obj, FL_SEEN_OBJ_ID)); + } + else { + GC_ASSERT(!FL_TEST(obj, FL_SEEN_OBJ_ID)); + id = retrieve_next_obj_id(OBJ_ID_INCREMENT); + + VALUE already_disabled = gc_disable_no_rest(objspace); + st_insert(objspace->local_data.obj_to_id_tbl, (st_data_t)obj, (st_data_t)id); + st_insert(objspace->local_data.id_to_obj_tbl, (st_data_t)id, (st_data_t)obj); + if (already_disabled == Qfalse) rb_objspace_gc_enable(objspace); + FL_SET(obj, FL_SEEN_OBJ_ID); + } + rb_native_mutex_unlock(&objspace->local_data.obj_id_lock); + WITH_OBJSPACE_OF_VALUE_LEAVE(objspace); + + return id; +} + +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, bool global_pages_locked); + +static struct heap_page ** +page_list_expand_sorted_to_size(struct heap_page **sorted_list, size_t sorted_list_length, size_t target_size) +{ + struct heap_page **sorted; + if (sorted_list_length > 0) { + sorted = (struct heap_page **)realloc(sorted_list, target_size); + } + else { + sorted = (struct heap_page **)malloc(target_size); + } + + if (sorted == 0) { + rb_memerror(); + } + return sorted; +} + +static void +heap_pages_expand_sorted_to(rb_objspace_t *objspace, size_t next_length) +{ + size_t length_diff = next_length - heap_pages_sorted_length; + size_t size = rb_size_mul_or_raise(next_length, sizeof(struct heap_page *), rb_eRuntimeError); + + gc_report(3, objspace, "heap_pages_expand_sorted: next_length: %"PRIdSIZE", size: %"PRIdSIZE"\n", + next_length, size); + + heap_pages_sorted = page_list_expand_sorted_to_size(heap_pages_sorted, heap_pages_sorted_length, size); + heap_pages_sorted_length = next_length; + + rb_global_space_t *global_space = &rb_global_space; + rb_native_mutex_lock(&global_space->all_pages.global_pages_lock); + + size_t next_length_global = length_diff + all_pages_sorted_length_global; + size_t global_size = rb_size_mul_or_raise(next_length_global, sizeof(struct heap_page *), rb_eRuntimeError); + all_pages_sorted_global = page_list_expand_sorted_to_size(all_pages_sorted_global, all_pages_sorted_length_global, global_size); + all_pages_sorted_length_global = next_length_global; + + rb_native_mutex_unlock(&global_space->all_pages.global_pages_lock); + +} + +static void +heap_pages_expand_sorted(rb_objspace_t *objspace) +{ + /* usually heap_allocatable_pages + heap_eden->total_pages == heap_pages_sorted_length + * because heap_allocatable_pages contains heap_tomb->total_pages (recycle heap_tomb pages). + * however, if there are pages which do not have empty slots, then try to create new pages + * so that the additional allocatable_pages counts (heap_tomb->total_pages) are added. + */ + size_t next_length = heap_allocatable_pages(objspace); + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + next_length += SIZE_POOL_EDEN_HEAP(size_pool)->total_pages; + next_length += SIZE_POOL_TOMB_HEAP(size_pool)->total_pages; + } + + if (next_length > heap_pages_sorted_length) { + heap_pages_expand_sorted_to(objspace, next_length); + } + + GC_ASSERT(heap_allocatable_pages(objspace) + heap_eden_total_pages(objspace) <= heap_pages_sorted_length); + GC_ASSERT(heap_allocated_pages <= heap_pages_sorted_length); +} + +static void +size_pool_allocatable_pages_set(rb_objspace_t *objspace, rb_size_pool_t *size_pool, size_t s) +{ + size_pool->allocatable_pages = s; + heap_pages_expand_sorted(objspace); +} + +static inline void +heap_page_add_freeobj(rb_objspace_t *objspace, struct heap_page *page, VALUE obj) +{ + asan_unpoison_object(obj, false); + + asan_unlock_freelist(page); + + struct free_slot *slot = (struct free_slot *)obj; + slot->flags = 0; + slot->next = page->freelist; + page->freelist = slot; + asan_lock_freelist(page); + + RVALUE_AGE_RESET(obj); + + if (RGENGC_CHECK_MODE && + /* obj should belong to page */ + !(page->start <= (uintptr_t)obj && + (uintptr_t)obj < ((uintptr_t)page->start + (page->total_slots * page->slot_size)) && + obj % BASE_SLOT_SIZE == 0)) { + rb_bug("heap_page_add_freeobj: %p is not rvalue.", (void *)obj); + } + + asan_poison_object(obj); + gc_report(3, objspace, "heap_page_add_freeobj: add %p to freelist\n", (void *)obj); +} + +static size_t +heap_extend_pages(rb_objspace_t *objspace, rb_size_pool_t *size_pool, + size_t free_slots, size_t total_slots, size_t used); + + static void +size_pool_allocatable_pages_expand(rb_objspace_t *objspace, + rb_size_pool_t *size_pool, size_t swept_slots, size_t total_slots, size_t total_pages) +{ + size_t extend_page_count = heap_extend_pages(objspace, size_pool, swept_slots, + total_slots, total_pages); + + if (extend_page_count > size_pool->allocatable_pages) { + size_pool_allocatable_pages_set(objspace, size_pool, extend_page_count); + } +} + +static inline void +heap_add_freepage(rb_heap_t *heap, struct heap_page *page) +{ + asan_unlock_freelist(page); + GC_ASSERT(page->free_slots != 0); + GC_ASSERT(page->freelist != NULL); + + page->free_next = heap->free_pages; + heap->free_pages = page; + + RUBY_DEBUG_LOG("page:%p freelist:%p", (void *)page, (void *)page->freelist); + + asan_lock_freelist(page); +} + +static inline void +heap_add_poolpage(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *page) +{ + asan_unlock_freelist(page); + GC_ASSERT(page->free_slots != 0); + GC_ASSERT(page->freelist != NULL); + + page->free_next = heap->pooled_pages; + heap->pooled_pages = page; + objspace->rincgc.pooled_slots += page->free_slots; + + asan_lock_freelist(page); +} + +static void +heap_unlink_page(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *page) +{ + ccan_list_del(&page->page_node); + heap->total_pages--; + heap->total_slots -= page->total_slots; +} + +static void +gc_aligned_free(void *ptr, size_t size) +{ +#if defined __MINGW32__ + __mingw_aligned_free(ptr); +#elif defined _WIN32 + _aligned_free(ptr); +#elif defined(HAVE_POSIX_MEMALIGN) || defined(HAVE_MEMALIGN) + free(ptr); +#else + free(((void**)ptr)[-1]); +#endif +} + +static void +heap_page_body_free(struct heap_page_body *page_body) +{ + GC_ASSERT((uintptr_t)page_body % HEAP_PAGE_ALIGN == 0); + + if (HEAP_PAGE_ALLOC_USE_MMAP) { +#ifdef HAVE_MMAP + GC_ASSERT(HEAP_PAGE_SIZE % sysconf(_SC_PAGE_SIZE) == 0); + if (munmap(page_body, HEAP_PAGE_SIZE)) { + rb_bug("heap_page_body_free: munmap failed"); + } +#endif + } + else { + gc_aligned_free(page_body, HEAP_PAGE_SIZE); + } +} + +static void +heap_page_free(rb_objspace_t *objspace, struct heap_page *page, bool global_pages_locked) +{ + heap_allocated_pages--; + + decrement_global_allocated_pages(global_pages_locked); + + page->size_pool->total_freed_pages++; + heap_page_body_free(GET_PAGE_BODY(page->start)); + free(page); +} + +static void +set_sorted_page_list_range(struct heap_page **sorted_page_list, size_t list_allocated_pages, uintptr_t *lo, uintptr_t *hi) +{ + struct heap_page *hipage = sorted_page_list[list_allocated_pages - 1]; + uintptr_t himem = (uintptr_t)hipage->start + (hipage->total_slots * hipage->slot_size); + GC_ASSERT(himem <= *hi); + *hi = himem; + + struct heap_page *lopage = sorted_page_list[0]; + uintptr_t lomem = (uintptr_t)lopage->start; + GC_ASSERT(lomem >= *lo); + *lo = lomem; +} + +static void +heap_pages_free_unused_pages(rb_objspace_t *objspace) +{ + size_t i, j; + + bool has_pages_in_tomb_heap = FALSE; + for (i = 0; i < SIZE_POOL_COUNT; i++) { + if (!ccan_list_empty(&SIZE_POOL_TOMB_HEAP(&size_pools[i])->pages)) { + has_pages_in_tomb_heap = TRUE; + break; + } + } + + if (has_pages_in_tomb_heap) { + int unlinked_pages = 0; + for (i = j = 0; j < heap_allocated_pages - unlinked_pages; i++) { + struct heap_page *page = heap_pages_sorted[i]; + + if (page->flags.in_tomb && page->free_slots == page->total_slots) { + heap_unlink_page(objspace, SIZE_POOL_TOMB_HEAP(page->size_pool), page); + page->unlinked = true; + unlinked_pages++; + } + else { + if (i != j) { + heap_pages_sorted[j] = page; + } + j++; + } + } + + set_sorted_page_list_range(heap_pages_sorted, heap_allocated_pages - unlinked_pages, &heap_pages_lomem, &heap_pages_himem); + + GC_ASSERT(j == heap_allocated_pages - unlinked_pages); + + rb_global_space_t *global_space = &rb_global_space; + rb_native_mutex_lock(&global_space->all_pages.global_pages_lock); + + for (i = j = 0; j < all_allocated_pages_global; i++) { + struct heap_page *page = all_pages_sorted_global[i]; + + if (page->unlinked) { + heap_page_free(objspace, page, true); + } + else { + if (i != j) { + all_pages_sorted_global[j] = page; + } + j++; + } + } + + set_sorted_page_list_range(all_pages_sorted_global, all_allocated_pages_global, &all_pages_lomem_global, &all_pages_himem_global); + + GC_ASSERT(j == all_allocated_pages_global); + + rb_native_mutex_unlock(&global_space->all_pages.global_pages_lock); + } +} + +static void * +gc_aligned_malloc(size_t alignment, size_t size) +{ + /* alignment must be a power of 2 */ + GC_ASSERT(((alignment - 1) & alignment) == 0); + GC_ASSERT(alignment % sizeof(void*) == 0); + + void *res; + +#if defined __MINGW32__ + res = __mingw_aligned_malloc(size, alignment); +#elif defined _WIN32 + void *_aligned_malloc(size_t, size_t); + res = _aligned_malloc(size, alignment); +#elif defined(HAVE_POSIX_MEMALIGN) + if (posix_memalign(&res, alignment, size) != 0) { + return NULL; + } +#elif defined(HAVE_MEMALIGN) + res = memalign(alignment, size); +#else + char* aligned; + res = malloc(alignment + size + sizeof(void*)); + aligned = (char*)res + alignment + sizeof(void*); + aligned -= ((VALUE)aligned & (alignment - 1)); + ((void**)aligned)[-1] = res; + res = (void*)aligned; +#endif + + GC_ASSERT((uintptr_t)res % alignment == 0); + + return res; +} + +static struct heap_page_body * +heap_page_body_allocate(void) +{ + struct heap_page_body *page_body; + + if (HEAP_PAGE_ALLOC_USE_MMAP) { +#ifdef HAVE_MMAP + GC_ASSERT(HEAP_PAGE_ALIGN % sysconf(_SC_PAGE_SIZE) == 0); + + char *ptr = mmap(NULL, HEAP_PAGE_ALIGN + HEAP_PAGE_SIZE, + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (ptr == MAP_FAILED) { + return NULL; + } + + char *aligned = ptr + HEAP_PAGE_ALIGN; + aligned -= ((VALUE)aligned & (HEAP_PAGE_ALIGN - 1)); + GC_ASSERT(aligned > ptr); + GC_ASSERT(aligned <= ptr + HEAP_PAGE_ALIGN); + + size_t start_out_of_range_size = aligned - ptr; + GC_ASSERT(start_out_of_range_size % sysconf(_SC_PAGE_SIZE) == 0); + if (start_out_of_range_size > 0) { + if (munmap(ptr, start_out_of_range_size)) { + rb_bug("heap_page_body_allocate: munmap failed for start"); + } + } + + size_t end_out_of_range_size = HEAP_PAGE_ALIGN - start_out_of_range_size; + GC_ASSERT(end_out_of_range_size % sysconf(_SC_PAGE_SIZE) == 0); + if (end_out_of_range_size > 0) { + if (munmap(aligned + HEAP_PAGE_SIZE, end_out_of_range_size)) { + rb_bug("heap_page_body_allocate: munmap failed for end"); + } + } + + page_body = (struct heap_page_body *)aligned; +#endif + } + else { + page_body = gc_aligned_malloc(HEAP_PAGE_ALIGN, HEAP_PAGE_SIZE); + } + + GC_ASSERT((uintptr_t)page_body % HEAP_PAGE_ALIGN == 0); + + return page_body; +} + +static void +insert_into_sorted_page_list(rb_objspace_t *objspace, struct heap_page **sorted_list, size_t allocated_pages, struct heap_page *page, uintptr_t start) +{ + uintptr_t hi, lo, mid; + lo = 0; + hi = (uintptr_t)allocated_pages; + while (lo < hi) { + struct heap_page *mid_page; + + mid = (lo + hi) / 2; + mid_page = sorted_list[mid]; + if ((uintptr_t)mid_page->start < start) { + lo = mid + 1; + } + else if ((uintptr_t)mid_page->start > start) { + hi = mid; + } + else { + rb_bug("same heap page is allocated: %p at %"PRIuVALUE, (void *)GET_PAGE_BODY(start), (VALUE)mid); + } + } + + if (hi < (uintptr_t)allocated_pages) { + MEMMOVE(&sorted_list[hi+1], &sorted_list[hi], struct heap_page_header*, allocated_pages - hi); + } + + sorted_list[hi] = page; +} + +static struct heap_page * +heap_page_allocate(rb_objspace_t *objspace, rb_size_pool_t *size_pool) +{ + uintptr_t start, end, p; + struct heap_page *page; + size_t stride = size_pool->slot_size; + unsigned int limit = (unsigned int)((HEAP_PAGE_SIZE - sizeof(struct heap_page_header)))/(int)stride; + + /* assign heap_page body (contains heap_page_header and RVALUEs) */ + struct heap_page_body *page_body = heap_page_body_allocate(); + if (page_body == 0) { + rb_memerror(); + } + + /* assign heap_page entry */ + page = calloc1(sizeof(struct heap_page)); + if (page == 0) { + heap_page_body_free(page_body); + rb_memerror(); + } + + /* adjust obj_limit (object number available in this page) */ + start = (uintptr_t)((VALUE)page_body + sizeof(struct heap_page_header)); + + if (start % BASE_SLOT_SIZE != 0) { + int delta = BASE_SLOT_SIZE - (start % BASE_SLOT_SIZE); + start = start + delta; + GC_ASSERT(NUM_IN_PAGE(start) == 0 || NUM_IN_PAGE(start) == 1); + + /* Find a num in page that is evenly divisible by `stride`. + * This is to ensure that objects are aligned with bit planes. + * In other words, ensure there are an even number of objects + * per bit plane. */ + if (NUM_IN_PAGE(start) == 1) { + start += stride - BASE_SLOT_SIZE; + } + + GC_ASSERT(NUM_IN_PAGE(start) * BASE_SLOT_SIZE % stride == 0); + + limit = (HEAP_PAGE_SIZE - (int)(start - (uintptr_t)page_body))/(int)stride; + } + end = start + (limit * (int)stride); + + /* setup heap_pages_sorted */ + insert_into_sorted_page_list(objspace, heap_pages_sorted, heap_allocated_pages, page, start); + + heap_allocated_pages++; + + rb_global_space_t *global_space = &rb_global_space; + rb_native_mutex_lock(&global_space->all_pages.global_pages_lock); + + insert_into_sorted_page_list(objspace, all_pages_sorted_global, all_allocated_pages_global, page, start); + all_allocated_pages_global++; + + rb_native_mutex_unlock(&global_space->all_pages.global_pages_lock); + + GC_ASSERT(heap_eden_total_pages(objspace) + heap_allocatable_pages(objspace) <= heap_pages_sorted_length); + GC_ASSERT(heap_eden_total_pages(objspace) + heap_tomb_total_pages(objspace) == heap_allocated_pages - 1); + GC_ASSERT(heap_allocated_pages <= heap_pages_sorted_length); + + size_pool->total_allocated_pages++; + + if (heap_allocated_pages > heap_pages_sorted_length) { + rb_bug("heap_page_allocate: allocated(%"PRIdSIZE") > sorted(%"PRIdSIZE")", + heap_allocated_pages, heap_pages_sorted_length); + } + + if (heap_pages_lomem == 0 || heap_pages_lomem > start) heap_pages_lomem = start; + if (heap_pages_himem < end) heap_pages_himem = end; + + rb_native_mutex_lock(&global_space->all_pages.global_pages_lock); + + if (all_pages_lomem_global == 0 || all_pages_lomem_global > start) all_pages_lomem_global = start; + if (all_pages_himem_global < end) all_pages_himem_global = end; + + rb_native_mutex_unlock(&global_space->all_pages.global_pages_lock); + + page->start = start; + page->total_slots = limit; + page->slot_size = size_pool->slot_size; + page->size_pool = size_pool; + page_body->header.page = page; + + page->ractor = objspace->local_data.ractor; + page->objspace = objspace; + + for (p = start; p != end; p += stride) { + gc_report(3, objspace, "assign_heap_page: %p is added to freelist\n", (void *)p); + heap_page_add_freeobj(objspace, page, (VALUE)p); + } + page->free_slots = limit; + + asan_lock_freelist(page); + return page; +} + +static struct heap_page * +heap_page_resurrect(rb_objspace_t *objspace, rb_size_pool_t *size_pool) +{ + struct heap_page *page = 0, *next; + + ccan_list_for_each_safe(&SIZE_POOL_TOMB_HEAP(size_pool)->pages, page, next, page_node) { + asan_unlock_freelist(page); + if (page->freelist != NULL) { + heap_unlink_page(objspace, &size_pool->tomb_heap, page); + asan_lock_freelist(page); + return page; + } + } + + return NULL; +} + +static struct heap_page * +heap_page_create(rb_objspace_t *objspace, rb_size_pool_t *size_pool) +{ + struct heap_page *page; + const char *method = "recycle"; + + size_pool->allocatable_pages--; + + page = heap_page_resurrect(objspace, size_pool); + + if (page == NULL) { + page = heap_page_allocate(objspace, size_pool); + method = "allocate"; + } + if (0) fprintf(stderr, "heap_page_create: %s - %p, " + "heap_allocated_pages: %"PRIdSIZE", " + "heap_allocated_pages: %"PRIdSIZE", " + "tomb->total_pages: %"PRIdSIZE"\n", + method, (void *)page, heap_pages_sorted_length, heap_allocated_pages, SIZE_POOL_TOMB_HEAP(size_pool)->total_pages); + return page; +} + +static void +heap_add_page(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap, struct heap_page *page) +{ + /* Adding to eden heap during incremental sweeping is forbidden */ + GC_ASSERT(!(heap == SIZE_POOL_EDEN_HEAP(size_pool) && heap->sweeping_page)); + page->flags.in_tomb = (heap == SIZE_POOL_TOMB_HEAP(size_pool)); + ccan_list_add_tail(&heap->pages, &page->page_node); + heap->total_pages++; + heap->total_slots += page->total_slots; +} + +rb_heap_t * +select_heap(rb_objspace_t *objspace, int size_pool_idx, bool eden) +{ + return eden ? SIZE_POOL_EDEN_HEAP(&size_pools[size_pool_idx]) : SIZE_POOL_TOMB_HEAP(&size_pools[size_pool_idx]); +} + +void +absorb_page_into_objspace(rb_objspace_t *objspace, struct heap_page *page, int size_pool_idx, bool eden) +{ + VM_ASSERT (page->objspace != objspace); + VM_ASSERT(objspace->local_data.currently_absorbing); + + ccan_list_del_init(&page->page_node); + + rb_heap_t *heap = select_heap(objspace, size_pool_idx, eden); + + rb_size_pool_t *size_pool = &size_pools[size_pool_idx]; + + heap_add_page(objspace, size_pool, heap, page); + insert_into_sorted_page_list(objspace, heap_pages_sorted, heap_allocated_pages, page, page->start); + size_pool->allocatable_pages--; + heap_allocated_pages++; + + struct heap_page_body *page_body = GET_PAGE_BODY(page->start); + size_t stride = (size_pool)->slot_size; + unsigned int limit = (HEAP_PAGE_SIZE - (int)(page->start - (uintptr_t)page_body))/(int)stride; + uintptr_t end = page->start + (limit * (int)stride); + if (heap_pages_lomem == 0 || heap_pages_lomem > page->start) heap_pages_lomem = page->start; + if (heap_pages_himem < end) heap_pages_himem = end; + + RUBY_ATOMIC_PTR_EXCHANGE(page->ractor, objspace->local_data.ractor); + RUBY_ATOMIC_PTR_EXCHANGE(page->objspace, objspace); + + //TODO: What if another Ractor tries to access this pointer at this exact moment? + page->size_pool = size_pool; +} + +void +heap_append_free_page(rb_heap_t *heap, struct heap_page *page) +{ + struct heap_page *end_page = heap->free_pages; + if (end_page) { + while (end_page->free_next) end_page = end_page->free_next; + end_page->free_next = page; + } + else { + heap->free_pages = page; + } +} + +struct heap_page * +get_freepages(rb_heap_t *heap) +{ + return heap->free_pages; +} + +struct heap_page * +heap_get_top_page(rb_heap_t *heap) +{ + return ccan_list_top(&heap->pages, struct heap_page, page_node); +} + +size_t +total_pages_in_heap(rb_heap_t *heap) +{ + return heap->total_pages; +} + +void +size_pool_allocatable_pages_update(rb_objspace_t *objspace, size_t additional_pages[SIZE_POOL_COUNT]) +{ + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + size_pools[i].allocatable_pages += additional_pages[i]; + } + heap_pages_expand_sorted(objspace); +} + +void +absorption_finished(rb_objspace_t *objspace) +{ + objspace->rgengc.need_major_gc |= GPR_FLAG_MAJOR_BY_ABSORB; +} + +void +rb_disconnect_ractor_from_unabsorbed_objspace(rb_ractor_t *r) +{ + r->local_objspace->local_data.ractor = NULL; +} + +struct objspace_local_data * +objspace_get_local_data(rb_objspace_t *objspace) +{ + return &objspace->local_data; +} + +static void +heap_assign_page(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap) +{ + struct heap_page *page = heap_page_create(objspace, size_pool); + heap_add_page(objspace, size_pool, heap, page); + heap_add_freepage(heap, page); +} + +#if GC_CAN_COMPILE_COMPACTION +static void +heap_add_pages(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap, size_t add) +{ + size_t i; + + size_pool_allocatable_pages_set(objspace, size_pool, add); + + for (i = 0; i < add; i++) { + heap_assign_page(objspace, size_pool, heap); + } + + GC_ASSERT(size_pool->allocatable_pages == 0); +} +#endif + +static size_t +heap_extend_pages(rb_objspace_t *objspace, rb_size_pool_t *size_pool, size_t free_slots, size_t total_slots, size_t used) +{ + double goal_ratio = gc_params.heap_free_slots_goal_ratio; + size_t next_used; + + if (goal_ratio == 0.0) { + next_used = (size_t)(used * gc_params.growth_factor); + } + else if (total_slots == 0) { + next_used = minimum_pages_for_size_pool(objspace, size_pool); + } + else { + /* Find `f' where free_slots = f * total_slots * goal_ratio + * => f = (total_slots - free_slots) / ((1 - goal_ratio) * total_slots) + */ + double f = (double)(total_slots - free_slots) / ((1 - goal_ratio) * total_slots); + + if (f > gc_params.growth_factor) f = gc_params.growth_factor; + if (f < 1.0) f = 1.1; + + next_used = (size_t)(f * used); + + if (0) { + fprintf(stderr, + "free_slots(%8"PRIuSIZE")/total_slots(%8"PRIuSIZE")=%1.2f," + " G(%1.2f), f(%1.2f)," + " used(%8"PRIuSIZE") => next_used(%8"PRIuSIZE")\n", + free_slots, total_slots, free_slots/(double)total_slots, + goal_ratio, f, used, next_used); + } + } + + if (gc_params.growth_max_slots > 0) { + size_t max_used = (size_t)(used + gc_params.growth_max_slots/HEAP_PAGE_OBJ_LIMIT); + if (next_used > max_used) next_used = max_used; + } + + size_t extend_page_count = next_used - used; + /* Extend by at least 1 page. */ + if (extend_page_count == 0) extend_page_count = 1; + + return extend_page_count; +} + +static int +heap_increment(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap) +{ + if (size_pool->allocatable_pages > 0) { + gc_report(1, objspace, "heap_increment: heap_pages_sorted_length: %"PRIdSIZE", " + "heap_pages_inc: %"PRIdSIZE", heap->total_pages: %"PRIdSIZE"\n", + heap_pages_sorted_length, size_pool->allocatable_pages, heap->total_pages); + + GC_ASSERT(heap_allocatable_pages(objspace) + heap_eden_total_pages(objspace) <= heap_pages_sorted_length); + GC_ASSERT(heap_allocated_pages <= heap_pages_sorted_length); + + heap_assign_page(objspace, size_pool, heap); + return TRUE; + } + return FALSE; +} + +static void +gc_continue(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap) +{ + LOCAL_GC_BEGIN(objspace); + { + gc_enter(objspace, gc_enter_event_continue); + + /* Continue marking if in incremental marking. */ + if (is_incremental_marking(objspace)) { + if (gc_marks_continue(objspace, size_pool, heap)) { + gc_sweep(objspace); + } + } + + /* Continue sweeping if in lazy sweeping or the previous incremental + * marking finished and did not yield a free page. */ + if (heap->free_pages == NULL && is_lazy_sweeping(objspace)) { + gc_sweep_continue(objspace, size_pool, heap); + } + + gc_exit(objspace, gc_enter_event_continue); + } + LOCAL_GC_END(objspace); +} + +static void +heap_prepare(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap) +{ + GC_ASSERT(heap->free_pages == NULL); + + if (!borrowing) { + /* Continue incremental marking or lazy sweeping, if in any of those steps. */ + gc_continue(objspace, size_pool, heap); + } + + /* If we still don't have a free page and not allowed to create a new page, + * we should start a new GC cycle. */ + if (heap->free_pages == NULL && + (will_be_incremental_marking(objspace) || + (heap_increment(objspace, size_pool, heap) == FALSE))) { + if (borrowing) { + size_pool_allocatable_pages_set(objspace, size_pool, 1); + if (!heap_increment(objspace, size_pool, heap)) { + rb_bug("cannot create a new borrowing page in target Ractor"); + } + } + else if (garbage_collect(objspace, GPR_FLAG_NEWOBJ, false) == FALSE) { + rb_memerror(); + } + else { + if (size_pool->allocatable_pages == 0 && !gc_config_full_mark_val) { + size_pool_allocatable_pages_expand(objspace, size_pool, + size_pool->freed_slots + size_pool->empty_slots, + heap->total_slots + SIZE_POOL_TOMB_HEAP(size_pool)->total_slots, + heap->total_pages + SIZE_POOL_TOMB_HEAP(size_pool)->total_pages); + GC_ASSERT(size_pool->allocatable_pages > 0); + } + /* Do steps of incremental marking or lazy sweeping if the GC run permits. */ + gc_continue(objspace, size_pool, heap); + + /* If we're not incremental marking (e.g. a minor GC) or finished + * 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 (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 + if (garbage_collect(objspace, GPR_FLAG_NEWOBJ, false) == FALSE) { + rb_memerror(); + } + else { + /* Do steps of incremental marking or lazy sweeping. */ + gc_continue(objspace, size_pool, heap); + + if (heap->free_pages == NULL && + !heap_increment(objspace, size_pool, heap)) { + rb_bug("cannot create a new page after major GC"); + } + } + } + } + } + } + + GC_ASSERT(heap->free_pages != NULL); +} + +static inline VALUE +newobj_fill(VALUE obj, VALUE v1, VALUE v2, VALUE v3) +{ + VALUE *p = (VALUE *)obj; + p[2] = v1; + p[3] = v2; + p[4] = v3; + return obj; +} + +#if GC_DEBUG +static inline const char* +rb_gc_impl_source_location_cstr(int *ptr) +{ + /* We could directly refer `rb_source_location_cstr()` before, but not any + * longer. We have to heavy lift using our debugging API. */ + if (! ptr) { + return NULL; + } + else if (! (*ptr = rb_sourceline())) { + return NULL; + } + else { + return rb_sourcefile(); + } +} +#endif + +static inline VALUE +newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace, VALUE obj) +{ +#if !__has_feature(memory_sanitizer) + GC_ASSERT(BUILTIN_TYPE(obj) == T_NONE); + GC_ASSERT((flags & FL_WB_PROTECTED) == 0); +#endif + RBASIC(obj)->flags = flags; + *((VALUE *)&RBASIC(obj)->klass) = klass; + + int t = flags & RUBY_T_MASK; + if (t == T_CLASS || t == T_MODULE || t == T_ICLASS) { + RVALUE_AGE_SET_CANDIDATE(objspace, obj); + } + +#if RACTOR_CHECK_MODE + void rb_ractor_setup_belonging(VALUE obj); + rb_ractor_setup_belonging(obj); +#endif + +#if RGENGC_CHECK_MODE + newobj_fill(obj, 0, 0, 0); + + HEAP_LOCK_ENTER(objspace); + { + check_rvalue_consistency(objspace, obj); + + GC_ASSERT(RVALUE_MARKED(objspace, obj) == FALSE); + GC_ASSERT(RVALUE_MARKING(objspace, obj) == FALSE); + GC_ASSERT(RVALUE_OLD_P(objspace, obj) == FALSE); + GC_ASSERT(RVALUE_WB_UNPROTECTED(objspace, obj) == FALSE); + GC_ASSERT(RVALUE_LOCAL_IMMUNE(objspace, obj) == FALSE); + + if (RVALUE_REMEMBERED(objspace, obj)) rb_bug("newobj: %s is remembered.", rb_obj_info(obj)); + } + HEAP_LOCK_LEAVE(objspace); +#endif + + if (RB_UNLIKELY(wb_protected == FALSE)) { + MARK_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(obj), obj); + } + +#if RGENGC_PROFILE + if (wb_protected) { + objspace->profile.total_generated_normal_object_count++; +#if RGENGC_PROFILE >= 2 + objspace->profile.generated_normal_object_count_types[BUILTIN_TYPE(obj)]++; +#endif + } + else { + objspace->profile.total_generated_shady_object_count++; +#if RGENGC_PROFILE >= 2 + objspace->profile.generated_shady_object_count_types[BUILTIN_TYPE(obj)]++; +#endif + } +#endif + +#if GC_DEBUG + GET_RVALUE_OVERHEAD(obj)->file = rb_gc_impl_source_location_cstr(&GET_RVALUE_OVERHEAD(obj)->line); + GC_ASSERT(!SPECIAL_CONST_P(obj)); /* check alignment */ +#endif + + gc_report(5, objspace, "newobj: %s\n", rb_obj_info(obj)); + + RUBY_DEBUG_LOG("obj:%p (%s)", (void *)obj, rb_obj_info(obj)); + return obj; +} + +size_t +rb_gc_impl_obj_slot_size(VALUE obj) +{ + return GET_HEAP_PAGE(obj)->slot_size - RVALUE_OVERHEAD; +} + +static inline size_t +size_pool_slot_size(unsigned char pool_id) +{ + GC_ASSERT(pool_id < SIZE_POOL_COUNT); + + size_t slot_size = (1 << pool_id) * BASE_SLOT_SIZE; + +#if RGENGC_CHECK_MODE + rb_objspace_t *objspace = rb_gc_get_objspace(); + GC_ASSERT(size_pools[pool_id].slot_size == (short)slot_size); +#endif + + slot_size -= RVALUE_OVERHEAD; + + return slot_size; +} + +bool +rb_gc_impl_size_allocatable_p(size_t size) +{ + return size <= size_pool_slot_size(SIZE_POOL_COUNT - 1); +} + +static inline VALUE +ractor_cache_allocate_slot(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, + size_t size_pool_idx, bool borrowing) +{ + rb_ractor_newobj_size_pool_cache_t *size_pool_cache = &cache->size_pool_caches[size_pool_idx]; + struct free_slot *p = size_pool_cache->freelist; + + if (!borrowing && is_incremental_marking(objspace)) { + // Not allowed to allocate without running an incremental marking step + if (cache->incremental_mark_step_allocated_slots >= INCREMENTAL_MARK_STEP_ALLOCATIONS) { + return Qfalse; + } + + if (p) { + cache->incremental_mark_step_allocated_slots++; + } + } + + if (p) { + VALUE obj = (VALUE)p; + MAYBE_UNUSED(const size_t) stride = size_pool_slot_size(size_pool_idx); + size_pool_cache->freelist = p->next; + asan_unpoison_memory_region(p, stride, true); +#if RGENGC_CHECK_MODE + GC_ASSERT(rb_gc_impl_obj_slot_size(obj) == stride); + // zero clear + MEMZERO((char *)obj, char, stride); +#endif + return obj; + } + else { + return Qfalse; + } +} + +static struct heap_page * +heap_next_free_page(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap) +{ + struct heap_page *page; + + if (heap->free_pages == NULL) { + heap_prepare(objspace, size_pool, heap, borrowing); + } + + page = heap->free_pages; + heap->free_pages = page->free_next; + + GC_ASSERT(page->free_slots != 0); + + asan_unlock_freelist(page); + + return page; +} + +static inline void +ractor_cache_set_page(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t size_pool_idx, + struct heap_page *page) +{ + gc_report(3, objspace, "ractor_set_cache: Using page %p\n", (void *)GET_PAGE_BODY(page->start)); + + rb_ractor_newobj_size_pool_cache_t *size_pool_cache = &cache->size_pool_caches[size_pool_idx]; + + GC_ASSERT(size_pool_cache->freelist == NULL); + GC_ASSERT(page->free_slots != 0); + GC_ASSERT(page->freelist != NULL); + + size_pool_cache->using_page = page; + size_pool_cache->freelist = page->freelist; + page->free_slots = 0; + page->freelist = NULL; + + asan_unpoison_object((VALUE)size_pool_cache->freelist, false); + GC_ASSERT(RB_TYPE_P((VALUE)size_pool_cache->freelist, T_NONE)); + asan_poison_object((VALUE)size_pool_cache->freelist); +} + +static inline size_t +size_pool_idx_for_size(size_t size) +{ + size += RVALUE_OVERHEAD; + + size_t slot_count = CEILDIV(size, BASE_SLOT_SIZE); + + /* size_pool_idx is ceil(log2(slot_count)) */ + size_t size_pool_idx = 64 - nlz_int64(slot_count - 1); + + if (size_pool_idx >= SIZE_POOL_COUNT) { + rb_bug("size_pool_idx_for_size: allocation size too large " + "(size=%"PRIuSIZE"u, size_pool_idx=%"PRIuSIZE"u)", size, size_pool_idx); + } + +#if RGENGC_CHECK_MODE + rb_objspace_t *objspace = rb_gc_get_objspace(); + GC_ASSERT(size <= (size_t)size_pools[size_pool_idx].slot_size); + if (size_pool_idx > 0) GC_ASSERT(size > (size_t)size_pools[size_pool_idx - 1].slot_size); +#endif + + return size_pool_idx; +} + +size_t +rb_gc_impl_size_pool_id_for_size(void *objspace_ptr, size_t size) +{ + return size_pool_idx_for_size(size); +} + + +static size_t size_pool_sizes[SIZE_POOL_COUNT + 1] = { 0 }; + +size_t * +rb_gc_impl_size_pool_sizes(void *objspace_ptr) +{ + if (size_pool_sizes[0] == 0) { + for (unsigned char i = 0; i < SIZE_POOL_COUNT; i++) { + size_pool_sizes[i] = size_pool_slot_size(i); + } + } + + return size_pool_sizes; +} + +static void gc_ractor_newobj_size_pool_cache_clear(rb_ractor_newobj_size_pool_cache_t *cache); + +static VALUE +newobj_alloc(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t size_pool_idx) +{ + rb_size_pool_t *size_pool = &size_pools[size_pool_idx]; + rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); + + VALUE obj = ractor_cache_allocate_slot(objspace, cache, size_pool_idx, false); + + if (RB_UNLIKELY(obj == Qfalse)) { + + HEAP_LOCK_ENTER(objspace); + { + if (is_incremental_marking(objspace)) { + gc_continue(objspace, size_pool, heap); + cache->incremental_mark_step_allocated_slots = 0; + + // Retry allocation after resetting incremental_mark_step_allocated_slots + obj = ractor_cache_allocate_slot(objspace, cache, size_pool_idx, false); + } + + if (obj == Qfalse) { + // Get next free page (possibly running GC) + struct heap_page *page = heap_next_free_page(objspace, size_pool, heap, false); + + ractor_cache_set_page(objspace, cache, size_pool_idx, page); + + // Retry allocation after moving to new page + obj = ractor_cache_allocate_slot(objspace, cache, size_pool_idx, false); + } + } + HEAP_LOCK_LEAVE(objspace); + + } + + if (RB_UNLIKELY(obj == Qfalse)) { + rb_memerror(); + } + + RUBY_ATOMIC_SIZE_ADD(size_pool->total_allocated_objects, 1); + + return obj; +} + +static VALUE +newobj_alloc_borrowing(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t size_pool_idx) +{ + rb_ractor_t *alloc_target_ractor = objspace->local_data.ractor; + rb_ractor_t *cr = GET_RACTOR(); + + VM_ASSERT(alloc_target_ractor->borrowing_sync.lock_owner == cr); + + rb_size_pool_t *size_pool = &size_pools[size_pool_idx]; + rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); + + bool borrowable_page_locked = false; + borrowable_page_locked = !rb_native_mutex_trylock(&alloc_target_ractor->borrowing_sync.page_lock[size_pool_idx]); + if (borrowable_page_locked) { + alloc_target_ractor->borrowing_sync.page_lock_owner[size_pool_idx] = cr; + } + + bool need_new_borrowing_page = !borrowable_page_locked && alloc_target_ractor->borrowing_sync.page_recently_locked[size_pool_idx]; + + VALUE obj; + if (need_new_borrowing_page) { + obj = Qfalse; + } + else { + obj = ractor_cache_allocate_slot(objspace, cache, size_pool_idx, true); + } + + if (RB_UNLIKELY(obj == Qfalse)) { + + HEAP_LOCK_ENTER(objspace); + { + if (obj == Qfalse) { + // Get next free page (possibly running GC) + struct heap_page *page = heap_next_free_page(objspace, size_pool, heap, true); + if (need_new_borrowing_page) + { + rb_ractor_newobj_size_pool_cache_t *size_pool_cache = &cache->size_pool_caches[size_pool_idx]; + gc_ractor_newobj_size_pool_cache_clear(size_pool_cache); + } + + ractor_cache_set_page(objspace, cache, size_pool_idx, page); + + // Retry allocation after moving to new page + obj = ractor_cache_allocate_slot(objspace, cache, size_pool_idx, true); + } + } + HEAP_LOCK_LEAVE(objspace); + } + + if (RB_UNLIKELY(obj == Qfalse)) { + rb_memerror(); + } + + RUBY_ATOMIC_SIZE_ADD(size_pool->newly_created_by_borrowing_count, 1); + + if (borrowable_page_locked) { + alloc_target_ractor->borrowing_sync.page_lock_owner[size_pool_idx] = NULL; + rb_native_mutex_unlock(&alloc_target_ractor->borrowing_sync.page_lock[size_pool_idx]); + } + alloc_target_ractor->borrowing_sync.page_recently_locked[size_pool_idx] = false; + + return obj; +} + +ALWAYS_INLINE(static VALUE newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, int wb_protected, size_t size_pool_idx, bool borrowing)); + +static inline VALUE +newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, int wb_protected, size_t size_pool_idx, bool borrowing) +{ + VALUE obj; + unsigned int lev; + if (RB_UNLIKELY((during_gc) || ruby_gc_stressful)) { + if (during_gc && !(borrowing && objspace->local_data.waiting_for_object_graph_safety)) { + dont_gc_on(); + during_gc = 0; + rb_bug("object allocation during garbage collection phase"); + } + + if (ruby_gc_stressful) { + if (!garbage_collect(objspace, GPR_FLAG_NEWOBJ, false)) { + rb_memerror(); + } + } + } + + obj = UNLIKELY(borrowing) ? newobj_alloc_borrowing(objspace, cache, size_pool_idx) : newobj_alloc(objspace, cache, size_pool_idx); + + HEAP_LOCK_ENTER(objspace); + { + newobj_init(klass, flags, wb_protected, objspace, obj); + + gc_event_hook_prep(objspace, RUBY_INTERNAL_EVENT_NEWOBJ, obj, newobj_zero_slot(obj)); + } + HEAP_LOCK_LEAVE(objspace); + + return obj; +} + +NOINLINE(static VALUE newobj_slowpath_wb_protected(VALUE klass, VALUE flags, + rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t size_pool_idx, bool borrowing)); +NOINLINE(static VALUE newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, + rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t size_pool_idx, bool borrowing)); + +static VALUE +newobj_slowpath_wb_protected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t size_pool_idx) +{ + return newobj_slowpath(klass, flags, objspace, cache, TRUE, size_pool_idx, borrowing); +} + +static VALUE +newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t size_pool_idx) +{ + return newobj_slowpath(klass, flags, objspace, cache, FALSE, size_pool_idx, borrowing); +} + +static inline int gc_mark_set(rb_objspace_t *objspace, VALUE obj); +static void gc_grey(rb_objspace_t *objspace, VALUE obj); +static void gc_aging(VALUE obj); + +VALUE +rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, bool wb_protected, size_t alloc_size) +{ + VALUE obj; + rb_objspace_t *objspace = objspace_ptr; + + RB_DEBUG_COUNTER_INC(obj_newobj); + (void)RB_DEBUG_COUNTER_INC_IF(obj_newobj_wb_unprotected, !wb_protected); + + if (RB_UNLIKELY(stress_to_class)) { + long cnt = RARRAY_LEN(stress_to_class); + for (long i = 0; i < cnt; i++) { + if (klass == RARRAY_AREF(stress_to_class, i)) rb_memerror(); + } + } + + size_t size_pool_idx = size_pool_idx_for_size(alloc_size); + + rb_ractor_newobj_cache_t *cache = (rb_ractor_newobj_cache_t *)cache_ptr; + + if (!RB_UNLIKELY(during_gc || ruby_gc_stressful) && + wb_protected) { + obj = RB_UNLIKELY(borrowing) ? newobj_alloc_borrowing(objspace, cache, size_pool_idx) : newobj_alloc(objspace, cache, size_pool_idx); + newobj_init(klass, flags, wb_protected, objspace, obj); + } + else { + RB_DEBUG_COUNTER_INC(obj_newobj_slowpath); + + obj = wb_protected ? + newobj_slowpath_wb_protected(klass, flags, objspace, cache, size_pool_idx, borrowing) : + newobj_slowpath_wb_unprotected(klass, flags, objspace, cache, size_pool_idx, borrowing); + } + + if (borrowing && (is_incremental_marking(objspace) || objspace->local_data.waiting_for_object_graph_safety)) { + if (gc_mark_set(objspace, obj)) { + gc_aging(obj); + gc_grey(objspace, obj); + } + } + if (borrowing) { + register_received_obj(objspace, GET_RACTOR()->borrowing_sync.borrowing_id, obj); + rb_borrowing_sync_unlock(objspace->local_data.ractor); + + return newobj_fill(obj, v1, v2, v3); +} + +static int +ptr_in_page_body_p(const void *ptr, const void *memb) +{ + struct heap_page *page = *(struct heap_page **)memb; + uintptr_t p_body = (uintptr_t)GET_PAGE_BODY(page->start); + + if ((uintptr_t)ptr >= p_body) { + return (uintptr_t)ptr < (p_body + HEAP_PAGE_SIZE) ? 0 : 1; + } + else { + return -1; + } +} + +PUREFUNC(static inline struct heap_page *heap_page_for_ptr(rb_objspace_t *objspace, uintptr_t ptr);) +static inline struct heap_page * +heap_page_for_ptr(rb_objspace_t *objspace, uintptr_t ptr) +{ + struct heap_page **res; + + if (ptr < (uintptr_t)heap_pages_lomem || + ptr > (uintptr_t)heap_pages_himem) { + return NULL; + } + + res = bsearch((void *)ptr, heap_pages_sorted, + (size_t)heap_allocated_pages, sizeof(struct heap_page *), + ptr_in_page_body_p); + + if (res) { + return *res; + } + else { + return NULL; + } +} + +PUREFUNC(static inline struct heap_page *global_heap_page_for_ptr(rb_objspace_t *objspace, uintptr_t ptr);) +static inline struct heap_page * +global_heap_page_for_ptr(rb_objspace_t *objspace, uintptr_t ptr) +{ + rb_global_space_t *global_space = &rb_global_space; + struct heap_page **res; + + if (ptr < (uintptr_t)all_pages_lomem_global || + ptr > (uintptr_t)all_pages_himem_global) { + return NULL; + } + + res = bsearch((void *)ptr, all_pages_sorted_global, + (size_t)all_allocated_pages_global, sizeof(struct heap_page *), + ptr_in_page_body_p); + + if (res) { + return *res; + } + else { + return NULL; + } +} + +PUREFUNC(static inline bool pointer_is_on_page(register uintptr_t p, register struct heap_page *page);) +static inline bool +pointer_is_on_page(register uintptr_t p, register struct heap_page *page) +{ + if (page) { + RB_DEBUG_COUNTER_INC(gc_isptr_maybe); + if (page->flags.in_tomb) { + return FALSE; + } + else { + if (p < page->start) return FALSE; + if (p >= page->start + (page->total_slots * page->slot_size)) return FALSE; + if ((NUM_IN_PAGE(p) * BASE_SLOT_SIZE) % page->slot_size != 0) return FALSE; + + return TRUE; + } + } + return FALSE; +} + +PUREFUNC(static inline bool heap_page_possible(rb_objspace_t *objspace, register uintptr_t p, uintptr_t lomem, uintptr_t himem);) +static inline bool +heap_page_possible(rb_objspace_t *objspace, register uintptr_t p, uintptr_t lomem, uintptr_t himem) +{ + RB_DEBUG_COUNTER_INC(gc_isptr_trial); + + if (p < lomem || p > himem) return FALSE; + RB_DEBUG_COUNTER_INC(gc_isptr_range); + + if (p % BASE_SLOT_SIZE != 0) return FALSE; + RB_DEBUG_COUNTER_INC(gc_isptr_align); + + return TRUE; +} + +PUREFUNC(static inline bool is_pointer_to_local_heap(rb_objspace_t *objspace, const void *ptr);) +static inline bool +is_pointer_to_local_heap(rb_objspace_t *objspace, const void *ptr) +{ + register uintptr_t p = (uintptr_t)ptr; + register struct heap_page *page = NULL; + + if (during_gc) { + if (heap_page_possible(objspace, p, heap_pages_lomem, heap_pages_himem) == TRUE) { + page = heap_page_for_ptr(objspace, (uintptr_t)ptr); + } + return pointer_is_on_page(p, page); + } + else { + int ret; + HEAP_LOCK_ENTER(objspace); + { + if (heap_page_possible(objspace, p, heap_pages_lomem, heap_pages_himem) == TRUE) { + page = heap_page_for_ptr(objspace, (uintptr_t)ptr); + } + ret = pointer_is_on_page(p, page); + } + HEAP_LOCK_LEAVE(objspace); + return ret; + } +} + +PUREFUNC(static inline bool is_pointer_to_global_heap(rb_objspace_t *objspace, const void *ptr);) +static inline bool +is_pointer_to_global_heap(rb_objspace_t *objspace, const void *ptr) +{ + rb_global_space_t *global_space = &rb_global_space; + register uintptr_t p = (uintptr_t)ptr; + register struct heap_page *page = NULL; + + if (objspace->flags.during_global_gc) { + if (heap_page_possible(global_space, p, all_pages_lomem_global, all_pages_himem_global) == TRUE) { + page = global_heap_page_for_ptr(objspace, (uintptr_t)ptr); + } + return pointer_is_on_page(p, page); + } + else { + rb_objspace_t *page_objspace; + int ret; + + rb_native_mutex_lock(&global_space->all_pages.global_pages_lock); + if (heap_page_possible(global_space, p, all_pages_lomem_global, all_pages_himem_global) == TRUE) { + page = global_heap_page_for_ptr(objspace, (uintptr_t)ptr); + if (page) { + page_objspace = page->objspace; + rb_native_mutex_unlock(&global_space->all_pages.global_pages_lock); + HEAP_LOCK_ENTER(page_objspace); + { + ret = pointer_is_on_page(p, page); + } + HEAP_LOCK_LEAVE(page_objspace); + return ret; + } + } + rb_native_mutex_unlock(&global_space->all_pages.global_pages_lock); + return FALSE; + } +} + +PUREFUNC(static inline bool is_pointer_to_heap(rb_objspace_t *objspace, const void *ptr);) +static inline bool +is_pointer_to_heap(rb_objspace_t *objspace, const void *ptr) +{ + if (is_pointer_to_local_heap(objspace, ptr)) { + return TRUE; + } + else if (!objspace->belong_to_single_main_ractor) { + int ret; + SUSPEND_HEAP_LOCK_BEGIN(objspace); + { + ret = is_pointer_to_global_heap(objspace, ptr); + } + SUSPEND_HEAP_LOCK_END(objspace); + return ret; + } + else { + return FALSE; + } +} + +bool +rb_gc_impl_pointer_to_heap_p(void *objspace_ptr, const void *ptr) +{ + return is_pointer_to_heap(objspace_ptr, ptr); +} + +#define ZOMBIE_OBJ_KEPT_FLAGS (FL_SEEN_OBJ_ID | FL_FINALIZE) + +void +rb_gc_impl_make_zombie(void *objspace_ptr, VALUE obj, void (*dfree)(void *), void *data) +{ + rb_objspace_t *objspace = objspace_ptr; + + struct RZombie *zombie = RZOMBIE(obj); + zombie->basic.flags = T_ZOMBIE | (zombie->basic.flags & ZOMBIE_OBJ_KEPT_FLAGS); + zombie->dfree = dfree; + zombie->data = data; + VALUE prev, next = heap_pages_deferred_final; + do { + zombie->next = prev = next; + next = RUBY_ATOMIC_VALUE_CAS(heap_pages_deferred_final, prev, obj); + } while (next != prev); + + struct heap_page *page = GET_HEAP_PAGE(obj); + page->final_slots++; + heap_pages_final_slots++; +} + +static void +obj_free_object_id(rb_objspace_t *objspace, VALUE obj) +{ + st_data_t o = (st_data_t)obj, id; + + GC_ASSERT(BUILTIN_TYPE(obj) == T_NONE || FL_TEST(obj, FL_SEEN_OBJ_ID)); + FL_UNSET(obj, FL_SEEN_OBJ_ID); + + if (!delete_from_obj_id_tables(obj, &o, &id)) { + rb_bug("Object ID seen, but not in mapping table: %s", obj_info(obj)); + } +} + +typedef int each_obj_callback(void *, void *, size_t, void *); +typedef int each_page_callback(struct heap_page *, void *); + +struct each_obj_data { + rb_objspace_t *objspace; + bool reenable_incremental; + + each_obj_callback *each_obj_callback; + each_page_callback *each_page_callback; + void *data; + + struct heap_page **pages[SIZE_POOL_COUNT]; + size_t pages_counts[SIZE_POOL_COUNT]; + + bool using_borrowable_page[SIZE_POOL_COUNT]; +}; + +static VALUE +objspace_each_objects_ensure(VALUE arg) +{ + struct each_obj_data *data = (struct each_obj_data *)arg; + rb_objspace_t *objspace = data->objspace; + + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + if (data->using_borrowable_page[i]) { + unlock_own_borrowable_page(GET_RACTOR(), i); + } + } + + /* Reenable incremental GC */ + if (data->reenable_incremental) { + objspace->flags.dont_incremental = FALSE; + } + + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + struct heap_page **pages = data->pages[i]; + free(pages); + } + + return Qnil; +} + +static VALUE +objspace_each_objects_try(VALUE arg) +{ + struct each_obj_data *data = (struct each_obj_data *)arg; + rb_objspace_t *objspace = data->objspace; + + /* Copy pages from all size_pools to their respective buffers. */ + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + size_t size = SIZE_POOL_EDEN_HEAP(size_pool)->total_pages * sizeof(struct heap_page *); + + struct heap_page **pages = malloc(size); + if (!pages) rb_memerror(); + + /* Set up pages buffer by iterating over all pages in the current eden + * heap. This will be a snapshot of the state of the heap before we + * call the callback over each page that exists in this buffer. Thus it + * is safe for the callback to allocate objects without possibly entering + * an infinite loop. */ + struct heap_page *page = 0; + size_t pages_count = 0; + ccan_list_for_each(&SIZE_POOL_EDEN_HEAP(size_pool)->pages, page, page_node) { + pages[pages_count] = page; + pages_count++; + } + data->pages[i] = pages; + data->pages_counts[i] = pages_count; + GC_ASSERT(pages_count == SIZE_POOL_EDEN_HEAP(size_pool)->total_pages); + } + + rb_ractor_t *r = GET_RACTOR(); + for (int size_pool_idx = 0; size_pool_idx < SIZE_POOL_COUNT; size_pool_idx++) { + rb_size_pool_t *size_pool = &size_pools[size_pool_idx]; + size_t pages_count = data->pages_counts[size_pool_idx]; + struct heap_page **pages = data->pages[size_pool_idx]; + + struct heap_page *page = ccan_list_top(&SIZE_POOL_EDEN_HEAP(size_pool)->pages, struct heap_page, page_node); + for (size_t i = 0; i < pages_count; i++) { + /* If we have reached the end of the linked list then there are no + * more pages, so break. */ + if (page == NULL) break; + + /* If this page does not match the one in the buffer, then move to + * the next page in the buffer. */ + if (pages[i] != page) continue; + + rb_borrowing_sync_lock(r); + if (current_borrowable_page(r, size_pool_idx) == page) { + lock_own_borrowable_page(r, size_pool_idx); + data->using_borrowable_page[size_pool_idx] = true; + } + rb_borrowing_sync_unlock(r); + + uintptr_t pstart = (uintptr_t)page->start; + uintptr_t pend = pstart + (page->total_slots * size_pool->slot_size); + + if (data->each_obj_callback && + (*data->each_obj_callback)((void *)pstart, (void *)pend, size_pool->slot_size, data->data)) { + break; + } + if (data->each_page_callback && + (*data->each_page_callback)(page, data->data)) { + break; + } + + if (data->using_borrowable_page[size_pool_idx]) { + unlock_own_borrowable_page(r, size_pool_idx); + data->using_borrowable_page[size_pool_idx] = false; + } + + page = ccan_list_next(&SIZE_POOL_EDEN_HEAP(size_pool)->pages, page, page_node); + } + + if (data->using_borrowable_page[size_pool_idx]) { + unlock_own_borrowable_page(r, size_pool_idx); + data->using_borrowable_page[size_pool_idx] = false; + } + } + + return Qnil; +} + +static void +objspace_each_exec(bool protected, struct each_obj_data *each_obj_data) +{ + /* Disable incremental GC */ + rb_objspace_t *objspace = each_obj_data->objspace; + bool reenable_incremental = FALSE; + if (protected) { + reenable_incremental = !objspace->flags.dont_incremental; + + gc_rest(objspace); + objspace->flags.dont_incremental = TRUE; + } + + each_obj_data->reenable_incremental = reenable_incremental; + memset(&each_obj_data->pages, 0, sizeof(each_obj_data->pages)); + memset(&each_obj_data->pages_counts, 0, sizeof(each_obj_data->pages_counts)); + rb_ensure(objspace_each_objects_try, (VALUE)each_obj_data, + objspace_each_objects_ensure, (VALUE)each_obj_data); +} + +static void +objspace_each_objects(rb_objspace_t *objspace, each_obj_callback *callback, void *data, bool protected) +{ + struct each_obj_data each_obj_data = { + .objspace = objspace, + .each_obj_callback = callback, + .each_page_callback = NULL, + .data = data, + }; + objspace_each_exec(protected, &each_obj_data); +} + +void +rb_gc_impl_each_objects(void *objspace_ptr, each_obj_callback *callback, void *data) +{ + objspace_each_objects(objspace_ptr, callback, data, TRUE); +} + +#if GC_CAN_COMPILE_COMPACTION +static void +objspace_each_pages(rb_objspace_t *objspace, each_page_callback *callback, void *data, bool protected) +{ + struct each_obj_data each_obj_data = { + .objspace = objspace, + .each_obj_callback = NULL, + .each_page_callback = callback, + .data = data, + }; + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + each_obj_data.using_borrowable_page[i] = false; + } + objspace_each_exec(protected, &each_obj_data); +} +#endif + +VALUE +rb_gc_impl_define_finalizer(void *objspace_ptr, VALUE obj, VALUE block) +{ + rb_objspace_t *objspace = objspace_ptr; + VALUE table; + st_data_t data; + + RBASIC(obj)->flags |= FL_FINALIZE; + + if (st_lookup(finalizer_table, obj, &data)) { + table = (VALUE)data; + + /* avoid duplicate block, table is usually small */ + { + long len = RARRAY_LEN(table); + long i; + + for (i = 0; i < len; i++) { + VALUE recv = RARRAY_AREF(table, i); + if (rb_equal(recv, block)) { + block = recv; + goto end; + } + } + } + + rb_ary_push(table, block); + } + else { + table = rb_ary_new3(1, block); + FL_SET_RAW(table, RUBY_FL_SHAREABLE); //TODO: Protect table from data races + *(VALUE *)&RBASIC(table)->klass = 0; + st_add_direct(finalizer_table, obj, table); + } + end: + block = rb_ary_new3(2, INT2FIX(0), block); + OBJ_FREEZE(block); + return block; +} + +VALUE +rb_gc_impl_undefine_finalizer(void *objspace_ptr, VALUE obj) +{ + rb_objspace_t *objspace = objspace_ptr; + st_data_t data = obj; + rb_check_frozen(obj); + st_delete(finalizer_table, &data, 0); + FL_UNSET(obj, FL_FINALIZE); + return obj; +} + +VALUE +rb_gc_impl_get_finalizers(void *objspace_ptr, VALUE obj) +{ + rb_objspace_t *objspace = objspace_ptr; + + if (FL_TEST(obj, FL_FINALIZE)) { + st_data_t data; + if (st_lookup(finalizer_table, obj, &data)) { + return (VALUE)data; + } + } + + return Qnil; +} + +void +rb_gc_impl_copy_finalizer(void *objspace_ptr, VALUE dest, VALUE obj) +{ + rb_objspace_t *objspace = objspace_ptr; + VALUE table; + st_data_t data; + + if (!FL_TEST(obj, FL_FINALIZE)) return; + + if (RB_LIKELY(st_lookup(finalizer_table, obj, &data))) { + table = (VALUE)data; + WITH_OBJSPACE_OF_VALUE_ENTER(dest, objspace); + { + st_insert(finalizer_table, dest, table); + FL_SET(dest, FL_FINALIZE); + } + WITH_OBJSPACE_OF_VALUE_LEAVE(objspace); + FL_SET(dest, FL_FINALIZE); + } + else { + rb_bug("rb_gc_copy_finalizer: FL_FINALIZE set but not found in finalizer_table: %s", rb_obj_info(obj)); + } +} + +static VALUE +get_final(long i, void *data) +{ + VALUE table = (VALUE)data; + + return RARRAY_AREF(table, i); +} + +static void +run_final(rb_objspace_t *objspace, VALUE zombie) +{ + if (RZOMBIE(zombie)->dfree) { + RZOMBIE(zombie)->dfree(RZOMBIE(zombie)->data); + } + + st_data_t key = (st_data_t)zombie; + if (FL_TEST_RAW(zombie, FL_FINALIZE)) { + FL_UNSET(zombie, FL_FINALIZE); + st_data_t table; + if (st_delete(finalizer_table, &key, &table)) { + rb_gc_run_obj_finalizer(rb_gc_impl_object_id(objspace, zombie), RARRAY_LEN(table), get_final, (void *)table); + } + else { + rb_bug("FL_FINALIZE flag is set, but finalizers are not found"); + } + } + else { + GC_ASSERT(!st_lookup(finalizer_table, key, NULL)); + } +} + +static void +finalize_list(rb_objspace_t *objspace, VALUE zombie) +{ + while (zombie) { + VALUE next_zombie; + struct heap_page *page; + asan_unpoison_object(zombie, false); + next_zombie = RZOMBIE(zombie)->next; + page = GET_HEAP_PAGE(zombie); + + run_final(objspace, zombie); + + HEAP_LOCK_ENTER(objspace); + { + GC_ASSERT(BUILTIN_TYPE(zombie) == T_ZOMBIE); + if (FL_TEST(zombie, FL_SEEN_OBJ_ID)) { + obj_free_object_id(objspace, zombie); + } + + GC_ASSERT(heap_pages_final_slots > 0); + GC_ASSERT(page->final_slots > 0); + + heap_pages_final_slots--; + page->final_slots--; + page->free_slots++; + heap_page_add_freeobj(objspace, page, zombie); + page->size_pool->total_freed_objects++; + } + HEAP_LOCK_LEAVE(objspace); + + zombie = next_zombie; + } +} + +static void +finalize_deferred_heap_pages(rb_objspace_t *objspace) +{ + SUSPEND_HEAP_LOCK_BEGIN(objspace); + { + VALUE zombie; + while ((zombie = ATOMIC_VALUE_EXCHANGE(heap_pages_deferred_final, 0)) != 0) { + finalize_list(objspace, zombie); + } + } + SUSPEND_HEAP_LOCK_END(objspace); +} + +static void +finalize_deferred(rb_objspace_t *objspace) +{ + rb_gc_set_pending_interrupt(); + finalize_deferred_heap_pages(objspace); + rb_gc_unset_pending_interrupt(); +} + +static void +gc_finalize_deferred(void *dmy) +{ + rb_objspace_t *objspace = &rb_gc_get_objspace(); + if (!objspace || ATOMIC_EXCHANGE(finalizing, 1)) return; + + finalize_deferred(objspace); + RUBY_ATOMIC_SET(finalizing, 0); +} + +static void +gc_finalize_deferred_register(rb_objspace_t *objspace) +{ + /* will enqueue a call to gc_finalize_deferred */ + rb_postponed_job_trigger(objspace->finalize_deferred_pjob); +} + +static int pop_mark_stack(mark_stack_t *stack, VALUE *data); + +static void +gc_abort(void *objspace_ptr) +{ + rb_objspace_t *objspace = objspace_ptr; + + if (is_incremental_marking(objspace)) { + /* Remove all objects from the mark stack. */ + VALUE obj; + while (pop_mark_stack(&objspace->mark_stack, &obj)); + + objspace->flags.during_incremental_marking = FALSE; + } + + if (is_lazy_sweeping(objspace)) { + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); + + heap->sweeping_page = NULL; + struct heap_page *page = NULL; + + ccan_list_for_each(&heap->pages, page, page_node) { + page->flags.before_sweep = false; + } + } + } + + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); + rgengc_mark_and_rememberset_clear(objspace, heap); + } + + gc_mode_set(objspace, gc_mode_none); +} + +struct force_finalize_list { + VALUE obj; + VALUE table; + struct force_finalize_list *next; +}; + +static int +force_chain_object(st_data_t key, st_data_t val, st_data_t arg) +{ + struct force_finalize_list **prev = (struct force_finalize_list **)arg; + struct force_finalize_list *curr = ALLOC(struct force_finalize_list); + curr->obj = key; + curr->table = val; + curr->next = *prev; + *prev = curr; + return ST_CONTINUE; +} + +static int +get_size_pool_idx(rb_objspace_t *objspace, rb_size_pool_t *size_pool) +{ + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + if(&size_pools[i] == size_pool) { + return i; + } + } + return -1; +} + +void +rb_gc_impl_shutdown_free_objects(void *objspace_ptr) +{ + rb_objspace_t *objspace = objspace_ptr; + + objspace->freeing_all = true; + + for (size_t i = 0; i < heap_allocated_pages; i++) { + struct heap_page *page = heap_pages_sorted[i]; + + POSSIBLE_USAGE_OF_BORROWING_PAGE_BEGIN(objspace, page); + + short stride = page->slot_size; + + uintptr_t p = (uintptr_t)page->start; + uintptr_t pend = p + page->total_slots * stride; + for (; p < pend; p += stride) { + VALUE vp = (VALUE)p; + asan_unpoisoning_object(vp) { + if (RB_BUILTIN_TYPE(vp) != T_NONE) { + rb_gc_obj_free(objspace, vp); + } + } + } + + POSSIBLE_USAGE_OF_BORROWING_PAGE_END(); + + } + + objspace->freeing_all = false; +} + +void +rb_gc_impl_shutdown_call_finalizer(void *objspace_ptr) +{ + rb_objspace_t *objspace = objspace_ptr; + +#if RGENGC_CHECK_MODE >= 2 + gc_verify_internal_consistency(objspace); +#endif + if (RUBY_ATOMIC_EXCHANGE(finalizing, 1)) return; + + /* run finalizers */ + finalize_deferred(objspace); + GC_ASSERT(heap_pages_deferred_final == 0); + + /* prohibit incremental GC */ + objspace->flags.dont_incremental = 1; + + /* force to run finalizer */ + while (finalizer_table->num_entries) { + struct force_finalize_list *list = 0; + st_foreach(finalizer_table, force_chain_object, (st_data_t)&list); + while (list) { + struct force_finalize_list *curr = list; + + st_data_t obj = (st_data_t)curr->obj; + st_delete(finalizer_table, &obj, 0); + FL_UNSET(curr->obj, FL_FINALIZE); + + rb_gc_run_obj_finalizer(rb_gc_impl_object_id(objspace, curr->obj), RARRAY_LEN(curr->table), get_final, (void *)curr->table); + + list = curr->next; + xfree(curr); + } + } + + /* Abort incremental marking and lazy sweeping to speed up shutdown. */ + gc_abort(objspace); + + /* prohibit GC because force T_DATA finalizers can break an object graph consistency */ + dont_gc_on(); + + /* running data/file finalizers are part of garbage collection */ + unsigned int lock_lev; + LOCAL_GC_BEGIN(objspace); + { + gc_enter(objspace, gc_enter_event_finalizer); + + /* run data/file object's finalizers */ + for (size_t i = 0; i < heap_allocated_pages; i++) { + struct heap_page *page = heap_pages_sorted[i]; + + POSSIBLE_USAGE_OF_BORROWING_PAGE_BEGIN(objspace, page); + + short stride = page->slot_size; + + uintptr_t p = (uintptr_t)page->start; + uintptr_t pend = p + page->total_slots * stride; + for (; p < pend; p += stride) { + VALUE vp = (VALUE)p; + void *poisoned = asan_unpoison_object_temporary(vp); + + if (rb_gc_shutdown_call_finalizer_p(vp)) { + rb_gc_obj_free(objspace, vp); + } + + if (poisoned) { + GC_ASSERT(BUILTIN_TYPE(vp) == T_NONE); + asan_poison_object(vp); + } + } + + POSSIBLE_USAGE_OF_BORROWING_PAGE_END(); + + } + + gc_exit(objspace, gc_enter_event_finalizer); + } + LOCAL_GC_END(objspace); + + finalize_deferred_heap_pages(objspace); + + st_free_table(finalizer_table); + finalizer_table = 0; + RUBY_ATOMIC_SET(finalizing, 0); +} + +void +rb_gc_impl_each_object(void *objspace_ptr, void (*func)(VALUE obj, void *data), void *data) +{ + rb_objspace_t *objspace = objspace_ptr; + + for (size_t i = 0; i < heap_allocated_pages; i++) { + struct heap_page *page = heap_pages_sorted[i]; + + POSSIBLE_USAGE_OF_BORROWING_PAGE_BEGIN(objspace, page); + + short stride = page->slot_size; + + uintptr_t p = (uintptr_t)page->start; + uintptr_t pend = p + page->total_slots * stride; + for (; p < pend; p += stride) { + VALUE obj = (VALUE)p; + + void *poisoned = asan_unpoison_object_temporary(obj); + + func(obj, data); + + if (poisoned) { + GC_ASSERT(BUILTIN_TYPE(obj) == T_NONE); + asan_poison_object(obj); + } + } + + POSSIBLE_USAGE_OF_BORROWING_PAGE_END(); + + } +} + +/* + ------------------------ Garbage Collection ------------------------ +*/ + +/* Sweeping */ + +static size_t +objspace_available_slots(rb_objspace_t *objspace) +{ + size_t total_slots = 0; + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + total_slots += SIZE_POOL_EDEN_HEAP(size_pool)->total_slots; + total_slots += SIZE_POOL_TOMB_HEAP(size_pool)->total_slots; + } + return total_slots; +} + +static size_t +objspace_live_slots(rb_objspace_t *objspace) +{ + return total_allocated_objects(objspace) - total_freed_objects(objspace) - heap_pages_final_slots; +} + +static size_t +objspace_free_slots(rb_objspace_t *objspace) +{ + return objspace_available_slots(objspace) - objspace_live_slots(objspace) - heap_pages_final_slots; +} + +static void +gc_setup_mark_bits(struct heap_page *page) +{ + /* copy oldgen bitmap to mark bitmap */ + memcpy(&page->mark_bits[0], &page->uncollectible_bits[0], HEAP_PAGE_BITMAP_SIZE); +} + +static int gc_is_moveable_obj(rb_objspace_t *objspace, VALUE obj); +static VALUE gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, size_t slot_size); + +#if defined(_WIN32) +enum {HEAP_PAGE_LOCK = PAGE_NOACCESS, HEAP_PAGE_UNLOCK = PAGE_READWRITE}; + +static BOOL +protect_page_body(struct heap_page_body *body, DWORD protect) +{ + DWORD old_protect; + return VirtualProtect(body, HEAP_PAGE_SIZE, protect, &old_protect) != 0; +} +#else +enum {HEAP_PAGE_LOCK = PROT_NONE, HEAP_PAGE_UNLOCK = PROT_READ | PROT_WRITE}; +#define protect_page_body(body, protect) !mprotect((body), HEAP_PAGE_SIZE, (protect)) +#endif + +static void +lock_page_body(rb_objspace_t *objspace, struct heap_page_body *body) +{ + if (!protect_page_body(body, HEAP_PAGE_LOCK)) { + rb_bug("Couldn't protect page %p, errno: %s", (void *)body, strerror(errno)); + } + else { + gc_report(5, objspace, "Protecting page in move %p\n", (void *)body); + } +} + +static void +unlock_page_body(rb_objspace_t *objspace, struct heap_page_body *body) +{ + if (!protect_page_body(body, HEAP_PAGE_UNLOCK)) { + rb_bug("Couldn't unprotect page %p, errno: %s", (void *)body, strerror(errno)); + } + else { + gc_report(5, objspace, "Unprotecting page in move %p\n", (void *)body); + } +} + +static bool +try_move(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *free_page, VALUE src) +{ + GC_ASSERT(gc_is_moveable_obj(objspace, src)); + + struct heap_page *src_page = GET_HEAP_PAGE(src); + if (!free_page) { + return false; + } + + /* We should return true if either src is successfully moved, or src is + * unmoveable. A false return will cause the sweeping cursor to be + * incremented to the next page, and src will attempt to move again */ + GC_ASSERT(RVALUE_MARKED(objspace, src)); + + asan_unlock_freelist(free_page); + VALUE dest = (VALUE)free_page->freelist; + asan_lock_freelist(free_page); + asan_unpoison_object(dest, false); + if (!dest) { + /* if we can't get something from the freelist then the page must be + * full */ + return false; + } + asan_unlock_freelist(free_page); + free_page->freelist = ((struct free_slot *)dest)->next; + asan_lock_freelist(free_page); + + GC_ASSERT(RB_BUILTIN_TYPE(dest) == T_NONE); + + if (src_page->slot_size > free_page->slot_size) { + objspace->rcompactor.moved_down_count_table[BUILTIN_TYPE(src)]++; + } + else if (free_page->slot_size > src_page->slot_size) { + objspace->rcompactor.moved_up_count_table[BUILTIN_TYPE(src)]++; + } + objspace->rcompactor.moved_count_table[BUILTIN_TYPE(src)]++; + objspace->rcompactor.total_moved++; + + gc_move(objspace, src, dest, src_page->slot_size, free_page->slot_size); + gc_pin(objspace, src); + free_page->free_slots--; + + return true; +} + +static void +gc_unprotect_pages(rb_objspace_t *objspace, rb_heap_t *heap) +{ + struct heap_page *cursor = heap->compact_cursor; + + while (cursor) { + unlock_page_body(objspace, GET_PAGE_BODY(cursor->start)); + cursor = ccan_list_next(&heap->pages, cursor, page_node); + } +} + +static void gc_update_references(rb_objspace_t *objspace); +#if GC_CAN_COMPILE_COMPACTION +static void invalidate_moved_page(rb_objspace_t *objspace, struct heap_page *page); +#endif + +#if defined(__MINGW32__) || defined(_WIN32) +# define GC_COMPACTION_SUPPORTED 1 +#else +/* If not MinGW, Windows, or does not have mmap, we cannot use mprotect for + * the read barrier, so we must disable compaction. */ +# define GC_COMPACTION_SUPPORTED (GC_CAN_COMPILE_COMPACTION && HEAP_PAGE_ALLOC_USE_MMAP) +#endif + +#if GC_CAN_COMPILE_COMPACTION +static void +read_barrier_handler(uintptr_t original_address) +{ + VALUE obj; + rb_objspace_t *objspace = (rb_objspace_t *)rb_gc_get_objspace(); + + /* Calculate address aligned to slots. */ + uintptr_t address = original_address - (original_address % BASE_SLOT_SIZE); + + obj = (VALUE)address; + + struct heap_page_body *page_body = GET_PAGE_BODY(obj); + + /* If the page_body is NULL, then mprotect cannot handle it and will crash + * with "Cannot allocate memory". */ + if (page_body == NULL) { + rb_bug("read_barrier_handler: segmentation fault at %p", (void *)original_address); + } + + RB_VM_LOCK_ENTER_NO_BARRIER(); + { + HEAP_LOCK_ENTER(objspace); + { + unlock_page_body(objspace, page_body); + + objspace->profile.read_barrier_faults++; + + invalidate_moved_page(objspace, GET_HEAP_PAGE(obj)); + } + HEAP_LOCK_LEAVE(objspace); + } + RB_VM_LOCK_LEAVE_NO_BARRIER(); + +} +#endif + +#if !GC_CAN_COMPILE_COMPACTION +static void +uninstall_handlers(void) +{ + /* no-op */ +} + +static void +install_handlers(void) +{ + /* no-op */ +} +#elif defined(_WIN32) +static LPTOP_LEVEL_EXCEPTION_FILTER old_handler; +typedef void (*signal_handler)(int); +static signal_handler old_sigsegv_handler; + +static LONG WINAPI +read_barrier_signal(EXCEPTION_POINTERS *info) +{ + /* EXCEPTION_ACCESS_VIOLATION is what's raised by access to protected pages */ + if (info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { + /* > The second array element specifies the virtual address of the inaccessible data. + * https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-exception_record + * + * Use this address to invalidate the page */ + read_barrier_handler((uintptr_t)info->ExceptionRecord->ExceptionInformation[1]); + return EXCEPTION_CONTINUE_EXECUTION; + } + else { + return EXCEPTION_CONTINUE_SEARCH; + } +} + +static void +uninstall_handlers(void) +{ + signal(SIGSEGV, old_sigsegv_handler); + SetUnhandledExceptionFilter(old_handler); +} + +static void +install_handlers(void) +{ + /* Remove SEGV handler so that the Unhandled Exception Filter handles it */ + old_sigsegv_handler = signal(SIGSEGV, NULL); + /* Unhandled Exception Filter has access to the violation address similar + * to si_addr from sigaction */ + old_handler = SetUnhandledExceptionFilter(read_barrier_signal); +} +#else +static struct sigaction old_sigbus_handler; +static struct sigaction old_sigsegv_handler; + +#ifdef HAVE_MACH_TASK_EXCEPTION_PORTS +static exception_mask_t old_exception_masks[32]; +static mach_port_t old_exception_ports[32]; +static exception_behavior_t old_exception_behaviors[32]; +static thread_state_flavor_t old_exception_flavors[32]; +static mach_msg_type_number_t old_exception_count; + +static void +disable_mach_bad_access_exc(void) +{ + old_exception_count = sizeof(old_exception_masks) / sizeof(old_exception_masks[0]); + task_swap_exception_ports( + mach_task_self(), EXC_MASK_BAD_ACCESS, + MACH_PORT_NULL, EXCEPTION_DEFAULT, 0, + old_exception_masks, &old_exception_count, + old_exception_ports, old_exception_behaviors, old_exception_flavors + ); +} + +static void +restore_mach_bad_access_exc(void) +{ + for (mach_msg_type_number_t i = 0; i < old_exception_count; i++) { + task_set_exception_ports( + mach_task_self(), + old_exception_masks[i], old_exception_ports[i], + old_exception_behaviors[i], old_exception_flavors[i] + ); + } +} +#endif + +static void +read_barrier_signal(int sig, siginfo_t *info, void *data) +{ + // setup SEGV/BUS handlers for errors + struct sigaction prev_sigbus, prev_sigsegv; + sigaction(SIGBUS, &old_sigbus_handler, &prev_sigbus); + sigaction(SIGSEGV, &old_sigsegv_handler, &prev_sigsegv); + + // enable SIGBUS/SEGV + sigset_t set, prev_set; + sigemptyset(&set); + sigaddset(&set, SIGBUS); + sigaddset(&set, SIGSEGV); + sigprocmask(SIG_UNBLOCK, &set, &prev_set); +#ifdef HAVE_MACH_TASK_EXCEPTION_PORTS + disable_mach_bad_access_exc(); +#endif + // run handler + read_barrier_handler((uintptr_t)info->si_addr); + + // reset SEGV/BUS handlers +#ifdef HAVE_MACH_TASK_EXCEPTION_PORTS + restore_mach_bad_access_exc(); +#endif + sigaction(SIGBUS, &prev_sigbus, NULL); + sigaction(SIGSEGV, &prev_sigsegv, NULL); + sigprocmask(SIG_SETMASK, &prev_set, NULL); +} + +static void +uninstall_handlers(void) +{ +#ifdef HAVE_MACH_TASK_EXCEPTION_PORTS + restore_mach_bad_access_exc(); +#endif + sigaction(SIGBUS, &old_sigbus_handler, NULL); + sigaction(SIGSEGV, &old_sigsegv_handler, NULL); +} + +static void +install_handlers(void) +{ + struct sigaction action; + memset(&action, 0, sizeof(struct sigaction)); + sigemptyset(&action.sa_mask); + action.sa_sigaction = read_barrier_signal; + action.sa_flags = SA_SIGINFO | SA_ONSTACK; + + sigaction(SIGBUS, &action, &old_sigbus_handler); + sigaction(SIGSEGV, &action, &old_sigsegv_handler); +#ifdef HAVE_MACH_TASK_EXCEPTION_PORTS + disable_mach_bad_access_exc(); +#endif +} +#endif + +static void +gc_compact_finish(rb_objspace_t *objspace) +{ + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); + gc_unprotect_pages(objspace, heap); + } + + uninstall_handlers(); + + gc_update_references(objspace); + objspace->profile.compact_count++; + + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); + heap->compact_cursor = NULL; + heap->free_pages = NULL; + heap->compact_cursor_index = 0; + } + + if (gc_prof_enabled(objspace)) { + gc_profile_record *record = gc_prof_record(objspace); + record->moved_objects = objspace->rcompactor.total_moved - record->moved_objects; + } + objspace->flags.during_compacting = FALSE; +} + +struct gc_sweep_context { + struct heap_page *page; + int final_slots; + int freed_slots; + int empty_slots; +}; + +static inline void +gc_sweep_plane(rb_objspace_t *objspace, rb_heap_t *heap, uintptr_t p, bits_t bitset, struct gc_sweep_context *ctx) +{ + struct heap_page *sweep_page = ctx->page; + short slot_size = sweep_page->slot_size; + short slot_bits = slot_size / BASE_SLOT_SIZE; + GC_ASSERT(slot_bits > 0); + + do { + VALUE vp = (VALUE)p; + GC_ASSERT(vp % BASE_SLOT_SIZE == 0); + + asan_unpoison_object(vp, false); + if (bitset & 1) { + switch (BUILTIN_TYPE(vp)) { + default: /* majority case */ + gc_report(2, objspace, "page_sweep: free %p\n", (void *)p); +#if RGENGC_CHECK_MODE + if (!is_full_marking(objspace)) { + if (RVALUE_OLD_P(objspace, vp)) rb_bug("page_sweep: %p - old while minor GC.", (void *)p); + if (RVALUE_REMEMBERED(objspace, vp)) rb_bug("page_sweep: %p - remembered.", (void *)p); + } +#endif + + if (RVALUE_WB_UNPROTECTED(objspace, vp)) CLEAR_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(vp), vp); + if (RVALUE_LOCAL_IMMUNE(obj)) { + VM_ASSERT(!using_local_limits(objspace)); + CLEAR_IN_BITMAP(GET_HEAP_LOCAL_IMMUNE_BITS(obj), obj); + } + +#if RGENGC_CHECK_MODE +#define CHECK(x) if (x(objspace, vp) != FALSE) rb_bug("obj_free: " #x "(%s) != FALSE", rb_obj_info(vp)) + CHECK(RVALUE_WB_UNPROTECTED); + CHECK(RVALUE_LOCAL_IMMUNE); + CHECK(RVALUE_MARKED); + CHECK(RVALUE_MARKING); + CHECK(RVALUE_UNCOLLECTIBLE); +#undef CHECK +#endif + + bool has_object_id = FL_TEST(vp, FL_SEEN_OBJ_ID); + if (rb_gc_obj_free(objspace, vp)) { + if (has_object_id) { + obj_free_object_id(objspace, vp); + } + // always add free slots back to the swept pages freelist, + // so that if we're compacting, we can re-use the slots + (void)VALGRIND_MAKE_MEM_UNDEFINED((void*)p, BASE_SLOT_SIZE); + heap_page_add_freeobj(objspace, sweep_page, vp); + gc_report(3, objspace, "page_sweep: %s is added to freelist\n", rb_obj_info(vp)); + ctx->freed_slots++; + } + else { + ctx->final_slots++; + } + break; + + case T_MOVED: + if (objspace->flags.during_compacting) { + /* The sweep cursor shouldn't have made it to any + * T_MOVED slots while the compact flag is enabled. + * The sweep cursor and compact cursor move in + * opposite directions, and when they meet references will + * get updated and "during_compacting" should get disabled */ + rb_bug("T_MOVED shouldn't be seen until compaction is finished"); + } + gc_report(3, objspace, "page_sweep: %s is added to freelist\n", rb_obj_info(vp)); + ctx->empty_slots++; + heap_page_add_freeobj(objspace, sweep_page, vp); + break; + case T_ZOMBIE: + /* already counted */ + break; + case T_NONE: + ctx->empty_slots++; /* already freed */ + break; + } + } + p += slot_size; + bitset >>= slot_bits; + } while (bitset); +} + +static inline void +gc_sweep_page(rb_objspace_t *objspace, rb_heap_t *heap, struct gc_sweep_context *ctx) +{ + struct heap_page *sweep_page = ctx->page; + GC_ASSERT(SIZE_POOL_EDEN_HEAP(sweep_page->size_pool) == heap); + + uintptr_t p; + bits_t *bits, bitset; + + gc_report(2, objspace, "page_sweep: start.\n"); + +#if RGENGC_CHECK_MODE + if (!objspace->flags.immediate_sweep) { + GC_ASSERT(sweep_page->flags.before_sweep == TRUE); + } +#endif + sweep_page->flags.before_sweep = FALSE; + sweep_page->free_slots = 0; + + p = (uintptr_t)sweep_page->start; + bits = sweep_page->mark_bits; + + int page_rvalue_count = sweep_page->total_slots * (sweep_page->slot_size / BASE_SLOT_SIZE); + int out_of_range_bits = (NUM_IN_PAGE(p) + page_rvalue_count) % BITS_BITLENGTH; + if (out_of_range_bits != 0) { // sizeof(RVALUE) == 64 + bits[BITMAP_INDEX(p) + page_rvalue_count / BITS_BITLENGTH] |= ~(((bits_t)1 << out_of_range_bits) - 1); + } + + /* The last bitmap plane may not be used if the last plane does not + * have enough space for the slot_size. In that case, the last plane must + * be skipped since none of the bits will be set. */ + int bitmap_plane_count = CEILDIV(NUM_IN_PAGE(p) + page_rvalue_count, BITS_BITLENGTH); + GC_ASSERT(bitmap_plane_count == HEAP_PAGE_BITMAP_LIMIT - 1 || + bitmap_plane_count == HEAP_PAGE_BITMAP_LIMIT); + + bool local_immunity_active = using_local_limits(objspace); + + // Skip out of range slots at the head of the page + bitset = ~bits[0]; + if (local_immunity_active) bitset &= ~sweep_page->local_immune_bits[0]; + bitset >>= NUM_IN_PAGE(p); + if (bitset) { + gc_sweep_plane(objspace, heap, p, bitset, ctx); + } + p += (BITS_BITLENGTH - NUM_IN_PAGE(p)) * BASE_SLOT_SIZE; + + for (int i = 1; i < bitmap_plane_count; i++) { + bitset = ~bits[i]; + if (local_immunity_active) bitset &= ~sweep_page->local_immune_bits[i]; + if (bitset) { + gc_sweep_plane(objspace, heap, p, bitset, ctx); + } + p += BITS_BITLENGTH * BASE_SLOT_SIZE; + } + + if (!heap->compact_cursor) { + gc_setup_mark_bits(sweep_page); + } + +#if GC_PROFILE_MORE_DETAIL + if (gc_prof_enabled(objspace)) { + gc_profile_record *record = gc_prof_record(objspace); + record->removing_objects += ctx->final_slots + ctx->freed_slots; + record->empty_objects += ctx->empty_slots; + } +#endif + if (0) fprintf(stderr, "gc_sweep_page(%"PRIdSIZE"): total_slots: %d, freed_slots: %d, empty_slots: %d, final_slots: %d\n", + rb_gc_count(), + sweep_page->total_slots, + ctx->freed_slots, ctx->empty_slots, ctx->final_slots); + + sweep_page->free_slots += ctx->freed_slots + ctx->empty_slots; + sweep_page->size_pool->total_freed_objects += ctx->freed_slots; + + if (heap_pages_deferred_final && !finalizing) { + gc_finalize_deferred_register(objspace); + } + +#if RGENGC_CHECK_MODE + short freelist_len = 0; + asan_unlock_freelist(sweep_page); + struct free_slot *ptr = sweep_page->freelist; + while (ptr) { + freelist_len++; + ptr = ptr->next; + } + asan_lock_freelist(sweep_page); + if (freelist_len != sweep_page->free_slots) { + rb_bug("inconsistent freelist length: expected %d but was %d", sweep_page->free_slots, freelist_len); + } +#endif + + gc_report(2, objspace, "page_sweep: end.\n"); +} + +static const char * +gc_mode_name(enum gc_mode mode) +{ + switch (mode) { + case gc_mode_none: return "none"; + case gc_mode_marking: return "marking"; + case gc_mode_sweeping: return "sweeping"; + case gc_mode_compacting: return "compacting"; + default: rb_bug("gc_mode_name: unknown mode: %d", (int)mode); + } +} + +static void +gc_mode_transition(rb_objspace_t *objspace, enum gc_mode mode) +{ +#if RGENGC_CHECK_MODE + enum gc_mode prev_mode = gc_mode(objspace); + switch (prev_mode) { + case gc_mode_none: GC_ASSERT(mode == gc_mode_marking); break; + case gc_mode_marking: GC_ASSERT(mode == gc_mode_sweeping); break; + case gc_mode_sweeping: GC_ASSERT(mode == gc_mode_none || mode == gc_mode_compacting); break; + case gc_mode_compacting: GC_ASSERT(mode == gc_mode_none); break; + } +#endif + if (0) fprintf(stderr, "gc_mode_transition: %s->%s\n", gc_mode_name(gc_mode(objspace)), gc_mode_name(mode)); + gc_mode_set(objspace, mode); +} + +static void +heap_page_freelist_append(struct heap_page *page, struct free_slot *freelist) +{ + if (freelist) { + asan_unlock_freelist(page); + if (page->freelist) { + struct free_slot *p = page->freelist; + asan_unpoison_object((VALUE)p, false); + while (p->next) { + struct free_slot *prev = p; + p = p->next; + asan_poison_object((VALUE)prev); + asan_unpoison_object((VALUE)p, false); + } + p->next = freelist; + asan_poison_object((VALUE)p); + } + else { + page->freelist = freelist; + } + asan_lock_freelist(page); + } +} + +static void +gc_sweep_start_heap(rb_objspace_t *objspace, rb_heap_t *heap) +{ + heap->sweeping_page = ccan_list_top(&heap->pages, struct heap_page, page_node); + heap->free_pages = NULL; + heap->pooled_pages = NULL; + if (!objspace->flags.immediate_sweep) { + struct heap_page *page = NULL; + + ccan_list_for_each(&heap->pages, page, page_node) { + page->flags.before_sweep = TRUE; + } + } +} + +#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 4 +__attribute__((noinline)) +#endif + +#if GC_CAN_COMPILE_COMPACTION +static void gc_sort_heap_by_compare_func(rb_objspace_t *objspace, gc_compact_compare_func compare_func); +static int compare_pinned_slots(const void *left, const void *right, void *d); +#endif + +static void +gc_ractor_newobj_size_pool_cache_clear(rb_ractor_newobj_size_pool_cache_t *cache) +{ + struct heap_page *page = cache->using_page; + struct free_slot *freelist = cache->freelist; + RUBY_DEBUG_LOG("ractor using_page:%p freelist:%p", (void *)page, (void *)freelist); + heap_page_freelist_append(page, freelist); + + cache->using_page = NULL; + cache->freelist = NULL; +} + +void +gc_ractor_newobj_cache_clear(void *c, void *data) +{ + rb_ractor_newobj_cache_t *newobj_cache = c; + newobj_cache->incremental_mark_step_allocated_slots = 0; + + for (size_t size_pool_idx = 0; size_pool_idx < SIZE_POOL_COUNT; size_pool_idx++) { + rb_ractor_newobj_size_pool_cache_t *cache = &newobj_cache->size_pool_caches[size_pool_idx]; + gc_ractor_newobj_size_pool_cache_clear(cache); + } +} + +static void +gc_sweep_start(rb_objspace_t *objspace) +{ + gc_mode_transition(objspace, gc_mode_sweeping); + objspace->rincgc.pooled_slots = 0; + +#if GC_CAN_COMPILE_COMPACTION + if (objspace->flags.during_compacting) { + gc_sort_heap_by_compare_func( + objspace, + objspace->rcompactor.compare_func ? objspace->rcompactor.compare_func : compare_pinned_slots + ); + } +#endif + + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); + + gc_sweep_start_heap(objspace, heap); + + /* We should call gc_sweep_finish_size_pool for size pools with no pages. */ + if (heap->sweeping_page == NULL) { + GC_ASSERT(heap->total_pages == 0); + GC_ASSERT(heap->total_slots == 0); + gc_sweep_finish_size_pool(objspace, size_pool); + } + } + + gc_ractor_newobj_cache_clear(&objspace->local_data.ractor->newobj_cache, NULL); + gc_ractor_newobj_cache_clear(&objspace->local_data.ractor->newobj_borrowing_cache, NULL); +} + +static void +gc_sweep_finish_size_pool(rb_objspace_t *objspace, rb_size_pool_t *size_pool) +{ + rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); + size_t total_slots = heap->total_slots + SIZE_POOL_TOMB_HEAP(size_pool)->total_slots; + size_t total_pages = heap->total_pages + SIZE_POOL_TOMB_HEAP(size_pool)->total_pages; + size_t swept_slots = size_pool->freed_slots + size_pool->empty_slots; + + size_t init_slots = gc_params.size_pool_init_slots[size_pool - size_pools]; + size_t min_free_slots = (size_t)(MAX(total_slots, init_slots) * gc_params.heap_free_slots_min_ratio); + + /* If we don't have enough slots and we have pages on the tomb heap, move + * pages from the tomb heap to the eden heap. This may prevent page + * creation thrashing (frequently allocating and deallocting pages) and + * GC thrashing (running GC more frequently than required). */ + struct heap_page *resurrected_page; + while (swept_slots < min_free_slots && + (resurrected_page = heap_page_resurrect(objspace, size_pool))) { + swept_slots += resurrected_page->free_slots; + + heap_add_page(objspace, size_pool, heap, resurrected_page); + heap_add_freepage(heap, resurrected_page); + } + + if (swept_slots < min_free_slots) { + bool grow_heap = is_full_marking(objspace); + + /* Consider growing or starting a major GC if we are not currently in a + * major GC and we can't allocate any more pages. */ + if (!is_full_marking(objspace) && size_pool->allocatable_pages == 0) { + /* The heap is a growth heap if it freed more slots than had empty slots. */ + bool is_growth_heap = size_pool->empty_slots == 0 || size_pool->freed_slots > size_pool->empty_slots; + + /* Grow this heap if we haven't run at least RVALUE_OLD_AGE minor + * GC since the last major GC or if this heap is smaller than the + * the configured initial size. */ + if (objspace->profile.count - objspace->rgengc.last_major_gc < RVALUE_OLD_AGE || + total_slots < init_slots) { + grow_heap = TRUE; + } + else if (is_growth_heap) { /* Only growth heaps are allowed to start a major GC. */ + gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_NOFREE; + size_pool->force_major_gc_count++; + } + } + + if (grow_heap) { + size_pool_allocatable_pages_expand(objspace, size_pool, swept_slots, + total_slots, total_pages); + } + } +} + +static void +gc_sweep_finish(rb_objspace_t *objspace) +{ + gc_report(1, objspace, "gc_sweep_finish\n"); + + gc_prof_set_heap_info(objspace); + heap_pages_free_unused_pages(objspace); + + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + + /* if heap_pages has unused pages, then assign them to increment */ + size_t tomb_pages = SIZE_POOL_TOMB_HEAP(size_pool)->total_pages; + if (size_pool->allocatable_pages < tomb_pages) { + size_pool->allocatable_pages = tomb_pages; + } + + size_pool->freed_slots = 0; + size_pool->empty_slots = 0; + + if (!will_be_incremental_marking(objspace)) { + rb_heap_t *eden_heap = SIZE_POOL_EDEN_HEAP(size_pool); + struct heap_page *end_page = eden_heap->free_pages; + if (end_page) { + while (end_page->free_next) end_page = end_page->free_next; + end_page->free_next = eden_heap->pooled_pages; + } + else { + eden_heap->free_pages = eden_heap->pooled_pages; + } + eden_heap->pooled_pages = NULL; + objspace->rincgc.pooled_slots = 0; + } + } + heap_pages_expand_sorted(objspace); + + rb_gc_event_hook(0, RUBY_INTERNAL_EVENT_GC_END_SWEEP); + gc_mode_transition(objspace, gc_mode_none); + +#if RGENGC_CHECK_MODE >= 2 + gc_verify_internal_consistency(objspace); +#endif +} + +static int +gc_sweep_step(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap) +{ + struct heap_page *sweep_page = heap->sweeping_page; + int unlink_limit = GC_SWEEP_PAGES_FREEABLE_PER_STEP; + int swept_slots = 0; + int pooled_slots = 0; + + if (sweep_page == NULL) return FALSE; + +#if GC_ENABLE_LAZY_SWEEP + gc_prof_sweep_timer_start(objspace); +#endif + + do { + RUBY_DEBUG_LOG("sweep_page:%p", (void *)sweep_page); + + struct gc_sweep_context ctx = { + .page = sweep_page, + .final_slots = 0, + .freed_slots = 0, + .empty_slots = 0, + }; + gc_sweep_page(objspace, heap, &ctx); + int free_slots = ctx.freed_slots + ctx.empty_slots; + + heap->sweeping_page = ccan_list_next(&heap->pages, sweep_page, page_node); + + if (sweep_page->final_slots + free_slots == sweep_page->total_slots && + heap_pages_freeable_pages > 0 && + unlink_limit > 0) { + heap_pages_freeable_pages--; + unlink_limit--; + /* there are no living objects -> move this page to tomb heap */ + heap_unlink_page(objspace, heap, sweep_page); + heap_add_page(objspace, size_pool, SIZE_POOL_TOMB_HEAP(size_pool), sweep_page); + } + else if (free_slots > 0) { + size_pool->freed_slots += ctx.freed_slots; + size_pool->empty_slots += ctx.empty_slots; + + if (pooled_slots < GC_INCREMENTAL_SWEEP_POOL_SLOT_COUNT) { + heap_add_poolpage(objspace, heap, sweep_page); + pooled_slots += free_slots; + } + else { + heap_add_freepage(heap, sweep_page); + swept_slots += free_slots; + if (swept_slots > GC_INCREMENTAL_SWEEP_SLOT_COUNT) { + break; + } + } + } + else { + sweep_page->free_next = NULL; + } + } while ((sweep_page = heap->sweeping_page)); + + if (!heap->sweeping_page) { + gc_sweep_finish_size_pool(objspace, size_pool); + + if (!has_sweeping_pages(objspace)) { + gc_sweep_finish(objspace); + } + } + +#if GC_ENABLE_LAZY_SWEEP + gc_prof_sweep_timer_stop(objspace); +#endif + + return heap->free_pages != NULL; +} + +static void +gc_sweep_rest(rb_objspace_t *objspace) +{ + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + + while (SIZE_POOL_EDEN_HEAP(size_pool)->sweeping_page) { + gc_sweep_step(objspace, size_pool, SIZE_POOL_EDEN_HEAP(size_pool)); + } + } +} + +static void +gc_sweep_continue(rb_objspace_t *objspace, rb_size_pool_t *sweep_size_pool, rb_heap_t *heap) +{ + GC_ASSERT(dont_gc_val() == FALSE); + if (!GC_ENABLE_LAZY_SWEEP) return; + + gc_sweeping_enter(objspace); + + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + if (!gc_sweep_step(objspace, size_pool, SIZE_POOL_EDEN_HEAP(size_pool))) { + /* sweep_size_pool requires a free slot but sweeping did not yield any. */ + if (size_pool == sweep_size_pool) { + if (size_pool->allocatable_pages > 0) { + heap_increment(objspace, size_pool, heap); + } + else { + /* Not allowed to create a new page so finish sweeping. */ + gc_sweep_rest(objspace); + break; + } + } + } + } + + gc_sweeping_exit(objspace); +} + +VALUE +rb_gc_impl_location(void *objspace_ptr, VALUE value) +{ + VALUE destination; + + if (!SPECIAL_CONST_P(value)) { + void *poisoned = asan_unpoison_object_temporary(value); + + if (BUILTIN_TYPE(value) == T_MOVED) { + destination = (VALUE)RMOVED(value)->destination; + GC_ASSERT(BUILTIN_TYPE(destination) != T_NONE); + } + else { + destination = value; + } + + /* Re-poison slot if it's not the one we want */ + if (poisoned) { + GC_ASSERT(BUILTIN_TYPE(value) == T_NONE); + asan_poison_object(value); + } + } + else { + destination = value; + } + + return destination; +} + +#if GC_CAN_COMPILE_COMPACTION +static void +invalidate_moved_plane(rb_objspace_t *objspace, struct heap_page *page, uintptr_t p, bits_t bitset) +{ + if (bitset) { + do { + if (bitset & 1) { + VALUE forwarding_object = (VALUE)p; + VALUE object; + + if (BUILTIN_TYPE(forwarding_object) == T_MOVED) { + GC_ASSERT(RVALUE_PINNED(objspace, forwarding_object)); + GC_ASSERT(!RVALUE_MARKED(objspace, forwarding_object)); + + CLEAR_IN_BITMAP(GET_HEAP_PINNED_BITS(forwarding_object), forwarding_object); + + object = rb_gc_impl_location(objspace, forwarding_object); + + uint32_t original_shape_id = 0; + if (RB_TYPE_P(object, T_OBJECT)) { + original_shape_id = RMOVED(forwarding_object)->original_shape_id; + } + + gc_move(objspace, object, forwarding_object, GET_HEAP_PAGE(object)->slot_size, page->slot_size); + /* forwarding_object is now our actual object, and "object" + * is the free slot for the original page */ + + if (original_shape_id) { + rb_gc_set_shape(forwarding_object, original_shape_id); + } + + struct heap_page *orig_page = GET_HEAP_PAGE(object); + orig_page->free_slots++; + heap_page_add_freeobj(objspace, orig_page, object); + + GC_ASSERT(RVALUE_MARKED(objspace, forwarding_object)); + GC_ASSERT(BUILTIN_TYPE(forwarding_object) != T_MOVED); + GC_ASSERT(BUILTIN_TYPE(forwarding_object) != T_NONE); + } + } + p += BASE_SLOT_SIZE; + bitset >>= 1; + } while (bitset); + } +} + +static void +invalidate_moved_page(rb_objspace_t *objspace, struct heap_page *page) +{ + int i; + bits_t *mark_bits, *pin_bits; + bits_t bitset; + + mark_bits = page->mark_bits; + pin_bits = page->pinned_bits; + + uintptr_t p = page->start; + + // Skip out of range slots at the head of the page + bitset = pin_bits[0] & ~mark_bits[0]; + bitset >>= NUM_IN_PAGE(p); + invalidate_moved_plane(objspace, page, p, bitset); + p += (BITS_BITLENGTH - NUM_IN_PAGE(p)) * BASE_SLOT_SIZE; + + for (i=1; i < HEAP_PAGE_BITMAP_LIMIT; i++) { + /* Moved objects are pinned but never marked. We reuse the pin bits + * to indicate there is a moved object in this slot. */ + bitset = pin_bits[i] & ~mark_bits[i]; + + invalidate_moved_plane(objspace, page, p, bitset); + p += BITS_BITLENGTH * BASE_SLOT_SIZE; + } +} +#endif + +static void +gc_compact_start(rb_objspace_t *objspace) +{ + struct heap_page *page = NULL; + gc_mode_transition(objspace, gc_mode_compacting); + + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(&size_pools[i]); + ccan_list_for_each(&heap->pages, page, page_node) { + page->flags.before_sweep = TRUE; + } + + heap->compact_cursor = ccan_list_tail(&heap->pages, struct heap_page, page_node); + heap->compact_cursor_index = 0; + } + + if (gc_prof_enabled(objspace)) { + gc_profile_record *record = gc_prof_record(objspace); + record->moved_objects = objspace->rcompactor.total_moved; + } + + memset(objspace->rcompactor.considered_count_table, 0, T_MASK * sizeof(size_t)); + memset(objspace->rcompactor.moved_count_table, 0, T_MASK * sizeof(size_t)); + memset(objspace->rcompactor.moved_up_count_table, 0, T_MASK * sizeof(size_t)); + memset(objspace->rcompactor.moved_down_count_table, 0, T_MASK * sizeof(size_t)); + + /* Set up read barrier for pages containing MOVED objects */ + install_handlers(); +} + +static void gc_sweep_compact(rb_objspace_t *objspace); + +static void +gc_sweep(rb_objspace_t *objspace) +{ + gc_sweeping_enter(objspace); + + const unsigned int immediate_sweep = objspace->flags.immediate_sweep; + + gc_report(1, objspace, "gc_sweep: immediate: %d\n", immediate_sweep); + + gc_sweep_start(objspace); + if (objspace->flags.during_compacting) { + gc_sweep_compact(objspace); + } + + if (immediate_sweep) { +#if !GC_ENABLE_LAZY_SWEEP + gc_prof_sweep_timer_start(objspace); +#endif + gc_sweep_rest(objspace); +#if !GC_ENABLE_LAZY_SWEEP + gc_prof_sweep_timer_stop(objspace); +#endif + } + else { + + /* Sweep every size pool. */ + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + gc_sweep_step(objspace, size_pool, SIZE_POOL_EDEN_HEAP(size_pool)); + } + } + + gc_sweeping_exit(objspace); +} + +/* Marking - Marking stack */ + +static stack_chunk_t * +stack_chunk_alloc(void) +{ + stack_chunk_t *res; + + res = malloc(sizeof(stack_chunk_t)); + if (!res) + rb_memerror(); + + return res; +} + +static inline int +is_mark_stack_empty(mark_stack_t *stack) +{ + return stack->chunk == NULL; +} + +static size_t +mark_stack_size(mark_stack_t *stack) +{ + size_t size = stack->index; + stack_chunk_t *chunk = stack->chunk ? stack->chunk->next : NULL; + + while (chunk) { + size += stack->limit; + chunk = chunk->next; + } + return size; +} + +static void +add_stack_chunk_cache(mark_stack_t *stack, stack_chunk_t *chunk) +{ + chunk->next = stack->cache; + stack->cache = chunk; + stack->cache_size++; +} + +static void +shrink_stack_chunk_cache(mark_stack_t *stack) +{ + stack_chunk_t *chunk; + + if (stack->unused_cache_size > (stack->cache_size/2)) { + chunk = stack->cache; + stack->cache = stack->cache->next; + stack->cache_size--; + free(chunk); + } + stack->unused_cache_size = stack->cache_size; +} + +static void +push_mark_stack_chunk(mark_stack_t *stack) +{ + stack_chunk_t *next; + + GC_ASSERT(stack->index == stack->limit); + + if (stack->cache_size > 0) { + next = stack->cache; + stack->cache = stack->cache->next; + stack->cache_size--; + if (stack->unused_cache_size > stack->cache_size) + stack->unused_cache_size = stack->cache_size; + } + else { + next = stack_chunk_alloc(); + } + next->next = stack->chunk; + stack->chunk = next; + stack->index = 0; +} + +static void +pop_mark_stack_chunk(mark_stack_t *stack) +{ + stack_chunk_t *prev; + + prev = stack->chunk->next; + GC_ASSERT(stack->index == 0); + add_stack_chunk_cache(stack, stack->chunk); + stack->chunk = prev; + stack->index = stack->limit; +} + +static void +mark_stack_chunk_list_free(stack_chunk_t *chunk) +{ + stack_chunk_t *next = NULL; + + while (chunk != NULL) { + next = chunk->next; + free(chunk); + chunk = next; + } +} + +static void +free_stack_chunks(mark_stack_t *stack) +{ + mark_stack_chunk_list_free(stack->chunk); +} + +static void +mark_stack_free_cache(mark_stack_t *stack) +{ + mark_stack_chunk_list_free(stack->cache); + stack->cache_size = 0; + stack->unused_cache_size = 0; +} + +static void +push_mark_stack(mark_stack_t *stack, VALUE obj) +{ + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + case T_CLASS: + case T_MODULE: + case T_FLOAT: + case T_STRING: + case T_REGEXP: + case T_ARRAY: + case T_HASH: + case T_STRUCT: + case T_BIGNUM: + case T_FILE: + case T_DATA: + case T_MATCH: + case T_COMPLEX: + case T_RATIONAL: + case T_TRUE: + case T_FALSE: + case T_SYMBOL: + case T_IMEMO: + case T_ICLASS: + if (stack->index == stack->limit) { + push_mark_stack_chunk(stack); + } + stack->chunk->data[stack->index++] = obj; + return; + + case T_NONE: + case T_NIL: + case T_FIXNUM: + case T_MOVED: + case T_ZOMBIE: + case T_UNDEF: + case T_MASK: + rb_bug("push_mark_stack() called for broken object"); + break; + + case T_NODE: + rb_bug("push_mark_stack: unexpected T_NODE object"); + break; + } + + rb_bug("rb_gc_mark(): unknown data type 0x%x(%p) %s", + BUILTIN_TYPE(obj), (void *)obj, + is_pointer_to_heap((rb_objspace_t *)rb_gc_get_objspace(), (void *)obj) ? "corrupted object" : "non object"); +} + +static int +pop_mark_stack(mark_stack_t *stack, VALUE *data) +{ + if (is_mark_stack_empty(stack)) { + return FALSE; + } + if (stack->index == 1) { + *data = stack->chunk->data[--stack->index]; + pop_mark_stack_chunk(stack); + } + else { + *data = stack->chunk->data[--stack->index]; + } + return TRUE; +} + +static void +init_mark_stack(mark_stack_t *stack) +{ + int i; + + MEMZERO(stack, mark_stack_t, 1); + stack->index = stack->limit = STACK_CHUNK_SIZE; + + for (i=0; i < 4; i++) { + add_stack_chunk_cache(stack, stack_chunk_alloc()); + } + stack->unused_cache_size = stack->cache_size; +} + +/* Marking */ + +static void +rgengc_check_relation(rb_objspace_t *objspace, VALUE obj) +{ + const VALUE old_parent = objspace->rgengc.parent_object; + + if (old_parent) { /* parent object is old */ + if (RVALUE_WB_UNPROTECTED(objspace, obj) || !RVALUE_OLD_P(objspace, obj)) { + rgengc_remember(objspace, old_parent); + } + } + + GC_ASSERT(old_parent == objspace->rgengc.parent_object); +} + +static inline int +gc_mark_set(rb_objspace_t *objspace, VALUE obj) +{ + ASSERT_ractor_safe_gc_state(); + //TODO assertion needed? + //VM_ASSERT(during_gc || heap_locked(objspace)); + if (RVALUE_MARKED(objspace, obj)) return 0; + MARK_IN_BITMAP(GET_HEAP_MARK_BITS(obj), obj); + return 1; +} + +static void +gc_aging(VALUE obj) +{ + /* Disable aging if Major GC's are disabled. This will prevent longish lived + * objects filling up the heap at the expense of marking many more objects. + * + * We should always pre-warm our process when disabling majors, by running + * GC manually several times so that most objects likely to become oldgen + * are already oldgen. + */ + if(!gc_config_full_mark_val) + return; + + struct heap_page *page = GET_HEAP_PAGE(obj); + rb_objspace_t *objspace = page->objspace; + + GC_ASSERT(RVALUE_MARKING(objspace, obj) == FALSE); + check_rvalue_consistency(objspace, obj); + + if (!RVALUE_PAGE_WB_UNPROTECTED(page, obj)) { + if (!RVALUE_OLD_P(objspace, obj)) { + gc_report(3, objspace, "gc_aging: YOUNG: %s\n", rb_obj_info(obj)); + RVALUE_AGE_INC(objspace, obj); + } + else if (is_full_marking(objspace)) { + GC_ASSERT(RVALUE_PAGE_UNCOLLECTIBLE(page, obj) == FALSE); + RVALUE_PAGE_OLD_UNCOLLECTIBLE_SET(objspace, page, obj); + } + } + check_rvalue_consistency(objspace, obj); + + objspace->marked_slots++; +} + +static void +gc_grey(rb_objspace_t *objspace, VALUE obj) +{ +#if RGENGC_CHECK_MODE + if (RVALUE_MARKED(objspace, obj) == FALSE) rb_bug("gc_grey: %s is not marked.", rb_obj_info(obj)); + if (RVALUE_MARKING(objspace, obj) == TRUE) rb_bug("gc_grey: %s is marking/remembered.", rb_obj_info(obj)); +#endif + + if (is_incremental_marking(objspace)) { + MARK_IN_BITMAP(GET_HEAP_MARKING_BITS(obj), obj); + } + + push_mark_stack(&objspace->mark_stack, obj); +} + +static bool +in_marking_range(rb_objspace_t *objspace, VALUE obj) +{ + return !FL_TEST_RAW(obj, FL_SHAREABLE) || GET_OBJSPACE_OF_VALUE(obj) == objspace; +} + +static void +check_not_tnone(VALUE obj) +{ + if (RB_UNLIKELY(RB_TYPE_P(obj, T_NONE))) { /* check here will help debugging */ + rb_obj_info_dump(obj); + if (rb_multi_ractor_p()) { + rb_objspace_t *objspace = &rb_objspace; + if (objspace->flags.during_global_gc) { + rb_bug("try to mark T_NONE object (belonging to Ractor #%d) from global GC", GET_RACTOR_OF_VALUE(obj)->pub.id); + } + else { + rb_bug("try to mark T_NONE object (belonging to Ractor #%d) from local GC of Ractor #%d", GET_RACTOR_OF_VALUE(obj)->pub.id, objspace->local_data.ractor->pub.id); + } + } + else { + rb_bug("try to mark T_NONE object"); + } + } +} + +static void +gc_mark(rb_objspace_t *objspace, VALUE obj) +{ + VM_ASSERT(GET_OBJSPACE_OF_VALUE(obj) == objspace || FL_TEST(obj, FL_SHAREABLE) || !using_local_limits(objspace)); + + if (RB_LIKELY(during_gc)) { + + if (!ruby_single_main_objspace) { + if (objspace->flags.during_global_gc) { + VM_ASSERT(is_full_marking(objspace)); + if (objspace->current_parent_objspace != GET_OBJSPACE_OF_VALUE(obj)) { + check_not_tnone(obj); + mark_in_external_reference_tbl(objspace->current_parent_objspace, obj); + } + } + else { + if (!in_marking_range(objspace, obj)) { + if (is_full_marking(objspace)) { + check_not_tnone(obj); + mark_in_external_reference_tbl(objspace, obj); + } + return; + } + } + } + + rgengc_check_relation(objspace, obj); + if (!gc_mark_set(objspace, obj)) return; /* already marked */ + + if (0) { // for debug GC marking miss + if (objspace->rgengc.parent_object) { + RUBY_DEBUG_LOG("%p (%s) parent:%p (%s)", + (void *)obj, obj_type_name(obj), + (void *)objspace->rgengc.parent_object, obj_type_name(objspace->rgengc.parent_object)); + } + else { + RUBY_DEBUG_LOG("%p (%s)", (void *)obj, obj_type_name(obj)); + } + } + + check_not_tnone(obj); + gc_aging(obj); + gc_grey(objspace, obj); + } + else { + VM_ASSERT(dont_gc_val() == TRUE); + if (GET_OBJSPACE_OF_VALUE(obj) == objspace) { + rb_gc_reachable_objects_from_callback(obj); + } + } +} + +static inline void +gc_pin(rb_objspace_t *objspace, VALUE obj) +{ + GC_ASSERT(!SPECIAL_CONST_P(obj)); + if (RB_UNLIKELY(objspace->flags.during_compacting)) { + if (RB_LIKELY(during_gc)) { + if (!RVALUE_PINNED(objspace, obj)) { + GC_ASSERT(GET_HEAP_PAGE(obj)->pinned_slots <= GET_HEAP_PAGE(obj)->total_slots); + GET_HEAP_PAGE(obj)->pinned_slots++; + MARK_IN_BITMAP(GET_HEAP_PINNED_BITS(obj), obj); + } + } + } +} + +static inline void +gc_mark_and_pin(rb_objspace_t *objspace, VALUE obj) +{ + if (SPECIAL_CONST_P(obj)) return; + gc_pin(objspace, obj); + gc_mark(objspace, obj); +} + +void +rb_gc_impl_mark_and_move(void *objspace_ptr, VALUE *ptr) +{ + rb_objspace_t *objspace = objspace_ptr; + + if (SPECIAL_CONST_P(*ptr)) return; + + if (RB_UNLIKELY(objspace->flags.during_reference_updating)) { + GC_ASSERT(objspace->flags.during_compacting); + GC_ASSERT(during_gc); + + *ptr = rb_gc_impl_location(objspace, *ptr); + } + else { + gc_mark(objspace, *ptr); + } +} + +void +rb_gc_impl_mark(void *objspace_ptr, VALUE obj) +{ + rb_objspace_t *objspace = objspace_ptr; + + if (RB_SPECIAL_CONST_P(obj)) return; + + gc_mark(objspace, obj); +} + +void +rb_gc_impl_mark_and_pin(void *objspace_ptr, VALUE obj) +{ + rb_objspace_t *objspace = objspace_ptr; + + if (RB_SPECIAL_CONST_P(obj)) return; + + gc_mark_and_pin(objspace, obj); +} + +void +rb_gc_impl_mark_maybe(void *objspace_ptr, VALUE obj) +{ + rb_objspace_t *objspace = objspace_ptr; + + (void)VALGRIND_MAKE_MEM_DEFINED(&obj, sizeof(obj)); + + if (is_pointer_to_heap(objspace, (void *)obj)) { + void *ptr = asan_unpoison_object_temporary(obj); + + /* Garbage can live on the stack, so do not mark or pin */ + switch (BUILTIN_TYPE(obj)) { + case T_ZOMBIE: + case T_NONE: + break; + default: + gc_mark_and_pin(objspace, obj); + break; + } + + if (ptr) { + GC_ASSERT(BUILTIN_TYPE(obj) == T_NONE); + asan_poison_object(obj); + } + } +} + +void +rb_gc_impl_stack_location_mark_maybe(void *objspace_ptr, VALUE obj) +{ + rb_objspace_t *objspace = objspace_ptr; + + (void)VALGRIND_MAKE_MEM_DEFINED(&obj, sizeof(obj)); + + if (is_pointer_to_heap(objspace, (void *)obj)) { + void *ptr = asan_unpoison_object_temporary(obj); + + /* Garbage can live on the stack, so do not mark or pin */ + switch (BUILTIN_TYPE(obj)) { + case T_ZOMBIE: + case T_NONE: + break; + default: + if (GET_OBJSPACE_OF_VALUE(obj) == objspace || FL_TEST(obj, FL_SHAREABLE) || !using_local_limits(objspace)) { + gc_mark_and_pin(objspace, obj); + } + break; + } + + if (ptr) { + GC_ASSERT(BUILTIN_TYPE(obj) == T_NONE); + asan_poison_object(obj); + } + } +} + +void +rb_gc_impl_mark_weak(void *objspace_ptr, VALUE *ptr) +{ + rb_objspace_t *objspace = objspace_ptr; + + if (RB_UNLIKELY(!during_gc)) return; + + VALUE obj = *ptr; + if (RB_SPECIAL_CONST_P(obj) || (using_local_limits(objspace) && GET_OBJSPACE_OF_VALUE(obj) != objspace)) return; + + GC_ASSERT(objspace->rgengc.parent_object == 0 || FL_TEST(objspace->rgengc.parent_object, FL_WB_PROTECTED)); + + if (RB_UNLIKELY(RB_TYPE_P(obj, T_NONE))) { + rb_obj_info_dump(obj); + rb_bug("try to mark T_NONE object"); + } + + /* If we are in a minor GC and the other object is old, then obj should + * already be marked and cannot be reclaimed in this GC cycle so we don't + * need to add it to the weak refences list. */ + if (!is_full_marking(objspace) && RVALUE_OLD_P(objspace, obj)) { + GC_ASSERT(RVALUE_MARKED(objspace, obj)); + GC_ASSERT(!objspace->flags.during_compacting); + + return; + } + + rgengc_check_relation(objspace, obj); + + DURING_GC_COULD_MALLOC_REGION_START(); + { + rb_darray_append(&objspace->weak_references, ptr); + } + DURING_GC_COULD_MALLOC_REGION_END(); + + objspace->profile.weak_references_count++; +} + +void +rb_gc_impl_remove_weak(void *objspace_ptr, VALUE parent_obj, VALUE *ptr) +{ + rb_objspace_t *objspace = objspace_ptr; + + /* If we're not incremental marking, then the state of the objects can't + * change so we don't need to do anything. */ + if (!is_incremental_marking(objspace)) return; + /* If parent_obj has not been marked, then ptr has not yet been marked + * weak, so we don't need to do anything. */ + if (!RVALUE_MARKED(objspace, parent_obj)) return; + + VALUE **ptr_ptr; + rb_darray_foreach(objspace->weak_references, i, ptr_ptr) { + if (*ptr_ptr == ptr) { + *ptr_ptr = NULL; + break; + } + } +} + +static inline void +gc_mark_set_parent(rb_objspace_t *objspace, VALUE obj) +{ + if (RVALUE_OLD_P(objspace, obj)) { + objspace->rgengc.parent_object = obj; + } + else { + objspace->rgengc.parent_object = Qfalse; + } + +#if VM_CHECK_MODE > 0 + objspace->current_parent_objspace = GET_OBJSPACE_OF_VALUE(obj); +#else + if (!using_local_limits(objspace)) { + objspace->current_parent_objspace = GET_OBJSPACE_OF_VALUE(obj); + } +#endif +} + +void +gc_mark_reset_parent(rb_objspace_t *objspace) +{ + objspace->rgengc.parent_object = Qfalse; + objspace->current_parent_objspace = objspace; +} + +static void +gc_mark_children(rb_objspace_t *objspace, VALUE obj) +{ + gc_mark_set_parent(objspace, obj); + rb_gc_mark_children(objspace, obj); +} + +/** + * incremental: 0 -> not incremental (do all) + * incremental: n -> mark at most `n' objects + */ +static inline int +gc_mark_stacked_objects(rb_objspace_t *objspace, int incremental, size_t count) +{ + mark_stack_t *mstack = &objspace->mark_stack; + VALUE obj; + size_t marked_slots_at_the_beginning = objspace->marked_slots; + size_t popped_count = 0; + + while (pop_mark_stack(mstack, &obj)) { + if (obj == Qundef) continue; /* skip */ + + if (RGENGC_CHECK_MODE && !RVALUE_MARKED(objspace, obj)) { + rb_bug("gc_mark_stacked_objects: %s is not marked.", rb_obj_info(obj)); + } + gc_mark_children(objspace, obj); + + if (incremental) { + if (RGENGC_CHECK_MODE && !RVALUE_MARKING(objspace, obj)) { + rb_bug("gc_mark_stacked_objects: incremental, but marking bit is 0"); + } + CLEAR_IN_BITMAP(GET_HEAP_MARKING_BITS(obj), obj); + popped_count++; + + if (popped_count + (objspace->marked_slots - marked_slots_at_the_beginning) > count) { + break; + } + } + else { + /* just ignore marking bits */ + } + } + + if (RGENGC_CHECK_MODE >= 3) gc_verify_internal_consistency(objspace); + + if (is_mark_stack_empty(mstack)) { + shrink_stack_chunk_cache(mstack); + gc_mark_reset_parent(objspace); + return TRUE; + } + else { + return FALSE; + } +} + +static int +gc_mark_stacked_objects_incremental(rb_objspace_t *objspace, size_t count) +{ + if (gc_mark_stacked_objects(objspace, TRUE, count) == TRUE && mark_externally_modifiable_tables(objspace)) { + return TRUE; + } + else + { + return FALSE; + } +} + +static int +gc_mark_stacked_objects_all(rb_objspace_t *objspace) +{ + while (true) { + gc_mark_stacked_objects(objspace, FALSE, 0); + if (mark_externally_modifiable_tables(objspace)) return TRUE; + } +} + +#if RGENGC_CHECK_MODE >= 4 + +#define MAKE_ROOTSIG(obj) (((VALUE)(obj) << 1) | 0x01) +#define IS_ROOTSIG(obj) ((VALUE)(obj) & 0x01) +#define GET_ROOTSIG(obj) ((const char *)((VALUE)(obj) >> 1)) + +struct reflist { + VALUE *list; + int pos; + int size; +}; + +static struct reflist * +reflist_create(VALUE obj) +{ + struct reflist *refs = xmalloc(sizeof(struct reflist)); + refs->size = 1; + refs->list = ALLOC_N(VALUE, refs->size); + refs->list[0] = obj; + refs->pos = 1; + return refs; +} + +static void +reflist_destruct(struct reflist *refs) +{ + xfree(refs->list); + xfree(refs); +} + +static void +reflist_add(struct reflist *refs, VALUE obj) +{ + if (refs->pos == refs->size) { + refs->size *= 2; + SIZED_REALLOC_N(refs->list, VALUE, refs->size, refs->size/2); + } + + refs->list[refs->pos++] = obj; +} + +static void +reflist_dump(struct reflist *refs) +{ + int i; + for (i=0; ipos; i++) { + VALUE obj = refs->list[i]; + if (IS_ROOTSIG(obj)) { /* root */ + fprintf(stderr, "", GET_ROOTSIG(obj)); + } + else { + fprintf(stderr, "<%s>", rb_obj_info(obj)); + } + if (i+1 < refs->pos) fprintf(stderr, ", "); + } +} + +static int +reflist_referred_from_machine_context(struct reflist *refs) +{ + int i; + for (i=0; ipos; i++) { + VALUE obj = refs->list[i]; + if (IS_ROOTSIG(obj) && strcmp(GET_ROOTSIG(obj), "machine_context") == 0) return 1; + } + return 0; +} + +struct allrefs { + rb_objspace_t *objspace; + /* a -> obj1 + * b -> obj1 + * c -> obj1 + * c -> obj2 + * d -> obj3 + * #=> {obj1 => [a, b, c], obj2 => [c, d]} + */ + struct st_table *references; + const char *category; + VALUE root_obj; + mark_stack_t mark_stack; +}; + +static int +allrefs_add(struct allrefs *data, VALUE obj) +{ + struct reflist *refs; + st_data_t r; + + if (st_lookup(data->references, obj, &r)) { + refs = (struct reflist *)r; + reflist_add(refs, data->root_obj); + return 0; + } + else { + refs = reflist_create(data->root_obj); + st_insert(data->references, obj, (st_data_t)refs); + return 1; + } +} + +static void +allrefs_i(VALUE obj, void *ptr) +{ + struct allrefs *data = (struct allrefs *)ptr; + + if (allrefs_add(data, obj)) { + push_mark_stack(&data->mark_stack, obj); + } +} + +static void +allrefs_roots_i(VALUE obj, void *ptr) +{ + struct allrefs *data = (struct allrefs *)ptr; + if (strlen(data->category) == 0) rb_bug("!!!"); + data->root_obj = MAKE_ROOTSIG(data->category); + + if (allrefs_add(data, obj)) { + push_mark_stack(&data->mark_stack, obj); + } +} +#define PUSH_MARK_FUNC_DATA(v) do { \ + struct gc_mark_func_data_struct *prev_mark_func_data = GET_RACTOR()->mfd; \ + GET_RACTOR()->mfd = (v); + +#define POP_MARK_FUNC_DATA() GET_RACTOR()->mfd = prev_mark_func_data;} while (0) + +static st_table * +objspace_allrefs(rb_objspace_t *objspace) +{ + struct allrefs data; + struct gc_mark_func_data_struct mfd; + VALUE obj; + int prev_dont_gc = dont_gc_val(); + dont_gc_on(); + + data.objspace = objspace; + data.references = st_init_numtable(); + init_mark_stack(&data.mark_stack); + + mfd.mark_func = allrefs_roots_i; + mfd.data = &data; + + /* traverse root objects */ + PUSH_MARK_FUNC_DATA(&mfd); + GET_RACTOR()->mfd = &mfd; + rb_gc_mark_roots(objspace, &data.category); + POP_MARK_FUNC_DATA(); + + /* traverse rest objects reachable from root objects */ + while (pop_mark_stack(&data.mark_stack, &obj)) { + rb_objspace_reachable_objects_from(data.root_obj = obj, allrefs_i, &data); + } + free_stack_chunks(&data.mark_stack); + + dont_gc_set(prev_dont_gc); + return data.references; +} + +static int +objspace_allrefs_destruct_i(st_data_t key, st_data_t value, st_data_t ptr) +{ + struct reflist *refs = (struct reflist *)value; + reflist_destruct(refs); + return ST_CONTINUE; +} + +static void +objspace_allrefs_destruct(struct st_table *refs) +{ + st_foreach(refs, objspace_allrefs_destruct_i, 0); + st_free_table(refs); +} + +#if RGENGC_CHECK_MODE >= 5 +static int +allrefs_dump_i(st_data_t k, st_data_t v, st_data_t ptr) +{ + VALUE obj = (VALUE)k; + struct reflist *refs = (struct reflist *)v; + fprintf(stderr, "[allrefs_dump_i] %s <- ", rb_obj_info(obj)); + reflist_dump(refs); + fprintf(stderr, "\n"); + return ST_CONTINUE; +} + +static void +allrefs_dump(rb_objspace_t *objspace) +{ + VALUE size = objspace->rgengc.allrefs_table->num_entries; + fprintf(stderr, "[all refs] (size: %"PRIuVALUE")\n", size); + st_foreach(objspace->rgengc.allrefs_table, allrefs_dump_i, 0); +} +#endif + +static int +gc_check_after_marks_i(st_data_t k, st_data_t v, st_data_t ptr) +{ + VALUE obj = k; + struct reflist *refs = (struct reflist *)v; + rb_objspace_t *objspace = (rb_objspace_t *)ptr; + + /* object should be marked or oldgen */ + if (!RVALUE_MARKED(obj)) { + fprintf(stderr, "gc_check_after_marks_i: %s is not marked and not oldgen.\n", rb_obj_info(obj)); + fprintf(stderr, "gc_check_after_marks_i: %p is referred from ", (void *)obj); + reflist_dump(refs); + + if (reflist_referred_from_machine_context(refs)) { + fprintf(stderr, " (marked from machine stack).\n"); + /* marked from machine context can be false positive */ + } + else { + objspace->rgengc.error_count++; + fprintf(stderr, "\n"); + } + } + return ST_CONTINUE; +} + +static void +gc_marks_check(rb_objspace_t *objspace, st_foreach_callback_func *checker_func, const char *checker_name) +{ + size_t saved_malloc_increase = objspace->malloc_params.increase; +#if RGENGC_ESTIMATE_OLDMALLOC + size_t saved_oldmalloc_increase = objspace->rgengc.oldmalloc_increase; +#endif + VALUE already_disabled = rb_objspace_gc_disable(objspace); + + objspace->rgengc.allrefs_table = objspace_allrefs(objspace); + + if (checker_func) { + st_foreach(objspace->rgengc.allrefs_table, checker_func, (st_data_t)objspace); + } + + if (objspace->rgengc.error_count > 0) { +#if RGENGC_CHECK_MODE >= 5 + allrefs_dump(objspace); +#endif + if (checker_name) rb_bug("%s: GC has problem.", checker_name); + } + + objspace_allrefs_destruct(objspace->rgengc.allrefs_table); + objspace->rgengc.allrefs_table = 0; + + if (already_disabled == Qfalse) rb_objspace_gc_enable(objspace); + objspace->malloc_params.increase = saved_malloc_increase; +#if RGENGC_ESTIMATE_OLDMALLOC + objspace->rgengc.oldmalloc_increase = saved_oldmalloc_increase; +#endif +} +#endif /* RGENGC_CHECK_MODE >= 4 */ + +struct verify_internal_consistency_struct { + rb_objspace_t *objspace; + int err_count; + size_t live_object_count; + size_t zombie_object_count; + + VALUE parent; + size_t old_object_count; + size_t remembered_shady_count; +}; + +static void +check_generation_i(const VALUE child, void *ptr) +{ + struct verify_internal_consistency_struct *data = (struct verify_internal_consistency_struct *)ptr; + const VALUE parent = data->parent; + + if (RGENGC_CHECK_MODE) GC_ASSERT(RVALUE_OLD_P(data->objspace, parent)); + + bool absorption_correction_needed = data->objspace->rgengc.need_major_gc & GPR_FLAG_MAJOR_BY_ABSORB; + if (absorption_correction_needed) return; + if (!RVALUE_OLD_P(data->objspace, child)) { + if (!RVALUE_REMEMBERED(data->objspace, parent) && + !RVALUE_REMEMBERED(data->objspace, child) && + !RVALUE_UNCOLLECTIBLE(data->objspace, child)) { + fprintf(stderr, "verify_internal_consistency_reachable_i: WB miss (O->Y) %s -> %s\n", rb_obj_info(parent), rb_obj_info(child)); + data->err_count++; + } + } +} + +static void +check_limmune_i(const VALUE child, void *ptr) +{ + struct verify_internal_consistency_struct *data = (struct verify_internal_consistency_struct *)ptr; + const VALUE parent = data->parent; + + if (false && MUTABLE_SHAREABLE(parent)) { + if (!RVALUE_LOCAL_IMMUNE(data->objspace, child)) { + fprintf(stderr, "check_limmune_i: (mutable-shareable -> non-local-immune) - %s -> %s\n", + obj_info(parent), obj_info(child)); + } + data->err_count++; + } + if (RVALUE_LOCAL_IMMUNE(data->objspace, parent)) { + if (BUILTIN_TYPE(parent) != T_CLASS) { + if (!RVALUE_LOCAL_IMMUNE(data->objspace, child)) { + fprintf(stderr, "check_limmune_i: (local-immune -> non-local-immune) - %s -> %s\n", + obj_info(parent), obj_info(child)); + data->err_count++; + rb_bug("sigh"); + } + } + } +} + +static void +check_color_i(const VALUE child, void *ptr) +{ + struct verify_internal_consistency_struct *data = (struct verify_internal_consistency_struct *)ptr; + const VALUE parent = data->parent; + + if (!RVALUE_WB_UNPROTECTED(data->objspace, parent) && RVALUE_WHITE_P(data->objspace, child)) { + fprintf(stderr, "verify_internal_consistency_reachable_i: WB miss (B->W) - %s -> %s\n", + rb_obj_info(parent), rb_obj_info(child)); + data->err_count++; + } +} + +static void +check_children_i(const VALUE child, void *ptr) +{ + struct verify_internal_consistency_struct *data = (struct verify_internal_consistency_struct *)ptr; + if (check_rvalue_consistency_force(data->objspace, child, FALSE) != 0) { + fprintf(stderr, "check_children_i: %s has error (referenced from %s)", + rb_obj_info(child), rb_obj_info(data->parent)); + + data->err_count++; + } +} + +static int +verify_internal_consistency_i(void *page_start, void *page_end, size_t stride, + struct verify_internal_consistency_struct *data) +{ + VALUE obj; + rb_objspace_t *objspace = data->objspace; + + for (obj = (VALUE)page_start; obj != (VALUE)page_end; obj += stride) { + void *poisoned = asan_unpoison_object_temporary(obj); + + if (!rb_gc_impl_garbage_object_p(objspace, obj)) { + /* count objects */ + data->live_object_count++; + data->parent = obj; + + /* Normally, we don't expect T_MOVED objects to be in the heap. + * But they can stay alive on the stack, */ + if (!gc_object_moved_p(objspace, obj)) { + /* moved slots don't have children */ + rb_objspace_reachable_objects_from(obj, check_children_i, (void *)data); + } + + /* check health of children */ + if (RVALUE_OLD_P(obj)) { + if (RVALUE_MARKED(obj)) { + data->old_object_count++; + } + else { + VM_ASSERT(RVALUE_LOCAL_IMMUNE(obj)); + } + } + if (RVALUE_WB_UNPROTECTED(objspace, obj) && RVALUE_UNCOLLECTIBLE(objspace, obj)) data->remembered_shady_count++; + + if (!is_marking(objspace) && RVALUE_OLD_P(objspace, obj)) { + /* reachable objects from an oldgen object should be old or (young with remember) */ + data->parent = obj; + rb_objspace_reachable_objects_from(obj, check_generation_i, (void *)data); + } + + if (is_incremental_marking(objspace)) { + if (RVALUE_BLACK_P(objspace, obj)) { + /* reachable objects from black objects should be black or grey objects */ + data->parent = obj; + rb_objspace_reachable_objects_from(obj, check_color_i, (void *)data); + } + } + rb_objspace_reachable_objects_from(obj, check_limmune_i, (void *)data); + } + else { + if (BUILTIN_TYPE(obj) == T_ZOMBIE) { + data->zombie_object_count++; + + if ((RBASIC(obj)->flags & ~ZOMBIE_OBJ_KEPT_FLAGS) != T_ZOMBIE) { + fprintf(stderr, "verify_internal_consistency_i: T_ZOMBIE has extra flags set: %s\n", + rb_obj_info(obj)); + data->err_count++; + } + + if (!!FL_TEST(obj, FL_FINALIZE) != !!st_is_member(finalizer_table, obj)) { + fprintf(stderr, "verify_internal_consistency_i: FL_FINALIZE %s but %s finalizer_table: %s\n", + FL_TEST(obj, FL_FINALIZE) ? "set" : "not set", st_is_member(finalizer_table, obj) ? "in" : "not in", + rb_obj_info(obj)); + data->err_count++; + } + } + } + if (poisoned) { + GC_ASSERT(BUILTIN_TYPE(obj) == T_NONE); + asan_poison_object(obj); + } + } + + return 0; +} + +static int +gc_verify_heap_page(rb_objspace_t *objspace, struct heap_page *page, VALUE obj) +{ + unsigned int has_remembered_shady = FALSE; + unsigned int has_remembered_old = FALSE; + int remembered_old_objects = 0; + int free_objects = 0; + int zombie_objects = 0; + + short slot_size = page->slot_size; + uintptr_t start = (uintptr_t)page->start; + uintptr_t end = start + page->total_slots * slot_size; + + for (uintptr_t ptr = start; ptr < end; ptr += slot_size) { + VALUE val = (VALUE)ptr; + void *poisoned = asan_unpoison_object_temporary(val); + enum ruby_value_type type = BUILTIN_TYPE(val); + + if (type == T_NONE) free_objects++; + if (type == T_ZOMBIE) zombie_objects++; + if (RVALUE_PAGE_UNCOLLECTIBLE(page, val) && RVALUE_PAGE_WB_UNPROTECTED(page, val)) { + has_remembered_shady = TRUE; + } + if (RVALUE_PAGE_MARKING(page, val)) { + has_remembered_old = TRUE; + remembered_old_objects++; + } + + if (poisoned) { + GC_ASSERT(BUILTIN_TYPE(val) == T_NONE); + asan_poison_object(val); + } + } + + if (!is_incremental_marking(objspace) && + page->flags.has_remembered_objects == FALSE && has_remembered_old == TRUE) { + + for (uintptr_t ptr = start; ptr < end; ptr += slot_size) { + VALUE val = (VALUE)ptr; + if (RVALUE_PAGE_MARKING(page, val)) { + fprintf(stderr, "marking -> %s\n", rb_obj_info(val)); + } + } + rb_bug("page %p's has_remembered_objects should be false, but there are remembered old objects (%d). %s", + (void *)page, remembered_old_objects, obj ? rb_obj_info(obj) : ""); + } + + if (page->flags.has_uncollectible_wb_unprotected_objects == FALSE && has_remembered_shady == TRUE) { + rb_bug("page %p's has_remembered_shady should be false, but there are remembered shady objects. %s", + (void *)page, obj ? rb_obj_info(obj) : ""); + } + + if (0) { + /* free_slots may not equal to free_objects */ + if (page->free_slots != free_objects) { + rb_bug("page %p's free_slots should be %d, but %d", (void *)page, page->free_slots, free_objects); + } + } + if (page->final_slots != zombie_objects) { + rb_bug("page %p's final_slots should be %d, but %d", (void *)page, page->final_slots, zombie_objects); + } + + return remembered_old_objects; +} + +static int +gc_verify_heap_pages_(rb_objspace_t *objspace, struct ccan_list_head *head) +{ + int remembered_old_objects = 0; + struct heap_page *page = 0; + + ccan_list_for_each(head, page, page_node) { + asan_unlock_freelist(page); + struct free_slot *p = page->freelist; + while (p) { + VALUE vp = (VALUE)p; + VALUE prev = vp; + asan_unpoison_object(vp, false); + if (BUILTIN_TYPE(vp) != T_NONE) { + fprintf(stderr, "freelist slot expected to be T_NONE but was: %s\n", rb_obj_info(vp)); + } + p = p->next; + asan_poison_object(prev); + } + asan_lock_freelist(page); + + if (page->flags.has_remembered_objects == FALSE) { + remembered_old_objects += gc_verify_heap_page(objspace, page, Qfalse); + } + } + + return remembered_old_objects; +} + +static int +gc_verify_heap_pages(rb_objspace_t *objspace) +{ + int remembered_old_objects = 0; + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + remembered_old_objects += gc_verify_heap_pages_(objspace, &(SIZE_POOL_EDEN_HEAP(&size_pools[i])->pages)); + remembered_old_objects += gc_verify_heap_pages_(objspace, &(SIZE_POOL_TOMB_HEAP(&size_pools[i])->pages)); + } + return remembered_old_objects; +} + +static void +gc_verify_internal_consistency_(rb_objspace_t *objspace) +{ + struct verify_internal_consistency_struct data = {0}; + + data.objspace = objspace; + gc_report(5, objspace, "gc_verify_internal_consistency: start\n"); + + /* check relations */ + for (size_t i = 0; i < heap_allocated_pages; i++) { + struct heap_page *page = heap_pages_sorted[i]; + short slot_size = page->slot_size; + + uintptr_t start = (uintptr_t)page->start; + uintptr_t end = start + page->total_slots * slot_size; + + verify_internal_consistency_i((void *)start, (void *)end, slot_size, &data); + } + + if (data.err_count != 0) { +#if RGENGC_CHECK_MODE >= 5 + objspace->rgengc.error_count = data.err_count; + gc_marks_check(objspace, NULL, NULL); + allrefs_dump(objspace); +#endif + rb_bug("gc_verify_internal_consistency: found internal inconsistency."); + } + + /* check heap_page status */ + gc_verify_heap_pages(objspace); + + /* check counters */ + + if (!is_lazy_sweeping(objspace) && + !finalizing) { + if (objspace_live_slots(objspace) != data.live_object_count) { + fprintf(stderr, "heap_pages_final_slots: %"PRIdSIZE", total_freed_objects: %"PRIdSIZE"\n", + heap_pages_final_slots, total_freed_objects(objspace)); + rb_bug("inconsistent live slot number: expect %"PRIuSIZE", but %"PRIuSIZE".", + objspace_live_slots(objspace), data.live_object_count); + } + } + + if (!is_marking(objspace)) { + if (objspace->rgengc.old_objects != data.old_object_count) { + rb_bug("inconsistent old slot number: expect %"PRIuSIZE", but %"PRIuSIZE".", + objspace->rgengc.old_objects, data.old_object_count); + } + if (objspace->rgengc.uncollectible_wb_unprotected_objects != data.remembered_shady_count) { + rb_bug("inconsistent number of wb unprotected objects: expect %"PRIuSIZE", but %"PRIuSIZE".", + objspace->rgengc.uncollectible_wb_unprotected_objects, data.remembered_shady_count); + } + } + + if (!finalizing) { + size_t list_count = 0; + + { + VALUE z = heap_pages_deferred_final; + while (z) { + list_count++; + z = RZOMBIE(z)->next; + } + } + + if (heap_pages_final_slots != data.zombie_object_count || + heap_pages_final_slots != list_count) { + + rb_bug("inconsistent finalizing object count:\n" + " expect %"PRIuSIZE"\n" + " but %"PRIuSIZE" zombies\n" + " heap_pages_deferred_final list has %"PRIuSIZE" items.", + heap_pages_final_slots, + data.zombie_object_count, + list_count); + } + } + + gc_report(5, objspace, "gc_verify_internal_consistency: OK\n"); +} + +static void +gc_verify_internal_consistency(void *objspace_ptr) +{ + rb_objspace_t *objspace = objspace_ptr; + + unsigned int lev = rb_gc_vm_lock(); + { + rb_gc_vm_barrier(); // stop other ractors + + unsigned int prev_during_gc = during_gc; + during_gc = FALSE; // stop gc here + HEAP_LOCK_ENTER(objspace); + { + gc_verify_internal_consistency_(objspace); + } + HEAP_LOCK_LEAVE(objspace); + during_gc = prev_during_gc; + } + rb_gc_vm_unlock(lev); +} + +static void +heap_move_pooled_pages_to_free_pages(rb_heap_t *heap) +{ + if (heap->pooled_pages) { + if (heap->free_pages) { + struct heap_page *free_pages_tail = heap->free_pages; + while (free_pages_tail->free_next) { + free_pages_tail = free_pages_tail->free_next; + } + free_pages_tail->free_next = heap->pooled_pages; + } + else { + heap->free_pages = heap->pooled_pages; + } + + heap->pooled_pages = NULL; + } +} + +static int +gc_remember_unprotected(rb_objspace_t *objspace, VALUE obj) +{ + struct heap_page *page = GET_HEAP_PAGE(obj); + bits_t *uncollectible_bits = &page->uncollectible_bits[0]; + + if (!MARKED_IN_BITMAP(uncollectible_bits, obj)) { + page->flags.has_uncollectible_wb_unprotected_objects = TRUE; + MARK_IN_BITMAP(uncollectible_bits, obj); + objspace->rgengc.uncollectible_wb_unprotected_objects++; + +#if RGENGC_PROFILE > 0 + objspace->profile.total_remembered_shady_object_count++; +#if RGENGC_PROFILE >= 2 + objspace->profile.remembered_shady_object_count_types[BUILTIN_TYPE(obj)]++; +#endif +#endif + return TRUE; + } + else { + return FALSE; + } +} + +static inline void +gc_marks_wb_unprotected_objects_plane(rb_objspace_t *objspace, uintptr_t p, bits_t bits) +{ + if (bits) { + do { + if (bits & 1) { + gc_report(2, objspace, "gc_marks_wb_unprotected_objects: marked shady: %s\n", rb_obj_info((VALUE)p)); + GC_ASSERT(RVALUE_WB_UNPROTECTED(objspace, (VALUE)p)); + GC_ASSERT(RVALUE_MARKED(objspace, (VALUE)p)); + gc_mark_children(objspace, (VALUE)p); + } + p += BASE_SLOT_SIZE; + bits >>= 1; + } while (bits); + } +} + +static void +gc_marks_wb_unprotected_objects(rb_objspace_t *objspace, rb_heap_t *heap) +{ + struct heap_page *page = 0; + + ccan_list_for_each(&heap->pages, page, page_node) { + bits_t *mark_bits = page->mark_bits; + bits_t *wbun_bits = page->wb_unprotected_bits; + uintptr_t p = page->start; + size_t j; + + bits_t bits = mark_bits[0] & wbun_bits[0]; + bits >>= NUM_IN_PAGE(p); + gc_marks_wb_unprotected_objects_plane(objspace, p, bits); + p += (BITS_BITLENGTH - NUM_IN_PAGE(p)) * BASE_SLOT_SIZE; + + for (j=1; jweak_references, i, ptr_ptr) { + if (!*ptr_ptr) continue; + + VALUE obj = **ptr_ptr; + + if (RB_SPECIAL_CONST_P(obj)) continue; + + if (!RVALUE_MARKED(obj) && !(using_local_limits(objspace) && RVALUE_LOCAL_IMMUNE(obj))) { + **ptr_ptr = Qundef; + } + else { + retained_weak_references_count++; + } + } + + objspace->profile.retained_weak_references_count = retained_weak_references_count; + + rb_darray_clear(objspace->weak_references); + DURING_GC_COULD_MALLOC_REGION_START(); + { + rb_darray_resize_capa(&objspace->weak_references, retained_weak_references_count); + } + DURING_GC_COULD_MALLOC_REGION_END(); +} + +static void +gc_marks_finish(rb_objspace_t *objspace) +{ + /* finish incremental GC */ + if (is_incremental_marking(objspace)) { + if (RGENGC_CHECK_MODE && is_mark_stack_empty(&objspace->mark_stack) == 0) { + rb_bug("gc_marks_finish: mark stack is not empty (%"PRIdSIZE").", + mark_stack_size(&objspace->mark_stack)); + } + + rb_gc_mark_roots(objspace, NULL); + while (gc_mark_stacked_objects_incremental(objspace, INT_MAX) == false); + +#if RGENGC_CHECK_MODE >= 2 + if (gc_verify_heap_pages(objspace) != 0) { + rb_bug("gc_marks_finish (incremental): there are remembered old objects."); + } +#endif + + objspace->flags.during_incremental_marking = FALSE; + /* check children of all marked wb-unprotected objects */ + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + gc_marks_wb_unprotected_objects(objspace, SIZE_POOL_EDEN_HEAP(&size_pools[i])); + } + } + + gc_update_weak_references(objspace); + gc_update_external_weak_references(objspace); + if (is_full_marking(objspace)) update_shared_object_references(objspace); + +#if RGENGC_CHECK_MODE >= 2 + gc_verify_internal_consistency(objspace); +#endif + +#if RGENGC_CHECK_MODE >= 4 + during_gc = FALSE; + gc_marks_check(objspace, gc_check_after_marks_i, "after_marks"); + during_gc = TRUE; +#endif + + { + /* decide full GC is needed or not */ + size_t total_slots = heap_allocatable_slots(objspace) + heap_eden_total_slots(objspace); + size_t sweep_slots = total_slots - objspace->marked_slots; /* will be swept slots */ + size_t max_free_slots = (size_t)(total_slots * gc_params.heap_free_slots_max_ratio); + size_t min_free_slots = (size_t)(total_slots * gc_params.heap_free_slots_min_ratio); + int full_marking = is_full_marking(objspace); + const unsigned long r_mul = objspace->live_ractor_cache_count > 8 ? 8 : objspace->live_ractor_cache_count; // upto 8 + + GC_ASSERT(heap_eden_total_slots(objspace) >= objspace->marked_slots); + + /* Setup freeable slots. */ + size_t total_init_slots = 0; + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + total_init_slots += gc_params.size_pool_init_slots[i] * r_mul; + } + + if (max_free_slots < total_init_slots) { + max_free_slots = total_init_slots; + } + + if (sweep_slots > max_free_slots) { + heap_pages_freeable_pages = (sweep_slots - max_free_slots) / HEAP_PAGE_OBJ_LIMIT; + } + else { + heap_pages_freeable_pages = 0; + } + + /* check free_min */ + if (min_free_slots < gc_params.heap_free_slots * r_mul) { + min_free_slots = gc_params.heap_free_slots * r_mul; + } + + if (sweep_slots < min_free_slots) { + if (!full_marking) { + if (objspace->profile.count - objspace->rgengc.last_major_gc < RVALUE_OLD_AGE) { + full_marking = TRUE; + /* do not update last_major_gc, because full marking is not done. */ + /* goto increment; */ + } + else { + gc_report(1, objspace, "gc_marks_finish: next is full GC!!)\n"); + gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_NOFREE; + } + } + } + + if (full_marking) { + /* See the comment about RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR */ + const double r = gc_params.oldobject_limit_factor; + objspace->rgengc.uncollectible_wb_unprotected_objects_limit = MAX( + (size_t)(objspace->rgengc.uncollectible_wb_unprotected_objects * r), + (size_t)(objspace->rgengc.old_objects * gc_params.uncollectible_wb_unprotected_objects_limit_ratio) + ); + objspace->rgengc.old_objects_limit = (size_t)(objspace->rgengc.old_objects * r); + + arrange_next_gc_global_status(gc_params.sharedobject_limit_factor); + } + + if (objspace->rgengc.uncollectible_wb_unprotected_objects > objspace->rgengc.uncollectible_wb_unprotected_objects_limit) { + gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_SHADY; + } + if (objspace->rgengc.old_objects > objspace->rgengc.old_objects_limit) { + gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_OLDGEN; + } + if (RGENGC_FORCE_MAJOR_GC) { + 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), + global_gc_needed() ? "global" : (gc_needs_major_flags ? "major" : "minor")); + } + + // TODO: refactor so we don't need to call this + rb_ractor_finish_marking(); + + rb_gc_event_hook(0, RUBY_INTERNAL_EVENT_GC_END_MARK); +} + +static bool +gc_compact_heap_cursors_met_p(rb_heap_t *heap) +{ + return heap->sweeping_page == heap->compact_cursor; +} + + +static rb_size_pool_t * +gc_compact_destination_pool(rb_objspace_t *objspace, rb_size_pool_t *src_pool, VALUE obj) +{ + size_t obj_size = rb_gc_obj_optimal_size(obj); + if (obj_size == 0) { + return src_pool; + } + + size_t idx = 0; + if (rb_gc_impl_size_allocatable_p(obj_size)) { + idx = size_pool_idx_for_size(obj_size); + } + + return &size_pools[idx]; +} + +static bool +gc_compact_move(rb_objspace_t *objspace, rb_heap_t *heap, rb_size_pool_t *size_pool, VALUE src) +{ + GC_ASSERT(BUILTIN_TYPE(src) != T_MOVED); + GC_ASSERT(gc_is_moveable_obj(objspace, src)); + + rb_size_pool_t *dest_pool = gc_compact_destination_pool(objspace, size_pool, src); + rb_heap_t *dheap = SIZE_POOL_EDEN_HEAP(dest_pool); + uint32_t orig_shape = 0; + uint32_t new_shape = 0; + + if (gc_compact_heap_cursors_met_p(dheap)) { + return dheap != heap; + } + + if (RB_TYPE_P(src, T_OBJECT)) { + orig_shape = rb_gc_get_shape(src); + + if (dheap != heap) { + new_shape = rb_gc_rebuild_shape(src, dest_pool - size_pools); + + if (new_shape == 0) { + dheap = heap; + } + } + } + + while (!try_move(objspace, dheap, dheap->free_pages, src)) { + struct gc_sweep_context ctx = { + .page = dheap->sweeping_page, + .final_slots = 0, + .freed_slots = 0, + .empty_slots = 0, + }; + + /* The page of src could be partially compacted, so it may contain + * T_MOVED. Sweeping a page may read objects on this page, so we + * need to lock the page. */ + lock_page_body(objspace, GET_PAGE_BODY(src)); + gc_sweep_page(objspace, dheap, &ctx); + unlock_page_body(objspace, GET_PAGE_BODY(src)); + + if (dheap->sweeping_page->free_slots > 0) { + heap_add_freepage(dheap, dheap->sweeping_page); + } + + dheap->sweeping_page = ccan_list_next(&dheap->pages, dheap->sweeping_page, page_node); + if (gc_compact_heap_cursors_met_p(dheap)) { + return dheap != heap; + } + } + + if (orig_shape != 0) { + if (new_shape != 0) { + VALUE dest = rb_gc_impl_location(objspace, src); + rb_gc_set_shape(dest, new_shape); + } + RMOVED(src)->original_shape_id = orig_shape; + } + + return true; +} + +static bool +gc_compact_plane(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap, uintptr_t p, bits_t bitset, struct heap_page *page) +{ + short slot_size = page->slot_size; + short slot_bits = slot_size / BASE_SLOT_SIZE; + GC_ASSERT(slot_bits > 0); + + do { + VALUE vp = (VALUE)p; + GC_ASSERT(vp % BASE_SLOT_SIZE == 0); + + if (bitset & 1) { + objspace->rcompactor.considered_count_table[BUILTIN_TYPE(vp)]++; + + if (gc_is_moveable_obj(objspace, vp)) { + if (!gc_compact_move(objspace, heap, size_pool, vp)) { + //the cursors met. bubble up + return false; + } + } + } + p += slot_size; + bitset >>= slot_bits; + } while (bitset); + + return true; +} + +// Iterate up all the objects in page, moving them to where they want to go +static bool +gc_compact_page(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap, struct heap_page *page) +{ + GC_ASSERT(page == heap->compact_cursor); + + bits_t *mark_bits, *pin_bits; + bits_t bitset; + uintptr_t p = page->start; + + mark_bits = page->mark_bits; + pin_bits = page->pinned_bits; + + // objects that can be moved are marked and not pinned + bitset = (mark_bits[0] & ~pin_bits[0]); + bitset >>= NUM_IN_PAGE(p); + if (bitset) { + if (!gc_compact_plane(objspace, size_pool, heap, (uintptr_t)p, bitset, page)) + return false; + } + p += (BITS_BITLENGTH - NUM_IN_PAGE(p)) * BASE_SLOT_SIZE; + + for (int j = 1; j < HEAP_PAGE_BITMAP_LIMIT; j++) { + bitset = (mark_bits[j] & ~pin_bits[j]); + if (bitset) { + if (!gc_compact_plane(objspace, size_pool, heap, (uintptr_t)p, bitset, page)) + return false; + } + p += BITS_BITLENGTH * BASE_SLOT_SIZE; + } + + return true; +} + +static bool +gc_compact_all_compacted_p(rb_objspace_t *objspace) +{ + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); + + if (heap->total_pages > 0 && + !gc_compact_heap_cursors_met_p(heap)) { + return false; + } + } + + return true; +} + +static void +gc_sweep_compact(rb_objspace_t *objspace) +{ + gc_compact_start(objspace); +#if RGENGC_CHECK_MODE >= 2 + gc_verify_internal_consistency(objspace); +#endif + + while (!gc_compact_all_compacted_p(objspace)) { + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); + + if (gc_compact_heap_cursors_met_p(heap)) { + continue; + } + + struct heap_page *start_page = heap->compact_cursor; + + if (!gc_compact_page(objspace, size_pool, heap, start_page)) { + lock_page_body(objspace, GET_PAGE_BODY(start_page->start)); + + continue; + } + + // If we get here, we've finished moving all objects on the compact_cursor page + // So we can lock it and move the cursor on to the next one. + lock_page_body(objspace, GET_PAGE_BODY(start_page->start)); + heap->compact_cursor = ccan_list_prev(&heap->pages, heap->compact_cursor, page_node); + } + } + + gc_compact_finish(objspace); + +#if RGENGC_CHECK_MODE >= 2 + gc_verify_internal_consistency(objspace); +#endif +} + +static void +gc_marks_rest_no_finish(rb_objspace_t *objspace) +{ + gc_report(1, objspace, "gc_marks_rest\n"); + + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + SIZE_POOL_EDEN_HEAP(&size_pools[i])->pooled_pages = NULL; + } + + if (is_incremental_marking(objspace)) { + while (gc_mark_stacked_objects_incremental(objspace, INT_MAX) == FALSE); + } + else { + gc_mark_stacked_objects_all(objspace); + } +} + +static void +gc_marks_rest(rb_objspace_t *objspace) +{ + gc_marks_rest_no_finish(objspace); + gc_marks_finish(objspace); +} + +static bool +gc_marks_step(rb_objspace_t *objspace, size_t slots) +{ + bool marking_finished = false; + + GC_ASSERT(is_marking(objspace)); + if (gc_mark_stacked_objects_incremental(objspace, slots)) { + gc_marks_finish(objspace); + + marking_finished = true; + } + + return marking_finished; +} + +static bool +gc_marks_continue(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap) +{ + GC_ASSERT(dont_gc_val() == FALSE); + bool marking_finished = true; + + gc_marking_enter(objspace); + + if (heap->free_pages) { + gc_report(2, objspace, "gc_marks_continue: has pooled pages"); + + marking_finished = gc_marks_step(objspace, objspace->rincgc.step_slots); + } + else { + gc_report(2, objspace, "gc_marks_continue: no more pooled pages (stack depth: %"PRIdSIZE").\n", + mark_stack_size(&objspace->mark_stack)); + size_pool->force_incremental_marking_finish_count++; + gc_marks_rest(objspace); + } + + gc_marking_exit(objspace); + + return marking_finished; +} + +static void +gc_marks_prepare(rb_objspace_t *objspace, int full_mark) +{ + /* start marking */ + gc_report(1, objspace, "gc_marks_start: (%s)\n", full_mark ? "full" : "minor"); + gc_mode_transition(objspace, gc_mode_marking); + + VM_ASSERT(count_objspaces(GET_VM()) > 1 || (shared_reference_tbl_empty(objspace) && external_reference_tbl_empty(objspace))); + confirm_externally_added_external_references(objspace); + + if (full_mark) { + size_t incremental_marking_steps = (objspace->rincgc.pooled_slots / INCREMENTAL_MARK_STEP_ALLOCATIONS) + 1; + objspace->rincgc.step_slots = (objspace->marked_slots * 2) / incremental_marking_steps; + + if (0) fprintf(stderr, "objspace->marked_slots: %"PRIdSIZE", " + "objspace->rincgc.pooled_page_num: %"PRIdSIZE", " + "objspace->rincgc.step_slots: %"PRIdSIZE", \n", + objspace->marked_slots, objspace->rincgc.pooled_slots, objspace->rincgc.step_slots); + objspace->flags.during_minor_gc = FALSE; + if (ruby_enable_autocompact) { + objspace->flags.during_compacting |= TRUE; + } + objspace->profile.major_gc_count++; + objspace->rgengc.uncollectible_wb_unprotected_objects = 0; + objspace->rgengc.old_objects = 0; + objspace->rgengc.last_major_gc = objspace->profile.count; + objspace->marked_slots = 0; + + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); + rgengc_mark_and_rememberset_clear(objspace, heap); + heap_move_pooled_pages_to_free_pages(heap); + + if (objspace->flags.during_compacting) { + struct heap_page *page = NULL; + + ccan_list_for_each(&heap->pages, page, page_node) { + page->pinned_slots = 0; + } + } + } + } + else { + objspace->flags.during_minor_gc = TRUE; + objspace->marked_slots = + objspace->rgengc.old_objects + objspace->rgengc.uncollectible_wb_unprotected_objects; /* uncollectible objects are marked already */ + objspace->profile.minor_gc_count++; + + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rgengc_rememberset_mark(objspace, SIZE_POOL_EDEN_HEAP(&size_pools[i])); + } + } + +} + +static void +gc_marks_start(rb_objspace_t *objspace, int full_mark) +{ + rb_gc_mark_roots(objspace, NULL); + + gc_report(1, objspace, "gc_marks_start: (%s) end, stack in %"PRIdSIZE"\n", + full_mark ? "full" : "minor", mark_stack_size(&objspace->mark_stack)); +} + +#if RGENGC_PROFILE > 0 +static void +gc_record_old_objects(rb_objspace_t *objspace) +{ + if (gc_prof_record(objspace)) { + gc_profile_record *record = gc_prof_record(objspace); + record->old_objects = objspace->rgengc.old_objects; + } +} +#endif + +static bool +gc_marks(rb_objspace_t *objspace, int full_mark) +{ + gc_prof_mark_timer_start(objspace); + gc_marking_enter(objspace); + + bool marking_finished = false; + + /* setup marking */ + + gc_marks_prepare(objspace, full_mark); + gc_marks_start(objspace, full_mark); + if (!is_incremental_marking(objspace)) { + gc_marks_rest(objspace); + marking_finished = true; + } + +#if RGENGC_PROFILE > 0 + gc_record_old_objects(objspace); +#endif + + gc_marking_exit(objspace); + gc_prof_mark_timer_stop(objspace); + + return marking_finished; +} + +static void +gc_marks_prepare_full(rb_objspace_t *objspace) +{ + gc_marks_prepare(objspace, true); +} + +static void +gc_marks_start_full(rb_objspace_t *objspace) +{ + gc_marks_start(objspace, true); +} + +static void +gc_marks_global(rb_objspace_t *objspace) +{ + rb_vm_t *vm = GET_VM(); + + global_gc_for_each_objspace(vm, objspace, gc_prof_mark_timer_start); + global_gc_for_each_objspace(vm, objspace, gc_marks_prepare_full); + global_gc_for_each_objspace(vm, objspace, gc_marks_start_full); + + rb_vm_ractor_mark(vm); + + global_gc_for_each_objspace(vm, objspace, gc_marks_rest_no_finish); + global_gc_for_each_objspace(vm, objspace, gc_marks_finish); + +#if RGENGC_PROFILE > 0 + global_gc_for_each_objspace(vm, objspace, gc_record_old_objects); +#endif + + global_gc_for_each_objspace(vm, objspace, gc_prof_mark_timer_stop); + global_gc_for_each_objspace(vm, objspace, gc_sweep); +} + +/* RGENGC */ + +static void +gc_report_body(int level, rb_objspace_t *objspace, const char *fmt, ...) +{ + if (level <= RGENGC_DEBUG) { + char buf[1024]; + FILE *out = stderr; + va_list args; + const char *status = " "; + + if (during_gc) { + status = is_full_marking(objspace) ? "+" : "-"; + } + else { + if (is_lazy_sweeping(objspace)) { + status = "S"; + } + if (is_incremental_marking(objspace)) { + status = "M"; + } + } + + va_start(args, fmt); + vsnprintf(buf, 1024, fmt, args); + va_end(args); + + fprintf(out, "%s|", status); + fputs(buf, out); + } +} + +/* bit operations */ + +static int +rgengc_remembersetbits_set(rb_objspace_t *objspace, VALUE obj) +{ + struct heap_page *page = GET_HEAP_PAGE(obj); + bits_t *bits = &page->remembered_bits[0]; + + if (MARKED_IN_BITMAP(bits, obj)) { + return FALSE; + } + else { + page->flags.has_remembered_objects = TRUE; + MARK_IN_BITMAP(bits, obj); + return TRUE; + } +} + +/* wb, etc */ + +/* return FALSE if already remembered */ +static int +rgengc_remember(rb_objspace_t *objspace, VALUE obj) +{ + gc_report(6, objspace, "rgengc_remember: %s %s\n", rb_obj_info(obj), + RVALUE_REMEMBERED(objspace, obj) ? "was already remembered" : "is remembered now"); + + check_rvalue_consistency(objspace, obj); + + if (RGENGC_CHECK_MODE) { + if (RVALUE_WB_UNPROTECTED(objspace, obj)) rb_bug("rgengc_remember: %s is not wb protected.", rb_obj_info(obj)); + } + +#if RGENGC_PROFILE > 0 + if (!RVALUE_REMEMBERED(objspace, obj)) { + if (RVALUE_WB_UNPROTECTED(objspace, obj) == 0) { + objspace->profile.total_remembered_normal_object_count++; +#if RGENGC_PROFILE >= 2 + objspace->profile.remembered_normal_object_count_types[BUILTIN_TYPE(obj)]++; +#endif + } + } +#endif /* RGENGC_PROFILE > 0 */ + + return rgengc_remembersetbits_set(objspace, obj); +} + +#ifndef PROFILE_REMEMBERSET_MARK +#define PROFILE_REMEMBERSET_MARK 0 +#endif + +static inline void +rgengc_rememberset_mark_plane(rb_objspace_t *objspace, uintptr_t p, bits_t bitset) +{ + if (bitset) { + do { + if (bitset & 1) { + VALUE obj = (VALUE)p; + gc_report(2, objspace, "rgengc_rememberset_mark: mark %s\n", rb_obj_info(obj)); + GC_ASSERT(RVALUE_UNCOLLECTIBLE(objspace, obj)); + GC_ASSERT(RVALUE_OLD_P(objspace, obj) || RVALUE_WB_UNPROTECTED(objspace, obj)); + + gc_mark_children(objspace, obj); + } + p += BASE_SLOT_SIZE; + bitset >>= 1; + } while (bitset); + } +} + +static void +rgengc_rememberset_mark(rb_objspace_t *objspace, rb_heap_t *heap) +{ + size_t j; + struct heap_page *page = 0; +#if PROFILE_REMEMBERSET_MARK + int has_old = 0, has_shady = 0, has_both = 0, skip = 0; +#endif + gc_report(1, objspace, "rgengc_rememberset_mark: start\n"); + + ccan_list_for_each(&heap->pages, page, page_node) { + if (page->flags.has_remembered_objects | page->flags.has_uncollectible_wb_unprotected_objects) { + uintptr_t p = page->start; + bits_t bitset, bits[HEAP_PAGE_BITMAP_LIMIT]; + bits_t *remembered_bits = page->remembered_bits; + bits_t *uncollectible_bits = page->uncollectible_bits; + bits_t *wb_unprotected_bits = page->wb_unprotected_bits; +#if PROFILE_REMEMBERSET_MARK + if (page->flags.has_remembered_objects && page->flags.has_uncollectible_wb_unprotected_objects) has_both++; + else if (page->flags.has_remembered_objects) has_old++; + else if (page->flags.has_uncollectible_wb_unprotected_objects) has_shady++; +#endif + for (j=0; jflags.has_remembered_objects = FALSE; + + bitset = bits[0]; + bitset >>= NUM_IN_PAGE(p); + rgengc_rememberset_mark_plane(objspace, p, bitset); + p += (BITS_BITLENGTH - NUM_IN_PAGE(p)) * BASE_SLOT_SIZE; + + for (j=1; j < HEAP_PAGE_BITMAP_LIMIT; j++) { + bitset = bits[j]; + rgengc_rememberset_mark_plane(objspace, p, bitset); + p += BITS_BITLENGTH * BASE_SLOT_SIZE; + } + } +#if PROFILE_REMEMBERSET_MARK + else { + skip++; + } +#endif + } + +#if PROFILE_REMEMBERSET_MARK + fprintf(stderr, "%d\t%d\t%d\t%d\n", has_both, has_old, has_shady, skip); +#endif + gc_report(1, objspace, "rgengc_rememberset_mark: finished\n"); +} + +static void +rgengc_mark_and_rememberset_clear(rb_objspace_t *objspace, rb_heap_t *heap) +{ + struct heap_page *page = 0; + + ccan_list_for_each(&heap->pages, page, page_node) { + memset(&page->mark_bits[0], 0, HEAP_PAGE_BITMAP_SIZE); + memset(&page->uncollectible_bits[0], 0, HEAP_PAGE_BITMAP_SIZE); + memset(&page->marking_bits[0], 0, HEAP_PAGE_BITMAP_SIZE); + memset(&page->remembered_bits[0], 0, HEAP_PAGE_BITMAP_SIZE); + memset(&page->pinned_bits[0], 0, HEAP_PAGE_BITMAP_SIZE); + page->flags.has_uncollectible_wb_unprotected_objects = FALSE; + page->flags.has_remembered_objects = FALSE; + } +} + +/* RGENGC: APIs */ + +NOINLINE(static void gc_writebarrier_generational(VALUE a, VALUE b, rb_objspace_t *objspace)); + +static void +gc_writebarrier_generational(VALUE a, VALUE b, rb_objspace_t *objspace) +{ + if (RGENGC_CHECK_MODE) { + if (!RVALUE_OLD_P(objspace, a)) rb_bug("gc_writebarrier_generational: %s is not an old object.", rb_obj_info(a)); + if ( RVALUE_OLD_P(objspace, b)) rb_bug("gc_writebarrier_generational: %s is an old object.", rb_obj_info(b)); + if (is_incremental_marking(objspace)) rb_bug("gc_writebarrier_generational: called while incremental marking: %s -> %s", rb_obj_info(a), rb_obj_info(b)); + } + + /* mark `a' and remember (default behavior) */ + if (!RVALUE_REMEMBERED(objspace, a)) { + int lev = rb_gc_vm_lock_no_barrier(); + { + rgengc_remember(objspace, a); + } + rb_gc_vm_unlock_no_barrier(lev); + + gc_report(1, objspace, "gc_writebarrier_generational: %s (remembered) -> %s\n", rb_obj_info(a), rb_obj_info(b)); + } + + check_rvalue_consistency(objspace, a); + check_rvalue_consistency(objspace, b); +} + +static void +gc_mark_from(rb_objspace_t *objspace, VALUE obj, VALUE parent) +{ + VM_ASSERT(GET_OBJSPACE_OF_VALUE(obj) == objspace); + gc_mark_set_parent(objspace, parent); + rgengc_check_relation(objspace, obj); + if (gc_mark_set(objspace, obj) == FALSE) return; + gc_aging(obj); + gc_grey(objspace, obj); +} + +NOINLINE(static void gc_writebarrier_incremental(VALUE a, VALUE b, rb_objspace_t *objspace)); + +static void +gc_writebarrier_incremental(VALUE a, VALUE b, rb_objspace_t *objspace) +{ + gc_report(2, objspace, "gc_writebarrier_incremental: [LG] %p -> %s\n", (void *)a, rb_obj_info(b)); + + VM_ASSERT(GET_OBJSPACE_OF_VALUE(b) == objspace); + VM_ASSERT(is_incremental_marking(objspace)); + + if (RB_LIKELY(GET_OBJSPACE_OF_VALUE(a) == objspace)) { + if (RVALUE_BLACK_P(objspace, a)) { + if (RVALUE_WHITE_P(objspace, b)) { + if (!RVALUE_WB_UNPROTECTED(a)) { + gc_report(2, objspace, "gc_writebarrier_incremental: [IN] %p -> %s\n", (void *)a, rb_obj_info(b)); + gc_mark_from(objspace, b, a); + } + } + else if (RVALUE_OLD_P(objspace, a) && !RVALUE_OLD_P(objspace, b)) { + rgengc_remember(objspace, a); + } + + if (RB_UNLIKELY(objspace->flags.during_compacting)) { + MARK_IN_BITMAP(GET_HEAP_PINNED_BITS(b), b); + } + } + } + else { + if (RVALUE_WHITE_P(objspace, b)) { + if (gc_mark_set(objspace, b)) { + gc_aging(b); + gc_grey(objspace, b); + } + } + } +} + +void +rb_gc_writebarrier_safe_objspace(VALUE a, VALUE b, rb_objspace_t *objspace) +{ + VM_ASSERT(GET_OBJSPACE_OF_VALUE(b) == objspace); + + if (is_incremental_marking(objspace)) { + gc_writebarrier_incremental(a, b, objspace); + } + else { + gc_writebarrier_generational(a, b, objspace); + } +} + +static VALUE +limmune(VALUE os, VALUE obj) +{ + rb_gc_give_local_immunity_traversal(obj); + return Qnil; +} + +void +rb_gc_impl_writebarrier(void *objspace_ptr, VALUE a, VALUE b) +{ + rb_objspace_t *objspace = objspace_ptr; + + if (RGENGC_CHECK_MODE) { + if (SPECIAL_CONST_P(a)) rb_bug("rb_gc_writebarrier: a is special const: %"PRIxVALUE, a); + if (SPECIAL_CONST_P(b)) rb_bug("rb_gc_writebarrier: b is special const: %"PRIxVALUE, b); + } + + rb_objspace_t *current_objspace = &rb_objspace; + + //VM_ASSERT(!NEEDS_LOCAL_IMMUNE_CHILDREN(a) || RVALUE_LOCAL_IMMUNE(b)); + + if (ruby_single_main_objspace) { + rb_gc_writebarrier_safe_objspace(a, b, current_objspace); + } + else { + rb_gc_writebarrier_multi_objspace(a, b, current_objspace); + } +} + +void +rb_gc_impl_writebarrier_unprotect(void *objspace_ptr, VALUE obj) +{ + rb_objspace_t *objspace = GET_OBJSPACE_OF_VALUE(obj); + + if (RVALUE_WB_UNPROTECTED(objspace, obj)) { + return; + } + else { + gc_report(2, objspace, "rb_gc_writebarrier_unprotect: %s %s\n", rb_obj_info(obj), + RVALUE_REMEMBERED(objspace, obj) ? " (already remembered)" : ""); + + HEAP_LOCK_ENTER(objspace); + { + if (RVALUE_OLD_P(objspace, obj)) { + gc_report(1, objspace, "rb_gc_writebarrier_unprotect: %s\n", rb_obj_info(obj)); + RVALUE_DEMOTE(objspace, obj); + gc_mark_set(objspace, obj); + gc_remember_unprotected(objspace, obj); + +#if RGENGC_PROFILE + objspace->profile.total_shade_operation_count++; +#if RGENGC_PROFILE >= 2 + objspace->profile.shade_operation_count_types[BUILTIN_TYPE(obj)]++; +#endif /* RGENGC_PROFILE >= 2 */ +#endif /* RGENGC_PROFILE */ + } + else { + RVALUE_AGE_RESET(obj); + } + + RB_DEBUG_COUNTER_INC(obj_wb_unprotect); + MARK_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(obj), obj); + } + HEAP_LOCK_LEAVE(objspace); + } +} + +void +rb_gc_impl_copy_attributes(void *objspace_ptr, VALUE dest, VALUE obj) +{ + rb_objspace_t *objspace = objspace_ptr; + + if (RVALUE_WB_UNPROTECTED(objspace, obj)) { + rb_gc_writebarrier_unprotect(dest); + } + if (RVALUE_LOCAL_IMMUNE(obj)) { + MARK_IN_BITMAP(GET_HEAP_LOCAL_IMMUNE_BITS(obj), obj); + } + rb_gc_copy_finalizer(dest, obj); +} + +// TODO: rearchitect this function to work for a generic GC +size_t +rb_gc_impl_obj_flags(void *objspace_ptr, VALUE obj, ID* flags, size_t max) +{ + rb_objspace_t *objspace = objspace_ptr; + size_t n = 0; + static ID ID_marked; + static ID ID_wb_protected, ID_old, ID_marking, ID_uncollectible, ID_pinned, ID_local_immune; + + if (!ID_marked) { +#define I(s) ID_##s = rb_intern(#s); + I(marked); + I(wb_protected); + I(old); + I(marking); + I(uncollectible); + I(pinned); + I(local_immune); +#undef I + } + + if (RVALUE_WB_UNPROTECTED(objspace, obj) == 0 && n < max) flags[n++] = ID_wb_protected; + if (RVALUE_OLD_P(objspace, obj) && n < max) flags[n++] = ID_old; + if (RVALUE_UNCOLLECTIBLE(objspace, obj) && n < max) flags[n++] = ID_uncollectible; + if (RVALUE_MARKING(objspace, obj) && n < max) flags[n++] = ID_marking; + if (RVALUE_MARKED(objspace, obj) && n < max) flags[n++] = ID_marked; + if (RVALUE_PINNED(objspace, obj) && n < max) flags[n++] = ID_pinned; + if (RVALUE_LOCAL_IMMUNE(objspace, obj) && n < max) flags[n++] = ID_local_immune; + return n; +} + +void * +rb_gc_impl_ractor_cache_alloc(void *objspace_ptr) +{ + rb_objspace_t *objspace = objspace_ptr; + + objspace->live_ractor_cache_count++; + + return calloc1(sizeof(rb_ractor_newobj_cache_t)); +} + +void +rb_gc_impl_ractor_cache_free(void *objspace_ptr, void *cache) +{ + rb_objspace_t *objspace = objspace_ptr; + + objspace->live_ractor_cache_count--; + + gc_ractor_newobj_cache_clear(cache, NULL); + free(cache); +} + +static void +heap_ready_to_gc(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap) +{ + if (!heap->free_pages) { + if (!heap_increment(objspace, size_pool, heap)) { + size_pool_allocatable_pages_set(objspace, size_pool, 1); + heap_increment(objspace, size_pool, heap); + } + } +} + +static int +ready_to_gc(rb_objspace_t *objspace) +{ + if (dont_gc_val() || during_gc || ruby_disable_gc) { + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + heap_ready_to_gc(objspace, size_pool, SIZE_POOL_EDEN_HEAP(size_pool)); + } + return FALSE; + } + else { + return TRUE; + } +} + +static void +gc_reset_malloc_info(rb_objspace_t *objspace, bool full_mark) +{ + gc_prof_set_malloc_info(objspace); + { + size_t inc = RUBY_ATOMIC_SIZE_EXCHANGE(malloc_increase, 0); + size_t old_limit = malloc_limit; + + if (inc > malloc_limit) { + malloc_limit = (size_t)(inc * gc_params.malloc_limit_growth_factor); + if (malloc_limit > gc_params.malloc_limit_max) { + malloc_limit = gc_params.malloc_limit_max; + } + } + else { + malloc_limit = (size_t)(malloc_limit * 0.98); /* magic number */ + if (malloc_limit < gc_params.malloc_limit_min) { + malloc_limit = gc_params.malloc_limit_min; + } + } + + if (0) { + if (old_limit != malloc_limit) { + fprintf(stderr, "[%"PRIuSIZE"] malloc_limit: %"PRIuSIZE" -> %"PRIuSIZE"\n", + rb_gc_count(), old_limit, malloc_limit); + } + else { + fprintf(stderr, "[%"PRIuSIZE"] malloc_limit: not changed (%"PRIuSIZE")\n", + rb_gc_count(), malloc_limit); + } + } + } + + /* reset oldmalloc info */ +#if RGENGC_ESTIMATE_OLDMALLOC + if (!full_mark) { + if (objspace->rgengc.oldmalloc_increase > objspace->rgengc.oldmalloc_increase_limit) { + 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); + + if (objspace->rgengc.oldmalloc_increase_limit > gc_params.oldmalloc_limit_max) { + objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_max; + } + } + + if (0) fprintf(stderr, "%"PRIdSIZE"\t%d\t%"PRIuSIZE"\t%"PRIuSIZE"\t%"PRIdSIZE"\n", + rb_gc_count(), + gc_needs_major_flags, + objspace->rgengc.oldmalloc_increase, + objspace->rgengc.oldmalloc_increase_limit, + gc_params.oldmalloc_limit_max); + } + else { + /* major GC */ + objspace->rgengc.oldmalloc_increase = 0; + + if ((objspace->profile.latest_gc_info & GPR_FLAG_MAJOR_BY_OLDMALLOC) == 0) { + objspace->rgengc.oldmalloc_increase_limit = + (size_t)(objspace->rgengc.oldmalloc_increase_limit / ((gc_params.oldmalloc_limit_growth_factor - 1)/10 + 1)); + if (objspace->rgengc.oldmalloc_increase_limit < gc_params.oldmalloc_limit_min) { + objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_min; + } + } + } +#endif +} + +static int +garbage_collect(rb_objspace_t *objspace, unsigned int reason, bool need_finalize_deferred) +{ + if (global_gc_needed()) { + reason |= GPR_FLAG_GLOBAL; + } + VM_ASSERT(!(reason & GPR_FLAG_COMPACT) || !(reason & GPR_FLAG_GLOBAL)); + + if (reason & GPR_FLAG_GLOBAL) { + return garbage_collect_global(objspace, reason, need_finalize_deferred); + } + else { + return garbage_collect_local(objspace, reason, need_finalize_deferred); + } +} + +static int +garbage_collect_local(rb_objspace_t *objspace, unsigned int reason, bool need_finalize_deferred) +{ + int ret; + + if (rb_redirecting_allocation()) { + return TRUE; + } + + LOCAL_GC_BEGIN(objspace); + { +#if GC_PROFILE_MORE_DETAIL + objspace->profile.prepare_time = getrusage_time(); +#endif + + gc_rest(objspace); + +#if GC_PROFILE_MORE_DETAIL + objspace->profile.prepare_time = getrusage_time() - objspace->profile.prepare_time; +#endif + + ret = gc_start(objspace, reason); + + if (need_finalize_deferred) { + gc_finalize_deferred(objspace); + } + } + LOCAL_GC_END(objspace); + + return ret; +} + +void +gc_rest_global(rb_objspace_t *objspace) +{ + global_gc_for_each_objspace(GET_VM(), objspace, gc_rest); +} + +static int +garbage_collect_global(rb_objspace_t *objspace, unsigned int reason, bool need_finalize_deferred) +{ + int ret; + rb_vm_t *vm = GET_VM(); + + bool global_gc_possible = true; + GLOBAL_GC_BEGIN(vm, objspace); + { +#if GC_PROFILE_MORE_DETAIL + objspace->profile.prepare_time = getrusage_time(); +#endif + + gc_rest_global(objspace); + +#if GC_PROFILE_MORE_DETAIL + objspace->profile.prepare_time = getrusage_time() - objspace->profile.prepare_time; +#endif + + struct objspace_local_data *local_data = NULL; + ccan_list_for_each(&vm->objspace_set, local_data, objspace_node) { + rb_objspace_t *os = local_data->objspace; + if (os == objspace) continue; + + global_gc_possible = global_gc_possible && (os->heap_pages.allocated_pages && ((reason & GPR_FLAG_METHOD) || ready_to_gc(os))); + + GC_ASSERT(gc_mode(os) == gc_mode_none); + GC_ASSERT(!is_lazy_sweeping(os)); + GC_ASSERT(!is_incremental_marking(os)); + } + + if (global_gc_possible) { + ret = gc_start(objspace, reason); + + if (need_finalize_deferred) { + global_gc_for_each_objspace(GET_VM(), objspace, gc_finalize_deferred); + } + } + } + GLOBAL_GC_END(vm, objspace); + if (!global_gc_possible) { + reason &= ~GPR_FLAG_GLOBAL; + return garbage_collect_local(objspace, reason, need_finalize_deferred); + } + + return ret; +} + +static void +gc_set_flags_start(rb_objspace_t *objspace, unsigned int reason, unsigned int *do_full_mark) +{ + /* reason may be clobbered, later, so keep set immediate_sweep here */ + objspace->flags.immediate_sweep = !!(reason & GPR_FLAG_IMMEDIATE_SWEEP); + + objspace->flags.during_global_gc = !!(reason & GPR_FLAG_GLOBAL); +} + +static void +gc_set_flags_finish(rb_objspace_t *objspace, unsigned int reason, unsigned int *do_full_mark, unsigned int *immediate_mark) +{ + if (ruby_gc_stressful) { + int flag = FIXNUM_P(ruby_gc_stress_mode) ? FIX2INT(ruby_gc_stress_mode) : 0; + + if ((flag & (1 << gc_stress_no_major)) == 0) { + *do_full_mark = TRUE; + } + + objspace->flags.immediate_sweep = !(flag & (1<flags.dont_incremental || + *immediate_mark || + ruby_gc_stressful) { + objspace->flags.during_incremental_marking = FALSE; + } + else { + objspace->flags.during_incremental_marking = *do_full_mark; + } + + /* Explicitly enable compaction (GC.compact) */ + if (do_full_mark && ruby_enable_autocompact) { + objspace->flags.during_compacting = TRUE; +#if RGENGC_CHECK_MODE + objspace->rcompactor.compare_func = ruby_autocompact_compare_func; +#endif + } + else { + objspace->flags.during_compacting = !!(reason & GPR_FLAG_COMPACT); + } + + if (!GC_ENABLE_LAZY_SWEEP || objspace->flags.dont_incremental) { + objspace->flags.immediate_sweep = TRUE; + } + + if (objspace->flags.immediate_sweep) reason |= GPR_FLAG_IMMEDIATE_SWEEP; + + gc_report(1, objspace, "gc_start(reason: %x) => %u, %d, %d\n", + reason, + *do_full_mark, !is_incremental_marking(objspace), objspace->flags.immediate_sweep); + +#if USE_DEBUG_COUNTER + RB_DEBUG_COUNTER_INC(gc_count); + + if (reason & GPR_FLAG_MAJOR_MASK) { + (void)RB_DEBUG_COUNTER_INC_IF(gc_major_nofree, reason & GPR_FLAG_MAJOR_BY_NOFREE); + (void)RB_DEBUG_COUNTER_INC_IF(gc_major_oldgen, reason & GPR_FLAG_MAJOR_BY_OLDGEN); + (void)RB_DEBUG_COUNTER_INC_IF(gc_major_shady, reason & GPR_FLAG_MAJOR_BY_SHADY); + (void)RB_DEBUG_COUNTER_INC_IF(gc_major_force, reason & GPR_FLAG_MAJOR_BY_FORCE); + (void)RB_DEBUG_COUNTER_INC_IF(gc_major_absorb, reason & GPR_FLAG_MAJOR_BY_ABSORB); +#if RGENGC_ESTIMATE_OLDMALLOC + (void)RB_DEBUG_COUNTER_INC_IF(gc_major_oldmalloc, reason & GPR_FLAG_MAJOR_BY_OLDMALLOC); +#endif + } + else { + (void)RB_DEBUG_COUNTER_INC_IF(gc_minor_newobj, reason & GPR_FLAG_NEWOBJ); + (void)RB_DEBUG_COUNTER_INC_IF(gc_minor_malloc, reason & GPR_FLAG_MALLOC); + (void)RB_DEBUG_COUNTER_INC_IF(gc_minor_method, reason & GPR_FLAG_METHOD); + (void)RB_DEBUG_COUNTER_INC_IF(gc_minor_capi, reason & GPR_FLAG_CAPI); + (void)RB_DEBUG_COUNTER_INC_IF(gc_minor_stress, reason & GPR_FLAG_STRESS); + } +#endif + + objspace->profile.count++; + objspace->profile.latest_gc_info = reason; + objspace->profile.total_allocated_objects_at_gc_start = total_allocated_objects(objspace); + objspace->profile.heap_used_at_gc_start = heap_allocated_pages; + objspace->profile.weak_references_count = 0; + objspace->profile.retained_weak_references_count = 0; + gc_prof_setup_new_record(objspace, reason); + gc_reset_malloc_info(objspace, *do_full_mark); + + rb_gc_event_hook(0, RUBY_INTERNAL_EVENT_GC_START); + GC_ASSERT(during_gc); +} + +static int +gc_start(rb_objspace_t *objspace, unsigned int reason) +{ + VM_ASSERT(objspace->local_data.local_gc_level > 0 || objspace->local_data.running_global_gc); + VM_ASSERT(!GET_RACTOR()->teardown_cleanup_done); + + rb_vm_t *vm = GET_VM(); + + unsigned int global_gc = !!(reason & GPR_FLAG_GLOBAL); + if (global_gc) { + reason |= GPR_FLAG_FULL_MARK; + reason |= GPR_FLAG_IMMEDIATE_MARK; + reason |= GPR_FLAG_IMMEDIATE_SWEEP; + } + unsigned int do_full_mark = !!(reason & GPR_FLAG_FULL_MARK); + unsigned int immediate_mark = reason & GPR_FLAG_IMMEDIATE_MARK; + gc_set_flags_start(objspace, reason, &do_full_mark); + + if (!heap_allocated_pages) return TRUE; /* heap is not ready */ + if (!(reason & GPR_FLAG_METHOD) && !ready_to_gc(objspace)) return TRUE; /* GC is not allowed */ + + GC_ASSERT(gc_mode(objspace) == gc_mode_none); + GC_ASSERT(!is_lazy_sweeping(objspace)); + GC_ASSERT(!is_incremental_marking(objspace)); + + if (global_gc) { + struct objspace_local_data *local_data = NULL; + ccan_list_for_each(&vm->objspace_set, local_data, objspace_node) { + rb_objspace_t *os = local_data->objspace; + if (os == objspace) continue; + gc_set_flags_start(os, reason, &do_full_mark); + } + } + + rb_global_space_t *global_space = &rb_global_space; + + unsigned int lock_lev; + if (global_gc) { + VM_ASSERT(objspace->local_data.running_global_gc); + ASSERT_vm_locking(); + ASSERT_ractor_safe_gc_state(); + gc_global_enter(objspace, gc_enter_event_start, &lock_lev); +#if RGENGC_CHECK_MODE >= 2 + gc_verify_internal_consistency(objspace); +#endif + + struct objspace_local_data *local_data = NULL; + ccan_list_for_each(&vm->objspace_set, local_data, objspace_node) { + rb_objspace_t *os = local_data->objspace; + if (os->local_data.ractor && os->local_data.ractor->gc_chain_node) { + os->local_data.ractor->gc_chain_node->value = 0; + } + gc_set_flags_finish(os, reason, &do_full_mark, &immediate_mark); + } + rb_native_mutex_lock(&global_space->rglobalgc.shared_tracking_lock); + global_space->rglobalgc.need_global_gc = false; + rb_native_mutex_unlock(&global_space->rglobalgc.shared_tracking_lock); + gc_marks_global(objspace); + gc_global_exit(objspace, gc_enter_event_start, &lock_lev); + } + else { + VM_ASSERT(objspace->local_data.local_gc_level > 0); + gc_enter(objspace, gc_enter_event_start); +#if RGENGC_CHECK_MODE >= 2 + gc_verify_internal_consistency(objspace); +#endif + + gc_set_flags_finish(objspace, reason, &do_full_mark, &immediate_mark); + gc_prof_timer_start(objspace); + { + if (gc_marks(objspace, do_full_mark)) { + gc_sweep(objspace); + } + } + gc_prof_timer_stop(objspace); + gc_exit(objspace, gc_enter_event_start); + } + + return TRUE; +} + +static void +gc_rest(rb_objspace_t *objspace) +{ + if (is_incremental_marking(objspace) || is_lazy_sweeping(objspace)) { + unsigned int lock_lev; + LOCAL_GC_BEGIN(objspace); + { + gc_enter(objspace, gc_enter_event_rest); + + if (RGENGC_CHECK_MODE >= 2) gc_verify_internal_consistency(objspace); + + if (is_incremental_marking(objspace)) { + gc_marking_enter(objspace); + gc_marks_rest(objspace); + gc_marking_exit(objspace); + + gc_sweep(objspace); + } + + if (is_lazy_sweeping(objspace)) { + gc_sweeping_enter(objspace); + gc_sweep_rest(objspace); + gc_sweeping_exit(objspace); + } + + gc_exit(objspace, gc_enter_event_rest); + } + LOCAL_GC_END(objspace); + } +} + +struct objspace_and_reason { + rb_objspace_t *objspace; + unsigned int reason; +}; + +static void +gc_current_status_fill(rb_objspace_t *objspace, char *buff) +{ + int i = 0; + if (is_marking(objspace)) { + buff[i++] = 'M'; + if (is_full_marking(objspace)) buff[i++] = 'F'; + if (is_incremental_marking(objspace)) buff[i++] = 'I'; + } + else if (is_sweeping(objspace)) { + buff[i++] = 'S'; + if (is_lazy_sweeping(objspace)) buff[i++] = 'L'; + } + else { + buff[i++] = 'N'; + } + buff[i] = '\0'; +} + +static const char * +gc_current_status(rb_objspace_t *objspace) +{ + static char buff[0x10]; + gc_current_status_fill(objspace, buff); + return buff; +} + +#if PRINT_ENTER_EXIT_TICK + +static tick_t last_exit_tick; +static tick_t enter_tick; +static int enter_count = 0; +static char last_gc_status[0x10]; + +static inline void +gc_record(rb_objspace_t *objspace, int direction, const char *event) +{ + if (direction == 0) { /* enter */ + enter_count++; + enter_tick = tick(); + gc_current_status_fill(objspace, last_gc_status); + } + else { /* exit */ + tick_t exit_tick = tick(); + char current_gc_status[0x10]; + gc_current_status_fill(objspace, current_gc_status); +#if 1 + /* [last mutator time] [gc time] [event] */ + fprintf(stderr, "%"PRItick"\t%"PRItick"\t%s\t[%s->%s|%c]\n", + enter_tick - last_exit_tick, + exit_tick - enter_tick, + event, + last_gc_status, current_gc_status, + (objspace->profile.latest_gc_info & GPR_FLAG_MAJOR_MASK) ? '+' : '-'); + last_exit_tick = exit_tick; +#else + /* [enter_tick] [gc time] [event] */ + fprintf(stderr, "%"PRItick"\t%"PRItick"\t%s\t[%s->%s|%c]\n", + enter_tick, + exit_tick - enter_tick, + event, + last_gc_status, current_gc_status, + (objspace->profile.latest_gc_info & GPR_FLAG_MAJOR_MASK) ? '+' : '-'); +#endif + } +} +#else /* PRINT_ENTER_EXIT_TICK */ +static inline void +gc_record(rb_objspace_t *objspace, int direction, const char *event) +{ + /* null */ +} +#endif /* PRINT_ENTER_EXIT_TICK */ + +static const char * +gc_enter_event_cstr(enum gc_enter_event event) +{ + switch (event) { + case gc_enter_event_start: return "start"; + case gc_enter_event_continue: return "continue"; + case gc_enter_event_rest: return "rest"; + case gc_enter_event_finalizer: return "finalizer"; + } + return NULL; +} + +static void +gc_enter_count(enum gc_enter_event event) +{ + switch (event) { + case gc_enter_event_start: RB_DEBUG_COUNTER_INC(gc_enter_start); break; + case gc_enter_event_continue: RB_DEBUG_COUNTER_INC(gc_enter_continue); break; + case gc_enter_event_rest: RB_DEBUG_COUNTER_INC(gc_enter_rest); break; + case gc_enter_event_finalizer: RB_DEBUG_COUNTER_INC(gc_enter_finalizer); break; + } +} + +static bool current_process_time(struct timespec *ts); + +static void +gc_clock_start(struct timespec *ts) +{ + if (!current_process_time(ts)) { + ts->tv_sec = 0; + ts->tv_nsec = 0; + } +} + +static uint64_t +gc_clock_end(struct timespec *ts) +{ + struct timespec end_time; + + if ((ts->tv_sec > 0 || ts->tv_nsec > 0) && + current_process_time(&end_time) && + end_time.tv_sec >= ts->tv_sec) { + return (uint64_t)(end_time.tv_sec - ts->tv_sec) * (1000 * 1000 * 1000) + + (end_time.tv_nsec - ts->tv_nsec); + } + + return 0; +} + +static inline void +gc_enter(rb_objspace_t *objspace, enum gc_enter_event event) +{ + gc_enter_count(event); + if (RB_UNLIKELY(during_gc != 0)) rb_bug("during_gc != 0"); + if (RGENGC_CHECK_MODE >= 3) gc_verify_internal_consistency(objspace); + + VM_ASSERT(!rb_redirecting_allocation()); + + during_gc = TRUE; + RUBY_DEBUG_LOG("%s (%s)",gc_enter_event_cstr(event), gc_current_status(objspace)); + gc_report(1, objspace, "gc_enter: %s [%s]\n", gc_enter_event_cstr(event), gc_current_status(objspace)); + gc_record(objspace, 0, gc_enter_event_cstr(event)); + + rb_gc_event_hook(0, RUBY_INTERNAL_EVENT_GC_ENTER); +} + +static inline void +gc_global_enter(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_lev) +{ + ASSERT_vm_locking(); + gc_enter(objspace, event); + + struct objspace_local_data *local_data = NULL; + ccan_list_for_each(&GET_VM()->objspace_set, local_data, objspace_node) { + rb_objspace_t *os = local_data->objspace; + if (os == objspace) + continue; + gc_enter(os, event); + } +} + +static inline void +gc_exit(rb_objspace_t *objspace, enum gc_enter_event event) +{ + GC_ASSERT(during_gc != 0); + + rb_gc_event_hook(0, RUBY_INTERNAL_EVENT_GC_EXIT); + + gc_record(objspace, 1, gc_enter_event_cstr(event)); + RUBY_DEBUG_LOG("%s (%s)", gc_enter_event_cstr(event), gc_current_status(objspace)); + gc_report(1, objspace, "gc_exit: %s [%s]\n", gc_enter_event_cstr(event), gc_current_status(objspace)); + during_gc = FALSE; + objspace->flags.during_global_gc = false; + + +#if RGENGC_CHECK_MODE >= 2 + if (event == gc_enter_event_sweep_continue && gc_mode(objspace) == gc_mode_none) { + GC_ASSERT(!during_gc); + // sweep finished + gc_verify_internal_consistency(objspace); + } +#endif +} + +static inline void +gc_global_exit(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_lev) +{ + struct objspace_local_data *local_data = NULL; + ccan_list_for_each(&GET_VM()->objspace_set, local_data, objspace_node) { + rb_objspace_t *os = local_data->objspace; + if (os == objspace) + continue; + gc_exit(os, event); + } + + gc_exit(objspace, event); +} + +#ifndef MEASURE_GC +#define MEASURE_GC (objspace->flags.measure_gc) +#endif + +static void +gc_marking_enter(rb_objspace_t *objspace) +{ + GC_ASSERT(during_gc != 0); + + if (MEASURE_GC) { + gc_clock_start(&objspace->profile.marking_start_time); + } +} + +static void +gc_marking_exit(rb_objspace_t *objspace) +{ + GC_ASSERT(during_gc != 0); + + if (MEASURE_GC) { + objspace->profile.marking_time_ns += gc_clock_end(&objspace->profile.marking_start_time); + } +} + +static void +gc_sweeping_enter(rb_objspace_t *objspace) +{ + GC_ASSERT(during_gc != 0); + + if (MEASURE_GC) { + gc_clock_start(&objspace->profile.sweeping_start_time); + } +} + +static void +gc_sweeping_exit(rb_objspace_t *objspace) +{ + GC_ASSERT(during_gc != 0); + + if (MEASURE_GC) { + objspace->profile.sweeping_time_ns += gc_clock_end(&objspace->profile.sweeping_start_time); + } +} + +static void * +gc_with_gvl(void *ptr) +{ + struct objspace_and_reason *oar = (struct objspace_and_reason *)ptr; + return (void *)(VALUE)garbage_collect(oar->objspace, oar->reason, false); +} + +int ruby_thread_has_gvl_p(void); + +static int +garbage_collect_with_gvl(rb_objspace_t *objspace, unsigned int reason) +{ + if (dont_gc_val()) return TRUE; + if (ruby_thread_has_gvl_p()) { + return garbage_collect(objspace, reason, false); + } + else { + if (ruby_native_thread_p()) { + struct objspace_and_reason oar; + oar.objspace = objspace; + oar.reason = reason; + return (int)(VALUE)rb_thread_call_with_gvl(gc_with_gvl, (void *)&oar); + } + else { + /* no ruby thread */ + fprintf(stderr, "[FATAL] failed to allocate memory\n"); + exit(EXIT_FAILURE); + } + } +} + +static int +gc_set_candidate_object_i(void *vstart, void *vend, size_t stride, void *data) +{ + rb_objspace_t *objspace = (rb_objspace_t *)data; + + VALUE v = (VALUE)vstart; + for (; v != (VALUE)vend; v += stride) { + asan_unpoisoning_object(v) { + switch (BUILTIN_TYPE(v)) { + case T_NONE: + case T_ZOMBIE: + break; + case T_STRING: + // precompute the string coderange. This both save time for when it will be + // eventually needed, and avoid mutating heap pages after a potential fork. + rb_enc_str_coderange(v); + // fall through + default: + if (!RVALUE_OLD_P(objspace, v) && !RVALUE_WB_UNPROTECTED(objspace, v)) { + RVALUE_AGE_SET_CANDIDATE(objspace, v); + } + } + } + } + + return 0; +} + +static VALUE +rb_gc_impl_start(void *objspace_ptr, bool full_mark, bool immediate_mark, bool immediate_sweep, bool compact) +{ + if (global) { + if (!full_mark) rb_raise(rb_eArgError, "`full_mark' must be true if `global' is true"); + if (!immediate_mark) rb_raise(rb_eArgError, "`immediate_mark' must be true if `global' is true"); + if (!immediate_sweep) rb_raise(rb_eArgError, "`immediate_sweep' must be true if `global' is true"); + if (compact) rb_raise(rb_eArgError, "global compaction is not yet implemented"); + } + + rb_objspace_t *objspace = objspace_ptr; + unsigned int reason = (GPR_FLAG_FULL_MARK | + GPR_FLAG_IMMEDIATE_MARK | + GPR_FLAG_IMMEDIATE_SWEEP | + GPR_FLAG_GLOBAL | + GPR_FLAG_METHOD); + + int full_marking_p = gc_config_full_mark_val; + gc_config_full_mark_set(TRUE); + + /* For now, compact implies full mark / sweep, so ignore other flags */ + if (compact) { + GC_ASSERT(GC_COMPACTION_SUPPORTED); + + reason |= GPR_FLAG_COMPACT; + } + else { + if (!full_mark) reason &= ~GPR_FLAG_FULL_MARK; + if (!immediate_mark) reason &= ~GPR_FLAG_IMMEDIATE_MARK; + if (!immediate_sweep) reason &= ~GPR_FLAG_IMMEDIATE_SWEEP; + } + if (!global) reason &= ~GPR_FLAG_GLOBAL; + + if(!GET_VM()->gc_deactivated) { + if (reason & GPR_FLAG_COMPACT) { //TODO: Implement global compaction + reason &= ~GPR_FLAG_COMPACT; + while (global_gc_needed()) { + garbage_collect(objspace, reason, true); + } + reason |= GPR_FLAG_COMPACT; + } + garbage_collect(objspace, reason, true); + } + + gc_config_full_mark_set(full_marking_p); +} + +static void +free_empty_pages(void *objspace_ptr) +{ + rb_objspace_t *objspace = objspace_ptr; + + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + /* Move all empty pages to the tomb heap for freeing. */ + rb_size_pool_t *size_pool = &size_pools[i]; + rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); + rb_heap_t *tomb_heap = SIZE_POOL_TOMB_HEAP(size_pool); + + size_t freed_pages = 0; + + struct heap_page **next_page_ptr = &heap->free_pages; + struct heap_page *page = heap->free_pages; + while (page) { + /* All finalizers should have been ran in gc_start_internal, so there + * should be no objects that require finalization. */ + GC_ASSERT(page->final_slots == 0); + + struct heap_page *next_page = page->free_next; + + if (page->free_slots == page->total_slots) { + heap_unlink_page(objspace, heap, page); + heap_add_page(objspace, size_pool, tomb_heap, page); + freed_pages++; + } + else { + *next_page_ptr = page; + next_page_ptr = &page->free_next; + } + + page = next_page; + } + + *next_page_ptr = NULL; + + size_pool_allocatable_pages_set(objspace, size_pool, size_pool->allocatable_pages + freed_pages); + } + + heap_pages_free_unused_pages(objspace); +} + +void +rb_gc_impl_prepare_heap(void *objspace_ptr) +{ + rb_gc_impl_each_objects(objspace_ptr, gc_set_candidate_object_i, objspace_ptr); + rb_gc_impl_start(objspace_ptr, true, true, true, false, true); + free_empty_pages(objspace_ptr); + +#if defined(HAVE_MALLOC_TRIM) && !defined(RUBY_ALTERNATIVE_MALLOC_HEADER) + malloc_trim(0); +#endif +} + +static int +gc_is_moveable_obj(rb_objspace_t *objspace, VALUE obj) +{ + GC_ASSERT(!SPECIAL_CONST_P(obj)); + + if (using_local_limits(objspace) && (FL_TEST_RAW(obj, FL_SHAREABLE) || RVALUE_LOCAL_IMMUNE(obj))) { + return FALSE; + } + + switch (BUILTIN_TYPE(obj)) { + case T_NONE: + case T_MOVED: + case T_ZOMBIE: + return FALSE; + case T_SYMBOL: + // TODO: restore original behavior + // if (RSYMBOL(obj)->id & ~ID_SCOPE_MASK) { + // return FALSE; + // } + return false; + /* fall through */ + case T_STRING: + case T_OBJECT: + case T_FLOAT: + case T_IMEMO: + case T_ARRAY: + case T_BIGNUM: + case T_ICLASS: + case T_MODULE: + case T_REGEXP: + case T_DATA: + case T_MATCH: + case T_STRUCT: + case T_HASH: + case T_FILE: + case T_COMPLEX: + case T_RATIONAL: + case T_NODE: + case T_CLASS: + if (FL_TEST(obj, FL_FINALIZE)) { + /* The finalizer table is a numtable. It looks up objects by address. + * We can't mark the keys in the finalizer table because that would + * prevent the objects from being collected. This check prevents + * objects that are keys in the finalizer table from being moved + * without directly pinning them. */ + GC_ASSERT(st_is_member(finalizer_table, obj)); + + return FALSE; + } + GC_ASSERT(RVALUE_MARKED(objspace, obj)); + GC_ASSERT(!RVALUE_PINNED(objspace, obj)); + + return TRUE; + + default: + rb_bug("gc_is_moveable_obj: unreachable (%d)", (int)BUILTIN_TYPE(obj)); + break; + } + + return FALSE; +} + +void rb_mv_generic_ivar(VALUE src, VALUE dst); + +static void +update_obj_id_mapping(rb_objspace_t *objspace, RVALUE *dest, RVALUE *src) +{ + rb_native_mutex_lock(&objspace->local_data.obj_id_lock); + if (FL_TEST((VALUE)src, FL_SEEN_OBJ_ID)) { + /* If the source object's object_id has been seen, we need to update + * the object to object id mapping. */ + st_data_t srcid = (st_data_t)src, id; + + gc_report(4, objspace, "Moving object with seen id: %p -> %p\n", (void *)src, (void *)dest); + /* Resizing the st table could cause a malloc */ + DURING_GC_COULD_MALLOC_REGION_START(); + { + if (!st_delete(objspace->local_data.obj_to_id_tbl, &srcid, &id)) { + rb_bug("gc_move: object ID seen, but not in mapping table: %s", obj_info((VALUE)src)); + } + + st_insert(objspace->local_data.obj_to_id_tbl, (st_data_t)dest, id); + } + DURING_GC_COULD_MALLOC_REGION_END(); + } + else { + GC_ASSERT(!st_lookup(objspace->local_data.obj_to_id_tbl, (st_data_t)src, NULL)); + } + rb_native_mutex_unlock(&objspace->local_data.obj_id_lock); +} + +static VALUE +gc_move(rb_objspace_t *objspace, VALUE src, VALUE dest, size_t src_slot_size, size_t slot_size) +{ + int marked; + int wb_unprotected; + int uncollectible; + int age; + + gc_report(4, objspace, "Moving object: %p -> %p\n", (void *)src, (void *)dest); + + GC_ASSERT(BUILTIN_TYPE(src) != T_NONE); + GC_ASSERT(!MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(dest), dest)); + + GC_ASSERT(!RVALUE_MARKING(objspace, src)); + + VM_ASSERT(!RVALUE_LOCAL_IMMUNE(objspace, (VALUE)src)); + + /* Save off bits for current object. */ + marked = RVALUE_MARKED(objspace, src); + wb_unprotected = RVALUE_WB_UNPROTECTED(objspace, src); + uncollectible = RVALUE_UNCOLLECTIBLE(objspace, src); + bool remembered = RVALUE_REMEMBERED(objspace, src); + age = RVALUE_AGE_GET(src); + + /* Clear bits for eventual T_MOVED */ + CLEAR_IN_BITMAP(GET_HEAP_MARK_BITS(src), src); + CLEAR_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(src), src); + CLEAR_IN_BITMAP(GET_HEAP_UNCOLLECTIBLE_BITS(src), src); + CLEAR_IN_BITMAP(GET_HEAP_PAGE(src)->remembered_bits, src); + + if (FL_TEST(src, FL_EXIVAR)) { + /* Resizing the st table could cause a malloc */ + DURING_GC_COULD_MALLOC_REGION_START(); + { + rb_mv_generic_ivar(src, dest); + } + DURING_GC_COULD_MALLOC_REGION_END(); + } + + update_obj_id_mapping(objspace, dest, src); + +#if VM_CHECK_MODE > 0 + rb_native_mutex_lock(&objspace->local_data.shared_reference_tbl_lock); + VM_ASSERT(!st_lookup(objspace->local_data.shared_reference_tbl, (st_data_t)src, NULL)); + rb_native_mutex_unlock(&objspace->local_data.shared_reference_tbl_lock); +#endif + + /* Move the object */ + memcpy((void *)dest, (void *)src, MIN(src_slot_size, slot_size)); + + if (RVALUE_OVERHEAD > 0) { + void *dest_overhead = (void *)(((uintptr_t)dest) + slot_size - RVALUE_OVERHEAD); + void *src_overhead = (void *)(((uintptr_t)src) + src_slot_size - RVALUE_OVERHEAD); + + memcpy(dest_overhead, src_overhead, RVALUE_OVERHEAD); + } + + memset((void *)src, 0, src_slot_size); + RVALUE_AGE_RESET(src); + + /* Set bits for object in new location */ + if (remembered) { + MARK_IN_BITMAP(GET_HEAP_PAGE(dest)->remembered_bits, dest); + } + else { + CLEAR_IN_BITMAP(GET_HEAP_PAGE(dest)->remembered_bits, dest); + } + + if (marked) { + MARK_IN_BITMAP(GET_HEAP_MARK_BITS(dest), dest); + } + else { + CLEAR_IN_BITMAP(GET_HEAP_MARK_BITS(dest), dest); + } + + if (wb_unprotected) { + MARK_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(dest), dest); + } + else { + CLEAR_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(dest), dest); + } + + if (uncollectible) { + MARK_IN_BITMAP(GET_HEAP_UNCOLLECTIBLE_BITS(dest), dest); + } + else { + CLEAR_IN_BITMAP(GET_HEAP_UNCOLLECTIBLE_BITS(dest), dest); + } + + RVALUE_AGE_SET(dest, age); + /* Assign forwarding address */ + RMOVED(src)->flags = T_MOVED; + RMOVED(src)->dummy = Qundef; + RMOVED(src)->destination = dest; + GC_ASSERT(BUILTIN_TYPE(dest) != T_NONE); + + return src; +} + +#if GC_CAN_COMPILE_COMPACTION +static int +compare_pinned_slots(const void *left, const void *right, void *dummy) +{ + struct heap_page *left_page; + struct heap_page *right_page; + + left_page = *(struct heap_page * const *)left; + right_page = *(struct heap_page * const *)right; + + return left_page->pinned_slots - right_page->pinned_slots; +} + +static int +compare_free_slots(const void *left, const void *right, void *dummy) +{ + struct heap_page *left_page; + struct heap_page *right_page; + + left_page = *(struct heap_page * const *)left; + right_page = *(struct heap_page * const *)right; + + return left_page->free_slots - right_page->free_slots; +} + +static void +gc_sort_heap_by_compare_func(rb_objspace_t *objspace, gc_compact_compare_func compare_func) +{ + for (int j = 0; j < SIZE_POOL_COUNT; j++) { + rb_size_pool_t *size_pool = &size_pools[j]; + + size_t total_pages = SIZE_POOL_EDEN_HEAP(size_pool)->total_pages; + size_t size = rb_size_mul_or_raise(total_pages, sizeof(struct heap_page *), rb_eRuntimeError); + struct heap_page *page = 0, **page_list = malloc(size); + size_t i = 0; + + SIZE_POOL_EDEN_HEAP(size_pool)->free_pages = NULL; + ccan_list_for_each(&SIZE_POOL_EDEN_HEAP(size_pool)->pages, page, page_node) { + page_list[i++] = page; + GC_ASSERT(page); + } + + GC_ASSERT((size_t)i == total_pages); + + /* Sort the heap so "filled pages" are first. `heap_add_page` adds to the + * head of the list, so empty pages will end up at the start of the heap */ + ruby_qsort(page_list, total_pages, sizeof(struct heap_page *), compare_func, NULL); + + /* Reset the eden heap */ + ccan_list_head_init(&SIZE_POOL_EDEN_HEAP(size_pool)->pages); + + for (i = 0; i < total_pages; i++) { + ccan_list_add(&SIZE_POOL_EDEN_HEAP(size_pool)->pages, &page_list[i]->page_node); + if (page_list[i]->free_slots != 0) { + heap_add_freepage(SIZE_POOL_EDEN_HEAP(size_pool), page_list[i]); + } + } + + free(page_list); + } +} +#endif + +bool +rb_gc_impl_object_moved_p(void *objspace_ptr, VALUE obj) +{ + return gc_object_moved_p(objspace_ptr, obj); +} + +static int +gc_ref_update(void *vstart, void *vend, size_t stride, rb_objspace_t *objspace, struct heap_page *page) +{ + VALUE v = (VALUE)vstart; + asan_unlock_freelist(page); + asan_lock_freelist(page); + page->flags.has_uncollectible_wb_unprotected_objects = FALSE; + page->flags.has_remembered_objects = FALSE; + + /* For each object on the page */ + for (; v != (VALUE)vend; v += stride) { + void *poisoned = asan_unpoison_object_temporary(v); + + switch (BUILTIN_TYPE(v)) { + case T_NONE: + case T_MOVED: + case T_ZOMBIE: + break; + default: + if (RVALUE_WB_UNPROTECTED(objspace, v)) { + page->flags.has_uncollectible_wb_unprotected_objects = TRUE; + } + if (RVALUE_REMEMBERED(objspace, v)) { + page->flags.has_remembered_objects = TRUE; + } + if (page->flags.before_sweep) { + if (RVALUE_MARKED(objspace, v)) { + rb_gc_update_object_references(objspace, v); + } + } + else { + rb_gc_update_object_references(objspace, v); + } + } + + if (poisoned) { + asan_poison_object(v); + } + } + + return 0; +} + +static int +hash_replace_ref_value(st_data_t *key, st_data_t *value, st_data_t argp, int existing) +{ + void *objspace = (void *)argp; + + if (rb_gc_impl_object_moved_p(objspace, (VALUE)*value)) { + *value = rb_gc_impl_location(objspace, (VALUE)*value); + } + + return ST_CONTINUE; +} + +static int +hash_foreach_replace_value(st_data_t key, st_data_t value, st_data_t argp, int error) +{ + void *objspace; + + objspace = (void *)argp; + + if (rb_gc_impl_object_moved_p(objspace, (VALUE)value)) { + return ST_REPLACE; + } + return ST_CONTINUE; +} + +static void +gc_ref_update_table_values_only(void *objspace, st_table *tbl) +{ + if (!tbl || tbl->num_entries == 0) return; + + if (st_foreach_with_replace(tbl, hash_foreach_replace_value, hash_replace_ref_value, (st_data_t)objspace)) { + rb_raise(rb_eRuntimeError, "hash modified during iteration"); + } +} + +static int +hash_foreach_replace(st_data_t key, st_data_t value, st_data_t argp, int error) +{ + void *objspace; + + objspace = (void *)argp; + + if (rb_gc_impl_object_moved_p(objspace, (VALUE)key)) { + return ST_REPLACE; + } + + if (rb_gc_impl_object_moved_p(objspace, (VALUE)value)) { + return ST_REPLACE; + } + return ST_CONTINUE; +} + +static int +hash_replace_ref(st_data_t *key, st_data_t *value, st_data_t argp, int existing) +{ + void *objspace = (void *)argp; + + if (rb_gc_impl_object_moved_p(objspace, (VALUE)*key)) { + *key = rb_gc_impl_location(objspace, (VALUE)*key); + } + + if (rb_gc_impl_object_moved_p(objspace, (VALUE)*value)) { + *value = rb_gc_impl_location(objspace, (VALUE)*value); + } + + return ST_CONTINUE; +} + +static void +gc_update_table_refs(void *objspace, st_table *tbl) +{ + if (!tbl || tbl->num_entries == 0) return; + + if (st_foreach_with_replace(tbl, hash_foreach_replace, hash_replace_ref, (st_data_t)objspace)) { + rb_raise(rb_eRuntimeError, "hash modified during iteration"); + } +} + +static void +gc_update_references(rb_objspace_t *objspace) +{ + objspace->flags.during_reference_updating = true; + + struct heap_page *page = NULL; + + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + bool should_set_mark_bits = TRUE; + rb_size_pool_t *size_pool = &size_pools[i]; + rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); + + ccan_list_for_each(&heap->pages, page, page_node) { + uintptr_t start = (uintptr_t)page->start; + uintptr_t end = start + (page->total_slots * size_pool->slot_size); + + gc_ref_update((void *)start, (void *)end, size_pool->slot_size, objspace, page); + if (page == heap->sweeping_page) { + should_set_mark_bits = FALSE; + } + if (should_set_mark_bits) { + gc_setup_mark_bits(page); + } + } + } + gc_ref_update_table_values_only(objspace, objspace->obj_to_id_tbl); + gc_update_table_refs(objspace, objspace->id_to_obj_tbl); + gc_update_table_refs(objspace, finalizer_table); + + rb_gc_update_vm_references((void *)objspace); + + objspace->flags.during_reference_updating = false; +} + +#if GC_CAN_COMPILE_COMPACTION +static void +root_obj_check_moved_i(const char *category, VALUE obj, void *data) +{ + rb_objspace_t *objspace = data; + + if (gc_object_moved_p(objspace, obj)) { + rb_bug("ROOT %s points to MOVED: %p -> %s", category, (void *)obj, rb_obj_info(rb_gc_impl_location(objspace, obj))); + } +} + +static void +reachable_object_check_moved_i(VALUE ref, void *data) +{ + VALUE parent = (VALUE)data; + if (gc_object_moved_p(rb_gc_get_objspace(), ref)) { + rb_bug("Object %s points to MOVED: %p -> %s", rb_obj_info(parent), (void *)ref, rb_obj_info(rb_gc_impl_location(rb_gc_get_objspace(), ref))); + } +} + +static int +heap_check_moved_i(void *vstart, void *vend, size_t stride, void *data) +{ + rb_objspace_t *objspace = data; + + VALUE v = (VALUE)vstart; + for (; v != (VALUE)vend; v += stride) { + if (gc_object_moved_p(objspace, v)) { + /* Moved object still on the heap, something may have a reference. */ + } + else { + void *poisoned = asan_unpoison_object_temporary(v); + + switch (BUILTIN_TYPE(v)) { + case T_NONE: + case T_ZOMBIE: + break; + default: + if (!rb_gc_impl_garbage_object_p(objspace, v)) { + rb_objspace_reachable_objects_from(v, reachable_object_check_moved_i, (void *)v); + } + } + + if (poisoned) { + GC_ASSERT(BUILTIN_TYPE(v) == T_NONE); + asan_poison_object(v); + } + } + } + + return 0; +} +#endif + +struct desired_compaction_pages_i_data { + rb_objspace_t *objspace; + size_t required_slots[SIZE_POOL_COUNT]; +}; + +static int +desired_compaction_pages_i(struct heap_page *page, void *data) +{ + struct desired_compaction_pages_i_data *tdata = data; + rb_objspace_t *objspace = tdata->objspace; + VALUE vstart = (VALUE)page->start; + VALUE vend = vstart + (VALUE)(page->total_slots * page->size_pool->slot_size); + + + for (VALUE v = vstart; v != vend; v += page->size_pool->slot_size) { + /* skip T_NONEs; they won't be moved */ + void *poisoned = asan_unpoison_object_temporary(v); + if (BUILTIN_TYPE(v) == T_NONE) { + if (poisoned) { + asan_poison_object(v); + } + continue; + } + + rb_size_pool_t *dest_pool = gc_compact_destination_pool(objspace, page->size_pool, v); + size_t dest_pool_idx = dest_pool - size_pools; + tdata->required_slots[dest_pool_idx]++; + } + + return 0; +} + +bool +rb_gc_impl_during_gc_p(void *objspace_ptr) +{ + rb_objspace_t *objspace = objspace_ptr; + + return during_gc; +} + +int +rb_during_local_gc(void) +{ + unless_objspace(objspace) { return FALSE; } + return during_gc && !objspace->flags.during_global_gc; +} + +int +rb_during_global_gc(void) +{ + unless_objspace(objspace) { return FALSE; } + return during_gc && objspace->flags.during_global_gc; +} + +#if RGENGC_PROFILE >= 2 + +static const char* +type_name(int type, VALUE obj) +{ + switch ((enum ruby_value_type)type) { + case RUBY_T_NONE: return "T_NONE"; + case RUBY_T_OBJECT: return "T_OBJECT"; + case RUBY_T_CLASS: return "T_CLASS"; + case RUBY_T_MODULE: return "T_MODULE"; + case RUBY_T_FLOAT: return "T_FLOAT"; + case RUBY_T_STRING: return "T_STRING"; + case RUBY_T_REGEXP: return "T_REGEXP"; + case RUBY_T_ARRAY: return "T_ARRAY"; + case RUBY_T_HASH: return "T_HASH"; + case RUBY_T_STRUCT: return "T_STRUCT"; + case RUBY_T_BIGNUM: return "T_BIGNUM"; + case RUBY_T_FILE: return "T_FILE"; + case RUBY_T_DATA: return "T_DATA"; + case RUBY_T_MATCH: return "T_MATCH"; + case RUBY_T_COMPLEX: return "T_COMPLEX"; + case RUBY_T_RATIONAL: return "T_RATIONAL"; + case RUBY_T_NIL: return "T_NIL"; + case RUBY_T_TRUE: return "T_TRUE"; + case RUBY_T_FALSE: return "T_FALSE"; + case RUBY_T_SYMBOL: return "T_SYMBOL"; + case RUBY_T_FIXNUM: return "T_FIXNUM"; + case RUBY_T_UNDEF: return "T_UNDEF"; + case RUBY_T_IMEMO: return "T_IMEMO"; + case RUBY_T_NODE: return "T_NODE"; + case RUBY_T_ICLASS: return "T_ICLASS"; + case RUBY_T_ZOMBIE: return "T_ZOMBIE"; + case RUBY_T_MOVED: return "T_MOVED"; + default: return "unknown"; + } +} + +static void +gc_count_add_each_types(VALUE hash, const char *name, const size_t *types) +{ + VALUE result = rb_hash_new_with_size(T_MASK); + int i; + for (i=0; iprofile.count; +} + +static VALUE +gc_info_decode(rb_objspace_t *objspace, const VALUE hash_or_key, const unsigned int orig_flags) +{ + static VALUE sym_major_by = Qnil, sym_gc_by, sym_immediate_sweep, sym_have_finalizer, sym_state, sym_need_major_by; + static VALUE sym_nofree, sym_oldgen, sym_shady, sym_force, sym_absorb, sym_stress; +#if RGENGC_ESTIMATE_OLDMALLOC + static VALUE sym_oldmalloc; +#endif + static VALUE sym_newobj, sym_malloc, sym_method, sym_capi; + static VALUE sym_none, sym_marking, sym_sweeping; + static VALUE sym_weak_references_count, sym_retained_weak_references_count; + VALUE hash = Qnil, key = Qnil; + VALUE major_by, need_major_by; + unsigned int flags = orig_flags ? orig_flags : objspace->profile.latest_gc_info; + + if (SYMBOL_P(hash_or_key)) { + key = hash_or_key; + } + else if (RB_TYPE_P(hash_or_key, T_HASH)) { + hash = hash_or_key; + } + else { + rb_raise(rb_eTypeError, "non-hash or symbol given"); + } + + if (NIL_P(sym_major_by)) { +#define S(s) sym_##s = ID2SYM(rb_intern_const(#s)) + S(major_by); + S(gc_by); + S(immediate_sweep); + S(have_finalizer); + S(state); + S(need_major_by); + + S(stress); + S(nofree); + S(oldgen); + S(shady); + S(force); +#if RGENGC_ESTIMATE_OLDMALLOC + S(oldmalloc); +#endif + S(newobj); + S(malloc); + S(method); + S(capi); + + S(none); + S(marking); + S(sweeping); + + S(weak_references_count); + S(retained_weak_references_count); +#undef S + } + +#define SET(name, attr) \ + if (key == sym_##name) \ + return (attr); \ + else if (hash != Qnil) \ + rb_hash_aset(hash, sym_##name, (attr)); + + major_by = + (flags & GPR_FLAG_MAJOR_BY_NOFREE) ? sym_nofree : + (flags & GPR_FLAG_MAJOR_BY_OLDGEN) ? sym_oldgen : + (flags & GPR_FLAG_MAJOR_BY_SHADY) ? sym_shady : + (flags & GPR_FLAG_MAJOR_BY_FORCE) ? sym_force : + (flags & GPR_FLAG_MAJOR_BY_ABSORB) ? sym_absorb : +#if RGENGC_ESTIMATE_OLDMALLOC + (flags & GPR_FLAG_MAJOR_BY_OLDMALLOC) ? sym_oldmalloc : +#endif + Qnil; + SET(major_by, major_by); + + if (orig_flags == 0) { /* set need_major_by only if flags not set explicitly */ + 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 : + (need_major_flags & GPR_FLAG_MAJOR_BY_SHADY) ? sym_shady : + (need_major_flags & GPR_FLAG_MAJOR_BY_FORCE) ? sym_force : + (need_major_flags & GPR_FLAG_MAJOR_BY_ABSORB) ? sym_absorb : +#if RGENGC_ESTIMATE_OLDMALLOC + (need_major_flags & GPR_FLAG_MAJOR_BY_OLDMALLOC) ? sym_oldmalloc : +#endif + Qnil; + SET(need_major_by, need_major_by); + } + + SET(gc_by, + (flags & GPR_FLAG_NEWOBJ) ? sym_newobj : + (flags & GPR_FLAG_MALLOC) ? sym_malloc : + (flags & GPR_FLAG_METHOD) ? sym_method : + (flags & GPR_FLAG_CAPI) ? sym_capi : + (flags & GPR_FLAG_STRESS) ? sym_stress : + Qnil + ); + + SET(have_finalizer, (flags & GPR_FLAG_HAVE_FINALIZE) ? Qtrue : Qfalse); + SET(immediate_sweep, (flags & GPR_FLAG_IMMEDIATE_SWEEP) ? Qtrue : Qfalse); + + if (orig_flags == 0) { + SET(state, gc_mode(objspace) == gc_mode_none ? sym_none : + gc_mode(objspace) == gc_mode_marking ? sym_marking : sym_sweeping); + } + + SET(weak_references_count, LONG2FIX(objspace->profile.weak_references_count)); + SET(retained_weak_references_count, LONG2FIX(objspace->profile.retained_weak_references_count)); +#undef SET + + if (!NIL_P(key)) {/* matched key should return above */ + rb_raise(rb_eArgError, "unknown key: %"PRIsVALUE, rb_sym2str(key)); + } + + return hash; +} + +VALUE +rb_gc_impl_latest_gc_info(void *objspace_ptr, VALUE key) +{ + rb_objspace_t *objspace = objspace_ptr; + + return gc_info_decode(objspace, key, 0); +} + + +enum gc_stat_sym { + gc_stat_sym_count, + gc_stat_sym_time, + gc_stat_sym_marking_time, + gc_stat_sym_sweeping_time, + gc_stat_sym_heap_allocated_pages, + gc_stat_sym_heap_sorted_length, + gc_stat_sym_heap_allocatable_pages, + gc_stat_sym_heap_available_slots, + gc_stat_sym_heap_live_slots, + gc_stat_sym_heap_free_slots, + gc_stat_sym_heap_final_slots, + gc_stat_sym_heap_marked_slots, + gc_stat_sym_heap_eden_pages, + gc_stat_sym_heap_tomb_pages, + gc_stat_sym_total_allocated_pages, + gc_stat_sym_total_freed_pages, + gc_stat_sym_total_allocated_objects, + gc_stat_sym_total_freed_objects, + gc_stat_sym_malloc_increase_bytes, + gc_stat_sym_malloc_increase_bytes_limit, + gc_stat_sym_minor_gc_count, + gc_stat_sym_major_gc_count, + gc_stat_sym_compact_count, + gc_stat_sym_read_barrier_faults, + gc_stat_sym_total_moved_objects, + gc_stat_sym_remembered_wb_unprotected_objects, + gc_stat_sym_remembered_wb_unprotected_objects_limit, + gc_stat_sym_old_objects, + gc_stat_sym_old_objects_limit, + gc_stat_sym_shared_objects, + gc_stat_sym_shared_objects_limit, +#if RGENGC_ESTIMATE_OLDMALLOC + gc_stat_sym_oldmalloc_increase_bytes, + gc_stat_sym_oldmalloc_increase_bytes_limit, +#endif + gc_stat_sym_weak_references_count, +#if RGENGC_PROFILE + gc_stat_sym_total_generated_normal_object_count, + gc_stat_sym_total_generated_shady_object_count, + gc_stat_sym_total_shade_operation_count, + gc_stat_sym_total_promoted_count, + gc_stat_sym_total_remembered_normal_object_count, + gc_stat_sym_total_remembered_shady_object_count, +#endif + gc_stat_sym_last +}; + +static VALUE gc_stat_symbols[gc_stat_sym_last]; + +static void +setup_gc_stat_symbols(void) +{ + if (gc_stat_symbols[0] == 0) { +#define S(s) gc_stat_symbols[gc_stat_sym_##s] = ID2SYM(rb_intern_const(#s)) + S(count); + S(time); + S(marking_time), + S(sweeping_time), + S(heap_allocated_pages); + S(heap_sorted_length); + S(heap_allocatable_pages); + S(heap_available_slots); + S(heap_live_slots); + S(heap_free_slots); + S(heap_final_slots); + S(heap_marked_slots); + S(heap_eden_pages); + S(heap_tomb_pages); + S(total_allocated_pages); + S(total_freed_pages); + S(total_allocated_objects); + S(total_freed_objects); + S(malloc_increase_bytes); + S(malloc_increase_bytes_limit); + S(minor_gc_count); + S(major_gc_count); + S(compact_count); + S(read_barrier_faults); + S(total_moved_objects); + S(remembered_wb_unprotected_objects); + S(remembered_wb_unprotected_objects_limit); + S(old_objects); + S(old_objects_limit); + if (rb_multi_ractor_p()) { + S(shared_objects); + S(shared_objects_limit); + } +#if RGENGC_ESTIMATE_OLDMALLOC + S(oldmalloc_increase_bytes); + S(oldmalloc_increase_bytes_limit); +#endif + S(weak_references_count); +#if RGENGC_PROFILE + S(total_generated_normal_object_count); + S(total_generated_shady_object_count); + S(total_shade_operation_count); + S(total_promoted_count); + S(total_remembered_normal_object_count); + S(total_remembered_shady_object_count); +#endif /* RGENGC_PROFILE */ +#undef S + } +} + +static uint64_t +ns_to_ms(uint64_t ns) +{ + return ns / (1000 * 1000); +} + +size_t +rb_gc_impl_stat(void *objspace_ptr, VALUE hash_or_sym) +{ + rb_objspace_t *objspace = objspace_ptr; + VALUE hash = Qnil, key = Qnil; + + setup_gc_stat_symbols(); + + if (RB_TYPE_P(hash_or_sym, T_HASH)) { + hash = hash_or_sym; + } + else if (SYMBOL_P(hash_or_sym)) { + key = hash_or_sym; + } + else { + rb_raise(rb_eTypeError, "non-hash or symbol given"); + } + + rb_global_space_t *global_space = &rb_global_space; + rb_native_mutex_lock(&global_space->rglobalgc.shared_tracking_lock); + size_t shared_objects_total_value = global_space->rglobalgc.shared_objects_total; + size_t shared_objects_limit_value = global_space->rglobalgc.shared_objects_limit; + rb_native_mutex_unlock(&global_space->rglobalgc.shared_tracking_lock); + +#define SET(name, attr) \ + if (key == gc_stat_symbols[gc_stat_sym_##name]) \ + return attr; \ + else if (hash != Qnil) \ + rb_hash_aset(hash, gc_stat_symbols[gc_stat_sym_##name], SIZET2NUM(attr)); + + SET(count, objspace->profile.count); + SET(time, (size_t)ns_to_ms(objspace->profile.marking_time_ns + objspace->profile.sweeping_time_ns)); // TODO: UINT64T2NUM + SET(marking_time, (size_t)ns_to_ms(objspace->profile.marking_time_ns)); + SET(sweeping_time, (size_t)ns_to_ms(objspace->profile.sweeping_time_ns)); + + /* implementation dependent counters */ + SET(heap_allocated_pages, heap_allocated_pages); + SET(heap_sorted_length, heap_pages_sorted_length); + SET(heap_allocatable_pages, heap_allocatable_pages(objspace)); + SET(heap_available_slots, objspace_available_slots(objspace)); + SET(heap_live_slots, objspace_live_slots(objspace)); + SET(heap_free_slots, objspace_free_slots(objspace)); + SET(heap_final_slots, heap_pages_final_slots); + SET(heap_marked_slots, objspace->marked_slots); + SET(heap_eden_pages, heap_eden_total_pages(objspace)); + SET(heap_tomb_pages, heap_tomb_total_pages(objspace)); + SET(total_allocated_pages, total_allocated_pages(objspace)); + SET(total_freed_pages, total_freed_pages(objspace)); + SET(total_allocated_objects, total_allocated_objects(objspace)); + SET(total_freed_objects, total_freed_objects(objspace)); + SET(malloc_increase_bytes, malloc_increase); + SET(malloc_increase_bytes_limit, malloc_limit); + SET(minor_gc_count, objspace->profile.minor_gc_count); + SET(major_gc_count, objspace->profile.major_gc_count); + SET(compact_count, objspace->profile.compact_count); + SET(read_barrier_faults, objspace->profile.read_barrier_faults); + SET(total_moved_objects, objspace->rcompactor.total_moved); + SET(remembered_wb_unprotected_objects, objspace->rgengc.uncollectible_wb_unprotected_objects); + SET(remembered_wb_unprotected_objects_limit, objspace->rgengc.uncollectible_wb_unprotected_objects_limit); + SET(old_objects, objspace->rgengc.old_objects); + SET(old_objects_limit, objspace->rgengc.old_objects_limit); + if (rb_multi_ractor_p()) { + SET(shared_objects, shared_objects_total_value); + SET(shared_objects_limit, shared_objects_limit_value); + } +#if RGENGC_ESTIMATE_OLDMALLOC + SET(oldmalloc_increase_bytes, objspace->rgengc.oldmalloc_increase); + SET(oldmalloc_increase_bytes_limit, objspace->rgengc.oldmalloc_increase_limit); +#endif + +#if RGENGC_PROFILE + SET(total_generated_normal_object_count, objspace->profile.total_generated_normal_object_count); + SET(total_generated_shady_object_count, objspace->profile.total_generated_shady_object_count); + SET(total_shade_operation_count, objspace->profile.total_shade_operation_count); + SET(total_promoted_count, objspace->profile.total_promoted_count); + SET(total_remembered_normal_object_count, objspace->profile.total_remembered_normal_object_count); + SET(total_remembered_shady_object_count, objspace->profile.total_remembered_shady_object_count); +#endif /* RGENGC_PROFILE */ +#undef SET + + if (!NIL_P(key)) { /* matched key should return above */ + rb_raise(rb_eArgError, "unknown key: %"PRIsVALUE, rb_sym2str(key)); + } + +#if defined(RGENGC_PROFILE) && RGENGC_PROFILE >= 2 + if (hash != Qnil) { + gc_count_add_each_types(hash, "generated_normal_object_count_types", objspace->profile.generated_normal_object_count_types); + gc_count_add_each_types(hash, "generated_shady_object_count_types", objspace->profile.generated_shady_object_count_types); + gc_count_add_each_types(hash, "shade_operation_count_types", objspace->profile.shade_operation_count_types); + gc_count_add_each_types(hash, "promoted_types", objspace->profile.promoted_types); + gc_count_add_each_types(hash, "remembered_normal_object_count_types", objspace->profile.remembered_normal_object_count_types); + gc_count_add_each_types(hash, "remembered_shady_object_count_types", objspace->profile.remembered_shady_object_count_types); + } +#endif + + return 0; +} + +enum gc_stat_heap_sym { + gc_stat_heap_sym_slot_size, + gc_stat_heap_sym_heap_allocatable_pages, + gc_stat_heap_sym_heap_eden_pages, + gc_stat_heap_sym_heap_eden_slots, + gc_stat_heap_sym_heap_tomb_pages, + gc_stat_heap_sym_heap_tomb_slots, + gc_stat_heap_sym_total_allocated_pages, + gc_stat_heap_sym_total_freed_pages, + gc_stat_heap_sym_force_major_gc_count, + gc_stat_heap_sym_force_incremental_marking_finish_count, + gc_stat_heap_sym_total_allocated_objects, + gc_stat_heap_sym_total_freed_objects, + gc_stat_heap_sym_last +}; + +static VALUE gc_stat_heap_symbols[gc_stat_heap_sym_last]; + +static void +setup_gc_stat_heap_symbols(void) +{ + if (gc_stat_heap_symbols[0] == 0) { +#define S(s) gc_stat_heap_symbols[gc_stat_heap_sym_##s] = ID2SYM(rb_intern_const(#s)) + S(slot_size); + S(heap_allocatable_pages); + S(heap_eden_pages); + S(heap_eden_slots); + S(heap_tomb_pages); + S(heap_tomb_slots); + S(total_allocated_pages); + S(total_freed_pages); + S(force_major_gc_count); + S(force_incremental_marking_finish_count); + S(total_allocated_objects); + S(total_freed_objects); +#undef S + } +} + +static size_t +stat_one_heap(rb_size_pool_t *size_pool, VALUE hash, VALUE key) +{ + if (!during_gc) rb_borrowing_sync_lock(objspace->local_data.ractor); + size_pool->total_allocated_objects += size_pool->newly_created_by_borrowing_count; + size_pool->newly_created_by_borrowing_count = 0; + if (!during_gc) rb_borrowing_sync_unlock(objspace->local_data.ractor); + +#define SET(name, attr) \ + if (key == gc_stat_heap_symbols[gc_stat_heap_sym_##name]) \ + return attr; \ + else if (hash != Qnil) \ + rb_hash_aset(hash, gc_stat_heap_symbols[gc_stat_heap_sym_##name], SIZET2NUM(attr)); + + SET(slot_size, size_pool->slot_size); + SET(heap_allocatable_pages, size_pool->allocatable_pages); + SET(heap_eden_pages, SIZE_POOL_EDEN_HEAP(size_pool)->total_pages); + SET(heap_eden_slots, SIZE_POOL_EDEN_HEAP(size_pool)->total_slots); + SET(heap_tomb_pages, SIZE_POOL_TOMB_HEAP(size_pool)->total_pages); + SET(heap_tomb_slots, SIZE_POOL_TOMB_HEAP(size_pool)->total_slots); + SET(total_allocated_pages, size_pool->total_allocated_pages); + SET(total_freed_pages, size_pool->total_freed_pages); + SET(force_major_gc_count, size_pool->force_major_gc_count); + SET(force_incremental_marking_finish_count, size_pool->force_incremental_marking_finish_count); + SET(total_allocated_objects, size_pool->total_allocated_objects); + SET(total_freed_objects, size_pool->total_freed_objects); +#undef SET + + if (!NIL_P(key)) { /* matched key should return above */ + rb_raise(rb_eArgError, "unknown key: %"PRIsVALUE, rb_sym2str(key)); + } + + return 0; +} + +size_t +rb_gc_impl_stat_heap(void *objspace_ptr, VALUE heap_name, VALUE hash_or_sym) +{ + rb_objspace_t *objspace = objspace_ptr; + + setup_gc_stat_heap_symbols(); + + if (NIL_P(heap_name)) { + if (!RB_TYPE_P(hash_or_sym, T_HASH)) { + rb_raise(rb_eTypeError, "non-hash given"); + } + + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + VALUE hash = rb_hash_aref(hash_or_sym, INT2FIX(i)); + if (NIL_P(hash)) { + hash = rb_hash_new(); + rb_hash_aset(hash_or_sym, INT2FIX(i), hash); + } + + stat_one_heap(&size_pools[i], hash, Qnil); + } + } + else if (FIXNUM_P(heap_name)) { + int size_pool_idx = FIX2INT(heap_name); + + if (size_pool_idx < 0 || size_pool_idx >= SIZE_POOL_COUNT) { + rb_raise(rb_eArgError, "size pool index out of range"); + } + + if (SYMBOL_P(hash_or_sym)) { + return stat_one_heap(&size_pools[size_pool_idx], Qnil, hash_or_sym); + } + else if (RB_TYPE_P(hash_or_sym, T_HASH)) { + return stat_one_heap(&size_pools[size_pool_idx], hash_or_sym, Qnil); + } + else { + rb_raise(rb_eTypeError, "non-hash or symbol given"); + } + } + else { + rb_raise(rb_eTypeError, "heap_name must be nil or an Integer"); + } + + return 0; +} + +/* I could include internal.h for this, but doing so undefines some Array macros + * necessary for initialising objects, and I don't want to include all the array + * headers to get them back + * TODO: Investigate why RARRAY_AREF gets undefined in internal.h + */ +#ifndef RBOOL +#define RBOOL(v) (v ? Qtrue : Qfalse) +#endif + +VALUE +rb_gc_impl_config_get(void *objspace_ptr) +{ +#define sym(name) ID2SYM(rb_intern_const(name)) + rb_objspace_t *objspace = objspace_ptr; + VALUE hash = rb_hash_new(); + + rb_hash_aset(hash, sym("rgengc_allow_full_mark"), RBOOL(gc_config_full_mark_val)); + + return hash; +} + +static int +gc_config_set_key(st_data_t key, st_data_t value, st_data_t data) +{ + rb_objspace_t *objspace = (rb_objspace_t *)data; + if (rb_sym2id(key) == rb_intern("rgengc_allow_full_mark")) { + gc_rest(objspace); + gc_config_full_mark_set(RBOOL(value)); + } + return ST_CONTINUE; +} + +VALUE +rb_gc_impl_config_set(void *objspace_ptr, VALUE hash) +{ + rb_objspace_t *objspace = objspace_ptr; + + if(!RB_TYPE_P(hash, T_HASH)) { + rb_raise(rb_eArgError, "expected keyword arguments"); + } + + rb_hash_stlike_foreach(hash, gc_config_set_key, (st_data_t)objspace); + return rb_gc_impl_config_get(objspace_ptr); +} + +VALUE +rb_gc_impl_stress_get(void *objspace_ptr) +{ + rb_objspace_t *objspace = objspace_ptr; + return ruby_gc_stress_mode; +} + +void +rb_gc_impl_stress_set(void *objspace_ptr, VALUE flag) +{ + rb_objspace_t *objspace = objspace_ptr; + + objspace->flags.gc_stressful = RTEST(flag); + objspace->gc_stress_mode = flag; +} + +static int +get_envparam_size(const char *name, size_t *default_value, size_t lower_bound) +{ + const char *ptr = getenv(name); + ssize_t val; + + if (ptr != NULL && *ptr) { + size_t unit = 0; + char *end; +#if SIZEOF_SIZE_T == SIZEOF_LONG_LONG + val = strtoll(ptr, &end, 0); +#else + val = strtol(ptr, &end, 0); +#endif + switch (*end) { + case 'k': case 'K': + unit = 1024; + ++end; + break; + case 'm': case 'M': + unit = 1024*1024; + ++end; + break; + case 'g': case 'G': + unit = 1024*1024*1024; + ++end; + break; + } + while (*end && isspace((unsigned char)*end)) end++; + if (*end) { + if (RTEST(ruby_verbose)) fprintf(stderr, "invalid string for %s: %s\n", name, ptr); + return 0; + } + if (unit > 0) { + if (val < -(ssize_t)(SIZE_MAX / 2 / unit) || (ssize_t)(SIZE_MAX / 2 / unit) < val) { + if (RTEST(ruby_verbose)) fprintf(stderr, "%s=%s is ignored because it overflows\n", name, ptr); + return 0; + } + val *= unit; + } + if (val > 0 && (size_t)val > lower_bound) { + if (RTEST(ruby_verbose)) { + fprintf(stderr, "%s=%"PRIdSIZE" (default value: %"PRIuSIZE")\n", name, val, *default_value); + } + *default_value = (size_t)val; + return 1; + } + else { + if (RTEST(ruby_verbose)) { + fprintf(stderr, "%s=%"PRIdSIZE" (default value: %"PRIuSIZE") is ignored because it must be greater than %"PRIuSIZE".\n", + name, val, *default_value, lower_bound); + } + return 0; + } + } + return 0; +} + +static int +get_envparam_double(const char *name, double *default_value, double lower_bound, double upper_bound, int accept_zero) +{ + const char *ptr = getenv(name); + double val; + + if (ptr != NULL && *ptr) { + char *end; + val = strtod(ptr, &end); + if (!*ptr || *end) { + if (RTEST(ruby_verbose)) fprintf(stderr, "invalid string for %s: %s\n", name, ptr); + return 0; + } + + if (accept_zero && val == 0.0) { + goto accept; + } + else if (val <= lower_bound) { + if (RTEST(ruby_verbose)) { + fprintf(stderr, "%s=%f (default value: %f) is ignored because it must be greater than %f.\n", + name, val, *default_value, lower_bound); + } + } + else if (upper_bound != 0.0 && /* ignore upper_bound if it is 0.0 */ + val > upper_bound) { + if (RTEST(ruby_verbose)) { + fprintf(stderr, "%s=%f (default value: %f) is ignored because it must be lower than %f.\n", + name, val, *default_value, upper_bound); + } + } + else { + goto accept; + } + } + return 0; + + accept: + if (RTEST(ruby_verbose)) fprintf(stderr, "%s=%f (default value: %f)\n", name, val, *default_value); + *default_value = val; + return 1; +} + +static void +gc_set_initial_pages(rb_objspace_t *objspace) +{ + gc_rest(objspace); + + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + char env_key[sizeof("RUBY_GC_HEAP_" "_INIT_SLOTS") + DECIMAL_SIZE_OF_BITS(sizeof(int) * CHAR_BIT)]; + snprintf(env_key, sizeof(env_key), "RUBY_GC_HEAP_%d_INIT_SLOTS", i); + + size_t size_pool_init_slots = gc_params.size_pool_init_slots[i]; + if (get_envparam_size(env_key, &size_pool_init_slots, 0)) { + gc_params.size_pool_init_slots[i] = size_pool_init_slots; + } + + if (size_pool_init_slots > size_pool->eden_heap.total_slots) { + size_t slots = size_pool_init_slots - size_pool->eden_heap.total_slots; + size_pool->allocatable_pages = slots_to_pages_for_size_pool(objspace, size_pool, slots); + } + else { + /* We already have more slots than size_pool_init_slots allows, so + * prevent creating more pages. */ + size_pool->allocatable_pages = 0; + } + } + heap_pages_expand_sorted(objspace); +} + +/* + * GC tuning environment variables + * + * * RUBY_GC_HEAP_FREE_SLOTS + * - Prepare at least this amount of slots after GC. + * - Allocate slots if there are not enough slots. + * * RUBY_GC_HEAP_GROWTH_FACTOR (new from 2.1) + * - Allocate slots by this factor. + * - (next slots number) = (current slots number) * (this factor) + * * RUBY_GC_HEAP_GROWTH_MAX_SLOTS (new from 2.1) + * - Allocation rate is limited to this number of slots. + * * RUBY_GC_HEAP_FREE_SLOTS_MIN_RATIO (new from 2.4) + * - Allocate additional pages when the number of free slots is + * lower than the value (total_slots * (this ratio)). + * * RUBY_GC_HEAP_FREE_SLOTS_GOAL_RATIO (new from 2.4) + * - Allocate slots to satisfy this formula: + * free_slots = total_slots * goal_ratio + * - In other words, prepare (total_slots * goal_ratio) free slots. + * - if this value is 0.0, then use RUBY_GC_HEAP_GROWTH_FACTOR directly. + * * RUBY_GC_HEAP_FREE_SLOTS_MAX_RATIO (new from 2.4) + * - Allow to free pages when the number of free slots is + * greater than the value (total_slots * (this ratio)). + * * RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR (new from 2.1.1) + * - Do full GC when the number of old objects is more than R * N + * where R is this factor and + * N is the number of old objects just after last full GC. + * + * * obsolete + * * RUBY_FREE_MIN -> RUBY_GC_HEAP_FREE_SLOTS (from 2.1) + * * RUBY_HEAP_MIN_SLOTS -> RUBY_GC_HEAP_INIT_SLOTS (from 2.1) + * + * * RUBY_GC_MALLOC_LIMIT + * * RUBY_GC_MALLOC_LIMIT_MAX (new from 2.1) + * * RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR (new from 2.1) + * + * * RUBY_GC_OLDMALLOC_LIMIT (new from 2.1) + * * RUBY_GC_OLDMALLOC_LIMIT_MAX (new from 2.1) + * * RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR (new from 2.1) + */ + +void +rb_gc_impl_set_params(void *objspace_ptr) +{ + rb_objspace_t *objspace = objspace_ptr; + /* RUBY_GC_HEAP_FREE_SLOTS */ + if (get_envparam_size("RUBY_GC_HEAP_FREE_SLOTS", &gc_params.heap_free_slots, 0)) { + /* ok */ + } + + gc_set_initial_pages(objspace); + + get_envparam_double("RUBY_GC_HEAP_GROWTH_FACTOR", &gc_params.growth_factor, 1.0, 0.0, FALSE); + get_envparam_size ("RUBY_GC_HEAP_GROWTH_MAX_SLOTS", &gc_params.growth_max_slots, 0); + get_envparam_double("RUBY_GC_HEAP_FREE_SLOTS_MIN_RATIO", &gc_params.heap_free_slots_min_ratio, + 0.0, 1.0, FALSE); + get_envparam_double("RUBY_GC_HEAP_FREE_SLOTS_MAX_RATIO", &gc_params.heap_free_slots_max_ratio, + gc_params.heap_free_slots_min_ratio, 1.0, FALSE); + get_envparam_double("RUBY_GC_HEAP_FREE_SLOTS_GOAL_RATIO", &gc_params.heap_free_slots_goal_ratio, + gc_params.heap_free_slots_min_ratio, gc_params.heap_free_slots_max_ratio, TRUE); + get_envparam_double("RUBY_GC_HEAP_SHAREDOBJECT_LIMIT_FACTOR", &gc_params.sharedobject_limit_factor, 0.0, 0.0, TRUE); + get_envparam_double("RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR", &gc_params.oldobject_limit_factor, 0.0, 0.0, TRUE); + get_envparam_double("RUBY_GC_HEAP_REMEMBERED_WB_UNPROTECTED_OBJECTS_LIMIT_RATIO", &gc_params.uncollectible_wb_unprotected_objects_limit_ratio, 0.0, 0.0, TRUE); + + if (get_envparam_size("RUBY_GC_MALLOC_LIMIT", &gc_params.malloc_limit_min, 0)) { + malloc_limit = gc_params.malloc_limit_min; + } + get_envparam_size ("RUBY_GC_MALLOC_LIMIT_MAX", &gc_params.malloc_limit_max, 0); + if (!gc_params.malloc_limit_max) { /* ignore max-check if 0 */ + gc_params.malloc_limit_max = SIZE_MAX; + } + get_envparam_double("RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR", &gc_params.malloc_limit_growth_factor, 1.0, 0.0, FALSE); + +#if RGENGC_ESTIMATE_OLDMALLOC + if (get_envparam_size("RUBY_GC_OLDMALLOC_LIMIT", &gc_params.oldmalloc_limit_min, 0)) { + objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_min; + } + get_envparam_size ("RUBY_GC_OLDMALLOC_LIMIT_MAX", &gc_params.oldmalloc_limit_max, 0); + get_envparam_double("RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR", &gc_params.oldmalloc_limit_growth_factor, 1.0, 0.0, FALSE); +#endif +} + +static inline size_t +objspace_malloc_size(rb_objspace_t *objspace, void *ptr, size_t hint) +{ +#ifdef HAVE_MALLOC_USABLE_SIZE + return malloc_usable_size(ptr); +#else + return hint; +#endif +} + +enum memop_type { + MEMOP_TYPE_MALLOC = 0, + MEMOP_TYPE_FREE, + MEMOP_TYPE_REALLOC +}; + +static inline void +atomic_sub_nounderflow(size_t *var, size_t sub) +{ + if (sub == 0) return; + + while (1) { + size_t val = *var; + if (val < sub) sub = val; + if (RUBY_ATOMIC_SIZE_CAS(*var, val, val-sub) == val) break; + } +} + +#define gc_stress_full_mark_after_malloc_p() \ + (FIXNUM_P(ruby_gc_stress_mode) && (FIX2LONG(ruby_gc_stress_mode) & (1< old_size) { + RUBY_ATOMIC_SIZE_ADD(malloc_increase, new_size - old_size); +#if RGENGC_ESTIMATE_OLDMALLOC + RUBY_ATOMIC_SIZE_ADD(objspace->rgengc.oldmalloc_increase, new_size - old_size); +#endif + } + else { + atomic_sub_nounderflow(&malloc_increase, old_size - new_size); +#if RGENGC_ESTIMATE_OLDMALLOC + atomic_sub_nounderflow(&objspace->rgengc.oldmalloc_increase, old_size - new_size); +#endif + } + + if (type == MEMOP_TYPE_MALLOC) { + retry: + if (malloc_increase > malloc_limit && ruby_native_thread_p() && !dont_gc_val()) { + if (ruby_thread_has_gvl_p() && is_lazy_sweeping(objspace)) { + gc_rest(objspace); /* gc_rest can reduce malloc_increase */ + goto retry; + } + garbage_collect_with_gvl(objspace, GPR_FLAG_MALLOC); + } + } + +#if MALLOC_ALLOCATED_SIZE + if (new_size >= old_size) { + RUBY_ATOMIC_SIZE_ADD(objspace->malloc_params.allocated_size, new_size - old_size); + } + else { + size_t dec_size = old_size - new_size; + size_t allocated_size = objspace->malloc_params.allocated_size; + +#if MALLOC_ALLOCATED_SIZE_CHECK + if (allocated_size < dec_size) { + rb_bug("objspace_malloc_increase: underflow malloc_params.allocated_size."); + } +#endif + atomic_sub_nounderflow(&objspace->malloc_params.allocated_size, dec_size); + } + + switch (type) { + case MEMOP_TYPE_MALLOC: + RUBY_ATOMIC_SIZE_INC(objspace->malloc_params.allocations); + break; + case MEMOP_TYPE_FREE: + { + size_t allocations = objspace->malloc_params.allocations; + if (allocations > 0) { + atomic_sub_nounderflow(&objspace->malloc_params.allocations, 1); + } +#if MALLOC_ALLOCATED_SIZE_CHECK + else { + GC_ASSERT(objspace->malloc_params.allocations > 0); + } +#endif + } + break; + case MEMOP_TYPE_REALLOC: /* ignore */ break; + } +#endif + return true; +} + +#define objspace_malloc_increase(...) \ + for (bool malloc_increase_done = objspace_malloc_increase_report(__VA_ARGS__); \ + !malloc_increase_done; \ + malloc_increase_done = objspace_malloc_increase_body(__VA_ARGS__)) + +struct malloc_obj_info { /* 4 words */ + size_t size; +}; + +static inline size_t +objspace_malloc_prepare(rb_objspace_t *objspace, size_t size) +{ + if (size == 0) size = 1; + +#if CALC_EXACT_MALLOC_SIZE + size += sizeof(struct malloc_obj_info); +#endif + + return size; +} + +static bool +malloc_during_gc_p(rb_objspace_t *objspace) +{ + /* malloc is not allowed during GC when we're not using multiple ractors + * (since ractors can run while another thread is sweeping) and when we + * have the GVL (since if we don't have the GVL, we'll try to acquire the + * GVL which will block and ensure the other thread finishes GC). */ + return during_gc && !dont_gc_val() && !rb_gc_multi_ractor_p() && ruby_thread_has_gvl_p(); +} + +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) {} + +#if CALC_EXACT_MALLOC_SIZE + { + struct malloc_obj_info *info = mem; + info->size = size; + mem = info + 1; + } +#endif + + return mem; +} + +#if defined(__GNUC__) && RUBY_DEBUG +#define RB_BUG_INSTEAD_OF_RB_MEMERROR 1 +#endif + +#ifndef RB_BUG_INSTEAD_OF_RB_MEMERROR +# define RB_BUG_INSTEAD_OF_RB_MEMERROR 0 +#endif + +#define GC_MEMERROR(...) \ + ((RB_BUG_INSTEAD_OF_RB_MEMERROR+0) ? rb_bug("" __VA_ARGS__) : rb_memerror()) + +#define TRY_WITH_GC(siz, expr) do { \ + const gc_profile_record_flag gpr = \ + GPR_FLAG_FULL_MARK | \ + GPR_FLAG_IMMEDIATE_MARK | \ + GPR_FLAG_IMMEDIATE_SWEEP | \ + GPR_FLAG_MALLOC; \ + objspace_malloc_gc_stress(objspace); \ + \ + if (RB_LIKELY((expr))) { \ + /* Success on 1st try */ \ + } \ + else if (!garbage_collect_with_gvl(objspace, gpr)) { \ + /* @shyouhei thinks this doesn't happen */ \ + GC_MEMERROR("TRY_WITH_GC: could not GC"); \ + } \ + else if ((expr)) { \ + /* Success on 2nd try */ \ + } \ + else { \ + GC_MEMERROR("TRY_WITH_GC: could not allocate:" \ + "%"PRIdSIZE" bytes for %s", \ + siz, # expr); \ + } \ + } while (0) + +static void +check_malloc_not_in_gc(rb_objspace_t *objspace, const char *msg) +{ + if (RB_UNLIKELY(malloc_during_gc_p(objspace))) { + dont_gc_on(); + during_gc = false; + rb_bug("Cannot %s during GC", msg); + } +} + +void +rb_gc_impl_free(void *objspace_ptr, void *ptr, size_t old_size) +{ + rb_objspace_t *objspace = objspace_ptr; + + if (!ptr) { + /* + * ISO/IEC 9899 says "If ptr is a null pointer, no action occurs" since + * its first version. We would better follow. + */ + return; + } +#if CALC_EXACT_MALLOC_SIZE + struct malloc_obj_info *info = (struct malloc_obj_info *)ptr - 1; + ptr = info; + old_size = info->size; +#endif + old_size = objspace_malloc_size(objspace, ptr, old_size); + + objspace_malloc_increase(objspace, ptr, 0, old_size, MEMOP_TYPE_FREE) { + free(ptr); + ptr = NULL; + RB_DEBUG_COUNTER_INC(heap_xfree); + } +} + +void * +rb_gc_impl_malloc(void *objspace_ptr, size_t size) +{ + rb_objspace_t *objspace = objspace_ptr; + check_malloc_not_in_gc(objspace, "malloc"); + + void *mem; + + size = objspace_malloc_prepare(objspace, size); + TRY_WITH_GC(size, mem = malloc(size)); + RB_DEBUG_COUNTER_INC(heap_xmalloc); + return objspace_malloc_fixup(objspace, mem, size); +} + +void * +rb_gc_impl_calloc(void *objspace_ptr, size_t size) +{ + rb_objspace_t *objspace = objspace_ptr; + + if (RB_UNLIKELY(malloc_during_gc_p(objspace))) { + rb_warn("calloc during GC detected, this could cause crashes if it triggers another GC"); +#if RGENGC_CHECK_MODE || RUBY_DEBUG + rb_bug("Cannot calloc during GC"); +#endif + } + + void *mem; + + size = objspace_malloc_prepare(objspace, size); + TRY_WITH_GC(size, mem = calloc1(size)); + return objspace_malloc_fixup(objspace, mem, size); +} + +static inline size_t +xmalloc2_size(const size_t count, const size_t elsize) +{ + return rb_size_mul_or_raise(count, elsize, rb_eArgError); +} + +void * +rb_gc_impl_realloc(void *objspace_ptr, void *ptr, size_t new_size, size_t old_size) +{ + rb_objspace_t *objspace = objspace_ptr; + + check_malloc_not_in_gc(objspace, "realloc"); + + void *mem; + + if (!ptr) return rb_gc_impl_malloc(objspace, new_size); + + /* + * The behavior of realloc(ptr, 0) is implementation defined. + * Therefore we don't use realloc(ptr, 0) for portability reason. + * see http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_400.htm + */ + if (new_size == 0) { + if ((mem = rb_gc_impl_malloc(objspace, 0)) != NULL) { + /* + * - OpenBSD's malloc(3) man page says that when 0 is passed, it + * returns a non-NULL pointer to an access-protected memory page. + * The returned pointer cannot be read / written at all, but + * still be a valid argument of free(). + * + * https://man.openbsd.org/malloc.3 + * + * - Linux's malloc(3) man page says that it _might_ perhaps return + * a non-NULL pointer when its argument is 0. That return value + * is safe (and is expected) to be passed to free(). + * + * https://man7.org/linux/man-pages/man3/malloc.3.html + * + * - As I read the implementation jemalloc's malloc() returns fully + * normal 16 bytes memory region when its argument is 0. + * + * - As I read the implementation musl libc's malloc() returns + * fully normal 32 bytes memory region when its argument is 0. + * + * - Other malloc implementations can also return non-NULL. + */ + rb_gc_impl_free(objspace, ptr, old_size); + return mem; + } + else { + /* + * It is dangerous to return NULL here, because that could lead to + * RCE. Fallback to 1 byte instead of zero. + * + * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11932 + */ + new_size = 1; + } + } + +#if CALC_EXACT_MALLOC_SIZE + { + struct malloc_obj_info *info = (struct malloc_obj_info *)ptr - 1; + new_size += sizeof(struct malloc_obj_info); + ptr = info; + old_size = info->size; + } +#endif + + old_size = objspace_malloc_size(objspace, ptr, old_size); + TRY_WITH_GC(new_size, mem = RB_GNUC_EXTENSION_BLOCK(realloc(ptr, new_size))); + new_size = objspace_malloc_size(objspace, mem, new_size); + +#if CALC_EXACT_MALLOC_SIZE + { + struct malloc_obj_info *info = mem; + info->size = new_size; + mem = info + 1; + } +#endif + + objspace_malloc_increase(objspace, mem, new_size, old_size, MEMOP_TYPE_REALLOC); + + RB_DEBUG_COUNTER_INC(heap_xrealloc); + return mem; +} + +void +rb_gc_impl_adjust_memory_usage(void *objspace_ptr, ssize_t diff) +{ + rb_objspace_t *objspace = objspace_ptr; + + if (diff > 0) { + objspace_malloc_increase(objspace, 0, diff, 0, MEMOP_TYPE_REALLOC); + } + else if (diff < 0) { + objspace_malloc_increase(objspace, 0, 0, -diff, MEMOP_TYPE_REALLOC); + } +} + +// TODO: move GC profiler stuff back into gc.c +/* + ------------------------------ GC profiler ------------------------------ +*/ + +#define GC_PROFILE_RECORD_DEFAULT_SIZE 100 + +static bool +current_process_time(struct timespec *ts) +{ +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID) + { + static int try_clock_gettime = 1; + if (try_clock_gettime && clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ts) == 0) { + return true; + } + else { + try_clock_gettime = 0; + } + } +#endif + +#ifdef RUSAGE_SELF + { + struct rusage usage; + struct timeval time; + if (getrusage(RUSAGE_SELF, &usage) == 0) { + time = usage.ru_utime; + ts->tv_sec = time.tv_sec; + ts->tv_nsec = (int32_t)time.tv_usec * 1000; + return true; + } + } +#endif + +#ifdef _WIN32 + { + FILETIME creation_time, exit_time, kernel_time, user_time; + ULARGE_INTEGER ui; + + if (GetProcessTimes(GetCurrentProcess(), + &creation_time, &exit_time, &kernel_time, &user_time) != 0) { + memcpy(&ui, &user_time, sizeof(FILETIME)); +#define PER100NSEC (uint64_t)(1000 * 1000 * 10) + ts->tv_nsec = (long)(ui.QuadPart % PER100NSEC); + ts->tv_sec = (time_t)(ui.QuadPart / PER100NSEC); + return true; + } + } +#endif + + return false; +} + +static double +getrusage_time(void) +{ + struct timespec ts; + if (current_process_time(&ts)) { + return ts.tv_sec + ts.tv_nsec * 1e-9; + } + else { + return 0.0; + } +} + + +static inline void +gc_prof_setup_new_record(rb_objspace_t *objspace, unsigned int reason) +{ + if (objspace->profile.run) { + size_t index = objspace->profile.next_index; + gc_profile_record *record; + + /* create new record */ + objspace->profile.next_index++; + + if (!objspace->profile.records) { + objspace->profile.size = GC_PROFILE_RECORD_DEFAULT_SIZE; + objspace->profile.records = malloc(xmalloc2_size(sizeof(gc_profile_record), objspace->profile.size)); + } + if (index >= objspace->profile.size) { + void *ptr; + objspace->profile.size += 1000; + ptr = realloc(objspace->profile.records, xmalloc2_size(sizeof(gc_profile_record), objspace->profile.size)); + if (!ptr) rb_memerror(); + objspace->profile.records = ptr; + } + if (!objspace->profile.records) { + rb_bug("gc_profile malloc or realloc miss"); + } + record = objspace->profile.current_record = &objspace->profile.records[objspace->profile.next_index - 1]; + MEMZERO(record, gc_profile_record, 1); + + /* setup before-GC parameter */ + record->flags = reason | (ruby_gc_stressful ? GPR_FLAG_STRESS : 0); +#if MALLOC_ALLOCATED_SIZE + record->allocated_size = malloc_allocated_size; +#endif +#if GC_PROFILE_MORE_DETAIL && GC_PROFILE_DETAIL_MEMORY +#ifdef RUSAGE_SELF + { + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage) == 0) { + record->maxrss = usage.ru_maxrss; + record->minflt = usage.ru_minflt; + record->majflt = usage.ru_majflt; + } + } +#endif +#endif + } +} + +static inline void +gc_prof_timer_start(rb_objspace_t *objspace) +{ + if (gc_prof_enabled(objspace)) { + gc_profile_record *record = gc_prof_record(objspace); +#if GC_PROFILE_MORE_DETAIL + record->prepare_time = objspace->profile.prepare_time; +#endif + record->gc_time = 0; + record->gc_invoke_time = getrusage_time(); + } +} + +static double +elapsed_time_from(double time) +{ + double now = getrusage_time(); + if (now > time) { + return now - time; + } + else { + return 0; + } +} + +static inline void +gc_prof_timer_stop(rb_objspace_t *objspace) +{ + if (gc_prof_enabled(objspace)) { + gc_profile_record *record = gc_prof_record(objspace); + record->gc_time = elapsed_time_from(record->gc_invoke_time); + record->gc_invoke_time -= objspace->profile.invoke_time; + } +} + +#ifdef BUILDING_SHARED_GC +# define RUBY_DTRACE_GC_HOOK(name) +#else +# define RUBY_DTRACE_GC_HOOK(name) \ + do {if (RUBY_DTRACE_GC_##name##_ENABLED()) RUBY_DTRACE_GC_##name();} while (0) +#endif + +static inline void +gc_prof_mark_timer_start(rb_objspace_t *objspace) +{ + RUBY_DTRACE_GC_HOOK(MARK_BEGIN); +#if GC_PROFILE_MORE_DETAIL + if (gc_prof_enabled(objspace)) { + gc_prof_record(objspace)->gc_mark_time = getrusage_time(); + } +#endif +} + +static inline void +gc_prof_mark_timer_stop(rb_objspace_t *objspace) +{ + RUBY_DTRACE_GC_HOOK(MARK_END); +#if GC_PROFILE_MORE_DETAIL + if (gc_prof_enabled(objspace)) { + gc_profile_record *record = gc_prof_record(objspace); + record->gc_mark_time = elapsed_time_from(record->gc_mark_time); + } +#endif +} + +static inline void +gc_prof_sweep_timer_start(rb_objspace_t *objspace) +{ + RUBY_DTRACE_GC_HOOK(SWEEP_BEGIN); + if (gc_prof_enabled(objspace)) { + gc_profile_record *record = gc_prof_record(objspace); + + if (record->gc_time > 0 || GC_PROFILE_MORE_DETAIL) { + objspace->profile.gc_sweep_start_time = getrusage_time(); + } + } +} + +static inline void +gc_prof_sweep_timer_stop(rb_objspace_t *objspace) +{ + RUBY_DTRACE_GC_HOOK(SWEEP_END); + + if (gc_prof_enabled(objspace)) { + double sweep_time; + gc_profile_record *record = gc_prof_record(objspace); + + if (record->gc_time > 0) { + sweep_time = elapsed_time_from(objspace->profile.gc_sweep_start_time); + /* need to accumulate GC time for lazy sweep after gc() */ + record->gc_time += sweep_time; + } + else if (GC_PROFILE_MORE_DETAIL) { + sweep_time = elapsed_time_from(objspace->profile.gc_sweep_start_time); + } + +#if GC_PROFILE_MORE_DETAIL + record->gc_sweep_time += sweep_time; + if (heap_pages_deferred_final) record->flags |= GPR_FLAG_HAVE_FINALIZE; +#endif + if (heap_pages_deferred_final) objspace->profile.latest_gc_info |= GPR_FLAG_HAVE_FINALIZE; + } +} + +static inline void +gc_prof_set_malloc_info(rb_objspace_t *objspace) +{ +#if GC_PROFILE_MORE_DETAIL + if (gc_prof_enabled(objspace)) { + gc_profile_record *record = gc_prof_record(objspace); + record->allocate_increase = malloc_increase; + record->allocate_limit = malloc_limit; + } +#endif +} + +static inline void +gc_prof_set_heap_info(rb_objspace_t *objspace) +{ + if (gc_prof_enabled(objspace)) { + gc_profile_record *record = gc_prof_record(objspace); + size_t live = objspace->profile.total_allocated_objects_at_gc_start - total_freed_objects(objspace); + size_t total = objspace->profile.heap_used_at_gc_start * HEAP_PAGE_OBJ_LIMIT; + +#if GC_PROFILE_MORE_DETAIL + record->heap_use_pages = objspace->profile.heap_used_at_gc_start; + record->heap_live_objects = live; + record->heap_free_objects = total - live; +#endif + + record->heap_total_objects = total; + record->heap_use_size = live * BASE_SLOT_SIZE; + record->heap_total_size = total * BASE_SLOT_SIZE; + } +} + +/* + * call-seq: + * GC::Profiler.clear -> nil + * + * Clears the \GC profiler data. + * + */ + +static VALUE +gc_profile_clear(VALUE _) +{ + rb_objspace_t *objspace = rb_gc_get_objspace(); + void *p = objspace->profile.records; + objspace->profile.records = NULL; + objspace->profile.size = 0; + objspace->profile.next_index = 0; + objspace->profile.current_record = 0; + free(p); + return Qnil; +} + +/* + * call-seq: + * GC::Profiler.raw_data -> [Hash, ...] + * + * Returns an Array of individual raw profile data Hashes ordered + * from earliest to latest by +:GC_INVOKE_TIME+. + * + * For example: + * + * [ + * { + * :GC_TIME=>1.3000000000000858e-05, + * :GC_INVOKE_TIME=>0.010634999999999999, + * :HEAP_USE_SIZE=>289640, + * :HEAP_TOTAL_SIZE=>588960, + * :HEAP_TOTAL_OBJECTS=>14724, + * :GC_IS_MARKED=>false + * }, + * # ... + * ] + * + * The keys mean: + * + * +:GC_TIME+:: + * Time elapsed in seconds for this GC run + * +:GC_INVOKE_TIME+:: + * Time elapsed in seconds from startup to when the GC was invoked + * +:HEAP_USE_SIZE+:: + * Total bytes of heap used + * +:HEAP_TOTAL_SIZE+:: + * Total size of heap in bytes + * +:HEAP_TOTAL_OBJECTS+:: + * Total number of objects + * +:GC_IS_MARKED+:: + * Returns +true+ if the GC is in mark phase + * + * If ruby was built with +GC_PROFILE_MORE_DETAIL+, you will also have access + * to the following hash keys: + * + * +:GC_MARK_TIME+:: + * +:GC_SWEEP_TIME+:: + * +:ALLOCATE_INCREASE+:: + * +:ALLOCATE_LIMIT+:: + * +:HEAP_USE_PAGES+:: + * +:HEAP_LIVE_OBJECTS+:: + * +:HEAP_FREE_OBJECTS+:: + * +:HAVE_FINALIZE+:: + * + */ + +static VALUE +gc_profile_record_get(VALUE _) +{ + VALUE prof; + VALUE gc_profile = rb_ary_new(); + size_t i; + rb_objspace_t *objspace = rb_gc_get_objspace(); + + if (!objspace->profile.run) { + return Qnil; + } + + for (i =0; i < objspace->profile.next_index; i++) { + gc_profile_record *record = &objspace->profile.records[i]; + + prof = rb_hash_new(); + rb_hash_aset(prof, ID2SYM(rb_intern("GC_FLAGS")), gc_info_decode(objspace, rb_hash_new(), record->flags)); + rb_hash_aset(prof, ID2SYM(rb_intern("GC_TIME")), DBL2NUM(record->gc_time)); + rb_hash_aset(prof, ID2SYM(rb_intern("GC_INVOKE_TIME")), DBL2NUM(record->gc_invoke_time)); + rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_USE_SIZE")), SIZET2NUM(record->heap_use_size)); + rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_TOTAL_SIZE")), SIZET2NUM(record->heap_total_size)); + rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_TOTAL_OBJECTS")), SIZET2NUM(record->heap_total_objects)); + rb_hash_aset(prof, ID2SYM(rb_intern("MOVED_OBJECTS")), SIZET2NUM(record->moved_objects)); + rb_hash_aset(prof, ID2SYM(rb_intern("GC_IS_MARKED")), Qtrue); +#if GC_PROFILE_MORE_DETAIL + rb_hash_aset(prof, ID2SYM(rb_intern("GC_MARK_TIME")), DBL2NUM(record->gc_mark_time)); + rb_hash_aset(prof, ID2SYM(rb_intern("GC_SWEEP_TIME")), DBL2NUM(record->gc_sweep_time)); + rb_hash_aset(prof, ID2SYM(rb_intern("ALLOCATE_INCREASE")), SIZET2NUM(record->allocate_increase)); + rb_hash_aset(prof, ID2SYM(rb_intern("ALLOCATE_LIMIT")), SIZET2NUM(record->allocate_limit)); + rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_USE_PAGES")), SIZET2NUM(record->heap_use_pages)); + rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_LIVE_OBJECTS")), SIZET2NUM(record->heap_live_objects)); + rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_FREE_OBJECTS")), SIZET2NUM(record->heap_free_objects)); + + rb_hash_aset(prof, ID2SYM(rb_intern("REMOVING_OBJECTS")), SIZET2NUM(record->removing_objects)); + rb_hash_aset(prof, ID2SYM(rb_intern("EMPTY_OBJECTS")), SIZET2NUM(record->empty_objects)); + + rb_hash_aset(prof, ID2SYM(rb_intern("HAVE_FINALIZE")), (record->flags & GPR_FLAG_HAVE_FINALIZE) ? Qtrue : Qfalse); +#endif + +#if RGENGC_PROFILE > 0 + rb_hash_aset(prof, ID2SYM(rb_intern("OLD_OBJECTS")), SIZET2NUM(record->old_objects)); + rb_hash_aset(prof, ID2SYM(rb_intern("REMEMBERED_NORMAL_OBJECTS")), SIZET2NUM(record->remembered_normal_objects)); + rb_hash_aset(prof, ID2SYM(rb_intern("REMEMBERED_SHADY_OBJECTS")), SIZET2NUM(record->remembered_shady_objects)); +#endif + rb_ary_push(gc_profile, prof); + } + + return gc_profile; +} + +#if GC_PROFILE_MORE_DETAIL +#define MAJOR_REASON_MAX 0x10 + +static char * +gc_profile_dump_major_reason(unsigned int flags, char *buff) +{ + unsigned int reason = flags & GPR_FLAG_MAJOR_MASK; + int i = 0; + + if (reason == GPR_FLAG_NONE) { + buff[0] = '-'; + buff[1] = 0; + } + else { +#define C(x, s) \ + if (reason & GPR_FLAG_MAJOR_BY_##x) { \ + buff[i++] = #x[0]; \ + if (i >= MAJOR_REASON_MAX) rb_bug("gc_profile_dump_major_reason: overflow"); \ + buff[i] = 0; \ + } + C(NOFREE, N); + C(OLDGEN, O); + C(SHADY, S); +#if RGENGC_ESTIMATE_OLDMALLOC + C(OLDMALLOC, M); +#endif +#undef C + } + return buff; +} +#endif + + + +static void +gc_profile_dump_on(VALUE out, VALUE (*append)(VALUE, VALUE)) +{ + rb_objspace_t *objspace = rb_gc_get_objspace(); + size_t count = objspace->profile.next_index; +#ifdef MAJOR_REASON_MAX + char reason_str[MAJOR_REASON_MAX]; +#endif + + if (objspace->profile.run && count /* > 1 */) { + size_t i; + const gc_profile_record *record; + + append(out, rb_sprintf("GC %"PRIuSIZE" invokes.\n", objspace->profile.count)); + append(out, rb_str_new_cstr("Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC Time(ms)\n")); + + for (i = 0; i < count; i++) { + record = &objspace->profile.records[i]; + append(out, rb_sprintf("%5"PRIuSIZE" %19.3f %20"PRIuSIZE" %20"PRIuSIZE" %20"PRIuSIZE" %30.20f\n", + i+1, record->gc_invoke_time, record->heap_use_size, + record->heap_total_size, record->heap_total_objects, record->gc_time*1000)); + } + +#if GC_PROFILE_MORE_DETAIL + const char *str = "\n\n" \ + "More detail.\n" \ + "Prepare Time = Previously GC's rest sweep time\n" + "Index Flags Allocate Inc. Allocate Limit" +#if CALC_EXACT_MALLOC_SIZE + " Allocated Size" +#endif + " Use Page Mark Time(ms) Sweep Time(ms) Prepare Time(ms) LivingObj FreeObj RemovedObj EmptyObj" +#if RGENGC_PROFILE + " OldgenObj RemNormObj RemShadObj" +#endif +#if GC_PROFILE_DETAIL_MEMORY + " MaxRSS(KB) MinorFLT MajorFLT" +#endif + "\n"; + append(out, rb_str_new_cstr(str)); + + for (i = 0; i < count; i++) { + record = &objspace->profile.records[i]; + append(out, rb_sprintf("%5"PRIuSIZE" %4s/%c/%6s%c %13"PRIuSIZE" %15"PRIuSIZE +#if CALC_EXACT_MALLOC_SIZE + " %15"PRIuSIZE +#endif + " %9"PRIuSIZE" %17.12f %17.12f %17.12f %10"PRIuSIZE" %10"PRIuSIZE" %10"PRIuSIZE" %10"PRIuSIZE +#if RGENGC_PROFILE + "%10"PRIuSIZE" %10"PRIuSIZE" %10"PRIuSIZE +#endif +#if GC_PROFILE_DETAIL_MEMORY + "%11ld %8ld %8ld" +#endif + + "\n", + i+1, + gc_profile_dump_major_reason(record->flags, reason_str), + (record->flags & GPR_FLAG_HAVE_FINALIZE) ? 'F' : '.', + (record->flags & GPR_FLAG_NEWOBJ) ? "NEWOBJ" : + (record->flags & GPR_FLAG_MALLOC) ? "MALLOC" : + (record->flags & GPR_FLAG_METHOD) ? "METHOD" : + (record->flags & GPR_FLAG_CAPI) ? "CAPI__" : "??????", + (record->flags & GPR_FLAG_STRESS) ? '!' : ' ', + record->allocate_increase, record->allocate_limit, +#if CALC_EXACT_MALLOC_SIZE + record->allocated_size, +#endif + record->heap_use_pages, + record->gc_mark_time*1000, + record->gc_sweep_time*1000, + record->prepare_time*1000, + + record->heap_live_objects, + record->heap_free_objects, + record->removing_objects, + record->empty_objects +#if RGENGC_PROFILE + , + record->old_objects, + record->remembered_normal_objects, + record->remembered_shady_objects +#endif +#if GC_PROFILE_DETAIL_MEMORY + , + record->maxrss / 1024, + record->minflt, + record->majflt +#endif + + )); + } +#endif + } +} + +/* + * call-seq: + * GC::Profiler.result -> String + * + * Returns a profile data report such as: + * + * GC 1 invokes. + * Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC time(ms) + * 1 0.012 159240 212940 10647 0.00000000000001530000 + */ + +static VALUE +gc_profile_result(VALUE _) +{ + VALUE str = rb_str_buf_new(0); + gc_profile_dump_on(str, rb_str_buf_append); + return str; +} + +/* + * call-seq: + * GC::Profiler.report + * GC::Profiler.report(io) + * + * Writes the GC::Profiler.result to $stdout or the given IO object. + * + */ + +static VALUE +gc_profile_report(int argc, VALUE *argv, VALUE self) +{ + VALUE out; + + out = (!rb_check_arity(argc, 0, 1) ? rb_stdout : argv[0]); + gc_profile_dump_on(out, rb_io_write); + + return Qnil; +} + +/* + * call-seq: + * GC::Profiler.total_time -> float + * + * The total time used for garbage collection in seconds + */ + +static VALUE +gc_profile_total_time(VALUE self) +{ + double time = 0; + rb_objspace_t *objspace = rb_gc_get_objspace(); + + if (objspace->profile.run && objspace->profile.next_index > 0) { + size_t i; + size_t count = objspace->profile.next_index; + + for (i = 0; i < count; i++) { + time += objspace->profile.records[i].gc_time; + } + } + return DBL2NUM(time); +} + +/* + * call-seq: + * GC::Profiler.enabled? -> true or false + * + * The current status of \GC profile mode. + */ + +static VALUE +gc_profile_enable_get(VALUE self) +{ + rb_objspace_t *objspace = rb_gc_get_objspace(); + return objspace->profile.run ? Qtrue : Qfalse; +} + +/* + * call-seq: + * GC::Profiler.enable -> nil + * + * Starts the \GC profiler. + * + */ + +static VALUE +gc_profile_enable(VALUE _) +{ + rb_objspace_t *objspace = rb_gc_get_objspace(); + objspace->profile.run = TRUE; + objspace->profile.current_record = 0; + return Qnil; +} + +/* + * call-seq: + * GC::Profiler.disable -> nil + * + * Stops the \GC profiler. + * + */ + +static VALUE +gc_profile_disable(VALUE _) +{ + rb_objspace_t *objspace = rb_gc_get_objspace(); + + objspace->profile.run = FALSE; + objspace->profile.current_record = 0; + return Qnil; +} + +/* + * call-seq: + * GC.verify_internal_consistency -> nil + * + * Verify internal consistency. + * + * This method is implementation specific. + * Now this method checks generational consistency + * if RGenGC is supported. + */ +static VALUE +gc_verify_internal_consistency_m(VALUE dummy) +{ + gc_verify_internal_consistency(rb_gc_get_objspace()); + return Qnil; +} + +#if GC_CAN_COMPILE_COMPACTION +/* + * call-seq: + * GC.auto_compact = flag + * + * Updates automatic compaction mode. + * + * When enabled, the compactor will execute on every major collection. + * + * Enabling compaction will degrade performance on major collections. + */ +static VALUE +gc_set_auto_compact(VALUE _, VALUE v) +{ + GC_ASSERT(GC_COMPACTION_SUPPORTED); + + ruby_enable_autocompact = RTEST(v); + +#if RGENGC_CHECK_MODE + ruby_autocompact_compare_func = NULL; + + if (SYMBOL_P(v)) { + ID id = RB_SYM2ID(v); + if (id == rb_intern("empty")) { + ruby_autocompact_compare_func = compare_free_slots; + } + } +#endif + + return v; +} +#else +# define gc_set_auto_compact rb_f_notimplement +#endif + +#if GC_CAN_COMPILE_COMPACTION +/* + * call-seq: + * GC.auto_compact -> true or false + * + * Returns whether or not automatic compaction has been enabled. + */ +static VALUE +gc_get_auto_compact(VALUE _) +{ + return ruby_enable_autocompact ? Qtrue : Qfalse; +} +#else +# define gc_get_auto_compact rb_f_notimplement +#endif + +#if GC_CAN_COMPILE_COMPACTION +static VALUE +type_sym(size_t type) +{ + switch (type) { +#define COUNT_TYPE(t) case (t): return ID2SYM(rb_intern(#t)); break; + COUNT_TYPE(T_NONE); + COUNT_TYPE(T_OBJECT); + COUNT_TYPE(T_CLASS); + COUNT_TYPE(T_MODULE); + COUNT_TYPE(T_FLOAT); + COUNT_TYPE(T_STRING); + COUNT_TYPE(T_REGEXP); + COUNT_TYPE(T_ARRAY); + COUNT_TYPE(T_HASH); + COUNT_TYPE(T_STRUCT); + COUNT_TYPE(T_BIGNUM); + COUNT_TYPE(T_FILE); + COUNT_TYPE(T_DATA); + COUNT_TYPE(T_MATCH); + COUNT_TYPE(T_COMPLEX); + COUNT_TYPE(T_RATIONAL); + COUNT_TYPE(T_NIL); + COUNT_TYPE(T_TRUE); + COUNT_TYPE(T_FALSE); + COUNT_TYPE(T_SYMBOL); + COUNT_TYPE(T_FIXNUM); + COUNT_TYPE(T_IMEMO); + COUNT_TYPE(T_UNDEF); + COUNT_TYPE(T_NODE); + COUNT_TYPE(T_ICLASS); + COUNT_TYPE(T_ZOMBIE); + COUNT_TYPE(T_MOVED); +#undef COUNT_TYPE + default: return SIZET2NUM(type); break; + } +} + +/* + * call-seq: + * GC.latest_compact_info -> hash + * + * Returns information about object moved in the most recent \GC compaction. + * + * The returned +hash+ contains the following keys: + * + * [considered] + * Hash containing the type of the object as the key and the number of + * objects of that type that were considered for movement. + * [moved] + * Hash containing the type of the object as the key and the number of + * objects of that type that were actually moved. + * [moved_up] + * Hash containing the type of the object as the key and the number of + * objects of that type that were increased in size. + * [moved_down] + * Hash containing the type of the object as the key and the number of + * objects of that type that were decreased in size. + * + * Some objects can't be moved (due to pinning) so these numbers can be used to + * calculate compaction efficiency. + */ +static VALUE +gc_compact_stats(VALUE self) +{ + rb_objspace_t *objspace = rb_gc_get_objspace(); + VALUE h = rb_hash_new(); + VALUE considered = rb_hash_new(); + VALUE moved = rb_hash_new(); + VALUE moved_up = rb_hash_new(); + VALUE moved_down = rb_hash_new(); + + for (size_t i = 0; i < T_MASK; i++) { + if (objspace->rcompactor.considered_count_table[i]) { + rb_hash_aset(considered, type_sym(i), SIZET2NUM(objspace->rcompactor.considered_count_table[i])); + } + + if (objspace->rcompactor.moved_count_table[i]) { + rb_hash_aset(moved, type_sym(i), SIZET2NUM(objspace->rcompactor.moved_count_table[i])); + } + + if (objspace->rcompactor.moved_up_count_table[i]) { + rb_hash_aset(moved_up, type_sym(i), SIZET2NUM(objspace->rcompactor.moved_up_count_table[i])); + } + + if (objspace->rcompactor.moved_down_count_table[i]) { + rb_hash_aset(moved_down, type_sym(i), SIZET2NUM(objspace->rcompactor.moved_down_count_table[i])); + } + } + + rb_hash_aset(h, ID2SYM(rb_intern("considered")), considered); + rb_hash_aset(h, ID2SYM(rb_intern("moved")), moved); + rb_hash_aset(h, ID2SYM(rb_intern("moved_up")), moved_up); + rb_hash_aset(h, ID2SYM(rb_intern("moved_down")), moved_down); + + return h; +} +#else +# define gc_compact_stats rb_f_notimplement +#endif + +#if GC_CAN_COMPILE_COMPACTION +/* + * call-seq: + * GC.compact -> hash + * + * This function compacts objects together in Ruby's heap. It eliminates + * unused space (or fragmentation) in the heap by moving objects in to that + * unused space. + * + * The returned +hash+ contains statistics about the objects that were moved; + * see GC.latest_compact_info. + * + * This method is only expected to work on CRuby. + * + * To test whether \GC compaction is supported, use the idiom: + * + * GC.respond_to?(:compact) + */ +static VALUE +gc_compact(VALUE self) +{ + rb_objspace_t *objspace = rb_gc_get_objspace(); + int full_marking_p = gc_config_full_mark_val; + gc_config_full_mark_set(TRUE); + + /* Run GC with compaction enabled */ + rb_gc_impl_start(rb_gc_get_objspace(), true, true, true, false, true); + gc_config_full_mark_set(full_marking_p); + + return gc_compact_stats(self); +} +#else +# define gc_compact rb_f_notimplement +#endif + +#if GC_CAN_COMPILE_COMPACTION +/* call-seq: + * GC.verify_compaction_references(toward: nil, double_heap: false) -> hash + * + * Verify compaction reference consistency. + * + * This method is implementation specific. During compaction, objects that + * were moved are replaced with T_MOVED objects. No object should have a + * reference to a T_MOVED object after compaction. + * + * This function expands the heap to ensure room to move all objects, + * compacts the heap to make sure everything moves, updates all references, + * then performs a full \GC. If any object contains a reference to a T_MOVED + * object, that object should be pushed on the mark stack, and will + * make a SEGV. + */ +static VALUE +gc_verify_compaction_references(int argc, VALUE* argv, VALUE self) +{ + static ID keywords[3] = {0}; + if (!keywords[0]) { + keywords[0] = rb_intern("toward"); + keywords[1] = rb_intern("double_heap"); + keywords[2] = rb_intern("expand_heap"); + } + + VALUE options; + rb_scan_args_kw(rb_keyword_given_p(), argc, argv, ":", &options); + + VALUE arguments[3] = { Qnil, Qfalse, Qfalse }; + int kwarg_count = rb_get_kwargs(options, keywords, 0, 3, arguments); + bool toward_empty = kwarg_count > 0 && SYMBOL_P(arguments[0]) && SYM2ID(arguments[0]) == rb_intern("empty"); + bool expand_heap = (kwarg_count > 1 && RTEST(arguments[1])) || (kwarg_count > 2 && RTEST(arguments[2])); + + rb_objspace_t *objspace = rb_gc_get_objspace(); + + /* Clear the heap. */ + rb_gc_impl_start(objspace, true, true, true, false, false); + + HEAP_LOCK_ENTER(objspace); + { + gc_rest(objspace); + + /* if both double_heap and expand_heap are set, expand_heap takes precedence */ + if (expand_heap) { + struct desired_compaction_pages_i_data desired_compaction = { + .objspace = objspace, + .required_slots = {0}, + }; + /* Work out how many objects want to be in each size pool, taking account of moves */ + objspace_each_pages(objspace, desired_compaction_pages_i, &desired_compaction, TRUE); + + /* Find out which pool has the most pages */ + size_t max_existing_pages = 0; + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); + max_existing_pages = MAX(max_existing_pages, heap->total_pages); + } + /* Add pages to each size pool so that compaction is guaranteed to move every object */ + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); + + size_t pages_to_add = 0; + /* + * Step 1: Make sure every pool has the same number of pages, by adding empty pages + * to smaller pools. This is required to make sure the compact cursor can advance + * through all of the pools in `gc_sweep_compact` without hitting the "sweep & + * compact cursors met" condition on some pools before fully compacting others + */ + pages_to_add += max_existing_pages - heap->total_pages; + /* + * Step 2: Now add additional free pages to each size pool sufficient to hold all objects + * that want to be in that size pool, whether moved into it or moved within it + */ + pages_to_add += slots_to_pages_for_size_pool(objspace, size_pool, desired_compaction.required_slots[i]); + /* + * Step 3: Add two more pages so that the compact & sweep cursors will meet _after_ all objects + * have been moved, and not on the last iteration of the `gc_sweep_compact` loop + */ + pages_to_add += 2; + + heap_add_pages(objspace, size_pool, heap, pages_to_add); + } + } + + if (toward_empty) { + objspace->rcompactor.compare_func = compare_free_slots; + } + } + HEAP_LOCK_LEAVE(objspace); + + rb_gc_impl_start(rb_gc_get_objspace(), true, true, true, false, true); + + rb_objspace_reachable_objects_from_root(root_obj_check_moved_i, objspace); + objspace_each_objects(objspace, heap_check_moved_i, objspace, TRUE); + + objspace->rcompactor.compare_func = NULL; + + return gc_compact_stats(self); +} +#else +# define gc_verify_compaction_references rb_f_notimplement +#endif + +void +rb_gc_impl_objspace_free(void *objspace_ptr) +{ + rb_objspace_t *objspace = objspace_ptr; + + if (is_lazy_sweeping(objspace)) + rb_bug("lazy sweeping underway when freeing object space"); + + free(objspace->profile.records); + objspace->profile.records = NULL; + + bool pages_to_free = false; + if (heap_pages_sorted) { + if (objspace->local_data.objspace_closed) { + free(heap_pages_sorted); + } + else { + pages_to_free = true; + } + } + + if (pages_to_free) { + size_t i; + size_t total_heap_pages = heap_allocated_pages; + for (i = 0; i < total_heap_pages; ++i) { + heap_page_free(objspace, heap_pages_sorted[i], false); + } + free(heap_pages_sorted); + heap_allocated_pages = 0; + heap_pages_sorted_length = 0; + heap_pages_lomem = 0; + heap_pages_himem = 0; + + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + SIZE_POOL_EDEN_HEAP(size_pool)->total_pages = 0; + SIZE_POOL_EDEN_HEAP(size_pool)->total_slots = 0; + } + } + free_local_data_contents(&objspace->local_data); + + free_stack_chunks(&objspace->mark_stack); + mark_stack_free_cache(&objspace->mark_stack); + + rb_darray_free(objspace->weak_references); + + if (objspace == GET_VM()->objspace) rb_global_space_free(GET_VM()->global_space); + + free(objspace); +} + +static int +pin_value(st_data_t key, st_data_t value, st_data_t data) +{ + rb_gc_impl_mark_and_pin((void *)data, (VALUE)value); + + return ST_CONTINUE; +} + +void rb_gc_impl_mark(void *objspace_ptr, VALUE obj); + +static int +gc_mark_tbl_no_pin_i(st_data_t key, st_data_t value, st_data_t data) +{ + rb_gc_impl_mark((void *)data, (VALUE)value); + + return ST_CONTINUE; +} + +#if MALLOC_ALLOCATED_SIZE +/* + * call-seq: + * GC.malloc_allocated_size -> Integer + * + * Returns the size of memory allocated by malloc(). + * + * Only available if ruby was built with +CALC_EXACT_MALLOC_SIZE+. + */ + +static VALUE +gc_malloc_allocated_size(VALUE self) +{ + return UINT2NUM(rb_objspace.malloc_params.allocated_size); +} + +/* + * call-seq: + * GC.malloc_allocations -> Integer + * + * Returns the number of malloc() allocations. + * + * Only available if ruby was built with +CALC_EXACT_MALLOC_SIZE+. + */ + +static VALUE +gc_malloc_allocations(VALUE self) +{ + return UINT2NUM(rb_objspace.malloc_params.allocations); +} +#endif + +void +rb_gc_impl_objspace_mark(void *objspace_ptr) +{ + rb_objspace_t *objspace = objspace_ptr; + + objspace->rgengc.parent_object = Qfalse; + + if (finalizer_table != NULL) { + st_foreach(finalizer_table, pin_value, (st_data_t)objspace); + } + + rb_native_mutex_lock(&objspace->local_data.obj_id_lock); + mark_tbl_no_pin(objspace, objspace->local_data.obj_to_id_tbl); + rb_native_mutex_unlock(&objspace->local_data.obj_id_lock); + + if (stress_to_class) rb_gc_mark(stress_to_class); +} + +#define LOCAL_SIZE_POOL_STAT_COUNT 7 +void +size_pool_local_stats_init(rb_objspace_t *objspace, int size_pool_idx) +{ + objspace->local_data.size_pool_stats[size_pool_idx].stats = malloc(LOCAL_SIZE_POOL_STAT_COUNT * sizeof(size_t *)); + objspace->local_data.size_pool_stats[size_pool_idx].field_count = LOCAL_SIZE_POOL_STAT_COUNT; + + objspace->local_data.size_pool_stats[size_pool_idx].stats[0] = &size_pools[size_pool_idx].total_allocated_pages; + objspace->local_data.size_pool_stats[size_pool_idx].stats[1] = &size_pools[size_pool_idx].total_freed_pages; + objspace->local_data.size_pool_stats[size_pool_idx].stats[2] = &size_pools[size_pool_idx].force_major_gc_count; + objspace->local_data.size_pool_stats[size_pool_idx].stats[3] = &size_pools[size_pool_idx].force_incremental_marking_finish_count; + objspace->local_data.size_pool_stats[size_pool_idx].stats[4] = &size_pools[size_pool_idx].total_allocated_objects; + objspace->local_data.size_pool_stats[size_pool_idx].stats[5] = &size_pools[size_pool_idx].newly_created_by_borrowing_count; + objspace->local_data.size_pool_stats[size_pool_idx].stats[6] = &size_pools[size_pool_idx].total_freed_objects; +} + +#define LOCAL_GC_STAT_COUNT 7 +void +objspace_local_stats_init(rb_objspace_t *objspace) +{ + objspace->local_data.gc_stats.stats = malloc(LOCAL_GC_STAT_COUNT * sizeof(size_t *)); + objspace->local_data.gc_stats.field_count = LOCAL_GC_STAT_COUNT; + objspace->local_data.gc_stats.stats[0] = &objspace->heap_pages.allocatable_pages; + objspace->local_data.gc_stats.stats[1] = &heap_pages_freeable_pages; + objspace->local_data.gc_stats.stats[2] = &heap_pages_final_slots; + objspace->local_data.gc_stats.stats[3] = &objspace->rgengc.uncollectible_wb_unprotected_objects; + objspace->local_data.gc_stats.stats[4] = &objspace->rgengc.uncollectible_wb_unprotected_objects_limit; + objspace->local_data.gc_stats.stats[5] = &objspace->rgengc.old_objects; + objspace->local_data.gc_stats.stats[6] = &objspace->rgengc.old_objects_limit; + + + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + size_pool_local_stats_init(objspace, i); + } +} + +rb_objspace_t * +objspace_setup(rb_objspace_t *objspace, rb_ractor_t *ractor) +{ + ractor->local_objspace = objspace; + objspace->local_data.ractor = ractor; + + gc_config_full_mark_set(TRUE); + + objspace->flags.gc_stressful = RTEST(initial_stress); + objspace->gc_stress_mode = initial_stress; + + objspace->flags.measure_gc = 1; + malloc_limit = gc_params.malloc_limit_min; + objspace->finalize_deferred_pjob = rb_postponed_job_preregister(0, gc_finalize_deferred, NULL); + if (objspace->finalize_deferred_pjob == POSTPONED_JOB_HANDLE_INVALID) { + rb_bug("Could not preregister postponed job for GC"); + } + + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + + size_pool->slot_size = (1 << i) * BASE_SLOT_SIZE; + + ccan_list_head_init(&SIZE_POOL_EDEN_HEAP(size_pool)->pages); + ccan_list_head_init(&SIZE_POOL_TOMB_HEAP(size_pool)->pages); + } + + rb_darray_make(&objspace->weak_references, 0); + + // TODO: debug why on Windows Ruby crashes on boot when GC is on. +#ifdef _WIN32 + dont_gc_on(); +#endif + +#if defined(INIT_HEAP_PAGE_ALLOC_USE_MMAP) + /* Need to determine if we can use mmap at runtime. */ + heap_page_alloc_use_mmap = INIT_HEAP_PAGE_ALLOC_USE_MMAP; +#endif + +#if RGENGC_ESTIMATE_OLDMALLOC + objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_min; +#endif + /* Set size pools allocatable pages. */ + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + /* Set the default value of size_pool_init_slots. */ + gc_params.size_pool_init_slots[i] = GC_HEAP_INIT_SLOTS; + size_pool->allocatable_pages = minimum_pages_for_size_pool(objspace, size_pool); + } + + heap_pages_expand_sorted(objspace); + + init_mark_stack(&objspace->mark_stack); + + objspace->profile.invoke_time = getrusage_time(); + + objspace_local_data_init(objspace, &objspace->local_data); + objspace_local_stats_init(objspace); + + return objspace; +} + +void +rb_assign_main_ractor_objspace(rb_ractor_t *ractor) +{ + VM_ASSERT(ractor == ruby_single_main_ractor); + + rb_vm_t *vm = GET_VM(); + vm->objspace->belong_to_single_main_ractor = true; + ractor->local_objspace = vm->objspace; + vm->objspace->local_data.ractor = ractor; +} + +void +rb_create_ractor_local_objspace(rb_ractor_t *ractor) +{ + rb_objspace_t *objspace = calloc1(sizeof(rb_objspace_t)); + objspace_setup(objspace, ractor); + rb_vm_t *vm = GET_VM(); + if (ractor != vm->ractor.main_ractor) { + ruby_single_main_objspace = NULL; + vm->objspace->belong_to_single_main_ractor = false; + } + rb_objspace_gc_enable(ractor->local_objspace); +} + +void * +rb_gc_impl_objspace_alloc(void) +{ + rb_objspace_t *objspace = calloc1(sizeof(rb_objspace_t)); + + return objspace; +} + +void +rb_gc_impl_objspace_init(void *objspace_ptr) +{ + ruby_current_vm_ptr->objspace = objspace; + ruby_single_main_objspace = objspace; + ruby_current_vm_ptr->ractor.main_ractor = rb_ractor_main_alloc(); + return objspace_setup(objspace, ruby_current_vm_ptr->ractor.main_ractor); +} + +void +rb_gc_impl_init(void) +{ + VALUE gc_constants = rb_hash_new(); + rb_hash_aset(gc_constants, ID2SYM(rb_intern("DEBUG")), GC_DEBUG ? Qtrue : Qfalse); + 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("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)); + rb_hash_aset(gc_constants, ID2SYM(rb_intern("SIZE_POOL_COUNT")), LONG2FIX(SIZE_POOL_COUNT)); + rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVARGC_MAX_ALLOCATE_SIZE")), LONG2FIX(size_pool_slot_size(SIZE_POOL_COUNT - 1))); + rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_OLD_AGE")), LONG2FIX(RVALUE_OLD_AGE)); + if (RB_BUG_INSTEAD_OF_RB_MEMERROR+0) { + rb_hash_aset(gc_constants, ID2SYM(rb_intern("RB_BUG_INSTEAD_OF_RB_MEMERROR")), Qtrue); + } + OBJ_FREEZE(gc_constants); + /* Internal constants in the garbage collector. */ + rb_define_const(rb_mGC, "INTERNAL_CONSTANTS", gc_constants); + + if (GC_COMPACTION_SUPPORTED) { + rb_define_singleton_method(rb_mGC, "compact", gc_compact, 0); + rb_define_singleton_method(rb_mGC, "auto_compact", gc_get_auto_compact, 0); + rb_define_singleton_method(rb_mGC, "auto_compact=", gc_set_auto_compact, 1); + rb_define_singleton_method(rb_mGC, "latest_compact_info", gc_compact_stats, 0); + rb_define_singleton_method(rb_mGC, "verify_compaction_references", gc_verify_compaction_references, -1); + } + else { + rb_define_singleton_method(rb_mGC, "compact", rb_f_notimplement, 0); + rb_define_singleton_method(rb_mGC, "auto_compact", rb_f_notimplement, 0); + rb_define_singleton_method(rb_mGC, "auto_compact=", rb_f_notimplement, 1); + rb_define_singleton_method(rb_mGC, "latest_compact_info", rb_f_notimplement, 0); + rb_define_singleton_method(rb_mGC, "verify_compaction_references", rb_f_notimplement, -1); + } + + /* internal methods */ + rb_define_singleton_method(rb_mGC, "verify_internal_consistency", gc_verify_internal_consistency_m, 0); + +#if MALLOC_ALLOCATED_SIZE + rb_define_singleton_method(rb_mGC, "malloc_allocated_size", gc_malloc_allocated_size, 0); + rb_define_singleton_method(rb_mGC, "malloc_allocations", gc_malloc_allocations, 0); +#endif + + VALUE rb_mProfiler = rb_define_module_under(rb_mGC, "Profiler"); + rb_define_singleton_method(rb_mProfiler, "enabled?", gc_profile_enable_get, 0); + rb_define_singleton_method(rb_mProfiler, "enable", gc_profile_enable, 0); + rb_define_singleton_method(rb_mProfiler, "raw_data", gc_profile_record_get, 0); + rb_define_singleton_method(rb_mProfiler, "disable", gc_profile_disable, 0); + rb_define_singleton_method(rb_mProfiler, "clear", gc_profile_clear, 0); + rb_define_singleton_method(rb_mProfiler, "result", gc_profile_result, 0); + rb_define_singleton_method(rb_mProfiler, "report", gc_profile_report, -1); + rb_define_singleton_method(rb_mProfiler, "total_time", gc_profile_total_time, 0); + + { + VALUE opts; + /* \GC build options */ + rb_define_const(rb_mGC, "OPTS", opts = rb_ary_new()); +#define OPT(o) if (o) rb_ary_push(opts, rb_interned_str(#o, sizeof(#o) - 1)) + OPT(GC_DEBUG); + OPT(USE_RGENGC); + OPT(RGENGC_DEBUG); + OPT(RGENGC_CHECK_MODE); + OPT(RGENGC_PROFILE); + OPT(RGENGC_ESTIMATE_OLDMALLOC); + OPT(GC_PROFILE_MORE_DETAIL); + OPT(GC_ENABLE_LAZY_SWEEP); + OPT(CALC_EXACT_MALLOC_SIZE); + OPT(MALLOC_ALLOCATED_SIZE); + OPT(MALLOC_ALLOCATED_SIZE_CHECK); + OPT(GC_PROFILE_DETAIL_MEMORY); + OPT(GC_COMPACTION_SUPPORTED); +#undef OPT + OBJ_FREEZE(opts); + } +} diff --git a/gc/gc.h b/gc/gc.h new file mode 100644 index 00000000000000..5f9a7b218b2474 --- /dev/null +++ b/gc/gc.h @@ -0,0 +1,42 @@ +#ifndef GC_GC_H +#define GC_GC_H + +#include "ruby/ruby.h" + +RUBY_SYMBOL_EXPORT_BEGIN +unsigned int rb_gc_vm_lock(void); +void rb_gc_vm_unlock(unsigned int lev); +unsigned int rb_gc_cr_lock(void); +void rb_gc_cr_unlock(unsigned int lev); +unsigned int rb_gc_vm_lock_no_barrier(void); +void rb_gc_vm_unlock_no_barrier(unsigned int lev); +void rb_gc_vm_barrier(void); +size_t rb_gc_obj_optimal_size(VALUE obj); +void rb_gc_mark_children(void *objspace, VALUE obj); +void rb_gc_update_object_references(void *objspace, VALUE obj); +void rb_gc_update_vm_references(void *objspace); +void rb_gc_reachable_objects_from_callback(VALUE obj); +void rb_gc_event_hook(VALUE obj, rb_event_flag_t event); +void *rb_gc_get_objspace(void); +size_t rb_size_mul_or_raise(size_t x, size_t y, VALUE exc); +void rb_gc_run_obj_finalizer(VALUE objid, long count, VALUE (*callback)(long i, void *data), void *data); +void rb_gc_set_pending_interrupt(void); +void rb_gc_unset_pending_interrupt(void); +bool rb_gc_obj_free(void *objspace, VALUE obj); +void rb_gc_mark_roots(void *objspace, const char **categoryp); +void rb_gc_ractor_newobj_cache_foreach(void (*func)(void *cache, void *data), void *data); +bool rb_gc_multi_ractor_p(void); +void rb_objspace_reachable_objects_from_root(void (func)(const char *category, VALUE, void *), void *passing_data); +void rb_objspace_reachable_objects_from(VALUE obj, void (func)(VALUE, void *), void *data); +void rb_obj_info_dump(VALUE obj); +const char *rb_obj_info(VALUE obj); +bool rb_gc_shutdown_call_finalizer_p(VALUE obj); +uint32_t rb_gc_get_shape(VALUE obj); +void rb_gc_set_shape(VALUE obj, uint32_t shape_id); +uint32_t rb_gc_rebuild_shape(VALUE obj, size_t size_pool_id); +size_t rb_obj_memsize_of(VALUE obj); +RUBY_SYMBOL_EXPORT_END + +void rb_ractor_finish_marking(void); + +#endif diff --git a/gc/gc_impl.h b/gc/gc_impl.h new file mode 100644 index 00000000000000..ab21e4e81b7bd8 --- /dev/null +++ b/gc/gc_impl.h @@ -0,0 +1,83 @@ +#ifndef GC_GC_IMPL_H +#define GC_GC_IMPL_H + +#include "ruby/ruby.h" + +// Bootup +void *rb_gc_impl_objspace_alloc(void); +void rb_gc_impl_objspace_init(void *objspace_ptr); +void rb_gc_impl_objspace_free(void *objspace_ptr); +void *rb_gc_impl_ractor_cache_alloc(void *objspace_ptr); +void rb_gc_impl_ractor_cache_free(void *objspace_ptr, void *cache); +void rb_gc_impl_set_params(void *objspace_ptr); +void rb_gc_impl_init(void); +void rb_gc_impl_initial_stress_set(VALUE flag); +size_t *rb_gc_impl_size_pool_sizes(void *objspace_ptr); +// Shutdown +void rb_gc_impl_shutdown_free_objects(void *objspace_ptr); +// GC +void rb_gc_impl_start(void *objspace_ptr, bool full_mark, bool immediate_mark, bool immediate_sweep, bool compact); +bool rb_gc_impl_during_gc_p(void *objspace_ptr); +void rb_gc_impl_prepare_heap(void *objspace_ptr); +void rb_gc_impl_gc_enable(void *objspace_ptr); +void rb_gc_impl_gc_disable(void *objspace_ptr, bool finish_current_gc); +bool rb_gc_impl_gc_enabled_p(void *objspace_ptr); +void rb_gc_impl_stress_set(void *objspace_ptr, VALUE flag); +VALUE rb_gc_impl_stress_get(void *objspace_ptr); +VALUE rb_gc_impl_config_get(void *objspace_ptr); +VALUE rb_gc_impl_config_set(void *objspace_ptr, VALUE hash); +// Object allocation +VALUE rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, bool wb_protected, size_t alloc_size); +size_t rb_gc_impl_obj_slot_size(VALUE obj); +size_t rb_gc_impl_size_pool_id_for_size(void *objspace_ptr, size_t size); +bool rb_gc_impl_size_allocatable_p(size_t size); +// Malloc +void *rb_gc_impl_malloc(void *objspace_ptr, size_t size); +void *rb_gc_impl_calloc(void *objspace_ptr, size_t size); +void *rb_gc_impl_realloc(void *objspace_ptr, void *ptr, size_t new_size, size_t old_size); +void rb_gc_impl_free(void *objspace_ptr, void *ptr, size_t old_size); +void rb_gc_impl_adjust_memory_usage(void *objspace_ptr, ssize_t diff); +// Marking +void rb_gc_impl_mark(void *objspace_ptr, VALUE obj); +void rb_gc_impl_mark_and_move(void *objspace_ptr, VALUE *ptr); +void rb_gc_impl_mark_and_pin(void *objspace_ptr, VALUE obj); +void rb_gc_impl_mark_maybe(void *objspace_ptr, VALUE obj); +void rb_gc_impl_mark_weak(void *objspace_ptr, VALUE *ptr); +void rb_gc_impl_remove_weak(void *objspace_ptr, VALUE parent_obj, VALUE *ptr); +void rb_gc_impl_objspace_mark(void *objspace_ptr); +// Compaction +bool rb_gc_impl_object_moved_p(void *objspace_ptr, VALUE obj); +VALUE rb_gc_impl_location(void *objspace_ptr, VALUE value); +// Write barriers +void rb_gc_impl_writebarrier(void *objspace_ptr, VALUE a, VALUE b); +void rb_gc_impl_writebarrier_unprotect(void *objspace_ptr, VALUE obj); +void rb_gc_impl_writebarrier_remember(void *objspace_ptr, VALUE obj); +// Heap walking +void rb_gc_impl_each_objects(void *objspace_ptr, int (*callback)(void *, void *, size_t, void *), void *data); +void rb_gc_impl_each_object(void *objspace_ptr, void (*func)(VALUE obj, void *data), void *data); +// Finalizers +void rb_gc_impl_make_zombie(void *objspace_ptr, VALUE obj, void (*dfree)(void *), void *data); +VALUE rb_gc_impl_define_finalizer(void *objspace_ptr, VALUE obj, VALUE block); +VALUE rb_gc_impl_undefine_finalizer(void *objspace_ptr, VALUE obj); +void rb_gc_impl_copy_finalizer(void *objspace_ptr, VALUE dest, VALUE obj); +void rb_gc_impl_shutdown_call_finalizer(void *objspace_ptr); +// Object ID +VALUE rb_gc_impl_object_id(void *objspace_ptr, VALUE obj); +VALUE rb_gc_impl_object_id_to_ref(void *objspace_ptr, VALUE object_id); +// Statistics +VALUE rb_gc_impl_set_measure_total_time(void *objspace_ptr, VALUE flag); +VALUE rb_gc_impl_get_measure_total_time(void *objspace_ptr); +VALUE rb_gc_impl_get_profile_total_time(void *objspace_ptr); +size_t rb_gc_impl_gc_count(void *objspace_ptr); +VALUE rb_gc_impl_latest_gc_info(void *objspace_ptr, VALUE key); +size_t rb_gc_impl_stat(void *objspace_ptr, VALUE hash_or_sym); +size_t rb_gc_impl_stat_heap(void *objspace_ptr, VALUE heap_name, VALUE hash_or_sym); +// Miscellaneous +size_t rb_gc_impl_obj_flags(void *objspace_ptr, VALUE obj, ID* flags, size_t max); +bool rb_gc_impl_pointer_to_heap_p(void *objspace_ptr, const void *ptr); +bool rb_gc_impl_garbage_object_p(void *objspace_ptr, VALUE obj); +void rb_gc_impl_set_event_hook(void *objspace_ptr, const rb_event_flag_t event); +void rb_gc_impl_copy_attributes(void *objspace_ptr, VALUE dest, VALUE obj); + + +#endif diff --git a/gems/bundled_gems b/gems/bundled_gems index c13421d95f29d1..2dfeb3288696ba 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -10,7 +10,7 @@ minitest 5.24.1 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.3.1 https://github.com/ruby/rexml +rexml 3.3.2 https://github.com/ruby/rexml rss 0.3.0 https://github.com/ruby/rss net-ftp 0.3.7 https://github.com/ruby/net-ftp net-imap 0.4.14 https://github.com/ruby/net-imap @@ -18,7 +18,7 @@ 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 prime 0.1.2 https://github.com/ruby/prime -rbs 3.5.1 https://github.com/ruby/rbs 97e12999b14dc36e374ed30a03fca58af62dfd90 +rbs 3.5.2 https://github.com/ruby/rbs typeprof 0.21.11 https://github.com/ruby/typeprof b19a6416da3a05d57fadd6ffdadb382b6d236ca5 debug 1.9.2 https://github.com/ruby/debug racc 1.8.0 https://github.com/ruby/racc diff --git a/glospace.c b/glospace.c index 17ffebf5137614..62d5066cfe9978 100644 --- a/glospace.c +++ b/glospace.c @@ -758,7 +758,7 @@ begin_global_gc_section(rb_vm_t *vm, rb_objspace_t *objspace, unsigned int *lev) VM_COND_AND_BARRIER_WAIT(vm, vm->global_gc_finished, !vm->global_gc_underway); local_data->running_global_gc = true; vm->global_gc_underway = true; - rb_vm_barrier(); + rb_gc_vm_barrier(); } void diff --git a/glospace.h b/glospace.h index 79b5fefe5e361d..e49ad917463dc3 100644 --- a/glospace.h +++ b/glospace.h @@ -1313,9 +1313,9 @@ rb_nonexistent_id(VALUE objid) { rb_global_space_t *global_space = &rb_global_space; rb_native_mutex_lock(&global_space->next_object_id_lock); - VALUE not_id_value = rb_int_ge(objid, ULL2NUM(global_space->next_object_id)); + bool not_id_value = rb_funcall(object_id, rb_intern(">="), 1, ULL2NUM(global_space->next_object_id)); rb_native_mutex_unlock(&global_space->next_object_id_lock); - return !!not_id_value; + return not_id_value; } static VALUE @@ -1439,4 +1439,23 @@ rb_gc_writebarrier_multi_objspace(VALUE a, VALUE b, rb_objspace_t *current_objsp WITH_OBJSPACE_OF_VALUE_LEAVE(b_objspace); } +#define POSSIBLE_USAGE_OF_BORROWING_PAGE_BEGIN(objspace, page) { \ + rb_ractor_t *_cr = GET_RACTOR(); \ + int _size_pool_idx = get_size_pool_idx(objspace, page->size_pool); \ + bool _using_borrowable_page = false; \ + if (!objspace->freeing_all) { \ + rb_borrowing_sync_lock(_cr); \ + if (current_borrowable_page(_cr, _size_pool_idx) == page) { \ + lock_own_borrowable_page(_cr, _size_pool_idx); \ + _using_borrowable_page = true; \ + } \ + rb_borrowing_sync_unlock(_cr); \ + } + +#define POSSIBLE_USAGE_OF_BORROWING_PAGE_END() \ + if (_using_borrowable_page) { \ + unlock_own_borrowable_page(_cr, _size_pool_idx); \ + } \ +} + #endif /* RUBY_GLOSPACE_H */ diff --git a/hash.c b/hash.c index dd45f95dfc9bef..677307e2cbc886 100644 --- a/hash.c +++ b/hash.c @@ -49,6 +49,7 @@ #include "ruby/thread_native.h" #include "ruby/ractor.h" #include "vm_sync.h" +#include "builtin.h" /* Flags of RHash * @@ -1763,58 +1764,31 @@ set_proc_default(VALUE hash, VALUE proc) RHASH_SET_IFNONE(hash, proc); } -/* - * call-seq: - * Hash.new(default_value = nil) -> new_hash - * Hash.new {|hash, key| ... } -> new_hash - * - * Returns a new empty +Hash+ object. - * - * The initial default value and initial default proc for the new hash - * depend on which form above was used. See {Default Values}[rdoc-ref:Hash@Default+Values]. - * - * If neither an argument nor a block given, - * initializes both the default value and the default proc to nil: - * h = Hash.new - * h.default # => nil - * h.default_proc # => nil - * - * If argument default_value given but no block given, - * initializes the default value to the given default_value - * and the default proc to nil: - * h = Hash.new(false) - * h.default # => false - * h.default_proc # => nil - * - * If a block given but no argument, stores the block as the default proc - * and sets the default value to nil: - * h = Hash.new {|hash, key| "Default value for #{key}" } - * h.default # => nil - * h.default_proc.class # => Proc - * h[:nosuch] # => "Default value for nosuch" - */ - static VALUE -rb_hash_initialize(int argc, VALUE *argv, VALUE hash) +rb_hash_init(rb_execution_context_t *ec, VALUE hash, VALUE capa_value, VALUE ifnone_unset, VALUE ifnone, VALUE block) { rb_hash_modify(hash); - if (rb_block_given_p()) { - rb_check_arity(argc, 0, 0); - SET_PROC_DEFAULT(hash, rb_block_proc()); + if (capa_value != INT2FIX(0)) { + long capa = NUM2LONG(capa_value); + if (capa > 0 && RHASH_SIZE(hash) == 0 && RHASH_AR_TABLE_P(hash)) { + hash_st_table_init(hash, &objhash, capa); + } } - else { - rb_check_arity(argc, 0, 1); - VALUE options, ifnone; - rb_scan_args(argc, argv, "01:", &ifnone, &options); - if (NIL_P(ifnone) && !NIL_P(options)) { - ifnone = options; - rb_warn_deprecated_to_remove("3.4", "Calling Hash.new with keyword arguments", "Hash.new({ key: value })"); + if (!NIL_P(block)) { + if (ifnone_unset != Qtrue) { + rb_check_arity(1, 0, 0); } - RHASH_SET_IFNONE(hash, ifnone); + else { + SET_PROC_DEFAULT(hash, block); + } + } + else { + RHASH_SET_IFNONE(hash, ifnone_unset == Qtrue ? Qnil : ifnone); } + hash_verify(hash); return hash; } @@ -7151,7 +7125,6 @@ Init_Hash(void) rb_define_alloc_func(rb_cHash, empty_hash_alloc); rb_define_singleton_method(rb_cHash, "[]", rb_hash_s_create, -1); rb_define_singleton_method(rb_cHash, "try_convert", rb_hash_s_try_convert, 1); - rb_define_method(rb_cHash, "initialize", rb_hash_initialize, -1); rb_define_method(rb_cHash, "initialize_copy", rb_hash_replace, 1); rb_define_method(rb_cHash, "rehash", rb_hash_rehash, 0); @@ -7478,3 +7451,5 @@ Init_Hash(void) HASH_ASSERT(sizeof(ar_hint_t) * RHASH_AR_TABLE_MAX_SIZE == sizeof(VALUE)); } + +#include "hash.rbinc" diff --git a/hash.rb b/hash.rb new file mode 100644 index 00000000000000..3103c6260bf632 --- /dev/null +++ b/hash.rb @@ -0,0 +1,40 @@ +class Hash + # call-seq: + # Hash.new(default_value = nil) -> new_hash + # Hash.new(default_value = nil, capacity: size) -> new_hash + # Hash.new {|hash, key| ... } -> new_hash + # Hash.new(capacity: size) {|hash, key| ... } -> new_hash + # + # Returns a new empty +Hash+ object. + # + # The initial default value and initial default proc for the new hash + # depend on which form above was used. See {Default Values}[rdoc-ref:Hash@Default+Values]. + # + # If neither an argument nor a block is given, + # initializes both the default value and the default proc to nil: + # h = Hash.new + # h.default # => nil + # h.default_proc # => nil + # + # If argument default_value is given but no block is given, + # initializes the default value to the given default_value + # and the default proc to nil: + # h = Hash.new(false) + # h.default # => false + # h.default_proc # => nil + # + # If a block is given but no default_value, stores the block as the default proc + # and sets the default value to nil: + # h = Hash.new {|hash, key| "Default value for #{key}" } + # h.default # => nil + # h.default_proc.class # => Proc + # h[:nosuch] # => "Default value for nosuch" + # + # If both a block and a default_value are given, raises an +ArgumentError+ + # + # If the optional keyword argument +capacity+ is given, the hash will be allocated + # with enough capacity to accomodate this many keys without having to be resized. + def initialize(ifnone = (ifnone_unset = true), capacity: 0, &block) + Primitive.rb_hash_init(capacity, ifnone_unset, ifnone, block) + end +end diff --git a/imemo.c b/imemo.c index d89af227c35409..ed2b88a1b90e46 100644 --- a/imemo.c +++ b/imemo.c @@ -234,7 +234,7 @@ rb_cc_table_mark(VALUE klass) static bool moved_or_living_object_strictly_p(VALUE obj) { - return obj && (rb_objspace_markable_object_p(obj) || BUILTIN_TYPE(obj) == T_MOVED); + return obj && (!rb_objspace_garbage_object_p(obj) || BUILTIN_TYPE(obj) == T_MOVED); } static void @@ -477,7 +477,7 @@ vm_ccs_free(struct rb_class_cc_entries *ccs, int alive, VALUE klass) if (!alive) { void *ptr = asan_unpoison_object_temporary((VALUE)cc); // ccs can be free'ed. - if (rb_objspace_markable_object_p((VALUE)cc) && + if (!rb_objspace_garbage_object_p((VALUE)cc) && IMEMO_TYPE_P(cc, imemo_callcache) && cc->klass == klass) { // OK. maybe target cc. diff --git a/include/ruby/internal/fl_type.h b/include/ruby/internal/fl_type.h index 0a05166784b4e0..2a8bdccd46becb 100644 --- a/include/ruby/internal/fl_type.h +++ b/include/ruby/internal/fl_type.h @@ -906,6 +906,14 @@ RB_OBJ_FROZEN(VALUE obj) } RUBY_SYMBOL_EXPORT_BEGIN +/** + * Prevents further modifications to the given object. ::rb_eFrozenError shall + * be raised if modification is attempted. + * + * @param[out] x Object in question. + * @exception rb_eNoMemError Failed to allocate memory for the frozen + * representation of the object. + */ void rb_obj_freeze_inline(VALUE obj); RUBY_SYMBOL_EXPORT_END diff --git a/include/ruby/internal/intern/array.h b/include/ruby/internal/intern/array.h index 1909fdf17b822c..b2cc6b132dae8a 100644 --- a/include/ruby/internal/intern/array.h +++ b/include/ruby/internal/intern/array.h @@ -144,7 +144,13 @@ void rb_ary_free(VALUE ary); */ void rb_ary_modify(VALUE ary); -/** @alias{rb_obj_freeze} */ +/** + * Freeze an array, preventing further modifications. The underlying buffer may + * be shrunk before freezing to conserve memory. + * + * @param[out] obj Object assumed to be an array to freeze. + * @see RB_OBJ_FREEZE() + */ VALUE rb_ary_freeze(VALUE obj); RBIMPL_ATTR_PURE() diff --git a/include/ruby/internal/scan_args.h b/include/ruby/internal/scan_args.h index 1ed2bf636874f3..2dbc1ee7bc39b9 100644 --- a/include/ruby/internal/scan_args.h +++ b/include/ruby/internal/scan_args.h @@ -75,7 +75,7 @@ * Pass keywords if current method is called with keywords, useful for argument * delegation */ -#define RB_PASS_CALLED_KEYWORDS rb_keyword_given_p() +#define RB_PASS_CALLED_KEYWORDS !!rb_keyword_given_p() /** @} */ diff --git a/include/ruby/internal/xmalloc.h b/include/ruby/internal/xmalloc.h index 57552e4e7decf0..132bc478ce26ec 100644 --- a/include/ruby/internal/xmalloc.h +++ b/include/ruby/internal/xmalloc.h @@ -283,110 +283,6 @@ void ruby_xfree(void *ptr) RBIMPL_ATTR_NOEXCEPT(free(ptr)) ; -#if USE_GC_MALLOC_OBJ_INFO_DETAILS -# define ruby_xmalloc(s1) ruby_xmalloc_with_location(s1, __FILE__, __LINE__) -# define ruby_xmalloc2(s1, s2) ruby_xmalloc2_with_location(s1, s2, __FILE__, __LINE__) -# define ruby_xcalloc(s1, s2) ruby_xcalloc_with_location(s1, s2, __FILE__, __LINE__) -# define ruby_xrealloc(ptr, s1) ruby_xrealloc_with_location(ptr, s1, __FILE__, __LINE__) -# define ruby_xrealloc2(ptr, s1, s2) ruby_xrealloc2_with_location(ptr, s1, s2, __FILE__, __LINE__) - -RBIMPL_ATTR_NODISCARD() -RBIMPL_ATTR_RESTRICT() -RBIMPL_ATTR_RETURNS_NONNULL() -RBIMPL_ATTR_ALLOC_SIZE((1)) -void *ruby_xmalloc_body(size_t size) -RBIMPL_ATTR_NOEXCEPT(malloc(size)) -; - -RBIMPL_ATTR_NODISCARD() -RBIMPL_ATTR_RESTRICT() -RBIMPL_ATTR_RETURNS_NONNULL() -RBIMPL_ATTR_ALLOC_SIZE((1,2)) -void *ruby_xmalloc2_body(size_t nelems, size_t elemsiz) -RBIMPL_ATTR_NOEXCEPT(malloc(nelems * elemsiz)) -; - -RBIMPL_ATTR_NODISCARD() -RBIMPL_ATTR_RESTRICT() -RBIMPL_ATTR_RETURNS_NONNULL() -RBIMPL_ATTR_ALLOC_SIZE((1,2)) -void *ruby_xcalloc_body(size_t nelems, size_t elemsiz) -RBIMPL_ATTR_NOEXCEPT(calloc(nelems, elemsiz)) -; - -RBIMPL_ATTR_NODISCARD() -RBIMPL_ATTR_RETURNS_NONNULL() -RBIMPL_ATTR_ALLOC_SIZE((2)) -void *ruby_xrealloc_body(void *ptr, size_t newsiz) -RBIMPL_ATTR_NOEXCEPT(realloc(ptr, newsiz)) -; - -RBIMPL_ATTR_NODISCARD() -RBIMPL_ATTR_RETURNS_NONNULL() -RBIMPL_ATTR_ALLOC_SIZE((2,3)) -void *ruby_xrealloc2_body(void *ptr, size_t newelems, size_t newsiz) -RBIMPL_ATTR_NOEXCEPT(realloc(ptr, newelems * newsiz)) -; - -RUBY_EXTERN const char *ruby_malloc_info_file; -RUBY_EXTERN int ruby_malloc_info_line; - -static inline void * -ruby_xmalloc_with_location(size_t s, const char *file, int line) -{ - void *ptr; - ruby_malloc_info_file = file; - ruby_malloc_info_line = line; - ptr = ruby_xmalloc_body(s); - ruby_malloc_info_file = NULL; - return ptr; -} - -static inline void * -ruby_xmalloc2_with_location(size_t s1, size_t s2, const char *file, int line) -{ - void *ptr; - ruby_malloc_info_file = file; - ruby_malloc_info_line = line; - ptr = ruby_xmalloc2_body(s1, s2); - ruby_malloc_info_file = NULL; - return ptr; -} - -static inline void * -ruby_xcalloc_with_location(size_t s1, size_t s2, const char *file, int line) -{ - void *ptr; - ruby_malloc_info_file = file; - ruby_malloc_info_line = line; - ptr = ruby_xcalloc_body(s1, s2); - ruby_malloc_info_file = NULL; - return ptr; -} - -static inline void * -ruby_xrealloc_with_location(void *ptr, size_t s, const char *file, int line) -{ - void *rptr; - ruby_malloc_info_file = file; - ruby_malloc_info_line = line; - rptr = ruby_xrealloc_body(ptr, s); - ruby_malloc_info_file = NULL; - return rptr; -} - -static inline void * -ruby_xrealloc2_with_location(void *ptr, size_t s1, size_t s2, const char *file, int line) -{ - void *rptr; - ruby_malloc_info_file = file; - ruby_malloc_info_line = line; - rptr = ruby_xrealloc2_body(ptr, s1, s2); - ruby_malloc_info_file = NULL; - return rptr; -} -#endif - RBIMPL_SYMBOL_EXPORT_END() #endif /* RBIMPL_XMALLOC_H */ diff --git a/inits.c b/inits.c index 677a384f9a37c5..ef0805d2ee2aac 100644 --- a/inits.c +++ b/inits.c @@ -94,6 +94,7 @@ rb_call_builtin_inits(void) BUILTIN(pack); BUILTIN(warning); BUILTIN(array); + BUILTIN(hash); BUILTIN(kernel); BUILTIN(symbol); BUILTIN(timev); diff --git a/internal/gc.h b/internal/gc.h index 54b234c9ef9ef0..30acb75b900f78 100644 --- a/internal/gc.h +++ b/internal/gc.h @@ -232,12 +232,6 @@ void rb_ractor_object_graph_safety_withdraw(rb_ractor_t *r, unsigned int reason) RB_OBJ_WRITE(old, _slot, young); \ } while (0) -// We use SIZE_POOL_COUNT number of shape IDs for transitions out of different size pools -// The next available shape ID will be the SPECIAL_CONST_SHAPE_ID -#ifndef SIZE_POOL_COUNT -# define SIZE_POOL_COUNT 5 -#endif - /* Used in places that could malloc during, which can cause the GC to run. We * need to temporarily disable the GC to allow the malloc to happen. * Allocating memory during GC is a bad idea, so use this only when absolutely @@ -249,16 +243,6 @@ void rb_ractor_object_graph_safety_withdraw(rb_ractor_t *r, unsigned int reason) #define DURING_GC_COULD_MALLOC_REGION_END() \ if (_already_disabled == Qfalse) rb_gc_enable() -typedef struct ractor_newobj_size_pool_cache { - struct RVALUE *freelist; - struct heap_page *using_page; -} rb_ractor_newobj_size_pool_cache_t; - -typedef struct ractor_newobj_cache { - size_t incremental_mark_step_allocated_slots; - rb_ractor_newobj_size_pool_cache_t size_pool_caches[SIZE_POOL_COUNT]; -} rb_ractor_newobj_cache_t; - /* gc.c */ extern int ruby_disable_gc; RUBY_ATTR_MALLOC void *ruby_mimmalloc(size_t size); @@ -266,8 +250,8 @@ 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); -VALUE rb_objspace_gc_enable(struct rb_objspace *); -VALUE rb_objspace_gc_disable(struct rb_objspace *); +VALUE rb_objspace_gc_enable(void *objspace); +VALUE rb_objspace_gc_disable(void *objspace); struct rb_objspace *get_objspace_of_value(VALUE v); void ruby_gc_set_params(void); void rb_gc_copy_attributes(VALUE dest, VALUE obj); @@ -282,12 +266,13 @@ RUBY_ATTR_MALLOC void *rb_xcalloc_mul_add_mul(size_t, size_t, size_t, size_t); static inline void *ruby_sized_xrealloc_inlined(void *ptr, size_t new_size, size_t old_size) RUBY_ATTR_RETURNS_NONNULL RUBY_ATTR_ALLOC_SIZE((2)); static inline void *ruby_sized_xrealloc2_inlined(void *ptr, size_t new_count, size_t elemsiz, size_t old_count) RUBY_ATTR_RETURNS_NONNULL RUBY_ATTR_ALLOC_SIZE((2, 3)); static inline void ruby_sized_xfree_inlined(void *ptr, size_t size); -void rb_gc_ractor_newobj_cache_clear(rb_ractor_newobj_cache_t *newobj_cache); + +void *rb_gc_ractor_cache_alloc(void); +void rb_gc_ractor_cache_free(void *cache); + bool rb_gc_size_allocatable_p(size_t size); size_t *rb_gc_size_pool_sizes(void); size_t rb_gc_size_pool_id_for_size(size_t size); -int rb_objspace_garbage_object_p(VALUE obj); -bool rb_gc_is_ptr_to_obj(const void *ptr); int rb_during_local_gc(void); int rb_during_global_gc(void); @@ -311,8 +296,8 @@ RUBY_SYMBOL_EXPORT_BEGIN /* exports for objspace module */ void rb_objspace_reachable_objects_from(VALUE obj, void (func)(VALUE, void *), void *data); void rb_objspace_reachable_objects_from_root(void (func)(const char *category, VALUE, void *), void *data); -int rb_objspace_markable_object_p(VALUE obj); int rb_objspace_internal_object_p(VALUE obj); +int rb_objspace_garbage_object_p(VALUE obj); rb_ractor_t *get_ractor_of_value(VALUE obj); bool rb_contained_in_objspace_p(struct rb_objspace *objspace, VALUE obj); @@ -331,7 +316,6 @@ const char *rb_objspace_data_type_name(VALUE obj); VALUE rb_wb_protected_newobj_of(struct rb_execution_context_struct *, VALUE, VALUE, size_t); VALUE rb_wb_unprotected_newobj_of(VALUE, VALUE, size_t); size_t rb_obj_memsize_of(VALUE); -void rb_gc_verify_internal_consistency(void); size_t rb_obj_gc_flags(VALUE, ID[], size_t); void rb_gc_mark_values(long n, const VALUE *values); void rb_gc_mark_vm_stack_values(long n, const VALUE *values); @@ -339,6 +323,10 @@ void rb_gc_update_values(long n, VALUE *values); void *ruby_sized_xrealloc(void *ptr, size_t new_size, size_t old_size) RUBY_ATTR_RETURNS_NONNULL RUBY_ATTR_ALLOC_SIZE((2)); void *ruby_sized_xrealloc2(void *ptr, size_t new_count, size_t element_size, size_t old_count) RUBY_ATTR_RETURNS_NONNULL RUBY_ATTR_ALLOC_SIZE((2, 3)); void ruby_sized_xfree(void *x, size_t size); + +#if USE_SHARED_GC +void ruby_load_external_gc_from_argv(int argc, char **argv); +#endif RUBY_SYMBOL_EXPORT_END int rb_ec_stack_check(struct rb_execution_context_struct *ec); diff --git a/internal/imemo.h b/internal/imemo.h index 36c07769874877..7420909a140546 100644 --- a/internal/imemo.h +++ b/internal/imemo.h @@ -87,6 +87,7 @@ struct vm_ifunc { const void *data; struct vm_ifunc_argc argc; }; +#define IFUNC_YIELD_OPTIMIZABLE IMEMO_FL_USER0 struct rb_imemo_tmpbuf_struct { VALUE flags; diff --git a/internal/vm.h b/internal/vm.h index d640434344ea87..0dc13b467ac778 100644 --- a/internal/vm.h +++ b/internal/vm.h @@ -78,6 +78,8 @@ VALUE rb_lambda_call(VALUE obj, ID mid, int argc, const VALUE *argv, rb_block_call_func_t bl_proc, int min_argc, int max_argc, VALUE data2); void rb_check_stack_overflow(void); +#define RB_BLOCK_NO_USE_PACKED_ARGS 2 +VALUE rb_block_call2(VALUE obj, ID mid, int argc, const VALUE *argv, rb_block_call_func_t bl_proc, VALUE data2, long flags); #if USE_YJIT /* vm_exec.c */ diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index d332fceb545cd1..192e2e2d0ce4bf 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -81,7 +81,7 @@ def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, opti @resolved_bundler_version = nil @locked_ruby_version = nil - @new_platform = nil + @new_platforms = [] @removed_platform = nil if lockfile_exists? @@ -367,6 +367,10 @@ def to_lock end def ensure_equivalent_gemfile_and_lockfile(explicit_flag = false) + return unless Bundler.frozen_bundle? + + raise ProductionError, "Frozen mode is set, but there's no lockfile" unless lockfile_exists? + added = [] deleted = [] changed = [] @@ -395,7 +399,7 @@ def ensure_equivalent_gemfile_and_lockfile(explicit_flag = false) changed << "* #{name} from `#{lockfile_source_name}` to `#{gemfile_source_name}`" end - reason = change_reason + reason = nothing_changed? ? "some dependencies were deleted from your gemfile" : change_reason msg = String.new msg << "#{reason.capitalize.strip}, but the lockfile can't be updated because frozen mode is set" msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any? @@ -453,8 +457,10 @@ def validate_platforms! end def add_platform(platform) - @new_platform ||= !@platforms.include?(platform) - @platforms |= [platform] + return if @platforms.include?(platform) + + @new_platforms << platform + @platforms << platform end def remove_platform(platform) @@ -478,7 +484,7 @@ def nothing_changed? !@source_changes && !@dependency_changes && - !@new_platform && + @new_platforms.empty? && !@path_changes && !@local_changes && !@missing_lockfile_dep && @@ -638,8 +644,6 @@ def start_resolution @platforms = result.add_extra_platforms!(platforms) if should_add_extra_platforms? - result.complete_platforms!(platforms) - SpecSet.new(result.for(dependencies, false, @platforms)) end @@ -701,7 +705,7 @@ def change_reason [ [@source_changes, "the list of sources changed"], [@dependency_changes, "the dependencies in your gemfile changed"], - [@new_platform, "you added a new platform to your gemfile"], + [@new_platforms.any?, "you added a new platform to your gemfile"], [@path_changes, "the gemspecs for path gems changed"], [@local_changes, "the gemspecs for git local gems changed"], [@missing_lockfile_dep, "your lock file is missing \"#{@missing_lockfile_dep}\""], @@ -1059,7 +1063,7 @@ def remove_invalid_platforms!(dependencies) platforms.reverse_each do |platform| next if local_platform == platform || - (@new_platform && platforms.last == platform) || + @new_platforms.include?(platform) || @path_changes || @dependency_changes || @locked_spec_with_invalid_deps || diff --git a/lib/bundler/env.rb b/lib/bundler/env.rb index f6cb198e383c4c..074bef6edcf0c9 100644 --- a/lib/bundler/env.rb +++ b/lib/bundler/env.rb @@ -120,7 +120,7 @@ def self.environment specs = Bundler.rubygems.find_name(name) out << [" #{name}", "(#{specs.map(&:version).join(",")})"] unless specs.empty? end - if (exe = caller.last.split(":").first)&.match? %r{(exe|bin)/bundler?\z} + if (exe = caller_locations.last.absolute_path)&.match? %r{(exe|bin)/bundler?\z} shebang = File.read(exe).lines.first shebang.sub!(/^#!\s*/, "") unless shebang.start_with?(Gem.ruby, "/usr/bin/env ruby") diff --git a/lib/bundler/gem_helpers.rb b/lib/bundler/gem_helpers.rb index de007523ec7797..70ccceb4915c64 100644 --- a/lib/bundler/gem_helpers.rb +++ b/lib/bundler/gem_helpers.rb @@ -46,19 +46,26 @@ def platform_specificity_match(spec_platform, user_platform) end module_function :platform_specificity_match - def select_best_platform_match(specs, platform) - matching = specs.select {|spec| spec.match_platform(platform) } + def select_best_platform_match(specs, platform, force_ruby: false, prefer_locked: false) + matching = if force_ruby + specs.select {|spec| spec.match_platform(Gem::Platform::RUBY) && spec.force_ruby_platform! } + else + specs.select {|spec| spec.match_platform(platform) } + end + + if prefer_locked + locked_originally = matching.select {|spec| spec.is_a?(LazySpecification) } + return locked_originally if locked_originally.any? + end sort_best_platform_match(matching, platform) end module_function :select_best_platform_match - def force_ruby_platform(specs) - matching = specs.select {|spec| spec.match_platform(Gem::Platform::RUBY) && spec.force_ruby_platform! } - - sort_best_platform_match(matching, Gem::Platform::RUBY) + def select_best_local_platform_match(specs, force_ruby: false) + select_best_platform_match(specs, local_platform, force_ruby: force_ruby).map(&:materialize_for_installation).compact end - module_function :force_ruby_platform + module_function :select_best_local_platform_match def sort_best_platform_match(matching, platform) exact = matching.select {|spec| spec.platform == platform } diff --git a/lib/bundler/injector.rb b/lib/bundler/injector.rb index 879b481339281e..c7e93c9ee00273 100644 --- a/lib/bundler/injector.rb +++ b/lib/bundler/injector.rb @@ -23,10 +23,7 @@ def initialize(deps, options = {}) # @param [Pathname] lockfile_path The lockfile in which to inject the new dependency. # @return [Array] def inject(gemfile_path, lockfile_path) - if Bundler.frozen_bundle? - # ensure the lock and Gemfile are synced - Bundler.definition.ensure_equivalent_gemfile_and_lockfile(true) - end + Bundler.definition.ensure_equivalent_gemfile_and_lockfile(true) # temporarily unfreeze Bundler.settings.temporary(deployment: false, frozen: false) do diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb index 256f0be3487cba..485782d1b410de 100644 --- a/lib/bundler/installer.rb +++ b/lib/bundler/installer.rb @@ -69,9 +69,7 @@ def run(options) Bundler.create_bundle_path ProcessLock.lock do - if Bundler.frozen_bundle? - @definition.ensure_equivalent_gemfile_and_lockfile(options[:deployment]) - end + @definition.ensure_equivalent_gemfile_and_lockfile(options[:deployment]) if @definition.dependencies.empty? Bundler.ui.warn "The Gemfile specifies no dependencies" diff --git a/lib/bundler/installer/standalone.rb b/lib/bundler/installer/standalone.rb index 5331df2e95bd63..cf5993448c5b11 100644 --- a/lib/bundler/installer/standalone.rb +++ b/lib/bundler/installer/standalone.rb @@ -58,9 +58,6 @@ def gem_path(path, spec) else SharedHelpers.relative_path_to(full_path, from: Bundler.root.join(bundler_path)) end - rescue TypeError - error_message = "#{spec.name} #{spec.version} has an invalid gemspec" - raise Gem::InvalidSpecificationException.new(error_message) end def prevent_gem_activation diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index 1a6711ea6fcacd..2be7af94a23c35 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -79,7 +79,8 @@ def setup_solver def solve_versions(root:, logger:) solver = PubGrub::VersionSolver.new(source: self, root: root, logger: logger) result = solver.solve - result.map {|package, version| version.to_specs(package) }.flatten.uniq + resolved_specs = result.map {|package, version| version.to_specs(package) }.flatten + resolved_specs |= @base.specs_compatible_with(SpecSet.new(resolved_specs)) rescue PubGrub::SolveFailure => e incompatibility = e.incompatibility @@ -254,7 +255,7 @@ def all_versions_for(package) results = filter_matching_specs(results, locked_requirement) if locked_requirement results.group_by(&:version).reduce([]) do |groups, (version, specs)| - platform_specs = package.platforms.map {|platform| select_best_platform_match(specs, platform) } + platform_specs = package.platform_specs(specs) # If package is a top-level dependency, # candidate is only valid if there are matching versions for all resolution platforms. @@ -269,14 +270,22 @@ def all_versions_for(package) next groups if platform_specs.all?(&:empty?) end - platform_specs.flatten! - ruby_specs = select_best_platform_match(specs, Gem::Platform::RUBY) - groups << Resolver::Candidate.new(version, specs: ruby_specs) if ruby_specs.any? + ruby_group = Resolver::SpecGroup.new(ruby_specs) + + unless ruby_group.empty? + platform_specs.each do |specs| + ruby_group.merge(Resolver::SpecGroup.new(specs)) + end + + groups << Resolver::Candidate.new(version, group: ruby_group, priority: -1) + next groups if package.force_ruby_platform? + end - next groups if platform_specs == ruby_specs || package.force_ruby_platform? + platform_group = Resolver::SpecGroup.new(platform_specs.flatten.uniq) + next groups if platform_group == ruby_group - groups << Resolver::Candidate.new(version, specs: platform_specs) + groups << Resolver::Candidate.new(version, group: platform_group, priority: 1) groups end @@ -431,8 +440,8 @@ def other_specs_matching_message(specs, requirement) def requirement_to_range(requirement) ranges = requirement.requirements.map do |(op, version)| - ver = Resolver::Candidate.new(version).generic! - platform_ver = Resolver::Candidate.new(version).platform_specific! + ver = Resolver::Candidate.new(version, priority: -1) + platform_ver = Resolver::Candidate.new(version, priority: 1) case op when "~>" diff --git a/lib/bundler/resolver/base.rb b/lib/bundler/resolver/base.rb index ad19eeb3f44992..b0c5c58047ed55 100644 --- a/lib/bundler/resolver/base.rb +++ b/lib/bundler/resolver/base.rb @@ -30,6 +30,10 @@ def initialize(source_requirements, dependencies, base, platforms, options) end.compact end + def specs_compatible_with(result) + @base.specs_compatible_with(result) + end + def [](name) @base[name] end diff --git a/lib/bundler/resolver/candidate.rb b/lib/bundler/resolver/candidate.rb index 9e8b9133358c69..f593fc5d6109dd 100644 --- a/lib/bundler/resolver/candidate.rb +++ b/lib/bundler/resolver/candidate.rb @@ -24,10 +24,10 @@ class Candidate attr_reader :version - def initialize(version, specs: []) - @spec_group = Resolver::SpecGroup.new(specs) + def initialize(version, group: nil, priority: -1) + @spec_group = group || SpecGroup.new([]) @version = Gem::Version.new(version) - @ruby_only = specs.map(&:platform).uniq == [Gem::Platform::RUBY] + @priority = priority end def dependencies @@ -40,18 +40,6 @@ def to_specs(package) @spec_group.to_specs(package.force_ruby_platform?) end - def generic! - @ruby_only = true - - self - end - - def platform_specific! - @ruby_only = false - - self - end - def prerelease? @version.prerelease? end @@ -61,7 +49,7 @@ def segments end def sort_obj - [@version, @ruby_only ? -1 : 1] + [@version, @priority] end def <=>(other) diff --git a/lib/bundler/resolver/package.rb b/lib/bundler/resolver/package.rb index 04613286835977..16c43d05af363c 100644 --- a/lib/bundler/resolver/package.rb +++ b/lib/bundler/resolver/package.rb @@ -25,6 +25,10 @@ def initialize(name, platforms, locked_specs:, unlock:, prerelease: false, depen @prerelease = @dependency.prerelease? || @locked_version&.prerelease? || prerelease ? :consider_first : :ignore end + def platform_specs(specs) + platforms.map {|platform| GemHelpers.select_best_platform_match(specs, platform, prefer_locked: !unlock?) } + end + def to_s @name.delete("\0") end diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb index 5cee444e5e1bd3..f950df6eda3965 100644 --- a/lib/bundler/resolver/spec_group.rb +++ b/lib/bundler/resolver/spec_group.rb @@ -3,6 +3,8 @@ module Bundler class Resolver class SpecGroup + attr_reader :specs + def initialize(specs) @specs = specs end @@ -38,17 +40,33 @@ def to_s def dependencies @dependencies ||= @specs.map do |spec| __dependencies(spec) + metadata_dependencies(spec) - end.flatten.uniq + end.flatten.uniq.sort + end + + def ==(other) + sorted_spec_names == other.sorted_spec_names + end + + def merge(other) + return false unless equivalent?(other) + + @specs |= other.specs + + true end protected def sorted_spec_names - @sorted_spec_names ||= @specs.map(&:full_name).sort + @specs.map(&:full_name).sort end private + def equivalent?(other) + name == other.name && version == other.version && source == other.source && dependencies == other.dependencies + end + def exemplary_spec @specs.first end diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb index f2537f716808fd..2a18ce1c49ed44 100644 --- a/lib/bundler/rubygems_ext.rb +++ b/lib/bundler/rubygems_ext.rb @@ -30,12 +30,42 @@ def self.freebsd_platform? end end + # Can be removed once RubyGems 3.5.14 support is dropped + unless Gem.respond_to?(:open_file_with_flock) + def self.open_file_with_flock(path, &block) + flags = File.exist?(path) ? "r+" : "a+" + + File.open(path, flags) do |io| + begin + io.flock(File::LOCK_EX) + rescue Errno::ENOSYS, Errno::ENOTSUP + end + yield io + rescue Errno::ENOLCK # NFS + if Thread.main != Thread.current + raise + else + File.open(path, flags, &block) + end + end + end + end + require "rubygems/specification" - # Can be removed once RubyGems 3.5.15 support is dropped + # Can be removed once RubyGems 3.5.14 support is dropped VALIDATES_FOR_RESOLUTION = Specification.new.respond_to?(:validate_for_resolution).freeze + # Can be removed once RubyGems 3.3.15 support is dropped + FLATTENS_REQUIRED_PATHS = Specification.new.respond_to?(:flatten_require_paths).freeze + class Specification + # Can be removed once RubyGems 3.5.15 support is dropped + correct_array_attributes = @@default_value.select {|_k,v| v.is_a?(Array) }.keys + unless @@array_attributes == correct_array_attributes + @@array_attributes = correct_array_attributes # rubocop:disable Style/ClassVars + end + require_relative "match_metadata" require_relative "match_platform" @@ -140,6 +170,27 @@ def validate_for_resolution end end + unless FLATTENS_REQUIRED_PATHS + def flatten_require_paths + return unless raw_require_paths.first.is_a?(Array) + + warn "#{name} #{version} includes a gemspec with `require_paths` set to an array of arrays. Newer versions of this gem might've already fixed this" + raw_require_paths.flatten! + end + + class << self + module RequirePathFlattener + def from_yaml(input) + spec = super(input) + spec.flatten_require_paths + spec + end + end + + prepend RequirePathFlattener + end + end + private def dependencies_to_gemfile(dependencies, group = nil) @@ -258,7 +309,7 @@ def ===(other) # cpu ([nil,"universal"].include?(@cpu) || [nil, "universal"].include?(other.cpu) || @cpu == other.cpu || - (@cpu == "arm" && other.cpu.start_with?("arm"))) && + (@cpu == "arm" && other.cpu.start_with?("armv"))) && # os @os == other.os && diff --git a/lib/bundler/rubygems_gem_installer.rb b/lib/bundler/rubygems_gem_installer.rb index 125c6874a2be96..4d4fd20fea6706 100644 --- a/lib/bundler/rubygems_gem_installer.rb +++ b/lib/bundler/rubygems_gem_installer.rb @@ -81,6 +81,26 @@ def generate_plugins end end + if Bundler.rubygems.provides?("< 3.5.15") + def generate_bin_script(filename, bindir) + bin_script_path = File.join bindir, formatted_program_filename(filename) + + Gem.open_file_with_flock("#{bin_script_path}.lock") do + require "fileutils" + FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers + + File.open(bin_script_path, "wb", 0o755) do |file| + file.write app_script_text(filename) + file.chmod(options[:prog_mode] || 0o755) + end + end + + verbose bin_script_path + + generate_windows_script filename, bindir + end + end + def build_extensions extension_cache_path = options[:bundler_extension_cache_path] extension_dir = spec.extension_dir diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb index 1302333510577a..72dead88f2741a 100644 --- a/lib/bundler/rubygems_integration.rb +++ b/lib/bundler/rubygems_integration.rb @@ -220,7 +220,7 @@ def replace_gem(specs, specs_by_name) [::Kernel.singleton_class, ::Kernel].each do |kernel_class| redefine_method(kernel_class, :gem) do |dep, *reqs| - if executables&.include?(File.basename(caller.first.split(":").first)) + if executables&.include?(File.basename(caller_locations(1, 1).first.path)) break end diff --git a/lib/bundler/runtime.rb b/lib/bundler/runtime.rb index 54aa30ce0bd0ad..4b2c54d0b6e3ae 100644 --- a/lib/bundler/runtime.rb +++ b/lib/bundler/runtime.rb @@ -10,7 +10,7 @@ def initialize(root, definition) end def setup(*groups) - @definition.ensure_equivalent_gemfile_and_lockfile if Bundler.frozen_bundle? + @definition.ensure_equivalent_gemfile_and_lockfile # Has to happen first clean_load_path diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb index 2933d284500668..5f513f3f22d2f7 100644 --- a/lib/bundler/spec_set.rb +++ b/lib/bundler/spec_set.rb @@ -71,12 +71,6 @@ def add_extra_platforms!(platforms) platforms end - def complete_platforms!(platforms) - platforms.each do |platform| - complete_platform(platform) - end - end - def validate_deps(s) s.runtime_dependencies.each do |dep| next if dep.name == "bundler" @@ -158,6 +152,12 @@ def find_by_name_and_platform(name, platform) @specs.detect {|spec| spec.name == name && spec.match_platform(platform) } end + def specs_compatible_with(other) + select do |spec| + other.valid?(spec) + end + end + def delete_by_name(name) @specs.reject! {|spec| spec.name == name } @@ -195,6 +195,10 @@ def names lookup.keys end + def valid?(s) + s.matches_current_metadata? && valid_dependencies?(s) + end + private def reset! @@ -209,7 +213,7 @@ def complete_platform(platform) spec = specs.first matching_specs = spec.source.specs.search([spec.name, spec.version]) platform_spec = GemHelpers.select_best_platform_match(matching_specs, platform).find do |s| - s.matches_current_metadata? && valid_dependencies?(s) + valid?(s) end if platform_spec @@ -273,13 +277,11 @@ def specs_for_dependency(dep, platform) specs_for_name = lookup[dep.name] return [] unless specs_for_name - matching_specs = if dep.force_ruby_platform - GemHelpers.force_ruby_platform(specs_for_name) + if platform + GemHelpers.select_best_platform_match(specs_for_name, platform, force_ruby: dep.force_ruby_platform) else - GemHelpers.select_best_platform_match(specs_for_name, platform || Bundler.local_platform) + GemHelpers.select_best_local_platform_match(specs_for_name, force_ruby: dep.force_ruby_platform) end - matching_specs.map!(&:materialize_for_installation).compact! if platform.nil? - matching_specs end def tsort_each_child(s) diff --git a/lib/error_highlight/base.rb b/lib/error_highlight/base.rb index 81b1b574defba8..1a29ee476c118a 100644 --- a/lib/error_highlight/base.rb +++ b/lib/error_highlight/base.rb @@ -62,7 +62,7 @@ def self.spot(obj, **opts) # includes "prism" when the ISEQ was compiled with the prism compiler. # In this case, we'll try to parse again with prism instead. raise unless error.message.include?("prism") - prism_find(loc, **opts) + prism_find(loc) end Spotter.new(node, **opts).spot @@ -82,66 +82,16 @@ def self.spot(obj, **opts) end # Accepts a Thread::Backtrace::Location object and returns a Prism::Node - # corresponding to the location in the source code. - def self.prism_find(loc, point_type: :name, name: nil) + # corresponding to the backtrace location in the source code. + def self.prism_find(location) require "prism" return nil if Prism::VERSION < "0.29.0" - path = loc.absolute_path - return unless path - - lineno = loc.lineno - column = RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(loc) - tunnel = Prism.parse_file(path).value.tunnel(lineno, column) - - # Prism provides the Prism::Node#tunnel API to find all of the nodes that - # correspond to the given line and column in the source code, with the first - # node in the list being the top-most node and the last node in the list - # being the bottom-most node. - tunnel.each_with_index.reverse_each.find do |part, index| - case part - when Prism::CallNode, Prism::CallOperatorWriteNode, Prism::IndexOperatorWriteNode, Prism::LocalVariableOperatorWriteNode - # If we find any of these nodes, we can stop searching as these are the - # nodes that triggered the exceptions. - break part - when Prism::ConstantReadNode, Prism::ConstantPathNode - if index != 0 && tunnel[index - 1].is_a?(Prism::ConstantPathOperatorWriteNode) - # If we're inside of a constant path operator write node, then this - # constant path may be highlighting a couple of different kinds of - # parts. - if part.name == name - # Explicitly turn off Foo::Bar += 1 where Foo and Bar are on - # different lines because error highlight expects this to not work. - break nil if part.delimiter_loc.end_line != part.name_loc.start_line - - # Otherwise, because we have matched the name we can return this - # part. - break part - end + absolute_path = location.absolute_path + return unless absolute_path - # If we haven't matched the name, it's the operator that we're looking - # for, and we can return the parent node here. - break tunnel[index - 1] - elsif part.name == name - # If we have matched the name of the constant, then we can return this - # inner node as the node that triggered the exception. - break part - else - # If we are at the beginning of the tunnel or we are at the beginning - # of a constant lookup chain, then we will return this node. - break part if index == 0 || !tunnel[index - 1].is_a?(Prism::ConstantPathNode) - end - when Prism::LocalVariableReadNode, Prism::ParenthesesNode - # If we find any of these nodes, we want to continue searching up the - # tree because these nodes cannot trigger the exceptions. - false - else - # If we find a different kind of node that we haven't already handled, - # we don't know how to handle it so we'll stop searching and assume this - # is not an exception we can decorate. - break nil - end - end + node_id = RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(location) + Prism.parse_file(absolute_path).value.breadth_first_search { |node| node.node_id == node_id } end private_class_method :prism_find @@ -178,31 +128,49 @@ def initialize(node, point_type: :name, name: nil) def spot return nil unless @node - if OPT_GETCONSTANT_PATH && @node.type == :COLON2 + if OPT_GETCONSTANT_PATH # In Ruby 3.2 or later, a nested constant access (like `Foo::Bar::Baz`) # is compiled to one instruction (opt_getconstant_path). # @node points to the node of the whole `Foo::Bar::Baz` even if `Foo` # or `Foo::Bar` causes NameError. # So we try to spot the sub-node that causes the NameError by using # `NameError#name`. - subnodes = [] - node = @node - while node.type == :COLON2 - node2, const = node.children - subnodes << node if const == @name - node = node2 - end - if node.type == :CONST || node.type == :COLON3 - if node.children.first == @name + case @node.type + when :COLON2 + subnodes = [] + node = @node + while node.type == :COLON2 + node2, const = node.children + subnodes << node if const == @name + node = node2 + end + if node.type == :CONST || node.type == :COLON3 + if node.children.first == @name + subnodes << node + end + + # If we found only one sub-node whose name is equal to @name, use it + return nil if subnodes.size != 1 + @node = subnodes.first + else + # Do nothing; opt_getconstant_path is used only when the const base is + # NODE_CONST (`Foo`) or NODE_COLON3 (`::Foo`) + end + when :constant_path_node + subnodes = [] + node = @node + + begin + subnodes << node if node.name == @name + end while (node = node.parent).is_a?(Prism::ConstantPathNode) + + if node.is_a?(Prism::ConstantReadNode) && node.name == @name subnodes << node end # If we found only one sub-node whose name is equal to @name, use it return nil if subnodes.size != 1 @node = subnodes.first - else - # Do nothing; opt_getconstant_path is used only when the const base is - # NODE_CONST (`Foo`) or NODE_COLON3 (`::Foo`) end end @@ -847,7 +815,11 @@ def prism_spot_constant_path # Foo::Bar += 1 # ^^^^^^^^ def prism_spot_constant_path_operator_write - prism_location(@node.binary_operator_loc.chop) + if @name == (target = @node.target).name + prism_location(target.delimiter_loc.join(target.name_loc)) + else + prism_location(@node.binary_operator_loc.chop) + end end end diff --git a/lib/irb.rb b/lib/irb.rb index b417d9c2ec1821..3d45fa89db426c 100644 --- a/lib/irb.rb +++ b/lib/irb.rb @@ -880,40 +880,42 @@ module IRB # An exception raised by IRB.irb_abort class Abort < Exception;end - # The current IRB::Context of the session, see IRB.conf - # - # irb - # irb(main):001:0> IRB.CurrentContext.irb_name = "foo" - # foo(main):002:0> IRB.conf[:MAIN_CONTEXT].irb_name #=> "foo" - def IRB.CurrentContext # :nodoc: - IRB.conf[:MAIN_CONTEXT] - end + class << self + # The current IRB::Context of the session, see IRB.conf + # + # irb + # irb(main):001:0> IRB.CurrentContext.irb_name = "foo" + # foo(main):002:0> IRB.conf[:MAIN_CONTEXT].irb_name #=> "foo" + def CurrentContext # :nodoc: + conf[:MAIN_CONTEXT] + end - # Initializes IRB and creates a new Irb.irb object at the `TOPLEVEL_BINDING` - def IRB.start(ap_path = nil) - STDOUT.sync = true - $0 = File::basename(ap_path, ".rb") if ap_path + # Initializes IRB and creates a new Irb.irb object at the `TOPLEVEL_BINDING` + def start(ap_path = nil) + STDOUT.sync = true + $0 = File::basename(ap_path, ".rb") if ap_path - IRB.setup(ap_path) + setup(ap_path) - if @CONF[:SCRIPT] - irb = Irb.new(nil, @CONF[:SCRIPT]) - else - irb = Irb.new + if @CONF[:SCRIPT] + irb = Irb.new(nil, @CONF[:SCRIPT]) + else + irb = Irb.new + end + irb.run(@CONF) end - irb.run(@CONF) - end - # Quits irb - def IRB.irb_exit(*) # :nodoc: - throw :IRB_EXIT, false - end + # Quits irb + def irb_exit(*) # :nodoc: + throw :IRB_EXIT, false + end - # Aborts then interrupts irb. - # - # Will raise an Abort exception, or the given `exception`. - def IRB.irb_abort(irb, exception = Abort) # :nodoc: - irb.context.thread.raise exception, "abort then interrupt!" + # Aborts then interrupts irb. + # + # Will raise an Abort exception, or the given `exception`. + def irb_abort(irb, exception = Abort) # :nodoc: + irb.context.thread.raise exception, "abort then interrupt!" + end end class Irb diff --git a/lib/irb/command/base.rb b/lib/irb/command/base.rb index 1d406630a2c3a4..af810ed3438b7a 100644 --- a/lib/irb/command/base.rb +++ b/lib/irb/command/base.rb @@ -10,8 +10,10 @@ module IRB module Command class CommandArgumentError < StandardError; end - def self.extract_ruby_args(*args, **kwargs) - throw :EXTRACT_RUBY_ARGS, [args, kwargs] + class << self + def extract_ruby_args(*args, **kwargs) + throw :EXTRACT_RUBY_ARGS, [args, kwargs] + end end class Base @@ -31,6 +33,12 @@ def help_message(help_message = nil) @help_message end + def execute(irb_context, arg) + new(irb_context).execute(arg) + rescue CommandArgumentError => e + puts e.message + end + private def highlight(text) @@ -38,12 +46,6 @@ def highlight(text) end end - def self.execute(irb_context, arg) - new(irb_context).execute(arg) - rescue CommandArgumentError => e - puts e.message - end - def initialize(irb_context) @irb_context = irb_context end diff --git a/lib/irb/command/cd.rb b/lib/irb/command/cd.rb new file mode 100644 index 00000000000000..b83c8689aed901 --- /dev/null +++ b/lib/irb/command/cd.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module IRB + module Command + class CD < Base + category "Workspace" + description "Move into the given object or leave the current context." + + help_message(<<~HELP) + Usage: cd ([target]|..) + + IRB uses a stack of workspaces to keep track of context(s), with `pushws` and `popws` commands to manipulate the stack. + The `cd` command is an attempt to simplify the operation and will be subject to change. + + When given: + - an object, cd will use that object as the new context by pushing it onto the workspace stack. + - "..", cd will leave the current context by popping the top workspace off the stack. + - no arguments, cd will move to the top workspace on the stack by popping off all workspaces. + + Examples: + + cd Foo + cd Foo.new + cd @ivar + cd .. + cd + HELP + + def execute(arg) + case arg + when ".." + irb_context.pop_workspace + when "" + # TODO: decide what workspace commands should be kept, and underlying APIs should look like, + # and perhaps add a new API to clear the workspace stack. + prev_workspace = irb_context.pop_workspace + while prev_workspace + prev_workspace = irb_context.pop_workspace + end + else + begin + obj = eval(arg, irb_context.workspace.binding) + irb_context.push_workspace(obj) + rescue StandardError => e + warn "Error: #{e}" + end + end + end + end + end +end diff --git a/lib/irb/command/debug.rb b/lib/irb/command/debug.rb index 8a091a49ed0002..3ebb57fe54389a 100644 --- a/lib/irb/command/debug.rb +++ b/lib/irb/command/debug.rb @@ -58,13 +58,15 @@ def execute_debug_command(pre_cmds: nil, do_cmds: nil) end class DebugCommand < Debug - def self.category - "Debugging" - end + class << self + def category + "Debugging" + end - def self.description - command_name = self.name.split("::").last.downcase - "Start the debugger of debug.gem and run its `#{command_name}` command." + def description + command_name = self.name.split("::").last.downcase + "Start the debugger of debug.gem and run its `#{command_name}` command." + end end end end diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb index a3d89373c36820..7f102dcdf40a95 100644 --- a/lib/irb/completion.rb +++ b/lib/irb/completion.rb @@ -33,6 +33,8 @@ def defined? do yield ] + HELP_COMMAND_PREPOSING = /\Ahelp\s+/ + def completion_candidates(preposing, target, postposing, bind:) raise NotImplementedError end @@ -86,8 +88,8 @@ def retrieve_files_to_require_from_load_path ) end - def command_completions(preposing, target) - if preposing.empty? && !target.empty? + def command_candidates(target) + if !target.empty? IRB::Command.command_names.select { _1.start_with?(target) } else [] @@ -111,8 +113,18 @@ def inspect end def completion_candidates(preposing, target, _postposing, bind:) - commands = command_completions(preposing, target) + # When completing the argument of `help` command, only commands should be candidates + return command_candidates(target) if preposing.match?(HELP_COMMAND_PREPOSING) + + commands = if preposing.empty? + command_candidates(target) + # It doesn't make sense to propose commands with other preposing + else + [] + end + result = ReplTypeCompletor.analyze(preposing + target, binding: bind, filename: @context.irb_path) + return commands unless result commands | result.completion_candidates.map { target + _1 } @@ -187,12 +199,20 @@ def complete_require_path(target, preposing, postposing) end def completion_candidates(preposing, target, postposing, bind:) - if preposing && postposing - result = complete_require_path(target, preposing, postposing) - return result if result + if result = complete_require_path(target, preposing, postposing) + return result end - commands = command_completions(preposing || '', target) - commands | retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.map{ |i| i.encode(Encoding.default_external) } + + commands = command_candidates(target) + + # When completing the argument of `help` command, only commands should be candidates + return commands if preposing.match?(HELP_COMMAND_PREPOSING) + + # It doesn't make sense to propose commands with other preposing + commands = [] unless preposing.empty? + + completion_data = retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.map{ |i| i.encode(Encoding.default_external) } + commands | completion_data end def doc_namespace(_preposing, matched, _postposing, bind:) @@ -470,7 +490,7 @@ def retrieve_completion_data(input, bind: IRB.conf[:MAIN_CONTEXT].workspace.bind end end CompletionProc = ->(target, preposing = nil, postposing = nil) { - regexp_completor.completion_candidates(preposing, target, postposing, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding) + regexp_completor.completion_candidates(preposing || '', target, postposing || '', bind: IRB.conf[:MAIN_CONTEXT].workspace.binding) } end deprecate_constant :InputCompletor diff --git a/lib/irb/default_commands.rb b/lib/irb/default_commands.rb index 91c6b2d0417411..fec41df4e2eff2 100644 --- a/lib/irb/default_commands.rb +++ b/lib/irb/default_commands.rb @@ -5,6 +5,7 @@ require_relative "command/backtrace" require_relative "command/break" require_relative "command/catch" +require_relative "command/cd" require_relative "command/chws" require_relative "command/context" require_relative "command/continue" @@ -240,6 +241,8 @@ def load_command(command) _register_with_aliases(:irb_disable_irb, Command::DisableIrb, [:disable_irb, NO_OVERRIDE] ) + + register(:cd, Command::CD) end ExtendCommand = Command @@ -256,10 +259,12 @@ module ExtendCommandBundle # Deprecated. Doesn't have any effect. @EXTEND_COMMANDS = [] - # Drepcated. Use Command.regiser instead. - def self.def_extend_command(cmd_name, cmd_class, _, *aliases) - Command._register_with_aliases(cmd_name, cmd_class, *aliases) - Command.class_variable_set(:@@command_override_policies, nil) + class << self + # Drepcated. Use Command.regiser instead. + def def_extend_command(cmd_name, cmd_class, _, *aliases) + Command._register_with_aliases(cmd_name, cmd_class, *aliases) + Command.class_variable_set(:@@command_override_policies, nil) + end end end end diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb index ced35a2c5af58c..f6b8d00e539b76 100644 --- a/lib/irb/input-method.rb +++ b/lib/irb/input-method.rb @@ -171,11 +171,13 @@ def close end class ReadlineInputMethod < StdioInputMethod - def self.initialize_readline - require "readline" - rescue LoadError - else - include ::Readline + class << self + def initialize_readline + require "readline" + rescue LoadError + else + include ::Readline + end end include HistorySavingAbility diff --git a/lib/irb/inspector.rb b/lib/irb/inspector.rb index 667087ccba3d72..8046744f889391 100644 --- a/lib/irb/inspector.rb +++ b/lib/irb/inspector.rb @@ -6,7 +6,6 @@ module IRB # :nodoc: - # Convenience method to create a new Inspector, using the given +inspect+ # proc, and optional +init+ proc and passes them to Inspector.new # @@ -43,38 +42,40 @@ class Inspector # +:marshal+:: Using Marshal.dump INSPECTORS = {} - # Determines the inspector to use where +inspector+ is one of the keys passed - # during inspector definition. - def self.keys_with_inspector(inspector) - INSPECTORS.select{|k, v| v == inspector}.collect{|k, v| k} - end - - # Example - # - # Inspector.def_inspector(key, init_p=nil){|v| v.inspect} - # Inspector.def_inspector([key1,..], init_p=nil){|v| v.inspect} - # Inspector.def_inspector(key, inspector) - # Inspector.def_inspector([key1,...], inspector) - def self.def_inspector(key, arg=nil, &block) - if block_given? - inspector = IRB::Inspector(block, arg) - else - inspector = arg + class << self + # Determines the inspector to use where +inspector+ is one of the keys passed + # during inspector definition. + def keys_with_inspector(inspector) + INSPECTORS.select{|k, v| v == inspector}.collect{|k, v| k} end - case key - when Array - for k in key - def_inspector(k, inspector) + # Example + # + # Inspector.def_inspector(key, init_p=nil){|v| v.inspect} + # Inspector.def_inspector([key1,..], init_p=nil){|v| v.inspect} + # Inspector.def_inspector(key, inspector) + # Inspector.def_inspector([key1,...], inspector) + def def_inspector(key, arg=nil, &block) + if block_given? + inspector = IRB::Inspector(block, arg) + else + inspector = arg + end + + case key + when Array + for k in key + def_inspector(k, inspector) + end + when Symbol + INSPECTORS[key] = inspector + INSPECTORS[key.to_s] = inspector + when String + INSPECTORS[key] = inspector + INSPECTORS[key.intern] = inspector + else + INSPECTORS[key] = inspector end - when Symbol - INSPECTORS[key] = inspector - INSPECTORS[key.to_s] = inspector - when String - INSPECTORS[key] = inspector - INSPECTORS[key.intern] = inspector - else - INSPECTORS[key] = inspector end end diff --git a/lib/irb/nesting_parser.rb b/lib/irb/nesting_parser.rb index 5aa940cc285841..fc71d64aee8544 100644 --- a/lib/irb/nesting_parser.rb +++ b/lib/irb/nesting_parser.rb @@ -3,235 +3,237 @@ module IRB module NestingParser IGNORE_TOKENS = %i[on_sp on_ignored_nl on_comment on_embdoc_beg on_embdoc on_embdoc_end] - # Scan each token and call the given block with array of token and other information for parsing - def self.scan_opens(tokens) - opens = [] - pending_heredocs = [] - first_token_on_line = true - tokens.each do |t| - skip = false - last_tok, state, args = opens.last - case state - when :in_alias_undef - skip = t.event == :on_kw - when :in_unquoted_symbol - unless IGNORE_TOKENS.include?(t.event) - opens.pop - skip = true - end - when :in_lambda_head - opens.pop if t.event == :on_tlambeg || (t.event == :on_kw && t.tok == 'do') - when :in_method_head - unless IGNORE_TOKENS.include?(t.event) - next_args = [] - body = nil - if args.include?(:receiver) - case t.event - when :on_lparen, :on_ivar, :on_gvar, :on_cvar - # def (receiver). | def @ivar. | def $gvar. | def @@cvar. - next_args << :dot - when :on_kw - case t.tok - when 'self', 'true', 'false', 'nil' - # def self(arg) | def self. - next_args.push(:arg, :dot) - else - # def if(arg) + class << self + # Scan each token and call the given block with array of token and other information for parsing + def scan_opens(tokens) + opens = [] + pending_heredocs = [] + first_token_on_line = true + tokens.each do |t| + skip = false + last_tok, state, args = opens.last + case state + when :in_alias_undef + skip = t.event == :on_kw + when :in_unquoted_symbol + unless IGNORE_TOKENS.include?(t.event) + opens.pop + skip = true + end + when :in_lambda_head + opens.pop if t.event == :on_tlambeg || (t.event == :on_kw && t.tok == 'do') + when :in_method_head + unless IGNORE_TOKENS.include?(t.event) + next_args = [] + body = nil + if args.include?(:receiver) + case t.event + when :on_lparen, :on_ivar, :on_gvar, :on_cvar + # def (receiver). | def @ivar. | def $gvar. | def @@cvar. + next_args << :dot + when :on_kw + case t.tok + when 'self', 'true', 'false', 'nil' + # def self(arg) | def self. + next_args.push(:arg, :dot) + else + # def if(arg) + skip = true + next_args << :arg + end + when :on_op, :on_backtick + # def +(arg) skip = true next_args << :arg + when :on_ident, :on_const + # def a(arg) | def a. + next_args.push(:arg, :dot) end - when :on_op, :on_backtick - # def +(arg) - skip = true - next_args << :arg - when :on_ident, :on_const - # def a(arg) | def a. - next_args.push(:arg, :dot) end - end - if args.include?(:dot) - # def receiver.name - next_args << :name if t.event == :on_period || (t.event == :on_op && t.tok == '::') - end - if args.include?(:name) - if %i[on_ident on_const on_op on_kw on_backtick].include?(t.event) - # def name(arg) | def receiver.name(arg) - next_args << :arg - skip = true + if args.include?(:dot) + # def receiver.name + next_args << :name if t.event == :on_period || (t.event == :on_op && t.tok == '::') end - end - if args.include?(:arg) - case t.event - when :on_nl, :on_semicolon - # def receiver.f; - body = :normal - when :on_lparen - # def receiver.f() - next_args << :eq - else + if args.include?(:name) + if %i[on_ident on_const on_op on_kw on_backtick].include?(t.event) + # def name(arg) | def receiver.name(arg) + next_args << :arg + skip = true + end + end + if args.include?(:arg) + case t.event + when :on_nl, :on_semicolon + # def receiver.f; + body = :normal + when :on_lparen + # def receiver.f() + next_args << :eq + else + if t.event == :on_op && t.tok == '=' + # def receiver.f = + body = :oneliner + else + # def receiver.f arg + next_args << :arg_without_paren + end + end + end + if args.include?(:eq) if t.event == :on_op && t.tok == '=' - # def receiver.f = body = :oneliner else - # def receiver.f arg - next_args << :arg_without_paren + body = :normal end end - end - if args.include?(:eq) - if t.event == :on_op && t.tok == '=' - body = :oneliner - else - body = :normal + if args.include?(:arg_without_paren) + if %i[on_semicolon on_nl].include?(t.event) + # def f a; + body = :normal + else + # def f a, b + next_args << :arg_without_paren + end end - end - if args.include?(:arg_without_paren) - if %i[on_semicolon on_nl].include?(t.event) - # def f a; - body = :normal + if body == :oneliner + opens.pop + elsif body + opens[-1] = [last_tok, nil] else - # def f a, b - next_args << :arg_without_paren + opens[-1] = [last_tok, :in_method_head, next_args] end end - if body == :oneliner - opens.pop - elsif body + when :in_for_while_until_condition + if t.event == :on_semicolon || t.event == :on_nl || (t.event == :on_kw && t.tok == 'do') + skip = true if t.event == :on_kw && t.tok == 'do' opens[-1] = [last_tok, nil] - else - opens[-1] = [last_tok, :in_method_head, next_args] end end - when :in_for_while_until_condition - if t.event == :on_semicolon || t.event == :on_nl || (t.event == :on_kw && t.tok == 'do') - skip = true if t.event == :on_kw && t.tok == 'do' - opens[-1] = [last_tok, nil] - end - end - unless skip - case t.event - when :on_kw - case t.tok - when 'begin', 'class', 'module', 'do', 'case' - opens << [t, nil] - when 'end' - opens.pop - when 'def' - opens << [t, :in_method_head, [:receiver, :name]] - when 'if', 'unless' - unless t.state.allbits?(Ripper::EXPR_LABEL) + unless skip + case t.event + when :on_kw + case t.tok + when 'begin', 'class', 'module', 'do', 'case' opens << [t, nil] - end - when 'while', 'until' - unless t.state.allbits?(Ripper::EXPR_LABEL) - opens << [t, :in_for_while_until_condition] - end - when 'ensure', 'rescue' - unless t.state.allbits?(Ripper::EXPR_LABEL) + when 'end' + opens.pop + when 'def' + opens << [t, :in_method_head, [:receiver, :name]] + when 'if', 'unless' + unless t.state.allbits?(Ripper::EXPR_LABEL) + opens << [t, nil] + end + when 'while', 'until' + unless t.state.allbits?(Ripper::EXPR_LABEL) + opens << [t, :in_for_while_until_condition] + end + when 'ensure', 'rescue' + unless t.state.allbits?(Ripper::EXPR_LABEL) + opens.pop + opens << [t, nil] + end + when 'alias' + opens << [t, :in_alias_undef, 2] + when 'undef' + opens << [t, :in_alias_undef, 1] + when 'elsif', 'else', 'when' opens.pop opens << [t, nil] + when 'for' + opens << [t, :in_for_while_until_condition] + when 'in' + if last_tok&.event == :on_kw && %w[case in].include?(last_tok.tok) && first_token_on_line + opens.pop + opens << [t, nil] + end end - when 'alias' - opens << [t, :in_alias_undef, 2] - when 'undef' - opens << [t, :in_alias_undef, 1] - when 'elsif', 'else', 'when' + when :on_tlambda + opens << [t, :in_lambda_head] + when :on_lparen, :on_lbracket, :on_lbrace, :on_tlambeg, :on_embexpr_beg, :on_embdoc_beg + opens << [t, nil] + when :on_rparen, :on_rbracket, :on_rbrace, :on_embexpr_end, :on_embdoc_end + opens.pop + when :on_heredoc_beg + pending_heredocs << t + when :on_heredoc_end opens.pop + when :on_backtick + opens << [t, nil] if t.state.allbits?(Ripper::EXPR_BEG) + when :on_tstring_beg, :on_words_beg, :on_qwords_beg, :on_symbols_beg, :on_qsymbols_beg, :on_regexp_beg opens << [t, nil] - when 'for' - opens << [t, :in_for_while_until_condition] - when 'in' - if last_tok&.event == :on_kw && %w[case in].include?(last_tok.tok) && first_token_on_line - opens.pop + when :on_tstring_end, :on_regexp_end, :on_label_end + opens.pop + when :on_symbeg + if t.tok == ':' + opens << [t, :in_unquoted_symbol] + else opens << [t, nil] end end - when :on_tlambda - opens << [t, :in_lambda_head] - when :on_lparen, :on_lbracket, :on_lbrace, :on_tlambeg, :on_embexpr_beg, :on_embdoc_beg - opens << [t, nil] - when :on_rparen, :on_rbracket, :on_rbrace, :on_embexpr_end, :on_embdoc_end - opens.pop - when :on_heredoc_beg - pending_heredocs << t - when :on_heredoc_end - opens.pop - when :on_backtick - opens << [t, nil] if t.state.allbits?(Ripper::EXPR_BEG) - when :on_tstring_beg, :on_words_beg, :on_qwords_beg, :on_symbols_beg, :on_qsymbols_beg, :on_regexp_beg - opens << [t, nil] - when :on_tstring_end, :on_regexp_end, :on_label_end - opens.pop - when :on_symbeg - if t.tok == ':' - opens << [t, :in_unquoted_symbol] - else - opens << [t, nil] - end end + if t.event == :on_nl || t.event == :on_semicolon + first_token_on_line = true + elsif t.event != :on_sp + first_token_on_line = false + end + if pending_heredocs.any? && t.tok.include?("\n") + pending_heredocs.reverse_each { |t| opens << [t, nil] } + pending_heredocs = [] + end + if opens.last && opens.last[1] == :in_alias_undef && !IGNORE_TOKENS.include?(t.event) && t.event != :on_heredoc_end + tok, state, arg = opens.pop + opens << [tok, state, arg - 1] if arg >= 1 + end + yield t, opens if block_given? end - if t.event == :on_nl || t.event == :on_semicolon - first_token_on_line = true - elsif t.event != :on_sp - first_token_on_line = false - end - if pending_heredocs.any? && t.tok.include?("\n") - pending_heredocs.reverse_each { |t| opens << [t, nil] } - pending_heredocs = [] - end - if opens.last && opens.last[1] == :in_alias_undef && !IGNORE_TOKENS.include?(t.event) && t.event != :on_heredoc_end - tok, state, arg = opens.pop - opens << [tok, state, arg - 1] if arg >= 1 - end - yield t, opens if block_given? + opens.map(&:first) + pending_heredocs.reverse end - opens.map(&:first) + pending_heredocs.reverse - end - def self.open_tokens(tokens) - # scan_opens without block will return a list of open tokens at last token position - scan_opens(tokens) - end + def open_tokens(tokens) + # scan_opens without block will return a list of open tokens at last token position + scan_opens(tokens) + end - # Calculates token information [line_tokens, prev_opens, next_opens, min_depth] for each line. - # Example code - # ["hello - # world"+( - # First line - # line_tokens: [[lbracket, '['], [tstring_beg, '"'], [tstring_content("hello\nworld"), "hello\n"]] - # prev_opens: [] - # next_tokens: [lbracket, tstring_beg] - # min_depth: 0 (minimum at beginning of line) - # Second line - # line_tokens: [[tstring_content("hello\nworld"), "world"], [tstring_end, '"'], [op, '+'], [lparen, '(']] - # prev_opens: [lbracket, tstring_beg] - # next_tokens: [lbracket, lparen] - # min_depth: 1 (minimum just after tstring_end) - def self.parse_by_line(tokens) - line_tokens = [] - prev_opens = [] - min_depth = 0 - output = [] - last_opens = scan_opens(tokens) do |t, opens| - depth = t == opens.last&.first ? opens.size - 1 : opens.size - min_depth = depth if depth < min_depth - if t.tok.include?("\n") - t.tok.each_line do |line| - line_tokens << [t, line] - next if line[-1] != "\n" - next_opens = opens.map(&:first) - output << [line_tokens, prev_opens, next_opens, min_depth] - prev_opens = next_opens - min_depth = prev_opens.size - line_tokens = [] + # Calculates token information [line_tokens, prev_opens, next_opens, min_depth] for each line. + # Example code + # ["hello + # world"+( + # First line + # line_tokens: [[lbracket, '['], [tstring_beg, '"'], [tstring_content("hello\nworld"), "hello\n"]] + # prev_opens: [] + # next_tokens: [lbracket, tstring_beg] + # min_depth: 0 (minimum at beginning of line) + # Second line + # line_tokens: [[tstring_content("hello\nworld"), "world"], [tstring_end, '"'], [op, '+'], [lparen, '(']] + # prev_opens: [lbracket, tstring_beg] + # next_tokens: [lbracket, lparen] + # min_depth: 1 (minimum just after tstring_end) + def parse_by_line(tokens) + line_tokens = [] + prev_opens = [] + min_depth = 0 + output = [] + last_opens = scan_opens(tokens) do |t, opens| + depth = t == opens.last&.first ? opens.size - 1 : opens.size + min_depth = depth if depth < min_depth + if t.tok.include?("\n") + t.tok.each_line do |line| + line_tokens << [t, line] + next if line[-1] != "\n" + next_opens = opens.map(&:first) + output << [line_tokens, prev_opens, next_opens, min_depth] + prev_opens = next_opens + min_depth = prev_opens.size + line_tokens = [] + end + else + line_tokens << [t, t.tok] end - else - line_tokens << [t, t.tok] end + output << [line_tokens, prev_opens, last_opens, min_depth] if line_tokens.any? + output end - output << [line_tokens, prev_opens, last_opens, min_depth] if line_tokens.any? - output end end end diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb index f6ac7f0f5f51f6..3abb53b4ea5668 100644 --- a/lib/irb/ruby-lex.rb +++ b/lib/irb/ruby-lex.rb @@ -36,29 +36,6 @@ class RubyLex :massign, ] - class TerminateLineInput < StandardError - def initialize - super("Terminate Line Input") - end - end - - def self.compile_with_errors_suppressed(code, line_no: 1) - begin - result = yield code, line_no - rescue ArgumentError - # Ruby can issue an error for the code if there is an - # incomplete magic comment for encoding in it. Force an - # expression with a new line before the code in this - # case to prevent magic comment handling. To make sure - # line numbers in the lexed code remain the same, - # decrease the line number by one. - code = ";\n#{code}" - line_no -= 1 - result = yield code, line_no - end - result - end - ERROR_TOKENS = [ :on_parse_error, :compile_error, @@ -68,70 +45,102 @@ def self.compile_with_errors_suppressed(code, line_no: 1) :on_param_error ] - def self.generate_local_variables_assign_code(local_variables) - "#{local_variables.join('=')}=nil;" unless local_variables.empty? + LTYPE_TOKENS = %i[ + on_heredoc_beg on_tstring_beg + on_regexp_beg on_symbeg on_backtick + on_symbols_beg on_qsymbols_beg + on_words_beg on_qwords_beg + ] + + class TerminateLineInput < StandardError + def initialize + super("Terminate Line Input") + end end - # Some part of the code is not included in Ripper's token. - # Example: DATA part, token after heredoc_beg when heredoc has unclosed embexpr. - # With interpolated tokens, tokens.map(&:tok).join will be equal to code. - def self.interpolate_ripper_ignored_tokens(code, tokens) - line_positions = [0] - code.lines.each do |line| - line_positions << line_positions.last + line.bytesize + class << self + def compile_with_errors_suppressed(code, line_no: 1) + begin + result = yield code, line_no + rescue ArgumentError + # Ruby can issue an error for the code if there is an + # incomplete magic comment for encoding in it. Force an + # expression with a new line before the code in this + # case to prevent magic comment handling. To make sure + # line numbers in the lexed code remain the same, + # decrease the line number by one. + code = ";\n#{code}" + line_no -= 1 + result = yield code, line_no + end + result + end + + def generate_local_variables_assign_code(local_variables) + "#{local_variables.join('=')}=nil;" unless local_variables.empty? end - prev_byte_pos = 0 - interpolated = [] - prev_line = 1 - tokens.each do |t| - line, col = t.pos - byte_pos = line_positions[line - 1] + col - if prev_byte_pos < byte_pos - tok = code.byteslice(prev_byte_pos...byte_pos) + + # Some part of the code is not included in Ripper's token. + # Example: DATA part, token after heredoc_beg when heredoc has unclosed embexpr. + # With interpolated tokens, tokens.map(&:tok).join will be equal to code. + def interpolate_ripper_ignored_tokens(code, tokens) + line_positions = [0] + code.lines.each do |line| + line_positions << line_positions.last + line.bytesize + end + prev_byte_pos = 0 + interpolated = [] + prev_line = 1 + tokens.each do |t| + line, col = t.pos + byte_pos = line_positions[line - 1] + col + if prev_byte_pos < byte_pos + tok = code.byteslice(prev_byte_pos...byte_pos) + pos = [prev_line, prev_byte_pos - line_positions[prev_line - 1]] + interpolated << Ripper::Lexer::Elem.new(pos, :on_ignored_by_ripper, tok, 0) + prev_line += tok.count("\n") + end + interpolated << t + prev_byte_pos = byte_pos + t.tok.bytesize + prev_line += t.tok.count("\n") + end + if prev_byte_pos < code.bytesize + tok = code.byteslice(prev_byte_pos..) pos = [prev_line, prev_byte_pos - line_positions[prev_line - 1]] interpolated << Ripper::Lexer::Elem.new(pos, :on_ignored_by_ripper, tok, 0) - prev_line += tok.count("\n") end - interpolated << t - prev_byte_pos = byte_pos + t.tok.bytesize - prev_line += t.tok.count("\n") - end - if prev_byte_pos < code.bytesize - tok = code.byteslice(prev_byte_pos..) - pos = [prev_line, prev_byte_pos - line_positions[prev_line - 1]] - interpolated << Ripper::Lexer::Elem.new(pos, :on_ignored_by_ripper, tok, 0) + interpolated end - interpolated - end - def self.ripper_lex_without_warning(code, local_variables: []) - verbose, $VERBOSE = $VERBOSE, nil - lvars_code = generate_local_variables_assign_code(local_variables) - original_code = code - if lvars_code - code = "#{lvars_code}\n#{code}" - line_no = 0 - else - line_no = 1 - end + def ripper_lex_without_warning(code, local_variables: []) + verbose, $VERBOSE = $VERBOSE, nil + lvars_code = generate_local_variables_assign_code(local_variables) + original_code = code + if lvars_code + code = "#{lvars_code}\n#{code}" + line_no = 0 + else + line_no = 1 + end - compile_with_errors_suppressed(code, line_no: line_no) do |inner_code, line_no| - lexer = Ripper::Lexer.new(inner_code, '-', line_no) - tokens = [] - lexer.scan.each do |t| - next if t.pos.first == 0 - prev_tk = tokens.last - position_overlapped = prev_tk && t.pos[0] == prev_tk.pos[0] && t.pos[1] < prev_tk.pos[1] + prev_tk.tok.bytesize - if position_overlapped - tokens[-1] = t if ERROR_TOKENS.include?(prev_tk.event) && !ERROR_TOKENS.include?(t.event) - else - tokens << t + compile_with_errors_suppressed(code, line_no: line_no) do |inner_code, line_no| + lexer = Ripper::Lexer.new(inner_code, '-', line_no) + tokens = [] + lexer.scan.each do |t| + next if t.pos.first == 0 + prev_tk = tokens.last + position_overlapped = prev_tk && t.pos[0] == prev_tk.pos[0] && t.pos[1] < prev_tk.pos[1] + prev_tk.tok.bytesize + if position_overlapped + tokens[-1] = t if ERROR_TOKENS.include?(prev_tk.event) && !ERROR_TOKENS.include?(t.event) + else + tokens << t + end end + interpolate_ripper_ignored_tokens(original_code, tokens) end - interpolate_ripper_ignored_tokens(original_code, tokens) + ensure + $VERBOSE = verbose end - ensure - $VERBOSE = verbose end def check_code_state(code, local_variables:) @@ -391,13 +400,6 @@ def process_indent_level(tokens, lines, line_index, is_newline) end end - LTYPE_TOKENS = %i[ - on_heredoc_beg on_tstring_beg - on_regexp_beg on_symbeg on_backtick - on_symbols_beg on_qsymbols_beg - on_words_beg on_qwords_beg - ] - def ltype_from_open_tokens(opens) start_token = opens.reverse_each.find do |tok| LTYPE_TOKENS.include?(tok.event) diff --git a/lib/irb/version.rb b/lib/irb/version.rb index 25b7903f01faad..e935a1d7f73ba4 100644 --- a/lib/irb/version.rb +++ b/lib/irb/version.rb @@ -5,7 +5,7 @@ # module IRB # :nodoc: - VERSION = "1.13.2" + VERSION = "1.14.0" @RELEASE_VERSION = VERSION - @LAST_UPDATE_DATE = "2024-06-15" + @LAST_UPDATE_DATE = "2024-07-06" end diff --git a/lib/irb/workspace.rb b/lib/irb/workspace.rb index d24d1cc38db232..632b43243907e5 100644 --- a/lib/irb/workspace.rb +++ b/lib/irb/workspace.rb @@ -176,11 +176,13 @@ def code_around_binding 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) + class << self + def 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 end diff --git a/lib/logger.rb b/lib/logger.rb index 4099955ef28a87..4a9c81b72bc1a7 100644 --- a/lib/logger.rb +++ b/lib/logger.rb @@ -574,10 +574,14 @@ def fatal!; self.level = FATAL; end # - +shift_period_suffix+: sets the format for the filename suffix # for periodic log file rotation; default is '%Y%m%d'. # See {Periodic Rotation}[rdoc-ref:Logger@Periodic+Rotation]. + # - +reraise_write_errors+: An array of exception classes, which will + # be reraised if there is an error when writing to the log device. + # The default is to swallow all exceptions raised. # def initialize(logdev, shift_age = 0, shift_size = 1048576, level: DEBUG, progname: nil, formatter: nil, datetime_format: nil, - binmode: false, shift_period_suffix: '%Y%m%d') + binmode: false, shift_period_suffix: '%Y%m%d', + reraise_write_errors: []) self.level = level self.progname = progname @default_formatter = Formatter.new @@ -589,7 +593,8 @@ def initialize(logdev, shift_age = 0, shift_size = 1048576, level: DEBUG, @logdev = LogDevice.new(logdev, shift_age: shift_age, shift_size: shift_size, shift_period_suffix: shift_period_suffix, - binmode: binmode) + binmode: binmode, + reraise_write_errors: reraise_write_errors) end end diff --git a/lib/logger/log_device.rb b/lib/logger/log_device.rb index 84277a2656e607..4876adf0b7ff25 100644 --- a/lib/logger/log_device.rb +++ b/lib/logger/log_device.rb @@ -11,9 +11,10 @@ class LogDevice attr_reader :filename include MonitorMixin - def initialize(log = nil, shift_age: nil, shift_size: nil, shift_period_suffix: nil, binmode: false) + def initialize(log = nil, shift_age: nil, shift_size: nil, shift_period_suffix: nil, binmode: false, reraise_write_errors: []) @dev = @filename = @shift_age = @shift_size = @shift_period_suffix = nil @binmode = binmode + @reraise_write_errors = reraise_write_errors mon_initialize set_dev(log) if @filename @@ -34,16 +35,22 @@ def write(message) if @shift_age and @dev.respond_to?(:stat) begin check_shift_log + rescue *@reraise_write_errors + raise rescue warn("log shifting failed. #{$!}") end end begin @dev.write(message) + rescue *@reraise_write_errors + raise rescue warn("log writing failed. #{$!}") end end + rescue *@reraise_write_errors + raise rescue Exception => ignored warn("log writing failed. #{ignored}") end diff --git a/lib/net/http.rb b/lib/net/http.rb index 958ff09f0e489e..5cc9d2ce883b33 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -1103,7 +1103,7 @@ class << HTTP # For proxy-defining arguments +p_addr+ through +p_no_proxy+, # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server]. # - def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_no_proxy = nil) + def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_no_proxy = nil, p_use_ssl = nil) http = super address, port if proxy_class? then # from Net::HTTP::Proxy() @@ -1112,6 +1112,7 @@ def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_p http.proxy_port = @proxy_port http.proxy_user = @proxy_user http.proxy_pass = @proxy_pass + http.proxy_use_ssl = @proxy_use_ssl elsif p_addr == :ENV then http.proxy_from_env = true else @@ -1123,34 +1124,67 @@ def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_p http.proxy_port = p_port || default_port http.proxy_user = p_user http.proxy_pass = p_pass + http.proxy_use_ssl = p_use_ssl end http end + class << HTTP + # Allows to set the default configuration that will be used + # when creating a new connection. + # + # Example: + # + # Net::HTTP.default_configuration = { + # read_timeout: 1, + # write_timeout: 1 + # } + # http = Net::HTTP.new(hostname) + # http.open_timeout # => 60 + # http.read_timeout # => 1 + # http.write_timeout # => 1 + # + attr_accessor :default_configuration + end + # Creates a new \Net::HTTP object for the specified server address, # without opening the TCP connection or initializing the \HTTP session. # The +address+ should be a DNS hostname or IP address. def initialize(address, port = nil) # :nodoc: + defaults = { + keep_alive_timeout: 2, + close_on_empty_response: false, + open_timeout: 60, + read_timeout: 60, + write_timeout: 60, + continue_timeout: nil, + max_retries: 1, + debug_output: nil, + response_body_encoding: false, + ignore_eof: true + } + options = defaults.merge(self.class.default_configuration || {}) + @address = address @port = (port || HTTP.default_port) @ipaddr = nil @local_host = nil @local_port = nil @curr_http_version = HTTPVersion - @keep_alive_timeout = 2 + @keep_alive_timeout = options[:keep_alive_timeout] @last_communicated = nil - @close_on_empty_response = false + @close_on_empty_response = options[:close_on_empty_response] @socket = nil @started = false - @open_timeout = 60 - @read_timeout = 60 - @write_timeout = 60 - @continue_timeout = nil - @max_retries = 1 - @debug_output = nil - @response_body_encoding = false - @ignore_eof = true + @open_timeout = options[:open_timeout] + @read_timeout = options[:read_timeout] + @write_timeout = options[:write_timeout] + @continue_timeout = options[:continue_timeout] + @max_retries = options[:max_retries] + @debug_output = options[:debug_output] + @response_body_encoding = options[:response_body_encoding] + @ignore_eof = options[:ignore_eof] @proxy_from_env = false @proxy_uri = nil @@ -1158,6 +1192,7 @@ def initialize(address, port = nil) # :nodoc: @proxy_port = nil @proxy_user = nil @proxy_pass = nil + @proxy_use_ssl = nil @use_ssl = false @ssl_context = nil @@ -1292,6 +1327,7 @@ def response_body_encoding=(value) # Sets the proxy password; # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server]. attr_writer :proxy_pass + attr_writer :proxy_use_ssl # Returns the IP address for the connection. # @@ -1481,23 +1517,6 @@ def use_ssl=(flag) @use_ssl = flag end - SSL_IVNAMES = [ - :@ca_file, - :@ca_path, - :@cert, - :@cert_store, - :@ciphers, - :@extra_chain_cert, - :@key, - :@ssl_timeout, - :@ssl_version, - :@min_version, - :@max_version, - :@verify_callback, - :@verify_depth, - :@verify_mode, - :@verify_hostname, - ] # :nodoc: SSL_ATTRIBUTES = [ :ca_file, :ca_path, @@ -1516,6 +1535,8 @@ def use_ssl=(flag) :verify_hostname, ] # :nodoc: + SSL_IVNAMES = SSL_ATTRIBUTES.map { |a| "@#{a}".to_sym } # :nodoc: + # Sets or returns the path to a CA certification file in PEM format. attr_accessor :ca_file @@ -1651,7 +1672,13 @@ def connect debug "opened" if use_ssl? if proxy? - plain_sock = BufferedIO.new(s, read_timeout: @read_timeout, + if @proxy_use_ssl + proxy_sock = OpenSSL::SSL::SSLSocket.new(s) + ssl_socket_connect(proxy_sock, @open_timeout) + else + proxy_sock = s + end + proxy_sock = BufferedIO.new(proxy_sock, read_timeout: @read_timeout, write_timeout: @write_timeout, continue_timeout: @continue_timeout, debug_output: @debug_output) @@ -1662,8 +1689,8 @@ def connect buf << "Proxy-Authorization: Basic #{credential}\r\n" end buf << "\r\n" - plain_sock.write(buf) - HTTPResponse.read_new(plain_sock).value + proxy_sock.write(buf) + HTTPResponse.read_new(proxy_sock).value # assuming nothing left in buffers after successful CONNECT response end @@ -1771,13 +1798,14 @@ def do_finish @proxy_port = nil @proxy_user = nil @proxy_pass = nil + @proxy_use_ssl = nil # Creates an \HTTP proxy class which behaves like \Net::HTTP, but # performs all access via the specified proxy. # # This class is obsolete. You may pass these same parameters directly to # \Net::HTTP.new. See Net::HTTP.new for details of the arguments. - def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil) #:nodoc: + def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_use_ssl = nil) #:nodoc: return self unless p_addr Class.new(self) { @@ -1795,6 +1823,7 @@ def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil) #:nodoc: @proxy_user = p_user @proxy_pass = p_pass + @proxy_use_ssl = p_use_ssl } end @@ -1819,6 +1848,9 @@ def proxy_class? # Returns the password for accessing the proxy, or +nil+ if none; # see Net::HTTP@Proxy+Server. attr_reader :proxy_pass + + # Use SSL when talking to the proxy. If Net::HTTP does not use a proxy, nil. + attr_reader :proxy_use_ssl end # Returns +true+ if a proxy server is defined, +false+ otherwise; diff --git a/lib/open-uri.rb b/lib/open-uri.rb index ba2379325f5997..91eb61e54acbda 100644 --- a/lib/open-uri.rb +++ b/lib/open-uri.rb @@ -212,7 +212,7 @@ def OpenURI.open_loop(uri, options) # :nodoc: end uri_set = {} - max_redirects = options[:max_redirects] + max_redirects = options[:max_redirects] || Options.fetch(:max_redirects) buf = nil while true redirect = catch(:open_uri_redirect) { @@ -746,6 +746,12 @@ module OpenRead # Using +true+ also means that redirections between http and ftp are # permitted. # + # [:max_redirects] + # Synopsis: + # :max_redirects=>int + # + # Number of HTTP redirects allowed before OpenURI::TooManyRedirects is raised. + # The default is 64. def open(*rest, &block) OpenURI.open_uri(self, *rest, &block) end diff --git a/lib/prism/desugar_compiler.rb b/lib/prism/desugar_compiler.rb index de02445149302c..c51bdd2b1cf9f9 100644 --- a/lib/prism/desugar_compiler.rb +++ b/lib/prism/desugar_compiler.rb @@ -2,11 +2,13 @@ module Prism class DesugarAndWriteNode # :nodoc: - attr_reader :node, :source, :read_class, :write_class, :arguments + include DSL - def initialize(node, source, read_class, write_class, *arguments) + attr_reader :node, :default_source, :read_class, :write_class, :arguments + + def initialize(node, default_source, read_class, write_class, **arguments) @node = node - @source = source + @default_source = default_source @read_class = read_class @write_class = write_class @arguments = arguments @@ -14,22 +16,30 @@ def initialize(node, source, read_class, write_class, *arguments) # Desugar `x &&= y` to `x && x = y` def compile - AndNode.new( - source, - read_class.new(source, *arguments, node.name_loc), - write_class.new(source, *arguments, node.name_loc, node.value, node.operator_loc, node.location), - node.operator_loc, - node.location + and_node( + location: node.location, + left: public_send(read_class, location: node.name_loc, **arguments), + right: public_send( + write_class, + location: node.location, + **arguments, + name_loc: node.name_loc, + value: node.value, + operator_loc: node.operator_loc + ), + operator_loc: node.operator_loc ) end end class DesugarOrWriteDefinedNode # :nodoc: - attr_reader :node, :source, :read_class, :write_class, :arguments + include DSL + + attr_reader :node, :default_source, :read_class, :write_class, :arguments - def initialize(node, source, read_class, write_class, *arguments) + def initialize(node, default_source, read_class, write_class, **arguments) @node = node - @source = source + @default_source = default_source @read_class = read_class @write_class = write_class @arguments = arguments @@ -37,35 +47,50 @@ def initialize(node, source, read_class, write_class, *arguments) # Desugar `x ||= y` to `defined?(x) ? x : x = y` def compile - IfNode.new( - source, - node.operator_loc, - DefinedNode.new(source, nil, read_class.new(source, *arguments, node.name_loc), nil, node.operator_loc, node.name_loc), - node.operator_loc, - StatementsNode.new(source, [read_class.new(source, *arguments, node.name_loc)], node.location), - ElseNode.new( - source, - node.operator_loc, - StatementsNode.new( - source, - [write_class.new(source, *arguments, node.name_loc, node.value, node.operator_loc, node.location)], - node.location + if_node( + location: node.location, + if_keyword_loc: node.operator_loc, + predicate: defined_node( + location: node.name_loc, + value: public_send(read_class, location: node.name_loc, **arguments), + keyword_loc: node.operator_loc + ), + then_keyword_loc: node.operator_loc, + statements: statements_node( + location: node.location, + body: [public_send(read_class, location: node.name_loc, **arguments)] + ), + consequent: else_node( + location: node.location, + else_keyword_loc: node.operator_loc, + statements: statements_node( + location: node.location, + body: [ + public_send( + write_class, + location: node.location, + **arguments, + name_loc: node.name_loc, + value: node.value, + operator_loc: node.operator_loc + ) + ] ), - node.operator_loc, - node.location + end_keyword_loc: node.operator_loc ), - node.operator_loc, - node.location + end_keyword_loc: node.operator_loc ) end end class DesugarOperatorWriteNode # :nodoc: - attr_reader :node, :source, :read_class, :write_class, :arguments + include DSL - def initialize(node, source, read_class, write_class, *arguments) + attr_reader :node, :default_source, :read_class, :write_class, :arguments + + def initialize(node, default_source, read_class, write_class, **arguments) @node = node - @source = source + @default_source = default_source @read_class = read_class @write_class = write_class @arguments = arguments @@ -75,35 +100,41 @@ def initialize(node, source, read_class, write_class, *arguments) def compile binary_operator_loc = node.binary_operator_loc.chop - write_class.new( - source, - *arguments, - node.name_loc, - CallNode.new( - source, - 0, - read_class.new(source, *arguments, node.name_loc), - nil, - binary_operator_loc.slice.to_sym, - binary_operator_loc, - nil, - ArgumentsNode.new(source, 0, [node.value], node.value.location), - nil, - nil, - node.location + public_send( + write_class, + location: node.location, + **arguments, + name_loc: node.name_loc, + value: call_node( + location: node.location, + receiver: public_send( + read_class, + location: node.name_loc, + **arguments + ), + name: binary_operator_loc.slice.to_sym, + message_loc: binary_operator_loc, + arguments: arguments_node( + location: node.value.location, + arguments: [node.value] + ) ), - node.binary_operator_loc.copy(start_offset: node.binary_operator_loc.end_offset - 1, length: 1), - node.location + operator_loc: node.binary_operator_loc.copy( + start_offset: node.binary_operator_loc.end_offset - 1, + length: 1 + ) ) end end class DesugarOrWriteNode # :nodoc: - attr_reader :node, :source, :read_class, :write_class, :arguments + include DSL - def initialize(node, source, read_class, write_class, *arguments) + attr_reader :node, :default_source, :read_class, :write_class, :arguments + + def initialize(node, default_source, read_class, write_class, **arguments) @node = node - @source = source + @default_source = default_source @read_class = read_class @write_class = write_class @arguments = arguments @@ -111,12 +142,18 @@ def initialize(node, source, read_class, write_class, *arguments) # Desugar `x ||= y` to `x || x = y` def compile - OrNode.new( - source, - read_class.new(source, *arguments, node.name_loc), - write_class.new(source, *arguments, node.name_loc, node.value, node.operator_loc, node.location), - node.operator_loc, - node.location + or_node( + location: node.location, + left: public_send(read_class, location: node.name_loc, **arguments), + right: public_send( + write_class, + location: node.location, + **arguments, + name_loc: node.name_loc, + value: node.value, + operator_loc: node.operator_loc + ), + operator_loc: node.operator_loc ) end end @@ -125,91 +162,91 @@ def compile class ClassVariableAndWriteNode def desugar # :nodoc: - DesugarAndWriteNode.new(self, source, ClassVariableReadNode, ClassVariableWriteNode, name).compile + DesugarAndWriteNode.new(self, source, :class_variable_read_node, :class_variable_write_node, name: name).compile end end class ClassVariableOrWriteNode def desugar # :nodoc: - DesugarOrWriteDefinedNode.new(self, source, ClassVariableReadNode, ClassVariableWriteNode, name).compile + DesugarOrWriteDefinedNode.new(self, source, :class_variable_read_node, :class_variable_write_node, name: name).compile end end class ClassVariableOperatorWriteNode def desugar # :nodoc: - DesugarOperatorWriteNode.new(self, source, ClassVariableReadNode, ClassVariableWriteNode, name).compile + DesugarOperatorWriteNode.new(self, source, :class_variable_read_node, :class_variable_write_node, name: name).compile end end class ConstantAndWriteNode def desugar # :nodoc: - DesugarAndWriteNode.new(self, source, ConstantReadNode, ConstantWriteNode, name).compile + DesugarAndWriteNode.new(self, source, :constant_read_node, :constant_write_node, name: name).compile end end class ConstantOrWriteNode def desugar # :nodoc: - DesugarOrWriteDefinedNode.new(self, source, ConstantReadNode, ConstantWriteNode, name).compile + DesugarOrWriteDefinedNode.new(self, source, :constant_read_node, :constant_write_node, name: name).compile end end class ConstantOperatorWriteNode def desugar # :nodoc: - DesugarOperatorWriteNode.new(self, source, ConstantReadNode, ConstantWriteNode, name).compile + DesugarOperatorWriteNode.new(self, source, :constant_read_node, :constant_write_node, name: name).compile end end class GlobalVariableAndWriteNode def desugar # :nodoc: - DesugarAndWriteNode.new(self, source, GlobalVariableReadNode, GlobalVariableWriteNode, name).compile + DesugarAndWriteNode.new(self, source, :global_variable_read_node, :global_variable_write_node, name: name).compile end end class GlobalVariableOrWriteNode def desugar # :nodoc: - DesugarOrWriteDefinedNode.new(self, source, GlobalVariableReadNode, GlobalVariableWriteNode, name).compile + DesugarOrWriteDefinedNode.new(self, source, :global_variable_read_node, :global_variable_write_node, name: name).compile end end class GlobalVariableOperatorWriteNode def desugar # :nodoc: - DesugarOperatorWriteNode.new(self, source, GlobalVariableReadNode, GlobalVariableWriteNode, name).compile + DesugarOperatorWriteNode.new(self, source, :global_variable_read_node, :global_variable_write_node, name: name).compile end end class InstanceVariableAndWriteNode def desugar # :nodoc: - DesugarAndWriteNode.new(self, source, InstanceVariableReadNode, InstanceVariableWriteNode, name).compile + DesugarAndWriteNode.new(self, source, :instance_variable_read_node, :instance_variable_write_node, name: name).compile end end class InstanceVariableOrWriteNode def desugar # :nodoc: - DesugarOrWriteNode.new(self, source, InstanceVariableReadNode, InstanceVariableWriteNode, name).compile + DesugarOrWriteNode.new(self, source, :instance_variable_read_node, :instance_variable_write_node, name: name).compile end end class InstanceVariableOperatorWriteNode def desugar # :nodoc: - DesugarOperatorWriteNode.new(self, source, InstanceVariableReadNode, InstanceVariableWriteNode, name).compile + DesugarOperatorWriteNode.new(self, source, :instance_variable_read_node, :instance_variable_write_node, name: name).compile end end class LocalVariableAndWriteNode def desugar # :nodoc: - DesugarAndWriteNode.new(self, source, LocalVariableReadNode, LocalVariableWriteNode, name, depth).compile + DesugarAndWriteNode.new(self, source, :local_variable_read_node, :local_variable_write_node, name: name, depth: depth).compile end end class LocalVariableOrWriteNode def desugar # :nodoc: - DesugarOrWriteNode.new(self, source, LocalVariableReadNode, LocalVariableWriteNode, name, depth).compile + DesugarOrWriteNode.new(self, source, :local_variable_read_node, :local_variable_write_node, name: name, depth: depth).compile end end class LocalVariableOperatorWriteNode def desugar # :nodoc: - DesugarOperatorWriteNode.new(self, source, LocalVariableReadNode, LocalVariableWriteNode, name, depth).compile + DesugarOperatorWriteNode.new(self, source, :local_variable_read_node, :local_variable_write_node, name: name, depth: depth).compile end end diff --git a/lib/prism/node_ext.rb b/lib/prism/node_ext.rb index aa6a18cf296209..354bfdb2058000 100644 --- a/lib/prism/node_ext.rb +++ b/lib/prism/node_ext.rb @@ -21,7 +21,10 @@ module RegularExpressionOptions # :nodoc: # Returns a numeric value that represents the flags that were used to create # the regular expression. def options - o = flags & (RegularExpressionFlags::IGNORE_CASE | RegularExpressionFlags::EXTENDED | RegularExpressionFlags::MULTI_LINE) + o = 0 + o |= Regexp::IGNORECASE if flags.anybits?(RegularExpressionFlags::IGNORE_CASE) + o |= Regexp::EXTENDED if flags.anybits?(RegularExpressionFlags::EXTENDED) + o |= Regexp::MULTILINE if flags.anybits?(RegularExpressionFlags::MULTI_LINE) o |= Regexp::FIXEDENCODING if flags.anybits?(RegularExpressionFlags::EUC_JP | RegularExpressionFlags::WINDOWS_31J | RegularExpressionFlags::UTF_8) o |= Regexp::NOENCODING if flags.anybits?(RegularExpressionFlags::ASCII_8BIT) o @@ -69,11 +72,12 @@ class StringNode < Node def to_interpolated InterpolatedStringNode.new( source, + -1, + location, frozen? ? InterpolatedStringNodeFlags::FROZEN : 0, opening_loc, - [copy(opening_loc: nil, closing_loc: nil, location: content_loc)], - closing_loc, - location + [copy(location: content_loc, opening_loc: nil, closing_loc: nil)], + closing_loc ) end end @@ -86,10 +90,12 @@ class XStringNode < Node def to_interpolated InterpolatedXStringNode.new( source, + -1, + location, + flags, opening_loc, - [StringNode.new(source, 0, nil, content_loc, nil, unescaped, content_loc)], - closing_loc, - location + [StringNode.new(source, node_id, content_loc, 0, nil, content_loc, nil, unescaped)], + closing_loc ) end end @@ -115,9 +121,9 @@ def numeric deprecated("value", "numerator", "denominator") if denominator == 1 - IntegerNode.new(source, flags, numerator, location.chop) + IntegerNode.new(source, -1, location.chop, flags, numerator) else - FloatNode.new(source, numerator.to_f / denominator, location.chop) + FloatNode.new(source, -1, location.chop, 0, numerator.to_f / denominator) end end end @@ -195,7 +201,12 @@ def full_name # continue to supply that API. def child deprecated("name", "name_loc") - name ? ConstantReadNode.new(source, name, name_loc) : MissingNode.new(source, location) + + if name + ConstantReadNode.new(source, -1, name_loc, 0, name) + else + MissingNode.new(source, -1, location, 0) + end end end @@ -231,7 +242,12 @@ def full_name # continue to supply that API. def child deprecated("name", "name_loc") - name ? ConstantReadNode.new(source, name, name_loc) : MissingNode.new(source, location) + + if name + ConstantReadNode.new(source, -1, name_loc, 0, name) + else + MissingNode.new(source, -1, location, 0) + end end end diff --git a/lib/prism/parse_result.rb b/lib/prism/parse_result.rb index 798fde09e5a7c8..df1d66f44cb16b 100644 --- a/lib/prism/parse_result.rb +++ b/lib/prism/parse_result.rb @@ -10,7 +10,11 @@ class Source # 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) + if source.ascii_only? + ASCIISource.new(source, start_line, offsets) + else + new(source, start_line, offsets) + end end # The source code that this source object represents. @@ -87,7 +91,12 @@ def character_column(byte_offset) # encodings, it is not captured here. def code_units_offset(byte_offset, encoding) byteslice = (source.byteslice(0, byte_offset) or raise).encode(encoding) - (encoding == Encoding::UTF_16LE || encoding == Encoding::UTF_16BE) ? (byteslice.bytesize / 2) : byteslice.length + + if encoding == Encoding::UTF_16LE || encoding == Encoding::UTF_16BE + byteslice.bytesize / 2 + else + byteslice.length + end end # Returns the column number in code units for the given encoding for the @@ -575,9 +584,11 @@ def failure? # This is a result specific to the `parse` and `parse_file` methods. class ParseResult < Result autoload :Comments, "prism/parse_result/comments" + autoload :Errors, "prism/parse_result/errors" autoload :Newlines, "prism/parse_result/newlines" private_constant :Comments + private_constant :Errors private_constant :Newlines # The syntax tree that was parsed from the source code. @@ -604,6 +615,12 @@ def attach_comments! def mark_newlines! value.accept(Newlines.new(source.offsets.size)) # steep:ignore end + + # Returns a string representation of the syntax tree with the errors + # displayed inline. + def errors_format + Errors.new(self).format + end end # This is a result specific to the `lex` and `lex_file` methods. diff --git a/lib/prism/parse_result/errors.rb b/lib/prism/parse_result/errors.rb new file mode 100644 index 00000000000000..847a8442fedd9e --- /dev/null +++ b/lib/prism/parse_result/errors.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require "stringio" + +module Prism + class ParseResult < Result + # An object to represent the set of errors on a parse result. This object + # can be used to format the errors in a human-readable way. + class Errors + # The parse result that contains the errors. + attr_reader :parse_result + + # Initialize a new set of errors from the given parse result. + def initialize(parse_result) + @parse_result = parse_result + end + + # Formats the errors in a human-readable way and return them as a string. + def format + error_lines = {} + parse_result.errors.each do |error| + location = error.location + (location.start_line..location.end_line).each do |line| + error_lines[line] ||= [] + error_lines[line] << error + end + end + + source_lines = parse_result.source.source.lines + source_lines << "" if error_lines.key?(source_lines.size + 1) + + io = StringIO.new + source_lines.each.with_index(1) do |line, line_number| + io.puts(line) + + (error_lines.delete(line_number) || []).each do |error| + location = error.location + + case line_number + when location.start_line + io.print(" " * location.start_column + "^") + + if location.start_line == location.end_line + if location.start_column != location.end_column + io.print("~" * (location.end_column - location.start_column - 1)) + end + + io.puts(" " + error.message) + else + io.puts("~" * (line.bytesize - location.start_column)) + end + when location.end_line + io.puts("~" * location.end_column + " " + error.message) + else + io.puts("~" * line.bytesize) + end + end + end + + io.puts + io.string + end + end + end +end diff --git a/lib/prism/parse_result/newlines.rb b/lib/prism/parse_result/newlines.rb index 808a129a6bacf1..a04fa78a75c8b8 100644 --- a/lib/prism/parse_result/newlines.rb +++ b/lib/prism/parse_result/newlines.rb @@ -45,7 +45,7 @@ def visit_block_node(node) # Mark if/unless nodes as newlines. def visit_if_node(node) - node.newline!(@lines) + node.newline_flag!(@lines) super(node) end @@ -54,7 +54,7 @@ def visit_if_node(node) # Permit statements lists to mark newlines within themselves. def visit_statements_node(node) node.body.each do |child| - child.newline!(@lines) + child.newline_flag!(@lines) end super(node) end @@ -62,93 +62,93 @@ def visit_statements_node(node) end class Node - def newline? # :nodoc: - @newline ? true : false + def newline_flag? # :nodoc: + @newline_flag ? true : false end - def newline!(lines) # :nodoc: + def newline_flag!(lines) # :nodoc: line = location.start_line unless lines[line] lines[line] = true - @newline = true + @newline_flag = true end end end class BeginNode < Node - def newline!(lines) # :nodoc: + def newline_flag!(lines) # :nodoc: # Never mark BeginNode with a newline flag, mark children instead. end end class ParenthesesNode < Node - def newline!(lines) # :nodoc: + def newline_flag!(lines) # :nodoc: # Never mark ParenthesesNode with a newline flag, mark children instead. end end class IfNode < Node - def newline!(lines) # :nodoc: - predicate.newline!(lines) + def newline_flag!(lines) # :nodoc: + predicate.newline_flag!(lines) end end class UnlessNode < Node - def newline!(lines) # :nodoc: - predicate.newline!(lines) + def newline_flag!(lines) # :nodoc: + predicate.newline_flag!(lines) end end class UntilNode < Node - def newline!(lines) # :nodoc: - predicate.newline!(lines) + def newline_flag!(lines) # :nodoc: + predicate.newline_flag!(lines) end end class WhileNode < Node - def newline!(lines) # :nodoc: - predicate.newline!(lines) + def newline_flag!(lines) # :nodoc: + predicate.newline_flag!(lines) end end class RescueModifierNode < Node - def newline!(lines) # :nodoc: - expression.newline!(lines) + def newline_flag!(lines) # :nodoc: + expression.newline_flag!(lines) end end class InterpolatedMatchLastLineNode < Node - def newline!(lines) # :nodoc: + def newline_flag!(lines) # :nodoc: first = parts.first - first.newline!(lines) if first + first.newline_flag!(lines) if first end end class InterpolatedRegularExpressionNode < Node - def newline!(lines) # :nodoc: + def newline_flag!(lines) # :nodoc: first = parts.first - first.newline!(lines) if first + first.newline_flag!(lines) if first end end class InterpolatedStringNode < Node - def newline!(lines) # :nodoc: + def newline_flag!(lines) # :nodoc: first = parts.first - first.newline!(lines) if first + first.newline_flag!(lines) if first end end class InterpolatedSymbolNode < Node - def newline!(lines) # :nodoc: + def newline_flag!(lines) # :nodoc: first = parts.first - first.newline!(lines) if first + first.newline_flag!(lines) if first end end class InterpolatedXStringNode < Node - def newline!(lines) # :nodoc: + def newline_flag!(lines) # :nodoc: first = parts.first - first.newline!(lines) if first + first.newline_flag!(lines) if first end end end diff --git a/lib/prism/prism.gemspec b/lib/prism/prism.gemspec index b39ba706dcde13..08212318bc18e2 100644 --- a/lib/prism/prism.gemspec +++ b/lib/prism/prism.gemspec @@ -82,6 +82,7 @@ Gem::Specification.new do |spec| "lib/prism/pack.rb", "lib/prism/parse_result.rb", "lib/prism/parse_result/comments.rb", + "lib/prism/parse_result/errors.rb", "lib/prism/parse_result/newlines.rb", "lib/prism/pattern.rb", "lib/prism/polyfill/byteindex.rb", @@ -103,6 +104,7 @@ Gem::Specification.new do |spec| "prism.gemspec", "rbi/prism.rbi", "rbi/prism/compiler.rbi", + "rbi/prism/dsl.rbi", "rbi/prism/inspect_visitor.rbi", "rbi/prism/node_ext.rbi", "rbi/prism/node.rbi", diff --git a/lib/prism/translation/ruby_parser.rb b/lib/prism/translation/ruby_parser.rb index 38690c54b3e84d..45b9c4690b36a6 100644 --- a/lib/prism/translation/ruby_parser.rb +++ b/lib/prism/translation/ruby_parser.rb @@ -55,7 +55,19 @@ def visit_alternation_pattern_node(node) # a and b # ^^^^^^^ def visit_and_node(node) - s(node, :and, visit(node.left), visit(node.right)) + left = visit(node.left) + + if left[0] == :and + # ruby_parser has the and keyword as right-associative as opposed to + # prism which has it as left-associative. We reverse that + # associativity here. + nest = left + nest = nest[2] while nest[2][0] == :and + nest[2] = s(node, :and, nest[2], visit(node.right)) + left + else + s(node, :and, left, visit(node.right)) + end end # [] @@ -251,6 +263,11 @@ def visit_call_node(node) when RegularExpressionNode, InterpolatedRegularExpressionNode return s(node, :match2, visit(node.receiver), visit(node.arguments.arguments.first)) end + + case node.arguments.arguments.first + when RegularExpressionNode, InterpolatedRegularExpressionNode + return s(node, :match3, visit(node.arguments.arguments.first), visit(node.receiver)) + end end end @@ -876,6 +893,13 @@ def visit_interpolated_x_string_node(node) visited << result end elsif result[0] == :dstr + if !visited.empty? && part.parts[0].is_a?(StringNode) + # If we are in the middle of an implicitly concatenated string, + # we should not have a bare string as the first part. In this + # case we need to visit just that first part and then we can + # push the rest of the parts onto the visited array. + result[1] = visit(part.parts[0]) + end visited.concat(result[1..-1]) else visited << result @@ -1136,7 +1160,19 @@ def visit_optional_parameter_node(node) # a or b # ^^^^^^ def visit_or_node(node) - s(node, :or, visit(node.left), visit(node.right)) + left = visit(node.left) + + if left[0] == :or + # ruby_parser has the or keyword as right-associative as opposed to + # prism which has it as left-associative. We reverse that + # associativity here. + nest = left + nest = nest[2] while nest[2][0] == :or + nest[2] = s(node, :or, nest[2], visit(node.right)) + left + else + s(node, :or, left, visit(node.right)) + end end # def foo(bar, *baz); end diff --git a/lib/rdoc/code_object/alias.rb b/lib/rdoc/code_object/alias.rb index 446cf9ccb4855f..92df7e448f12d9 100644 --- a/lib/rdoc/code_object/alias.rb +++ b/lib/rdoc/code_object/alias.rb @@ -70,7 +70,7 @@ def full_old_name # HTML id-friendly version of +#new_name+. def html_name - CGI.escape(@new_name.gsub('-', '-2D')).gsub('%','-').sub(/^-/, '') + CGI.escape(@new_name.gsub('-', '-2D')).gsub('%', '-').sub(/^-/, '') end def inspect # :nodoc: diff --git a/lib/rdoc/code_object/method_attr.rb b/lib/rdoc/code_object/method_attr.rb index 61ddb32f46edb0..27e6599bc1600c 100644 --- a/lib/rdoc/code_object/method_attr.rb +++ b/lib/rdoc/code_object/method_attr.rb @@ -268,8 +268,8 @@ def block_params=(value) when 'const_get' then 'const' when 'new' then $1.split('::').last. # ClassName => class_name - gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). - gsub(/([a-z\d])([A-Z])/,'\1_\2'). + gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2'). + gsub(/([a-z\d])([A-Z])/, '\1_\2'). downcase else $2 @@ -291,7 +291,7 @@ def block_params=(value) def html_name require 'cgi/util' - CGI.escape(@name.gsub('-', '-2D')).gsub('%','-').sub(/^-/, '') + CGI.escape(@name.gsub('-', '-2D')).gsub('%', '-').sub(/^-/, '') end ## diff --git a/lib/rdoc/code_object/top_level.rb b/lib/rdoc/code_object/top_level.rb index 3864f66431160c..b93e3802fc973c 100644 --- a/lib/rdoc/code_object/top_level.rb +++ b/lib/rdoc/code_object/top_level.rb @@ -183,8 +183,8 @@ def inspect # :nodoc: "#<%s:0x%x %p modules: %p classes: %p>" % [ self.class, object_id, base_name, - @modules.map { |n,m| m }, - @classes.map { |n,c| c } + @modules.map { |n, m| m }, + @classes.map { |n, c| c } ] end @@ -254,8 +254,8 @@ def pretty_print q # :nodoc: q.text "base name: #{base_name.inspect}" q.breakable - items = @modules.map { |n,m| m } - items.concat @modules.map { |n,c| c } + items = @modules.map { |n, m| m } + items.concat @modules.map { |n, c| c } q.seplist items do |mod| q.pp mod end end end diff --git a/lib/rdoc/generator/pot/message_extractor.rb b/lib/rdoc/generator/pot/message_extractor.rb index 313dfd2dc71393..4938858bdc4c8b 100644 --- a/lib/rdoc/generator/pot/message_extractor.rb +++ b/lib/rdoc/generator/pot/message_extractor.rb @@ -29,7 +29,7 @@ def extract_from_klass klass extract_text(klass.comment_location, klass.full_name) klass.each_section do |section, constants, attributes| - extract_text(section.title ,"#{klass.full_name}: section title") + extract_text(section.title, "#{klass.full_name}: section title") section.comments.each do |comment| extract_text(comment, "#{klass.full_name}: #{section.title}") end diff --git a/lib/rdoc/markdown.rb b/lib/rdoc/markdown.rb index 1416059763c092..881d19ecb49041 100644 --- a/lib/rdoc/markdown.rb +++ b/lib/rdoc/markdown.rb @@ -1158,7 +1158,7 @@ def _AtxStart return _tmp end - # AtxHeading = AtxStart:s @Sp AtxInline+:a (@Sp /#*/ @Sp)? @Newline { RDoc::Markup::Heading.new(s, a.join) } + # AtxHeading = AtxStart:s @Spacechar+ AtxInline+:a (@Sp /#*/ @Sp)? @Newline { RDoc::Markup::Heading.new(s, a.join) } def _AtxHeading _save = self.pos @@ -1169,12 +1169,22 @@ def _AtxHeading self.pos = _save break end - _tmp = _Sp() + _save1 = self.pos + _tmp = _Spacechar() + if _tmp + while true + _tmp = _Spacechar() + break unless _tmp + end + _tmp = true + else + self.pos = _save1 + end unless _tmp self.pos = _save break end - _save1 = self.pos + _save2 = self.pos _ary = [] _tmp = apply(:_AtxInline) if _tmp @@ -1187,37 +1197,37 @@ def _AtxHeading _tmp = true @result = _ary else - self.pos = _save1 + self.pos = _save2 end a = @result unless _tmp self.pos = _save break end - _save2 = self.pos - _save3 = self.pos + + _save4 = self.pos while true # sequence _tmp = _Sp() unless _tmp - self.pos = _save3 + self.pos = _save4 break end _tmp = scan(/\G(?-mix:#*)/) unless _tmp - self.pos = _save3 + self.pos = _save4 break end _tmp = _Sp() unless _tmp - self.pos = _save3 + self.pos = _save4 end break end # end sequence unless _tmp _tmp = true - self.pos = _save2 + self.pos = _save3 end unless _tmp self.pos = _save @@ -16539,7 +16549,7 @@ def _DefinitionListDefinition Rules[:_Plain] = rule_info("Plain", "Inlines:a { paragraph a }") Rules[:_AtxInline] = rule_info("AtxInline", "!@Newline !(@Sp /\#*/ @Sp @Newline) Inline") Rules[:_AtxStart] = rule_info("AtxStart", "< /\\\#{1,6}/ > { text.length }") - Rules[:_AtxHeading] = rule_info("AtxHeading", "AtxStart:s @Sp AtxInline+:a (@Sp /\#*/ @Sp)? @Newline { RDoc::Markup::Heading.new(s, a.join) }") + Rules[:_AtxHeading] = rule_info("AtxHeading", "AtxStart:s @Spacechar+ AtxInline+:a (@Sp /\#*/ @Sp)? @Newline { RDoc::Markup::Heading.new(s, a.join) }") Rules[:_SetextHeading] = rule_info("SetextHeading", "(SetextHeading1 | SetextHeading2)") Rules[:_SetextBottom1] = rule_info("SetextBottom1", "/={1,}/ @Newline") Rules[:_SetextBottom2] = rule_info("SetextBottom2", "/-{1,}/ @Newline") diff --git a/lib/rdoc/markup/attribute_manager.rb b/lib/rdoc/markup/attribute_manager.rb index f6eb06da953296..ed014f255ba611 100644 --- a/lib/rdoc/markup/attribute_manager.rb +++ b/lib/rdoc/markup/attribute_manager.rb @@ -260,7 +260,7 @@ def unmask_protected_sequences def add_word_pair(start, stop, name, exclusive = false) raise ArgumentError, "Word flags may not start with '<'" if - start[0,1] == '<' + start[0, 1] == '<' bitmap = @attributes.bitmap_for name @@ -271,7 +271,7 @@ def add_word_pair(start, stop, name, exclusive = false) @word_pair_map[pattern] = bitmap end - @protectable << start[0,1] + @protectable << start[0, 1] @protectable.uniq! @exclusive_bitmap |= bitmap if exclusive diff --git a/lib/rdoc/markup/to_bs.rb b/lib/rdoc/markup/to_bs.rb index afd9d6e9812497..b7b73e73f7d267 100644 --- a/lib/rdoc/markup/to_bs.rb +++ b/lib/rdoc/markup/to_bs.rb @@ -24,7 +24,7 @@ def initialize markup = nil def init_tags add_tag :BOLD, '+b', '-b' add_tag :EM, '+_', '-_' - add_tag :TT, '' , '' # we need in_tt information maintained + add_tag :TT, '', '' # we need in_tt information maintained end ## diff --git a/lib/rdoc/options.rb b/lib/rdoc/options.rb index 7518e6cc5436fe..c8fca4a262a6f8 100644 --- a/lib/rdoc/options.rb +++ b/lib/rdoc/options.rb @@ -683,7 +683,7 @@ def parse argv EOF - parsers = Hash.new { |h,parser| h[parser] = [] } + parsers = Hash.new { |h, parser| h[parser] = [] } RDoc::Parser.parsers.each do |regexp, parser| parsers[parser.name.sub('RDoc::Parser::', '')] << regexp.source diff --git a/lib/rdoc/parser/c.rb b/lib/rdoc/parser/c.rb index f8f238fd74906b..4050d7aa49248d 100644 --- a/lib/rdoc/parser/c.rb +++ b/lib/rdoc/parser/c.rb @@ -440,7 +440,7 @@ def do_constants # Scans #content for rb_include_module def do_includes - @content.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c,m| + @content.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c, m| next unless cls = @classes[c] m = @known_classes[m] || m diff --git a/lib/rdoc/parser/changelog.rb b/lib/rdoc/parser/changelog.rb index 4014c6eb1c678a..12a50f8d0ef757 100644 --- a/lib/rdoc/parser/changelog.rb +++ b/lib/rdoc/parser/changelog.rb @@ -193,7 +193,7 @@ class << self; prepend Git; end entries << [entry_name, entry_body] if entry_name - entries.reject! do |(entry,_)| + entries.reject! do |(entry, _)| entry == nil end diff --git a/lib/rdoc/parser/ripper_state_lex.rb b/lib/rdoc/parser/ripper_state_lex.rb index f6cefd03058f40..2212906bbdc4d2 100644 --- a/lib/rdoc/parser/ripper_state_lex.rb +++ b/lib/rdoc/parser/ripper_state_lex.rb @@ -7,307 +7,12 @@ class RDoc::Parser::RipperStateLex # :stopdoc: - # TODO: Remove this constants after Ruby 2.4 EOL - RIPPER_HAS_LEX_STATE = Ripper::Filter.method_defined?(:state) - Token = Struct.new(:line_no, :char_no, :kind, :text, :state) - EXPR_NONE = 0 - EXPR_BEG = 1 - EXPR_END = 2 - EXPR_ENDARG = 4 - EXPR_ENDFN = 8 - EXPR_ARG = 16 - EXPR_CMDARG = 32 - EXPR_MID = 64 - EXPR_FNAME = 128 - EXPR_DOT = 256 - EXPR_CLASS = 512 - EXPR_LABEL = 1024 - EXPR_LABELED = 2048 - EXPR_FITEM = 4096 - EXPR_VALUE = EXPR_BEG - EXPR_BEG_ANY = (EXPR_BEG | EXPR_MID | EXPR_CLASS) - EXPR_ARG_ANY = (EXPR_ARG | EXPR_CMDARG) - EXPR_END_ANY = (EXPR_END | EXPR_ENDARG | EXPR_ENDFN) - - class InnerStateLex < Ripper::Filter - attr_accessor :lex_state - - def initialize(code) - @lex_state = EXPR_BEG - @in_fname = false - @continue = false - reset - super(code) - end - - def reset - @command_start = false - @cmd_state = @command_start - end - - def on_nl(tok, data) - case @lex_state - when EXPR_FNAME, EXPR_DOT - @continue = true - else - @continue = false - @lex_state = EXPR_BEG unless (EXPR_LABEL & @lex_state) != 0 - end - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_ignored_nl(tok, data) - case @lex_state - when EXPR_FNAME, EXPR_DOT - @continue = true - else - @continue = false - @lex_state = EXPR_BEG unless (EXPR_LABEL & @lex_state) != 0 - end - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_op(tok, data) - case tok - when '&', '|', '!', '!=', '!~' - case @lex_state - when EXPR_FNAME, EXPR_DOT - @lex_state = EXPR_ARG - else - @lex_state = EXPR_BEG - end - when '<<' - # TODO next token? - case @lex_state - when EXPR_FNAME, EXPR_DOT - @lex_state = EXPR_ARG - else - @lex_state = EXPR_BEG - end - when '?' - @lex_state = EXPR_BEG - when '&&', '||', '+=', '-=', '*=', '**=', - '&=', '|=', '^=', '<<=', '>>=', '||=', '&&=' - @lex_state = EXPR_BEG - when '::' - case @lex_state - when EXPR_ARG, EXPR_CMDARG - @lex_state = EXPR_DOT - when EXPR_FNAME, EXPR_DOT - @lex_state = EXPR_ARG - else - @lex_state = EXPR_BEG - end - else - case @lex_state - when EXPR_FNAME, EXPR_DOT - @lex_state = EXPR_ARG - else - @lex_state = EXPR_BEG - end - end - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_kw(tok, data) - case tok - when 'class' - @lex_state = EXPR_CLASS - @in_fname = true - when 'def' - @lex_state = EXPR_FNAME - @continue = true - @in_fname = true - when 'if', 'unless', 'while', 'until' - if ((EXPR_MID | EXPR_END | EXPR_ENDARG | EXPR_ENDFN | EXPR_ARG | EXPR_CMDARG) & @lex_state) != 0 # postfix if - @lex_state = EXPR_BEG | EXPR_LABEL - else - @lex_state = EXPR_BEG - end - when 'begin', 'case', 'when' - @lex_state = EXPR_BEG - when 'return', 'break' - @lex_state = EXPR_MID - else - if @lex_state == EXPR_FNAME - @lex_state = EXPR_END - else - @lex_state = EXPR_END - end - end - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_tstring_beg(tok, data) - @lex_state = EXPR_BEG - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_tstring_end(tok, data) - @lex_state = EXPR_END | EXPR_ENDARG - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_CHAR(tok, data) - @lex_state = EXPR_END - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_period(tok, data) - @lex_state = EXPR_DOT - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_int(tok, data) - @lex_state = EXPR_END | EXPR_ENDARG - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_float(tok, data) - @lex_state = EXPR_END | EXPR_ENDARG - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_rational(tok, data) - @lex_state = EXPR_END | EXPR_ENDARG - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_imaginary(tok, data) - @lex_state = EXPR_END | EXPR_ENDARG - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_symbeg(tok, data) - @lex_state = EXPR_FNAME - @continue = true - @in_fname = true - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - private def on_variables(event, tok, data) - if @in_fname - @lex_state = EXPR_ENDFN - @in_fname = false - @continue = false - elsif @continue - case @lex_state - when EXPR_DOT - @lex_state = EXPR_ARG - else - @lex_state = EXPR_ENDFN - @continue = false - end - else - @lex_state = EXPR_CMDARG - end - data << Token.new(lineno, column, event, tok, @lex_state) - end - - def on_ident(tok, data) - on_variables(__method__, tok, data) - end - - def on_ivar(tok, data) - @lex_state = EXPR_END - on_variables(__method__, tok, data) - end - - def on_cvar(tok, data) - @lex_state = EXPR_END - on_variables(__method__, tok, data) - end - - def on_gvar(tok, data) - @lex_state = EXPR_END - on_variables(__method__, tok, data) - end - - def on_backref(tok, data) - @lex_state = EXPR_END - on_variables(__method__, tok, data) - end - - def on_lparen(tok, data) - @lex_state = EXPR_LABEL | EXPR_BEG - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_rparen(tok, data) - @lex_state = EXPR_ENDFN - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_lbrace(tok, data) - @lex_state = EXPR_LABEL | EXPR_BEG - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_rbrace(tok, data) - @lex_state = EXPR_ENDARG - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_lbracket(tok, data) - @lex_state = EXPR_LABEL | EXPR_BEG - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_rbracket(tok, data) - @lex_state = EXPR_ENDARG - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_const(tok, data) - case @lex_state - when EXPR_FNAME - @lex_state = EXPR_ENDFN - when EXPR_CLASS, EXPR_CMDARG, EXPR_MID - @lex_state = EXPR_ARG - else - @lex_state = EXPR_CMDARG - end - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_sp(tok, data) - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_comma(tok, data) - @lex_state = EXPR_BEG | EXPR_LABEL if (EXPR_ARG_ANY & @lex_state) != 0 - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_comment(tok, data) - @lex_state = EXPR_BEG unless (EXPR_LABEL & @lex_state) != 0 - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_ignored_sp(tok, data) - @lex_state = EXPR_BEG unless (EXPR_LABEL & @lex_state) != 0 - data << Token.new(lineno, column, __method__, tok, @lex_state) - end - - def on_heredoc_beg(tok, data) - data << Token.new(lineno, column, __method__, tok, @lex_state) - @lex_state = EXPR_END - data - end - - def on_heredoc_end(tok, data) - data << Token.new(lineno, column, __method__, tok, @lex_state) - @lex_state = EXPR_BEG - data - end - - def on_default(event, tok, data) - reset - data << Token.new(lineno, column, event, tok, @lex_state) - end - end unless RIPPER_HAS_LEX_STATE + EXPR_END = Ripper::EXPR_END + EXPR_ENDFN = Ripper::EXPR_ENDFN + EXPR_ARG = Ripper::EXPR_ARG + EXPR_FNAME = Ripper::EXPR_FNAME class InnerStateLex < Ripper::Filter def initialize(code) @@ -317,7 +22,7 @@ def initialize(code) def on_default(event, tok, data) data << Token.new(lineno, column, event, tok, state) end - end if RIPPER_HAS_LEX_STATE + end def get_squashed_tk if @buf.empty? @@ -333,9 +38,8 @@ def get_squashed_tk tk = get_string_tk(tk) when :on_backtick then if (tk[:state] & (EXPR_FNAME | EXPR_ENDFN)) != 0 - @inner_lex.lex_state = EXPR_ARG unless RIPPER_HAS_LEX_STATE tk[:kind] = :on_ident - tk[:state] = Ripper::Lexer.const_defined?(:State) ? Ripper::Lexer::State.new(EXPR_ARG) : EXPR_ARG + tk[:state] = Ripper::Lexer::State.new(EXPR_ARG) else tk = get_string_tk(tk) end @@ -345,7 +49,6 @@ def get_squashed_tk tk = get_embdoc_tk(tk) when :on_heredoc_beg then @heredoc_queue << retrieve_heredoc_info(tk) - @inner_lex.lex_state = EXPR_END unless RIPPER_HAS_LEX_STATE when :on_nl, :on_ignored_nl, :on_comment, :on_heredoc_end then if !@heredoc_queue.empty? get_heredoc_tk(*@heredoc_queue.shift) @@ -549,8 +252,7 @@ def get_squashed_tk private def get_op_tk(tk) redefinable_operators = %w[! != !~ % & * ** + +@ - -@ / < << <= <=> == === =~ > >= >> [] []= ^ ` | ~] if redefinable_operators.include?(tk[:text]) and tk[:state] == EXPR_ARG then - @inner_lex.lex_state = EXPR_ARG unless RIPPER_HAS_LEX_STATE - tk[:state] = Ripper::Lexer.const_defined?(:State) ? Ripper::Lexer::State.new(EXPR_ARG) : EXPR_ARG + tk[:state] = Ripper::Lexer::State.new(EXPR_ARG) tk[:kind] = :on_ident elsif tk[:text] =~ /^[-+]$/ then tk_ahead = get_squashed_tk diff --git a/lib/rdoc/parser/ruby.rb b/lib/rdoc/parser/ruby.rb index 85f1cd03913963..47ad770daf0c38 100644 --- a/lib/rdoc/parser/ruby.rb +++ b/lib/rdoc/parser/ruby.rb @@ -513,7 +513,7 @@ def get_included_module_with_optional_parens when :on_comment, :on_embdoc then @read.pop if :on_nl == end_token[:kind] and "\n" == tk[:text][-1] and - (!continue or (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) != 0) then + (!continue or (tk[:state] & Ripper::EXPR_LABEL) != 0) then break if !continue and nest <= 0 end when :on_comma then @@ -526,7 +526,7 @@ def get_included_module_with_optional_parens nest += 1 when 'if', 'unless', 'while', 'until', 'rescue' # postfix if/unless/while/until/rescue must be EXPR_LABEL - nest += 1 unless (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) != 0 + nest += 1 unless (tk[:state] & Ripper::EXPR_LABEL) != 0 when 'end' nest -= 1 break if nest == 0 @@ -1041,7 +1041,7 @@ def parse_constant_body container, constant, is_array_or_hash # :nodoc: elsif (:on_kw == tk[:kind] && 'def' == tk[:text]) then nest += 1 elsif (:on_kw == tk[:kind] && %w{do if unless case begin}.include?(tk[:text])) then - if (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) == 0 + if (tk[:state] & Ripper::EXPR_LABEL) == 0 nest += 1 end elsif [:on_rparen, :on_rbrace, :on_rbracket].include?(tk[:kind]) || @@ -1662,7 +1662,7 @@ def parse_method_or_yield_parameters(method = nil, when :on_comment, :on_embdoc then @read.pop if :on_nl == end_token[:kind] and "\n" == tk[:text][-1] and - (!continue or (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) != 0) then + (!continue or (tk[:state] & Ripper::EXPR_LABEL) != 0) then if method && method.block_params.nil? then unget_tk tk read_documentation_modifiers method, modifiers @@ -1882,7 +1882,7 @@ def parse_statements(container, single = NORMAL, current_method = nil, end when 'until', 'while' then - if (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) == 0 + if (tk[:state] & Ripper::EXPR_LABEL) == 0 nest += 1 skip_optional_do_after_expression end @@ -1898,7 +1898,7 @@ def parse_statements(container, single = NORMAL, current_method = nil, skip_optional_do_after_expression when 'case', 'do', 'if', 'unless', 'begin' then - if (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) == 0 + if (tk[:state] & Ripper::EXPR_LABEL) == 0 nest += 1 end diff --git a/lib/reline.rb b/lib/reline.rb index 720df286a1a03a..b851144627ffc3 100644 --- a/lib/reline.rb +++ b/lib/reline.rb @@ -331,7 +331,7 @@ def readline(prompt = '', add_hist = false) end end - line_editor.print_nomultiline_prompt(prompt) + line_editor.print_nomultiline_prompt line_editor.update_dialogs line_editor.rerender diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb index 439dbb972757fe..9c97415050b233 100644 --- a/lib/reline/line_editor.rb +++ b/lib/reline/line_editor.rb @@ -490,13 +490,9 @@ def render_full_content @output.puts lines.map { |l| "#{l}\r\n" }.join end - def print_nomultiline_prompt(prompt) - return unless prompt && !@is_multiline - + def print_nomultiline_prompt # Readline's test `TestRelineAsReadline#test_readline` requires first output to be prompt, not cursor reset escape sequence. - @rendered_screen.lines = [[[0, Reline::Unicode.calculate_width(prompt, true), prompt]]] - @rendered_screen.cursor_y = 0 - @output.write prompt + @output.write @prompt if @prompt && !@is_multiline end def render_differential diff --git a/lib/rubygems.rb b/lib/rubygems.rb index 6de5b6e68c5536..2b52cde0a7494c 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -780,6 +780,7 @@ def self.read_binary(path) ## # Safely write a file in binary mode on all platforms. + def self.write_binary(path, data) open_file(path, "wb") do |io| io.write data @@ -791,33 +792,30 @@ def self.write_binary(path, data) end ## - # Open a file with given flags. It requires special logic on Windows, like - # protecting access with flock + # Open a file with given flags def self.open_file(path, flags, &block) - if !java_platform? && win_platform? - open_file_with_flock(path, flags, &block) - else - open_file_without_flock(path, flags, &block) - end + File.open(path, flags, &block) end ## # Open a file with given flags, and protect access with flock - def self.open_file_with_flock(path, flags, &block) + def self.open_file_with_flock(path, &block) + flags = File.exist?(path) ? "r+" : "a+" + File.open(path, flags) do |io| begin io.flock(File::LOCK_EX) rescue Errno::ENOSYS, Errno::ENOTSUP end yield io - end - rescue Errno::ENOLCK # NFS - if Thread.main != Thread.current - raise - else - open_file_without_flock(path, flags, &block) + rescue Errno::ENOLCK # NFS + if Thread.main != Thread.current + raise + else + open_file(path, flags, &block) + end end end @@ -1317,10 +1315,6 @@ def clear_default_specs private - def open_file_without_flock(path, flags, &block) - File.open(path, flags, &block) - end - def already_loaded?(file) $LOADED_FEATURES.any? do |feature_path| feature_path.end_with?(file) && default_gem_load_paths.any? {|load_path_entry| feature_path == "#{load_path_entry}/#{file}" } diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb index 7874ad0dc9a1ca..a2bcb6dfbc249b 100644 --- a/lib/rubygems/config_file.rb +++ b/lib/rubygems/config_file.rb @@ -522,12 +522,12 @@ def write # Return the configuration information for +key+. def [](key) - @hash[key.to_s] + @hash[key] || @hash[key.to_s] end # Set configuration option +key+ to +value+. def []=(key, value) - @hash[key.to_s] = value + @hash[key] = value end def ==(other) # :nodoc: @@ -555,8 +555,13 @@ def self.load_with_rubygems_config_hash(yaml) require_relative "yaml_serializer" content = Gem::YAMLSerializer.load(yaml) + deep_transform_config_keys!(content) + end - content.transform_keys! do |k| + private + + def self.deep_transform_config_keys!(config) + config.transform_keys! do |k| if k.match?(/\A:(.*)\Z/) k[1..-1].to_sym elsif k.include?("__") || k.match?(%r{/\Z}) @@ -570,7 +575,7 @@ def self.load_with_rubygems_config_hash(yaml) end end - content.transform_values! do |v| + config.transform_values! do |v| if v.is_a?(String) if v.match?(/\A:(.*)\Z/) v[1..-1].to_sym @@ -583,18 +588,18 @@ def self.load_with_rubygems_config_hash(yaml) else v end - elsif v.is_a?(Hash) && v.empty? + elsif v.empty? nil + elsif v.is_a?(Hash) + deep_transform_config_keys!(v) else v end end - content + config end - private - def set_config_file_name(args) @config_file_name = ENV["GEMRC"] need_config_file_name = false diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb index 453a18e836917d..d558c0be2bfaaa 100644 --- a/lib/rubygems/installer.rb +++ b/lib/rubygems/installer.rb @@ -222,7 +222,7 @@ def check_executable_overwrite(filename) # :nodoc: ruby_executable = false existing = nil - Gem.open_file_with_flock generated_bin, "rb+" do |io| + File.open generated_bin, "rb" do |io| line = io.gets shebang = /^#!.*ruby/o @@ -500,8 +500,7 @@ def generate_bin # :nodoc: dir_mode = options[:prog_mode] || (mode | 0o111) unless dir_mode == mode - require "fileutils" - FileUtils.chmod dir_mode, bin_path + File.chmod dir_mode, bin_path end check_executable_overwrite filename @@ -539,12 +538,14 @@ def generate_plugins # :nodoc: def generate_bin_script(filename, bindir) bin_script_path = File.join bindir, formatted_program_filename(filename) - require "fileutils" - FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers + Gem.open_file_with_flock("#{bin_script_path}.lock") do + require "fileutils" + FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers - File.open bin_script_path, "wb", 0o755 do |file| - file.print app_script_text(filename) - file.chmod(options[:prog_mode] || 0o755) + File.open(bin_script_path, "wb", 0o755) do |file| + file.write app_script_text(filename) + file.chmod(options[:prog_mode] || 0o755) + end end verbose bin_script_path diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb index 9ef61ba2181520..450c2141676e58 100644 --- a/lib/rubygems/platform.rb +++ b/lib/rubygems/platform.rb @@ -177,7 +177,7 @@ def hash # :nodoc: # they have the same version, or either one has no version # # Additionally, the platform will match if the local CPU is 'arm' and the - # other CPU starts with "arm" (for generic ARM family support). + # other CPU starts with "armv" (for generic 32-bit ARM family support). # # Of note, this method is not commutative. Indeed the OS 'linux' has a # special case: the version is the libc name, yet while "no version" stands @@ -198,7 +198,7 @@ def ===(other) # cpu ([nil,"universal"].include?(@cpu) || [nil, "universal"].include?(other.cpu) || @cpu == other.cpu || - (@cpu == "arm" && other.cpu.start_with?("arm"))) && + (@cpu == "arm" && other.cpu.start_with?("armv"))) && # os @os == other.os && diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index 7160064dd2eeac..d0e0e4e91ac6ab 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -175,7 +175,7 @@ class Gem::Specification < Gem::BasicSpecification end @@attributes = @@default_value.keys.sort_by(&:to_s) - @@array_attributes = @@default_value.reject {|_k,v| v != [] }.keys + @@array_attributes = @@default_value.select {|_k,v| v.is_a?(Array) }.keys @@nil_attributes, @@non_nil_attributes = @@default_value.keys.partition do |k| @@default_value[k].nil? end @@ -874,7 +874,7 @@ def self.remove_spec(spec) # You probably want to use one of the Enumerable methods instead. def self.all - warn "NOTE: Specification.all called from #{caller.first}" unless + warn "NOTE: Specification.all called from #{caller(1, 1).first}" unless Gem::Deprecate.skip _all end diff --git a/main.c b/main.c index ddfa0569a22000..92363d3fb7f013 100644 --- a/main.c +++ b/main.c @@ -24,6 +24,9 @@ #ifdef HAVE_LOCALE_H #include #endif +#if USE_SHARED_GC +#include "internal/gc.h" +#endif #if defined RUBY_DEVEL && !defined RUBY_DEBUG_ENV # define RUBY_DEBUG_ENV 1 @@ -32,15 +35,10 @@ # undef RUBY_DEBUG_ENV #endif -void ruby_load_external_gc_from_argv(int argc, char **argv); - static int rb_main(int argc, char **argv) { RUBY_INIT_STACK; -#if USE_SHARED_GC - ruby_load_external_gc_from_argv(argc, argv); -#endif ruby_init(); return ruby_run_node(ruby_options(argc, argv)); } diff --git a/numeric.c b/numeric.c index 785bdc53d7ed89..98c50f46faa944 100644 --- a/numeric.c +++ b/numeric.c @@ -2213,36 +2213,78 @@ flo_floor(int argc, VALUE *argv, VALUE num) } /* + * :markup: markdown + * * call-seq: * ceil(ndigits = 0) -> float or integer * - * Returns the smallest number greater than or equal to +self+ with - * a precision of +ndigits+ decimal digits. - * - * When +ndigits+ is positive, returns a float with +ndigits+ - * digits after the decimal point (as available): - * - * f = 12345.6789 - * f.ceil(1) # => 12345.7 - * f.ceil(3) # => 12345.679 - * f = -12345.6789 - * f.ceil(1) # => -12345.6 - * f.ceil(3) # => -12345.678 - * - * When +ndigits+ is non-positive, returns an integer with at least - * ndigits.abs trailing zeros: + * Returns a numeric that is a "ceiling" value for `self`, + * as specified by the given `ndigits`, + * which must be an + * [integer-convertible object](rdoc-ref:implicit_conversion.rdoc@Integer-Convertible+Objects). * - * f = 12345.6789 - * f.ceil(0) # => 12346 - * f.ceil(-3) # => 13000 - * f = -12345.6789 - * f.ceil(0) # => -12345 - * f.ceil(-3) # => -12000 + * When `ndigits` is positive, returns a Float with `ndigits` + * decimal digits after the decimal point + * (as available, but no fewer than 1): + * + * ``` + * f = 12345.6789 + * f.ceil(1) # => 12345.7 + * f.ceil(3) # => 12345.679 + * f.ceil(30) # => 12345.6789 + * f = -12345.6789 + * f.ceil(1) # => -12345.6 + * f.ceil(3) # => -12345.678 + * f.ceil(30) # => -12345.6789 + * f = 0.0 + * f.ceil(1) # => 0.0 + * f.ceil(100) # => 0.0 + * ``` + * + * When `ndigits` is non-positive, + * returns an Integer based on a computed granularity: + * + * - The granularity is `10 ** ndigits.abs`. + * - The returned value is the largest multiple of the granularity + * that is less than or equal to `self`. + * + * Examples with positive `self`: + * + * | ndigits | Granularity | 12345.6789.ceil(ndigits) | + * |--------:|------------:|-------------------------:| + * | 0 | 1 | 12346 | + * | -1 | 10 | 12350 | + * | -2 | 100 | 12400 | + * | -3 | 1000 | 13000 | + * | -4 | 10000 | 20000 | + * | -5 | 100000 | 100000 | + * + * Examples with negative `self`: + * + * | ndigits | Granularity | -12345.6789.ceil(ndigits) | + * |--------:|------------:|--------------------------:| + * | 0 | 1 | -12345 | + * | -1 | 10 | -12340 | + * | -2 | 100 | -12300 | + * | -3 | 1000 | -12000 | + * | -4 | 10000 | -10000 | + * | -5 | 100000 | 0 | + * + * When `self` is zero and `ndigits` is non-positive, + * returns Integer zero: + * + * ``` + * 0.0.ceil(0) # => 0 + * 0.0.ceil(-1) # => 0 + * 0.0.ceil(-2) # => 0 + * ``` * * Note that the limited precision of floating-point arithmetic * may lead to surprising results: * - * (2.1 / 0.7).ceil #=> 4 (!) + * ``` + * (2.1 / 0.7).ceil #=> 4 # Not 3 (because 2.1 / 0.7 # => 3.0000000000000004, not 3.0) + * ``` * * Related: Float#floor. * @@ -5623,50 +5665,6 @@ int_downto_size(VALUE from, VALUE args, VALUE eobj) return ruby_num_interval_step_size(from, RARRAY_AREF(args, 0), INT2FIX(-1), FALSE); } -/* - * call-seq: - * downto(limit) {|i| ... } -> self - * downto(limit) -> enumerator - * - * Calls the given block with each integer value from +self+ down to +limit+; - * returns +self+: - * - * a = [] - * 10.downto(5) {|i| a << i } # => 10 - * a # => [10, 9, 8, 7, 6, 5] - * a = [] - * 0.downto(-5) {|i| a << i } # => 0 - * a # => [0, -1, -2, -3, -4, -5] - * 4.downto(5) {|i| fail 'Cannot happen' } # => 4 - * - * With no block given, returns an Enumerator. - * - */ - -static VALUE -int_downto(VALUE from, VALUE to) -{ - RETURN_SIZED_ENUMERATOR(from, 1, &to, int_downto_size); - if (FIXNUM_P(from) && FIXNUM_P(to)) { - long i, end; - - end = FIX2LONG(to); - for (i=FIX2LONG(from); i >= end; i--) { - rb_yield(LONG2FIX(i)); - } - } - else { - VALUE i = from, c; - - while (!(c = rb_funcall(i, '<', 1, to))) { - rb_yield(i); - i = rb_funcall(i, '-', 1, INT2FIX(1)); - } - if (NIL_P(c)) rb_cmperr(i, to); - } - return from; -} - static VALUE int_dotimes_size(VALUE num, VALUE args, VALUE eobj) { @@ -5739,24 +5737,56 @@ int_round(int argc, VALUE* argv, VALUE num) } /* + * :markup: markdown + * * call-seq: * floor(ndigits = 0) -> integer * - * Returns the largest number less than or equal to +self+ with - * a precision of +ndigits+ decimal digits. + * Returns an integer that is a "floor" value for `self`, + * as specified by the given `ndigits`, + * which must be an + * [integer-convertible object](rdoc-ref:implicit_conversion.rdoc@Integer-Convertible+Objects). * - * When +ndigits+ is negative, the returned value - * has at least ndigits.abs trailing zeros: + * - When `self` is zero, returns zero (regardless of the value of `ndigits`): * - * 555.floor(-1) # => 550 - * 555.floor(-2) # => 500 - * -555.floor(-2) # => -600 - * 555.floor(-3) # => 0 + * ``` + * 0.floor(2) # => 0 + * 0.floor(-2) # => 0 + * ``` * - * Returns +self+ when +ndigits+ is zero or positive. + * - When `self` is non-zero and `ndigits` is non-negative, returns `self`: + * + * ``` + * 555.floor # => 555 + * 555.floor(50) # => 555 + * ``` * - * 555.floor # => 555 - * 555.floor(50) # => 555 + * - When `self` is non-zero and `ndigits` is negative, + * returns a value based on a computed granularity: + * + * - The granularity is `10 ** ndigits.abs`. + * - The returned value is the largest multiple of the granularity + * that is less than or equal to `self`. + * + * Examples with positive `self`: + * + * | ndigits | Granularity | 1234.floor(ndigits) | + * |--------:|------------:|--------------------:| + * | -1 | 10 | 1230 | + * | -2 | 100 | 1200 | + * | -3 | 1000 | 1000 | + * | -4 | 10000 | 0 | + * | -5 | 100000 | 0 | + * + * Examples with negative `self`: + * + * | ndigits | Granularity | -1234.floor(ndigits) | + * |--------:|------------:|---------------------:| + * | -1 | 10 | -1240 | + * | -2 | 100 | -1300 | + * | -3 | 1000 | -2000 | + * | -4 | 10000 | -10000 | + * | -5 | 100000 | -100000 | * * Related: Integer#ceil. * @@ -5803,7 +5833,7 @@ int_floor(int argc, VALUE* argv, VALUE num) * - When `self` is non-zero and `ndigits` is negative, * returns a value based on a computed granularity: * - * - The granularity is ndigits.abs * 10. + * - The granularity is `10 ** ndigits.abs`. * - The returned value is the smallest multiple of the granularity * that is greater than or equal to `self`. * @@ -5817,8 +5847,6 @@ int_floor(int argc, VALUE* argv, VALUE num) * | -4 | 10000 | 10000 | * | -5 | 100000 | 100000 | * - *
- * * Examples with negative `self`: * * | ndigits | Granularity | -1234.ceil(ndigits) | @@ -5829,8 +5857,6 @@ int_floor(int argc, VALUE* argv, VALUE num) * | -4 | 10000 | 0 | * | -5 | 100000 | 0 | * - *
- * * Related: Integer#floor. */ @@ -6246,7 +6272,6 @@ Init_Numeric(void) rb_define_method(rb_cInteger, "anybits?", int_anybits_p, 1); rb_define_method(rb_cInteger, "nobits?", int_nobits_p, 1); rb_define_method(rb_cInteger, "upto", int_upto, 1); - rb_define_method(rb_cInteger, "downto", int_downto, 1); rb_define_method(rb_cInteger, "succ", int_succ, 0); rb_define_method(rb_cInteger, "next", int_succ, 0); rb_define_method(rb_cInteger, "pred", int_pred, 0); diff --git a/numeric.rb b/numeric.rb index 4dc406fd2386a7..898b257fe46040 100644 --- a/numeric.rb +++ b/numeric.rb @@ -241,6 +241,36 @@ def times self end + # call-seq: + # downto(limit) {|i| ... } -> self + # downto(limit) -> enumerator + # + # Calls the given block with each integer value from +self+ down to +limit+; + # returns +self+: + # + # a = [] + # 10.downto(5) {|i| a << i } # => 10 + # a # => [10, 9, 8, 7, 6, 5] + # a = [] + # 0.downto(-5) {|i| a << i } # => 0 + # a # => [0, -1, -2, -3, -4, -5] + # 4.downto(5) {|i| fail 'Cannot happen' } # => 4 + # + # With no block given, returns an Enumerator. + def downto to + Primitive.attr! :inline_block + unless defined?(yield) + return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 1, &to, int_downto_size)' + end + + from = self + while from >= to + yield from + from = from.pred + end + self + end + # call-seq: # to_i -> self # diff --git a/parse.y b/parse.y index ec4a4ac6a8ae83..c121ddd9e8fece 100644 --- a/parse.y +++ b/parse.y @@ -2598,6 +2598,7 @@ rb_parser_ary_free(rb_parser_t *p, rb_parser_ary_t *ary) break; } # undef foreach_ary + xfree(ary->data); xfree(ary); } diff --git a/prism/config.yml b/prism/config.yml index 9001b388eefdd7..ad5d633755ba68 100644 --- a/prism/config.yml +++ b/prism/config.yml @@ -263,6 +263,7 @@ errors: - TERNARY_COLON - TERNARY_EXPRESSION_FALSE - TERNARY_EXPRESSION_TRUE + - UNARY_DISALLOWED - UNARY_RECEIVER - UNDEF_ARGUMENT - UNEXPECTED_BLOCK_ARGUMENT @@ -734,11 +735,6 @@ 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 @@ -858,10 +854,8 @@ nodes: left and right ^^^^^^^^^^^^^^ - name: ArgumentsNode + flags: ArgumentsNodeFlags fields: - - name: flags - type: flags - kind: ArgumentsNodeFlags - name: arguments type: node[] comment: | @@ -870,10 +864,8 @@ nodes: return foo, bar, baz ^^^^^^^^^^^^^ - name: ArrayNode + flags: ArrayNodeFlags fields: - - name: flags - type: flags - 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. @@ -1042,10 +1034,8 @@ nodes: bar(&args) ^^^^^^^^^^ - name: BlockLocalVariableNode + flags: ParameterFlags fields: - - name: flags - type: flags - kind: ParameterFlags - name: name type: constant comment: | @@ -1071,10 +1061,8 @@ nodes: [1, 2, 3].each { |i| puts x } ^^^^^^^^^^^^^^ - name: BlockParameterNode + flags: ParameterFlags fields: - - name: flags - type: flags - kind: ParameterFlags - name: name type: constant? - name: name_loc @@ -1131,10 +1119,8 @@ nodes: break foo ^^^^^^^^^ - name: CallAndWriteNode + flags: CallNodeFlags fields: - - name: flags - type: flags - kind: CallNodeFlags - name: receiver type: node? - name: call_operator_loc @@ -1155,10 +1141,8 @@ nodes: foo.bar &&= value ^^^^^^^^^^^^^^^^^ - name: CallNode + flags: CallNodeFlags fields: - - name: flags - type: flags - kind: CallNodeFlags - name: receiver type: node? comment: | @@ -1208,10 +1192,8 @@ nodes: foo&.bar ^^^^^^^^ - name: CallOperatorWriteNode + flags: CallNodeFlags fields: - - name: flags - type: flags - kind: CallNodeFlags - name: receiver type: node? - name: call_operator_loc @@ -1234,10 +1216,8 @@ nodes: foo.bar += baz ^^^^^^^^^^^^^^ - name: CallOrWriteNode + flags: CallNodeFlags fields: - - name: flags - type: flags - kind: CallNodeFlags - name: receiver type: node? - name: call_operator_loc @@ -1258,10 +1238,8 @@ nodes: foo.bar ||= value ^^^^^^^^^^^^^^^^^ - name: CallTargetNode + flags: CallNodeFlags fields: - - name: flags - type: flags - kind: CallNodeFlags - name: receiver type: node - name: call_operator_loc @@ -1856,10 +1834,8 @@ nodes: foo in Foo(*bar, baz, *qux) ^^^^^^^^^^^^^^^^^^^^ - name: FlipFlopNode + flags: RangeFlags fields: - - name: flags - type: flags - kind: RangeFlags - name: left type: node? - name: right @@ -2278,10 +2254,8 @@ nodes: case a; in b then c end ^^^^^^^^^^^ - name: IndexAndWriteNode + flags: CallNodeFlags fields: - - name: flags - type: flags - kind: CallNodeFlags - name: receiver type: node? - name: call_operator_loc @@ -2305,10 +2279,8 @@ nodes: foo.bar[baz] &&= value ^^^^^^^^^^^^^^^^^^^^^^ - name: IndexOperatorWriteNode + flags: CallNodeFlags fields: - - name: flags - type: flags - kind: CallNodeFlags - name: receiver type: node? - name: call_operator_loc @@ -2334,10 +2306,8 @@ nodes: foo.bar[baz] += value ^^^^^^^^^^^^^^^^^^^^^ - name: IndexOrWriteNode + flags: CallNodeFlags fields: - - name: flags - type: flags - kind: CallNodeFlags - name: receiver type: node? - name: call_operator_loc @@ -2361,10 +2331,8 @@ nodes: foo.bar[baz] ||= value ^^^^^^^^^^^^^^^^^^^^^^ - name: IndexTargetNode + flags: CallNodeFlags fields: - - name: flags - type: flags - kind: CallNodeFlags - name: receiver type: node - name: opening_loc @@ -2500,10 +2468,8 @@ nodes: @foo = 1 ^^^^^^^^ - name: IntegerNode + flags: IntegerBaseFlags fields: - - name: flags - type: flags - kind: IntegerBaseFlags - name: value type: integer comment: The value of the integer literal as a number. @@ -2513,10 +2479,8 @@ nodes: 1 ^ - name: InterpolatedMatchLastLineNode + flags: RegularExpressionFlags fields: - - name: flags - type: flags - kind: RegularExpressionFlags - name: opening_loc type: location - name: parts @@ -2534,10 +2498,8 @@ nodes: if /foo #{bar} baz/ then end ^^^^^^^^^^^^^^^^ - name: InterpolatedRegularExpressionNode + flags: RegularExpressionFlags fields: - - name: flags - type: flags - kind: RegularExpressionFlags - name: opening_loc type: location - name: parts @@ -2555,10 +2517,8 @@ nodes: /foo #{bar} baz/ ^^^^^^^^^^^^^^^^ - name: InterpolatedStringNode + flags: InterpolatedStringNodeFlags fields: - - name: flags - type: flags - kind: InterpolatedStringNodeFlags - name: opening_loc type: location? - name: parts @@ -2625,10 +2585,8 @@ nodes: -> { it + it } ^^^^^^^^^^^^^^ - name: KeywordHashNode + flags: KeywordHashNodeFlags fields: - - name: flags - type: flags - kind: KeywordHashNodeFlags - name: elements type: node[] kind: @@ -2640,10 +2598,8 @@ nodes: foo(a: b) ^^^^ - name: KeywordRestParameterNode + flags: ParameterFlags fields: - - name: flags - type: flags - kind: ParameterFlags - name: name type: constant? - name: name_loc @@ -2823,10 +2779,8 @@ nodes: foo = 1 ^^^^^^^ - name: MatchLastLineNode + flags: RegularExpressionFlags fields: - - name: flags - type: flags - kind: RegularExpressionFlags - name: opening_loc type: location - name: content_loc @@ -3142,10 +3096,8 @@ nodes: $1 ^^ - name: OptionalKeywordParameterNode + flags: ParameterFlags fields: - - name: flags - type: flags - kind: ParameterFlags - name: name type: constant - name: name_loc @@ -3159,10 +3111,8 @@ nodes: ^^^^ end - name: OptionalParameterNode + flags: ParameterFlags fields: - - name: flags - type: flags - kind: ParameterFlags - name: name type: constant - name: name_loc @@ -3337,10 +3287,8 @@ nodes: kind: StatementsNode comment: The top level node of any parse tree. - name: RangeNode + flags: RangeFlags fields: - - name: flags - type: flags - kind: RangeFlags - name: left type: node? comment: | @@ -3375,10 +3323,8 @@ nodes: c if a =~ /left/ ... b =~ /right/ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - name: RationalNode + flags: IntegerBaseFlags fields: - - name: flags - type: flags - kind: IntegerBaseFlags - name: numerator type: integer comment: | @@ -3403,10 +3349,8 @@ nodes: redo ^^^^ - name: RegularExpressionNode + flags: RegularExpressionFlags fields: - - name: flags - type: flags - kind: RegularExpressionFlags - name: opening_loc type: location - name: content_loc @@ -3421,10 +3365,8 @@ nodes: /foo/i ^^^^^^ - name: RequiredKeywordParameterNode + flags: ParameterFlags fields: - - name: flags - type: flags - kind: ParameterFlags - name: name type: constant - name: name_loc @@ -3436,10 +3378,8 @@ nodes: ^^ end - name: RequiredParameterNode + flags: ParameterFlags fields: - - name: flags - type: flags - kind: ParameterFlags - name: name type: constant comment: | @@ -3489,10 +3429,8 @@ nodes: `Foo, *splat, Bar` are in the `exceptions` field. `ex` is in the `exception` field. - name: RestParameterNode + flags: ParameterFlags fields: - - name: flags - type: flags - kind: ParameterFlags - name: name type: constant? - name: name_loc @@ -3513,9 +3451,6 @@ nodes: ^^^^^ - name: ReturnNode fields: - - name: flags - type: flags - kind: ReturnNodeFlags - name: keyword_loc type: location - name: arguments @@ -3533,10 +3468,8 @@ nodes: self ^^^^ - name: ShareableConstantNode + flags: ShareableConstantNodeFlags fields: - - name: flags - type: flags - kind: ShareableConstantNodeFlags - name: write type: node kind: @@ -3581,10 +3514,8 @@ nodes: __ENCODING__ ^^^^^^^^^^^^ - name: SourceFileNode + flags: StringFlags fields: - - name: flags - type: flags - 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. @@ -3620,10 +3551,8 @@ nodes: foo; bar; baz ^^^^^^^^^^^^^ - name: StringNode + flags: StringFlags fields: - - name: flags - type: flags - kind: StringFlags - name: opening_loc type: location? - name: content_loc @@ -3665,10 +3594,8 @@ nodes: super foo, bar ^^^^^^^^^^^^^^ - name: SymbolNode + flags: SymbolFlags fields: - - name: flags - type: flags - kind: SymbolFlags - name: opening_loc type: location? - name: value_loc @@ -3768,10 +3695,8 @@ nodes: unless foo then bar end ^^^^^^^^^^^^^^^^^^^^^^^ - name: UntilNode + flags: LoopFlags fields: - - name: flags - type: flags - kind: LoopFlags - name: keyword_loc type: location - name: closing_loc @@ -3809,10 +3734,8 @@ nodes: ^^^^^^^^^ end - name: WhileNode + flags: LoopFlags fields: - - name: flags - type: flags - kind: LoopFlags - name: keyword_loc type: location - name: closing_loc @@ -3832,10 +3755,8 @@ nodes: while foo do bar end ^^^^^^^^^^^^^^^^^^^^ - name: XStringNode + flags: EncodingFlags fields: - - name: flags - type: flags - kind: EncodingFlags - name: opening_loc type: location - name: content_loc diff --git a/prism/extension.c b/prism/extension.c index d0bc5e7f0c17c2..64affd40013003 100644 --- a/prism/extension.c +++ b/prism/extension.c @@ -856,8 +856,8 @@ parse_stream_fgets(char *string, int size, void *stream) { return NULL; } - const char *cstr = StringValueCStr(line); - size_t length = strlen(cstr); + const char *cstr = RSTRING_PTR(line); + long length = RSTRING_LEN(line); memcpy(string, cstr, length); string[length] = '\0'; diff --git a/prism/parser.h b/prism/parser.h index 048955409b470b..847b29f368f878 100644 --- a/prism/parser.h +++ b/prism/parser.h @@ -508,9 +508,9 @@ typedef struct { /** The type of shareable constant value that can be set. */ typedef uint8_t pm_shareable_constant_value_t; static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_NONE = 0x0; -static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_LITERAL = 0x1; -static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING = 0x2; -static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY = 0x4; +static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_LITERAL = PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL; +static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING = PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING; +static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY = PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY; /** * This tracks an individual local variable in a certain lexical context, as @@ -625,6 +625,13 @@ typedef uint32_t pm_state_stack_t; * it's considering. */ struct pm_parser { + /** + * The next node identifier that will be assigned. This is a unique + * identifier used to track nodes such that the syntax tree can be dropped + * but the node can be found through another parse. + */ + uint32_t node_id; + /** The current state of the lexer. */ pm_lex_state_t lex_state; diff --git a/prism/prism.c b/prism/prism.c index 6e93e2a96169ef..2d6cccffba76c0 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -1875,7 +1875,7 @@ pm_statements_node_body_length(pm_statements_node_t *node); * implement our own arena allocation. */ static inline void * -pm_alloc_node(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, size_t size) { +pm_node_alloc(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, size_t size) { void *memory = xcalloc(1, size); if (memory == NULL) { fprintf(stderr, "Failed to allocate %d bytes\n", (int) size); @@ -1884,15 +1884,22 @@ pm_alloc_node(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, size_t size) { return memory; } -#define PM_ALLOC_NODE(parser, type) (type *) pm_alloc_node(parser, sizeof(type)) +#define PM_NODE_ALLOC(parser, type) (type *) pm_node_alloc(parser, sizeof(type)) +#define PM_NODE_IDENTIFY(parser) (++parser->node_id) /** * Allocate a new MissingNode node. */ static pm_missing_node_t * pm_missing_node_create(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) { - pm_missing_node_t *node = PM_ALLOC_NODE(parser, pm_missing_node_t); - *node = (pm_missing_node_t) {{ .type = PM_MISSING_NODE, .location = { .start = start, .end = end } }}; + pm_missing_node_t *node = PM_NODE_ALLOC(parser, pm_missing_node_t); + + *node = (pm_missing_node_t) {{ + .type = PM_MISSING_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = { .start = start, .end = end } + }}; + return node; } @@ -1902,11 +1909,12 @@ pm_missing_node_create(pm_parser_t *parser, const uint8_t *start, const uint8_t static pm_alias_global_variable_node_t * pm_alias_global_variable_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *new_name, pm_node_t *old_name) { assert(keyword->type == PM_TOKEN_KEYWORD_ALIAS); - pm_alias_global_variable_node_t *node = PM_ALLOC_NODE(parser, pm_alias_global_variable_node_t); + pm_alias_global_variable_node_t *node = PM_NODE_ALLOC(parser, pm_alias_global_variable_node_t); *node = (pm_alias_global_variable_node_t) { { .type = PM_ALIAS_GLOBAL_VARIABLE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = keyword->start, .end = old_name->location.end @@ -1926,11 +1934,12 @@ pm_alias_global_variable_node_create(pm_parser_t *parser, const pm_token_t *keyw static pm_alias_method_node_t * pm_alias_method_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *new_name, pm_node_t *old_name) { assert(keyword->type == PM_TOKEN_KEYWORD_ALIAS); - pm_alias_method_node_t *node = PM_ALLOC_NODE(parser, pm_alias_method_node_t); + pm_alias_method_node_t *node = PM_NODE_ALLOC(parser, pm_alias_method_node_t); *node = (pm_alias_method_node_t) { { .type = PM_ALIAS_METHOD_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = keyword->start, .end = old_name->location.end @@ -1949,11 +1958,12 @@ pm_alias_method_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_n */ static pm_alternation_pattern_node_t * pm_alternation_pattern_node_create(pm_parser_t *parser, pm_node_t *left, pm_node_t *right, const pm_token_t *operator) { - pm_alternation_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_alternation_pattern_node_t); + pm_alternation_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_alternation_pattern_node_t); *node = (pm_alternation_pattern_node_t) { { .type = PM_ALTERNATION_PATTERN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = left->location.start, .end = right->location.end @@ -1974,11 +1984,12 @@ static pm_and_node_t * pm_and_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) { pm_assert_value_expression(parser, left); - pm_and_node_t *node = PM_ALLOC_NODE(parser, pm_and_node_t); + pm_and_node_t *node = PM_NODE_ALLOC(parser, pm_and_node_t); *node = (pm_and_node_t) { { .type = PM_AND_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = left->location.start, .end = right->location.end @@ -1997,11 +2008,12 @@ pm_and_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *opera */ static pm_arguments_node_t * pm_arguments_node_create(pm_parser_t *parser) { - pm_arguments_node_t *node = PM_ALLOC_NODE(parser, pm_arguments_node_t); + pm_arguments_node_t *node = PM_NODE_ALLOC(parser, pm_arguments_node_t); *node = (pm_arguments_node_t) { { .type = PM_ARGUMENTS_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_NULL_VALUE(parser) }, .arguments = { 0 } @@ -2036,12 +2048,13 @@ pm_arguments_node_arguments_append(pm_arguments_node_t *node, pm_node_t *argumen */ static pm_array_node_t * pm_array_node_create(pm_parser_t *parser, const pm_token_t *opening) { - pm_array_node_t *node = PM_ALLOC_NODE(parser, pm_array_node_t); + pm_array_node_t *node = PM_NODE_ALLOC(parser, pm_array_node_t); *node = (pm_array_node_t) { { .type = PM_ARRAY_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(opening) }, .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), @@ -2099,11 +2112,12 @@ pm_array_node_close_set(pm_array_node_t *node, const pm_token_t *closing) { */ static pm_array_pattern_node_t * pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *nodes) { - pm_array_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_array_pattern_node_t); + pm_array_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_array_pattern_node_t); *node = (pm_array_pattern_node_t) { { .type = PM_ARRAY_PATTERN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = nodes->nodes[0]->location.start, .end = nodes->nodes[nodes->size - 1]->location.end @@ -2141,11 +2155,12 @@ pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *node */ static pm_array_pattern_node_t * pm_array_pattern_node_rest_create(pm_parser_t *parser, pm_node_t *rest) { - pm_array_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_array_pattern_node_t); + pm_array_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_array_pattern_node_t); *node = (pm_array_pattern_node_t) { { .type = PM_ARRAY_PATTERN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = rest->location, }, .constant = NULL, @@ -2165,11 +2180,12 @@ pm_array_pattern_node_rest_create(pm_parser_t *parser, pm_node_t *rest) { */ static pm_array_pattern_node_t * pm_array_pattern_node_constant_create(pm_parser_t *parser, pm_node_t *constant, const pm_token_t *opening, const pm_token_t *closing) { - pm_array_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_array_pattern_node_t); + pm_array_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_array_pattern_node_t); *node = (pm_array_pattern_node_t) { { .type = PM_ARRAY_PATTERN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = constant->location.start, .end = closing->end @@ -2192,11 +2208,12 @@ pm_array_pattern_node_constant_create(pm_parser_t *parser, pm_node_t *constant, */ static pm_array_pattern_node_t * pm_array_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) { - pm_array_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_array_pattern_node_t); + pm_array_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_array_pattern_node_t); *node = (pm_array_pattern_node_t) { { .type = PM_ARRAY_PATTERN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = opening->start, .end = closing->end @@ -2223,7 +2240,7 @@ pm_array_pattern_node_requireds_append(pm_array_pattern_node_t *node, pm_node_t */ static pm_assoc_node_t * pm_assoc_node_create(pm_parser_t *parser, pm_node_t *key, const pm_token_t *operator, pm_node_t *value) { - pm_assoc_node_t *node = PM_ALLOC_NODE(parser, pm_assoc_node_t); + pm_assoc_node_t *node = PM_NODE_ALLOC(parser, pm_assoc_node_t); const uint8_t *end; if (value != NULL && value->location.end > key->location.end) { @@ -2252,6 +2269,7 @@ pm_assoc_node_create(pm_parser_t *parser, pm_node_t *key, const pm_token_t *oper { .type = PM_ASSOC_NODE, .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = key->location.start, .end = end @@ -2271,11 +2289,12 @@ pm_assoc_node_create(pm_parser_t *parser, pm_node_t *key, const pm_token_t *oper static pm_assoc_splat_node_t * pm_assoc_splat_node_create(pm_parser_t *parser, pm_node_t *value, const pm_token_t *operator) { assert(operator->type == PM_TOKEN_USTAR_STAR); - pm_assoc_splat_node_t *node = PM_ALLOC_NODE(parser, pm_assoc_splat_node_t); + pm_assoc_splat_node_t *node = PM_NODE_ALLOC(parser, pm_assoc_splat_node_t); *node = (pm_assoc_splat_node_t) { { .type = PM_ASSOC_SPLAT_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = operator->start, .end = value == NULL ? operator->end : value->location.end @@ -2294,11 +2313,12 @@ pm_assoc_splat_node_create(pm_parser_t *parser, pm_node_t *value, const pm_token static pm_back_reference_read_node_t * pm_back_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) { assert(name->type == PM_TOKEN_BACK_REFERENCE); - pm_back_reference_read_node_t *node = PM_ALLOC_NODE(parser, pm_back_reference_read_node_t); + pm_back_reference_read_node_t *node = PM_NODE_ALLOC(parser, pm_back_reference_read_node_t); *node = (pm_back_reference_read_node_t) { { .type = PM_BACK_REFERENCE_READ_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(name), }, .name = pm_parser_constant_id_token(parser, name) @@ -2312,11 +2332,12 @@ pm_back_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) */ static pm_begin_node_t * pm_begin_node_create(pm_parser_t *parser, const pm_token_t *begin_keyword, pm_statements_node_t *statements) { - pm_begin_node_t *node = PM_ALLOC_NODE(parser, pm_begin_node_t); + pm_begin_node_t *node = PM_NODE_ALLOC(parser, pm_begin_node_t); *node = (pm_begin_node_t) { { .type = PM_BEGIN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = begin_keyword->start, .end = statements == NULL ? begin_keyword->end : statements->base.location.end @@ -2377,11 +2398,12 @@ pm_begin_node_end_keyword_set(pm_begin_node_t *node, const pm_token_t *end_keywo */ static pm_block_argument_node_t * pm_block_argument_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *expression) { - pm_block_argument_node_t *node = PM_ALLOC_NODE(parser, pm_block_argument_node_t); + pm_block_argument_node_t *node = PM_NODE_ALLOC(parser, pm_block_argument_node_t); *node = (pm_block_argument_node_t) { { .type = PM_BLOCK_ARGUMENT_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = operator->start, .end = expression == NULL ? operator->end : expression->location.end @@ -2399,11 +2421,12 @@ pm_block_argument_node_create(pm_parser_t *parser, const pm_token_t *operator, p */ static pm_block_node_t * pm_block_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_token_t *opening, pm_node_t *parameters, pm_node_t *body, const pm_token_t *closing) { - pm_block_node_t *node = PM_ALLOC_NODE(parser, pm_block_node_t); + pm_block_node_t *node = PM_NODE_ALLOC(parser, pm_block_node_t); *node = (pm_block_node_t) { { .type = PM_BLOCK_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = opening->start, .end = closing->end }, }, .locals = *locals, @@ -2422,11 +2445,12 @@ pm_block_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const p static pm_block_parameter_node_t * pm_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, const pm_token_t *operator) { assert(operator->type == PM_TOKEN_NOT_PROVIDED || operator->type == PM_TOKEN_UAMPERSAND || operator->type == PM_TOKEN_AMPERSAND); - pm_block_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_block_parameter_node_t); + pm_block_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_block_parameter_node_t); *node = (pm_block_parameter_node_t) { { .type = PM_BLOCK_PARAMETER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = operator->start, .end = (name->type == PM_TOKEN_NOT_PROVIDED ? operator->end : name->end) @@ -2445,7 +2469,7 @@ pm_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, cons */ static pm_block_parameters_node_t * pm_block_parameters_node_create(pm_parser_t *parser, pm_parameters_node_t *parameters, const pm_token_t *opening) { - pm_block_parameters_node_t *node = PM_ALLOC_NODE(parser, pm_block_parameters_node_t); + pm_block_parameters_node_t *node = PM_NODE_ALLOC(parser, pm_block_parameters_node_t); const uint8_t *start; if (opening->type != PM_TOKEN_NOT_PROVIDED) { @@ -2468,6 +2492,7 @@ pm_block_parameters_node_create(pm_parser_t *parser, pm_parameters_node_t *param *node = (pm_block_parameters_node_t) { { .type = PM_BLOCK_PARAMETERS_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = start, .end = end @@ -2498,11 +2523,12 @@ pm_block_parameters_node_closing_set(pm_block_parameters_node_t *node, const pm_ */ static pm_block_local_variable_node_t * pm_block_local_variable_node_create(pm_parser_t *parser, const pm_token_t *name) { - pm_block_local_variable_node_t *node = PM_ALLOC_NODE(parser, pm_block_local_variable_node_t); + pm_block_local_variable_node_t *node = PM_NODE_ALLOC(parser, pm_block_local_variable_node_t); *node = (pm_block_local_variable_node_t) { { .type = PM_BLOCK_LOCAL_VARIABLE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(name), }, .name = pm_parser_constant_id_token(parser, name) @@ -2528,11 +2554,12 @@ pm_block_parameters_node_append_local(pm_block_parameters_node_t *node, const pm static pm_break_node_t * pm_break_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) { assert(keyword->type == PM_TOKEN_KEYWORD_BREAK); - pm_break_node_t *node = PM_ALLOC_NODE(parser, pm_break_node_t); + pm_break_node_t *node = PM_NODE_ALLOC(parser, pm_break_node_t); *node = (pm_break_node_t) { { .type = PM_BREAK_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = keyword->start, .end = (arguments == NULL ? keyword->end : arguments->base.location.end) @@ -2548,10 +2575,10 @@ pm_break_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_argument // There are certain flags that we want to use internally but don't want to // expose because they are not relevant beyond parsing. Therefore we'll define // them here and not define them in config.yml/a header file. -static const pm_node_flags_t PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY = 0x1; -static const pm_node_flags_t PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY = 0x10; -static const pm_node_flags_t PM_CALL_NODE_FLAGS_COMPARISON = 0x20; -static const pm_node_flags_t PM_CALL_NODE_FLAGS_INDEX = 0x40; +static const pm_node_flags_t PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY = 0x4; +static const pm_node_flags_t PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY = 0x40; +static const pm_node_flags_t PM_CALL_NODE_FLAGS_COMPARISON = 0x80; +static const pm_node_flags_t PM_CALL_NODE_FLAGS_INDEX = 0x100; /** * Allocate and initialize a new CallNode node. This sets everything to NULL or @@ -2560,12 +2587,13 @@ static const pm_node_flags_t PM_CALL_NODE_FLAGS_INDEX = 0x40; */ static pm_call_node_t * pm_call_node_create(pm_parser_t *parser, pm_node_flags_t flags) { - pm_call_node_t *node = PM_ALLOC_NODE(parser, pm_call_node_t); + pm_call_node_t *node = PM_NODE_ALLOC(parser, pm_call_node_t); *node = (pm_call_node_t) { { .type = PM_CALL_NODE, .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_NULL_VALUE(parser), }, .receiver = NULL, @@ -2864,12 +2892,13 @@ static pm_call_and_write_node_t * pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) { assert(target->block == NULL); assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL); - pm_call_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_call_and_write_node_t); + pm_call_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_call_and_write_node_t); *node = (pm_call_and_write_node_t) { { .type = PM_CALL_AND_WRITE_NODE, .flags = target->base.flags, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -2923,7 +2952,7 @@ pm_index_arguments_check(pm_parser_t *parser, const pm_arguments_node_t *argumen static pm_index_and_write_node_t * pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) { 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_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_index_and_write_node_t); pm_index_arguments_check(parser, target->arguments, target->block); @@ -2931,6 +2960,7 @@ pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, cons { .type = PM_INDEX_AND_WRITE_NODE, .flags = target->base.flags, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -2960,12 +2990,13 @@ pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, cons static pm_call_operator_write_node_t * pm_call_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) { assert(target->block == NULL); - pm_call_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_call_operator_write_node_t); + pm_call_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_call_operator_write_node_t); *node = (pm_call_operator_write_node_t) { { .type = PM_CALL_OPERATOR_WRITE_NODE, .flags = target->base.flags, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -2996,7 +3027,7 @@ pm_call_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, */ 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_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_index_operator_write_node_t); pm_index_arguments_check(parser, target->arguments, target->block); @@ -3004,6 +3035,7 @@ pm_index_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, { .type = PM_INDEX_OPERATOR_WRITE_NODE, .flags = target->base.flags, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -3035,12 +3067,13 @@ static pm_call_or_write_node_t * pm_call_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) { assert(target->block == NULL); assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL); - pm_call_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_call_or_write_node_t); + pm_call_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_call_or_write_node_t); *node = (pm_call_or_write_node_t) { { .type = PM_CALL_OR_WRITE_NODE, .flags = target->base.flags, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -3071,7 +3104,7 @@ pm_call_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const static pm_index_or_write_node_t * pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) { 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_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_index_or_write_node_t); pm_index_arguments_check(parser, target->arguments, target->block); @@ -3079,6 +3112,7 @@ pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const { .type = PM_INDEX_OR_WRITE_NODE, .flags = target->base.flags, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -3108,12 +3142,13 @@ pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const */ static pm_call_target_node_t * pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { - pm_call_target_node_t *node = PM_ALLOC_NODE(parser, pm_call_target_node_t); + pm_call_target_node_t *node = PM_NODE_ALLOC(parser, pm_call_target_node_t); *node = (pm_call_target_node_t) { { .type = PM_CALL_TARGET_NODE, .flags = target->base.flags, + .node_id = PM_NODE_IDENTIFY(parser), .location = target->base.location }, .receiver = target->receiver, @@ -3136,7 +3171,7 @@ pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { */ static pm_index_target_node_t * 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_index_target_node_t *node = PM_NODE_ALLOC(parser, pm_index_target_node_t); pm_node_flags_t flags = target->base.flags; pm_index_arguments_check(parser, target->arguments, target->block); @@ -3145,6 +3180,7 @@ pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { { .type = PM_INDEX_TARGET_NODE, .flags = flags | PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE, + .node_id = PM_NODE_IDENTIFY(parser), .location = target->base.location }, .receiver = target->receiver, @@ -3167,11 +3203,12 @@ pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { */ static pm_capture_pattern_node_t * pm_capture_pattern_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t *target, const pm_token_t *operator) { - pm_capture_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_capture_pattern_node_t); + pm_capture_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_capture_pattern_node_t); *node = (pm_capture_pattern_node_t) { { .type = PM_CAPTURE_PATTERN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = value->location.start, .end = target->location.end @@ -3190,11 +3227,12 @@ pm_capture_pattern_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t */ static pm_case_node_t * pm_case_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node_t *predicate, const pm_token_t *end_keyword) { - pm_case_node_t *node = PM_ALLOC_NODE(parser, pm_case_node_t); + pm_case_node_t *node = PM_NODE_ALLOC(parser, pm_case_node_t); *node = (pm_case_node_t) { { .type = PM_CASE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = case_keyword->start, .end = end_keyword->end @@ -3244,11 +3282,12 @@ pm_case_node_end_keyword_loc_set(pm_case_node_t *node, const pm_token_t *end_key */ static pm_case_match_node_t * pm_case_match_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node_t *predicate, const pm_token_t *end_keyword) { - pm_case_match_node_t *node = PM_ALLOC_NODE(parser, pm_case_match_node_t); + pm_case_match_node_t *node = PM_NODE_ALLOC(parser, pm_case_match_node_t); *node = (pm_case_match_node_t) { { .type = PM_CASE_MATCH_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = case_keyword->start, .end = end_keyword->end @@ -3298,11 +3337,12 @@ pm_case_match_node_end_keyword_loc_set(pm_case_match_node_t *node, const pm_toke */ static pm_class_node_t * pm_class_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_token_t *class_keyword, pm_node_t *constant_path, const pm_token_t *name, const pm_token_t *inheritance_operator, pm_node_t *superclass, pm_node_t *body, const pm_token_t *end_keyword) { - pm_class_node_t *node = PM_ALLOC_NODE(parser, pm_class_node_t); + pm_class_node_t *node = PM_NODE_ALLOC(parser, pm_class_node_t); *node = (pm_class_node_t) { { .type = PM_CLASS_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = class_keyword->start, .end = end_keyword->end }, }, .locals = *locals, @@ -3324,11 +3364,12 @@ pm_class_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const p static pm_class_variable_and_write_node_t * pm_class_variable_and_write_node_create(pm_parser_t *parser, pm_class_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL); - pm_class_variable_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_class_variable_and_write_node_t); + pm_class_variable_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_class_variable_and_write_node_t); *node = (pm_class_variable_and_write_node_t) { { .type = PM_CLASS_VARIABLE_AND_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -3348,11 +3389,12 @@ pm_class_variable_and_write_node_create(pm_parser_t *parser, pm_class_variable_r */ static pm_class_variable_operator_write_node_t * pm_class_variable_operator_write_node_create(pm_parser_t *parser, pm_class_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { - pm_class_variable_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_class_variable_operator_write_node_t); + pm_class_variable_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_class_variable_operator_write_node_t); *node = (pm_class_variable_operator_write_node_t) { { .type = PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -3374,11 +3416,12 @@ pm_class_variable_operator_write_node_create(pm_parser_t *parser, pm_class_varia static pm_class_variable_or_write_node_t * pm_class_variable_or_write_node_create(pm_parser_t *parser, pm_class_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL); - pm_class_variable_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_class_variable_or_write_node_t); + pm_class_variable_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_class_variable_or_write_node_t); *node = (pm_class_variable_or_write_node_t) { { .type = PM_CLASS_VARIABLE_OR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -3399,11 +3442,12 @@ pm_class_variable_or_write_node_create(pm_parser_t *parser, pm_class_variable_re static pm_class_variable_read_node_t * pm_class_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_CLASS_VARIABLE); - pm_class_variable_read_node_t *node = PM_ALLOC_NODE(parser, pm_class_variable_read_node_t); + pm_class_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_class_variable_read_node_t); *node = (pm_class_variable_read_node_t) { { .type = PM_CLASS_VARIABLE_READ_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(token) }, .name = pm_parser_constant_id_token(parser, token) @@ -3431,12 +3475,13 @@ pm_implicit_array_write_flags(const pm_node_t *node, pm_node_flags_t flags) { */ static pm_class_variable_write_node_t * pm_class_variable_write_node_create(pm_parser_t *parser, pm_class_variable_read_node_t *read_node, pm_token_t *operator, pm_node_t *value) { - pm_class_variable_write_node_t *node = PM_ALLOC_NODE(parser, pm_class_variable_write_node_t); + pm_class_variable_write_node_t *node = PM_NODE_ALLOC(parser, pm_class_variable_write_node_t); *node = (pm_class_variable_write_node_t) { { .type = PM_CLASS_VARIABLE_WRITE_NODE, .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = read_node->base.location.start, .end = value->location.end @@ -3457,11 +3502,12 @@ pm_class_variable_write_node_create(pm_parser_t *parser, pm_class_variable_read_ static pm_constant_path_and_write_node_t * pm_constant_path_and_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) { assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL); - pm_constant_path_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_path_and_write_node_t); + pm_constant_path_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_path_and_write_node_t); *node = (pm_constant_path_and_write_node_t) { { .type = PM_CONSTANT_PATH_AND_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -3480,11 +3526,12 @@ pm_constant_path_and_write_node_create(pm_parser_t *parser, pm_constant_path_nod */ static pm_constant_path_operator_write_node_t * pm_constant_path_operator_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) { - pm_constant_path_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_path_operator_write_node_t); + pm_constant_path_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_path_operator_write_node_t); *node = (pm_constant_path_operator_write_node_t) { { .type = PM_CONSTANT_PATH_OPERATOR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -3505,11 +3552,12 @@ pm_constant_path_operator_write_node_create(pm_parser_t *parser, pm_constant_pat static pm_constant_path_or_write_node_t * pm_constant_path_or_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) { assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL); - pm_constant_path_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_path_or_write_node_t); + pm_constant_path_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_path_or_write_node_t); *node = (pm_constant_path_or_write_node_t) { { .type = PM_CONSTANT_PATH_OR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -3529,7 +3577,7 @@ pm_constant_path_or_write_node_create(pm_parser_t *parser, pm_constant_path_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, 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_path_node_t *node = PM_NODE_ALLOC(parser, pm_constant_path_node_t); pm_constant_id_t name = PM_CONSTANT_ID_UNSET; if (name_token->type == PM_TOKEN_CONSTANT) { @@ -3539,6 +3587,7 @@ pm_constant_path_node_create(pm_parser_t *parser, pm_node_t *parent, const pm_to *node = (pm_constant_path_node_t) { { .type = PM_CONSTANT_PATH_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = parent == NULL ? delimiter->start : parent->location.start, .end = name_token->end @@ -3558,12 +3607,13 @@ pm_constant_path_node_create(pm_parser_t *parser, pm_node_t *parent, const pm_to */ static pm_constant_path_write_node_t * pm_constant_path_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) { - pm_constant_path_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_path_write_node_t); + pm_constant_path_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_path_write_node_t); *node = (pm_constant_path_write_node_t) { { .type = PM_CONSTANT_PATH_WRITE_NODE, .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -3583,11 +3633,12 @@ pm_constant_path_write_node_create(pm_parser_t *parser, pm_constant_path_node_t static pm_constant_and_write_node_t * pm_constant_and_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL); - pm_constant_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_and_write_node_t); + pm_constant_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_and_write_node_t); *node = (pm_constant_and_write_node_t) { { .type = PM_CONSTANT_AND_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -3607,11 +3658,12 @@ pm_constant_and_write_node_create(pm_parser_t *parser, pm_constant_read_node_t * */ static pm_constant_operator_write_node_t * pm_constant_operator_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { - pm_constant_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_operator_write_node_t); + pm_constant_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_operator_write_node_t); *node = (pm_constant_operator_write_node_t) { { .type = PM_CONSTANT_OPERATOR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -3633,11 +3685,12 @@ pm_constant_operator_write_node_create(pm_parser_t *parser, pm_constant_read_nod static pm_constant_or_write_node_t * pm_constant_or_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL); - pm_constant_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_or_write_node_t); + pm_constant_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_or_write_node_t); *node = (pm_constant_or_write_node_t) { { .type = PM_CONSTANT_OR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -3658,11 +3711,12 @@ pm_constant_or_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *t static pm_constant_read_node_t * pm_constant_read_node_create(pm_parser_t *parser, const pm_token_t *name) { assert(name->type == PM_TOKEN_CONSTANT || name->type == PM_TOKEN_MISSING); - pm_constant_read_node_t *node = PM_ALLOC_NODE(parser, pm_constant_read_node_t); + pm_constant_read_node_t *node = PM_NODE_ALLOC(parser, pm_constant_read_node_t); *node = (pm_constant_read_node_t) { { .type = PM_CONSTANT_READ_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(name) }, .name = pm_parser_constant_id_token(parser, name) @@ -3676,12 +3730,13 @@ pm_constant_read_node_create(pm_parser_t *parser, const pm_token_t *name) { */ static pm_constant_write_node_t * pm_constant_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { - pm_constant_write_node_t *node = PM_ALLOC_NODE(parser, pm_constant_write_node_t); + pm_constant_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_write_node_t); *node = (pm_constant_write_node_t) { { .type = PM_CONSTANT_WRITE_NODE, .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -3740,113 +3795,6 @@ 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. */ @@ -3866,7 +3814,7 @@ pm_def_node_create( const pm_token_t *equal, const pm_token_t *end_keyword ) { - pm_def_node_t *node = PM_ALLOC_NODE(parser, pm_def_node_t); + pm_def_node_t *node = PM_NODE_ALLOC(parser, pm_def_node_t); const uint8_t *end; if (end_keyword->type == PM_TOKEN_NOT_PROVIDED) { @@ -3879,13 +3827,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, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = def_keyword->start, .end = end }, }, .name = name, @@ -3910,11 +3855,12 @@ pm_def_node_create( */ static pm_defined_node_t * pm_defined_node_create(pm_parser_t *parser, const pm_token_t *lparen, pm_node_t *value, const pm_token_t *rparen, const pm_location_t *keyword_loc) { - pm_defined_node_t *node = PM_ALLOC_NODE(parser, pm_defined_node_t); + pm_defined_node_t *node = PM_NODE_ALLOC(parser, pm_defined_node_t); *node = (pm_defined_node_t) { { .type = PM_DEFINED_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = keyword_loc->start, .end = (rparen->type == PM_TOKEN_NOT_PROVIDED ? value->location.end : rparen->end) @@ -3934,7 +3880,7 @@ pm_defined_node_create(pm_parser_t *parser, const pm_token_t *lparen, pm_node_t */ static pm_else_node_t * pm_else_node_create(pm_parser_t *parser, const pm_token_t *else_keyword, pm_statements_node_t *statements, const pm_token_t *end_keyword) { - pm_else_node_t *node = PM_ALLOC_NODE(parser, pm_else_node_t); + pm_else_node_t *node = PM_NODE_ALLOC(parser, pm_else_node_t); const uint8_t *end = NULL; if ((end_keyword->type == PM_TOKEN_NOT_PROVIDED) && (statements != NULL)) { end = statements->base.location.end; @@ -3945,6 +3891,7 @@ pm_else_node_create(pm_parser_t *parser, const pm_token_t *else_keyword, pm_stat *node = (pm_else_node_t) { { .type = PM_ELSE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = else_keyword->start, .end = end, @@ -3963,11 +3910,12 @@ pm_else_node_create(pm_parser_t *parser, const pm_token_t *else_keyword, pm_stat */ static pm_embedded_statements_node_t * pm_embedded_statements_node_create(pm_parser_t *parser, const pm_token_t *opening, pm_statements_node_t *statements, const pm_token_t *closing) { - pm_embedded_statements_node_t *node = PM_ALLOC_NODE(parser, pm_embedded_statements_node_t); + pm_embedded_statements_node_t *node = PM_NODE_ALLOC(parser, pm_embedded_statements_node_t); *node = (pm_embedded_statements_node_t) { { .type = PM_EMBEDDED_STATEMENTS_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = opening->start, .end = closing->end @@ -3986,11 +3934,12 @@ pm_embedded_statements_node_create(pm_parser_t *parser, const pm_token_t *openin */ static pm_embedded_variable_node_t * pm_embedded_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *variable) { - pm_embedded_variable_node_t *node = PM_ALLOC_NODE(parser, pm_embedded_variable_node_t); + pm_embedded_variable_node_t *node = PM_NODE_ALLOC(parser, pm_embedded_variable_node_t); *node = (pm_embedded_variable_node_t) { { .type = PM_EMBEDDED_VARIABLE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = operator->start, .end = variable->location.end @@ -4008,11 +3957,12 @@ pm_embedded_variable_node_create(pm_parser_t *parser, const pm_token_t *operator */ static pm_ensure_node_t * pm_ensure_node_create(pm_parser_t *parser, const pm_token_t *ensure_keyword, pm_statements_node_t *statements, const pm_token_t *end_keyword) { - pm_ensure_node_t *node = PM_ALLOC_NODE(parser, pm_ensure_node_t); + pm_ensure_node_t *node = PM_NODE_ALLOC(parser, pm_ensure_node_t); *node = (pm_ensure_node_t) { { .type = PM_ENSURE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = ensure_keyword->start, .end = end_keyword->end @@ -4032,11 +3982,12 @@ pm_ensure_node_create(pm_parser_t *parser, const pm_token_t *ensure_keyword, pm_ static pm_false_node_t * pm_false_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_KEYWORD_FALSE); - pm_false_node_t *node = PM_ALLOC_NODE(parser, pm_false_node_t); + pm_false_node_t *node = PM_NODE_ALLOC(parser, pm_false_node_t); *node = (pm_false_node_t) {{ .type = PM_FALSE_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(token) }}; @@ -4049,7 +4000,7 @@ pm_false_node_create(pm_parser_t *parser, const pm_token_t *token) { */ static pm_find_pattern_node_t * pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) { - pm_find_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_find_pattern_node_t); + pm_find_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_find_pattern_node_t); pm_node_t *left = nodes->nodes[0]; pm_node_t *right; @@ -4063,6 +4014,7 @@ pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) { *node = (pm_find_pattern_node_t) { { .type = PM_FIND_PATTERN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = left->location.start, .end = right->location.end, @@ -4165,12 +4117,13 @@ pm_double_parse(pm_parser_t *parser, const pm_token_t *token) { static pm_float_node_t * pm_float_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_FLOAT); - pm_float_node_t *node = PM_ALLOC_NODE(parser, pm_float_node_t); + pm_float_node_t *node = PM_NODE_ALLOC(parser, pm_float_node_t); *node = (pm_float_node_t) { { .type = PM_FLOAT_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(token) }, .value = pm_double_parse(parser, token) @@ -4186,11 +4139,12 @@ static pm_imaginary_node_t * pm_float_node_imaginary_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_FLOAT_IMAGINARY); - pm_imaginary_node_t *node = PM_ALLOC_NODE(parser, pm_imaginary_node_t); + pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t); *node = (pm_imaginary_node_t) { { .type = PM_IMAGINARY_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(token) }, .numeric = (pm_node_t *) pm_float_node_create(parser, &((pm_token_t) { @@ -4210,11 +4164,12 @@ static pm_rational_node_t * pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_FLOAT_RATIONAL); - pm_rational_node_t *node = PM_ALLOC_NODE(parser, pm_rational_node_t); + pm_rational_node_t *node = PM_NODE_ALLOC(parser, pm_rational_node_t); *node = (pm_rational_node_t) { { .type = PM_RATIONAL_NODE, .flags = PM_INTEGER_BASE_FLAGS_DECIMAL | PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(token) }, .numerator = { 0 }, @@ -4263,11 +4218,12 @@ static pm_imaginary_node_t * pm_float_node_rational_imaginary_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_FLOAT_RATIONAL_IMAGINARY); - pm_imaginary_node_t *node = PM_ALLOC_NODE(parser, pm_imaginary_node_t); + pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t); *node = (pm_imaginary_node_t) { { .type = PM_IMAGINARY_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(token) }, .numeric = (pm_node_t *) pm_float_node_rational_create(parser, &((pm_token_t) { @@ -4294,11 +4250,12 @@ pm_for_node_create( const pm_token_t *do_keyword, const pm_token_t *end_keyword ) { - pm_for_node_t *node = PM_ALLOC_NODE(parser, pm_for_node_t); + pm_for_node_t *node = PM_NODE_ALLOC(parser, pm_for_node_t); *node = (pm_for_node_t) { { .type = PM_FOR_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = for_keyword->start, .end = end_keyword->end @@ -4322,8 +4279,14 @@ pm_for_node_create( static pm_forwarding_arguments_node_t * pm_forwarding_arguments_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_UDOT_DOT_DOT); - pm_forwarding_arguments_node_t *node = PM_ALLOC_NODE(parser, pm_forwarding_arguments_node_t); - *node = (pm_forwarding_arguments_node_t) {{ .type = PM_FORWARDING_ARGUMENTS_NODE, .location = PM_LOCATION_TOKEN_VALUE(token) }}; + pm_forwarding_arguments_node_t *node = PM_NODE_ALLOC(parser, pm_forwarding_arguments_node_t); + + *node = (pm_forwarding_arguments_node_t) {{ + .type = PM_FORWARDING_ARGUMENTS_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_TOKEN_VALUE(token) + }}; + return node; } @@ -4333,8 +4296,14 @@ pm_forwarding_arguments_node_create(pm_parser_t *parser, const pm_token_t *token static pm_forwarding_parameter_node_t * pm_forwarding_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_UDOT_DOT_DOT); - pm_forwarding_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_forwarding_parameter_node_t); - *node = (pm_forwarding_parameter_node_t) {{ .type = PM_FORWARDING_PARAMETER_NODE, .location = PM_LOCATION_TOKEN_VALUE(token) }}; + pm_forwarding_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_forwarding_parameter_node_t); + + *node = (pm_forwarding_parameter_node_t) {{ + .type = PM_FORWARDING_PARAMETER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_TOKEN_VALUE(token) + }}; + return node; } @@ -4345,7 +4314,7 @@ static pm_forwarding_super_node_t * pm_forwarding_super_node_create(pm_parser_t *parser, const pm_token_t *token, pm_arguments_t *arguments) { assert(arguments->block == NULL || PM_NODE_TYPE_P(arguments->block, PM_BLOCK_NODE)); assert(token->type == PM_TOKEN_KEYWORD_SUPER); - pm_forwarding_super_node_t *node = PM_ALLOC_NODE(parser, pm_forwarding_super_node_t); + pm_forwarding_super_node_t *node = PM_NODE_ALLOC(parser, pm_forwarding_super_node_t); pm_block_node_t *block = NULL; if (arguments->block != NULL) { @@ -4355,6 +4324,7 @@ pm_forwarding_super_node_create(pm_parser_t *parser, const pm_token_t *token, pm *node = (pm_forwarding_super_node_t) { { .type = PM_FORWARDING_SUPER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = token->start, .end = block != NULL ? block->base.location.end : token->end @@ -4372,11 +4342,12 @@ pm_forwarding_super_node_create(pm_parser_t *parser, const pm_token_t *token, pm */ static pm_hash_pattern_node_t * pm_hash_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) { - pm_hash_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_hash_pattern_node_t); + pm_hash_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_hash_pattern_node_t); *node = (pm_hash_pattern_node_t) { { .type = PM_HASH_PATTERN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = opening->start, .end = closing->end @@ -4397,7 +4368,7 @@ pm_hash_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening */ static pm_hash_pattern_node_t * pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *elements, pm_node_t *rest) { - pm_hash_pattern_node_t *node = PM_ALLOC_NODE(parser, pm_hash_pattern_node_t); + pm_hash_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_hash_pattern_node_t); const uint8_t *start; const uint8_t *end; @@ -4419,6 +4390,7 @@ pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *eleme *node = (pm_hash_pattern_node_t) { { .type = PM_HASH_PATTERN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = start, .end = end @@ -4465,11 +4437,12 @@ pm_global_variable_write_name(pm_parser_t *parser, const pm_node_t *target) { static pm_global_variable_and_write_node_t * pm_global_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) { assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL); - pm_global_variable_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_global_variable_and_write_node_t); + pm_global_variable_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_and_write_node_t); *node = (pm_global_variable_and_write_node_t) { { .type = PM_GLOBAL_VARIABLE_AND_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->location.start, .end = value->location.end @@ -4489,11 +4462,12 @@ pm_global_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, */ static pm_global_variable_operator_write_node_t * pm_global_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) { - pm_global_variable_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_global_variable_operator_write_node_t); + pm_global_variable_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_operator_write_node_t); *node = (pm_global_variable_operator_write_node_t) { { .type = PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->location.start, .end = value->location.end @@ -4515,11 +4489,12 @@ pm_global_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *ta static pm_global_variable_or_write_node_t * pm_global_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) { assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL); - pm_global_variable_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_global_variable_or_write_node_t); + pm_global_variable_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_or_write_node_t); *node = (pm_global_variable_or_write_node_t) { { .type = PM_GLOBAL_VARIABLE_OR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->location.start, .end = value->location.end @@ -4539,11 +4514,12 @@ pm_global_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, */ static pm_global_variable_read_node_t * pm_global_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) { - pm_global_variable_read_node_t *node = PM_ALLOC_NODE(parser, pm_global_variable_read_node_t); + pm_global_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_read_node_t); *node = (pm_global_variable_read_node_t) { { .type = PM_GLOBAL_VARIABLE_READ_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(name), }, .name = pm_parser_constant_id_token(parser, name) @@ -4557,11 +4533,12 @@ pm_global_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) */ static pm_global_variable_read_node_t * pm_global_variable_read_node_synthesized_create(pm_parser_t *parser, pm_constant_id_t name) { - pm_global_variable_read_node_t *node = PM_ALLOC_NODE(parser, pm_global_variable_read_node_t); + pm_global_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_read_node_t); *node = (pm_global_variable_read_node_t) { { .type = PM_GLOBAL_VARIABLE_READ_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_NULL_VALUE(parser) }, .name = name @@ -4575,11 +4552,12 @@ pm_global_variable_read_node_synthesized_create(pm_parser_t *parser, pm_constant */ static pm_global_variable_write_node_t * pm_global_variable_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) { - pm_global_variable_write_node_t *node = PM_ALLOC_NODE(parser, pm_global_variable_write_node_t); + pm_global_variable_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_write_node_t); *node = (pm_global_variable_write_node_t) { { .type = PM_GLOBAL_VARIABLE_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), .location = { .start = target->location.start, @@ -4600,11 +4578,12 @@ pm_global_variable_write_node_create(pm_parser_t *parser, pm_node_t *target, con */ static pm_global_variable_write_node_t * pm_global_variable_write_node_synthesized_create(pm_parser_t *parser, pm_constant_id_t name, pm_node_t *value) { - pm_global_variable_write_node_t *node = PM_ALLOC_NODE(parser, pm_global_variable_write_node_t); + pm_global_variable_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_write_node_t); *node = (pm_global_variable_write_node_t) { { .type = PM_GLOBAL_VARIABLE_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_NULL_VALUE(parser) }, .name = name, @@ -4622,12 +4601,13 @@ pm_global_variable_write_node_synthesized_create(pm_parser_t *parser, pm_constan static pm_hash_node_t * pm_hash_node_create(pm_parser_t *parser, const pm_token_t *opening) { assert(opening != NULL); - pm_hash_node_t *node = PM_ALLOC_NODE(parser, pm_hash_node_t); + pm_hash_node_t *node = PM_NODE_ALLOC(parser, pm_hash_node_t); *node = (pm_hash_node_t) { { .type = PM_HASH_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(opening) }, .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), @@ -4677,7 +4657,7 @@ pm_if_node_create(pm_parser_t *parser, const pm_token_t *end_keyword ) { pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); - pm_if_node_t *node = PM_ALLOC_NODE(parser, pm_if_node_t); + pm_if_node_t *node = PM_NODE_ALLOC(parser, pm_if_node_t); const uint8_t *end; if (end_keyword->type != PM_TOKEN_NOT_PROVIDED) { @@ -4694,6 +4674,7 @@ pm_if_node_create(pm_parser_t *parser, { .type = PM_IF_NODE, .flags = PM_NODE_FLAG_NEWLINE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = if_keyword->start, .end = end @@ -4716,7 +4697,7 @@ pm_if_node_create(pm_parser_t *parser, static pm_if_node_t * pm_if_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_token_t *if_keyword, pm_node_t *predicate) { pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); - pm_if_node_t *node = PM_ALLOC_NODE(parser, pm_if_node_t); + pm_if_node_t *node = PM_NODE_ALLOC(parser, pm_if_node_t); pm_statements_node_t *statements = pm_statements_node_create(parser); pm_statements_node_body_append(parser, statements, statement); @@ -4725,6 +4706,7 @@ pm_if_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_t { .type = PM_IF_NODE, .flags = PM_NODE_FLAG_NEWLINE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = statement->location.start, .end = predicate->location.end @@ -4758,12 +4740,13 @@ pm_if_node_ternary_create(pm_parser_t *parser, pm_node_t *predicate, const pm_to pm_token_t end_keyword = not_provided(parser); pm_else_node_t *else_node = pm_else_node_create(parser, colon, else_statements, &end_keyword); - pm_if_node_t *node = PM_ALLOC_NODE(parser, pm_if_node_t); + pm_if_node_t *node = PM_NODE_ALLOC(parser, pm_if_node_t); *node = (pm_if_node_t) { { .type = PM_IF_NODE, .flags = PM_NODE_FLAG_NEWLINE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = predicate->location.start, .end = false_expression->location.end, @@ -4798,11 +4781,12 @@ pm_else_node_end_keyword_loc_set(pm_else_node_t *node, const pm_token_t *keyword */ static pm_implicit_node_t * pm_implicit_node_create(pm_parser_t *parser, pm_node_t *value) { - pm_implicit_node_t *node = PM_ALLOC_NODE(parser, pm_implicit_node_t); + pm_implicit_node_t *node = PM_NODE_ALLOC(parser, pm_implicit_node_t); *node = (pm_implicit_node_t) { { .type = PM_IMPLICIT_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = value->location }, .value = value @@ -4818,11 +4802,12 @@ static pm_implicit_rest_node_t * pm_implicit_rest_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_COMMA); - pm_implicit_rest_node_t *node = PM_ALLOC_NODE(parser, pm_implicit_rest_node_t); + pm_implicit_rest_node_t *node = PM_NODE_ALLOC(parser, pm_implicit_rest_node_t); *node = (pm_implicit_rest_node_t) { { .type = PM_IMPLICIT_REST_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(token) } }; @@ -4836,12 +4821,13 @@ pm_implicit_rest_node_create(pm_parser_t *parser, const pm_token_t *token) { static pm_integer_node_t * pm_integer_node_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) { assert(token->type == PM_TOKEN_INTEGER); - pm_integer_node_t *node = PM_ALLOC_NODE(parser, pm_integer_node_t); + pm_integer_node_t *node = PM_NODE_ALLOC(parser, pm_integer_node_t); *node = (pm_integer_node_t) { { .type = PM_INTEGER_NODE, .flags = base | PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(token) }, .value = { 0 } @@ -4868,11 +4854,12 @@ static pm_imaginary_node_t * pm_integer_node_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) { assert(token->type == PM_TOKEN_INTEGER_IMAGINARY); - pm_imaginary_node_t *node = PM_ALLOC_NODE(parser, pm_imaginary_node_t); + pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t); *node = (pm_imaginary_node_t) { { .type = PM_IMAGINARY_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(token) }, .numeric = (pm_node_t *) pm_integer_node_create(parser, base, &((pm_token_t) { @@ -4893,11 +4880,12 @@ static pm_rational_node_t * pm_integer_node_rational_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) { assert(token->type == PM_TOKEN_INTEGER_RATIONAL); - pm_rational_node_t *node = PM_ALLOC_NODE(parser, pm_rational_node_t); + pm_rational_node_t *node = PM_NODE_ALLOC(parser, pm_rational_node_t); *node = (pm_rational_node_t) { { .type = PM_RATIONAL_NODE, .flags = base | PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(token) }, .numerator = { 0 }, @@ -4926,11 +4914,12 @@ static pm_imaginary_node_t * pm_integer_node_rational_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) { assert(token->type == PM_TOKEN_INTEGER_RATIONAL_IMAGINARY); - pm_imaginary_node_t *node = PM_ALLOC_NODE(parser, pm_imaginary_node_t); + pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t); *node = (pm_imaginary_node_t) { { .type = PM_IMAGINARY_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(token) }, .numeric = (pm_node_t *) pm_integer_node_rational_create(parser, base, &((pm_token_t) { @@ -4948,7 +4937,7 @@ pm_integer_node_rational_imaginary_create(pm_parser_t *parser, pm_node_flags_t b */ static pm_in_node_t * pm_in_node_create(pm_parser_t *parser, pm_node_t *pattern, pm_statements_node_t *statements, const pm_token_t *in_keyword, const pm_token_t *then_keyword) { - pm_in_node_t *node = PM_ALLOC_NODE(parser, pm_in_node_t); + pm_in_node_t *node = PM_NODE_ALLOC(parser, pm_in_node_t); const uint8_t *end; if (statements != NULL) { @@ -4962,6 +4951,7 @@ pm_in_node_create(pm_parser_t *parser, pm_node_t *pattern, pm_statements_node_t *node = (pm_in_node_t) { { .type = PM_IN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = in_keyword->start, .end = end @@ -4982,11 +4972,12 @@ pm_in_node_create(pm_parser_t *parser, pm_node_t *pattern, pm_statements_node_t static pm_instance_variable_and_write_node_t * pm_instance_variable_and_write_node_create(pm_parser_t *parser, pm_instance_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL); - pm_instance_variable_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_instance_variable_and_write_node_t); + pm_instance_variable_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_instance_variable_and_write_node_t); *node = (pm_instance_variable_and_write_node_t) { { .type = PM_INSTANCE_VARIABLE_AND_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -5006,11 +4997,12 @@ pm_instance_variable_and_write_node_create(pm_parser_t *parser, pm_instance_vari */ static pm_instance_variable_operator_write_node_t * pm_instance_variable_operator_write_node_create(pm_parser_t *parser, pm_instance_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { - pm_instance_variable_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_instance_variable_operator_write_node_t); + pm_instance_variable_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_instance_variable_operator_write_node_t); *node = (pm_instance_variable_operator_write_node_t) { { .type = PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -5032,11 +5024,12 @@ pm_instance_variable_operator_write_node_create(pm_parser_t *parser, pm_instance static pm_instance_variable_or_write_node_t * pm_instance_variable_or_write_node_create(pm_parser_t *parser, pm_instance_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL); - pm_instance_variable_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_instance_variable_or_write_node_t); + pm_instance_variable_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_instance_variable_or_write_node_t); *node = (pm_instance_variable_or_write_node_t) { { .type = PM_INSTANCE_VARIABLE_OR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -5057,11 +5050,12 @@ pm_instance_variable_or_write_node_create(pm_parser_t *parser, pm_instance_varia static pm_instance_variable_read_node_t * pm_instance_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_INSTANCE_VARIABLE); - pm_instance_variable_read_node_t *node = PM_ALLOC_NODE(parser, pm_instance_variable_read_node_t); + pm_instance_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_instance_variable_read_node_t); *node = (pm_instance_variable_read_node_t) { { .type = PM_INSTANCE_VARIABLE_READ_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(token) }, .name = pm_parser_constant_id_token(parser, token) @@ -5076,11 +5070,12 @@ pm_instance_variable_read_node_create(pm_parser_t *parser, const pm_token_t *tok */ static pm_instance_variable_write_node_t * pm_instance_variable_write_node_create(pm_parser_t *parser, pm_instance_variable_read_node_t *read_node, pm_token_t *operator, pm_node_t *value) { - pm_instance_variable_write_node_t *node = PM_ALLOC_NODE(parser, pm_instance_variable_write_node_t); + pm_instance_variable_write_node_t *node = PM_NODE_ALLOC(parser, pm_instance_variable_write_node_t); *node = (pm_instance_variable_write_node_t) { { .type = PM_INSTANCE_VARIABLE_WRITE_NODE, .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = read_node->base.location.start, .end = value->location.end @@ -5144,12 +5139,13 @@ pm_interpolated_node_append(pm_node_t *node, pm_node_list_t *parts, pm_node_t *p */ static pm_interpolated_regular_expression_node_t * pm_interpolated_regular_expression_node_create(pm_parser_t *parser, const pm_token_t *opening) { - pm_interpolated_regular_expression_node_t *node = PM_ALLOC_NODE(parser, pm_interpolated_regular_expression_node_t); + pm_interpolated_regular_expression_node_t *node = PM_NODE_ALLOC(parser, pm_interpolated_regular_expression_node_t); *node = (pm_interpolated_regular_expression_node_t) { { .type = PM_INTERPOLATED_REGULAR_EXPRESSION_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = opening->start, .end = NULL, @@ -5286,7 +5282,7 @@ pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_node_ */ 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_interpolated_string_node_t *node = PM_NODE_ALLOC(parser, pm_interpolated_string_node_t); pm_node_flags_t flags = PM_NODE_FLAG_STATIC_LITERAL; switch (parser->frozen_string_literal) { @@ -5302,6 +5298,7 @@ pm_interpolated_string_node_create(pm_parser_t *parser, const pm_token_t *openin { .type = PM_INTERPOLATED_STRING_NODE, .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = opening->start, .end = closing->end, @@ -5352,12 +5349,13 @@ pm_interpolated_symbol_node_closing_loc_set(pm_interpolated_symbol_node_t *node, */ static pm_interpolated_symbol_node_t * pm_interpolated_symbol_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_node_list_t *parts, const pm_token_t *closing) { - pm_interpolated_symbol_node_t *node = PM_ALLOC_NODE(parser, pm_interpolated_symbol_node_t); + pm_interpolated_symbol_node_t *node = PM_NODE_ALLOC(parser, pm_interpolated_symbol_node_t); *node = (pm_interpolated_symbol_node_t) { { .type = PM_INTERPOLATED_SYMBOL_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = opening->start, .end = closing->end, @@ -5383,11 +5381,12 @@ pm_interpolated_symbol_node_create(pm_parser_t *parser, const pm_token_t *openin */ static pm_interpolated_x_string_node_t * pm_interpolated_xstring_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) { - pm_interpolated_x_string_node_t *node = PM_ALLOC_NODE(parser, pm_interpolated_x_string_node_t); + pm_interpolated_x_string_node_t *node = PM_NODE_ALLOC(parser, pm_interpolated_x_string_node_t); *node = (pm_interpolated_x_string_node_t) { { .type = PM_INTERPOLATED_X_STRING_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = opening->start, .end = closing->end @@ -5418,11 +5417,12 @@ pm_interpolated_xstring_node_closing_set(pm_interpolated_x_string_node_t *node, */ static pm_it_local_variable_read_node_t * pm_it_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) { - pm_it_local_variable_read_node_t *node = PM_ALLOC_NODE(parser, pm_it_local_variable_read_node_t); + pm_it_local_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_it_local_variable_read_node_t); *node = (pm_it_local_variable_read_node_t) { { .type = PM_IT_LOCAL_VARIABLE_READ_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(name) } }; @@ -5435,11 +5435,12 @@ pm_it_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *nam */ static pm_it_parameters_node_t * pm_it_parameters_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) { - pm_it_parameters_node_t *node = PM_ALLOC_NODE(parser, pm_it_parameters_node_t); + pm_it_parameters_node_t *node = PM_NODE_ALLOC(parser, pm_it_parameters_node_t); *node = (pm_it_parameters_node_t) { { .type = PM_IT_PARAMETERS_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = opening->start, .end = closing->end @@ -5455,13 +5456,14 @@ pm_it_parameters_node_create(pm_parser_t *parser, const pm_token_t *opening, con */ static pm_keyword_hash_node_t * pm_keyword_hash_node_create(pm_parser_t *parser) { - pm_keyword_hash_node_t *node = PM_ALLOC_NODE(parser, pm_keyword_hash_node_t); + pm_keyword_hash_node_t *node = PM_NODE_ALLOC(parser, pm_keyword_hash_node_t); *node = (pm_keyword_hash_node_t) { .base = { .type = PM_KEYWORD_HASH_NODE, - .location = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, - .flags = PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS + .flags = PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE }, .elements = { 0 } }; @@ -5492,11 +5494,12 @@ pm_keyword_hash_node_elements_append(pm_keyword_hash_node_t *hash, pm_node_t *el */ static pm_required_keyword_parameter_node_t * pm_required_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t *name) { - pm_required_keyword_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_required_keyword_parameter_node_t); + pm_required_keyword_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_required_keyword_parameter_node_t); *node = (pm_required_keyword_parameter_node_t) { { .type = PM_REQUIRED_KEYWORD_PARAMETER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = name->start, .end = name->end @@ -5514,11 +5517,12 @@ pm_required_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t */ static pm_optional_keyword_parameter_node_t * pm_optional_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, pm_node_t *value) { - pm_optional_keyword_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_optional_keyword_parameter_node_t); + pm_optional_keyword_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_optional_keyword_parameter_node_t); *node = (pm_optional_keyword_parameter_node_t) { { .type = PM_OPTIONAL_KEYWORD_PARAMETER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = name->start, .end = value->location.end @@ -5537,11 +5541,12 @@ pm_optional_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t */ static pm_keyword_rest_parameter_node_t * pm_keyword_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *name) { - pm_keyword_rest_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_keyword_rest_parameter_node_t); + pm_keyword_rest_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_keyword_rest_parameter_node_t); *node = (pm_keyword_rest_parameter_node_t) { { .type = PM_KEYWORD_REST_PARAMETER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = operator->start, .end = (name->type == PM_TOKEN_NOT_PROVIDED ? operator->end : name->end) @@ -5568,11 +5573,12 @@ pm_lambda_node_create( pm_node_t *parameters, pm_node_t *body ) { - pm_lambda_node_t *node = PM_ALLOC_NODE(parser, pm_lambda_node_t); + pm_lambda_node_t *node = PM_NODE_ALLOC(parser, pm_lambda_node_t); *node = (pm_lambda_node_t) { { .type = PM_LAMBDA_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = operator->start, .end = closing->end @@ -5596,11 +5602,12 @@ static pm_local_variable_and_write_node_t * pm_local_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value, pm_constant_id_t name, uint32_t depth) { assert(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_READ_NODE) || PM_NODE_TYPE_P(target, PM_CALL_NODE)); assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL); - pm_local_variable_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_and_write_node_t); + pm_local_variable_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_and_write_node_t); *node = (pm_local_variable_and_write_node_t) { { .type = PM_LOCAL_VARIABLE_AND_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->location.start, .end = value->location.end @@ -5621,11 +5628,12 @@ pm_local_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, */ static pm_local_variable_operator_write_node_t * pm_local_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value, pm_constant_id_t name, uint32_t depth) { - pm_local_variable_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_operator_write_node_t); + pm_local_variable_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_operator_write_node_t); *node = (pm_local_variable_operator_write_node_t) { { .type = PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->location.start, .end = value->location.end @@ -5649,11 +5657,12 @@ static pm_local_variable_or_write_node_t * pm_local_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value, pm_constant_id_t name, uint32_t depth) { assert(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_READ_NODE) || PM_NODE_TYPE_P(target, PM_CALL_NODE)); assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL); - pm_local_variable_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_or_write_node_t); + pm_local_variable_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_or_write_node_t); *node = (pm_local_variable_or_write_node_t) { { .type = PM_LOCAL_VARIABLE_OR_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->location.start, .end = value->location.end @@ -5676,11 +5685,12 @@ static pm_local_variable_read_node_t * pm_local_variable_read_node_create_constant_id(pm_parser_t *parser, const pm_token_t *name, pm_constant_id_t name_id, uint32_t depth, bool missing) { if (!missing) pm_locals_read(&pm_parser_scope_find(parser, depth)->locals, name_id); - pm_local_variable_read_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_read_node_t); + pm_local_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_read_node_t); *node = (pm_local_variable_read_node_t) { { .type = PM_LOCAL_VARIABLE_READ_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(name) }, .name = name_id, @@ -5714,12 +5724,13 @@ pm_local_variable_read_node_missing_create(pm_parser_t *parser, const pm_token_t */ static pm_local_variable_write_node_t * pm_local_variable_write_node_create(pm_parser_t *parser, pm_constant_id_t name, uint32_t depth, pm_node_t *value, const pm_location_t *name_loc, const pm_token_t *operator) { - pm_local_variable_write_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_write_node_t); + pm_local_variable_write_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_write_node_t); *node = (pm_local_variable_write_node_t) { { .type = PM_LOCAL_VARIABLE_WRITE_NODE, .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = name_loc->start, .end = value->location.end @@ -5770,11 +5781,12 @@ pm_refute_numbered_parameter(pm_parser_t *parser, const uint8_t *start, const ui static pm_local_variable_target_node_t * pm_local_variable_target_node_create(pm_parser_t *parser, const pm_location_t *location, pm_constant_id_t name, uint32_t depth) { pm_refute_numbered_parameter(parser, location->start, location->end); - pm_local_variable_target_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_target_node_t); + pm_local_variable_target_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_target_node_t); *node = (pm_local_variable_target_node_t) { { .type = PM_LOCAL_VARIABLE_TARGET_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = *location }, .name = name, @@ -5791,11 +5803,12 @@ static pm_match_predicate_node_t * pm_match_predicate_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t *pattern, const pm_token_t *operator) { pm_assert_value_expression(parser, value); - pm_match_predicate_node_t *node = PM_ALLOC_NODE(parser, pm_match_predicate_node_t); + pm_match_predicate_node_t *node = PM_NODE_ALLOC(parser, pm_match_predicate_node_t); *node = (pm_match_predicate_node_t) { { .type = PM_MATCH_PREDICATE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = value->location.start, .end = pattern->location.end @@ -5816,11 +5829,12 @@ static pm_match_required_node_t * pm_match_required_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t *pattern, const pm_token_t *operator) { pm_assert_value_expression(parser, value); - pm_match_required_node_t *node = PM_ALLOC_NODE(parser, pm_match_required_node_t); + pm_match_required_node_t *node = PM_NODE_ALLOC(parser, pm_match_required_node_t); *node = (pm_match_required_node_t) { { .type = PM_MATCH_REQUIRED_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = value->location.start, .end = pattern->location.end @@ -5839,11 +5853,12 @@ pm_match_required_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t * */ static pm_match_write_node_t * pm_match_write_node_create(pm_parser_t *parser, pm_call_node_t *call) { - pm_match_write_node_t *node = PM_ALLOC_NODE(parser, pm_match_write_node_t); + pm_match_write_node_t *node = PM_NODE_ALLOC(parser, pm_match_write_node_t); *node = (pm_match_write_node_t) { { .type = PM_MATCH_WRITE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = call->base.location }, .call = call, @@ -5858,11 +5873,12 @@ pm_match_write_node_create(pm_parser_t *parser, pm_call_node_t *call) { */ static pm_module_node_t * pm_module_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_token_t *module_keyword, pm_node_t *constant_path, const pm_token_t *name, pm_node_t *body, const pm_token_t *end_keyword) { - pm_module_node_t *node = PM_ALLOC_NODE(parser, pm_module_node_t); + pm_module_node_t *node = PM_NODE_ALLOC(parser, pm_module_node_t); *node = (pm_module_node_t) { { .type = PM_MODULE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = module_keyword->start, .end = end_keyword->end @@ -5884,11 +5900,12 @@ pm_module_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const */ static pm_multi_target_node_t * pm_multi_target_node_create(pm_parser_t *parser) { - pm_multi_target_node_t *node = PM_ALLOC_NODE(parser, pm_multi_target_node_t); + pm_multi_target_node_t *node = PM_NODE_ALLOC(parser, pm_multi_target_node_t); *node = (pm_multi_target_node_t) { { .type = PM_MULTI_TARGET_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = NULL, .end = NULL } }, .lefts = { 0 }, @@ -5958,12 +5975,13 @@ pm_multi_target_node_closing_set(pm_multi_target_node_t *node, const pm_token_t */ static pm_multi_write_node_t * pm_multi_write_node_create(pm_parser_t *parser, pm_multi_target_node_t *target, const pm_token_t *operator, pm_node_t *value) { - pm_multi_write_node_t *node = PM_ALLOC_NODE(parser, pm_multi_write_node_t); + pm_multi_write_node_t *node = PM_NODE_ALLOC(parser, pm_multi_write_node_t); *node = (pm_multi_write_node_t) { { .type = PM_MULTI_WRITE_NODE, .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = target->base.location.start, .end = value->location.end @@ -5991,11 +6009,12 @@ pm_multi_write_node_create(pm_parser_t *parser, pm_multi_target_node_t *target, static pm_next_node_t * pm_next_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) { assert(keyword->type == PM_TOKEN_KEYWORD_NEXT); - pm_next_node_t *node = PM_ALLOC_NODE(parser, pm_next_node_t); + pm_next_node_t *node = PM_NODE_ALLOC(parser, pm_next_node_t); *node = (pm_next_node_t) { { .type = PM_NEXT_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = keyword->start, .end = (arguments == NULL ? keyword->end : arguments->base.location.end) @@ -6014,11 +6033,12 @@ pm_next_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments static pm_nil_node_t * pm_nil_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_KEYWORD_NIL); - pm_nil_node_t *node = PM_ALLOC_NODE(parser, pm_nil_node_t); + pm_nil_node_t *node = PM_NODE_ALLOC(parser, pm_nil_node_t); *node = (pm_nil_node_t) {{ .type = PM_NIL_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(token) }}; @@ -6032,11 +6052,12 @@ static pm_no_keywords_parameter_node_t * pm_no_keywords_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *keyword) { assert(operator->type == PM_TOKEN_USTAR_STAR || operator->type == PM_TOKEN_STAR_STAR); assert(keyword->type == PM_TOKEN_KEYWORD_NIL); - pm_no_keywords_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_no_keywords_parameter_node_t); + pm_no_keywords_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_no_keywords_parameter_node_t); *node = (pm_no_keywords_parameter_node_t) { { .type = PM_NO_KEYWORDS_PARAMETER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = operator->start, .end = keyword->end @@ -6054,11 +6075,12 @@ pm_no_keywords_parameter_node_create(pm_parser_t *parser, const pm_token_t *oper */ static pm_numbered_parameters_node_t * pm_numbered_parameters_node_create(pm_parser_t *parser, const pm_location_t *location, uint8_t maximum) { - pm_numbered_parameters_node_t *node = PM_ALLOC_NODE(parser, pm_numbered_parameters_node_t); + pm_numbered_parameters_node_t *node = PM_NODE_ALLOC(parser, pm_numbered_parameters_node_t); *node = (pm_numbered_parameters_node_t) { { .type = PM_NUMBERED_PARAMETERS_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = *location }, .maximum = maximum @@ -6119,11 +6141,12 @@ pm_numbered_reference_read_node_number(pm_parser_t *parser, const pm_token_t *to static pm_numbered_reference_read_node_t * pm_numbered_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) { assert(name->type == PM_TOKEN_NUMBERED_REFERENCE); - pm_numbered_reference_read_node_t *node = PM_ALLOC_NODE(parser, pm_numbered_reference_read_node_t); + pm_numbered_reference_read_node_t *node = PM_NODE_ALLOC(parser, pm_numbered_reference_read_node_t); *node = (pm_numbered_reference_read_node_t) { { .type = PM_NUMBERED_REFERENCE_READ_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(name), }, .number = pm_numbered_reference_read_node_number(parser, name) @@ -6137,11 +6160,12 @@ pm_numbered_reference_read_node_create(pm_parser_t *parser, const pm_token_t *na */ static pm_optional_parameter_node_t * pm_optional_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, const pm_token_t *operator, pm_node_t *value) { - pm_optional_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_optional_parameter_node_t); + pm_optional_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_optional_parameter_node_t); *node = (pm_optional_parameter_node_t) { { .type = PM_OPTIONAL_PARAMETER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = name->start, .end = value->location.end @@ -6163,11 +6187,12 @@ static pm_or_node_t * pm_or_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) { pm_assert_value_expression(parser, left); - pm_or_node_t *node = PM_ALLOC_NODE(parser, pm_or_node_t); + pm_or_node_t *node = PM_NODE_ALLOC(parser, pm_or_node_t); *node = (pm_or_node_t) { { .type = PM_OR_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = left->location.start, .end = right->location.end @@ -6186,11 +6211,12 @@ pm_or_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operat */ static pm_parameters_node_t * pm_parameters_node_create(pm_parser_t *parser) { - pm_parameters_node_t *node = PM_ALLOC_NODE(parser, pm_parameters_node_t); + pm_parameters_node_t *node = PM_NODE_ALLOC(parser, pm_parameters_node_t); *node = (pm_parameters_node_t) { { .type = PM_PARAMETERS_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(&parser->current) }, .rest = NULL, @@ -6293,11 +6319,12 @@ pm_parameters_node_block_set(pm_parameters_node_t *params, pm_block_parameter_no */ static pm_program_node_t * pm_program_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, pm_statements_node_t *statements) { - pm_program_node_t *node = PM_ALLOC_NODE(parser, pm_program_node_t); + pm_program_node_t *node = PM_NODE_ALLOC(parser, pm_program_node_t); *node = (pm_program_node_t) { { .type = PM_PROGRAM_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = statements == NULL ? parser->start : statements->base.location.start, .end = statements == NULL ? parser->end : statements->base.location.end @@ -6315,11 +6342,12 @@ pm_program_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, pm_st */ static pm_parentheses_node_t * pm_parentheses_node_create(pm_parser_t *parser, const pm_token_t *opening, pm_node_t *body, const pm_token_t *closing) { - pm_parentheses_node_t *node = PM_ALLOC_NODE(parser, pm_parentheses_node_t); + pm_parentheses_node_t *node = PM_NODE_ALLOC(parser, pm_parentheses_node_t); *node = (pm_parentheses_node_t) { { .type = PM_PARENTHESES_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = opening->start, .end = closing->end @@ -6338,11 +6366,12 @@ pm_parentheses_node_create(pm_parser_t *parser, const pm_token_t *opening, pm_no */ static pm_pinned_expression_node_t * pm_pinned_expression_node_create(pm_parser_t *parser, pm_node_t *expression, const pm_token_t *operator, const pm_token_t *lparen, const pm_token_t *rparen) { - pm_pinned_expression_node_t *node = PM_ALLOC_NODE(parser, pm_pinned_expression_node_t); + pm_pinned_expression_node_t *node = PM_NODE_ALLOC(parser, pm_pinned_expression_node_t); *node = (pm_pinned_expression_node_t) { { .type = PM_PINNED_EXPRESSION_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = operator->start, .end = rparen->end @@ -6362,11 +6391,12 @@ pm_pinned_expression_node_create(pm_parser_t *parser, pm_node_t *expression, con */ static pm_pinned_variable_node_t * pm_pinned_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *variable) { - pm_pinned_variable_node_t *node = PM_ALLOC_NODE(parser, pm_pinned_variable_node_t); + pm_pinned_variable_node_t *node = PM_NODE_ALLOC(parser, pm_pinned_variable_node_t); *node = (pm_pinned_variable_node_t) { { .type = PM_PINNED_VARIABLE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = operator->start, .end = variable->location.end @@ -6384,11 +6414,12 @@ pm_pinned_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, */ static pm_post_execution_node_t * pm_post_execution_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_token_t *opening, pm_statements_node_t *statements, const pm_token_t *closing) { - pm_post_execution_node_t *node = PM_ALLOC_NODE(parser, pm_post_execution_node_t); + pm_post_execution_node_t *node = PM_NODE_ALLOC(parser, pm_post_execution_node_t); *node = (pm_post_execution_node_t) { { .type = PM_POST_EXECUTION_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = keyword->start, .end = closing->end @@ -6408,11 +6439,12 @@ pm_post_execution_node_create(pm_parser_t *parser, const pm_token_t *keyword, co */ static pm_pre_execution_node_t * pm_pre_execution_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_token_t *opening, pm_statements_node_t *statements, const pm_token_t *closing) { - pm_pre_execution_node_t *node = PM_ALLOC_NODE(parser, pm_pre_execution_node_t); + pm_pre_execution_node_t *node = PM_NODE_ALLOC(parser, pm_pre_execution_node_t); *node = (pm_pre_execution_node_t) { { .type = PM_PRE_EXECUTION_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = keyword->start, .end = closing->end @@ -6435,7 +6467,7 @@ pm_range_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *ope pm_assert_value_expression(parser, left); pm_assert_value_expression(parser, right); - pm_range_node_t *node = PM_ALLOC_NODE(parser, pm_range_node_t); + pm_range_node_t *node = PM_NODE_ALLOC(parser, pm_range_node_t); pm_node_flags_t flags = 0; // Indicate that this node is an exclusive range if the operator is `...`. @@ -6457,6 +6489,7 @@ pm_range_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *ope { .type = PM_RANGE_NODE, .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = (left == NULL ? operator->start : left->location.start), .end = (right == NULL ? operator->end : right->location.end) @@ -6476,9 +6509,14 @@ pm_range_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *ope static pm_redo_node_t * pm_redo_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_KEYWORD_REDO); - pm_redo_node_t *node = PM_ALLOC_NODE(parser, pm_redo_node_t); + pm_redo_node_t *node = PM_NODE_ALLOC(parser, pm_redo_node_t); + + *node = (pm_redo_node_t) {{ + .type = PM_REDO_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_TOKEN_VALUE(token) + }}; - *node = (pm_redo_node_t) {{ .type = PM_REDO_NODE, .location = PM_LOCATION_TOKEN_VALUE(token) }}; return node; } @@ -6488,12 +6526,13 @@ pm_redo_node_create(pm_parser_t *parser, const pm_token_t *token) { */ static pm_regular_expression_node_t * pm_regular_expression_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing, const pm_string_t *unescaped) { - pm_regular_expression_node_t *node = PM_ALLOC_NODE(parser, pm_regular_expression_node_t); + pm_regular_expression_node_t *node = PM_NODE_ALLOC(parser, pm_regular_expression_node_t); *node = (pm_regular_expression_node_t) { { .type = PM_REGULAR_EXPRESSION_NODE, .flags = pm_regular_expression_flags_create(parser, closing) | PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = MIN(opening->start, closing->start), .end = MAX(opening->end, closing->end) @@ -6521,11 +6560,12 @@ pm_regular_expression_node_create(pm_parser_t *parser, const pm_token_t *opening */ static pm_required_parameter_node_t * pm_required_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) { - pm_required_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_required_parameter_node_t); + pm_required_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_required_parameter_node_t); *node = (pm_required_parameter_node_t) { { .type = PM_REQUIRED_PARAMETER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(token) }, .name = pm_parser_constant_id_token(parser, token) @@ -6539,11 +6579,12 @@ pm_required_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) */ static pm_rescue_modifier_node_t * pm_rescue_modifier_node_create(pm_parser_t *parser, pm_node_t *expression, const pm_token_t *keyword, pm_node_t *rescue_expression) { - pm_rescue_modifier_node_t *node = PM_ALLOC_NODE(parser, pm_rescue_modifier_node_t); + pm_rescue_modifier_node_t *node = PM_NODE_ALLOC(parser, pm_rescue_modifier_node_t); *node = (pm_rescue_modifier_node_t) { { .type = PM_RESCUE_MODIFIER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = expression->location.start, .end = rescue_expression->location.end @@ -6562,11 +6603,12 @@ pm_rescue_modifier_node_create(pm_parser_t *parser, pm_node_t *expression, const */ static pm_rescue_node_t * pm_rescue_node_create(pm_parser_t *parser, const pm_token_t *keyword) { - pm_rescue_node_t *node = PM_ALLOC_NODE(parser, pm_rescue_node_t); + pm_rescue_node_t *node = PM_NODE_ALLOC(parser, pm_rescue_node_t); *node = (pm_rescue_node_t) { { .type = PM_RESCUE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(keyword) }, .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), @@ -6628,11 +6670,12 @@ pm_rescue_node_exceptions_append(pm_rescue_node_t *node, pm_node_t *exception) { */ static pm_rest_parameter_node_t * pm_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *name) { - pm_rest_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_rest_parameter_node_t); + pm_rest_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_rest_parameter_node_t); *node = (pm_rest_parameter_node_t) { { .type = PM_REST_PARAMETER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = operator->start, .end = (name->type == PM_TOKEN_NOT_PROVIDED ? operator->end : name->end) @@ -6652,9 +6695,14 @@ pm_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, c static pm_retry_node_t * pm_retry_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_KEYWORD_RETRY); - pm_retry_node_t *node = PM_ALLOC_NODE(parser, pm_retry_node_t); + pm_retry_node_t *node = PM_NODE_ALLOC(parser, pm_retry_node_t); + + *node = (pm_retry_node_t) {{ + .type = PM_RETRY_NODE, + .node_id = PM_NODE_IDENTIFY(parser), + .location = PM_LOCATION_TOKEN_VALUE(token) + }}; - *node = (pm_retry_node_t) {{ .type = PM_RETRY_NODE, .location = PM_LOCATION_TOKEN_VALUE(token) }}; return node; } @@ -6663,12 +6711,12 @@ pm_retry_node_create(pm_parser_t *parser, const pm_token_t *token) { */ static pm_return_node_t * pm_return_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) { - pm_return_node_t *node = PM_ALLOC_NODE(parser, pm_return_node_t); + pm_return_node_t *node = PM_NODE_ALLOC(parser, pm_return_node_t); *node = (pm_return_node_t) { { .type = PM_RETURN_NODE, - .flags = 0, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = keyword->start, .end = (arguments == NULL ? keyword->end : arguments->base.location.end) @@ -6687,10 +6735,11 @@ pm_return_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_argumen static pm_self_node_t * pm_self_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_KEYWORD_SELF); - pm_self_node_t *node = PM_ALLOC_NODE(parser, pm_self_node_t); + pm_self_node_t *node = PM_NODE_ALLOC(parser, pm_self_node_t); *node = (pm_self_node_t) {{ .type = PM_SELF_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(token) }}; @@ -6702,12 +6751,13 @@ pm_self_node_create(pm_parser_t *parser, const pm_token_t *token) { */ static pm_shareable_constant_node_t * pm_shareable_constant_node_create(pm_parser_t *parser, pm_node_t *write, pm_shareable_constant_value_t value) { - pm_shareable_constant_node_t *node = PM_ALLOC_NODE(parser, pm_shareable_constant_node_t); + pm_shareable_constant_node_t *node = PM_NODE_ALLOC(parser, pm_shareable_constant_node_t); *node = (pm_shareable_constant_node_t) { { .type = PM_SHAREABLE_CONSTANT_NODE, .flags = (pm_node_flags_t) value, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_NODE_VALUE(write) }, .write = write @@ -6721,11 +6771,12 @@ pm_shareable_constant_node_create(pm_parser_t *parser, pm_node_t *write, pm_shar */ static pm_singleton_class_node_t * pm_singleton_class_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_token_t *class_keyword, const pm_token_t *operator, pm_node_t *expression, pm_node_t *body, const pm_token_t *end_keyword) { - pm_singleton_class_node_t *node = PM_ALLOC_NODE(parser, pm_singleton_class_node_t); + pm_singleton_class_node_t *node = PM_NODE_ALLOC(parser, pm_singleton_class_node_t); *node = (pm_singleton_class_node_t) { { .type = PM_SINGLETON_CLASS_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = class_keyword->start, .end = end_keyword->end @@ -6748,11 +6799,12 @@ pm_singleton_class_node_create(pm_parser_t *parser, pm_constant_id_list_t *local static pm_source_encoding_node_t * pm_source_encoding_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_KEYWORD___ENCODING__); - pm_source_encoding_node_t *node = PM_ALLOC_NODE(parser, pm_source_encoding_node_t); + pm_source_encoding_node_t *node = PM_NODE_ALLOC(parser, pm_source_encoding_node_t); *node = (pm_source_encoding_node_t) {{ .type = PM_SOURCE_ENCODING_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(token) }}; @@ -6764,7 +6816,7 @@ pm_source_encoding_node_create(pm_parser_t *parser, const pm_token_t *token) { */ static pm_source_file_node_t* pm_source_file_node_create(pm_parser_t *parser, const pm_token_t *file_keyword) { - pm_source_file_node_t *node = PM_ALLOC_NODE(parser, pm_source_file_node_t); + pm_source_file_node_t *node = PM_NODE_ALLOC(parser, pm_source_file_node_t); assert(file_keyword->type == PM_TOKEN_KEYWORD___FILE__); pm_node_flags_t flags = 0; @@ -6782,6 +6834,7 @@ pm_source_file_node_create(pm_parser_t *parser, const pm_token_t *file_keyword) { .type = PM_SOURCE_FILE_NODE, .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(file_keyword), }, .filepath = parser->filepath @@ -6796,11 +6849,12 @@ pm_source_file_node_create(pm_parser_t *parser, const pm_token_t *file_keyword) static pm_source_line_node_t * pm_source_line_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_KEYWORD___LINE__); - pm_source_line_node_t *node = PM_ALLOC_NODE(parser, pm_source_line_node_t); + pm_source_line_node_t *node = PM_NODE_ALLOC(parser, pm_source_line_node_t); *node = (pm_source_line_node_t) {{ .type = PM_SOURCE_LINE_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(token) }}; @@ -6812,11 +6866,12 @@ pm_source_line_node_create(pm_parser_t *parser, const pm_token_t *token) { */ static pm_splat_node_t * pm_splat_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *expression) { - pm_splat_node_t *node = PM_ALLOC_NODE(parser, pm_splat_node_t); + pm_splat_node_t *node = PM_NODE_ALLOC(parser, pm_splat_node_t); *node = (pm_splat_node_t) { { .type = PM_SPLAT_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = operator->start, .end = (expression == NULL ? operator->end : expression->location.end) @@ -6834,11 +6889,12 @@ pm_splat_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t */ static pm_statements_node_t * pm_statements_node_create(pm_parser_t *parser) { - pm_statements_node_t *node = PM_ALLOC_NODE(parser, pm_statements_node_t); + pm_statements_node_t *node = PM_NODE_ALLOC(parser, pm_statements_node_t); *node = (pm_statements_node_t) { { .type = PM_STATEMENTS_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_NULL_VALUE(parser) }, .body = { 0 } @@ -6920,7 +6976,7 @@ pm_statements_node_body_prepend(pm_statements_node_t *node, pm_node_t *statement */ static inline pm_string_node_t * pm_string_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing, const pm_string_t *string) { - pm_string_node_t *node = PM_ALLOC_NODE(parser, pm_string_node_t); + pm_string_node_t *node = PM_NODE_ALLOC(parser, pm_string_node_t); pm_node_flags_t flags = 0; switch (parser->frozen_string_literal) { @@ -6936,6 +6992,7 @@ pm_string_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, { .type = PM_STRING_NODE, .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = (opening->type == PM_TOKEN_NOT_PROVIDED ? content->start : opening->start), .end = (closing->type == PM_TOKEN_NOT_PROVIDED ? content->end : closing->end) @@ -6975,7 +7032,7 @@ pm_string_node_create_current_string(pm_parser_t *parser, const pm_token_t *open static pm_super_node_t * pm_super_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_t *arguments) { assert(keyword->type == PM_TOKEN_KEYWORD_SUPER); - pm_super_node_t *node = PM_ALLOC_NODE(parser, pm_super_node_t); + pm_super_node_t *node = PM_NODE_ALLOC(parser, pm_super_node_t); const uint8_t *end = pm_arguments_end(arguments); if (end == NULL) { @@ -6985,6 +7042,7 @@ pm_super_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_argument *node = (pm_super_node_t) { { .type = PM_SUPER_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = keyword->start, .end = end, @@ -7216,12 +7274,13 @@ parse_and_validate_regular_expression_encoding(pm_parser_t *parser, const pm_str */ static pm_symbol_node_t * pm_symbol_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing, const pm_string_t *unescaped, pm_node_flags_t flags) { - pm_symbol_node_t *node = PM_ALLOC_NODE(parser, pm_symbol_node_t); + pm_symbol_node_t *node = PM_NODE_ALLOC(parser, pm_symbol_node_t); *node = (pm_symbol_node_t) { { .type = PM_SYMBOL_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL | flags, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = (opening->type == PM_TOKEN_NOT_PROVIDED ? value->start : opening->start), .end = (closing->type == PM_TOKEN_NOT_PROVIDED ? value->end : closing->end) @@ -7297,12 +7356,13 @@ pm_symbol_node_label_create(pm_parser_t *parser, const pm_token_t *token) { */ static pm_symbol_node_t * pm_symbol_node_synthesized_create(pm_parser_t *parser, const char *content) { - pm_symbol_node_t *node = PM_ALLOC_NODE(parser, pm_symbol_node_t); + pm_symbol_node_t *node = PM_NODE_ALLOC(parser, pm_symbol_node_t); *node = (pm_symbol_node_t) { { .type = PM_SYMBOL_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_NULL_VALUE(parser) }, .value_loc = PM_LOCATION_NULL_VALUE(parser), @@ -7339,12 +7399,13 @@ pm_symbol_node_label_p(pm_node_t *node) { */ static pm_symbol_node_t * pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const pm_token_t *opening, const pm_token_t *closing) { - pm_symbol_node_t *new_node = PM_ALLOC_NODE(parser, pm_symbol_node_t); + pm_symbol_node_t *new_node = PM_NODE_ALLOC(parser, pm_symbol_node_t); *new_node = (pm_symbol_node_t) { { .type = PM_SYMBOL_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = opening->start, .end = closing->end @@ -7372,7 +7433,7 @@ pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const */ static pm_string_node_t * pm_symbol_node_to_string_node(pm_parser_t *parser, pm_symbol_node_t *node) { - pm_string_node_t *new_node = PM_ALLOC_NODE(parser, pm_string_node_t); + pm_string_node_t *new_node = PM_NODE_ALLOC(parser, pm_string_node_t); pm_node_flags_t flags = 0; switch (parser->frozen_string_literal) { @@ -7388,6 +7449,7 @@ pm_symbol_node_to_string_node(pm_parser_t *parser, pm_symbol_node_t *node) { { .type = PM_STRING_NODE, .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), .location = node->base.location }, .opening_loc = node->opening_loc, @@ -7410,11 +7472,12 @@ pm_symbol_node_to_string_node(pm_parser_t *parser, pm_symbol_node_t *node) { static pm_true_node_t * pm_true_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_KEYWORD_TRUE); - pm_true_node_t *node = PM_ALLOC_NODE(parser, pm_true_node_t); + pm_true_node_t *node = PM_NODE_ALLOC(parser, pm_true_node_t); *node = (pm_true_node_t) {{ .type = PM_TRUE_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(token) }}; @@ -7426,11 +7489,12 @@ pm_true_node_create(pm_parser_t *parser, const pm_token_t *token) { */ static pm_true_node_t * pm_true_node_synthesized_create(pm_parser_t *parser) { - pm_true_node_t *node = PM_ALLOC_NODE(parser, pm_true_node_t); + pm_true_node_t *node = PM_NODE_ALLOC(parser, pm_true_node_t); *node = (pm_true_node_t) {{ .type = PM_TRUE_NODE, .flags = PM_NODE_FLAG_STATIC_LITERAL, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = parser->start, .end = parser->end } }}; @@ -7443,11 +7507,12 @@ pm_true_node_synthesized_create(pm_parser_t *parser) { static pm_undef_node_t * pm_undef_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_KEYWORD_UNDEF); - pm_undef_node_t *node = PM_ALLOC_NODE(parser, pm_undef_node_t); + pm_undef_node_t *node = PM_NODE_ALLOC(parser, pm_undef_node_t); *node = (pm_undef_node_t) { { .type = PM_UNDEF_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_TOKEN_VALUE(token), }, .keyword_loc = PM_LOCATION_TOKEN_VALUE(token), @@ -7472,7 +7537,7 @@ pm_undef_node_append(pm_undef_node_t *node, pm_node_t *name) { static pm_unless_node_t * pm_unless_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *predicate, const pm_token_t *then_keyword, pm_statements_node_t *statements) { pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); - pm_unless_node_t *node = PM_ALLOC_NODE(parser, pm_unless_node_t); + pm_unless_node_t *node = PM_NODE_ALLOC(parser, pm_unless_node_t); const uint8_t *end; if (statements != NULL) { @@ -7485,6 +7550,7 @@ pm_unless_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t { .type = PM_UNLESS_NODE, .flags = PM_NODE_FLAG_NEWLINE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = keyword->start, .end = end @@ -7507,7 +7573,7 @@ pm_unless_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t static pm_unless_node_t * pm_unless_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_token_t *unless_keyword, pm_node_t *predicate) { pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); - pm_unless_node_t *node = PM_ALLOC_NODE(parser, pm_unless_node_t); + pm_unless_node_t *node = PM_NODE_ALLOC(parser, pm_unless_node_t); pm_statements_node_t *statements = pm_statements_node_create(parser); pm_statements_node_body_append(parser, statements, statement); @@ -7516,6 +7582,7 @@ pm_unless_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const { .type = PM_UNLESS_NODE, .flags = PM_NODE_FLAG_NEWLINE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = statement->location.start, .end = predicate->location.end @@ -7566,13 +7633,14 @@ pm_loop_modifier_block_exits(pm_parser_t *parser, pm_statements_node_t *statemen */ static pm_until_node_t * pm_until_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_token_t *closing, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) { - pm_until_node_t *node = PM_ALLOC_NODE(parser, pm_until_node_t); + pm_until_node_t *node = PM_NODE_ALLOC(parser, pm_until_node_t); pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); *node = (pm_until_node_t) { { .type = PM_UNTIL_NODE, .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = keyword->start, .end = closing->end, @@ -7592,7 +7660,7 @@ pm_until_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_to */ static pm_until_node_t * pm_until_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) { - pm_until_node_t *node = PM_ALLOC_NODE(parser, pm_until_node_t); + pm_until_node_t *node = PM_NODE_ALLOC(parser, pm_until_node_t); pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); pm_loop_modifier_block_exits(parser, statements); @@ -7600,6 +7668,7 @@ pm_until_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm { .type = PM_UNTIL_NODE, .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = statements->base.location.start, .end = predicate->location.end, @@ -7619,11 +7688,12 @@ pm_until_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm */ static pm_when_node_t * pm_when_node_create(pm_parser_t *parser, const pm_token_t *keyword) { - pm_when_node_t *node = PM_ALLOC_NODE(parser, pm_when_node_t); + pm_when_node_t *node = PM_NODE_ALLOC(parser, pm_when_node_t); *node = (pm_when_node_t) { { .type = PM_WHEN_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = keyword->start, .end = NULL @@ -7673,13 +7743,14 @@ pm_when_node_statements_set(pm_when_node_t *node, pm_statements_node_t *statemen */ static pm_while_node_t * pm_while_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_token_t *closing, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) { - pm_while_node_t *node = PM_ALLOC_NODE(parser, pm_while_node_t); + pm_while_node_t *node = PM_NODE_ALLOC(parser, pm_while_node_t); pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); *node = (pm_while_node_t) { { .type = PM_WHILE_NODE, .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = keyword->start, .end = closing->end @@ -7699,7 +7770,7 @@ pm_while_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_to */ static pm_while_node_t * pm_while_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) { - pm_while_node_t *node = PM_ALLOC_NODE(parser, pm_while_node_t); + pm_while_node_t *node = PM_NODE_ALLOC(parser, pm_while_node_t); pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); pm_loop_modifier_block_exits(parser, statements); @@ -7707,6 +7778,7 @@ pm_while_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm { .type = PM_WHILE_NODE, .flags = flags, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = statements->base.location.start, .end = predicate->location.end @@ -7726,11 +7798,12 @@ pm_while_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm */ static pm_while_node_t * pm_while_node_synthesized_create(pm_parser_t *parser, pm_node_t *predicate, pm_statements_node_t *statements) { - pm_while_node_t *node = PM_ALLOC_NODE(parser, pm_while_node_t); + pm_while_node_t *node = PM_NODE_ALLOC(parser, pm_while_node_t); *node = (pm_while_node_t) { { .type = PM_WHILE_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = PM_LOCATION_NULL_VALUE(parser) }, .keyword_loc = PM_LOCATION_NULL_VALUE(parser), @@ -7748,12 +7821,13 @@ pm_while_node_synthesized_create(pm_parser_t *parser, pm_node_t *predicate, pm_s */ static pm_x_string_node_t * pm_xstring_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing, const pm_string_t *unescaped) { - pm_x_string_node_t *node = PM_ALLOC_NODE(parser, pm_x_string_node_t); + pm_x_string_node_t *node = PM_NODE_ALLOC(parser, pm_x_string_node_t); *node = (pm_x_string_node_t) { { .type = PM_X_STRING_NODE, .flags = PM_STRING_FLAGS_FROZEN, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = opening->start, .end = closing->end @@ -7781,7 +7855,7 @@ pm_xstring_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_ */ static pm_yield_node_t * pm_yield_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_location_t *lparen_loc, pm_arguments_node_t *arguments, const pm_location_t *rparen_loc) { - pm_yield_node_t *node = PM_ALLOC_NODE(parser, pm_yield_node_t); + pm_yield_node_t *node = PM_NODE_ALLOC(parser, pm_yield_node_t); const uint8_t *end; if (rparen_loc->start != NULL) { @@ -7797,6 +7871,7 @@ pm_yield_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_lo *node = (pm_yield_node_t) { { .type = PM_YIELD_NODE, + .node_id = PM_NODE_IDENTIFY(parser), .location = { .start = keyword->start, .end = end @@ -7811,7 +7886,8 @@ pm_yield_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_lo return node; } -#undef PM_ALLOC_NODE +#undef PM_NODE_ALLOC +#undef PM_NODE_IDENTIFY /** * Check if any of the currently visible scopes contain a local variable @@ -13508,7 +13584,7 @@ parse_unwriteable_write(pm_parser_t *parser, pm_node_t *target, const pm_token_t default: break; } - pm_constant_id_t name = pm_parser_constant_id_location(parser, target->location.start, target->location.end); + pm_constant_id_t name = pm_parser_local_add_location(parser, target->location.start, target->location.end, 1); pm_local_variable_write_node_t *result = pm_local_variable_write_node_create(parser, name, 0, value, &target->location, equals); pm_node_destroy(parser, target); @@ -17168,6 +17244,10 @@ pm_parser_err_prefix(pm_parser_t *parser, pm_diagnostic_id_t diag_id) { PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, diag_id, human, parser->previous.start[0]); break; } + case PM_ERR_UNARY_DISALLOWED: { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, diag_id, pm_token_type_human(parser->current.type)); + break; + } default: pm_parser_err_previous(parser, diag_id); break; @@ -19717,6 +19797,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } } case PM_TOKEN_BANG: { + if (binding_power > PM_BINDING_POWER_UNARY) { + pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED); + } + parser_lex(parser); pm_token_t operator = parser->previous; @@ -19727,6 +19811,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b return (pm_node_t *) node; } case PM_TOKEN_TILDE: { + if (binding_power > PM_BINDING_POWER_UNARY) { + pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED); + } parser_lex(parser); pm_token_t operator = parser->previous; @@ -19736,6 +19823,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b return (pm_node_t *) node; } case PM_TOKEN_UMINUS: { + if (binding_power > PM_BINDING_POWER_UNARY) { + pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED); + } parser_lex(parser); pm_token_t operator = parser->previous; @@ -19852,6 +19942,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b return (pm_node_t *) pm_lambda_node_create(parser, &locals, &operator, &opening, &parser->previous, parameters, body); } case PM_TOKEN_UPLUS: { + if (binding_power > PM_BINDING_POWER_UNARY) { + pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED); + } parser_lex(parser); pm_token_t operator = parser->previous; @@ -21311,6 +21404,7 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm assert(source != NULL); *parser = (pm_parser_t) { + .node_id = 0, .lex_state = PM_LEX_STATE_BEG, .enclosure_nesting = 0, .lambda_enclosure_nesting = -1, @@ -21602,18 +21696,21 @@ pm_parse_stream_read(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t #define LINE_SIZE 4096 char line[LINE_SIZE]; - while (fgets(line, LINE_SIZE, stream) != NULL) { - size_t length = strlen(line); + while (memset(line, '\n', LINE_SIZE), fgets(line, LINE_SIZE, stream) != NULL) { + size_t length = LINE_SIZE; + while (length > 0 && line[length - 1] == '\n') length--; - if (length == LINE_SIZE && line[length - 1] != '\n') { + if (length == LINE_SIZE) { // If we read a line that is the maximum size and it doesn't end // with a newline, then we'll just append it to the buffer and // continue reading. + length--; pm_buffer_append_string(buffer, line, length); continue; } // Append the line to the buffer. + length--; pm_buffer_append_string(buffer, line, length); // Check if the line matches the __END__ marker. If it does, then stop diff --git a/prism/templates/ext/prism/api_node.c.erb b/prism/templates/ext/prism/api_node.c.erb index de6f4b95fd499d..03615b0ae2453b 100644 --- a/prism/templates/ext/prism/api_node.c.erb +++ b/prism/templates/ext/prism/api_node.c.erb @@ -170,14 +170,23 @@ pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encodi <%- nodes.each do |node| -%> #line <%= __LINE__ + 1 %> "prism/templates/ext/prism/<%= File.basename(__FILE__) %>" case <%= node.type %>: { - <%- if node.fields.any? { |field| ![Prism::Template::NodeField, Prism::Template::OptionalNodeField, Prism::Template::FlagsField].include?(field.class) } -%> + <%- if node.fields.any? { |field| ![Prism::Template::NodeField, Prism::Template::OptionalNodeField].include?(field.class) } -%> pm_<%= node.human %>_t *cast = (pm_<%= node.human %>_t *) node; <%- end -%> - VALUE argv[<%= node.fields.length + 2 %>]; + VALUE argv[<%= node.fields.length + 4 %>]; // source argv[0] = source; - <%- node.fields.each.with_index(1) do |field, index| -%> + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end); + + // flags + argv[3] = ULONG2NUM(node->flags); + <%- node.fields.each.with_index(4) do |field, index| -%> // <%= field.name %> <%- case field -%> @@ -218,9 +227,6 @@ pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encodi <%- when Prism::Template::UInt32Field -%> #line <%= __LINE__ + 1 %> "prism/templates/ext/prism/<%= File.basename(__FILE__) %>" argv[<%= index %>] = ULONG2NUM(cast-><%= field.name %>); - <%- when Prism::Template::FlagsField -%> -#line <%= __LINE__ + 1 %> "prism/templates/ext/prism/<%= File.basename(__FILE__) %>" - argv[<%= index %>] = ULONG2NUM(node->flags & ~PM_NODE_FLAG_COMMON_MASK); <%- when Prism::Template::IntegerField -%> #line <%= __LINE__ + 1 %> "prism/templates/ext/prism/<%= File.basename(__FILE__) %>" argv[<%= index %>] = pm_integer_new(&cast-><%= field.name %>); @@ -232,10 +238,7 @@ pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encodi <%- end -%> <%- end -%> - // location - argv[<%= node.fields.length + 1 %>] = pm_location_new(parser, node->location.start, node->location.end); - - rb_ary_push(value_stack, rb_class_new_instance(<%= node.fields.length + 2 %>, argv, rb_cPrism<%= node.name %>)); + rb_ary_push(value_stack, rb_class_new_instance(<%= node.fields.length + 4 %>, argv, rb_cPrism<%= node.name %>)); break; } <%- end -%> diff --git a/prism/templates/include/prism/ast.h.erb b/prism/templates/include/prism/ast.h.erb index 0fe7905e404bd5..54da751bd51666 100644 --- a/prism/templates/include/prism/ast.h.erb +++ b/prism/templates/include/prism/ast.h.erb @@ -100,11 +100,8 @@ typedef uint16_t pm_node_flags_t; * We store the flags enum in every node in the tree. Some flags are common to * all nodes (the ones listed below). Others are specific to certain node types. */ -#define PM_NODE_FLAG_BITS (sizeof(pm_node_flags_t) * 8) - -static const pm_node_flags_t PM_NODE_FLAG_NEWLINE = (1 << (PM_NODE_FLAG_BITS - 1)); -static const pm_node_flags_t PM_NODE_FLAG_STATIC_LITERAL = (1 << (PM_NODE_FLAG_BITS - 2)); -static const pm_node_flags_t PM_NODE_FLAG_COMMON_MASK = (1 << (PM_NODE_FLAG_BITS - 1)) | (1 << (PM_NODE_FLAG_BITS - 2)); +static const pm_node_flags_t PM_NODE_FLAG_NEWLINE = 0x1; +static const pm_node_flags_t PM_NODE_FLAG_STATIC_LITERAL = 0x2; /** * Cast the type to an enum to allow the compiler to provide exhaustiveness @@ -139,6 +136,12 @@ typedef struct pm_node { */ pm_node_flags_t flags; + /** + * The unique identifier for this node, which is deterministic based on the + * source. It is used to identify unique nodes across parses. + */ + uint32_t node_id; + /** * This is the location of the node in the source. It's a range of bytes * containing a start and an end. @@ -151,11 +154,10 @@ typedef struct pm_node { * <%= node.name %> * * Type: <%= node.type %> -<%- if (node_flags = node.fields.find { |field| field.is_a? Prism::Template::FlagsField }) -%> +<%- if (node_flags = node.flags) -%> * Flags: -<%- found = flags.find { |flag| flag.name == node_flags.kind }.tap { |found| raise "Expected to find #{field.kind}" unless found } -%> -<%- found.values.each do |value| -%> - * PM_<%= found.human.upcase %>_<%= value.name %> +<%- node_flags.values.each do |value| -%> + * PM_<%= node_flags.human.upcase %>_<%= value.name %> <%- end -%> <%- end -%> * @@ -164,7 +166,7 @@ typedef struct pm_node { typedef struct pm_<%= node.human %> { /** The embedded base node. */ pm_node_t base; -<%- node.fields.grep_v(Prism::Template::FlagsField).each do |field| -%> +<%- node.fields.each do |field| -%> /** * <%= node.name %>#<%= field.name %> @@ -201,7 +203,7 @@ typedef enum pm_<%= flag.human %> { <%- flag.values.each_with_index do |value, index| -%> <%= "\n" if index > 0 -%> /** <%= value.comment %> */ - PM_<%= flag.human.upcase %>_<%= value.name %> = <%= 1 << index %>, + PM_<%= flag.human.upcase %>_<%= value.name %> = <%= 1 << (index + 2) %>, <%- end -%> } pm_<%= flag.human %>_t; <%- end -%> diff --git a/prism/templates/lib/prism/dispatcher.rb.erb b/prism/templates/lib/prism/dispatcher.rb.erb index 1370ca76364231..0db000346425ec 100644 --- a/prism/templates/lib/prism/dispatcher.rb.erb +++ b/prism/templates/lib/prism/dispatcher.rb.erb @@ -14,7 +14,8 @@ module Prism # end # end # - # dispatcher = Dispatcher.new + # listener = OctalListener.new + # dispatcher = Prism::Dispatcher.new # dispatcher.register(listener, :on_integer_node_enter) # # Then, you can walk any number of trees and dispatch events to the listeners: diff --git a/prism/templates/lib/prism/dot_visitor.rb.erb b/prism/templates/lib/prism/dot_visitor.rb.erb index 93c94b19eedd30..e9c81e454502de 100644 --- a/prism/templates/lib/prism/dot_visitor.rb.erb +++ b/prism/templates/lib/prism/dot_visitor.rb.erb @@ -109,6 +109,11 @@ module Prism def visit_<%= node.human %>(node) table = Table.new("<%= node.name %>") id = node_id(node) + <%- if (node_flags = node.flags) -%> + + # flags + table.field("flags", <%= node_flags.human %>_inspect(node)) + <%- end -%> <%- node.fields.each do |field| -%> # <%= field.name %> @@ -141,9 +146,6 @@ module Prism unless (<%= field.name %> = node.<%= field.name %>).nil? table.field("<%= field.name %>", location_inspect(<%= field.name %>)) end - <%- when Prism::Template::FlagsField -%> - <%- flag = flags.find { |flag| flag.name == field.kind }.tap { |flag| raise "Expected to find #{field.kind}" unless flag } -%> - table.field("<%= field.name %>", <%= flag.human %>_inspect(node)) <%- else -%> <%- raise -%> <%- end -%> diff --git a/prism/templates/lib/prism/dsl.rb.erb b/prism/templates/lib/prism/dsl.rb.erb index eff0d1c4fcfdd4..f0dac2a4f0daec 100644 --- a/prism/templates/lib/prism/dsl.rb.erb +++ b/prism/templates/lib/prism/dsl.rb.erb @@ -5,45 +5,129 @@ module Prism # source = Prism::Source.for("[1]") # # Prism::ArrayNode.new( + # source, + # 0, + # Prism::Location.new(source, 0, 3), + # 0, # [ # Prism::IntegerNode.new( - # Prism::IntegerBaseFlags::DECIMAL, - # 1, + # source, + # 0, # Prism::Location.new(source, 1, 1), - # source + # Prism::IntegerBaseFlags::DECIMAL, + # 1 # ) # ], # Prism::Location.new(source, 0, 1), - # Prism::Location.new(source, 2, 1), - # source + # Prism::Location.new(source, 2, 1) # ) # # you could instead write: # - # source = Prism::Source.for("[1]") + # class Builder + # include Prism::DSL # - # ArrayNode( - # IntegerNode(Prism::IntegerBaseFlags::DECIMAL, 1, Location(source, 1, 1)), source), - # Location(source, 0, 1), - # Location(source, 2, 1), - # source - # ) + # attr_reader :default_source + # + # def initialize + # @default_source = source("[1]") + # end # - # This is mostly helpful in the context of writing tests, but can also be used - # to generate trees programmatically. + # def build + # array_node( + # location: location(start_offset: 0, length: 3), + # elements: [ + # integer_node( + # location: location(start_offset: 1, length: 1), + # flags: integer_base_flag(:decimal), + # value: 1 + # ) + # ], + # opening_loc: location(start_offset: 0, length: 1), + # closing_loc: location(start_offset: 2, length: 1) + # ) + # end + # end + # + # This is mostly helpful in the context of generating trees programmatically. module DSL - private + # Provide all of these methods as module methods as well, to allow for + # building nodes like Prism::DSL.nil_node. + extend self - # Create a new Location object - def Location(source = nil, start_offset = 0, length = 0) - Location.new(source, start_offset, length) # steep:ignore + # Create a new Source object. + def source(string) + Source.for(string) + end + + # Create a new Location object. + def location(source: default_source, start_offset: 0, length: 0) + Location.new(source, start_offset, length) end <%- nodes.each do |node| -%> - # Create a new <%= node.name %> node - def <%= node.name %>(<%= (node.fields.map(&:name) + ["source = nil, location = Location()"]).join(", ") %>) - <%= node.name %>.new(<%= ["source", *node.fields.map(&:name), "location"].join(", ") %>) + # Create a new <%= node.name %> node. + def <%= node.human %>(<%= ["source: default_source", "node_id: 0", "location: default_location", "flags: 0", *node.fields.map { |field| + case field + when Prism::Template::NodeField + if !field.kind? + "#{field.name}: default_node(source, location)" + else + kind = field.specific_kind || field.union_kind.first + "#{field.name}: #{kind.gsub(/(?<=.)[A-Z]/, "_\\0").downcase}(source: source)" + end + when Prism::Template::ConstantField + "#{field.name}: :\"\"" + when Prism::Template::OptionalNodeField, Prism::Template::OptionalConstantField, Prism::Template::OptionalLocationField + "#{field.name}: nil" + when Prism::Template::NodeListField, Prism::Template::ConstantListField + "#{field.name}: []" + when Prism::Template::StringField + "#{field.name}: \"\"" + when Prism::Template::LocationField + "#{field.name}: location" + when Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::IntegerField + "#{field.name}: 0" + when Prism::Template::DoubleField + "#{field.name}: 0.0" + else + raise + end + }].join(", ") %>) + <%= node.name %>.new(<%= ["source", "node_id", "location", "flags", *node.fields.map(&:name)].join(", ") %>) end <%- end -%> + <%- flags.each do |flag| -%> + + # Retrieve the value of one of the <%= flag.name %> flags. + def <%= flag.human.chomp("s") %>(name) + case name + <%- flag.values.each do |value| -%> + when :<%= value.name.downcase %> then <%= flag.name %>::<%= value.name %> + <%- end -%> + else Kernel.raise ArgumentError, "invalid <%= flag.name %> flag: #{name.inspect}" + end + end + <%- end -%> + + private + + # The default source object that gets attached to nodes and locations if no + # source is specified. + def default_source + Source.for("") + end + + # The default location object that gets attached to nodes if no location is + # specified, which uses the given source. + def default_location + Location.new(default_source, 0, 0) + end + + # The default node that gets attached to nodes if no node is specified for a + # required node field. + def default_node(source, location) + MissingNode.new(source, -1, location, 0) + end end end diff --git a/prism/templates/lib/prism/inspect_visitor.rb.erb b/prism/templates/lib/prism/inspect_visitor.rb.erb index 9328da636b0739..3cfe615d850934 100644 --- a/prism/templates/lib/prism/inspect_visitor.rb.erb +++ b/prism/templates/lib/prism/inspect_visitor.rb.erb @@ -69,10 +69,13 @@ module Prism # 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 ? " " : "│ " -%> + <%- (fields = [node.flags || Prism::Template::Flags.empty, *node.fields]).each_with_index do |field, index| -%> + <%- pointer = index == fields.length - 1 ? "└── " : "├── " -%> + <%- preadd = index == fields.length - 1 ? " " : "│ " -%> <%- case field -%> + <%- when Prism::Template::Flags -%> + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), <%= field.values.map { |value| "(\"#{value.name.downcase}\" if node.#{value.name.downcase}?)" }.join(", ") %>].compact + commands << ["<%= pointer %>flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] <%- when Prism::Template::NodeListField -%> commands << ["<%= pointer %><%= field.name %>: (length: #{(<%= field.name %> = node.<%= field.name %>).length})\n", indent] if <%= field.name %>.any? @@ -101,10 +104,6 @@ module Prism 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 -%> diff --git a/prism/templates/lib/prism/node.rb.erb b/prism/templates/lib/prism/node.rb.erb index f0ce226defcff9..1cd0ce0ba078a8 100644 --- a/prism/templates/lib/prism/node.rb.erb +++ b/prism/templates/lib/prism/node.rb.erb @@ -6,6 +6,12 @@ module Prism attr_reader :source private :source + # A unique identifier for this node. This is used in a very specific + # use case where you want to keep around a reference to a node without + # having to keep around the syntax tree in memory. This unique identifier + # will be consistent across multiple parses of the same source code. + attr_reader :node_id + # A Location instance that represents the location of this node in the # source. def location @@ -49,6 +55,21 @@ module Prism location.slice_lines end + # An bitset of flags for this node. There are certain flags that are common + # for all nodes, and then some nodes have specific flags. + attr_reader :flags + protected :flags + + # Returns true if the node has the newline flag set. + def newline? + flags.anybits?(NodeFlags::NEWLINE) + end + + # Returns true if the node has the static literal flag set. + def static_literal? + flags.anybits?(NodeFlags::STATIC_LITERAL) + end + # Similar to inspect, but respects the current level of indentation given by # the pretty print object. def pretty_print(q) @@ -101,6 +122,23 @@ module Prism result end + # Returns the first node that matches the given block when visited in a + # depth-first search. This is useful for finding a node that matches a + # particular condition. + # + # node.breadth_first_search { |node| node.node_id == node_id } + # + def breadth_first_search(&block) + queue = [self] #: Array[Prism::node] + + while (node = queue.shift) + return node if yield node + queue.concat(node.compact_child_nodes) + end + + nil + 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. @@ -144,18 +182,31 @@ module Prism raise NoMethodError, "undefined method `comment_targets' for #{inspect}" end - # Returns a symbol symbolizing the type of node that this represents. This - # is particularly useful for case statements and array comparisons. - def type - raise NoMethodError, "undefined method `type' for #{inspect}" - end - # Returns a string representation of the node. def inspect raise NoMethodError, "undefined method `inspect' for #{inspect}" end - # Returns the type of the node as a symbol. + # Sometimes you want to check an instance of a node against a list of + # classes to see what kind of behavior to perform. Usually this is done by + # calling `[cls1, cls2].include?(node.class)` or putting the node into a + # case statement and doing `case node; when cls1; when cls2; end`. Both of + # these approaches are relatively slow because of the constant lookups, + # method calls, and/or array allocations. + # + # Instead, you can call #type, which will return to you a symbol that you + # can use for comparison. This is faster than the other approaches because + # it uses a single integer comparison, but also because if you're on CRuby + # you can take advantage of the fact that case statements with all symbol + # keys will use a jump table. + def type + raise NoMethodError, "undefined method `type' for #{inspect}" + end + + # Similar to #type, this method returns a symbol that you can use for + # splitting on the type of the node without having to do a long === chain. + # Note that like #type, it will still be slower than using == for a single + # class, but should be faster in a case statement or an array comparison. def self.type raise NoMethodError, "undefined method `type' for #{inspect}" end @@ -166,10 +217,12 @@ module Prism #<%= line %> <%- end -%> class <%= node.name -%> < Node - # def initialize: (<%= (node.fields.map { |field| "#{field.rbs_class} #{field.name}" } + ["Location location"]).join(", ") %>) -> void - def initialize(source, <%= (node.fields.map(&:name) + ["location"]).join(", ") %>) + # Initialize a new <%= node.name %> node. + def initialize(<%= ["source", "node_id", "location", "flags", *node.fields.map(&:name)].join(", ") %>) @source = source + @node_id = node_id @location = location + @flags = flags <%- node.fields.each do |field| -%> <%- if Prism::Template::CHECK_FIELD_KIND && field.respond_to?(:check_field_kind) -%> raise <%= field.name %>.inspect unless <%= field.check_field_kind %> @@ -228,22 +281,31 @@ module Prism }.compact.join(", ") %>] #: Array[Prism::node | Location] end - # def copy: (<%= (node.fields.map { |field| "?#{field.name}: #{field.rbs_class}" } + ["?location: Location"]).join(", ") %>) -> <%= node.name %> - def copy(<%= (node.fields.map(&:name) + ["location"]).map { |field| "#{field}: self.#{field}" }.join(", ") %>) - <%= node.name %>.new(<%= ["source", *node.fields.map(&:name), "location"].join(", ") %>) + # def copy: (<%= (["?node_id: Integer", "?location: Location", "?flags: Integer"] + node.fields.map { |field| "?#{field.name}: #{field.rbs_class}" }).join(", ") %>) -> <%= node.name %> + def copy(<%= (["node_id", "location", "flags"] + node.fields.map(&:name)).map { |field| "#{field}: self.#{field}" }.join(", ") %>) + <%= node.name %>.new(<%= ["source", "node_id", "location", "flags", *node.fields.map(&:name)].join(", ") %>) end # def deconstruct: () -> Array[nil | Node] alias deconstruct child_nodes - # def deconstruct_keys: (Array[Symbol] keys) -> { <%= (node.fields.map { |field| "#{field.name}: #{field.rbs_class}" } + ["location: Location"]).join(", ") %> } + # def deconstruct_keys: (Array[Symbol] keys) -> { <%= (["node_id: Integer", "location: Location"] + node.fields.map { |field| "#{field.name}: #{field.rbs_class}" }).join(", ") %> } def deconstruct_keys(keys) - { <%= (node.fields.map { |field| "#{field.name}: #{field.name}" } + ["location: location"]).join(", ") %> } + { <%= (["node_id: node_id", "location: location"] + node.fields.map { |field| "#{field.name}: #{field.name}" }).join(", ") %> } end + <%- if (node_flags = node.flags) -%> + <%- node_flags.values.each do |value| -%> + + # def <%= value.name.downcase %>?: () -> bool + def <%= value.name.downcase %>? + flags.anybits?(<%= node_flags.name %>::<%= value.name %>) + end + <%- end -%> + <%- end -%> <%- node.fields.each do |field| -%> <%- if field.comment.nil? -%> - # <%= "protected " if field.is_a?(Prism::Template::FlagsField) %>attr_reader <%= field.name %>: <%= field.rbs_class %> + # attr_reader <%= field.name %>: <%= field.rbs_class %> <%- else -%> <%- field.each_comment_line do |line| -%> #<%= line %> @@ -269,7 +331,7 @@ module Prism end end <%- else -%> - attr_reader :<%= field.name -%><%= "\n protected :#{field.name}" if field.is_a?(Prism::Template::FlagsField) %> + attr_reader :<%= field.name -%> <%- end -%> <%- end -%> <%- node.fields.each do |field| -%> @@ -290,14 +352,6 @@ module Prism def <%= field.name.delete_suffix("_loc") %> <%= field.name %>&.slice end - <%- when Prism::Template::FlagsField -%> - <%- flags.find { |flag| flag.name == field.kind }.tap { |flag| raise "Expected to find #{field.kind}" unless flag }.values.each do |value| -%> - - # def <%= value.name.downcase %>?: () -> bool - def <%= value.name.downcase %>? - <%= field.name %>.anybits?(<%= field.kind %>::<%= value.name %>) - end - <%- end -%> <%- end -%> <%- end -%> @@ -306,30 +360,12 @@ module Prism InspectVisitor.compose(self) end - # Sometimes you want to check an instance of a node against a list of - # classes to see what kind of behavior to perform. Usually this is done by - # calling `[cls1, cls2].include?(node.class)` or putting the node into a - # case statement and doing `case node; when cls1; when cls2; end`. Both of - # these approaches are relatively slow because of the constant lookups, - # method calls, and/or array allocations. - # - # Instead, you can call #type, which will return to you a symbol that you - # can use for comparison. This is faster than the other approaches because - # it uses a single integer comparison, but also because if you're on CRuby - # you can take advantage of the fact that case statements with all symbol - # keys will use a jump table. - # - # def type: () -> Symbol + # Return a symbol representation of this node type. See `Node#type`. def type :<%= node.human %> end - # Similar to #type, this method returns a symbol that you can use for - # splitting on the type of the node without having to do a long === chain. - # Note that like #type, it will still be slower than using == for a single - # class, but should be faster in a case statement or an array comparison. - # - # def self.type: () -> Symbol + # Return a symbol representation of this node type. See `Node::type`. def self.type :<%= node.human %> end @@ -337,15 +373,17 @@ module Prism # 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| -%> + other.is_a?(<%= node.name %>)<%= " &&" if (fields = [*node.flags, *node.fields]).any? %> + <%- 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 %> + (<%= field.name %>.nil? == other.<%= field.name %>.nil?)<%= " &&" if index != 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 %> + <%= field.name %>.zip(other.<%= field.name %>).all? { |left, right| left === right }<%= " &&" if index != fields.length - 1 %> + <%- elsif field.is_a?(Prism::Template::Flags) -%> + (flags === other.flags)<%= " &&" if index != fields.length - 1 %> <%- else -%> - (<%= field.name %> === other.<%= field.name %>)<%= " &&" if index != node.fields.length - 1 %> + (<%= field.name %> === other.<%= field.name %>)<%= " &&" if index != fields.length - 1 %> <%- end -%> <%- end -%> end @@ -357,9 +395,20 @@ module Prism module <%= flag.name %> <%- flag.values.each_with_index do |value, index| -%> # <%= value.comment %> - <%= value.name %> = 1 << <%= index %> + <%= value.name %> = 1 << <%= (index + 2) %> <%= "\n" if value != flag.values.last -%> <%- end -%> end <%- end -%> + + # The flags that are common to all nodes. + module NodeFlags + # A flag to indicate that the node is a candidate to emit a :line event + # through tracepoint when compiled. + NEWLINE = 1 + + # A flag to indicate that the value that the node represents is a value that + # can be determined at parse-time. + STATIC_LITERAL = 2 + end end diff --git a/prism/templates/lib/prism/reflection.rb.erb b/prism/templates/lib/prism/reflection.rb.erb index 3c1d61c6c1ac91..6c8b2f4d257714 100644 --- a/prism/templates/lib/prism/reflection.rb.erb +++ b/prism/templates/lib/prism/reflection.rb.erb @@ -97,7 +97,7 @@ module Prism case node.type <%- nodes.each do |node| -%> when :<%= node.human %> - [<%= node.fields.map { |field| + [<%= [*node.flags, *node.fields].map { |field| case field when Prism::Template::NodeField "NodeField.new(:#{field.name})" @@ -121,9 +121,8 @@ module Prism "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::Flags + "FlagsField.new(:flags, [#{field.values.map { |value| ":#{value.name.downcase}?" }.join(", ")}])" 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 19116b9e6b49b0..7469f3683d17ca 100644 --- a/prism/templates/lib/prism/serialize.rb.erb +++ b/prism/templates/lib/prism/serialize.rb.erb @@ -131,8 +131,8 @@ module Prism comments = load_comments magic_comments = Array.new(load_varuint) { MagicComment.new(load_location_object, load_location_object) } data_loc = load_optional_location_object - errors = Array.new(load_varuint) { ParseError.new(DIAGNOSTIC_TYPES[load_varuint], load_embedded_string, load_location_object, load_error_level) } - warnings = Array.new(load_varuint) { ParseWarning.new(DIAGNOSTIC_TYPES[load_varuint], load_embedded_string, load_location_object, load_warning_level) } + errors = Array.new(load_varuint) { ParseError.new(DIAGNOSTIC_TYPES.fetch(load_varuint), load_embedded_string, load_location_object, load_error_level) } + warnings = Array.new(load_varuint) { ParseWarning.new(DIAGNOSTIC_TYPES.fetch(load_varuint), load_embedded_string, load_location_object, load_warning_level) } [comments, magic_comments, data_loc, errors, warnings] end @@ -319,9 +319,10 @@ module Prism end end - if RUBY_ENGINE == 'ruby' + if RUBY_ENGINE == "ruby" def load_node type = io.getbyte + node_id = load_varuint location = load_location case type @@ -330,8 +331,7 @@ module Prism <%- if node.needs_serialized_length? -%> load_uint32 <%- end -%> - <%= node.name %>.new( - source, <%= (node.fields.map { |field| + <%= node.name %>.new(<%= ["source", "node_id", "location", "load_varuint", *node.fields.map { |field| case field when Prism::Template::NodeField then "load_node" when Prism::Template::OptionalNodeField then "load_optional_node" @@ -343,12 +343,12 @@ module Prism when Prism::Template::LocationField then "load_location" when Prism::Template::OptionalLocationField then "load_optional_location" when Prism::Template::UInt8Field then "io.getbyte" - when Prism::Template::UInt32Field, Prism::Template::FlagsField then "load_varuint" + when Prism::Template::UInt32Field then "load_varuint" when Prism::Template::IntegerField then "load_integer" when Prism::Template::DoubleField then "load_double" else raise end - } + ["location"]).join(", ") -%>) + }].join(", ") -%>) <%- end -%> end end @@ -363,12 +363,12 @@ module Prism nil, <%- nodes.each do |node| -%> -> { + node_id = load_varuint location = load_location <%- if node.needs_serialized_length? -%> load_uint32 <%- end -%> - <%= node.name %>.new( - source, <%= (node.fields.map { |field| + <%= node.name %>.new(<%= ["source", "node_id", "location", "load_varuint", *node.fields.map { |field| case field when Prism::Template::NodeField then "load_node" when Prism::Template::OptionalNodeField then "load_optional_node" @@ -380,12 +380,12 @@ module Prism when Prism::Template::LocationField then "load_location" when Prism::Template::OptionalLocationField then "load_optional_location" when Prism::Template::UInt8Field then "io.getbyte" - when Prism::Template::UInt32Field, Prism::Template::FlagsField then "load_varuint" + when Prism::Template::UInt32Field then "load_varuint" when Prism::Template::IntegerField then "load_integer" when Prism::Template::DoubleField then "load_double" else raise end - } + ["location"]).join(", ") -%>) + }].join(", ") -%>) }, <%- end -%> ] diff --git a/prism/templates/src/diagnostic.c.erb b/prism/templates/src/diagnostic.c.erb index b09aca1c233b71..064063d9f46b15 100644 --- a/prism/templates/src/diagnostic.c.erb +++ b/prism/templates/src/diagnostic.c.erb @@ -346,6 +346,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [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_UNARY_DISALLOWED] = { "unexpected %s; unary calls are not allowed in this context", 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 assignment; blocks are not allowed in index assignment expressions", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_UNEXPECTED_INDEX_KEYWORDS] = { "unexpected keyword arg given in index assignment; keywords are not allowed in index assignment expressions", PM_ERROR_LEVEL_SYNTAX }, diff --git a/prism/templates/src/node.c.erb b/prism/templates/src/node.c.erb index da30a96ec433d6..2357e552000bc3 100644 --- a/prism/templates/src/node.c.erb +++ b/prism/templates/src/node.c.erb @@ -108,12 +108,12 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { <%- nodes.each do |node| -%> #line <%= __LINE__ + 1 %> "prism/templates/src/<%= File.basename(__FILE__) %>" case <%= node.type %>: { - <%- if node.fields.any? { |field| ![Prism::Template::LocationField, Prism::Template::OptionalLocationField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::FlagsField, Prism::Template::ConstantField, Prism::Template::OptionalConstantField, Prism::Template::DoubleField].include?(field.class) } -%> + <%- if node.fields.any? { |field| ![Prism::Template::LocationField, Prism::Template::OptionalLocationField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::ConstantField, Prism::Template::OptionalConstantField, Prism::Template::DoubleField].include?(field.class) } -%> pm_<%= node.human %>_t *cast = (pm_<%= node.human %>_t *) node; <%- end -%> <%- node.fields.each do |field| -%> <%- case field -%> - <%- when Prism::Template::LocationField, Prism::Template::OptionalLocationField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::FlagsField, Prism::Template::ConstantField, Prism::Template::OptionalConstantField, Prism::Template::DoubleField -%> + <%- when Prism::Template::LocationField, Prism::Template::OptionalLocationField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::ConstantField, Prism::Template::OptionalConstantField, Prism::Template::DoubleField -%> <%- when Prism::Template::NodeField -%> pm_node_destroy(parser, (pm_node_t *)cast-><%= field.name %>); <%- when Prism::Template::OptionalNodeField -%> @@ -244,7 +244,7 @@ pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *no const pm_<%= node.human %>_t *cast = (const pm_<%= node.human %>_t *) node; pm_dump_json_location(buffer, parser, &cast->base.location); - <%- node.fields.each_with_index do |field, index| -%> + <%- [*node.flags, *node.fields].each_with_index do |field, index| -%> // Dump the <%= field.name %> field pm_buffer_append_byte(buffer, ','); @@ -301,12 +301,11 @@ pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *no pm_buffer_append_format(buffer, "%" PRIu8, cast-><%= field.name %>); <%- when Prism::Template::UInt32Field -%> pm_buffer_append_format(buffer, "%" PRIu32, cast-><%= field.name %>); - <%- when Prism::Template::FlagsField -%> + <%- when Prism::Template::Flags -%> size_t flags = 0; pm_buffer_append_byte(buffer, '['); - <%- found = flags.find { |flag| flag.name == field.kind }.tap { |found| raise "Expected to find #{field.kind}" unless found } -%> - <%- found.values.each_with_index do |value, index| -%> - if (PM_NODE_FLAG_P(cast, PM_<%= found.human.upcase %>_<%= value.name %>)) { + <%- node.flags.values.each_with_index do |value, index| -%> + if (PM_NODE_FLAG_P(cast, PM_<%= node.flags.human.upcase %>_<%= value.name %>)) { if (flags != 0) pm_buffer_append_byte(buffer, ','); pm_buffer_append_string(buffer, "\"<%= value.name %>\"", <%= value.name.bytesize + 2 %>); flags++; diff --git a/prism/templates/src/prettyprint.c.erb b/prism/templates/src/prettyprint.c.erb index 27f44cd996f68a..639c2fecf33ba3 100644 --- a/prism/templates/src/prettyprint.c.erb +++ b/prism/templates/src/prettyprint.c.erb @@ -31,14 +31,14 @@ prettyprint_node(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm return; <%- nodes.each do |node| -%> case <%= node.type %>: { - <%- if node.fields.any? -%> + <%- if !node.flags.nil? || node.fields.any? -%> pm_<%= node.human %>_t *cast = (pm_<%= node.human %>_t *) node; <%- end -%> pm_buffer_append_string(output_buffer, "@ <%= node.name %> (location: ", <%= node.name.length + 14 %>); prettyprint_location(output_buffer, parser, &node->location); pm_buffer_append_string(output_buffer, ")\n", 2); - <%- node.fields.each_with_index do |field, index| -%> - <%- preadd = index == node.fields.length - 1 ? " " : "| " -%> + <%- (fields = [*node.flags, *node.fields]).each_with_index do |field, index| -%> + <%- preadd = index == fields.length - 1 ? " " : "| " -%> // <%= field.name %> { @@ -123,11 +123,10 @@ prettyprint_node(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm pm_buffer_append_format(output_buffer, " %" PRIu8 "\n", cast-><%= field.name %>); <%- when Prism::Template::UInt32Field -%> pm_buffer_append_format(output_buffer, " %" PRIu32 "\n", cast-><%= field.name %>); - <%- when Prism::Template::FlagsField -%> + <%- when Prism::Template::Flags -%> bool found = false; - <%- found = flags.find { |flag| flag.name == field.kind }.tap { |found| raise "Expected to find #{field.kind}" unless found } -%> - <%- found.values.each do |value| -%> - if (cast->base.<%= field.name %> & PM_<%= found.human.upcase %>_<%= value.name %>) { + <%- field.values.each do |value| -%> + if (cast->base.flags & PM_<%= field.human.upcase %>_<%= value.name %>) { if (found) pm_buffer_append_byte(output_buffer, ','); pm_buffer_append_string(output_buffer, " <%= value.name.downcase %>", <%= value.name.bytesize + 1 %>); found = true; diff --git a/prism/templates/src/serialize.c.erb b/prism/templates/src/serialize.c.erb index dd4b3fa5f049ed..f62453e69989e4 100644 --- a/prism/templates/src/serialize.c.erb +++ b/prism/templates/src/serialize.c.erb @@ -74,6 +74,7 @@ pm_serialize_node(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) { size_t offset = buffer->length; + pm_buffer_append_varuint(buffer, node->node_id); pm_serialize_location(parser, &node->location, buffer); switch (PM_NODE_TYPE(node)) { @@ -89,6 +90,7 @@ pm_serialize_node(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) { size_t length_offset = buffer->length; pm_buffer_append_string(buffer, "\0\0\0\0", 4); /* consume 4 bytes, updated below */ <%- end -%> + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); <%- node.fields.each do |field| -%> <%- case field -%> <%- when Prism::Template::NodeField -%> @@ -132,8 +134,6 @@ pm_serialize_node(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) { pm_buffer_append_byte(buffer, ((pm_<%= node.human %>_t *)node)-><%= field.name %>); <%- when Prism::Template::UInt32Field -%> pm_buffer_append_varuint(buffer, ((pm_<%= node.human %>_t *)node)-><%= field.name %>); - <%- when Prism::Template::FlagsField -%> - pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK)); <%- when Prism::Template::IntegerField -%> pm_serialize_integer(&((pm_<%= node.human %>_t *)node)-><%= field.name %>, buffer); <%- when Prism::Template::DoubleField -%> diff --git a/prism/templates/template.rb b/prism/templates/template.rb index 7a330f5e27caa7..130e15d075b2a7 100755 --- a/prism/templates/template.rb +++ b/prism/templates/template.rb @@ -93,6 +93,10 @@ def should_be_serialized? # Some node fields can be specialized if they point to a specific kind of # node and not just a generic node. class NodeKindField < Field + def kind? + options.key?(:kind) + end + def c_type if specific_kind "pm_#{specific_kind.gsub(/(?<=.)[A-Z]/, "_\\0").downcase}" @@ -357,27 +361,6 @@ def java_type end end - # This represents a set of flags. It is very similar to the UInt32Field, but - # can be directly embedded into the flags field on the struct and provides - # convenient methods for checking if a flag is set. - class FlagsField < Field - def rbs_class - "Integer" - end - - def rbi_class - "Integer" - end - - def java_type - "short" - end - - def kind - options.fetch(:kind) - end - end - # This represents an arbitrarily-sized integer. When it gets to Ruby it will # be an Integer. class IntegerField < Field @@ -414,9 +397,9 @@ def java_type # in YAML format. It contains information about the name of the node and the # various child nodes it contains. class NodeType - attr_reader :name, :type, :human, :fields, :newline, :comment + attr_reader :name, :type, :human, :flags, :fields, :newline, :comment - def initialize(config) + def initialize(config, flags) @name = config.fetch("name") type = @name.gsub(/(?<=.)[A-Z]/, "_\\0") @@ -430,13 +413,14 @@ def initialize(config) options = field.transform_keys(&:to_sym) options.delete(:type) - # If/when we have documentation on every field, this should be changed - # to use fetch instead of delete. + # If/when we have documentation on every field, this should be + # changed to use fetch instead of delete. comment = options.delete(:comment) type.new(comment: comment, **options) end + @flags = config.key?("flags") ? flags.fetch(config.fetch("flags")) : nil @newline = config.fetch("newline", true) @comment = config.fetch("comment") end @@ -474,7 +458,6 @@ def field_type_for(name) when "location?" then OptionalLocationField when "uint8" then UInt8Field when "uint32" then UInt32Field - when "flags" then FlagsField when "integer" then IntegerField when "double" then DoubleField else raise("Unknown field type: #{name.inspect}") @@ -514,6 +497,10 @@ def initialize(config) @values = config.fetch("values").map { |flag| Flag.new(flag) } @comment = config.fetch("comment") end + + def self.empty + new("name" => "", "values" => [], "comment" => "") + end end class << self @@ -603,13 +590,14 @@ def locals @locals ||= begin config = YAML.load_file(File.expand_path("../config.yml", __dir__)) + flags = config.fetch("flags").to_h { |flags| [flags["name"], Flags.new(flags)] } { errors: config.fetch("errors").map { |name| Error.new(name) }, warnings: config.fetch("warnings").map { |name| Warning.new(name) }, - nodes: config.fetch("nodes").map { |node| NodeType.new(node) }.sort_by(&:name), + nodes: config.fetch("nodes").map { |node| NodeType.new(node, flags) }.sort_by(&:name), tokens: config.fetch("tokens").map { |token| Token.new(token) }, - flags: config.fetch("flags").map { |flags| Flags.new(flags) } + flags: flags.values } end end @@ -640,6 +628,7 @@ def locals "src/prettyprint.c", "src/serialize.c", "src/token_type.c", + "rbi/prism/dsl.rbi", "rbi/prism/node.rbi", "rbi/prism/visitor.rbi", "sig/prism.rbs", diff --git a/prism/util/pm_newline_list.c b/prism/util/pm_newline_list.c index ce07ce8c8e4644..8331618f54ff20 100644 --- a/prism/util/pm_newline_list.c +++ b/prism/util/pm_newline_list.c @@ -54,6 +54,35 @@ pm_newline_list_append(pm_newline_list_t *list, const uint8_t *cursor) { return true; } +/** + * Returns the line of the given offset. If the offset is not in the list, the + * line of the closest offset less than the given offset is returned. + */ +int32_t +pm_newline_list_line(const pm_newline_list_t *list, const uint8_t *cursor, int32_t start_line) { + assert(cursor >= list->start); + size_t offset = (size_t) (cursor - list->start); + + size_t left = 0; + size_t right = list->size - 1; + + while (left <= right) { + size_t mid = left + (right - left) / 2; + + if (list->offsets[mid] == offset) { + return ((int32_t) mid) + start_line; + } + + if (list->offsets[mid] < offset) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + return ((int32_t) left) + start_line - 1; +} + /** * Returns the line and column of the given offset. If the offset is not in the * list, the line and column of the closest offset less than the given offset diff --git a/prism/util/pm_newline_list.h b/prism/util/pm_newline_list.h index 7ae9b6b3da0ac5..406abe8ba59062 100644 --- a/prism/util/pm_newline_list.h +++ b/prism/util/pm_newline_list.h @@ -80,6 +80,17 @@ pm_newline_list_clear(pm_newline_list_t *list); */ bool pm_newline_list_append(pm_newline_list_t *list, const uint8_t *cursor); +/** + * Returns the line of the given offset. If the offset is not in the list, the + * line of the closest offset less than the given offset is returned. + * + * @param list The list to search. + * @param cursor A pointer to the offset to search for. + * @param start_line The line to start counting from. + * @return The line of the given offset. + */ +int32_t pm_newline_list_line(const pm_newline_list_t *list, const uint8_t *cursor, int32_t start_line); + /** * Returns the line and column of the given offset. If the offset is not in the * list, the line and column of the closest offset less than the given offset diff --git a/prism_compile.c b/prism_compile.c index 467da684f15f1c..30555437034dcc 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -1,7 +1,20 @@ #include "prism.h" +/** + * This compiler defines its own concept of the location of a node. We do this + * because we want to pair line information with node identifier so that we can + * have reproducable parses. + */ +typedef struct { + /** This is the line number of a node. */ + int32_t line; + + /** This is a unique identifier for the node. */ + uint32_t node_id; +} pm_node_location_t; + /******************************************************************************/ -/* These macros operate on pm_line_column_t structs as opposed to NODE*s. */ +/* These macros operate on pm_node_location_t structs as opposed to NODE*s. */ /******************************************************************************/ #define PUSH_ADJUST(seq, location, label) \ @@ -11,16 +24,16 @@ ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), -1)) #define PUSH_INSN(seq, location, insn) \ - ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).column, BIN(insn), 0)) + ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).node_id, BIN(insn), 0)) #define PUSH_INSN1(seq, location, insn, op1) \ - ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).column, BIN(insn), 1, (VALUE)(op1))) + ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).node_id, BIN(insn), 1, (VALUE)(op1))) #define PUSH_INSN2(seq, location, insn, op1, op2) \ - ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).column, BIN(insn), 2, (VALUE)(op1), (VALUE)(op2))) + ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).node_id, BIN(insn), 2, (VALUE)(op1), (VALUE)(op2))) #define PUSH_INSN3(seq, location, insn, op1, op2, op3) \ - ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).column, BIN(insn), 3, (VALUE)(op1), (VALUE)(op2), (VALUE)(op3))) + ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).node_id, BIN(insn), 3, (VALUE)(op1), (VALUE)(op2), (VALUE)(op3))) #define PUSH_INSNL(seq, location, insn, label) \ (PUSH_INSN1(seq, location, insn, label), LABEL_REF(label)) @@ -29,7 +42,7 @@ ADD_ELEM((seq), (LINK_ELEMENT *) (label)) #define PUSH_SEND_R(seq, location, id, argc, block, flag, keywords) \ - ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_send(iseq, (int) (location).line, (int) (location).column, (id), (VALUE)(argc), (block), (VALUE)(flag), (keywords))) + ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_send(iseq, (int) (location).line, (int) (location).node_id, (id), (VALUE)(argc), (block), (VALUE)(flag), (keywords))) #define PUSH_SEND(seq, location, id, argc) \ PUSH_SEND_R((seq), location, (id), (argc), NULL, (VALUE)INT2FIX(0), NULL) @@ -68,34 +81,34 @@ /******************************************************************************/ static void -pm_iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line_no, int column, int idx, int level) +pm_iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int node_id, int idx, int level) { if (iseq_local_block_param_p(iseq, idx, level)) { - ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line_no, column, BIN(getblockparam), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level))); + ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(getblockparam), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level))); } else { - ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line_no, column, BIN(getlocal), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level))); + ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(getlocal), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level))); } if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qfalse); } static void -pm_iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line_no, int column, int idx, int level) +pm_iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int node_id, int idx, int level) { if (iseq_local_block_param_p(iseq, idx, level)) { - ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line_no, column, BIN(setblockparam), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level))); + ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(setblockparam), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level))); } else { - ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line_no, column, BIN(setlocal), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level))); + ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(setlocal), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level))); } if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qtrue); } #define PUSH_GETLOCAL(seq, location, idx, level) \ - pm_iseq_add_getlocal(iseq, (seq), (int) (location).line, (int) (location).column, (idx), (level)) + pm_iseq_add_getlocal(iseq, (seq), (int) (location).line, (int) (location).node_id, (idx), (level)) #define PUSH_SETLOCAL(seq, location, idx, level) \ - pm_iseq_add_setlocal(iseq, (seq), (int) (location).line, (int) (location).column, (idx), (level)) + pm_iseq_add_setlocal(iseq, (seq), (int) (location).line, (int) (location).node_id, (idx), (level)) /******************************************************************************/ /* These are helper macros for the compiler. */ @@ -131,6 +144,15 @@ pm_iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line_no, int c #define PM_CONSTANT_MULT ((pm_constant_id_t)(idMULT | PM_SPECIAL_CONSTANT_FLAG)) #define PM_CONSTANT_POW ((pm_constant_id_t)(idPow | PM_SPECIAL_CONSTANT_FLAG)) +#define PM_NODE_START_LOCATION(parser, node) \ + ((pm_node_location_t) { .line = pm_newline_list_line(&(parser)->newline_list, ((const pm_node_t *) (node))->location.start, (parser)->start_line), .node_id = ((const pm_node_t *) (node))->node_id }) + +#define PM_NODE_END_LOCATION(parser, node) \ + ((pm_node_location_t) { .line = pm_newline_list_line(&(parser)->newline_list, ((const pm_node_t *) (node))->location.end, (parser)->start_line), .node_id = ((const pm_node_t *) (node))->node_id }) + +#define PM_LOCATION_START_LOCATION(parser, location, id) \ + ((pm_node_location_t) { .line = pm_newline_list_line(&(parser)->newline_list, (location)->start, (parser)->start_line), .node_id = id }) + #define PM_NODE_START_LINE_COLUMN(parser, node) \ pm_newline_list_line_column(&(parser)->newline_list, ((const pm_node_t *) (node))->location.start, (parser)->start_line) @@ -143,12 +165,12 @@ pm_iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line_no, int c static int pm_node_line_number(const pm_parser_t *parser, const pm_node_t *node) { - return (int) PM_NODE_START_LINE_COLUMN(parser, node).line; + return (int) pm_newline_list_line(&parser->newline_list, node->location.start, parser->start_line); } static int pm_location_line_number(const pm_parser_t *parser, const pm_location_t *location) { - return (int) PM_LOCATION_START_LINE_COLUMN(parser, location).line; + return (int) pm_newline_list_line(&parser->newline_list, location->start, parser->start_line); } /** @@ -543,7 +565,7 @@ parse_regexp_concat(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm 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) +pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const pm_node_location_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; @@ -551,7 +573,7 @@ pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const if (parts_size > 0) { VALUE current_string = Qnil; - pm_line_column_t current_location = *node_location; + pm_node_location_t current_location = *node_location; for (size_t index = 0; index < parts_size; index++) { const pm_node_t *part = parts->nodes[index]; @@ -572,7 +594,7 @@ pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const } else { current_string = string_value; - if (index != 0) current_location = PM_NODE_END_LINE_COLUMN(scope_node->parser, part); + if (index != 0) current_location = PM_NODE_END_LOCATION(scope_node->parser, part); } } else { @@ -599,7 +621,7 @@ pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const } else { current_string = string_value; - current_location = PM_NODE_START_LINE_COLUMN(scope_node->parser, part); + current_location = PM_NODE_START_LOCATION(scope_node->parser, part); } } else { @@ -627,7 +649,7 @@ pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const PUSH_INSN1(ret, current_location, putobject, rb_fstring(current_string)); PM_COMPILE_NOT_POPPED(part); - const pm_line_column_t current_location = PM_NODE_START_LINE_COLUMN(scope_node->parser, part); + const pm_node_location_t current_location = PM_NODE_START_LOCATION(scope_node->parser, part); PUSH_INSN(ret, current_location, dup); PUSH_INSN1(ret, current_location, objtostring, new_callinfo(iseq, idTo_s, 0, VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE, NULL, FALSE)); PUSH_INSN(ret, current_location, anytostring); @@ -660,7 +682,7 @@ pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const } 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) +pm_compile_regexp_dynamic(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_list_t *parts, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *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; @@ -835,7 +857,7 @@ pm_compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_no static void pm_compile_logical(rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_node_t *cond, LABEL *then_label, LABEL *else_label, bool popped, pm_scope_node_t *scope_node) { - const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, cond); + const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, cond); DECL_ANCHOR(seq); INIT_ANCHOR(seq); @@ -865,7 +887,7 @@ pm_compile_logical(rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_node_t *cond, LAB static void pm_compile_flip_flop_bound(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) { - const pm_line_column_t location = { .line = ISEQ_BODY(iseq)->location.first_lineno, .column = -1 }; + const pm_node_location_t location = { .line = ISEQ_BODY(iseq)->location.first_lineno, .node_id = -1 }; if (PM_NODE_TYPE_P(node, PM_INTEGER_NODE)) { PM_COMPILE_NOT_POPPED(node); @@ -881,7 +903,7 @@ pm_compile_flip_flop_bound(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR * static void pm_compile_flip_flop(const pm_flip_flop_node_t *flip_flop_node, LABEL *else_label, LABEL *then_label, rb_iseq_t *iseq, const int lineno, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) { - const pm_line_column_t location = { .line = ISEQ_BODY(iseq)->location.first_lineno, .column = -1 }; + const pm_node_location_t location = { .line = ISEQ_BODY(iseq)->location.first_lineno, .node_id = -1 }; LABEL *lend = NEW_LABEL(location.line); int again = !(flip_flop_node->base.flags & PM_RANGE_FLAGS_EXCLUDE_END); @@ -920,12 +942,12 @@ pm_compile_flip_flop(const pm_flip_flop_node_t *flip_flop_node, LABEL *else_labe PUSH_INSNL(ret, location, jump, then_label); } -static void pm_compile_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition); +static void pm_compile_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition); 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) { - const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, cond); + const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, cond); again: switch (PM_NODE_TYPE(cond)) { @@ -967,7 +989,26 @@ pm_compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_no break; } default: { - pm_compile_node(iseq, cond, ret, false, scope_node); + DECL_ANCHOR(cond_seq); + INIT_ANCHOR(cond_seq); + pm_compile_node(iseq, cond, cond_seq, false, scope_node); + + if (LIST_INSN_SIZE_ONE(cond_seq)) { + INSN *insn = (INSN *)ELEM_FIRST_INSN(FIRST_ELEMENT(cond_seq)); + if (insn->insn_id == BIN(putobject)) { + if (RTEST(insn->operands[0])) { + ADD_INSNL(ret, cond, jump, then_label); + // maybe unreachable + return; + } + else { + ADD_INSNL(ret, cond, jump, else_label); + return; + } + } + } + + PUSH_SEQ(ret, cond_seq); break; } } @@ -980,9 +1021,9 @@ pm_compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_no * Compile an if or unless node. */ static void -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) +pm_compile_conditional(rb_iseq_t *iseq, const pm_node_location_t *node_location, 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; + const pm_node_location_t location = *node_location; LABEL *then_label = NEW_LABEL(location.line); LABEL *else_label = NEW_LABEL(location.line); LABEL *end_label = NULL; @@ -1084,15 +1125,14 @@ pm_compile_conditional(rb_iseq_t *iseq, const pm_line_column_t *line_column, pm_ * 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_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) +pm_compile_loop(rb_iseq_t *iseq, const pm_node_location_t *node_location, 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; + const pm_node_location_t location = *node_location; LABEL *prev_start_label = ISEQ_COMPILE_DATA(iseq)->start_label; LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label; LABEL *prev_redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label; - // TODO: Deal with ensures in here LABEL *next_label = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(location.line); /* next */ LABEL *redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label = NEW_LABEL(location.line); /* redo */ LABEL *break_label = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(location.line); /* break */ @@ -1102,6 +1142,12 @@ pm_compile_loop(rb_iseq_t *iseq, const pm_line_column_t *line_column, pm_node_fl LABEL *next_catch_label = NEW_LABEL(location.line); LABEL *tmp_label = NULL; + // We're pushing onto the ensure stack because breaks need to break out of + // this loop and not break into the ensure statements within the same + // lexical scope. + struct iseq_compile_data_ensure_node_stack enl; + push_ensure_entry(iseq, &enl, NULL, NULL); + // begin; end while true if (flags & PM_LOOP_FLAGS_BEGIN_MODIFIER) { tmp_label = NEW_LABEL(location.line); @@ -1154,6 +1200,8 @@ pm_compile_loop(rb_iseq_t *iseq, const pm_line_column_t *line_column, pm_node_fl ISEQ_COMPILE_DATA(iseq)->start_label = prev_start_label; ISEQ_COMPILE_DATA(iseq)->end_label = prev_end_label; ISEQ_COMPILE_DATA(iseq)->redo_label = prev_redo_label; + ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->prev; + return; } @@ -1219,7 +1267,7 @@ pm_new_child_iseq(rb_iseq_t *iseq, pm_scope_node_t *node, VALUE name, const rb_i } static int -pm_compile_class_path(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) +pm_compile_class_path(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) { if (PM_NODE_TYPE_P(node, PM_CONSTANT_PATH_NODE)) { const pm_node_t *parent = ((const pm_constant_path_node_t *) node)->parent; @@ -1247,9 +1295,9 @@ pm_compile_class_path(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_colu * method calls that are followed by a ||= or &&= operator. */ static void -pm_compile_call_and_or_write_node(rb_iseq_t *iseq, bool and_node, const pm_node_t *receiver, const pm_node_t *value, pm_constant_id_t write_name, pm_constant_id_t read_name, bool safe_nav, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) +pm_compile_call_and_or_write_node(rb_iseq_t *iseq, bool and_node, const pm_node_t *receiver, const pm_node_t *value, pm_constant_id_t write_name, pm_constant_id_t read_name, bool safe_nav, const pm_node_location_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_node_location_t location = *node_location; LABEL *lfin = NEW_LABEL(location.line); LABEL *lcfin = NEW_LABEL(location.line); LABEL *lskip = NULL; @@ -1303,74 +1351,171 @@ pm_compile_call_and_or_write_node(rb_iseq_t *iseq, bool and_node, const pm_node_ * contents of the hash are not popped. */ static void -pm_compile_hash_elements(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_list_t *elements, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node) +pm_compile_hash_elements(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_list_t *elements, bool argument, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node) { - const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node); + const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node); // If this element is not popped, then we need to create the hash on the // stack. Neighboring plain assoc nodes should be grouped together (either // by newhash or hash merge). Double splat nodes should be merged using the // merge_kwd method call. - int assoc_length = 0; - bool made_hash = false; + const int max_stack_length = 0x100; + const int min_tmp_hash_length = 0x800; + + int stack_length = 0; + bool first_chunk = true; + + // This is an optimization wherein we keep track of whether or not the + // previous element was a static literal. If it was, then we do not attempt + // to check if we have a subhash that can be optimized. If it was not, then + // we do check. + bool static_literal = false; + + DECL_ANCHOR(anchor); + INIT_ANCHOR(anchor); + + // Convert pushed elements to a hash, and merge if needed. +#define FLUSH_CHUNK \ + if (stack_length) { \ + if (first_chunk) { \ + PUSH_SEQ(ret, anchor); \ + PUSH_INSN1(ret, location, newhash, INT2FIX(stack_length)); \ + first_chunk = false; \ + } \ + else { \ + PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); \ + PUSH_INSN(ret, location, swap); \ + PUSH_SEQ(ret, anchor); \ + PUSH_SEND(ret, location, id_core_hash_merge_ptr, INT2FIX(stack_length + 1)); \ + } \ + INIT_ANCHOR(anchor); \ + stack_length = 0; \ + } for (size_t index = 0; index < elements->size; index++) { const pm_node_t *element = elements->nodes[index]; switch (PM_NODE_TYPE(element)) { case PM_ASSOC_NODE: { + // Pre-allocation check (this branch can be omitted). + if (PM_NODE_FLAG_P(element, PM_NODE_FLAG_STATIC_LITERAL) && !static_literal && ((index + min_tmp_hash_length) < elements->size)) { + // Count the elements that are statically-known. + size_t count = 1; + while (index + count < elements->size && PM_NODE_FLAG_P(elements->nodes[index + count], PM_NODE_FLAG_STATIC_LITERAL)) count++; + + if (count >= min_tmp_hash_length) { + // The subsequence of elements in this hash is long enough + // to merit its own hash. + VALUE ary = rb_ary_hidden_new(count); + + // Create a hidden hash. + for (size_t tmp_end = index + count; index < tmp_end; index++) { + const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[index]; + + VALUE elem[2] = { + pm_static_literal_value(iseq, assoc->key, scope_node), + pm_static_literal_value(iseq, assoc->value, scope_node) + }; + + rb_ary_cat(ary, elem, 2); + } + + VALUE hash = rb_hash_new_with_size(RARRAY_LEN(ary) / 2); + rb_hash_bulk_insert(RARRAY_LEN(ary), RARRAY_CONST_PTR(ary), hash); + hash = rb_obj_hide(hash); + OBJ_FREEZE(hash); + + // Emit optimized code. + FLUSH_CHUNK; + if (first_chunk) { + PUSH_INSN1(ret, location, duphash, hash); + first_chunk = false; + } + else { + PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); + PUSH_INSN(ret, location, swap); + PUSH_INSN1(ret, location, putobject, hash); + PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2)); + } + + break; + } + else { + static_literal = true; + } + } + else { + static_literal = false; + } + // If this is a plain assoc node, then we can compile it directly - // and then add to the number of assoc nodes we've seen so far. - PM_COMPILE_NOT_POPPED(element); - assoc_length++; + // and then add the total number of values on the stack. + pm_compile_node(iseq, element, anchor, false, scope_node); + if ((stack_length += 2) >= max_stack_length) FLUSH_CHUNK; break; } case PM_ASSOC_SPLAT_NODE: { - // If we are at a splat and we have already compiled some elements - // of the hash, then we need to either create the first hash or - // merge the current elements into the existing hash. - if (assoc_length > 0) { - if (!made_hash) { - PUSH_INSN1(ret, location, newhash, INT2FIX(assoc_length * 2)); - PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); - PUSH_INSN(ret, location, swap); - made_hash = true; - } - else { - // Here we are merging plain assoc nodes into the hash on - // the stack. - PUSH_SEND(ret, location, id_core_hash_merge_ptr, INT2FIX(assoc_length * 2 + 1)); + FLUSH_CHUNK; - // Since we already have a hash on the stack, we need to set - // up the method call for the next merge that will occur. - PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); - PUSH_INSN(ret, location, swap); - } + const pm_assoc_splat_node_t *assoc_splat = (const pm_assoc_splat_node_t *) element; + bool empty_hash = assoc_splat->value != NULL && ( + (PM_NODE_TYPE_P(assoc_splat->value, PM_HASH_NODE) && ((const pm_hash_node_t *) assoc_splat->value)->elements.size == 0) || + PM_NODE_TYPE_P(assoc_splat->value, PM_NIL_NODE) + ); - assoc_length = 0; - } + bool first_element = first_chunk && stack_length == 0; + bool last_element = index == elements->size - 1; + bool only_element = first_element && last_element; - // If this is the first time we've seen a splat, then we need to - // create a hash that we can merge into. - if (!made_hash) { - PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); - PUSH_INSN1(ret, location, newhash, INT2FIX(0)); - made_hash = true; + if (empty_hash) { + if (only_element && argument) { + // **{} appears at the only keyword argument in method call, + // so it won't be modified. + // + // This is only done for method calls and not for literal + // hashes, because literal hashes should always result in a + // new hash. + PUSH_INSN1(ret, location, putobject, rb_hash_new()); + } + else if (first_element) { + // **{} appears as the first keyword argument, so it may be + // modified. We need to create a fresh hash object. + PUSH_INSN1(ret, location, newhash, INT2FIX(0)); + } + // Any empty keyword splats that are not the first can be + // ignored since merging an empty hash into the existing hash is + // the same as not merging it. } + else { + if (only_element && argument) { + // ** is only keyword argument in the method call. Use it + // directly. This will be not be flagged as mutable. This is + // only done for method calls and not for literal hashes, + // because literal hashes should always result in a new + // hash. + PM_COMPILE_NOT_POPPED(element); + } + else { + // There is more than one keyword argument, or this is not a + // method call. In that case, we need to add an empty hash + // (if first keyword), or merge the hash to the accumulated + // hash (if not the first keyword). + PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); - // Now compile the splat node itself and merge it into the hash. - PM_COMPILE_NOT_POPPED(element); - PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2)); + if (first_element) { + PUSH_INSN1(ret, location, newhash, INT2FIX(0)); + } + else { + PUSH_INSN(ret, location, swap); + } - // We know that any subsequent elements will need to be merged in - // using one of the special core methods. So here we will put the - // receiver of the merge and then swap it with the hash that is - // going to be the first argument. - if (index != elements->size - 1) { - PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); - PUSH_INSN(ret, location, swap); + PM_COMPILE_NOT_POPPED(element); + PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2)); + } } + first_chunk = false; + static_literal = false; break; } default: @@ -1379,24 +1524,15 @@ pm_compile_hash_elements(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_l } } - if (!made_hash) { - // If we haven't already made the hash, then this means we only saw - // plain assoc nodes. In this case, we can just create the hash - // directly. - PUSH_INSN1(ret, location, newhash, INT2FIX(assoc_length * 2)); - } - else if (assoc_length > 0) { - // If we have already made the hash, then we need to merge the remaining - // assoc nodes into the hash on the stack. - PUSH_SEND(ret, location, id_core_hash_merge_ptr, INT2FIX(assoc_length * 2 + 1)); - } + FLUSH_CHUNK; +#undef FLUSH_CHUNK } // This is details. Users should call pm_setup_args() instead. static int -pm_setup_args_core(const pm_arguments_node_t *arguments_node, const pm_node_t *block, int *flags, const bool has_regular_blockarg, struct rb_callinfo_kwarg **kw_arg, rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, const pm_line_column_t *node_location) +pm_setup_args_core(const pm_arguments_node_t *arguments_node, const pm_node_t *block, int *flags, const bool has_regular_blockarg, struct rb_callinfo_kwarg **kw_arg, rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, const pm_node_location_t *node_location) { - const pm_line_column_t location = *node_location; + const pm_node_location_t location = *node_location; int orig_argc = 0; bool has_splat = false; @@ -1426,7 +1562,7 @@ pm_setup_args_core(const pm_arguments_node_t *arguments_node, const pm_node_t *b if (has_keyword_splat || has_splat) { *flags |= VM_CALL_KW_SPLAT; has_keyword_splat = true; - pm_compile_hash_elements(iseq, argument, elements, ret, scope_node); + pm_compile_hash_elements(iseq, argument, elements, true, ret, scope_node); } else { // We need to first figure out if all elements of the @@ -1655,7 +1791,7 @@ pm_setup_args_core(const pm_arguments_node_t *arguments_node, const pm_node_t *b // Compile the argument parts of a call static int -pm_setup_args(const pm_arguments_node_t *arguments_node, const pm_node_t *block, int *flags, struct rb_callinfo_kwarg **kw_arg, rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, const pm_line_column_t *node_location) +pm_setup_args(const pm_arguments_node_t *arguments_node, const pm_node_t *block, int *flags, struct rb_callinfo_kwarg **kw_arg, rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, const pm_node_location_t *node_location) { if (block && PM_NODE_TYPE_P(block, PM_BLOCK_ARGUMENT_NODE)) { // We compile the `&block_arg` expression first and stitch it later @@ -1701,9 +1837,9 @@ pm_setup_args(const pm_arguments_node_t *arguments_node, const pm_node_t *block, * and then calling the []= method with the result of the operator method. */ static void -pm_compile_index_operator_write_node(rb_iseq_t *iseq, const pm_index_operator_write_node_t *node, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) +pm_compile_index_operator_write_node(rb_iseq_t *iseq, const pm_index_operator_write_node_t *node, const pm_node_location_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_node_location_t location = *node_location; if (!popped) PUSH_INSN(ret, location, putnil); PM_COMPILE_NOT_POPPED(node->receiver); @@ -1818,9 +1954,9 @@ pm_compile_index_operator_write_node(rb_iseq_t *iseq, const pm_index_operator_wr * []= method. */ static void -pm_compile_index_control_flow_write_node(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_t *receiver, const pm_arguments_node_t *arguments, const pm_node_t *block, const pm_node_t *value, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) +pm_compile_index_control_flow_write_node(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_t *receiver, const pm_arguments_node_t *arguments, const pm_node_t *block, const pm_node_t *value, const pm_node_location_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_node_location_t location = *node_location; if (!popped) PUSH_INSN(ret, location, putnil); PM_COMPILE_NOT_POPPED(receiver); @@ -1961,7 +2097,7 @@ static int pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, cons static int pm_compile_pattern_generic_error(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, VALUE message, unsigned int base_index) { - const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node); + const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node); LABEL *match_succeeded_label = NEW_LABEL(location.line); PUSH_INSN(ret, location, dup); @@ -1991,7 +2127,7 @@ pm_compile_pattern_generic_error(rb_iseq_t *iseq, pm_scope_node_t *scope_node, c static int pm_compile_pattern_length_error(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, VALUE message, VALUE length, unsigned int base_index) { - const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node); + const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node); LABEL *match_succeeded_label = NEW_LABEL(location.line); PUSH_INSN(ret, location, dup); @@ -2024,7 +2160,7 @@ pm_compile_pattern_length_error(rb_iseq_t *iseq, pm_scope_node_t *scope_node, co static int pm_compile_pattern_eqq_error(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, unsigned int base_index) { - const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node); + const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node); LABEL *match_succeeded_label = NEW_LABEL(location.line); PUSH_INSN(ret, location, dup); @@ -2072,7 +2208,7 @@ pm_compile_pattern_match(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_ static int pm_compile_pattern_deconstruct(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *deconstruct_label, LABEL *match_failed_label, LABEL *deconstructed_label, LABEL *type_error_label, bool in_single_pattern, bool use_deconstructed_cache, unsigned int base_index) { - const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node); + const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node); if (use_deconstructed_cache) { PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE)); @@ -2124,7 +2260,7 @@ pm_compile_pattern_deconstruct(rb_iseq_t *iseq, pm_scope_node_t *scope_node, con static int pm_compile_pattern_constant(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *match_failed_label, bool in_single_pattern, unsigned int base_index) { - const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node); + const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node); PUSH_INSN(ret, location, dup); PM_COMPILE_NOT_POPPED(node); @@ -2147,7 +2283,7 @@ pm_compile_pattern_constant(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const static void pm_compile_pattern_error_handler(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *done_label, bool popped) { - const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node); + const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node); LABEL *key_error_label = NEW_LABEL(location.line); LABEL *cleanup_label = NEW_LABEL(location.line); @@ -2196,7 +2332,7 @@ pm_compile_pattern_error_handler(rb_iseq_t *iseq, const pm_scope_node_t *scope_n static int pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *matched_label, LABEL *unmatched_label, bool in_single_pattern, bool in_alternation_pattern, bool use_deconstructed_cache, unsigned int base_index) { - const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node); + const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node); switch (PM_NODE_TYPE(node)) { case PM_ARRAY_PATTERN_NODE: { @@ -3038,7 +3174,7 @@ pm_iseq_builtin_function_name(const pm_scope_node_t *scope_node, const pm_node_t // Compile Primitive.attr! :leaf, ... static int -pm_compile_builtin_attr(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_arguments_node_t *arguments, const pm_line_column_t *node_location) +pm_compile_builtin_attr(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_arguments_node_t *arguments, const pm_node_location_t *node_location) { if (arguments == NULL) { COMPILE_ERROR(iseq, node_location->line, "attr!: no argument"); @@ -3074,7 +3210,7 @@ pm_compile_builtin_attr(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, cons } static int -pm_compile_builtin_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_scope_node_t *scope_node, const pm_arguments_node_t *arguments, const pm_line_column_t *node_location, int popped) +pm_compile_builtin_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_scope_node_t *scope_node, const pm_arguments_node_t *arguments, const pm_node_location_t *node_location, int popped) { if (arguments == NULL) { COMPILE_ERROR(iseq, node_location->line, "arg!: no argument"); @@ -3104,7 +3240,7 @@ pm_compile_builtin_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_scope_n } static int -pm_compile_builtin_mandatory_only_method(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_call_node_t *call_node, const pm_line_column_t *node_location) +pm_compile_builtin_mandatory_only_method(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_call_node_t *call_node, const pm_node_location_t *node_location) { const pm_node_t *ast_node = scope_node->ast_node; if (!PM_NODE_TYPE_P(ast_node, PM_DEF_NODE)) { @@ -3169,7 +3305,7 @@ pm_compile_builtin_mandatory_only_method(rb_iseq_t *iseq, pm_scope_node_t *scope } static int -pm_compile_builtin_function_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, const pm_call_node_t *call_node, const pm_line_column_t *node_location, int popped, const rb_iseq_t *parent_block, const char *builtin_func) +pm_compile_builtin_function_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, const pm_call_node_t *call_node, const pm_node_location_t *node_location, int popped, const rb_iseq_t *parent_block, const char *builtin_func) { const pm_arguments_node_t *arguments = call_node->arguments; @@ -3270,14 +3406,14 @@ pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *c 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_START_LINE_COLUMN(scope_node->parser, message_loc); + const pm_node_location_t location = PM_LOCATION_START_LOCATION(scope_node->parser, message_loc, call_node->base.node_id); 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; + int node_id = location.node_id; if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { if (PM_BRANCH_COVERAGE_P(iseq)) { @@ -3387,11 +3523,11 @@ pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *c } static void -pm_compile_defined_expr0(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition, LABEL **lfinish, bool explicit_receiver) +pm_compile_defined_expr0(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition, LABEL **lfinish, bool explicit_receiver) { // in_condition is the same as compile.c's needstr enum defined_type dtype = DEFINED_NOT_DEFINED; - const pm_line_column_t location = *node_location; + const pm_node_location_t location = *node_location; switch (PM_NODE_TYPE(node)) { case PM_ARGUMENTS_NODE: { @@ -3739,7 +3875,7 @@ pm_compile_defined_expr0(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_c } static void -pm_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition, LABEL **lfinish, bool explicit_receiver) +pm_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition, LABEL **lfinish, bool explicit_receiver) { LINK_ELEMENT *lcur = ret->last; pm_compile_defined_expr0(iseq, node, node_location, ret, popped, scope_node, in_condition, lfinish, false); @@ -3770,7 +3906,7 @@ pm_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_column_t * } static void -pm_compile_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition) +pm_compile_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition) { LABEL *lfinish[3]; LINK_ELEMENT *last = ret->last; @@ -3784,7 +3920,7 @@ pm_compile_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_co } if (lfinish[1]) { - ELEM_INSERT_NEXT(last, &new_insn_body(iseq, node_location->line, node_location->column, BIN(putnil), 0)->link); + ELEM_INSERT_NEXT(last, &new_insn_body(iseq, node_location->line, node_location->node_id, BIN(putnil), 0)->link); PUSH_INSN(ret, *node_location, swap); if (lfinish[2]) PUSH_LABEL(ret, lfinish[2]); @@ -3952,7 +4088,7 @@ pm_compile_destructured_param_locals(const pm_multi_target_node_t *node, st_tabl static inline void pm_compile_destructured_param_write(rb_iseq_t *iseq, const pm_required_parameter_node_t *node, LINK_ANCHOR *const ret, const pm_scope_node_t *scope_node) { - const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node); + const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node); pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, node->name, 0); PUSH_SETLOCAL(ret, location, index.index, index.level); } @@ -3968,7 +4104,7 @@ pm_compile_destructured_param_write(rb_iseq_t *iseq, const pm_required_parameter static void pm_compile_destructured_param_writes(rb_iseq_t *iseq, const pm_multi_target_node_t *node, LINK_ANCHOR *const ret, const pm_scope_node_t *scope_node) { - const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node); + const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node); bool has_rest = (node->rest && PM_NODE_TYPE_P(node->rest, PM_SPLAT_NODE) && (((const pm_splat_node_t *) node->rest)->expression) != NULL); bool has_rights = node->rights.size > 0; @@ -4165,7 +4301,7 @@ pm_compile_multi_target_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR static void pm_compile_target_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const parents, LINK_ANCHOR *const writes, LINK_ANCHOR *const cleanup, pm_scope_node_t *scope_node, pm_multi_target_state_t *state) { - const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node); + const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node); switch (PM_NODE_TYPE(node)) { case PM_LOCAL_VARIABLE_TARGET_NODE: { @@ -4396,7 +4532,7 @@ pm_compile_target_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *cons static void pm_compile_multi_target_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const parents, LINK_ANCHOR *const writes, LINK_ANCHOR *const cleanup, pm_scope_node_t *scope_node, pm_multi_target_state_t *state) { - const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node); + const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node); const pm_node_list_t *lefts; const pm_node_t *rest; const pm_node_list_t *rights; @@ -4479,7 +4615,7 @@ pm_compile_multi_target_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR static void pm_compile_for_node_index(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node) { - const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node); + const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node); switch (PM_NODE_TYPE(node)) { case PM_LOCAL_VARIABLE_TARGET_NODE: { @@ -4567,7 +4703,7 @@ pm_compile_for_node_index(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *c } static void -pm_compile_rescue(rb_iseq_t *iseq, const pm_begin_node_t *cast, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) +pm_compile_rescue(rb_iseq_t *iseq, const pm_begin_node_t *cast, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) { const pm_parser_t *parser = scope_node->parser; @@ -4618,11 +4754,18 @@ pm_compile_rescue(rb_iseq_t *iseq, const pm_begin_node_t *cast, const pm_line_co } static void -pm_compile_ensure(rb_iseq_t *iseq, const pm_begin_node_t *cast, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) +pm_compile_ensure(rb_iseq_t *iseq, const pm_begin_node_t *cast, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node) { const pm_parser_t *parser = scope_node->parser; const pm_statements_node_t *statements = cast->ensure_clause->statements; - const pm_line_column_t location = statements != NULL ? PM_NODE_START_LINE_COLUMN(parser, statements) : *node_location; + + pm_node_location_t location; + if (statements != NULL) { + location = PM_NODE_START_LOCATION(parser, statements); + } + else { + location = *node_location; + } LABEL *estart = NEW_LABEL(location.line); LABEL *eend = NEW_LABEL(location.line); @@ -4741,9 +4884,9 @@ pm_opt_aset_with_p(const rb_iseq_t *iseq, const pm_call_node_t *node) * of the current iseq. */ 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) +pm_compile_constant_read(rb_iseq_t *iseq, VALUE name, const pm_location_t *name_loc, uint32_t node_id, LINK_ANCHOR *const ret, const pm_scope_node_t *scope_node) { - const pm_line_column_t location = PM_LOCATION_START_LINE_COLUMN(scope_node->parser, name_loc); + const pm_node_location_t location = PM_LOCATION_START_LOCATION(scope_node->parser, name_loc, node_id); if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) { ISEQ_BODY(iseq)->ic_size++; @@ -4802,7 +4945,7 @@ pm_constant_path_parts(const pm_node_t *node, const pm_scope_node_t *scope_node) static void pm_compile_constant_path(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const prefix, LINK_ANCHOR *const body, bool popped, pm_scope_node_t *scope_node) { - const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node); + const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node); switch (PM_NODE_TYPE(node)) { case PM_CONSTANT_READ_NODE: { @@ -4964,12 +5107,12 @@ pm_compile_shareable_constant_value(rb_iseq_t *iseq, const pm_node_t *node, cons { 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); + const pm_node_location_t location = PM_NODE_START_LOCATION(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); + const pm_node_location_t location = PM_NODE_START_LOCATION(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; @@ -5055,9 +5198,9 @@ pm_compile_shareable_constant_value(rb_iseq_t *iseq, const pm_node_t *node, cons * 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) +pm_compile_constant_write_node(rb_iseq_t *iseq, const pm_constant_write_node_t *node, const pm_node_flags_t shareability, const pm_node_location_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_node_location_t location = *node_location; ID name_id = pm_constant_id_lookup(scope_node, node->name); if (shareability != 0) { @@ -5077,14 +5220,14 @@ pm_compile_constant_write_node(rb_iseq_t *iseq, const pm_constant_write_node_t * * 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) +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_node_location_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_node_location_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); + pm_compile_constant_read(iseq, name, &node->name_loc, location.node_id, ret, scope_node); if (!popped) PUSH_INSN(ret, location, dup); PUSH_INSNL(ret, location, branchunless, end_label); @@ -5108,9 +5251,9 @@ pm_compile_constant_and_write_node(rb_iseq_t *iseq, const pm_constant_and_write_ * 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) +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_node_location_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_node_location_t location = *node_location; VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name)); LABEL *set_label = NEW_LABEL(location.line); @@ -5120,7 +5263,7 @@ pm_compile_constant_or_write_node(rb_iseq_t *iseq, const pm_constant_or_write_no 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); + pm_compile_constant_read(iseq, name, &node->name_loc, location.node_id, ret, scope_node); if (!popped) PUSH_INSN(ret, location, dup); PUSH_INSNL(ret, location, branchif, end_label); @@ -5145,14 +5288,14 @@ pm_compile_constant_or_write_node(rb_iseq_t *iseq, const pm_constant_or_write_no * 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) +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_node_location_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_node_location_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); + pm_compile_constant_read(iseq, name, &node->name_loc, location.node_id, ret, scope_node); if (shareability != 0) { pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true); @@ -5203,9 +5346,9 @@ pm_constant_path_path(const pm_constant_path_node_t *node, const pm_scope_node_t * 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) +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_node_location_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_node_location_t location = *node_location; const pm_constant_path_node_t *target = node->target; VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name)); @@ -5237,9 +5380,9 @@ pm_compile_constant_path_write_node(rb_iseq_t *iseq, const pm_constant_path_writ * 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) +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_node_location_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_node_location_t location = *node_location; const pm_constant_path_node_t *target = node->target; VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name)); @@ -5288,9 +5431,9 @@ pm_compile_constant_path_and_write_node(rb_iseq_t *iseq, const pm_constant_path_ * 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) +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_node_location_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_node_location_t location = *node_location; const pm_constant_path_node_t *target = node->target; VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name)); @@ -5345,9 +5488,9 @@ pm_compile_constant_path_or_write_node(rb_iseq_t *iseq, const pm_constant_path_o * 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) +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_node_location_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_node_location_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); @@ -5396,35 +5539,28 @@ 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) { const pm_parser_t *parser = scope_node->parser; - const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(parser, node); + const pm_node_location_t location = PM_NODE_START_LOCATION(parser, node); int lineno = (int) location.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 the node that we're compiling is a return node that is redundant, - // then it cannot be considered a line node because the other parser - // eliminates it from the parse tree. In this case we must replicate - // this behavior. - } else { - if (PM_NODE_TYPE_P(node, PM_BEGIN_NODE) && (((const pm_begin_node_t *) node)->statements == NULL) && (((const pm_begin_node_t *) node)->rescue_clause != NULL)) { - // If this node is a begin node and it has empty statements and also - // has a rescue clause, then the other parser considers it as - // starting on the same line as the rescue, as opposed to the - // location of the begin keyword. We replicate that behavior here. - lineno = (int) PM_NODE_START_LINE_COLUMN(parser, ((const pm_begin_node_t *) node)->rescue_clause).line; - } + if (PM_NODE_TYPE_P(node, PM_BEGIN_NODE) && (((const pm_begin_node_t *) node)->statements == NULL) && (((const pm_begin_node_t *) node)->rescue_clause != NULL)) { + // If this node is a begin node and it has empty statements and also + // has a rescue clause, then the other parser considers it as + // starting on the same line as the rescue, as opposed to the + // location of the begin keyword. We replicate that behavior here. + lineno = (int) PM_NODE_START_LINE_COLUMN(parser, ((const pm_begin_node_t *) node)->rescue_clause).line; + } - if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_NEWLINE) && ISEQ_COMPILE_DATA(iseq)->last_line != lineno) { - // If this node has the newline flag set and it is on a new line - // from the previous nodes that have been compiled for this ISEQ, - // then we need to emit a newline event. - int event = RUBY_EVENT_LINE; + if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_NEWLINE) && ISEQ_COMPILE_DATA(iseq)->last_line != lineno) { + // If this node has the newline flag set and it is on a new line + // from the previous nodes that have been compiled for this ISEQ, + // then we need to emit a newline event. + 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; - } - PUSH_TRACE(ret, event); + ISEQ_COMPILE_DATA(iseq)->last_line = lineno; + if (ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) { + event |= RUBY_EVENT_COVERAGE_LINE; } + PUSH_TRACE(ret, event); } switch (PM_NODE_TYPE(node)) { @@ -5532,29 +5668,34 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, // We treat all sequences of non-splat elements as their // own arrays, followed by a newarray, and then continually // concat the arrays with the SplatNode nodes. + const int max_new_array_size = 0x100; + const int min_tmp_array_size = 0x40; + int new_array_size = 0; + bool first_chunk = true; + + // This is an optimization wherein we keep track of whether or not + // the previous element was a static literal. If it was, then we do + // not attempt to check if we have a subarray that can be optimized. + // If it was not, then we do check. + bool static_literal = false; - bool need_to_concat_array = false; - bool has_kw_splat = false; + // Either create a new array, or push to the existing array. +#define FLUSH_CHUNK \ + if (new_array_size) { \ + if (first_chunk) PUSH_INSN1(ret, location, newarray, INT2FIX(new_array_size)); \ + else PUSH_INSN1(ret, location, pushtoarray, INT2FIX(new_array_size)); \ + first_chunk = false; \ + new_array_size = 0; \ + } for (size_t index = 0; index < elements->size; index++) { const pm_node_t *element = elements->nodes[index]; if (PM_NODE_TYPE_P(element, PM_SPLAT_NODE)) { - const pm_splat_node_t *splat_element = (const pm_splat_node_t *) element; - - // If we already have non-splat elements, we need to emit a - // newarray instruction. - if (new_array_size > 0) { - PUSH_INSN1(ret, location, newarray, INT2FIX(new_array_size)); - new_array_size = 0; - - // We don't want to emit a concat array in the case - // where we're seeing our first splat, and already have - // elements. - if (need_to_concat_array) PUSH_INSN(ret, location, concatarray); - } + FLUSH_CHUNK; + const pm_splat_node_t *splat_element = (const pm_splat_node_t *) element; if (splat_element->expression) { PM_COMPILE_NOT_POPPED(splat_element->expression); } @@ -5563,43 +5704,96 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, PUSH_GETLOCAL(ret, location, index.index, index.level); } - if (index > 0) { - PUSH_INSN(ret, location, concatarray); - } - else { + if (first_chunk) { // If this is the first element of the array then we // need to splatarray the elements into the list. PUSH_INSN1(ret, location, splatarray, Qtrue); + first_chunk = false; + } + else { + PUSH_INSN(ret, location, concattoarray); } - // Since we have now seen a splat and are concat-ing arrays, - // all subsequent splats will need to concat as well. - need_to_concat_array = true; + static_literal = false; } else if (PM_NODE_TYPE_P(element, PM_KEYWORD_HASH_NODE)) { - new_array_size++; - has_kw_splat = true; - pm_compile_hash_elements(iseq, element, &((const pm_keyword_hash_node_t *) element)->elements, ret, scope_node); - } - else { - new_array_size++; - PM_COMPILE_NOT_POPPED(element); - } - } + if (new_array_size == 0 && first_chunk) { + PUSH_INSN1(ret, location, newarray, INT2FIX(0)); + first_chunk = false; + } + else { + FLUSH_CHUNK; + } - if (new_array_size) { - if (has_kw_splat) { - PUSH_INSN1(ret, location, newarraykwsplat, INT2FIX(new_array_size)); + // If we get here, then this is the last element of the + // array/arguments, because it cannot be followed by + // anything else without a syntax error. This looks like: + // + // [foo, bar, baz: qux] + // ^^^^^^^^ + // + // [foo, bar, **baz] + // ^^^^^ + // + const pm_keyword_hash_node_t *keyword_hash = (const pm_keyword_hash_node_t *) element; + pm_compile_hash_elements(iseq, element, &keyword_hash->elements, false, ret, scope_node); + + // This boolean controls the manner in which we push the + // hash onto the array. If it's all keyword splats, then we + // can use the very specialized pushtoarraykwsplat + // instruction to check if it's empty before we push it. + size_t splats = 0; + while (splats < keyword_hash->elements.size && PM_NODE_TYPE_P(keyword_hash->elements.nodes[splats], PM_ASSOC_SPLAT_NODE)) splats++; + + if (keyword_hash->elements.size == splats) { + PUSH_INSN(ret, location, pushtoarraykwsplat); + } + else { + new_array_size++; + } } - else { - PUSH_INSN1(ret, location, newarray, INT2FIX(new_array_size)); + else if (PM_NODE_FLAG_P(element, PM_NODE_FLAG_STATIC_LITERAL) && !static_literal && ((index + min_tmp_array_size) < elements->size)) { + // If we have a static literal, then there's the potential + // to group a bunch of them together with a literal array + // and then concat them together. + size_t right_index = index + 1; + while (right_index < elements->size && PM_NODE_FLAG_P(elements->nodes[right_index], PM_NODE_FLAG_STATIC_LITERAL)) right_index++; + + size_t tmp_array_size = right_index - index; + if (tmp_array_size >= min_tmp_array_size) { + VALUE tmp_array = rb_ary_hidden_new(tmp_array_size); + + // Create the temporary array. + for (; tmp_array_size; tmp_array_size--) + rb_ary_push(tmp_array, pm_static_literal_value(iseq, elements->nodes[index++], scope_node)); + OBJ_FREEZE(tmp_array); + + // Emit the optimized code. + FLUSH_CHUNK; + if (first_chunk) { + PUSH_INSN1(ret, location, duparray, tmp_array); + first_chunk = false; + } + else { + PUSH_INSN1(ret, location, putobject, tmp_array); + PUSH_INSN(ret, location, concattoarray); + } + } + else { + static_literal = true; + } + } else { + PM_COMPILE_NOT_POPPED(element); + if (++new_array_size >= max_new_array_size) FLUSH_CHUNK; + static_literal = false; } - - if (need_to_concat_array) PUSH_INSN(ret, location, concatarray); } + FLUSH_CHUNK; if (popped) PUSH_INSN(ret, location, pop); } + +#undef FLUSH_CHUNK return; } case PM_ASSOC_NODE: { @@ -5770,7 +5964,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, const pm_location_t *message_loc = &cast->message_loc; if (message_loc->start == NULL) message_loc = &cast->base.location; - const pm_line_column_t location = PM_LOCATION_START_LINE_COLUMN(scope_node->parser, message_loc); + const pm_node_location_t location = PM_LOCATION_START_LOCATION(scope_node->parser, message_loc, cast->base.node_id); const char *builtin_func; if (UNLIKELY(iseq_has_builtin_function_table(iseq)) && (builtin_func = pm_iseq_builtin_function_name(scope_node, cast->receiver, method_id)) != NULL) { @@ -5981,7 +6175,7 @@ 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)) { - pm_line_column_t cond_location = PM_NODE_START_LINE_COLUMN(parser, condition); + pm_node_location_t cond_location = PM_NODE_START_LOCATION(parser, condition); PUSH_INSN(cond_seq, cond_location, putnil); pm_compile_node(iseq, condition, cond_seq, false, scope_node); PUSH_INSN1(cond_seq, cond_location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY)); @@ -6060,7 +6254,7 @@ 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]; - pm_line_column_t clause_location = PM_NODE_START_LINE_COLUMN(parser, (const pm_node_t *) clause); + pm_node_location_t clause_location = PM_NODE_START_LOCATION(parser, (const pm_node_t *) clause); const pm_node_list_t *conditions = &clause->conditions; LABEL *label = NEW_LABEL(clause_location.line); @@ -6070,7 +6264,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, // 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); + const pm_node_location_t condition_location = PM_NODE_START_LOCATION(parser, condition); // If we haven't already abandoned the optimization, then // we're going to try to compile the condition into the @@ -6145,7 +6339,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, PUSH_LABEL(ret, else_label); 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)); + pm_node_location_t else_location = PM_NODE_START_LOCATION(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. @@ -6245,8 +6439,8 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, RUBY_ASSERT(PM_NODE_TYPE_P(condition, PM_IN_NODE)); const pm_in_node_t *in_node = (const pm_in_node_t *) condition; - const pm_line_column_t in_location = PM_NODE_START_LINE_COLUMN(parser, in_node); - const pm_line_column_t pattern_location = PM_NODE_START_LINE_COLUMN(parser, in_node->pattern); + const pm_node_location_t in_location = PM_NODE_START_LOCATION(parser, in_node); + const pm_node_location_t pattern_location = PM_NODE_START_LOCATION(parser, in_node->pattern); if (branch_id) { PUSH_INSN(body_seq, in_location, putnil); @@ -6523,7 +6717,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node; VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name)); - pm_compile_constant_read(iseq, name, &cast->base.location, ret, scope_node); + pm_compile_constant_read(iseq, name, &cast->base.location, location.node_id, ret, scope_node); if (popped) PUSH_INSN(ret, location, pop); return; @@ -6970,9 +7164,16 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, // is popped, then we know we don't need to do anything since it's // statically known. if (!popped) { - VALUE value = pm_static_literal_value(iseq, node, scope_node); - PUSH_INSN1(ret, location, duphash, value); - RB_OBJ_WRITTEN(iseq, Qundef, value); + const pm_hash_node_t *cast = (const pm_hash_node_t *) node; + + if (cast->elements.size == 0) { + PUSH_INSN1(ret, location, newhash, INT2FIX(0)); + } + else { + VALUE value = pm_static_literal_value(iseq, node, scope_node); + PUSH_INSN1(ret, location, duphash, value); + RB_OBJ_WRITTEN(iseq, Qundef, value); + } } } else { @@ -6996,7 +7197,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, } } else { - pm_compile_hash_elements(iseq, node, elements, ret, scope_node); + pm_compile_hash_elements(iseq, node, elements, false, ret, scope_node); } } @@ -8144,9 +8345,11 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, lstart->rescued = LABEL_RESCUE_BEG; lend->rescued = LABEL_RESCUE_END; + PUSH_LABEL(ret, lstart); PM_COMPILE_NOT_POPPED(cast->expression); PUSH_LABEL(ret, lend); + PUSH_INSN(ret, location, nop); PUSH_LABEL(ret, lcont); if (popped) PUSH_INSN(ret, location, pop); @@ -8164,63 +8367,53 @@ 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; - if (PM_NODE_FLAG_P(cast, PM_RETURN_NODE_FLAGS_REDUNDANT)) { - if (arguments) { - PM_COMPILE_NOT_POPPED((const pm_node_t *) arguments); - } - else { - PUSH_INSN(ret, location, putnil); - } + 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; } - else { - 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 (arguments) { + rb_warn("argument of top-level return is ignored"); } - - 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 (parent_iseq == iseq) { + type = ISEQ_TYPE_METHOD; } + break; + default: + break; + } - if (type == ISEQ_TYPE_METHOD) { - splabel = NEW_LABEL(0); - PUSH_LABEL(ret, splabel); - PUSH_ADJUST(ret, location, 0); - } + 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 (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); - } + 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; @@ -9020,7 +9213,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, case ISEQ_TYPE_BLOCK: { LABEL *start = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(0); LABEL *end = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(0); - const pm_line_column_t block_location = { .line = body->location.first_lineno, .column = -1 }; + const pm_node_location_t block_location = { .line = body->location.first_lineno, .node_id = -1 }; start->rescued = LABEL_RESCUE_BEG; end->rescued = LABEL_RESCUE_END; @@ -9077,7 +9270,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, break; } case ISEQ_TYPE_ENSURE: { - const pm_line_column_t statements_location = (scope_node->body != NULL ? PM_NODE_START_LINE_COLUMN(scope_node->parser, scope_node->body) : location); + const pm_node_location_t statements_location = (scope_node->body != NULL ? PM_NODE_START_LOCATION(scope_node->parser, scope_node->body) : location); iseq_set_exception_local_table(iseq); if (scope_node->body != NULL) { @@ -9116,6 +9309,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, PUSH_INSNL(ret, location, branchif, lab); PUSH_INSNL(ret, location, jump, rescue_end); PUSH_LABEL(ret, lab); + PUSH_TRACE(ret, RUBY_EVENT_RESCUE); PM_COMPILE((const pm_node_t *) scope_node->body); PUSH_INSN(ret, location, leave); PUSH_LABEL(ret, rescue_end); @@ -9139,13 +9333,13 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, } 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); + const pm_node_location_t end_location = PM_NODE_END_LOCATION(scope_node->parser, scope_node->ast_node); ADD_TRACE(ret, RUBY_EVENT_END); ISEQ_COMPILE_DATA(iseq)->last_line = end_location.line; } if (!PM_NODE_TYPE_P(scope_node->ast_node, PM_ENSURE_NODE)) { - const pm_line_column_t location = { .line = ISEQ_COMPILE_DATA(iseq)->last_line, .column = -1 }; + const pm_node_location_t location = { .line = ISEQ_COMPILE_DATA(iseq)->last_line, .node_id = -1 }; PUSH_INSN(ret, location, leave); } @@ -9163,31 +9357,32 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, // A value that is being written to a constant that is being marked as // shared depending on the current lexical context. const pm_shareable_constant_node_t *cast = (const pm_shareable_constant_node_t *) node; + pm_node_flags_t shareability = (cast->base.flags & (PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL | PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING | PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY)); 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); + pm_compile_constant_write_node(iseq, (const pm_constant_write_node_t *) cast->write, shareability, &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); + pm_compile_constant_and_write_node(iseq, (const pm_constant_and_write_node_t *) cast->write, shareability, &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); + pm_compile_constant_or_write_node(iseq, (const pm_constant_or_write_node_t *) cast->write, shareability, &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); + pm_compile_constant_operator_write_node(iseq, (const pm_constant_operator_write_node_t *) cast->write, shareability, &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); + pm_compile_constant_path_write_node(iseq, (const pm_constant_path_write_node_t *) cast->write, shareability, &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); + pm_compile_constant_path_and_write_node(iseq, (const pm_constant_path_and_write_node_t *) cast->write, shareability, &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); + pm_compile_constant_path_or_write_node(iseq, (const pm_constant_path_or_write_node_t *) cast->write, shareability, &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); + pm_compile_constant_path_operator_write_node(iseq, (const pm_constant_path_operator_write_node_t *) cast->write, shareability, &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))); @@ -10296,8 +10491,8 @@ pm_parse_stdin_fgets(char *string, int size, void *stream) return NULL; } - const char *cstr = StringValueCStr(line); - size_t length = strlen(cstr); + const char *cstr = RSTRING_PTR(line); + long length = RSTRING_LEN(line); memcpy(string, cstr, length); string[length] = '\0'; diff --git a/proc.c b/proc.c index 09b288fecdc45e..fd1edb2bdc3d0a 100644 --- a/proc.c +++ b/proc.c @@ -1159,6 +1159,12 @@ rb_block_pair_yield_optimizable(void) return min > 1; } + case block_handler_type_ifunc: + { + const struct vm_ifunc *ifunc = block.as.captured.code.ifunc; + if (ifunc->flags & IFUNC_YIELD_OPTIMIZABLE) return 1; + } + default: return min > 1; } diff --git a/ractor.c b/ractor.c index 3bfeed70018d3c..a81bb9cde0dd66 100644 --- a/ractor.c +++ b/ractor.c @@ -319,6 +319,14 @@ ractor_free(void *ptr) rb_ractor_chains_unregister(r); } + + if (r->newobj_cache) { + RUBY_ASSERT(r == ruby_single_main_ractor); + + rb_gc_ractor_cache_free(r->newobj_cache); + r->newobj_cache = NULL; + } + ruby_xfree(r); } @@ -2054,6 +2062,13 @@ vm_insert_ractor0(rb_vm_t *vm, rb_ractor_t *r, bool single_ractor_mode) vm->ractor.cnt++; unlock_ractor_set(); rb_ractor_chains_register(r); + + if (r->newobj_cache) { + VM_ASSERT(r == ruby_single_main_ractor); + } + else { + r->newobj_cache = rb_gc_ractor_cache_alloc(); + } } static VALUE @@ -2129,9 +2144,10 @@ vm_remove_ractor(rb_vm_t *vm, rb_ractor_t *cr) } vm->ractor.cnt--; - /* Clear the cached freelist to prevent a memory leak. */ - rb_gc_ractor_newobj_cache_clear(&cr->newobj_cache); - rb_gc_ractor_newobj_cache_clear(&cr->newobj_borrowing_cache); + rb_gc_ractor_cache_free(cr->newobj_cache); + rb_gc_ractor_cache_free(cr->newobj_borrowing_cache); + cr->newobj_cache = NULL; + cr->newobj_borrowing_cache = NULL; ractor_status_set(cr, ractor_terminated); @@ -2171,6 +2187,7 @@ rb_ractor_main_alloc(void) r->loc = Qnil; r->name = Qnil; r->pub.self = Qnil; + r->newobj_cache = rb_gc_ractor_cache_alloc(); ruby_single_main_ractor = r; return r; @@ -3409,6 +3426,13 @@ rb_ractor_shareable_p_continue(VALUE obj) } #if RACTOR_CHECK_MODE > 0 +void +rb_ractor_setup_belonging(VALUE obj) +{ + rb_ractor_t *r = rb_current_allocating_ractor(); + rb_ractor_setup_belonging_to(obj, rb_ractor_id(r)); +} + static enum obj_traverse_iterator_result reset_belonging_enter(VALUE obj) { diff --git a/ractor_core.h b/ractor_core.h index c47628a1d0d735..34b72ab42f5d46 100644 --- a/ractor_core.h +++ b/ractor_core.h @@ -213,8 +213,8 @@ struct rb_ractor_struct { VALUE verbose; VALUE debug; - rb_ractor_newobj_cache_t newobj_cache; - rb_ractor_newobj_cache_t newobj_borrowing_cache; + void* newobj_cache; + void* newobj_borrowing_cache; struct { rb_nativethread_lock_t lock; rb_ractor_t *lock_owner; @@ -336,12 +336,13 @@ bool rb_ractor_main_p_(void); void rb_ractor_mark_object_ary_init(rb_ractor_t *r); void rb_ractor_related_objects_mark(void *ptr); void rb_ractor_update_references(void *ptr); -void rb_ractor_finish_marking(void); void rb_ractor_atfork(rb_vm_t *vm, rb_thread_t *th); VALUE rb_ractor_ensure_shareable(VALUE obj, VALUE name); RUBY_SYMBOL_EXPORT_BEGIN +void rb_ractor_finish_marking(void); + bool rb_ractor_shareable_p_continue(VALUE obj); // THIS FUNCTION SHOULD NOT CALL WHILE INCREMENTAL MARKING!! @@ -481,13 +482,6 @@ rb_ractor_setup_belonging_to(VALUE obj, uint32_t rid) RACTOR_BELONGING_ID(obj) = rid; } -static inline void -rb_ractor_setup_belonging(VALUE obj) -{ - rb_ractor_t *r = rb_current_allocating_ractor(); - rb_ractor_setup_belonging_to(obj, rb_ractor_id(r)); -} - static inline uint32_t rb_ractor_belonging(VALUE obj) { diff --git a/range.c b/range.c index bc4c5dde9aa88b..63ef6b62d71dbf 100644 --- a/range.c +++ b/range.c @@ -2439,8 +2439,12 @@ range_overlap(VALUE range, VALUE other) * A beginless range may be used to slice an array: * * a = [1, 2, 3, 4] - * r = (..2) # => nil...2 - * a[r] # => [1, 2] + * # Include the third array element in the slice + * r = (..2) # => nil..2 + * a[r] # => [1, 2, 3] + * # Exclude the third array element from the slice + * r = (...2) # => nil...2 + * a[r] # => [1, 2] * * \Method +each+ for a beginless range raises an exception. * diff --git a/re.c b/re.c index 0553b4dff725da..4d5ff33e48fcdd 100644 --- a/re.c +++ b/re.c @@ -1296,6 +1296,54 @@ match_byteoffset(VALUE match, VALUE n) } +/* + * call-seq: + * bytebegin(n) -> integer + * bytebegin(name) -> integer + * + * :include: doc/matchdata/bytebegin.rdoc + * + */ + +static VALUE +match_bytebegin(VALUE match, VALUE n) +{ + int i = match_backref_number(match, n); + struct re_registers *regs = RMATCH_REGS(match); + + match_check(match); + backref_number_check(regs, i); + + if (BEG(i) < 0) + return Qnil; + return LONG2NUM(BEG(i)); +} + + +/* + * call-seq: + * byteend(n) -> integer + * byteend(name) -> integer + * + * :include: doc/matchdata/byteend.rdoc + * + */ + +static VALUE +match_byteend(VALUE match, VALUE n) +{ + int i = match_backref_number(match, n); + struct re_registers *regs = RMATCH_REGS(match); + + match_check(match); + backref_number_check(regs, i); + + if (BEG(i) < 0) + return Qnil; + return LONG2NUM(END(i)); +} + + /* * call-seq: * begin(n) -> integer @@ -4844,6 +4892,8 @@ Init_Regexp(void) rb_define_method(rb_cMatch, "length", match_size, 0); rb_define_method(rb_cMatch, "offset", match_offset, 1); rb_define_method(rb_cMatch, "byteoffset", match_byteoffset, 1); + rb_define_method(rb_cMatch, "bytebegin", match_bytebegin, 1); + rb_define_method(rb_cMatch, "byteend", match_byteend, 1); rb_define_method(rb_cMatch, "begin", match_begin, 1); rb_define_method(rb_cMatch, "end", match_end, 1); rb_define_method(rb_cMatch, "match", match_nth, 1); diff --git a/ruby.c b/ruby.c index 84a336e88f8a87..3c78ada9364a48 100644 --- a/ruby.c +++ b/ruby.c @@ -1441,16 +1441,6 @@ proc_long_options(ruby_cmdline_options_t *opt, const char *s, long argc, char ** else if (is_option_with_arg("source-encoding", Qfalse, Qtrue)) { set_source_encoding_once(opt, s, 0); } -#endif -#if defined(USE_SHARED_GC) && USE_SHARED_GC - else if (is_option_with_arg("gc-library", Qfalse, Qfalse)) { - // no-op - // Handled by ruby_load_external_gc_from_argv - - if (!dln_supported_p()) { - rb_warn("--gc-library is ignored because this executable file can't load extension libraries"); - } - } #endif else if (strcmp("version", s) == 0) { if (envopt) goto noenvopt_long; diff --git a/sample/find_calls.rb b/sample/prism/find_calls.rb similarity index 100% rename from sample/find_calls.rb rename to sample/prism/find_calls.rb diff --git a/sample/find_comments.rb b/sample/prism/find_comments.rb similarity index 100% rename from sample/find_comments.rb rename to sample/prism/find_comments.rb diff --git a/sample/locate_nodes.rb b/sample/prism/locate_nodes.rb similarity index 100% rename from sample/locate_nodes.rb rename to sample/prism/locate_nodes.rb diff --git a/sample/visit_nodes.rb b/sample/prism/visit_nodes.rb similarity index 100% rename from sample/visit_nodes.rb rename to sample/prism/visit_nodes.rb diff --git a/spec/bundler/bundler/resolver/candidate_spec.rb b/spec/bundler/bundler/resolver/candidate_spec.rb index f7b378d32b506a..aefad3316ed173 100644 --- a/spec/bundler/bundler/resolver/candidate_spec.rb +++ b/spec/bundler/bundler/resolver/candidate_spec.rb @@ -2,20 +2,19 @@ RSpec.describe Bundler::Resolver::Candidate do it "compares fine" do - version1 = described_class.new("1.12.5", specs: [Gem::Specification.new("foo", "1.12.5") {|s| s.platform = Gem::Platform::RUBY }]) - version2 = described_class.new("1.12.5") # passing no specs creates a platform specific candidate, so sorts higher + version1 = described_class.new("1.12.5", priority: -1) + version2 = described_class.new("1.12.5", priority: 1) - expect(version2 >= version1).to be true + expect(version2 > version1).to be true - expect(version1.generic! == version2.generic!).to be true - expect(version1.platform_specific! == version2.platform_specific!).to be true + version1 = described_class.new("1.12.5") + version2 = described_class.new("1.12.5") - expect(version1.platform_specific! >= version2.generic!).to be true - expect(version2.platform_specific! >= version1.generic!).to be true + expect(version2 == version1).to be true - version1 = described_class.new("1.12.5", specs: [Gem::Specification.new("foo", "1.12.5") {|s| s.platform = Gem::Platform::RUBY }]) - version2 = described_class.new("1.12.5", specs: [Gem::Specification.new("foo", "1.12.5") {|s| s.platform = Gem::Platform::X64_LINUX }]) + version1 = described_class.new("1.12.5", priority: 1) + version2 = described_class.new("1.12.5", priority: -1) - expect(version2 >= version1).to be true + expect(version2 < version1).to be true end end diff --git a/spec/bundler/commands/add_spec.rb b/spec/bundler/commands/add_spec.rb index f549e4f8bc44f3..9eb9c876ca79b8 100644 --- a/spec/bundler/commands/add_spec.rb +++ b/spec/bundler/commands/add_spec.rb @@ -28,6 +28,15 @@ end end + context "when Gemfile is empty, and frozen mode is set" do + it "shows error" do + gemfile 'source "https://gem.repo2"' + bundle "add bar", raise_on_error: false, env: { "BUNDLE_FROZEN" => "true" } + + expect(err).to include("Frozen mode is set, but there's no lockfile") + end + end + describe "without version specified" do it "version requirement becomes ~> major.minor.patch when resolved version is < 1.0" do bundle "add 'bar'" diff --git a/spec/bundler/commands/cache_spec.rb b/spec/bundler/commands/cache_spec.rb index f6ea29a6b924d0..ab8eb06838fcdf 100644 --- a/spec/bundler/commands/cache_spec.rb +++ b/spec/bundler/commands/cache_spec.rb @@ -479,8 +479,7 @@ source "https://gem.repo1" gem "platform_specific" G - run "require 'platform_specific' ; puts PLATFORM_SPECIFIC" - expect(out).to eq("1.0.0 RUBY") + expect(the_bundle).to include_gems("platform_specific 1.0 ruby") end it "does not update the cache if --no-cache is passed" do diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb index e47aea368f6dc9..dc92aab35dcb73 100644 --- a/spec/bundler/commands/install_spec.rb +++ b/spec/bundler/commands/install_spec.rb @@ -249,15 +249,12 @@ describe "with a gem that installs multiple platforms" do it "installs gems for the local platform as first choice" do - skip "version is 1.0, not 1.0.0" if Gem.win_platform? - install_gemfile <<-G source "https://gem.repo1" gem "platform_specific" G - run "require 'platform_specific' ; puts PLATFORM_SPECIFIC" - expect(out).to eq("1.0.0 #{Bundler.local_platform}") + expect(the_bundle).to include_gems("platform_specific 1.0 #{Bundler.local_platform}") end it "falls back on plain ruby" do @@ -267,8 +264,7 @@ gem "platform_specific" G - run "require 'platform_specific' ; puts PLATFORM_SPECIFIC" - expect(out).to eq("1.0.0 RUBY") + expect(the_bundle).to include_gems("platform_specific 1.0 ruby") end it "installs gems for java" do @@ -278,8 +274,7 @@ gem "platform_specific" G - run "require 'platform_specific' ; puts PLATFORM_SPECIFIC" - expect(out).to eq("1.0.0 JAVA") + expect(the_bundle).to include_gems("platform_specific 1.0 java") end it "installs gems for windows" do @@ -290,8 +285,7 @@ gem "platform_specific" G - run "require 'platform_specific' ; puts PLATFORM_SPECIFIC" - expect(out).to eq("1.0 x86-mswin32") + expect(the_bundle).to include_gems("platform_specific 1.0 x86-mswin32") end end @@ -1188,19 +1182,19 @@ def run build_gem "nokogiri", "1.12.4" do |s| s.platform = "x86_64-darwin" - s.add_runtime_dependency "racca", "~> 1.4" + s.add_dependency "racca", "~> 1.4" end build_gem "nokogiri", "1.12.4" do |s| s.platform = "x86_64-linux" - s.add_runtime_dependency "racca", "~> 1.4" + s.add_dependency "racca", "~> 1.4" end build_gem "crass", "1.0.6" build_gem "loofah", "2.12.0" do |s| - s.add_runtime_dependency "crass", "~> 1.0.2" - s.add_runtime_dependency "nokogiri", ">= 1.5.9" + s.add_dependency "crass", "~> 1.0.2" + s.add_dependency "nokogiri", ">= 1.5.9" end end diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb index 235c2d6756c0ed..450436372e013c 100644 --- a/spec/bundler/commands/lock_spec.rb +++ b/spec/bundler/commands/lock_spec.rb @@ -629,6 +629,62 @@ expect(lockfile.platforms).to match_array(default_platform_list(java, x86_mingw32)) end + it "supports adding new platforms, when most specific locked platform is not the current platform, and current resolve is not compatible with the target platform" do + simulate_platform "arm64-darwin-23" do + build_repo4 do + build_gem "foo" do |s| + s.platform = "arm64-darwin" + end + + build_gem "foo" do |s| + s.platform = "java" + end + end + + gemfile <<-G + source "https://gem.repo4" + + gem "foo" + G + + lockfile <<-L + GEM + remote: https://gem.repo4/ + specs: + foo (1.0-arm64-darwin) + + PLATFORMS + arm64-darwin + + DEPENDENCIES + foo + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "lock --add-platform java" + + expect(lockfile).to eq <<~L + GEM + remote: https://gem.repo4/ + specs: + foo (1.0-arm64-darwin) + foo (1.0-java) + + PLATFORMS + arm64-darwin + java + + DEPENDENCIES + foo + + BUNDLED WITH + #{Bundler::VERSION} + L + end + end + it "supports adding new platforms with force_ruby_platform = true" do lockfile <<-L GEM @@ -1467,54 +1523,34 @@ #{Bundler::VERSION} L - bundle "lock", raise_on_error: false - - expect(err).to eq <<~ERR.strip + expected_error = <<~ERR.strip Could not find compatible versions - Because every version of activemodel depends on activesupport = 6.0.4 - and rails >= 7.0.2.3, < 7.0.3.1 depends on activesupport = 7.0.2.3, - every version of activemodel is incompatible with rails >= 7.0.2.3, < 7.0.3.1. - And because rails >= 7.0.2.3, < 7.0.3.1 depends on activemodel = 7.0.2.3, - rails >= 7.0.2.3, < 7.0.3.1 cannot be used. - (1) So, because rails >= 7.0.3.1, < 7.0.4 depends on activemodel = 7.0.3.1 - and rails >= 7.0.4 depends on activemodel = 7.0.4, - rails >= 7.0.2.3 requires activemodel = 7.0.3.1 OR = 7.0.4. + Because rails >= 7.0.4 depends on activemodel = 7.0.4 + and rails >= 7.0.3.1, < 7.0.4 depends on activemodel = 7.0.3.1, + rails >= 7.0.3.1 requires activemodel = 7.0.3.1 OR = 7.0.4. + (1) So, because rails >= 7.0.2.3, < 7.0.3.1 depends on activemodel = 7.0.2.3 + and every version of activemodel depends on activesupport = 6.0.4, + rails >= 7.0.2.3 requires activesupport = 6.0.4. - Because rails >= 7.0.2.3, < 7.0.3.1 depends on activemodel = 7.0.2.3 + Because rails >= 7.0.2.3, < 7.0.3.1 depends on activesupport = 7.0.2.3 and rails >= 7.0.3.1, < 7.0.4 depends on activesupport = 7.0.3.1, - rails >= 7.0.2.3, < 7.0.4 requires activemodel = 7.0.2.3 or activesupport = 7.0.3.1. - And because rails >= 7.0.4 depends on activesupport = 7.0.4 - and every version of activemodel depends on activesupport = 6.0.4, - activemodel != 7.0.2.3 is incompatible with rails >= 7.0.2.3. - And because rails >= 7.0.2.3 requires activemodel = 7.0.3.1 OR = 7.0.4 (1), + rails >= 7.0.2.3, < 7.0.4 requires activesupport = 7.0.2.3 OR = 7.0.3.1. + And because rails >= 7.0.4 depends on activesupport = 7.0.4, + rails >= 7.0.2.3 requires activesupport = 7.0.2.3 OR = 7.0.3.1 OR = 7.0.4. + And because rails >= 7.0.2.3 requires activesupport = 6.0.4 (1), rails >= 7.0.2.3 cannot be used. So, because Gemfile depends on rails >= 7.0.2.3, version solving has failed. ERR - lockfile lockfile.gsub(/PLATFORMS\n #{local_platform}/m, "PLATFORMS\n #{lockfile_platforms("ruby")}") - bundle "lock", raise_on_error: false + expect(err).to eq(expected_error) - expect(err).to eq <<~ERR.strip - Could not find compatible versions + lockfile lockfile.gsub(/PLATFORMS\n #{local_platform}/m, "PLATFORMS\n #{lockfile_platforms("ruby")}") - Because rails >= 7.0.3.1, < 7.0.4 depends on activemodel = 7.0.3.1 - and rails >= 7.0.2.3, < 7.0.3.1 depends on activemodel = 7.0.2.3, - rails >= 7.0.2.3, < 7.0.4 requires activemodel = 7.0.2.3 OR = 7.0.3.1. - And because every version of activemodel depends on activesupport = 6.0.4, - rails >= 7.0.2.3, < 7.0.4 requires activesupport = 6.0.4. - Because rails >= 7.0.3.1, < 7.0.4 depends on activesupport = 7.0.3.1 - and rails >= 7.0.2.3, < 7.0.3.1 depends on activesupport = 7.0.2.3, - rails >= 7.0.2.3, < 7.0.4 requires activesupport = 7.0.2.3 OR = 7.0.3.1. - Thus, rails >= 7.0.2.3, < 7.0.4 cannot be used. - And because rails >= 7.0.4 depends on activemodel = 7.0.4, - rails >= 7.0.2.3 requires activemodel = 7.0.4. - So, because activemodel = 7.0.4 could not be found in rubygems repository https://gem.repo4/ or installed locally - and Gemfile depends on rails >= 7.0.2.3, - version solving has failed. - ERR + bundle "lock", raise_on_error: false + expect(err).to eq(expected_error) end it "does not accidentally resolves to prereleases" do @@ -1663,7 +1699,8 @@ nokogiri (1.14.2) PLATFORMS - #{lockfile_platforms} + ruby + x86_64-linux DEPENDENCIES foo! diff --git a/spec/bundler/install/deploy_spec.rb b/spec/bundler/install/deploy_spec.rb index 23d2bd39badb33..dfb352f1706d02 100644 --- a/spec/bundler/install/deploy_spec.rb +++ b/spec/bundler/install/deploy_spec.rb @@ -388,7 +388,7 @@ expect(out).not_to include("* myrack-obama") end - it "explodes if you remove a gem and don't check in the lockfile" do + it "explodes if you replace a gem and don't check in the lockfile" do gemfile <<-G source "https://gem.repo1" gem "activesupport" @@ -402,6 +402,17 @@ expect(err).not_to include("You have changed in the Gemfile") end + it "explodes if you remove a gem and don't check in the lockfile" do + gemfile 'source "https://gem.repo1"' + + bundle "config set --local deployment true" + bundle :install, raise_on_error: false + expect(err).to include("Some dependencies were deleted") + expect(err).to include("frozen mode") + expect(err).to include("You have deleted from the Gemfile:\n* myrack") + expect(err).not_to include("You have changed in the Gemfile") + end + it "explodes if you add a source" do gemfile <<-G source "https://gem.repo1" diff --git a/spec/bundler/install/gemfile/force_ruby_platform_spec.rb b/spec/bundler/install/gemfile/force_ruby_platform_spec.rb index e715cc93031a01..1e6136951908d6 100644 --- a/spec/bundler/install/gemfile/force_ruby_platform_spec.rb +++ b/spec/bundler/install/gemfile/force_ruby_platform_spec.rb @@ -5,23 +5,17 @@ before do build_repo4 do # Build a gem with platform specific versions - build_gem("platform_specific") do |s| - s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 RUBY'" - end + build_gem("platform_specific") build_gem("platform_specific") do |s| s.platform = Bundler.local_platform - s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 #{Bundler.local_platform}'" end # Build the exact same gem with a different name to compare using vs not using the option - build_gem("platform_specific_forced") do |s| - s.write "lib/platform_specific_forced.rb", "PLATFORM_SPECIFIC_FORCED = '1.0.0 RUBY'" - end + build_gem("platform_specific_forced") build_gem("platform_specific_forced") do |s| s.platform = Bundler.local_platform - s.write "lib/platform_specific_forced.rb", "PLATFORM_SPECIFIC_FORCED = '1.0.0 #{Bundler.local_platform}'" end end end @@ -34,8 +28,8 @@ gem "platform_specific" G - expect(the_bundle).to include_gems "platform_specific_forced 1.0.0 RUBY" - expect(the_bundle).to include_gems "platform_specific 1.0.0 #{Bundler.local_platform}" + expect(the_bundle).to include_gems "platform_specific_forced 1.0 ruby" + expect(the_bundle).to include_gems "platform_specific 1.0 #{Bundler.local_platform}" end it "still respects a global `force_ruby_platform` config" do @@ -46,23 +40,20 @@ gem "platform_specific" G - expect(the_bundle).to include_gems "platform_specific_forced 1.0.0 RUBY" - expect(the_bundle).to include_gems "platform_specific 1.0.0 RUBY" + expect(the_bundle).to include_gems "platform_specific_forced 1.0 ruby" + expect(the_bundle).to include_gems "platform_specific 1.0 ruby" end end context "when also a transitive dependency" do before do build_repo4 do - build_gem("depends_on_platform_specific") {|s| s.add_runtime_dependency "platform_specific" } + build_gem("depends_on_platform_specific") {|s| s.add_dependency "platform_specific" } - build_gem("platform_specific") do |s| - s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 RUBY'" - end + build_gem("platform_specific") build_gem("platform_specific") do |s| s.platform = Bundler.local_platform - s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 #{Bundler.local_platform}'" end end end @@ -75,7 +66,7 @@ gem "platform_specific", :force_ruby_platform => true G - expect(the_bundle).to include_gems "platform_specific 1.0.0 RUBY" + expect(the_bundle).to include_gems "platform_specific 1.0 ruby" end end @@ -83,23 +74,18 @@ before do build_repo4 do build_gem("depends_on_platform_specific") do |s| - s.add_runtime_dependency "platform_specific" - s.write "lib/depends_on_platform_specific.rb", "DEPENDS_ON_PLATFORM_SPECIFIC = '1.0.0 RUBY'" + s.add_dependency "platform_specific" end build_gem("depends_on_platform_specific") do |s| - s.add_runtime_dependency "platform_specific" + s.add_dependency "platform_specific" s.platform = Bundler.local_platform - s.write "lib/depends_on_platform_specific.rb", "DEPENDS_ON_PLATFORM_SPECIFIC = '1.0.0 #{Bundler.local_platform}'" end - build_gem("platform_specific") do |s| - s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 RUBY'" - end + build_gem("platform_specific") build_gem("platform_specific") do |s| s.platform = Bundler.local_platform - s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 #{Bundler.local_platform}'" end end end @@ -111,11 +97,11 @@ gem "depends_on_platform_specific", :force_ruby_platform => true G - expect(the_bundle).to include_gems "depends_on_platform_specific 1.0.0 RUBY" - expect(the_bundle).to include_gems "platform_specific 1.0.0 #{Bundler.local_platform}" + expect(the_bundle).to include_gems "depends_on_platform_specific 1.0 ruby" + expect(the_bundle).to include_gems "platform_specific 1.0 #{Bundler.local_platform}" end - it "reinstalls the ruby variant when a platform specific variant is already installed, the lockile has only RUBY platform, and :force_ruby_platform is used in the Gemfile" do + it "reinstalls the ruby variant when a platform specific variant is already installed, the lockile has only ruby platform, and :force_ruby_platform is used in the Gemfile" do lockfile <<-L GEM remote: https://gem.repo4 @@ -140,7 +126,7 @@ gem "platform_specific", :force_ruby_platform => true G - expect(the_bundle).to include_gems "platform_specific 1.0.0 RUBY" + expect(the_bundle).to include_gems "platform_specific 1.0 ruby" end end end diff --git a/spec/bundler/install/gemfile/gemspec_spec.rb b/spec/bundler/install/gemfile/gemspec_spec.rb index b8843de0d27a96..2f3eb3236c61c4 100644 --- a/spec/bundler/install/gemfile/gemspec_spec.rb +++ b/spec/bundler/install/gemfile/gemspec_spec.rb @@ -210,7 +210,7 @@ def x64_mingw_checksums(checksums) G bundle "update --bundler", artifice: "compact_index", verbose: true - expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 JAVA" + expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 java" end it "should evaluate the gemspec in its directory" do @@ -461,7 +461,7 @@ def x64_mingw_checksums(checksums) context "as a runtime dependency" do it "keeps all platform dependencies in the lockfile" do - expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY" + expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 ruby" checksums = checksums_section_when_existing do |c| c.no_checksum "foo", "1.0" @@ -502,7 +502,7 @@ def x64_mingw_checksums(checksums) let(:platform_specific_type) { :development } it "keeps all platform dependencies in the lockfile" do - expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY" + expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 ruby" checksums = checksums_section_when_existing do |c| c.no_checksum "foo", "1.0" @@ -544,7 +544,7 @@ def x64_mingw_checksums(checksums) let(:dependency) { "indirect_platform_specific" } it "keeps all platform dependencies in the lockfile" do - expect(the_bundle).to include_gems "foo 1.0", "indirect_platform_specific 1.0", "platform_specific 1.0 RUBY" + expect(the_bundle).to include_gems "foo 1.0", "indirect_platform_specific 1.0", "platform_specific 1.0 ruby" checksums = checksums_section_when_existing do |c| c.no_checksum "foo", "1.0" diff --git a/spec/bundler/install/gemfile/platform_spec.rb b/spec/bundler/install/gemfile/platform_spec.rb index 75ec08ba3622a7..dd7ee83c92a1d9 100644 --- a/spec/bundler/install/gemfile/platform_spec.rb +++ b/spec/bundler/install/gemfile/platform_spec.rb @@ -47,7 +47,7 @@ gem "platform_specific" G - expect(the_bundle).to include_gems "platform_specific 1.0 JAVA" + expect(the_bundle).to include_gems "platform_specific 1.0 java" end it "pulls the pure ruby version on jruby if the java platform is not present in the lockfile and bundler is run in frozen mode", :jruby_only do @@ -72,7 +72,7 @@ gem "platform_specific" G - expect(the_bundle).to include_gems "platform_specific 1.0 RUBY" + expect(the_bundle).to include_gems "platform_specific 1.0 ruby" end context "on universal Rubies" do @@ -80,15 +80,12 @@ build_repo4 do build_gem "darwin_single_arch" do |s| s.platform = "ruby" - s.write "lib/darwin_single_arch.rb", "DARWIN_SINGLE_ARCH = '1.0 RUBY'" end build_gem "darwin_single_arch" do |s| s.platform = "arm64-darwin" - s.write "lib/darwin_single_arch.rb", "DARWIN_SINGLE_ARCH = '1.0 arm64-darwin'" end build_gem "darwin_single_arch" do |s| s.platform = "x86_64-darwin" - s.write "lib/darwin_single_arch.rb", "DARWIN_SINGLE_ARCH = '1.0 x86_64-darwin'" end end end @@ -158,7 +155,7 @@ gem "nokogiri" G - expect(the_bundle).to include_gems "nokogiri 1.4.2 JAVA", "weakling 0.0.3" + expect(the_bundle).to include_gems "nokogiri 1.4.2 java", "weakling 0.0.3" simulate_new_machine bundle "config set --local force_ruby_platform true" @@ -171,7 +168,7 @@ simulate_platform "java" bundle "install" - expect(the_bundle).to include_gems "nokogiri 1.4.2 JAVA", "weakling 0.0.3" + expect(the_bundle).to include_gems "nokogiri 1.4.2 java", "weakling 0.0.3" end it "does not keep unneeded platforms for gems that are used" do @@ -179,16 +176,16 @@ build_gem "empyrean", "0.1.0" build_gem "coderay", "1.1.2" build_gem "method_source", "0.9.0" - build_gem("spoon", "0.0.6") {|s| s.add_runtime_dependency "ffi" } + build_gem("spoon", "0.0.6") {|s| s.add_dependency "ffi" } build_gem "pry", "0.11.3" do |s| s.platform = "java" - s.add_runtime_dependency "coderay", "~> 1.1.0" - s.add_runtime_dependency "method_source", "~> 0.9.0" - s.add_runtime_dependency "spoon", "~> 0.0" + s.add_dependency "coderay", "~> 1.1.0" + s.add_dependency "method_source", "~> 0.9.0" + s.add_dependency "spoon", "~> 0.0" end build_gem "pry", "0.11.3" do |s| - s.add_runtime_dependency "coderay", "~> 1.1.0" - s.add_runtime_dependency "method_source", "~> 0.9.0" + s.add_dependency "coderay", "~> 1.1.0" + s.add_dependency "method_source", "~> 0.9.0" end build_gem("ffi", "1.9.23") {|s| s.platform = "java" } build_gem("ffi", "1.9.23") @@ -332,11 +329,11 @@ update_repo2 do build_gem "fspath", "3" build_gem "image_optim_pack", "1.2.3" do |s| - s.add_runtime_dependency "fspath", ">= 2.1", "< 4" + s.add_dependency "fspath", ">= 2.1", "< 4" end build_gem "image_optim_pack", "1.2.3" do |s| s.platform = "universal-darwin" - s.add_runtime_dependency "fspath", "< 4", ">= 2.1" + s.add_dependency "fspath", "< 4", ">= 2.1" end end @@ -400,7 +397,7 @@ checksums.checksum gem_repo1, "platform_specific", "1.0" - expect(the_bundle).to include_gem "platform_specific 1.0 RUBY" + expect(the_bundle).to include_gem "platform_specific 1.0 ruby" expect(lockfile).to eq <<~G GEM diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb index 89a2719b4ec372..ffeedad1971768 100644 --- a/spec/bundler/install/gemfile/specific_platform_spec.rb +++ b/spec/bundler/install/gemfile/specific_platform_spec.rb @@ -55,7 +55,7 @@ end end - it "understands that a non-platform specific gem in a new lockfile locked only to RUBY doesn't necessarily mean installing the non-specific variant" do + it "understands that a non-platform specific gem in a new lockfile locked only to ruby doesn't necessarily mean installing the non-specific variant" do simulate_platform "x86_64-darwin-15" do setup_multiplatform_gem @@ -113,7 +113,7 @@ end end - context "when running on a legacy lockfile locked only to RUBY" do + context "when running on a legacy lockfile locked only to ruby" do around do |example| build_repo4 do build_gem "nokogiri", "1.3.10" @@ -148,12 +148,12 @@ simulate_platform "arm64-darwin-22", &example end - it "still installs the generic RUBY variant if necessary" do + it "still installs the generic ruby variant if necessary" do bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } expect(out).to include("Installing nokogiri 1.3.10") end - it "still installs the generic RUBY variant if necessary, even in frozen mode" do + it "still installs the generic ruby variant if necessary, even in frozen mode" do bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s, "BUNDLE_FROZEN" => "true" } expect(out).to include("Installing nokogiri 1.3.10") end @@ -166,7 +166,7 @@ build_gem("libv8", "8.4.255.0") {|s| s.platform = "universal-darwin" } build_gem("mini_racer", "1.0.0") do |s| - s.add_runtime_dependency "libv8" + s.add_dependency "libv8" end end @@ -459,7 +459,7 @@ expect(err).to include(error_message).once end - it "does not generate a lockfile if RUBY platform is forced and some gem has no RUBY variant available" do + it "does not generate a lockfile if ruby platform is forced and some gem has no ruby variant available" do build_repo4 do build_gem("sorbet-static", "0.5.9889") {|s| s.platform = Gem::Platform.local } end @@ -480,15 +480,15 @@ ERROR end - it "automatically fixes the lockfile if RUBY platform is locked and some gem has no RUBY variant available" do + it "automatically fixes the lockfile if ruby platform is locked and some gem has no ruby variant available" do build_repo4 do build_gem("sorbet-static-and-runtime", "0.5.10160") do |s| - s.add_runtime_dependency "sorbet", "= 0.5.10160" - s.add_runtime_dependency "sorbet-runtime", "= 0.5.10160" + s.add_dependency "sorbet", "= 0.5.10160" + s.add_dependency "sorbet-runtime", "= 0.5.10160" end build_gem("sorbet", "0.5.10160") do |s| - s.add_runtime_dependency "sorbet-static", "= 0.5.10160" + s.add_dependency "sorbet-static", "= 0.5.10160" end build_gem("sorbet-runtime", "0.5.10160") @@ -558,7 +558,7 @@ L end - it "automatically fixes the lockfile if both RUBY platform and a more specific platform are locked, and some gem has no RUBY variant available" do + it "automatically fixes the lockfile if both ruby platform and a more specific platform are locked, and some gem has no ruby variant available" do build_repo4 do build_gem "nokogiri", "1.12.0" build_gem "nokogiri", "1.12.0" do |s| @@ -632,15 +632,15 @@ L end - it "automatically fixes the lockfile if only RUBY platform is locked and some gem has no RUBY variant available" do + it "automatically fixes the lockfile if only ruby platform is locked and some gem has no ruby variant available" do build_repo4 do build_gem("sorbet-static-and-runtime", "0.5.10160") do |s| - s.add_runtime_dependency "sorbet", "= 0.5.10160" - s.add_runtime_dependency "sorbet-runtime", "= 0.5.10160" + s.add_dependency "sorbet", "= 0.5.10160" + s.add_dependency "sorbet-runtime", "= 0.5.10160" end build_gem("sorbet", "0.5.10160") do |s| - s.add_runtime_dependency "sorbet-static", "= 0.5.10160" + s.add_dependency "sorbet-static", "= 0.5.10160" end build_gem("sorbet-runtime", "0.5.10160") @@ -844,11 +844,11 @@ end end - it "automatically fixes the lockfile if locked only to RUBY, and some locked specs don't meed locked dependencies" do + it "automatically fixes the lockfile if locked only to ruby, and some locked specs don't meed locked dependencies" do simulate_platform "x86_64-linux" do build_repo4 do build_gem("ibandit", "0.7.0") do |s| - s.add_runtime_dependency "i18n", "~> 0.7.0" + s.add_dependency "i18n", "~> 0.7.0" end build_gem("i18n", "0.7.0.beta1") @@ -1119,11 +1119,11 @@ end end - it "automatically fixes the lockfile when only RUBY platform locked, and adding a dependency with subdependencies not valid for RUBY" do + it "automatically fixes the lockfile when only ruby platform locked, and adding a dependency with subdependencies not valid for ruby" do simulate_platform "x86_64-linux" do build_repo4 do build_gem("sorbet", "0.5.10160") do |s| - s.add_runtime_dependency "sorbet-static", "= 0.5.10160" + s.add_dependency "sorbet-static", "= 0.5.10160" end build_gem("sorbet-static", "0.5.10160") do |s| @@ -1521,6 +1521,102 @@ end end + it "does not remove generic platform gems locked for a specific platform from lockfile when unlocking an unrelated gem" do + build_repo4 do + build_gem "ffi" + + build_gem "ffi" do |s| + s.platform = "x86_64-linux" + end + + build_gem "nokogiri" + end + + gemfile <<~G + source "https://gem.repo4" + + gem "ffi" + gem "nokogiri" + G + + original_lockfile = <<~L + GEM + remote: https://gem.repo4/ + specs: + ffi (1.0) + nokogiri (1.0) + + PLATFORMS + x86_64-linux + + DEPENDENCIES + ffi + nokogiri + + BUNDLED WITH + #{Bundler::VERSION} + L + + lockfile original_lockfile + + simulate_platform "x86_64-linux" do + bundle "lock --update nokogiri" + + expect(lockfile).to eq(original_lockfile) + end + end + + it "does not remove generic platform gems locked for a specific platform from lockfile when unlocking an unrelated gem, and variants for other platform also locked" do + build_repo4 do + build_gem "ffi" + + build_gem "ffi" do |s| + s.platform = "x86_64-linux" + end + + build_gem "ffi" do |s| + s.platform = "java" + end + + build_gem "nokogiri" + end + + gemfile <<~G + source "https://gem.repo4" + + gem "ffi" + gem "nokogiri" + G + + original_lockfile = <<~L + GEM + remote: https://gem.repo4/ + specs: + ffi (1.0) + ffi (1.0-java) + nokogiri (1.0) + + PLATFORMS + java + x86_64-linux + + DEPENDENCIES + ffi + nokogiri + + BUNDLED WITH + #{Bundler::VERSION} + L + + lockfile original_lockfile + + simulate_platform "x86_64-linux" do + bundle "lock --update nokogiri" + + expect(lockfile).to eq(original_lockfile) + end + end + private def setup_multiplatform_gem @@ -1546,7 +1642,7 @@ def setup_multiplatform_gem_with_different_dependencies_per_platform build_gem("facter", "2.4.6") build_gem("facter", "2.4.6") do |s| s.platform = "universal-darwin" - s.add_runtime_dependency "CFPropertyList" + s.add_dependency "CFPropertyList" end build_gem("CFPropertyList") end diff --git a/spec/bundler/install/gems/compact_index_spec.rb b/spec/bundler/install/gems/compact_index_spec.rb index 30273a6eaed268..39064e3b80b219 100644 --- a/spec/bundler/install/gems/compact_index_spec.rb +++ b/spec/bundler/install/gems/compact_index_spec.rb @@ -61,7 +61,7 @@ it "should handle case sensitivity conflicts" do build_repo4(build_compact_index: false) do build_gem "myrack", "1.0" do |s| - s.add_runtime_dependency("Myrack", "0.1") + s.add_dependency("Myrack", "0.1") end build_gem "Myrack", "0.1" end diff --git a/spec/bundler/install/gems/standalone_spec.rb b/spec/bundler/install/gems/standalone_spec.rb index 86589796edd287..08529f110ca55c 100644 --- a/spec/bundler/install/gems/standalone_spec.rb +++ b/spec/bundler/install/gems/standalone_spec.rb @@ -143,12 +143,14 @@ before do skip "Does not work on old Windows Rubies" if Gem.ruby_version < Gem::Version.new("3.2") && Gem.win_platform? - realworld_system_gems "tsort --version 0.1.0" + necessary_system_gems = ["tsort --version 0.1.0"] + necessary_system_gems += ["etc --version 1.4.3"] if Gem.ruby_version >= Gem::Version.new("3.3.2") && Gem.win_platform? + realworld_system_gems(*necessary_system_gems) - necessary_system_gems = ["optparse --version 0.1.1", "psych --version 3.3.2", "logger --version 1.4.3", "etc --version 1.2.0", "stringio --version 3.1.0"] - necessary_system_gems += ["shellwords --version 0.1.0", "base64 --version 0.1.0", "resolv --version 0.2.1"] if Gem.rubygems_version < Gem::Version.new("3.3.a") - necessary_system_gems += ["yaml --version 0.1.1"] if Gem.rubygems_version < Gem::Version.new("3.4.a") - realworld_system_gems(*necessary_system_gems, path: scoped_gem_path(bundled_app("bundle"))) + necessary_gems_in_bundle_path = ["optparse --version 0.1.1", "psych --version 3.3.2", "logger --version 1.4.3", "etc --version 1.4.3", "stringio --version 3.1.0"] + necessary_gems_in_bundle_path += ["shellwords --version 0.1.0", "base64 --version 0.1.0", "resolv --version 0.2.1"] if Gem.rubygems_version < Gem::Version.new("3.3.a") + necessary_gems_in_bundle_path += ["yaml --version 0.1.1"] if Gem.rubygems_version < Gem::Version.new("3.4.a") + realworld_system_gems(*necessary_gems_in_bundle_path, path: scoped_gem_path(bundled_app("bundle"))) build_gem "foo", "1.0.0", to_system: true, default: true do |s| s.add_dependency "bar" @@ -293,7 +295,7 @@ it "outputs a helpful error message" do expect(err).to include("You have one or more invalid gemspecs that need to be fixed.") - expect(err).to include("bar 1.0 has an invalid gemspec") + expect(err).to include("bar.gemspec is not valid") end end diff --git a/spec/bundler/install/gemspecs_spec.rb b/spec/bundler/install/gemspecs_spec.rb index 41232bd47b0a8f..7629870db27b47 100644 --- a/spec/bundler/install/gemspecs_spec.rb +++ b/spec/bundler/install/gemspecs_spec.rb @@ -41,7 +41,7 @@ spec = Gem::Specification.new do |s| s.name = "myrack" s.version = "1.0.0" - s.add_runtime_dependency "activesupport", "2.3.2" + s.add_dependency "activesupport", "2.3.2" end f.write spec.to_ruby end diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb index 703b535e2e7ca8..1f7faecd61f643 100644 --- a/spec/bundler/lock/lockfile_spec.rb +++ b/spec/bundler/lock/lockfile_spec.rb @@ -1321,7 +1321,7 @@ G end - it "adds compatible platform specific variants to the lockfile, even if resolution fallback to RUBY due to some other incompatible platform specific variant" do + it "adds compatible platform specific variants to the lockfile, even if resolution fallback to ruby due to some other incompatible platform specific variant" do simulate_platform "arm64-darwin-23" do build_repo4 do build_gem "google-protobuf", "3.25.1" diff --git a/spec/bundler/realworld/edgecases_spec.rb b/spec/bundler/realworld/edgecases_spec.rb index 94ca3554b11bf0..dc15c56c795243 100644 --- a/spec/bundler/realworld/edgecases_spec.rb +++ b/spec/bundler/realworld/edgecases_spec.rb @@ -198,18 +198,7 @@ def rubygems_version(name, requirement) expect(lockfile).to include(rubygems_version("paperclip", "~> 5.1.0")) end - it "outputs a helpful error message when gems have invalid gemspecs", rubygems: "< 3.3.16" do - install_gemfile <<-G, standalone: true, raise_on_error: false, env: { "BUNDLE_FORCE_RUBY_PLATFORM" => "1" } - source 'https://rubygems.org' - gem "resque-scheduler", "2.2.0" - gem "redis-namespace", "1.6.0" # for a consistent resolution including ruby 2.3.0 - gem "ruby2_keywords", "0.0.5" - G - expect(err).to include("You have one or more invalid gemspecs that need to be fixed.") - expect(err).to include("resque-scheduler 2.2.0 has an invalid gemspec") - end - - it "outputs a helpful warning when gems have a gemspec with invalid `require_paths`", rubygems: ">= 3.3.16" do + it "outputs a helpful warning when gems have a gemspec with invalid `require_paths`" do install_gemfile <<-G, standalone: true, env: { "BUNDLE_FORCE_RUBY_PLATFORM" => "1" } source 'https://rubygems.org' gem "resque-scheduler", "2.2.0" diff --git a/spec/bundler/runtime/platform_spec.rb b/spec/bundler/runtime/platform_spec.rb index b9e568b3b51e68..93b81cf2fc9458 100644 --- a/spec/bundler/runtime/platform_spec.rb +++ b/spec/bundler/runtime/platform_spec.rb @@ -181,7 +181,7 @@ bundle "install" expect(out).to include("Fetching nokogiri 1.4.2 (java)") - expect(the_bundle).to include_gems "nokogiri 1.4.2 JAVA" + expect(the_bundle).to include_gems "nokogiri 1.4.2 java" end it "will add the resolve for the current platform" do @@ -222,7 +222,7 @@ bundle "install" - expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 RUBY" + expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 ruby" end it "allows specifying only-ruby-platform" do @@ -236,7 +236,7 @@ bundle "install" - expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 RUBY" + expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 ruby" end it "allows specifying only-ruby-platform even if the lockfile is locked to a specific compatible platform" do @@ -250,7 +250,7 @@ bundle "install" - expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 RUBY" + expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 ruby" end it "doesn't pull platform specific gems on truffleruby", :truffleruby_only do @@ -259,10 +259,10 @@ gem "platform_specific" G - expect(the_bundle).to include_gems "platform_specific 1.0 RUBY" + expect(the_bundle).to include_gems "platform_specific 1.0 ruby" end - it "doesn't pull platform specific gems on truffleruby (except when whitelisted) even if lockfile was generated with an older version that declared RUBY as platform", :truffleruby_only do + it "doesn't pull platform specific gems on truffleruby (except when whitelisted) even if lockfile was generated with an older version that declared ruby as platform", :truffleruby_only do gemfile <<-G source "https://gem.repo1" gem "platform_specific" @@ -286,7 +286,7 @@ bundle "install" - expect(the_bundle).to include_gems "platform_specific 1.0 RUBY" + expect(the_bundle).to include_gems "platform_specific 1.0 ruby" simulate_platform "x86_64-linux" do build_repo4 do @@ -348,7 +348,7 @@ bundle "install" - expect(the_bundle).to include_gems "platform_specific 1.0 RUBY" + expect(the_bundle).to include_gems "platform_specific 1.0 ruby" end it "pulls platform specific gems correctly on musl" do @@ -380,7 +380,7 @@ bundle "install" - expect(the_bundle).to include_gems "platform_specific 1.0 RUBY" + expect(the_bundle).to include_gems "platform_specific 1.0 ruby" expect(the_bundle).to not_include_gems "nokogiri" end end diff --git a/spec/bundler/runtime/setup_spec.rb b/spec/bundler/runtime/setup_spec.rb index de09b0cdbba092..ededaab410a018 100644 --- a/spec/bundler/runtime/setup_spec.rb +++ b/spec/bundler/runtime/setup_spec.rb @@ -1305,7 +1305,7 @@ def lock_with(ruby_version = nil) s.files = Dir["lib/**/*.rb"] s.author = 'no one' - s.add_runtime_dependency 'digest' + s.add_dependency 'digest' end G end @@ -1397,7 +1397,7 @@ def lock_with(ruby_version = nil) describe "default gem activation" do let(:exemptions) do exempts = %w[did_you_mean bundler uri pathname] - exempts << "etc" if Gem.ruby_version < Gem::Version.new("3.2") && Gem.win_platform? + exempts << "etc" if (Gem.ruby_version < Gem::Version.new("3.2") || Gem.ruby_version >= Gem::Version.new("3.3.2")) && Gem.win_platform? exempts << "set" unless Gem.rubygems_version >= Gem::Version.new("3.2.6") exempts << "tsort" unless Gem.rubygems_version >= Gem::Version.new("3.2.31") exempts << "error_highlight" # added in Ruby 3.1 as a default gem diff --git a/spec/bundler/support/builders.rb b/spec/bundler/support/builders.rb index ec37b562e46806..cb7f419f423a58 100644 --- a/spec/bundler/support/builders.rb +++ b/spec/bundler/support/builders.rb @@ -92,74 +92,60 @@ def build_repo1 build_gem "platform_specific" do |s| s.platform = Gem::Platform.local - s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 #{Gem::Platform.local}'" end build_gem "platform_specific" do |s| s.platform = "java" - s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 JAVA'" end build_gem "platform_specific" do |s| s.platform = "ruby" - s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 RUBY'" end build_gem "platform_specific" do |s| s.platform = "x86-mswin32" - s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0 x86-mswin32'" end build_gem "platform_specific" do |s| s.platform = "x64-mswin64" - s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0 x64-mswin64'" end build_gem "platform_specific" do |s| s.platform = "x86-mingw32" - s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0 x86-mingw32'" end build_gem "platform_specific" do |s| s.platform = "x64-mingw32" - s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0 x64-mingw32'" end build_gem "platform_specific" do |s| s.platform = "x64-mingw-ucrt" - s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0 x64-mingw-ucrt'" end build_gem "platform_specific" do |s| s.platform = "x86-darwin-100" - s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 x86-darwin-100'" end build_gem "only_java", "1.0" do |s| s.platform = "java" - s.write "lib/only_java.rb", "ONLY_JAVA = '1.0.0 JAVA'" end build_gem "only_java", "1.1" do |s| s.platform = "java" - s.write "lib/only_java.rb", "ONLY_JAVA = '1.1.0 JAVA'" end build_gem "nokogiri", "1.4.2" build_gem "nokogiri", "1.4.2" do |s| s.platform = "java" - s.write "lib/nokogiri.rb", "NOKOGIRI = '1.4.2 JAVA'" s.add_dependency "weakling", ">= 0.0.3" end build_gem "laduradura", "5.15.2" build_gem "laduradura", "5.15.2" do |s| s.platform = "java" - s.write "lib/laduradura.rb", "LADURADURA = '5.15.2 JAVA'" end build_gem "laduradura", "5.15.3" do |s| s.platform = "java" - s.write "lib/laduradura.rb", "LADURADURA = '5.15.2 JAVA'" end build_gem "weakling", "0.0.3" @@ -259,7 +245,8 @@ def check_test_gems! end def update_repo(path, build_compact_index: true) - if path == gem_repo1 && caller.first.split(" ").last == "`build_repo`" + exempted_caller = Gem.ruby_version >= Gem::Version.new("3.4.0.dev") ? "#{Module.nesting.first}#build_repo" : "build_repo" + if path == gem_repo1 && caller_locations(1, 1).first.label != exempted_caller raise "Updating gem_repo1 is unsupported -- use gem_repo2 instead" end return unless block_given? diff --git a/spec/bundler/support/matchers.rb b/spec/bundler/support/matchers.rb index 0f027dcf0406fc..ed11e3ba523070 100644 --- a/spec/bundler/support/matchers.rb +++ b/spec/bundler/support/matchers.rb @@ -118,6 +118,7 @@ def indent(string, padding = 4, indent_character = " ") opts[:raise_on_error] = false @errors = names.map do |full_name| name, version, platform = full_name.split(/\s+/) + platform ||= "ruby" require_path = name.tr("-", "/") version_const = name == "bundler" ? "Bundler::VERSION" : Spec::Builders.constantize(name) source_const = "#{Spec::Builders.constantize(name)}_SOURCE" @@ -127,6 +128,7 @@ def indent(string, padding = 4, indent_character = " ") require '#{require_path}' actual_version, actual_platform = #{version_const}.split(/\s+/, 2) + actual_platform ||= "ruby" unless Gem::Version.new(actual_version) == Gem::Version.new('#{version}') puts actual_version exit 64 @@ -150,7 +152,7 @@ def indent(string, padding = 4, indent_character = " ") end if exitstatus == 65 actual_platform = out.split("\n").last - next "#{name} was expected to be of platform #{platform || "ruby"} but was #{actual_platform || "ruby"}" + next "#{name} was expected to be of platform #{platform} but was #{actual_platform}" end if exitstatus == 66 actual_source = out.split("\n").last diff --git a/spec/ruby/core/hash/new_spec.rb b/spec/ruby/core/hash/new_spec.rb index 6279815fd6e660..5ae3e1f98d6205 100644 --- a/spec/ruby/core/hash/new_spec.rb +++ b/spec/ruby/core/hash/new_spec.rb @@ -34,7 +34,7 @@ -> { Hash.new(nil) { 0 } }.should raise_error(ArgumentError) end - ruby_version_is "3.3" do + ruby_version_is "3.3"..."3.4" do it "emits a deprecation warning if keyword arguments are passed" do -> { Hash.new(unknown: true) }.should complain( Regexp.new(Regexp.escape("Calling Hash.new with keyword arguments is deprecated and will be removed in Ruby 3.4; use Hash.new({ key: value }) instead")) @@ -46,4 +46,22 @@ Hash.new({ unknown: true }).default.should == { unknown: true } end end + + ruby_version_is "3.4" do + it "accepts a capacity: argument" do + Hash.new(5, capacity: 42).default.should == 5 + Hash.new(capacity: 42).default.should == nil + (Hash.new(capacity: 42) { 1 }).default_proc.should_not == nil + end + + it "ignores negative capacity" do + -> { Hash.new(capacity: -42) }.should_not raise_error + end + + it "raises an error if unknown keyword arguments are passed" do + -> { Hash.new(unknown: true) }.should raise_error(ArgumentError) + -> { Hash.new(1, unknown: true) }.should raise_error(ArgumentError) + -> { Hash.new(unknown: true) { 0 } }.should raise_error(ArgumentError) + end + end end diff --git a/spec/ruby/core/kernel/format_spec.rb b/spec/ruby/core/kernel/format_spec.rb index e8b031e4801465..1d0c000c158761 100644 --- a/spec/ruby/core/kernel/format_spec.rb +++ b/spec/ruby/core/kernel/format_spec.rb @@ -12,4 +12,36 @@ it "is accessible as a module function" do Kernel.format("%s", "hello").should == "hello" end + + describe "when $VERBOSE is true" do + it "warns if too many arguments are passed" do + code = <<~RUBY + $VERBOSE = true + format("test", 1) + RUBY + + ruby_exe(code, args: "2>&1").should include("warning: too many arguments for format string") + end + + it "does not warns if too many keyword arguments are passed" do + code = <<~RUBY + $VERBOSE = true + format("test %{test}", test: 1, unused: 2) + RUBY + + ruby_exe(code, args: "2>&1").should_not include("warning") + end + + ruby_bug "#20593", ""..."3.4" do + it "doesn't warns if keyword arguments are passed and none are used" do + code = <<~RUBY + $VERBOSE = true + format("test", test: 1) + format("test", {}) + RUBY + + ruby_exe(code, args: "2>&1").should_not include("warning") + end + end + end end diff --git a/spec/ruby/core/process/daemon_spec.rb b/spec/ruby/core/process/daemon_spec.rb index 70ffd1b32015dd..36825771d1d783 100644 --- a/spec/ruby/core/process/daemon_spec.rb +++ b/spec/ruby/core/process/daemon_spec.rb @@ -2,6 +2,9 @@ require_relative 'fixtures/common' platform_is_not :windows do + # macOS 15 beta is not working this examples + return if /darwin/ =~ RUBY_PLATFORM && /15/ =~ `sw_vers -productVersion` + describe :process_daemon_keep_stdio_open_false, shared: true do it "redirects stdout to /dev/null" do @daemon.invoke("keep_stdio_open_false_stdout", @object).should == "" diff --git a/spec/ruby/core/thread/thread_variable_get_spec.rb b/spec/ruby/core/thread/thread_variable_get_spec.rb index 67017771fdf9c0..e8e03e3d18008e 100644 --- a/spec/ruby/core/thread/thread_variable_get_spec.rb +++ b/spec/ruby/core/thread/thread_variable_get_spec.rb @@ -41,13 +41,20 @@ @t.thread_variable_get(:a).should be_nil end - it "does not raise a TypeError if the key is neither Symbol nor String, nor responds to #to_str" do - @t.thread_variable_get(123).should be_nil + it "raises a TypeError if the key is neither Symbol nor String when thread variables are already set" do + @t.thread_variable_set(:a, 49) + -> { @t.thread_variable_get(123) }.should raise_error(TypeError, "123 is not a symbol") end - it "does not try to convert the key with #to_sym" do - key = mock('key') - key.should_not_receive(:to_sym) - @t.thread_variable_get(key).should be_nil + ruby_version_is '3.4' do + it "raises a TypeError if the key is neither Symbol nor String when no thread variables are set" do + -> { @t.thread_variable_get(123) }.should raise_error(TypeError, "123 is not a symbol") + end + + it "raises a TypeError if the key is neither Symbol nor String without calling #to_sym" do + key = mock('key') + key.should_not_receive(:to_sym) + -> { @t.thread_variable_get(key) }.should raise_error(TypeError, "#{key.inspect} is not a symbol") + end end end diff --git a/spec/ruby/core/thread/thread_variable_spec.rb b/spec/ruby/core/thread/thread_variable_spec.rb index d64e6ec63dce43..465b985365c722 100644 --- a/spec/ruby/core/thread/thread_variable_spec.rb +++ b/spec/ruby/core/thread/thread_variable_spec.rb @@ -41,13 +41,20 @@ @t.thread_variable?(:a).should be_false end - it "does not raise a TypeError if the key is neither Symbol nor String, nor responds to #to_str" do - @t.thread_variable?(123).should be_false + it "raises a TypeError if the key is neither Symbol nor String when thread variables are already set" do + @t.thread_variable_set(:a, 49) + -> { @t.thread_variable?(123) }.should raise_error(TypeError, "123 is not a symbol") end - it "does not try to convert the key with #to_sym" do - key = mock('key') - key.should_not_receive(:to_sym) - @t.thread_variable?(key).should be_false + ruby_version_is '3.4' do + it "raises a TypeError if the key is neither Symbol nor String when no thread variables are set" do + -> { @t.thread_variable?(123) }.should raise_error(TypeError, "123 is not a symbol") + end + + it "raises a TypeError if the key is neither Symbol nor String without calling #to_sym" do + key = mock('key') + key.should_not_receive(:to_sym) + -> { @t.thread_variable?(key) }.should raise_error(TypeError, "#{key.inspect} is not a symbol") + end end end diff --git a/spec/ruby/core/tracepoint/inspect_spec.rb b/spec/ruby/core/tracepoint/inspect_spec.rb index d0e633c49b0443..6cc2ebe2434280 100644 --- a/spec/ruby/core/tracepoint/inspect_spec.rb +++ b/spec/ruby/core/tracepoint/inspect_spec.rb @@ -67,7 +67,7 @@ def trace_point_spec_test_return end trace_point_spec_test_return end - ruby_version_is("3.4") { line = "(?:#{line}|#{line-1})" } + ruby_version_is("3.4") { line -= 1 } inspect.should =~ /\A#\z/ end diff --git a/spec/ruby/library/uri/shared/parse.rb b/spec/ruby/library/uri/shared/parse.rb index 87e1ee933ea52c..c5057b6c4bbb22 100644 --- a/spec/ruby/library/uri/shared/parse.rb +++ b/spec/ruby/library/uri/shared/parse.rb @@ -192,8 +192,15 @@ file.should be_kind_of(URI::Generic) end - it "raises errors on malformed URIs" do - -> { @object.parse('http://a_b:80/') }.should raise_error(URI::InvalidURIError) - -> { @object.parse('http://a_b/') }.should raise_error(URI::InvalidURIError) + if URI::DEFAULT_PARSER == URI::RFC2396_Parser + it "raises errors on malformed URIs" do + -> { @object.parse('http://a_b:80/') }.should raise_error(URI::InvalidURIError) + -> { @object.parse('http://a_b/') }.should raise_error(URI::InvalidURIError) + end + elsif URI::DEFAULT_PARSER == URI::RFC3986_Parser + it "does not raise errors on URIs contained underscore" do + -> { @object.parse('http://a_b:80/') }.should_not raise_error(URI::InvalidURIError) + -> { @object.parse('http://a_b/') }.should_not raise_error(URI::InvalidURIError) + end end end diff --git a/sprintf.c b/sprintf.c index 98877d25d27d05..4fa531d53e6659 100644 --- a/sprintf.c +++ b/sprintf.c @@ -937,7 +937,7 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt) rb_str_tmp_frozen_release(orig, fmt); /* XXX - We cannot validate the number of arguments if (digit)$ style used. */ - if (posarg >= 0 && nextarg < argc) { + if (posarg >= 0 && nextarg < argc && !(argc == 2 && RB_TYPE_P(argv[1], T_HASH))) { const char *mesg = "too many arguments for format string"; if (RTEST(ruby_debug)) rb_raise(rb_eArgError, "%s", mesg); if (RTEST(ruby_verbose)) rb_warn("%s", mesg); diff --git a/template/Makefile.in b/template/Makefile.in index 2d232d7925284a..e0de5f74dbd504 100644 --- a/template/Makefile.in +++ b/template/Makefile.in @@ -67,6 +67,9 @@ RUBY_VERSION_NAME = @RUBY_VERSION_NAME@ UNIVERSAL_ARCHNAMES = @UNIVERSAL_ARCHNAMES@ BUILTIN_BINARY = @X_BUILTIN_BINARY@ +BUILTIN_GC = default +shared_gc_dir = @shared_gc_dir@ + TESTUI = console TESTS = INSTALLDOC = @INSTALLDOC@ @@ -225,7 +228,6 @@ AR = @AR@ ARFLAGS = @ARFLAGS@$(empty) RANLIB = @RANLIB@ AS = @AS@ -ASFLAGS = @ASFLAGS@ $(ARCH_FLAG) $(INCFLAGS) IFCHANGE = $(SHELL) $(tooldir)/ifchange OBJDUMP = @OBJDUMP@ OBJCOPY = @OBJCOPY@ @@ -443,13 +445,17 @@ $(srcdir)/enc/jis/props.h: enc/jis/props.kwd $(CP) $@ $(?:.kwd=.h.blt); \ fi +gc_impl.$(OBJEXT): gc/$(BUILTIN_GC).c probes.h + @$(ECHO) compiling $(srcdir)/gc/$(BUILTIN_GC).c + $(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(srcdir)/gc/$(BUILTIN_GC).c + .c.$(OBJEXT): @$(ECHO) compiling $< $(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $< .$(ASMEXT).$(OBJEXT): @$(ECHO) assembling $< - $(Q) $(CC) $(ASFLAGS) -DSYMBOL_PREFIX=$(SYMBOL_PREFIX) -o $@ -c $< + $(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -DSYMBOL_PREFIX=$(SYMBOL_PREFIX) -c $< .c.$(ASMEXT): @$(ECHO) translating $< @@ -479,8 +485,9 @@ probes.stamp: $(DTRACE_REBUILD_OBJS) probes.$(OBJEXT): $(srcdir)/probes.d $(DTRACE_REBUILD:yes=probes.stamp) @$(ECHO) processing probes in object files - $(Q) $(RM) $@ - $(Q) $(DTRACE) -G -C $(INCFLAGS) -s $(srcdir)/probes.d -o $@ $(DTRACE_REBUILD_OBJS) + @# n.b. the dtrace script looks at the $CFLAGS environment variable to decide + @# how to assemble probes.o; so we need to actually _export_ $(CFLAGS) + $(Q) CC="$(CC)" CFLAGS="$(CFLAGS) $(XCFLAGS) $(CPPFLAGS)" $(DTRACE) -G -C $(INCFLAGS) -s $(srcdir)/probes.d -o $@ $(DTRACE_REBUILD_OBJS) # DTrace static library hacks described here: # https://marc.info/?l=opensolaris-dtrace-discuss&m=114761203110734&w=4 diff --git a/template/limits.c.tmpl b/template/limits.c.tmpl index de35354829478f..d1e5b44592e851 100644 --- a/template/limits.c.tmpl +++ b/template/limits.c.tmpl @@ -51,11 +51,26 @@ # include #endif +/* + * Document-const: LIMITS + * + * A Hash with the bounds of numeric types available to the \C compiler + * used to build Ruby. To access this constant, first run + * require 'rbconfig/sizeof'. + * + * require 'rbconfig/sizeof' + * RUBY_PLATFORM # => "x64-mingw-ucrt" + * RbConfig::LIMITS.fetch_values('FIXNUM_MAX', 'LONG_MAX') + * # => [1073741823, 2147483647] + * + */ + void Init_limits(void) { VALUE h = rb_hash_new(); - rb_define_const(rb_define_module("RbConfig"), "LIMITS", h); + VALUE mRbConfig = rb_define_module("RbConfig"); + rb_define_const(mRbConfig, "LIMITS", h); #ifdef HAVE_LONG_LONG #ifndef ULLONG_MAX diff --git a/template/sizes.c.tmpl b/template/sizes.c.tmpl index 31060f5fea676d..909902fd172ad7 100644 --- a/template/sizes.c.tmpl +++ b/template/sizes.c.tmpl @@ -25,12 +25,27 @@ conditions = { #endif % end + +/* + * Document-const: SIZEOF + * + * A Hash with the byte size of \C types available to the compiler + * used to build Ruby. To access this constant, first run + * require 'rbconfig/sizeof'. + * + * require 'rbconfig/sizeof' + * RUBY_PLATFORM # => "x64-mingw-ucrt" + * RbConfig::SIZEOF.fetch_values('long', 'void*') # => [4, 8] + * + */ + extern void Init_limits(void); void Init_sizeof(void) { VALUE s = rb_hash_new(); - rb_define_const(rb_define_module("RbConfig"), "SIZEOF", s); + VALUE mRbConfig = rb_define_module("RbConfig"); + rb_define_const(mRbConfig, "SIZEOF", s); #define DEFINE(type, size) rb_hash_aset(s, rb_str_new_cstr(#type), INT2FIX(SIZEOF_##size)) #define DEFINE_SIZE(type) rb_hash_aset(s, rb_str_new_cstr(#type), INT2FIX(sizeof(type))) diff --git a/test/-ext-/bug_reporter/test_bug_reporter.rb b/test/-ext-/bug_reporter/test_bug_reporter.rb index 76f913c27522f7..00743d0e28a30c 100644 --- a/test/-ext-/bug_reporter/test_bug_reporter.rb +++ b/test/-ext-/bug_reporter/test_bug_reporter.rb @@ -5,6 +5,8 @@ class TestBugReporter < Test::Unit::TestCase def test_bug_reporter_add + pend "macOS 15 beta is not working with this test" if /darwin/ =~ RUBY_PLATFORM && /15/ =~ `sw_vers -productVersion` + omit "flaky with RJIT" if JITSupport.rjit_enabled? description = RUBY_DESCRIPTION.sub(/\+PRISM /, '') description = description.sub(/\+RJIT /, '') unless JITSupport.rjit_force_enabled? diff --git a/test/.excludes-prism/TestSetTraceFunc.rb b/test/.excludes-prism/TestSetTraceFunc.rb deleted file mode 100644 index 9c79044c775fcc..00000000000000 --- a/test/.excludes-prism/TestSetTraceFunc.rb +++ /dev/null @@ -1,2 +0,0 @@ -exclude(:test_return, "[Bug #20457]") -exclude(:test_return2, "[Bug #20457]") diff --git a/test/error_highlight/test_error_highlight.rb b/test/error_highlight/test_error_highlight.rb index 9af530b69f6069..5f4c386990c76d 100644 --- a/test/error_highlight/test_error_highlight.rb +++ b/test/error_highlight/test_error_highlight.rb @@ -5,13 +5,6 @@ require "tempfile" class ErrorHighlightTest < Test::Unit::TestCase - # 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 self.compiling_with_prism? - RubyVM::InstructionSequence.compile("").to_a[4][:parser] == :prism - end - class DummyFormatter def self.message_for(corrections) "" @@ -876,11 +869,11 @@ def test_COLON2_4 end end - if ErrorHighlight.const_get(:Spotter).const_get(:OPT_GETCONSTANT_PATH) && !compiling_with_prism? + if ErrorHighlight.const_get(:Spotter).const_get(:OPT_GETCONSTANT_PATH) def test_COLON2_5 # Unfortunately, we cannot identify which `NotDefined` caused the NameError assert_error_message(NameError, <<~END) do -uninitialized constant ErrorHighlightTest::NotDefined + uninitialized constant ErrorHighlightTest::NotDefined END ErrorHighlightTest::NotDefined::NotDefined @@ -1342,7 +1335,11 @@ def test_spot_with_backtrace_location def test_spot_with_node omit unless RubyVM::AbstractSyntaxTree.respond_to?(:node_id_for_backtrace_location) - omit if ErrorHighlightTest.compiling_with_prism? + + # 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. + omit if RubyVM::InstructionSequence.compile("").to_a[4][:parser] == :prism begin raise_name_error diff --git a/test/irb/command/test_cd.rb b/test/irb/command/test_cd.rb new file mode 100644 index 00000000000000..4537286f73c46c --- /dev/null +++ b/test/irb/command/test_cd.rb @@ -0,0 +1,65 @@ +require "tempfile" +require_relative "../helper" + +module TestIRB + class CDTest < IntegrationTestCase + def setup + super + + write_ruby <<~'RUBY' + class Foo + class Bar + def bar + "this is bar" + end + end + + def foo + "this is foo" + end + end + + binding.irb + RUBY + end + + def test_cd + out = run_ruby_file do + type "cd Foo" + type "ls" + type "cd Bar" + type "ls" + type "cd .." + type "exit" + end + + assert_match(/irb\(Foo\):002>/, out) + assert_match(/Foo#methods: foo/, out) + assert_match(/irb\(Foo::Bar\):004>/, out) + assert_match(/Bar#methods: bar/, out) + assert_match(/irb\(Foo\):006>/, out) + end + + def test_cd_moves_top_level_with_no_args + out = run_ruby_file do + type "cd Foo" + type "cd Bar" + type "cd" + type "exit" + end + + assert_match(/irb\(Foo::Bar\):003>/, out) + assert_match(/irb\(main\):004>/, out) + end + + def test_cd_with_error + out = run_ruby_file do + type "cd Baz" + type "exit" + end + + assert_match(/Error: uninitialized constant Baz/, out) + assert_match(/irb\(main\):002>/, out) # the context should not change + end + end +end diff --git a/test/irb/helper.rb b/test/irb/helper.rb index acaf6277f3879d..ea2c6ef16a417e 100644 --- a/test/irb/helper.rb +++ b/test/irb/helper.rb @@ -49,6 +49,19 @@ def ruby_core? !Pathname(__dir__).join("../../", "irb.gemspec").exist? end + def setup_envs(home:) + @backup_home = ENV["HOME"] + ENV["HOME"] = home + @backup_xdg_config_home = ENV.delete("XDG_CONFIG_HOME") + @backup_irbrc = ENV.delete("IRBRC") + end + + def teardown_envs + ENV["HOME"] = @backup_home + ENV["XDG_CONFIG_HOME"] = @backup_xdg_config_home + ENV["IRBRC"] = @backup_irbrc + end + def save_encodings @default_encoding = [Encoding.default_external, Encoding.default_internal] @stdio_encodings = [STDIN, STDOUT, STDERR].map {|io| [io.external_encoding, io.internal_encoding] } diff --git a/test/irb/test_command.rb b/test/irb/test_command.rb index 30c3f5ca2c452f..567c3216ccec2f 100644 --- a/test/irb/test_command.rb +++ b/test/irb/test_command.rb @@ -15,9 +15,7 @@ def setup Dir.mkdir(@tmpdir) end Dir.chdir(@tmpdir) - @home_backup = ENV["HOME"] - ENV["HOME"] = @tmpdir - @xdg_config_home_backup = ENV.delete("XDG_CONFIG_HOME") + setup_envs(home: @tmpdir) save_encodings IRB.instance_variable_get(:@CONF).clear IRB.instance_variable_set(:@existing_rc_name_generators, nil) @@ -25,8 +23,7 @@ def setup end def teardown - ENV["XDG_CONFIG_HOME"] = @xdg_config_home_backup - ENV["HOME"] = @home_backup + teardown_envs Dir.chdir(@pwd) FileUtils.rm_rf(@tmpdir) restore_encodings diff --git a/test/irb/test_completion.rb b/test/irb/test_completion.rb index 5fe7952b3d70e6..c9a0eafa3d45bb 100644 --- a/test/irb/test_completion.rb +++ b/test/irb/test_completion.rb @@ -16,8 +16,14 @@ def doc_namespace(target, bind) class CommandCompletionTest < CompletionTest def test_command_completion - assert_include(IRB::RegexpCompletor.new.completion_candidates('', 'show_s', '', bind: binding), 'show_source') - assert_not_include(IRB::RegexpCompletor.new.completion_candidates(';', 'show_s', '', bind: binding), 'show_source') + completor = IRB::RegexpCompletor.new + binding.eval("some_var = 1") + # completion for help command's argument should only include command names + assert_include(completor.completion_candidates('help ', 's', '', bind: binding), 'show_source') + assert_not_include(completor.completion_candidates('help ', 's', '', bind: binding), 'some_var') + + assert_include(completor.completion_candidates('', 'show_s', '', bind: binding), 'show_source') + assert_not_include(completor.completion_candidates(';', 'show_s', '', bind: binding), 'show_source') end end diff --git a/test/irb/test_context.rb b/test/irb/test_context.rb index cd3f2c8f620a72..7ad8fd2fc4a84a 100644 --- a/test/irb/test_context.rb +++ b/test/irb/test_context.rb @@ -28,7 +28,6 @@ def teardown restore_encodings end - def test_eval_input verbose, $VERBOSE = $VERBOSE, nil input = TestInputMethod.new([ diff --git a/test/irb/test_history.rb b/test/irb/test_history.rb index 63be35fdaa2933..84f043892969d0 100644 --- a/test/irb/test_history.rb +++ b/test/irb/test_history.rb @@ -12,19 +12,14 @@ class HistoryTest < TestCase def setup @original_verbose, $VERBOSE = $VERBOSE, nil @tmpdir = Dir.mktmpdir("test_irb_history_") - @backup_home = ENV["HOME"] - @backup_xdg_config_home = ENV.delete("XDG_CONFIG_HOME") - @backup_irbrc = ENV.delete("IRBRC") + setup_envs(home: @tmpdir) @backup_default_external = Encoding.default_external - ENV["HOME"] = @tmpdir IRB.instance_variable_set(:@existing_rc_name_generators, nil) end def teardown IRB.instance_variable_set(:@existing_rc_name_generators, nil) - ENV["HOME"] = @backup_home - ENV["XDG_CONFIG_HOME"] = @backup_xdg_config_home - ENV["IRBRC"] = @backup_irbrc + teardown_envs Encoding.default_external = @backup_default_external $VERBOSE = @original_verbose FileUtils.rm_rf(@tmpdir) diff --git a/test/irb/test_init.rb b/test/irb/test_init.rb index c423fa112e5786..3e8d01c5ce03eb 100644 --- a/test/irb/test_init.rb +++ b/test/irb/test_init.rb @@ -270,17 +270,15 @@ def with_argv(argv) 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 + @home = Dir.mktmpdir + setup_envs(home: @home) super end def teardown super - ENV["IRBRC"] = @original_irbrc - ENV["HOME"] = @original_home + teardown_envs File.unlink(@irbrc) Dir.rmdir(@home) IRB.instance_variable_set(:@existing_rc_name_generators, nil) diff --git a/test/irb/test_type_completor.rb b/test/irb/test_type_completor.rb index 5ed8988b34928e..412d7c696d62d5 100644 --- a/test/irb/test_type_completor.rb +++ b/test/irb/test_type_completor.rb @@ -56,6 +56,11 @@ def test_empty_completion end def test_command_completion + binding.eval("some_var = 1") + # completion for help command's argument should only include command names + assert_include(@completor.completion_candidates('help ', 's', '', bind: binding), 'show_source') + assert_not_include(@completor.completion_candidates('help ', 's', '', bind: binding), 'some_var') + assert_include(@completor.completion_candidates('', 'show_s', '', bind: binding), 'show_source') assert_not_include(@completor.completion_candidates(';', 'show_s', '', bind: binding), 'show_source') end diff --git a/test/irb/test_workspace.rb b/test/irb/test_workspace.rb index 199ce95a37b0fa..ad515f91df3990 100644 --- a/test/irb/test_workspace.rb +++ b/test/irb/test_workspace.rb @@ -80,7 +80,6 @@ def test_code_around_binding_on_irb assert_equal(nil, workspace.code_around_binding) end - def test_toplevel_binding_local_variables bug17623 = '[ruby-core:102468]' bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : [] diff --git a/test/logger/test_logger.rb b/test/logger/test_logger.rb index 37d0f5862ab1b0..2023553a6c1c3b 100644 --- a/test/logger/test_logger.rb +++ b/test/logger/test_logger.rb @@ -113,6 +113,15 @@ def test_string_level assert_raise(ArgumentError) { @logger.level = 'something_wrong' } end + def test_reraise_write_errors + c = Object.new + e = Class.new(StandardError) + c.define_singleton_method(:write){|*| raise e} + c.define_singleton_method(:close){} + logger = Logger.new(c, :reraise_write_errors=>[e]) + assert_raise(e) { logger.warn('foo') } + end + def test_progname assert_nil(@logger.progname) @logger.progname = "name" diff --git a/test/net/http/test_http.rb b/test/net/http/test_http.rb index f0f1bc2d8f5a29..a49cc87e8d2454 100644 --- a/test/net/http/test_http.rb +++ b/test/net/http/test_http.rb @@ -254,6 +254,18 @@ def host.to_str; raise SocketError, "open failure"; end end end + def test_default_configuration + Net::HTTP.default_configuration = { open_timeout: 5 } + http = Net::HTTP.new 'hostname.example' + assert_equal 5, http.open_timeout + assert_equal 60, http.read_timeout + + http.open_timeout = 10 + assert_equal 10, http.open_timeout + ensure + Net::HTTP.default_configuration = nil + end + end module TestNetHTTP_version_1_1_methods @@ -442,7 +454,11 @@ def test_get2 def test_post start {|http| _test_post__base http + } + start {|http| _test_post__file http + } + start {|http| _test_post__no_data http } end @@ -629,10 +645,12 @@ def test_request # _test_request__range http # WEBrick does not support Range: header. _test_request__HEAD http _test_request__POST http - _test_request__stream_body http _test_request__uri http _test_request__uri_host http } + start {|http| + _test_request__stream_body http + } end def _test_request__GET(http) @@ -843,7 +861,13 @@ def test_set_form __EOM__ start {|http| _test_set_form_urlencoded(http, data.reject{|k,v|!v.is_a?(String)}) + } + start {|http| + @server.mount('/', lambda {|req, res| res.body = req.body }) _test_set_form_multipart(http, false, data, expected) + } + start {|http| + @server.mount('/', lambda {|req, res| res.body = req.body }) _test_set_form_multipart(http, true, data, expected) } } @@ -887,6 +911,7 @@ def test_set_form_with_file expected.sub!(//, filename) expected.sub!(//, $test_net_http_data) start {|http| + @server.mount('/', lambda {|req, res| res.body = req.body }) data.each{|k,v|v.rewind rescue nil} req = Net::HTTP::Post.new('/') req.set_form(data, 'multipart/form-data') @@ -902,10 +927,11 @@ def test_set_form_with_file header) assert_equal(expected, body) - data.each{|k,v|v.rewind rescue nil} - req['Transfer-Encoding'] = 'chunked' - res = http.request req - #assert_equal(expected, res.body) + # TODO: test with chunked + # data.each{|k,v|v.rewind rescue nil} + # req['Transfer-Encoding'] = 'chunked' + # res = http.request req + # assert_equal(expected, res.body) } } end @@ -984,7 +1010,7 @@ def logfile end def mount_proc(&block) - @server.mount('/continue', WEBrick::HTTPServlet::ProcHandler.new(block.to_proc)) + @server.mount('/continue', block.to_proc) end def test_expect_continue @@ -1039,7 +1065,7 @@ def test_expect_continue_error def test_expect_continue_error_before_body @log_tester = nil mount_proc {|req, res| - raise WEBrick::HTTPStatus::Forbidden + raise TestNetHTTPUtils::Forbidden } start {|http| uheader = {'content-type' => 'application/x-www-form-urlencoded', 'content-length' => '5', 'expect' => '100-continue'} @@ -1084,7 +1110,7 @@ def logfile end def mount_proc(&block) - @server.mount('/continue', WEBrick::HTTPServlet::ProcHandler.new(block.to_proc)) + @server.mount('/continue', block.to_proc) end def test_info @@ -1159,11 +1185,11 @@ def test_keep_alive_get_auto_retry end def test_keep_alive_reset_on_new_connection - # Using WEBrick's debug log output on accepting connection: + # Using debug log output on accepting connection: # # "[2021-04-29 20:36:46] DEBUG accept: 127.0.0.1:50674\n" @log_tester = nil - @server.logger.level = WEBrick::BasicLog::DEBUG + @logger_level = :debug start {|http| res = http.get('/') diff --git a/test/net/http/test_https.rb b/test/net/http/test_https.rb index cf297f37554ac1..e860c8745ed1f7 100644 --- a/test/net/http/test_https.rb +++ b/test/net/http/test_https.rb @@ -167,18 +167,16 @@ def test_session_reuse def test_session_reuse_but_expire # 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 http.cert_store = TEST_STORE - http.ssl_timeout = -1 + http.ssl_timeout = 1 http.start http.get("/") http.finish - + sleep 1.25 http.start http.get("/") @@ -240,27 +238,6 @@ def test_certificate_verify_failure http.request_get("/") {|res| } } assert_match(/certificate verify failed/, ex.message) - unless /mswin|mingw/ =~ RUBY_PLATFORM - # on Windows, Errno::ECONNRESET will be raised, and it'll be eaten by - # WEBrick - @log_tester = lambda {|log| - assert_equal(1, log.length) - assert_match(/ERROR OpenSSL::SSL::SSLError:/, log[0]) - } - end - end - - def test_identity_verify_failure - # the certificate's subject has CN=localhost - http = Net::HTTP.new(HOST_IP, config("port")) - http.use_ssl = true - http.cert_store = TEST_STORE - @log_tester = lambda {|_| } - ex = assert_raise(OpenSSL::SSL::SSLError){ - http.request_get("/") {|res| } - } - re_msg = /certificate verify failed|hostname \"#{HOST_IP}\" does not match/ - assert_match(re_msg, ex.message) end def test_timeout_during_SSL_handshake @@ -295,7 +272,7 @@ def test_min_version end def test_max_version - http = Net::HTTP.new(HOST_IP, config("port")) + http = Net::HTTP.new(HOST, config("port")) http.use_ssl = true http.max_version = :SSL2 http.verify_callback = Proc.new do |preverify_ok, store_ctx| @@ -310,3 +287,43 @@ def test_max_version end end if defined?(OpenSSL::SSL) + +class TestNetHTTPSIdentityVerifyFailure < Test::Unit::TestCase + include TestNetHTTPUtils + + def self.read_fixture(key) + File.read(File.expand_path("../fixtures/#{key}", __dir__)) + end + + HOST = 'localhost' + HOST_IP = '127.0.0.1' + CA_CERT = OpenSSL::X509::Certificate.new(read_fixture("cacert.pem")) + SERVER_KEY = OpenSSL::PKey.read(read_fixture("server.key")) + SERVER_CERT = OpenSSL::X509::Certificate.new(read_fixture("server.crt")) + DHPARAMS = OpenSSL::PKey::DH.new(read_fixture("dhparams.pem")) + TEST_STORE = OpenSSL::X509::Store.new.tap {|s| s.add_cert(CA_CERT) } + + CONFIG = { + 'host' => HOST_IP, + 'proxy_host' => nil, + 'proxy_port' => nil, + 'ssl_enable' => true, + 'ssl_certificate' => SERVER_CERT, + 'ssl_private_key' => SERVER_KEY, + 'ssl_tmp_dh_callback' => proc { DHPARAMS }, + } + + def test_identity_verify_failure + # the certificate's subject has CN=localhost + http = Net::HTTP.new(HOST_IP, config("port")) + http.use_ssl = true + http.cert_store = TEST_STORE + @log_tester = lambda {|_| } + ex = assert_raise(OpenSSL::SSL::SSLError){ + http.request_get("/") {|res| } + sleep 0.5 + } + re_msg = /certificate verify failed|hostname \"#{HOST_IP}\" does not match/ + assert_match(re_msg, ex.message) + end +end if defined?(OpenSSL::SSL) diff --git a/test/net/http/test_https_proxy.rb b/test/net/http/test_https_proxy.rb index 4c2a92ccd62803..f4c6aa0b6a015d 100644 --- a/test/net/http/test_https_proxy.rb +++ b/test/net/http/test_https_proxy.rb @@ -43,5 +43,52 @@ def test_https_proxy_authentication assert_join_threads([client_thread, server_thread]) } end -end if defined?(OpenSSL) + + def read_fixture(key) + File.read(File.expand_path("../fixtures/#{key}", __dir__)) + end + + def test_https_proxy_ssl_connection + begin + OpenSSL + rescue LoadError + omit 'autoload problem. see [ruby-dev:45021][Bug #5786]' + end + + TCPServer.open("127.0.0.1", 0) {|tcpserver| + ctx = OpenSSL::SSL::SSLContext.new + ctx.key = OpenSSL::PKey.read(read_fixture("server.key")) + ctx.cert = OpenSSL::X509::Certificate.new(read_fixture("server.crt")) + serv = OpenSSL::SSL::SSLServer.new(tcpserver, ctx) + + _, port, _, _ = serv.addr + client_thread = Thread.new { + proxy = Net::HTTP.Proxy("127.0.0.1", port, 'user', 'password', true) + http = proxy.new("foo.example.org", 8000) + http.use_ssl = true + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + begin + http.start + rescue EOFError + end + } + server_thread = Thread.new { + sock = serv.accept + begin + proxy_request = sock.gets("\r\n\r\n") + assert_equal( + "CONNECT foo.example.org:8000 HTTP/1.1\r\n" + + "Host: foo.example.org:8000\r\n" + + "Proxy-Authorization: Basic dXNlcjpwYXNzd29yZA==\r\n" + + "\r\n", + proxy_request, + "[ruby-core:96672]") + ensure + sock.close + end + } + assert_join_threads([client_thread, server_thread]) + } + end +end if defined?(OpenSSL) diff --git a/test/net/http/utils.rb b/test/net/http/utils.rb index e343e16712e19d..39f4152ef37a1e 100644 --- a/test/net/http/utils.rb +++ b/test/net/http/utils.rb @@ -1,13 +1,232 @@ # frozen_string_literal: false -require 'webrick' -begin - require "webrick/https" -rescue LoadError - # SSL features cannot be tested -end -require 'webrick/httpservlet/abstract' +require 'socket' +require 'openssl' module TestNetHTTPUtils + + class Forbidden < StandardError; end + + class HTTPServer + def initialize(config, &block) + @config = config + @server = TCPServer.new(@config['host'], 0) + @port = @server.addr[1] + @procs = {} + + if @config['ssl_enable'] + context = OpenSSL::SSL::SSLContext.new + context.cert = @config['ssl_certificate'] + context.key = @config['ssl_private_key'] + context.tmp_dh_callback = @config['ssl_tmp_dh_callback'] + @ssl_server = OpenSSL::SSL::SSLServer.new(@server, context) + end + + @block = block + end + + def start + @thread = Thread.new do + loop do + socket = @ssl_server ? @ssl_server.accept : @server.accept + run(socket) + rescue => e + ensure + socket.close if socket + end + end + end + + def run(socket) + handle_request(socket) + end + + def shutdown + @thread.kill if @thread + @server.close if @server + end + + def mount(path, proc) + @procs[path] = proc + end + + def mount_proc(path, &block) + mount(path, block.to_proc) + end + + def handle_request(socket) + request_line = socket.gets + return if request_line.nil? || request_line.strip.empty? + + method, path, version = request_line.split + headers = {} + while (line = socket.gets) + break if line.strip.empty? + key, value = line.split(': ', 2) + headers[key] = value.strip + end + + if headers['Expect'] == '100-continue' + socket.write "HTTP/1.1 100 Continue\r\n\r\n" + end + + req = Request.new(method, path, headers, socket) + if @procs.key?(req.path) || @procs.key?("#{req.path}/") + proc = @procs[req.path] || @procs["#{req.path}/"] + res = Response.new(socket) + begin + proc.call(req, res) + rescue Forbidden + res.status = 403 + end + res.finish + else + @block.call(method, path, headers, socket) + end + end + + def port + @port + end + + class Request + attr_reader :method, :path, :headers, :query, :body + def initialize(method, path, headers, socket) + @method = method + @path, @query = parse_path_and_query(path) + @headers = headers + @socket = socket + if method == 'POST' && (@path == '/continue' || @headers['Content-Type'].include?('multipart/form-data')) + if @headers['Transfer-Encoding'] == 'chunked' + @body = read_chunked_body + else + @body = read_body + end + @query = @body.split('&').each_with_object({}) do |pair, hash| + key, value = pair.split('=') + hash[key] = value + end if @body && @body.include?('=') + end + end + + def [](key) + @headers[key.downcase] + end + + def []=(key, value) + @headers[key.downcase] = value + end + + def continue + @socket.write "HTTP\/1.1 100 continue\r\n\r\n" + end + + def query + @query + end + + def remote_ip + @socket.peeraddr[3] + end + + def peeraddr + @socket.peeraddr + end + + private + + def parse_path_and_query(path) + path, query_string = path.split('?', 2) + query = {} + if query_string + query_string.split('&').each do |pair| + key, value = pair.split('=', 2) + query[key] = value + end + end + [path, query] + end + + def read_body + content_length = @headers['Content-Length']&.to_i + return unless content_length && content_length > 0 + @socket.read(content_length) + end + + def read_chunked_body + body = "" + while (chunk_size = @socket.gets.strip.to_i(16)) > 0 + body << @socket.read(chunk_size) + @socket.read(2) # read \r\n after each chunk + end + body + end + end + + class Response + attr_accessor :body, :headers, :status, :chunked, :cookies + def initialize(client) + @client = client + @body = "" + @headers = {} + @status = 200 + @chunked = false + @cookies = [] + end + + def [](key) + @headers[key.downcase] + end + + def []=(key, value) + @headers[key.downcase] = value + end + + def write_chunk(chunk) + return unless @chunked + @client.write("#{chunk.bytesize.to_s(16)}\r\n") + @client.write("#{chunk}\r\n") + end + + def finish + @client.write build_response_headers + if @chunked + write_chunk(@body) + @client.write "0\r\n\r\n" + else + @client.write @body + end + end + + private + + def build_response_headers + response = "HTTP/1.1 #{@status} #{status_message(@status)}\r\n" + if @chunked + @headers['Transfer-Encoding'] = 'chunked' + else + @headers['Content-Length'] = @body.bytesize.to_s + end + @headers.each do |key, value| + response << "#{key}: #{value}\r\n" + end + @cookies.each do |cookie| + response << "Set-Cookie: #{cookie}\r\n" + end + response << "\r\n" + response + end + + def status_message(code) + case code + when 200 then 'OK' + when 301 then 'Moved Permanently' + when 403 then 'Forbidden' + else 'Unknown' + end + end + end + end + def start(&block) new().start(&block) end @@ -33,91 +252,104 @@ def setup end def teardown + sleep 0.5 if @config['ssl_enable'] if @server @server.shutdown - @server_thread.join - WEBrick::Utils::TimeoutHandler.terminate end @log_tester.call(@log) if @log_tester - # resume global state Net::HTTP.version_1_2 end def spawn_server @log = [] - @log_tester = lambda {|log| assert_equal([], log ) } + @log_tester = lambda {|log| assert_equal([], log) } @config = self.class::CONFIG - server_config = { - :BindAddress => config('host'), - :Port => 0, - :Logger => WEBrick::Log.new(@log, WEBrick::BasicLog::WARN), - :AccessLog => [], - :ServerType => Thread, - } - server_config[:OutputBufferSize] = 4 if config('chunked') - server_config[:RequestTimeout] = config('RequestTimeout') if config('RequestTimeout') - if defined?(OpenSSL) and config('ssl_enable') - server_config.update({ - :SSLEnable => true, - :SSLCertificate => config('ssl_certificate'), - :SSLPrivateKey => config('ssl_private_key'), - :SSLTmpDhCallback => config('ssl_tmp_dh_callback'), - }) - end - @server = WEBrick::HTTPServer.new(server_config) - @server.mount('/', Servlet, config('chunked')) - @server_thread = @server.start - @config['port'] = @server[:Port] - end - - $test_net_http = nil - $test_net_http_data = (0...256).to_a.map {|i| i.chr }.join('') * 64 - $test_net_http_data.force_encoding("ASCII-8BIT") - $test_net_http_data_type = 'application/octet-stream' - - class Servlet < WEBrick::HTTPServlet::AbstractServlet - def initialize(this, chunked = false) - @chunked = chunked - end - - def do_GET(req, res) - if req['Accept'] != '*/*' - res['Content-Type'] = req['Accept'] + @server = HTTPServer.new(@config) do |method, path, headers, socket| + @log << "DEBUG accept: #{@config['host']}:#{socket.addr[1]}" if @logger_level == :debug + case method + when 'HEAD' + handle_head(path, headers, socket) + when 'GET' + handle_get(path, headers, socket) + when 'POST' + handle_post(path, headers, socket) + when 'PATCH' + handle_patch(path, headers, socket) else - res['Content-Type'] = $test_net_http_data_type + socket.print "HTTP/1.1 405 Method Not Allowed\r\nContent-Length: 0\r\n\r\n" end - res.body = $test_net_http_data - res.chunked = @chunked end + @server.start + @config['port'] = @server.port + end - # echo server - def do_POST(req, res) - res['Content-Type'] = req['Content-Type'] - res['X-request-uri'] = req.request_uri.to_s - res.body = req.body - res.chunked = @chunked + def handle_head(path, headers, socket) + if headers['Accept'] != '*/*' + content_type = headers['Accept'] + else + content_type = $test_net_http_data_type end + response = "HTTP/1.1 200 OK\r\nContent-Type: #{content_type}\r\nContent-Length: #{$test_net_http_data.bytesize}" + socket.print(response) + end - def do_PATCH(req, res) - res['Content-Type'] = req['Content-Type'] - res.body = req.body - res.chunked = @chunked + def handle_get(path, headers, socket) + if headers['Accept'] != '*/*' + content_type = headers['Accept'] + else + content_type = $test_net_http_data_type end + response = "HTTP/1.1 200 OK\r\nContent-Type: #{content_type}\r\nContent-Length: #{$test_net_http_data.bytesize}\r\n\r\n#{$test_net_http_data}" + socket.print(response) + end + + def handle_post(path, headers, socket) + body = socket.read(headers['Content-Length'].to_i) + scheme = headers['X-Request-Scheme'] || 'http' + host = @config['host'] + port = socket.addr[1] + charset = parse_content_type(headers['Content-Type'])[1] + path = "#{scheme}://#{host}:#{port}#{path}" + path = path.encode(charset) if charset + response = "HTTP/1.1 200 OK\r\nContent-Type: #{headers['Content-Type']}\r\nContent-Length: #{body.bytesize}\r\nX-request-uri: #{path}\r\n\r\n#{body}" + socket.print(response) end + def handle_patch(path, headers, socket) + body = socket.read(headers['Content-Length'].to_i) + response = "HTTP/1.1 200 OK\r\nContent-Type: #{headers['Content-Type']}\r\nContent-Length: #{body.bytesize}\r\n\r\n#{body}" + socket.print(response) + end + + def parse_content_type(content_type) + return [nil, nil] unless content_type + type, *params = content_type.split(';').map(&:strip) + charset = params.find { |param| param.start_with?('charset=') } + charset = charset.split('=', 2).last if charset + [type, charset] + end + + $test_net_http = nil + $test_net_http_data = (0...256).to_a.map { |i| i.chr }.join('') * 64 + $test_net_http_data.force_encoding("ASCII-8BIT") + $test_net_http_data_type = 'application/octet-stream' + class NullWriter - def <<(s) end - def puts(*args) end - def print(*args) end - def printf(*args) end + def <<(_s); end + + def puts(*_args); end + + def print(*_args); end + + def printf(*_args); end end def self.clean_http_proxy_env orig = { - 'http_proxy' => ENV['http_proxy'], + 'http_proxy' => ENV['http_proxy'], 'http_proxy_user' => ENV['http_proxy_user'], 'http_proxy_pass' => ENV['http_proxy_pass'], - 'no_proxy' => ENV['no_proxy'], + 'no_proxy' => ENV['no_proxy'], } orig.each_key do |key| diff --git a/test/objspace/test_objspace.rb b/test/objspace/test_objspace.rb index 17f73e44336801..3f4a58e98d9c59 100644 --- a/test/objspace/test_objspace.rb +++ b/test/objspace/test_objspace.rb @@ -558,7 +558,7 @@ def dump_my_heap_please next if obj["type"] == "SHAPE" assert_not_nil obj["slot_size"] - assert_equal 0, obj["slot_size"] % GC::INTERNAL_CONSTANTS[:RVALUE_SIZE] + assert_equal 0, obj["slot_size"] % (GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] + GC::INTERNAL_CONSTANTS[:RVALUE_OVERHEAD]) } end end diff --git a/test/open-uri/test_ftp.rb b/test/open-uri/test_ftp.rb new file mode 100644 index 00000000000000..9698ff2777ed89 --- /dev/null +++ b/test/open-uri/test_ftp.rb @@ -0,0 +1,216 @@ +# frozen_string_literal: true +require 'test/unit' +require 'socket' +require 'open-uri' + +class TestOpenURIFtp < Test::Unit::TestCase + def with_env(h) + begin + old = {} + h.each_key {|k| old[k] = ENV[k] } + h.each {|k, v| ENV[k] = v } + yield + ensure + h.each_key {|k| ENV[k] = old[k] } + end + end + + begin + require 'net/ftp' + + def test_ftp_invalid_request + assert_raise(ArgumentError) { URI("ftp://127.0.0.1/").read } + assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Db").read } + assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Ab").read } + assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Db/f").read } + assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Ab/f").read } + assert_nothing_raised(URI::InvalidComponentError) { URI("ftp://127.0.0.1/d/f;type=x") } + end + + def test_ftp + TCPServer.open("127.0.0.1", 0) {|serv| + _, port, _, host = serv.addr + th = Thread.new { + s = serv.accept + begin + s.print "220 Test FTP Server\r\n" + assert_equal("USER anonymous\r\n", s.gets); s.print "331 name ok\r\n" + assert_match(/\APASS .*\r\n\z/, s.gets); s.print "230 logged in\r\n" + assert_equal("TYPE I\r\n", s.gets); s.print "200 type set to I\r\n" + assert_equal("CWD foo\r\n", s.gets); s.print "250 CWD successful\r\n" + assert_equal("PASV\r\n", s.gets) + TCPServer.open("127.0.0.1", 0) {|data_serv| + _, data_serv_port, _, _ = data_serv.addr + hi = data_serv_port >> 8 + lo = data_serv_port & 0xff + s.print "227 Entering Passive Mode (127,0,0,1,#{hi},#{lo}).\r\n" + assert_equal("RETR bar\r\n", s.gets); s.print "150 file okay\r\n" + data_sock = data_serv.accept + begin + data_sock << "content" + ensure + data_sock.close + end + s.print "226 transfer complete\r\n" + assert_nil(s.gets) + } + ensure + s.close if s + end + } + begin + content = URI("ftp://#{host}:#{port}/foo/bar").read + assert_equal("content", content) + ensure + Thread.kill(th) + th.join + end + } + end + + def test_ftp_active + TCPServer.open("127.0.0.1", 0) {|serv| + _, port, _, host = serv.addr + th = Thread.new { + s = serv.accept + begin + content = "content" + s.print "220 Test FTP Server\r\n" + assert_equal("USER anonymous\r\n", s.gets); s.print "331 name ok\r\n" + assert_match(/\APASS .*\r\n\z/, s.gets); s.print "230 logged in\r\n" + assert_equal("TYPE I\r\n", s.gets); s.print "200 type set to I\r\n" + assert_equal("CWD foo\r\n", s.gets); s.print "250 CWD successful\r\n" + assert(m = /\APORT 127,0,0,1,(\d+),(\d+)\r\n\z/.match(s.gets)) + active_port = m[1].to_i << 8 | m[2].to_i + TCPSocket.open("127.0.0.1", active_port) {|data_sock| + s.print "200 data connection opened\r\n" + assert_equal("RETR bar\r\n", s.gets); s.print "150 file okay\r\n" + begin + data_sock << content + ensure + data_sock.close + end + s.print "226 transfer complete\r\n" + assert_nil(s.gets) + } + ensure + s.close if s + end + } + begin + content = URI("ftp://#{host}:#{port}/foo/bar").read(:ftp_active_mode=>true) + assert_equal("content", content) + ensure + Thread.kill(th) + th.join + end + } + end + + def test_ftp_ascii + TCPServer.open("127.0.0.1", 0) {|serv| + _, port, _, host = serv.addr + th = Thread.new { + s = serv.accept + begin + content = "content" + s.print "220 Test FTP Server\r\n" + assert_equal("USER anonymous\r\n", s.gets); s.print "331 name ok\r\n" + assert_match(/\APASS .*\r\n\z/, s.gets); s.print "230 logged in\r\n" + assert_equal("TYPE I\r\n", s.gets); s.print "200 type set to I\r\n" + assert_equal("CWD /foo\r\n", s.gets); s.print "250 CWD successful\r\n" + assert_equal("TYPE A\r\n", s.gets); s.print "200 type set to A\r\n" + assert_equal("SIZE bar\r\n", s.gets); s.print "213 #{content.bytesize}\r\n" + assert_equal("PASV\r\n", s.gets) + TCPServer.open("127.0.0.1", 0) {|data_serv| + _, data_serv_port, _, _ = data_serv.addr + hi = data_serv_port >> 8 + lo = data_serv_port & 0xff + s.print "227 Entering Passive Mode (127,0,0,1,#{hi},#{lo}).\r\n" + assert_equal("RETR bar\r\n", s.gets); s.print "150 file okay\r\n" + data_sock = data_serv.accept + begin + data_sock << content + ensure + data_sock.close + end + s.print "226 transfer complete\r\n" + assert_nil(s.gets) + } + ensure + s.close if s + end + } + begin + length = [] + progress = [] + content = URI("ftp://#{host}:#{port}/%2Ffoo/b%61r;type=a").read( + :content_length_proc => lambda {|n| length << n }, + :progress_proc => lambda {|n| progress << n }) + assert_equal("content", content) + assert_equal([7], length) + assert_equal(7, progress.inject(&:+)) + ensure + Thread.kill(th) + th.join + end + } + end + rescue LoadError + # net-ftp is the bundled gems at Ruby 3.1 + end + + def test_ftp_over_http_proxy + TCPServer.open("127.0.0.1", 0) {|proxy_serv| + proxy_port = proxy_serv.addr[1] + th = Thread.new { + proxy_sock = proxy_serv.accept + begin + req = proxy_sock.gets("\r\n\r\n") + assert_match(%r{\AGET ftp://192.0.2.1/foo/bar }, req) + proxy_sock.print "HTTP/1.0 200 OK\r\n" + proxy_sock.print "Content-Length: 4\r\n\r\n" + proxy_sock.print "ab\r\n" + ensure + proxy_sock.close + end + } + begin + with_env('ftp_proxy'=>"http://127.0.0.1:#{proxy_port}") { + content = URI("ftp://192.0.2.1/foo/bar").read + assert_equal("ab\r\n", content) + } + ensure + Thread.kill(th) + th.join + end + } + end + + def test_ftp_over_http_proxy_auth + TCPServer.open("127.0.0.1", 0) {|proxy_serv| + proxy_port = proxy_serv.addr[1] + th = Thread.new { + proxy_sock = proxy_serv.accept + begin + req = proxy_sock.gets("\r\n\r\n") + assert_match(%r{\AGET ftp://192.0.2.1/foo/bar }, req) + assert_match(%r{Proxy-Authorization: Basic #{['proxy-user:proxy-password'].pack('m').chomp}\r\n}, req) + proxy_sock.print "HTTP/1.0 200 OK\r\n" + proxy_sock.print "Content-Length: 4\r\n\r\n" + proxy_sock.print "ab\r\n" + ensure + proxy_sock.close + end + } + begin + content = URI("ftp://192.0.2.1/foo/bar").read( + :proxy_http_basic_authentication => ["http://127.0.0.1:#{proxy_port}", "proxy-user", "proxy-password"]) + assert_equal("ab\r\n", content) + ensure + Thread.kill(th) + th.join + end + } + end +end diff --git a/test/open-uri/test_open-uri.rb b/test/open-uri/test_open-uri.rb index 30e3b5c9c1e5e8..9db6a8bb3be41e 100644 --- a/test/open-uri/test_open-uri.rb +++ b/test/open-uri/test_open-uri.rb @@ -1,75 +1,18 @@ # frozen_string_literal: true require 'test/unit' require 'open-uri' -require 'webrick' -require 'webrick/httpproxy' +require 'stringio' +require_relative 'utils' begin require 'zlib' rescue LoadError end class TestOpenURI < Test::Unit::TestCase - - NullLog = Object.new - def NullLog.<<(arg) - #puts arg if / INFO / !~ arg - end - - def with_http(log_tester=lambda {|log| assert_equal([], log) }) - log = [] - logger = WEBrick::Log.new(log, WEBrick::BasicLog::WARN) - Dir.mktmpdir {|dr| - srv = WEBrick::HTTPServer.new({ - :DocumentRoot => dr, - :ServerType => Thread, - :Logger => logger, - :AccessLog => [[NullLog, ""]], - :BindAddress => '127.0.0.1', - :Port => 0}) - _, port, _, host = srv.listeners[0].addr - server_thread = srv.start - server_thread2 = Thread.new { - server_thread.join - if log_tester - log_tester.call(log) - end - } - client_thread = Thread.new { - begin - yield srv, dr, "http://#{host}:#{port}", server_thread, log - ensure - srv.shutdown - end - } - assert_join_threads([client_thread, server_thread2]) - } - ensure - WEBrick::Utils::TimeoutHandler.terminate - end - - def with_env(h) - begin - old = {} - h.each_key {|k| old[k] = ENV[k] } - h.each {|k, v| ENV[k] = v } - yield - ensure - h.each_key {|k| ENV[k] = old[k] } - end - end - - def setup - @proxies = %w[http_proxy HTTP_PROXY ftp_proxy FTP_PROXY no_proxy] - @old_proxies = @proxies.map {|k| ENV[k] } - @proxies.each {|k| ENV[k] = nil } - end - - def teardown - @proxies.each_with_index {|k, i| ENV[k] = @old_proxies[i] } - end + include TestOpenURIUtils def test_200_uri_open - with_http {|srv, dr, url| + with_http {|srv, url| srv.mount_proc("/urifoo200", lambda { |req, res| res.body = "urifoo200" } ) URI.open("#{url}/urifoo200") {|f| assert_equal("200", f.status[0]) @@ -79,7 +22,7 @@ def test_200_uri_open end def test_200 - with_http {|srv, dr, url| + with_http {|srv, url| srv.mount_proc("/foo200", lambda { |req, res| res.body = "foo200" } ) URI.open("#{url}/foo200") {|f| assert_equal("200", f.status[0]) @@ -89,7 +32,7 @@ def test_200 end def test_200big - with_http {|srv, dr, url| + with_http {|srv, url| content = "foo200big"*10240 srv.mount_proc("/foo200big", lambda { |req, res| res.body = content } ) URI.open("#{url}/foo200big") {|f| @@ -104,14 +47,14 @@ def test_404 assert_equal(1, server_log.length) assert_match(%r{ERROR `/not-exist' not found}, server_log[0]) } - with_http(log_tester) {|srv, dr, url, server_thread, server_log| + with_http(log_tester) {|srv, url, server_thread, server_log| exc = assert_raise(OpenURI::HTTPError) { URI.open("#{url}/not-exist") {} } assert_equal("404", exc.io.status[0]) } end def test_open_uri - with_http {|srv, dr, url| + with_http {|srv, url| srv.mount_proc("/foo_ou", lambda { |req, res| res.body = "foo_ou" } ) u = URI("#{url}/foo_ou") URI.open(u) {|f| @@ -155,7 +98,7 @@ def test_open_timeout URI("http://example.com/").read(open_timeout: 0.000001) end if false # avoid external resources in tests - with_http {|srv, dr, url| + with_http {|srv, url| url += '/' srv.mount_proc('/', lambda { |_, res| res.body = 'hi' }) begin @@ -186,7 +129,7 @@ def o.open(foo: ) foo end end def test_mode - with_http {|srv, dr, url| + with_http {|srv, url| srv.mount_proc("/mode", lambda { |req, res| res.body = "mode" } ) URI.open("#{url}/mode", "r") {|f| assert_equal("200", f.status[0]) @@ -208,7 +151,7 @@ def test_mode end def test_without_block - with_http {|srv, dr, url| + with_http {|srv, url| srv.mount_proc("/without_block", lambda { |req, res| res.body = "without_block" } ) begin f = URI.open("#{url}/without_block") @@ -221,7 +164,7 @@ def test_without_block end def test_close_in_block_small - with_http {|srv, dr, url| + with_http {|srv, url| srv.mount_proc("/close200", lambda { |req, res| res.body = "close200" } ) assert_nothing_raised { URI.open("#{url}/close200") {|f| @@ -232,7 +175,7 @@ def test_close_in_block_small end def test_close_in_block_big - with_http {|srv, dr, url| + with_http {|srv, url| content = "close200big"*10240 srv.mount_proc("/close200big", lambda { |req, res| res.body = content } ) assert_nothing_raised { @@ -246,8 +189,8 @@ def test_close_in_block_big def test_header myheader1 = 'barrrr' myheader2 = nil - with_http {|srv, dr, url| - srv.mount_proc("/h/") {|req, res| myheader2 = req['myheader']; res.body = "foo" } + with_http {|srv, url| + srv.mount_proc("/h/", lambda {|req, res| myheader2 = req['myheader']; res.body = "foo" } ) URI.open("#{url}/h/", 'MyHeader'=>myheader1) {|f| assert_equal("foo", f.read) assert_equal(myheader1, myheader2) @@ -267,175 +210,11 @@ def test_non_http_proxy } end - def test_proxy - with_http {|srv, dr, url| - proxy_log = StringIO.new(''.dup) - proxy_logger = WEBrick::Log.new(proxy_log, WEBrick::BasicLog::WARN) - proxy_auth_log = ''.dup - proxy = WEBrick::HTTPProxyServer.new({ - :ServerType => Thread, - :Logger => proxy_logger, - :AccessLog => [[NullLog, ""]], - :ProxyAuthProc => lambda {|req, res| - proxy_auth_log << req.request_line - }, - :BindAddress => '127.0.0.1', - :Port => 0}) - _, proxy_port, _, proxy_host = proxy.listeners[0].addr - proxy_url = "http://#{proxy_host}:#{proxy_port}/" - begin - proxy_thread = proxy.start - srv.mount_proc("/proxy", lambda { |req, res| res.body = "proxy" } ) - URI.open("#{url}/proxy", :proxy=>proxy_url) {|f| - assert_equal("200", f.status[0]) - assert_equal("proxy", f.read) - } - assert_match(/#{Regexp.quote url}/, proxy_auth_log); proxy_auth_log.clear - URI.open("#{url}/proxy", :proxy=>URI(proxy_url)) {|f| - assert_equal("200", f.status[0]) - assert_equal("proxy", f.read) - } - assert_match(/#{Regexp.quote url}/, proxy_auth_log); proxy_auth_log.clear - URI.open("#{url}/proxy", :proxy=>nil) {|f| - assert_equal("200", f.status[0]) - assert_equal("proxy", f.read) - } - assert_equal("", proxy_auth_log); proxy_auth_log.clear - assert_raise(ArgumentError) { - URI.open("#{url}/proxy", :proxy=>:invalid) {} - } - assert_equal("", proxy_auth_log); proxy_auth_log.clear - with_env("http_proxy"=>proxy_url) { - # should not use proxy for 127.0.0.0/8. - URI.open("#{url}/proxy") {|f| - assert_equal("200", f.status[0]) - assert_equal("proxy", f.read) - } - } - assert_equal("", proxy_auth_log); proxy_auth_log.clear - ensure - proxy.shutdown - proxy_thread.join - end - assert_equal("", proxy_log.string) - } - end - - def test_proxy_http_basic_authentication_failure - with_http {|srv, dr, url| - proxy_log = StringIO.new(''.dup) - proxy_logger = WEBrick::Log.new(proxy_log, WEBrick::BasicLog::WARN) - proxy_auth_log = ''.dup - proxy = WEBrick::HTTPProxyServer.new({ - :ServerType => Thread, - :Logger => proxy_logger, - :AccessLog => [[NullLog, ""]], - :ProxyAuthProc => lambda {|req, res| - proxy_auth_log << req.request_line - if req["Proxy-Authorization"] != "Basic #{['user:pass'].pack('m').chomp}" - raise WEBrick::HTTPStatus::ProxyAuthenticationRequired - end - }, - :BindAddress => '127.0.0.1', - :Port => 0}) - _, proxy_port, _, proxy_host = proxy.listeners[0].addr - proxy_url = "http://#{proxy_host}:#{proxy_port}/" - begin - th = proxy.start - srv.mount_proc("/proxy", lambda { |req, res| res.body = "proxy" } ) - exc = assert_raise(OpenURI::HTTPError) { URI.open("#{url}/proxy", :proxy=>proxy_url) {} } - assert_equal("407", exc.io.status[0]) - assert_match(/#{Regexp.quote url}/, proxy_auth_log); proxy_auth_log.clear - ensure - proxy.shutdown - th.join - end - assert_match(/ERROR WEBrick::HTTPStatus::ProxyAuthenticationRequired/, proxy_log.string) - } - end - - def test_proxy_http_basic_authentication_success - with_http {|srv, dr, url| - proxy_log = StringIO.new(''.dup) - proxy_logger = WEBrick::Log.new(proxy_log, WEBrick::BasicLog::WARN) - proxy_auth_log = ''.dup - proxy = WEBrick::HTTPProxyServer.new({ - :ServerType => Thread, - :Logger => proxy_logger, - :AccessLog => [[NullLog, ""]], - :ProxyAuthProc => lambda {|req, res| - proxy_auth_log << req.request_line - if req["Proxy-Authorization"] != "Basic #{['user:pass'].pack('m').chomp}" - raise WEBrick::HTTPStatus::ProxyAuthenticationRequired - end - }, - :BindAddress => '127.0.0.1', - :Port => 0}) - _, proxy_port, _, proxy_host = proxy.listeners[0].addr - proxy_url = "http://#{proxy_host}:#{proxy_port}/" - begin - th = proxy.start - srv.mount_proc("/proxy", lambda { |req, res| res.body = "proxy" } ) - URI.open("#{url}/proxy", - :proxy_http_basic_authentication=>[proxy_url, "user", "pass"]) {|f| - assert_equal("200", f.status[0]) - assert_equal("proxy", f.read) - } - assert_match(/#{Regexp.quote url}/, proxy_auth_log); proxy_auth_log.clear - assert_raise(ArgumentError) { - URI.open("#{url}/proxy", - :proxy_http_basic_authentication=>[true, "user", "pass"]) {} - } - assert_equal("", proxy_auth_log); proxy_auth_log.clear - ensure - proxy.shutdown - th.join - end - assert_equal("", proxy_log.string) - } - end - - def test_authenticated_proxy_http_basic_authentication_success - with_http {|srv, dr, url| - proxy_log = StringIO.new(''.dup) - proxy_logger = WEBrick::Log.new(proxy_log, WEBrick::BasicLog::WARN) - proxy_auth_log = ''.dup - proxy = WEBrick::HTTPProxyServer.new({ - :ServerType => Thread, - :Logger => proxy_logger, - :AccessLog => [[NullLog, ""]], - :ProxyAuthProc => lambda {|req, res| - proxy_auth_log << req.request_line - if req["Proxy-Authorization"] != "Basic #{['user:pass'].pack('m').chomp}" - raise WEBrick::HTTPStatus::ProxyAuthenticationRequired - end - }, - :BindAddress => '127.0.0.1', - :Port => 0}) - _, proxy_port, _, proxy_host = proxy.listeners[0].addr - proxy_url = "http://user:pass@#{proxy_host}:#{proxy_port}/" - begin - th = proxy.start - srv.mount_proc("/proxy", lambda { |req, res| res.body = "proxy" } ) - URI.open("#{url}/proxy", :proxy => proxy_url) {|f| - assert_equal("200", f.status[0]) - assert_equal("proxy", f.read) - } - assert_match(/#{Regexp.quote url}/, proxy_auth_log); proxy_auth_log.clear - assert_equal("", proxy_auth_log); proxy_auth_log.clear - ensure - proxy.shutdown - th.join - end - assert_equal("", proxy_log.string) - } - end - def test_redirect - with_http {|srv, dr, url| - srv.mount_proc("/r1/") {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" } - srv.mount_proc("/r2/") {|req, res| res.body = "r2" } - srv.mount_proc("/to-file/") {|req, res| res.status = 301; res["location"] = "file:///foo" } + with_http {|srv, url| + srv.mount_proc("/r1/", lambda {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" } ) + srv.mount_proc("/r2/", lambda {|req, res| res.body = "r2" } ) + srv.mount_proc("/to-file/", lambda {|req, res| res.status = 301; res["location"] = "file:///foo" } ) URI.open("#{url}/r1/") {|f| assert_equal("#{url}/r2", f.base_uri.to_s) assert_equal("r2", f.read) @@ -446,9 +225,9 @@ def test_redirect end def test_redirect_loop - with_http {|srv, dr, url| - srv.mount_proc("/r1/") {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" } - srv.mount_proc("/r2/") {|req, res| res.status = 301; res["location"] = "#{url}/r1"; res.body = "r2" } + with_http {|srv, url| + srv.mount_proc("/r1/", lambda {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" } ) + srv.mount_proc("/r2/", lambda {|req, res| res.status = 301; res["location"] = "#{url}/r1"; res.body = "r2" } ) assert_raise(RuntimeError) { URI.open("#{url}/r1/") {} } } end @@ -513,20 +292,20 @@ def test_redirect_invalid end def setup_redirect_auth(srv, url) - srv.mount_proc("/r1/") {|req, res| + srv.mount_proc("/r1/", lambda {|req, res| res.status = 301 res["location"] = "#{url}/r2" - } - srv.mount_proc("/r2/") {|req, res| + }) + srv.mount_proc("/r2/", lambda {|req, res| if req["Authorization"] != "Basic #{['user:pass'].pack('m').chomp}" - raise WEBrick::HTTPStatus::Unauthorized + raise Unauthorized end res.body = "r2" - } + }) end def test_redirect_auth_success - with_http {|srv, dr, url| + with_http {|srv, url| setup_redirect_auth(srv, url) URI.open("#{url}/r2/", :http_basic_authentication=>['user', 'pass']) {|f| assert_equal("r2", f.read) @@ -537,9 +316,9 @@ def test_redirect_auth_success def test_redirect_auth_failure_r2 log_tester = lambda {|server_log| assert_equal(1, server_log.length) - assert_match(/ERROR WEBrick::HTTPStatus::Unauthorized/, server_log[0]) + assert_match(/ERROR Unauthorized/, server_log[0]) } - with_http(log_tester) {|srv, dr, url, server_thread, server_log| + with_http(log_tester) {|srv, url, server_thread, server_log| setup_redirect_auth(srv, url) exc = assert_raise(OpenURI::HTTPError) { URI.open("#{url}/r2/") {} } assert_equal("401", exc.io.status[0]) @@ -549,9 +328,9 @@ def test_redirect_auth_failure_r2 def test_redirect_auth_failure_r1 log_tester = lambda {|server_log| assert_equal(1, server_log.length) - assert_match(/ERROR WEBrick::HTTPStatus::Unauthorized/, server_log[0]) + assert_match(/ERROR Unauthorized/, server_log[0]) } - with_http(log_tester) {|srv, dr, url, server_thread, server_log| + with_http(log_tester) {|srv, url, server_thread, server_log| setup_redirect_auth(srv, url) exc = assert_raise(OpenURI::HTTPError) { URI.open("#{url}/r1/", :http_basic_authentication=>['user', 'pass']) {} } assert_equal("401", exc.io.status[0]) @@ -559,19 +338,19 @@ def test_redirect_auth_failure_r1 end def test_max_redirects_success - with_http {|srv, dr, url| - srv.mount_proc("/r1/") {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" } - srv.mount_proc("/r2/") {|req, res| res.status = 301; res["location"] = "#{url}/r3"; res.body = "r2" } - srv.mount_proc("/r3/") {|req, res| res.body = "r3" } + with_http {|srv, url| + srv.mount_proc("/r1/", lambda {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" } ) + srv.mount_proc("/r2/", lambda {|req, res| res.status = 301; res["location"] = "#{url}/r3"; res.body = "r2" } ) + srv.mount_proc("/r3/", lambda {|req, res| res.body = "r3" } ) URI.open("#{url}/r1/", max_redirects: 2) { |f| assert_equal("r3", f.read) } } end def test_max_redirects_too_many - with_http {|srv, dr, url| - srv.mount_proc("/r1/") {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" } - srv.mount_proc("/r2/") {|req, res| res.status = 301; res["location"] = "#{url}/r3"; res.body = "r2" } - srv.mount_proc("/r3/") {|req, res| res.body = "r3" } + with_http {|srv, url| + srv.mount_proc("/r1/", lambda {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" } ) + srv.mount_proc("/r2/", lambda {|req, res| res.status = 301; res["location"] = "#{url}/r3"; res.body = "r2" } ) + srv.mount_proc("/r3/", lambda {|req, res| res.body = "r3" } ) exc = assert_raise(OpenURI::TooManyRedirects) { URI.open("#{url}/r1/", max_redirects: 1) {} } assert_equal("Too many redirects", exc.message) } @@ -582,9 +361,9 @@ def test_userinfo end def test_progress - with_http {|srv, dr, url| + with_http {|srv, url| content = "a" * 100000 - srv.mount_proc("/data/") {|req, res| res.body = content } + srv.mount_proc("/data/", lambda {|req, res| res.body = content }) length = [] progress = [] URI.open("#{url}/data/", @@ -602,9 +381,9 @@ def test_progress end def test_progress_chunked - with_http {|srv, dr, url| + with_http {|srv, url| content = "a" * 100000 - srv.mount_proc("/data/") {|req, res| res.body = content; res.chunked = true } + srv.mount_proc("/data/", lambda {|req, res| res.body = content; res.chunked = true } ) length = [] progress = [] URI.open("#{url}/data/", @@ -622,7 +401,7 @@ def test_progress_chunked end def test_uri_read - with_http {|srv, dr, url| + with_http {|srv, url| srv.mount_proc("/uriread", lambda { |req, res| res.body = "uriread" } ) data = URI("#{url}/uriread").read assert_equal("200", data.status[0]) @@ -631,12 +410,12 @@ def test_uri_read end def test_encoding - with_http {|srv, dr, url| + with_http {|srv, url| content_u8 = "\u3042" content_ej = "\xa2\xa4".dup.force_encoding("euc-jp") - srv.mount_proc("/u8/") {|req, res| res.body = content_u8; res['content-type'] = 'text/plain; charset=utf-8' } - srv.mount_proc("/ej/") {|req, res| res.body = content_ej; res['content-type'] = 'TEXT/PLAIN; charset=EUC-JP' } - srv.mount_proc("/nc/") {|req, res| res.body = "aa"; res['content-type'] = 'Text/Plain' } + srv.mount_proc("/u8/", lambda {|req, res| res.body = content_u8; res['content-type'] = 'text/plain; charset=utf-8' } ) + srv.mount_proc("/ej/", lambda {|req, res| res.body = content_ej; res['content-type'] = 'TEXT/PLAIN; charset=EUC-JP' } ) + srv.mount_proc("/nc/", lambda {|req, res| res.body = "aa"; res['content-type'] = 'Text/Plain' } ) URI.open("#{url}/u8/") {|f| assert_equal(content_u8, f.read) assert_equal("text/plain", f.content_type) @@ -675,9 +454,9 @@ def test_encoding end def test_quoted_attvalue - with_http {|srv, dr, url| + with_http {|srv, url| content_u8 = "\u3042" - srv.mount_proc("/qu8/") {|req, res| res.body = content_u8; res['content-type'] = 'text/plain; charset="utf\-8"' } + srv.mount_proc("/qu8/", lambda {|req, res| res.body = content_u8; res['content-type'] = 'text/plain; charset="utf\-8"' } ) URI.open("#{url}/qu8/") {|f| assert_equal(content_u8, f.read) assert_equal("text/plain", f.content_type) @@ -687,8 +466,8 @@ def test_quoted_attvalue end def test_last_modified - with_http {|srv, dr, url| - srv.mount_proc("/data/") {|req, res| res.body = "foo"; res['last-modified'] = 'Fri, 07 Aug 2009 06:05:04 GMT' } + with_http {|srv, url| + srv.mount_proc("/data/", lambda {|req, res| res.body = "foo"; res['last-modified'] = 'Fri, 07 Aug 2009 06:05:04 GMT' } ) URI.open("#{url}/data/") {|f| assert_equal("foo", f.read) assert_equal(Time.utc(2009,8,7,6,5,4), f.last_modified) @@ -697,12 +476,12 @@ def test_last_modified end def test_content_encoding - with_http {|srv, dr, url| + with_http {|srv, url| content = "abc" * 10000 Zlib::GzipWriter.wrap(StringIO.new(content_gz="".b)) {|z| z.write content } - srv.mount_proc("/data/") {|req, res| res.body = content_gz; res['content-encoding'] = 'gzip' } - srv.mount_proc("/data2/") {|req, res| res.body = content_gz; res['content-encoding'] = 'gzip'; res.chunked = true } - srv.mount_proc("/noce/") {|req, res| res.body = content_gz } + srv.mount_proc("/data/", lambda {|req, res| res.body = content_gz; res['content-encoding'] = 'gzip' } ) + srv.mount_proc("/data2/", lambda {|req, res| res.body = content_gz; res['content-encoding'] = 'gzip'; res.chunked = true } ) + srv.mount_proc("/noce/", lambda {|req, res| res.body = content_gz } ) URI.open("#{url}/data/") {|f| assert_equal [], f.content_encoding assert_equal(content, f.read) @@ -719,12 +498,12 @@ def test_content_encoding end if defined?(Zlib::GzipWriter) def test_multiple_cookies - with_http {|srv, dr, url| - srv.mount_proc("/mcookie/") {|req, res| + with_http {|srv, url| + srv.mount_proc("/mcookie/", lambda {|req, res| res.cookies << "name1=value1; blabla" res.cookies << "name2=value2; blabla" res.body = "foo" - } + }) URI.open("#{url}/mcookie/") {|f| assert_equal("foo", f.read) assert_equal(["name1=value1; blabla", "name2=value2; blabla"], @@ -735,205 +514,6 @@ def test_multiple_cookies # 192.0.2.0/24 is TEST-NET. [RFC3330] - begin - require 'net/ftp' - - def test_ftp_invalid_request - assert_raise(ArgumentError) { URI("ftp://127.0.0.1/").read } - assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Db").read } - assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Ab").read } - assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Db/f").read } - assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Ab/f").read } - assert_nothing_raised(URI::InvalidComponentError) { URI("ftp://127.0.0.1/d/f;type=x") } - end - - def test_ftp - TCPServer.open("127.0.0.1", 0) {|serv| - _, port, _, host = serv.addr - th = Thread.new { - s = serv.accept - begin - s.print "220 Test FTP Server\r\n" - assert_equal("USER anonymous\r\n", s.gets); s.print "331 name ok\r\n" - assert_match(/\APASS .*\r\n\z/, s.gets); s.print "230 logged in\r\n" - assert_equal("TYPE I\r\n", s.gets); s.print "200 type set to I\r\n" - assert_equal("CWD foo\r\n", s.gets); s.print "250 CWD successful\r\n" - assert_equal("PASV\r\n", s.gets) - TCPServer.open("127.0.0.1", 0) {|data_serv| - _, data_serv_port, _, _ = data_serv.addr - hi = data_serv_port >> 8 - lo = data_serv_port & 0xff - s.print "227 Entering Passive Mode (127,0,0,1,#{hi},#{lo}).\r\n" - assert_equal("RETR bar\r\n", s.gets); s.print "150 file okay\r\n" - data_sock = data_serv.accept - begin - data_sock << "content" - ensure - data_sock.close - end - s.print "226 transfer complete\r\n" - assert_nil(s.gets) - } - ensure - s.close if s - end - } - begin - content = URI("ftp://#{host}:#{port}/foo/bar").read - assert_equal("content", content) - ensure - Thread.kill(th) - th.join - end - } - end - - def test_ftp_active - TCPServer.open("127.0.0.1", 0) {|serv| - _, port, _, host = serv.addr - th = Thread.new { - s = serv.accept - begin - content = "content" - s.print "220 Test FTP Server\r\n" - assert_equal("USER anonymous\r\n", s.gets); s.print "331 name ok\r\n" - assert_match(/\APASS .*\r\n\z/, s.gets); s.print "230 logged in\r\n" - assert_equal("TYPE I\r\n", s.gets); s.print "200 type set to I\r\n" - assert_equal("CWD foo\r\n", s.gets); s.print "250 CWD successful\r\n" - assert(m = /\APORT 127,0,0,1,(\d+),(\d+)\r\n\z/.match(s.gets)) - active_port = m[1].to_i << 8 | m[2].to_i - TCPSocket.open("127.0.0.1", active_port) {|data_sock| - s.print "200 data connection opened\r\n" - assert_equal("RETR bar\r\n", s.gets); s.print "150 file okay\r\n" - begin - data_sock << content - ensure - data_sock.close - end - s.print "226 transfer complete\r\n" - assert_nil(s.gets) - } - ensure - s.close if s - end - } - begin - content = URI("ftp://#{host}:#{port}/foo/bar").read(:ftp_active_mode=>true) - assert_equal("content", content) - ensure - Thread.kill(th) - th.join - end - } - end - - def test_ftp_ascii - TCPServer.open("127.0.0.1", 0) {|serv| - _, port, _, host = serv.addr - th = Thread.new { - s = serv.accept - begin - content = "content" - s.print "220 Test FTP Server\r\n" - assert_equal("USER anonymous\r\n", s.gets); s.print "331 name ok\r\n" - assert_match(/\APASS .*\r\n\z/, s.gets); s.print "230 logged in\r\n" - assert_equal("TYPE I\r\n", s.gets); s.print "200 type set to I\r\n" - assert_equal("CWD /foo\r\n", s.gets); s.print "250 CWD successful\r\n" - assert_equal("TYPE A\r\n", s.gets); s.print "200 type set to A\r\n" - assert_equal("SIZE bar\r\n", s.gets); s.print "213 #{content.bytesize}\r\n" - assert_equal("PASV\r\n", s.gets) - TCPServer.open("127.0.0.1", 0) {|data_serv| - _, data_serv_port, _, _ = data_serv.addr - hi = data_serv_port >> 8 - lo = data_serv_port & 0xff - s.print "227 Entering Passive Mode (127,0,0,1,#{hi},#{lo}).\r\n" - assert_equal("RETR bar\r\n", s.gets); s.print "150 file okay\r\n" - data_sock = data_serv.accept - begin - data_sock << content - ensure - data_sock.close - end - s.print "226 transfer complete\r\n" - assert_nil(s.gets) - } - ensure - s.close if s - end - } - begin - length = [] - progress = [] - content = URI("ftp://#{host}:#{port}/%2Ffoo/b%61r;type=a").read( - :content_length_proc => lambda {|n| length << n }, - :progress_proc => lambda {|n| progress << n }) - assert_equal("content", content) - assert_equal([7], length) - assert_equal(7, progress.inject(&:+)) - ensure - Thread.kill(th) - th.join - end - } - end - rescue LoadError - # net-ftp is the bundled gems at Ruby 3.1 - end - - def test_ftp_over_http_proxy - TCPServer.open("127.0.0.1", 0) {|proxy_serv| - proxy_port = proxy_serv.addr[1] - th = Thread.new { - proxy_sock = proxy_serv.accept - begin - req = proxy_sock.gets("\r\n\r\n") - assert_match(%r{\AGET ftp://192.0.2.1/foo/bar }, req) - proxy_sock.print "HTTP/1.0 200 OK\r\n" - proxy_sock.print "Content-Length: 4\r\n\r\n" - proxy_sock.print "ab\r\n" - ensure - proxy_sock.close - end - } - begin - with_env('ftp_proxy'=>"http://127.0.0.1:#{proxy_port}") { - content = URI("ftp://192.0.2.1/foo/bar").read - assert_equal("ab\r\n", content) - } - ensure - Thread.kill(th) - th.join - end - } - end - - def test_ftp_over_http_proxy_auth - TCPServer.open("127.0.0.1", 0) {|proxy_serv| - proxy_port = proxy_serv.addr[1] - th = Thread.new { - proxy_sock = proxy_serv.accept - begin - req = proxy_sock.gets("\r\n\r\n") - assert_match(%r{\AGET ftp://192.0.2.1/foo/bar }, req) - assert_match(%r{Proxy-Authorization: Basic #{['proxy-user:proxy-password'].pack('m').chomp}\r\n}, req) - proxy_sock.print "HTTP/1.0 200 OK\r\n" - proxy_sock.print "Content-Length: 4\r\n\r\n" - proxy_sock.print "ab\r\n" - ensure - proxy_sock.close - end - } - begin - content = URI("ftp://192.0.2.1/foo/bar").read( - :proxy_http_basic_authentication => ["http://127.0.0.1:#{proxy_port}", "proxy-user", "proxy-password"]) - assert_equal("ab\r\n", content) - ensure - Thread.kill(th) - th.join - end - } - end - def test_meta_init_doesnt_bump_global_constant_state omit "RubyVM.stat not defined" unless defined? RubyVM.stat omit unless RubyVM.stat.has_key?(:global_constant_state) diff --git a/test/open-uri/test_proxy.rb b/test/open-uri/test_proxy.rb new file mode 100644 index 00000000000000..a36a63f21f2c80 --- /dev/null +++ b/test/open-uri/test_proxy.rb @@ -0,0 +1,174 @@ +# frozen_string_literal: true +require 'test/unit' +require 'open-uri' +require 'stringio' +require_relative 'utils' + +class TestOpenURIProxy < Test::Unit::TestCase + include TestOpenURIUtils + + def with_env(h) + begin + old = {} + h.each_key {|k| old[k] = ENV[k] } + h.each {|k, v| ENV[k] = v } + yield + ensure + h.each_key {|k| ENV[k] = old[k] } + end + end + + def setup + @proxies = %w[http_proxy HTTP_PROXY ftp_proxy FTP_PROXY no_proxy] + @old_proxies = @proxies.map {|k| ENV[k] } + @proxies.each {|k| ENV[k] = nil } + end + + def teardown + @proxies.each_with_index {|k, i| ENV[k] = @old_proxies[i] } + end + + def test_proxy + with_http {|srv, url| + proxy_log = StringIO.new(''.dup) + proxy_access_log = StringIO.new(''.dup) + proxy_auth_log = ''.dup + proxy_host = '127.0.0.1' + proxy = SimpleHTTPProxyServer.new(proxy_host, 0, lambda {|req, res| + proxy_auth_log << req.request_line + }, proxy_log, proxy_access_log) + proxy_port = proxy.instance_variable_get(:@server).addr[1] + proxy_url = "http://#{proxy_host}:#{proxy_port}/" + begin + proxy_thread = proxy.start + srv.mount_proc("/proxy", lambda { |req, res| res.body = "proxy" } ) + URI.open("#{url}/proxy", :proxy=>proxy_url) {|f| + assert_equal("200", f.status[0]) + assert_equal("proxy", f.read) + } + assert_match(/#{Regexp.quote url}/, proxy_auth_log); proxy_auth_log.clear + URI.open("#{url}/proxy", :proxy=>URI(proxy_url)) {|f| + assert_equal("200", f.status[0]) + assert_equal("proxy", f.read) + } + assert_match(/#{Regexp.quote url}/, proxy_auth_log); proxy_auth_log.clear + URI.open("#{url}/proxy", :proxy=>nil) {|f| + assert_equal("200", f.status[0]) + assert_equal("proxy", f.read) + } + assert_equal("", proxy_auth_log); proxy_auth_log.clear + assert_raise(ArgumentError) { + URI.open("#{url}/proxy", :proxy=>:invalid) {} + } + assert_equal("", proxy_auth_log); proxy_auth_log.clear + with_env("http_proxy"=>proxy_url) { + # should not use proxy for 127.0.0.0/8. + URI.open("#{url}/proxy") {|f| + assert_equal("200", f.status[0]) + assert_equal("proxy", f.read) + } + } + assert_equal("", proxy_auth_log); proxy_auth_log.clear + ensure + proxy.shutdown + proxy_thread.join + end + assert_equal("", proxy_log.string) + } + end + + def test_proxy_http_basic_authentication_failure + with_http {|srv, url| + proxy_log = StringIO.new(''.dup) + proxy_access_log = StringIO.new(''.dup) + proxy_auth_log = ''.dup + proxy_host = '127.0.0.1' + proxy = SimpleHTTPProxyServer.new(proxy_host, 0, lambda {|req, res| + proxy_auth_log << req.request_line + if req["Proxy-Authorization"] != "Basic #{['user:pass'].pack('m').chomp}" + raise ProxyAuthenticationRequired + end + }, proxy_log, proxy_access_log) + proxy_port = proxy.instance_variable_get(:@server).addr[1] + proxy_url = "http://#{proxy_host}:#{proxy_port}/" + begin + th = proxy.start + srv.mount_proc("/proxy", lambda { |req, res| res.body = "proxy" } ) + exc = assert_raise(OpenURI::HTTPError) { URI.open("#{url}/proxy", :proxy=>proxy_url) {} } + assert_equal("407", exc.io.status[0]) + assert_match(/#{Regexp.quote url}/, proxy_auth_log); proxy_auth_log.clear + ensure + proxy.shutdown + th.join + end + assert_match(/ERROR ProxyAuthenticationRequired/, proxy_log.string) + } + end + + def test_proxy_http_basic_authentication_success + with_http {|srv, url| + proxy_log = StringIO.new(''.dup) + proxy_access_log = StringIO.new(''.dup) + proxy_auth_log = ''.dup + proxy_host = '127.0.0.1' + proxy = SimpleHTTPProxyServer.new(proxy_host, 0, lambda {|req, res| + proxy_auth_log << req.request_line + if req["Proxy-Authorization"] != "Basic #{['user:pass'].pack('m').chomp}" + raise ProxyAuthenticationRequired + end + }, proxy_log, proxy_access_log) + proxy_port = proxy.instance_variable_get(:@server).addr[1] + proxy_url = "http://#{proxy_host}:#{proxy_port}/" + begin + th = proxy.start + srv.mount_proc("/proxy", lambda { |req, res| res.body = "proxy" } ) + URI.open("#{url}/proxy", + :proxy_http_basic_authentication=>[proxy_url, "user", "pass"]) {|f| + assert_equal("200", f.status[0]) + assert_equal("proxy", f.read) + } + assert_match(/#{Regexp.quote url}/, proxy_auth_log); proxy_auth_log.clear + assert_raise(ArgumentError) { + URI.open("#{url}/proxy", + :proxy_http_basic_authentication=>[true, "user", "pass"]) {} + } + assert_equal("", proxy_auth_log); proxy_auth_log.clear + ensure + proxy.shutdown + th.join + end + assert_equal("", proxy_log.string) + } + end + + def test_authenticated_proxy_http_basic_authentication_success + with_http {|srv, url| + proxy_log = StringIO.new(''.dup) + proxy_access_log = StringIO.new(''.dup) + proxy_auth_log = ''.dup + proxy_host = '127.0.0.1' + proxy = SimpleHTTPProxyServer.new(proxy_host, 0, lambda {|req, res| + proxy_auth_log << req.request_line + if req["Proxy-Authorization"] != "Basic #{['user:pass'].pack('m').chomp}" + raise ProxyAuthenticationRequired + end + }, proxy_log, proxy_access_log) + proxy_port = proxy.instance_variable_get(:@server).addr[1] + proxy_url = "http://user:pass@#{proxy_host}:#{proxy_port}/" + begin + th = proxy.start + srv.mount_proc("/proxy", lambda { |req, res| res.body = "proxy" } ) + URI.open("#{url}/proxy", :proxy => proxy_url) {|f| + assert_equal("200", f.status[0]) + assert_equal("proxy", f.read) + } + assert_match(/#{Regexp.quote url}/, proxy_auth_log); proxy_auth_log.clear + assert_equal("", proxy_auth_log); proxy_auth_log.clear + ensure + proxy.shutdown + th.join + end + assert_equal("", proxy_log.string) + } + end +end diff --git a/test/open-uri/test_ssl.rb b/test/open-uri/test_ssl.rb index 3f94cab40fe82f..389391e685fc59 100644 --- a/test/open-uri/test_ssl.rb +++ b/test/open-uri/test_ssl.rb @@ -1,59 +1,14 @@ # frozen_string_literal: true require 'test/unit' require 'open-uri' -require 'stringio' -require 'webrick' +require_relative 'utils' begin require 'openssl' - require 'webrick/https' rescue LoadError end -require 'webrick/httpproxy' class TestOpenURISSL < Test::Unit::TestCase -end - -class TestOpenURISSL - NullLog = Object.new - def NullLog.<<(arg) - end - - def with_https(log_tester=lambda {|log| assert_equal([], log) }) - log = [] - logger = WEBrick::Log.new(log, WEBrick::BasicLog::WARN) - Dir.mktmpdir {|dr| - srv = WEBrick::HTTPServer.new({ - :DocumentRoot => dr, - :ServerType => Thread, - :Logger => logger, - :AccessLog => [[NullLog, ""]], - :SSLEnable => true, - :SSLCertificate => OpenSSL::X509::Certificate.new(SERVER_CERT), - :SSLPrivateKey => OpenSSL::PKey::RSA.new(SERVER_KEY), - :SSLTmpDhCallback => proc { OpenSSL::PKey::DH.new(DHPARAMS) }, - :BindAddress => '127.0.0.1', - :Port => 0}) - _, port, _, host = srv.listeners[0].addr - threads = [] - server_thread = srv.start - threads << Thread.new { - server_thread.join - if log_tester - log_tester.call(log) - end - } - threads << Thread.new { - begin - yield srv, dr, "https://#{host}:#{port}", server_thread, log, threads - ensure - srv.shutdown - end - } - assert_join_threads(threads) - } - ensure - WEBrick::Utils::TimeoutHandler.terminate - end + include TestOpenURIUtils def setup @proxies = %w[http_proxy HTTP_PROXY https_proxy HTTPS_PROXY ftp_proxy FTP_PROXY no_proxy] @@ -68,7 +23,9 @@ def teardown def setup_validation(srv, dr) cacert_filename = "#{dr}/cacert.pem" URI.open(cacert_filename, "w") {|f| f << CA_CERT } - srv.mount_proc("/data", lambda { |req, res| res.body = "ddd" } ) + if srv.respond_to?(:mount_proc) + srv.mount_proc("/data", lambda { |req, res| res.body = "ddd" } ) + end cacert_filename end @@ -93,17 +50,10 @@ def test_validation_noverify end def test_validation_failure - unless /mswin|mingw/ =~ RUBY_PLATFORM - # on Windows, Errno::ECONNRESET will be raised, and it'll be eaten by - # WEBrick - log_tester = lambda {|server_log| - assert_equal(1, server_log.length) - assert_match(/ERROR OpenSSL::SSL::SSLError:/, server_log[0]) - } - end - with_https(log_tester) {|srv, dr, url, server_thread, server_log| + with_https(nil) {|srv, dr, url| setup_validation(srv, dr) assert_raise(OpenSSL::SSL::SSLError) { URI.open("#{url}/data") {} } + sleep 0.5 unless RUBY_PLATFORM =~ /mswin|mingw/ } end @@ -126,39 +76,6 @@ def test_bad_ssl_version } end - def with_https_proxy(proxy_log_tester=lambda {|proxy_log, proxy_access_log| assert_equal([], proxy_log) }) - proxy_log = [] - proxy_logger = WEBrick::Log.new(proxy_log, WEBrick::BasicLog::WARN) - with_https {|srv, dr, url, server_thread, server_log, threads| - srv.mount_proc("/proxy", lambda { |req, res| res.body = "proxy" } ) - cacert_filename = "#{dr}/cacert.pem" - open(cacert_filename, "w") {|f| f << CA_CERT } - cacert_directory = "#{dr}/certs" - Dir.mkdir cacert_directory - hashed_name = "%08x.0" % OpenSSL::X509::Certificate.new(CA_CERT).subject.hash - open("#{cacert_directory}/#{hashed_name}", "w") {|f| f << CA_CERT } - proxy = WEBrick::HTTPProxyServer.new({ - :ServerType => Thread, - :Logger => proxy_logger, - :AccessLog => [[proxy_access_log=[], WEBrick::AccessLog::COMMON_LOG_FORMAT]], - :BindAddress => '127.0.0.1', - :Port => 0}) - _, proxy_port, _, proxy_host = proxy.listeners[0].addr - proxy_thread = proxy.start - threads << Thread.new { - proxy_thread.join - if proxy_log_tester - proxy_log_tester.call(proxy_log, proxy_access_log) - end - } - begin - yield srv, dr, url, cacert_filename, cacert_directory, proxy_host, proxy_port - ensure - proxy.shutdown - end - } - end - def test_proxy_cacert_file url = nil proxy_log_tester = lambda {|proxy_log, proxy_access_log| @@ -192,341 +109,3 @@ def test_proxy_cacert_dir end end if defined?(OpenSSL::SSL) - -if defined?(OpenSSL::SSL) -# cp /etc/ssl/openssl.cnf . # I copied from OpenSSL 1.1.1b source - -# mkdir demoCA demoCA/private demoCA/newcerts -# touch demoCA/index.txt -# echo 00 > demoCA/serial -# openssl genrsa -des3 -out demoCA/private/cakey.pem 2048 -# openssl req -new -key demoCA/private/cakey.pem -out demoCA/careq.pem -subj "/C=JP/ST=Tokyo/O=RubyTest/CN=Ruby Test CA" -# # basicConstraints=CA:TRUE is required; the default openssl.cnf has it in [v3_ca] -# openssl ca -config openssl.cnf -extensions v3_ca -out demoCA/cacert.pem -startdate 090101000000Z -enddate 491231235959Z -batch -keyfile demoCA/private/cakey.pem -selfsign -infiles demoCA/careq.pem - -# mkdir server -# openssl genrsa -des3 -out server/server.key 2048 -# openssl req -new -key server/server.key -out server/csr.pem -subj "/C=JP/ST=Tokyo/O=RubyTest/CN=127.0.0.1" -# openssl ca -config openssl.cnf -startdate 090101000000Z -enddate 491231235959Z -in server/csr.pem -keyfile demoCA/private/cakey.pem -cert demoCA/cacert.pem -out server/cert.pem - -# demoCA/cacert.pem => TestOpenURISSL::CA_CERT -# server/cert.pem => TestOpenURISSL::SERVER_CERT -# `openssl rsa -in server/server.key -text` => TestOpenURISSL::SERVER_KEY - -TestOpenURISSL::CA_CERT = <<'End' -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 0 (0x0) - Signature Algorithm: sha256WithRSAEncryption - Issuer: C=JP, ST=Tokyo, O=RubyTest, CN=Ruby Test CA - Validity - Not Before: Jan 1 00:00:00 2009 GMT - Not After : Dec 31 23:59:59 2049 GMT - Subject: C=JP, ST=Tokyo, O=RubyTest, CN=Ruby Test CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public-Key: (2048 bit) - Modulus: - 00:ad:f3:4d:5b:0b:01:54:cc:86:36:d1:93:6b:33: - 56:25:90:61:d6:9a:a0:f4:24:20:ee:c8:14:ab:0f: - 4b:89:d8:7c:bb:c0:f8:7f:fb:e9:a2:d5:1c:6b:6f: - dc:5c:23:b1:49:aa:2c:e8:ca:43:48:64:69:4b:8a: - bd:44:57:9b:14:d9:7a:b2:49:00:d6:c2:74:67:62: - 52:1d:a9:32:df:fe:7a:22:20:49:83:e1:cb:3d:dc: - 1a:2a:f0:36:20:c1:e8:c8:89:d4:51:1a:68:91:20: - e0:ba:67:0a:b2:6b:f8:e3:8c:f5:ee:a1:36:b1:89: - ec:23:b6:f2:39:a9:b9:2e:ea:de:d9:86:e5:42:11: - 46:ed:10:9a:90:76:44:4e:4d:49:2d:49:e8:e3:cb: - ff:7a:7d:80:cb:bf:c4:c3:69:ba:9c:60:4a:de:af: - bf:26:78:b8:fb:46:d1:37:d0:89:ba:78:93:6a:37: - a5:e9:58:e7:e2:e3:7d:7c:95:20:79:41:56:15:cd: - b2:c6:3b:e1:b7:e7:ba:47:60:9a:05:b1:07:f3:26: - 72:9d:3b:1b:02:18:3d:d5:de:e6:e9:30:a9:b5:8f: - 15:1b:40:f9:64:61:54:d3:53:e8:c4:29:4a:89:f3: - e5:0d:fd:16:61:ee:f2:6d:8a:45:a8:34:7e:53:46: - 8e:87 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Subject Key Identifier: - A0:7E:0B:AD:A3:AD:37:D7:21:0B:75:6F:8A:90:5F:8C:C9:69:DF:98 - X509v3 Authority Key Identifier: - keyid:A0:7E:0B:AD:A3:AD:37:D7:21:0B:75:6F:8A:90:5F:8C:C9:69:DF:98 - - X509v3 Basic Constraints: critical - CA:TRUE - Signature Algorithm: sha256WithRSAEncryption - 06:ea:06:02:19:9a:cb:94:a2:7e:c0:86:71:66:e7:a5:71:46: - a2:25:55:f5:e5:58:df:d1:91:58:e6:8a:0e:91:b3:22:4c:88: - 4d:5f:02:af:0f:73:65:0d:af:9a:f2:e4:36:f3:1f:e8:28:1d: - 9c:74:72:5b:f7:12:e8:fa:45:d6:df:e5:f1:d3:91:f4:0e:db: - e2:56:63:ee:82:57:6f:12:ad:d7:0d:de:5a:8c:3d:76:d2:87: - c9:48:1c:c4:f3:89:63:3c:c2:25:e0:dd:63:a6:4c:6c:5a:07: - 7b:86:78:62:86:02:a1:ef:0e:41:75:c5:d4:61:ab:c3:3b:9b: - 51:0b:e6:34:6d:0b:14:5a:2d:aa:d3:58:26:43:8f:4c:d7:45: - 73:1e:67:66:5e:f3:0c:69:70:27:a1:d5:70:f3:5a:10:98:c8: - 4f:8a:3b:9f:ad:8e:8d:49:8f:fb:f6:36:5d:4f:70:f9:4f:54: - 33:cf:a2:a6:1d:8c:61:b9:30:42:f2:49:d1:3d:a1:f1:eb:1e: - 78:a6:30:f8:8a:48:89:c7:3e:bd:0d:d8:72:04:a6:00:e5:62: - a4:13:3f:9e:b6:86:25:dc:d1:ff:3a:fc:f5:0e:e4:0e:f7:b8: - 66:90:fe:4f:c2:54:2a:7f:61:6e:e7:4b:bf:40:7e:75:30:02: - 5b:bb:91:1b ------BEGIN CERTIFICATE----- -MIIDXDCCAkSgAwIBAgIBADANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJKUDEO -MAwGA1UECAwFVG9reW8xETAPBgNVBAoMCFJ1YnlUZXN0MRUwEwYDVQQDDAxSdWJ5 -IFRlc3QgQ0EwHhcNMDkwMTAxMDAwMDAwWhcNNDkxMjMxMjM1OTU5WjBHMQswCQYD -VQQGEwJKUDEOMAwGA1UECAwFVG9reW8xETAPBgNVBAoMCFJ1YnlUZXN0MRUwEwYD -VQQDDAxSdWJ5IFRlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB -AQCt801bCwFUzIY20ZNrM1YlkGHWmqD0JCDuyBSrD0uJ2Hy7wPh/++mi1Rxrb9xc -I7FJqizoykNIZGlLir1EV5sU2XqySQDWwnRnYlIdqTLf/noiIEmD4cs93Boq8DYg -wejIidRRGmiRIOC6Zwqya/jjjPXuoTaxiewjtvI5qbku6t7ZhuVCEUbtEJqQdkRO -TUktSejjy/96fYDLv8TDabqcYErer78meLj7RtE30Im6eJNqN6XpWOfi4318lSB5 -QVYVzbLGO+G357pHYJoFsQfzJnKdOxsCGD3V3ubpMKm1jxUbQPlkYVTTU+jEKUqJ -8+UN/RZh7vJtikWoNH5TRo6HAgMBAAGjUzBRMB0GA1UdDgQWBBSgfguto6031yEL -dW+KkF+MyWnfmDAfBgNVHSMEGDAWgBSgfguto6031yELdW+KkF+MyWnfmDAPBgNV -HRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAG6gYCGZrLlKJ+wIZxZuel -cUaiJVX15Vjf0ZFY5ooOkbMiTIhNXwKvD3NlDa+a8uQ28x/oKB2cdHJb9xLo+kXW -3+Xx05H0DtviVmPugldvEq3XDd5ajD120ofJSBzE84ljPMIl4N1jpkxsWgd7hnhi -hgKh7w5BdcXUYavDO5tRC+Y0bQsUWi2q01gmQ49M10VzHmdmXvMMaXAnodVw81oQ -mMhPijufrY6NSY/79jZdT3D5T1Qzz6KmHYxhuTBC8knRPaHx6x54pjD4ikiJxz69 -DdhyBKYA5WKkEz+etoYl3NH/Ovz1DuQO97hmkP5PwlQqf2Fu50u/QH51MAJbu5Eb ------END CERTIFICATE----- -End - -TestOpenURISSL::SERVER_CERT = <<'End' -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 1 (0x1) - Signature Algorithm: sha256WithRSAEncryption - Issuer: C=JP, ST=Tokyo, O=RubyTest, CN=Ruby Test CA - Validity - Not Before: Jan 1 00:00:00 2009 GMT - Not After : Dec 31 23:59:59 2049 GMT - Subject: C=JP, ST=Tokyo, O=RubyTest, CN=127.0.0.1 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public-Key: (2048 bit) - Modulus: - 00:cb:b3:71:95:12:70:fc:db:d4:a9:a7:66:d6:d3: - 09:dd:06:80:19:e1:f2:d6:1e:31:b6:6b:20:75:51: - dc:a7:37:a9:ac:5b:57:5d:69:36:b6:de:1d:2c:f6: - 44:64:f8:e8:d6:f0:da:38:6a:ba:c2:b1:9e:dc:bb: - 79:94:e0:25:0c:ce:76:87:17:5d:79:9e:14:9e:bd: - 4c:0d:aa:74:10:3a:96:ef:76:82:d5:72:16:b5:c1: - ac:17:2d:90:83:73:5c:d7:a6:f5:36:0f:4c:55:f3: - 30:5d:19:dc:01:0e:f8:e6:fe:a5:ad:52:88:59:dc: - 4a:07:ed:a2:eb:a1:01:63:c4:8a:92:ba:06:80:9b: - 0d:85:f2:9f:f9:70:ac:d7:ad:f0:7a:3f:b8:92:2a: - 33:ca:69:d0:01:65:5d:31:38:1d:f6:1f:b2:17:07: - 7e:ac:88:67:a6:c4:5f:3e:93:94:61:e6:e4:49:9d: - ba:d4:d2:e8:e3:93:d1:66:79:c5:e3:1d:f8:5a:50: - 54:58:3d:04:b0:fd:65:d1:b3:8a:b5:8a:30:5f:b2: - dc:34:1a:14:f7:74:4c:03:29:97:63:5a:d7:de:bb: - eb:7f:4a:2a:90:59:c0:2b:47:09:82:8f:75:de:14: - 3f:bc:78:9a:69:25:80:5b:6c:a0:65:12:0d:29:61: - ac:f9 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: - CA:FALSE - Netscape Comment: - OpenSSL Generated Certificate - X509v3 Subject Key Identifier: - EC:6B:7C:79:B8:3B:11:1D:42:F3:9A:2A:CF:9A:15:59:D7:F9:D8:C6 - X509v3 Authority Key Identifier: - keyid:A0:7E:0B:AD:A3:AD:37:D7:21:0B:75:6F:8A:90:5F:8C:C9:69:DF:98 - - Signature Algorithm: sha256WithRSAEncryption - 29:14:db:71:e9:a0:86:f8:cc:4d:e4:8a:76:78:a7:ff:4e:94: - b4:4d:92:dc:57:9a:52:64:46:27:15:8b:4f:2a:18:a7:0d:fc: - d2:75:ce:4e:49:97:0b:46:71:57:23:e3:a5:c0:c5:71:94:fc: - f2:1d:3b:06:93:82:03:59:56:d4:fb:09:06:08:b4:97:50:33: - cf:58:89:dd:91:31:07:26:9a:7e:7f:8d:71:de:09:dc:4f:e5: - 6b:a3:10:71:d4:50:24:43:a0:1c:f5:2a:d9:1a:fb:e3:d6:f1: - bc:6b:42:67:16:b4:3b:31:f4:ec:03:7d:78:e2:64:16:57:6d: - ba:7c:0c:e1:14:b2:7c:75:4e:2b:09:3e:86:e4:aa:cc:7e:5c: - 2b:bd:8d:26:4d:49:36:74:86:fe:c5:a6:15:4a:af:e8:b4:4e: - d5:f2:e1:59:c2:fb:7e:c3:c4:f1:63:d8:c2:b0:9a:ae:31:96: - 90:c3:09:d0:ce:2e:31:90:d7:83:dd:ac:31:cc:f7:87:41:08: - 92:33:28:52:fa:2d:9e:ad:ae:6a:9f:c3:be:ce:c1:a6:e4:16: - 2f:69:34:40:86:b6:10:21:0e:31:69:81:9e:fc:fd:c3:06:25: - 65:37:d3:d9:4a:20:84:aa:e7:0e:60:7c:bf:3f:88:67:ac:e5: - 8c:e0:61:d6 ------BEGIN CERTIFICATE----- -MIIDgTCCAmmgAwIBAgIBATANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJKUDEO -MAwGA1UECAwFVG9reW8xETAPBgNVBAoMCFJ1YnlUZXN0MRUwEwYDVQQDDAxSdWJ5 -IFRlc3QgQ0EwHhcNMDkwMTAxMDAwMDAwWhcNNDkxMjMxMjM1OTU5WjBEMQswCQYD -VQQGEwJKUDEOMAwGA1UECAwFVG9reW8xETAPBgNVBAoMCFJ1YnlUZXN0MRIwEAYD -VQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDL -s3GVEnD829Spp2bW0wndBoAZ4fLWHjG2ayB1UdynN6msW1ddaTa23h0s9kRk+OjW -8No4arrCsZ7cu3mU4CUMznaHF115nhSevUwNqnQQOpbvdoLVcha1wawXLZCDc1zX -pvU2D0xV8zBdGdwBDvjm/qWtUohZ3EoH7aLroQFjxIqSugaAmw2F8p/5cKzXrfB6 -P7iSKjPKadABZV0xOB32H7IXB36siGemxF8+k5Rh5uRJnbrU0ujjk9FmecXjHfha -UFRYPQSw/WXRs4q1ijBfstw0GhT3dEwDKZdjWtfeu+t/SiqQWcArRwmCj3XeFD+8 -eJppJYBbbKBlEg0pYaz5AgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgEN -BB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBTsa3x5 -uDsRHULzmirPmhVZ1/nYxjAfBgNVHSMEGDAWgBSgfguto6031yELdW+KkF+MyWnf -mDANBgkqhkiG9w0BAQsFAAOCAQEAKRTbcemghvjMTeSKdnin/06UtE2S3FeaUmRG -JxWLTyoYpw380nXOTkmXC0ZxVyPjpcDFcZT88h07BpOCA1lW1PsJBgi0l1Azz1iJ -3ZExByaafn+Ncd4J3E/la6MQcdRQJEOgHPUq2Rr749bxvGtCZxa0OzH07AN9eOJk -FldtunwM4RSyfHVOKwk+huSqzH5cK72NJk1JNnSG/sWmFUqv6LRO1fLhWcL7fsPE -8WPYwrCarjGWkMMJ0M4uMZDXg92sMcz3h0EIkjMoUvotnq2uap/Dvs7BpuQWL2k0 -QIa2ECEOMWmBnvz9wwYlZTfT2UoghKrnDmB8vz+IZ6zljOBh1g== ------END CERTIFICATE----- -End - -TestOpenURISSL::SERVER_KEY = <<'End' -RSA Private-Key: (2048 bit, 2 primes) -modulus: - 00:cb:b3:71:95:12:70:fc:db:d4:a9:a7:66:d6:d3: - 09:dd:06:80:19:e1:f2:d6:1e:31:b6:6b:20:75:51: - dc:a7:37:a9:ac:5b:57:5d:69:36:b6:de:1d:2c:f6: - 44:64:f8:e8:d6:f0:da:38:6a:ba:c2:b1:9e:dc:bb: - 79:94:e0:25:0c:ce:76:87:17:5d:79:9e:14:9e:bd: - 4c:0d:aa:74:10:3a:96:ef:76:82:d5:72:16:b5:c1: - ac:17:2d:90:83:73:5c:d7:a6:f5:36:0f:4c:55:f3: - 30:5d:19:dc:01:0e:f8:e6:fe:a5:ad:52:88:59:dc: - 4a:07:ed:a2:eb:a1:01:63:c4:8a:92:ba:06:80:9b: - 0d:85:f2:9f:f9:70:ac:d7:ad:f0:7a:3f:b8:92:2a: - 33:ca:69:d0:01:65:5d:31:38:1d:f6:1f:b2:17:07: - 7e:ac:88:67:a6:c4:5f:3e:93:94:61:e6:e4:49:9d: - ba:d4:d2:e8:e3:93:d1:66:79:c5:e3:1d:f8:5a:50: - 54:58:3d:04:b0:fd:65:d1:b3:8a:b5:8a:30:5f:b2: - dc:34:1a:14:f7:74:4c:03:29:97:63:5a:d7:de:bb: - eb:7f:4a:2a:90:59:c0:2b:47:09:82:8f:75:de:14: - 3f:bc:78:9a:69:25:80:5b:6c:a0:65:12:0d:29:61: - ac:f9 -publicExponent: 65537 (0x10001) -privateExponent: - 12:be:d5:b2:01:3b:72:99:8c:4d:7c:81:43:3d:b2: - 87:ab:84:78:5d:49:aa:98:a6:bc:81:c9:3f:e2:a3: - aa:a3:bd:b2:85:c9:59:68:48:47:b5:d2:fb:83:42: - 32:04:91:f0:cd:c3:57:33:c3:32:0d:84:70:0d:b4: - 97:95:b4:f3:23:c0:d6:97:b8:db:6b:47:bc:7f:f1: - 12:c4:df:df:6a:74:df:5e:89:95:b8:e5:0c:1e:e1: - 86:54:84:1b:04:af:c3:8c:b2:be:21:d4:45:88:96: - a7:ca:ac:6b:50:84:69:45:7f:db:9e:5f:bb:dd:40: - d6:cf:f0:91:3c:84:d3:38:65:c9:15:f7:9e:37:aa: - 1a:2e:bc:16:b6:95:be:bc:af:45:76:ba:ad:99:f6: - ef:6a:e8:fd:f0:31:89:19:c4:04:67:a1:ec:c4:79: - 59:08:77:ab:0b:65:88:88:02:b1:38:5c:80:4e:27: - 78:b2:a5:bd:b5:ad:d5:9c:4c:ea:ad:db:05:56:25: - 70:28:da:22:fb:d8:de:8c:3b:78:fe:3e:cf:ed:1b: - f9:97:c6:b6:4a:bf:60:08:8f:dc:85:5e:b1:49:ab: - 87:8b:68:72:f4:6a:3f:bc:db:a3:6c:f7:e8:b0:15: - bb:4b:ba:37:49:a2:d1:7c:f8:4f:1b:05:11:22:d9: - 81 -prime1: - 00:fb:d2:cb:14:61:00:c1:7a:83:ba:fe:79:97:a2: - 4d:5a:ea:40:78:96:6e:d2:be:71:5b:c6:2c:1f:c9: - 18:48:6b:ae:20:86:87:b5:08:0b:17:69:ca:93:cd: - 00:36:22:51:7b:d5:2d:8c:0c:0e:de:bc:86:a8:07: - 0e:c5:57:e4:df:be:ed:7d:cc:b1:a4:d6:a8:2b:00: - 65:2a:69:30:5e:dc:6d:6d:c4:c8:7e:20:34:eb:6f: - 5e:cf:b3:b8:2e:8d:56:31:44:a8:17:ea:be:65:19: - ff:da:14:e0:0c:73:56:14:08:47:4c:5b:79:51:74: - 5d:bc:e7:fe:01:2f:55:27:69 -prime2: - 00:cf:14:54:47:bb:5f:5d:d6:2b:2d:ed:a6:8a:6f: - 36:fc:47:5e:9f:84:ae:aa:1f:f8:44:50:91:15:f5: - ed:9d:29:d9:2b:2a:19:66:56:2e:96:15:b5:8e:a9: - 7f:89:27:21:b5:57:55:7e:2a:c5:8c:93:fe:f6:0a: - a5:17:15:91:91:b3:7d:35:1a:d5:9a:2e:b8:0d:ad: - e6:97:6d:83:a3:27:29:ee:00:74:ef:57:34:f3:07: - ad:12:43:37:0c:5c:b7:26:34:bc:4e:3a:43:65:6b: - 0c:b8:23:ac:77:fd:b2:23:eb:7b:65:70:f6:96:c4: - 17:2c:aa:24:b8:a5:5e:b7:11 -exponent1: - 00:92:32:ae:f4:05:dd:0a:76:b6:43:b9:b9:9d:ee: - fc:39:ec:05:c1:fc:94:1a:85:b6:0a:31:e3:2c:10: - f3:a8:17:db:df:c6:3a:c3:3f:08:31:6f:99:cc:75: - 17:ca:55:e2:38:a2:6a:ef:03:91:1e:7f:15:2e:37: - ea:bb:67:6b:d8:fa:5f:a6:c9:4f:d9:03:46:5e:b0: - bc:0b:03:46:b1:cc:07:3b:d3:23:13:16:5f:a2:cf: - e5:9b:70:1b:5d:eb:70:3e:ea:3d:2c:a5:7c:23:f6: - 14:33:e8:2a:ab:0f:ca:c9:96:84:ce:2f:cd:1f:1d: - 0f:ce:bc:61:1b:0e:ff:c1:01 -exponent2: - 00:9e:0b:f3:03:48:73:d1:e7:9a:cf:13:f9:ae:e0: - 91:03:dc:e8:d0:30:f1:2a:30:fa:48:11:81:9a:54: - 37:c5:62:e2:37:fa:8a:a6:3b:92:94:c3:fe:ec:e2: - 5a:cf:70:09:5f:21:47:c3:e2:9b:21:de:f6:92:0c: - af:d1:bd:89:7b:bd:95:0b:49:ee:cb:1d:6b:26:2d: - 9a:b7:ea:42:b4:ec:38:29:49:39:f6:4e:05:c0:93: - 14:39:c3:09:29:ab:3d:b1:b0:40:24:28:7d:b5:d3: - 0d:43:21:1f:09:f9:9b:d3:a4:6f:6a:8d:db:f6:57: - b5:24:46:bb:7e:1d:e0:fb:31 -coefficient: - 10:93:1d:c8:33:a5:c1:d3:84:6a:22:68:e5:60:cc: - 9c:27:0a:52:0b:58:a3:0c:83:f4:f4:46:09:0c:a1: - 41:a6:ea:bf:80:9d:0e:5d:d8:3d:25:00:c5:a1:35: - 7a:8c:ea:95:16:94:c3:7c:8f:2b:e0:53:ea:66:ae: - 19:be:55:04:3d:ee:e2:4b:a8:69:1b:7e:d8:09:7f: - ed:7c:ee:95:88:10:dc:4b:5b:bf:81:a4:e8:dc:7e: - 4f:e5:c3:90:c4:e5:5a:90:10:32:d6:08:b5:1f:5d: - 09:18:d8:44:28:e4:c4:c7:07:75:9b:9b:b3:80:86: - 68:9d:fe:68:f3:4d:db:66 -writing RSA key ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAy7NxlRJw/NvUqadm1tMJ3QaAGeHy1h4xtmsgdVHcpzeprFtX -XWk2tt4dLPZEZPjo1vDaOGq6wrGe3Lt5lOAlDM52hxddeZ4Unr1MDap0EDqW73aC -1XIWtcGsFy2Qg3Nc16b1Ng9MVfMwXRncAQ745v6lrVKIWdxKB+2i66EBY8SKkroG -gJsNhfKf+XCs163wej+4kiozymnQAWVdMTgd9h+yFwd+rIhnpsRfPpOUYebkSZ26 -1NLo45PRZnnF4x34WlBUWD0EsP1l0bOKtYowX7LcNBoU93RMAymXY1rX3rvrf0oq -kFnAK0cJgo913hQ/vHiaaSWAW2ygZRINKWGs+QIDAQABAoIBABK+1bIBO3KZjE18 -gUM9soerhHhdSaqYpryByT/io6qjvbKFyVloSEe10vuDQjIEkfDNw1czwzINhHAN -tJeVtPMjwNaXuNtrR7x/8RLE399qdN9eiZW45Qwe4YZUhBsEr8OMsr4h1EWIlqfK -rGtQhGlFf9ueX7vdQNbP8JE8hNM4ZckV9543qhouvBa2lb68r0V2uq2Z9u9q6P3w -MYkZxARnoezEeVkId6sLZYiIArE4XIBOJ3iypb21rdWcTOqt2wVWJXAo2iL72N6M -O3j+Ps/tG/mXxrZKv2AIj9yFXrFJq4eLaHL0aj+826Ns9+iwFbtLujdJotF8+E8b -BREi2YECgYEA+9LLFGEAwXqDuv55l6JNWupAeJZu0r5xW8YsH8kYSGuuIIaHtQgL -F2nKk80ANiJRe9UtjAwO3ryGqAcOxVfk377tfcyxpNaoKwBlKmkwXtxtbcTIfiA0 -629ez7O4Lo1WMUSoF+q+ZRn/2hTgDHNWFAhHTFt5UXRdvOf+AS9VJ2kCgYEAzxRU -R7tfXdYrLe2mim82/Eden4Suqh/4RFCRFfXtnSnZKyoZZlYulhW1jql/iSchtVdV -firFjJP+9gqlFxWRkbN9NRrVmi64Da3ml22Doycp7gB071c08wetEkM3DFy3JjS8 -TjpDZWsMuCOsd/2yI+t7ZXD2lsQXLKokuKVetxECgYEAkjKu9AXdCna2Q7m5ne78 -OewFwfyUGoW2CjHjLBDzqBfb38Y6wz8IMW+ZzHUXylXiOKJq7wORHn8VLjfqu2dr -2PpfpslP2QNGXrC8CwNGscwHO9MjExZfos/lm3AbXetwPuo9LKV8I/YUM+gqqw/K -yZaEzi/NHx0PzrxhGw7/wQECgYEAngvzA0hz0eeazxP5ruCRA9zo0DDxKjD6SBGB -mlQ3xWLiN/qKpjuSlMP+7OJaz3AJXyFHw+KbId72kgyv0b2Je72VC0nuyx1rJi2a -t+pCtOw4KUk59k4FwJMUOcMJKas9sbBAJCh9tdMNQyEfCfmb06Rvao3b9le1JEa7 -fh3g+zECgYAQkx3IM6XB04RqImjlYMycJwpSC1ijDIP09EYJDKFBpuq/gJ0OXdg9 -JQDFoTV6jOqVFpTDfI8r4FPqZq4ZvlUEPe7iS6hpG37YCX/tfO6ViBDcS1u/gaTo -3H5P5cOQxOVakBAy1gi1H10JGNhEKOTExwd1m5uzgIZonf5o803bZg== ------END RSA PRIVATE KEY----- -End - -TestOpenURISSL::DHPARAMS = <<'End' - DH Parameters: (2048 bit) - prime: - 00:ec:4e:a4:06:b6:22:ca:f9:8a:00:cc:d0:ee:2f: - 16:bf:05:64:f5:8f:fe:7f:c4:bb:b0:24:cd:ef:5d: - 8a:90:ad:dc:a9:dd:63:84:90:d8:25:ba:d8:78:d5: - 77:91:42:0a:84:fc:56:1e:13:9b:1c:aa:43:d5:1f: - 38:52:92:fe:b3:66:f9:e7:e8:8c:77:a1:a6:2f:b3: - 98:98:d2:13:fc:57:1c:2a:14:dc:bd:e6:9b:54:19: - 99:4f:ce:81:64:a6:32:7f:8e:61:50:5f:45:3a:e5: - 0c:f7:13:f3:b8:ad:d5:77:ca:09:42:f7:d8:30:27: - 7b:2c:f0:b4:b5:a0:04:96:34:0b:47:81:1d:7f:c1: - 3a:62:86:8e:7d:f8:13:7f:9a:b1:8b:09:23:9e:55: - 59:41:cd:f0:86:09:c4:b7:d1:69:54:cb:d0:f5:e9: - 27:c9:e1:81:e4:a1:df:6b:20:1c:df:e8:54:02:f2: - 37:fc:2a:f7:d5:b3:6f:79:7e:70:22:78:79:18:3c: - 75:14:68:4a:05:9f:ac:d4:7f:9a:79:db:9d:0a:6e: - ec:0a:04:70:bf:c9:4a:59:81:a2:1f:33:9b:4a:66: - bc:03:ce:8a:1b:e3:03:ec:ba:39:26:ab:90:dc:39: - 41:a1:d8:f7:20:3c:8f:af:12:2f:f7:a9:6f:44:f1: - 6d:03 - generator: 2 (0x2) ------BEGIN DH PARAMETERS----- -MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY -JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab -VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6 -YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3 -1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD -7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg== ------END DH PARAMETERS----- -End - -end diff --git a/test/open-uri/utils.rb b/test/open-uri/utils.rb new file mode 100644 index 00000000000000..e7ca64bd1c2dd0 --- /dev/null +++ b/test/open-uri/utils.rb @@ -0,0 +1,738 @@ +require 'socket' +require 'net/http' +begin + require 'openssl' +rescue LoadError +end + +class SimpleHTTPServer + def initialize(bind_addr, port, log) + @server = TCPServer.new(bind_addr, port) + @log = log + @procs = {} + end + + def mount_proc(path, proc) + @procs[path] = proc + end + + def start + @thread = Thread.new do + loop do + client = @server.accept + handle_request(client) + client.close + end + end + end + + def shutdown + @thread.kill + @server.close + end + + private + + def handle_request(client) + request_line = client.gets + return if request_line.nil? + + method, path, _ = request_line.split + headers = {} + while (line = client.gets) && line != "\r\n" + key, value = line.split(": ", 2) + headers[key.downcase] = value.strip + end + + if @procs.key?(path) || @procs.key?("#{path}/") + proc = @procs[path] || @procs["#{path}/"] + req = Request.new(method, path, headers) + res = Response.new(client) + proc.call(req, res) + res.finish + else + @log << "ERROR `#{path}' not found" + client.print "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n" + end + rescue ::TestOpenURI::Unauthorized + @log << "ERROR Unauthorized" + client.print "HTTP/1.1 401 Unauthorized\r\nContent-Length: 0\r\n\r\n" + end + + class Request + attr_reader :method, :path, :headers + def initialize(method, path, headers) + @method = method + @path = path + @headers = headers + parse_basic_auth + end + + def [](key) + @headers[key.downcase] + end + + def []=(key, value) + @headers[key.downcase] = value + end + + private + + def parse_basic_auth + auth = @headers['Authorization'] + return unless auth && auth.start_with?('Basic ') + + encoded_credentials = auth.split(' ', 2).last + decoded_credentials = [encoded_credentials].pack("m") + @username, @password = decoded_credentials.split(':', 2) + end + end + + class Response + attr_accessor :body, :headers, :status, :chunked, :cookies + def initialize(client) + @client = client + @body = "" + @headers = {} + @status = 200 + @chunked = false + @cookies = [] + end + + def [](key) + @headers[key.downcase] + end + + def []=(key, value) + @headers[key.downcase] = value + end + + def write_chunk(chunk) + return unless @chunked + @client.write("#{chunk.bytesize.to_s(16)}\r\n") + @client.write("#{chunk}\r\n") + end + + def finish + @client.write build_response_headers + if @chunked + write_chunk(@body) + @client.write "0\r\n\r\n" + else + @client.write @body + end + end + + private + + def build_response_headers + response = "HTTP/1.1 #{@status} #{status_message(@status)}\r\n" + if @chunked + @headers['Transfer-Encoding'] = 'chunked' + else + @headers['Content-Length'] = @body.bytesize.to_s + end + @headers.each do |key, value| + response << "#{key}: #{value}\r\n" + end + @cookies.each do |cookie| + response << "Set-Cookie: #{cookie}\r\n" + end + response << "\r\n" + response + end + + def status_message(code) + case code + when 200 then 'OK' + when 301 then 'Moved Permanently' + else 'Unknown' + end + end + end +end + +class SimpleHTTPProxyServer + def initialize(host, port, auth_proc = nil, log, access_log) + @server = TCPServer.new(host, port) + @auth_proc = auth_proc + @log = log + @access_log = access_log + end + + def start + @thread = Thread.new do + loop do + client = @server.accept + request_line = client.gets + headers = {} + while (line = client.gets) && (line != "\r\n") + key, value = line.chomp.split(/:\s*/, 2) + headers[key] = value + end + next unless request_line + + method, path, _ = request_line.split(' ') + handle_request(client, method, path, request_line, headers) + rescue IOError + end + end + end + + def shutdown + @thread.kill + @server.close + end + + private + + def handle_request(client, method, path, request_line, headers) + if @auth_proc + req = Request.new(method, path, request_line, headers) + res = Struct.new(:body, :status).new("", 200) + @auth_proc.call(req, res) + if res.status != 200 + client.print "HTTP/1.1 #{res.status}\r\nContent-Type: text/plain\r\n\r\n#{res.body}" + return + end + end + + if method == 'CONNECT' + proxy_connect(path, client) + else + proxy_request(path, client) + end + rescue TestOpenURIProxy::ProxyAuthenticationRequired + @log << "ERROR ProxyAuthenticationRequired" + client.print "HTTP/1.1 407 Proxy Authentication Required\r\nContent-Length: 0\r\n\r\n" + ensure + client.close + end + + def proxy_connect(path, client) + host, port = path.split(':') + backend = TCPSocket.new(host, port.to_i) + client.puts "HTTP/1.1 200 Connection Established\r\n\r\n" + @access_log << "CONNECT #{path} \n" + begin + while fds = IO.select([client, backend]) + if fds[0].include?(client) + data = client.readpartial(1024) + backend.write(data) + elsif fds[0].include?(backend) + data = backend.readpartial(1024) + client.write(data) + end + end + rescue + backend.close + end + end + + def proxy_request(path, client) + path.gsub!(/\Ahttps?:\/\//, '') + host, path = path.split('/') + host, port = host.split(':') + Net::HTTP.start(host, port) do |http| + response = http.get("/#{path}") + client.print "HTTP/1.1 #{response.code}\r\nContent-Type: #{response.content_type}\r\n\r\n#{response.body}" + end + end + + class Request + attr_reader :method, :path, :request_line, :headers + def initialize(method, path, request_line, headers) + @method = method + @path = path + @request_line = request_line + @headers = headers + end + + def [](key) + @headers[key] + end + end +end + +class SimpleHTTPSServer + def initialize(cert, key, dh, bind_addr, port, log) + @cert = cert + @key = key + @dh = dh + @bind_addr = bind_addr + @port = port + @log = log + @server = TCPServer.new(@bind_addr, @port) + context = OpenSSL::SSL::SSLContext.new + context.cert = @cert + context.key = @key + context.tmp_dh_callback = proc { @dh } + @ssl_server = OpenSSL::SSL::SSLServer.new(@server, context) + end + + def start + @thread = Thread.new do + loop do + ssl_socket = @ssl_server.accept + handle_request(ssl_socket) + ssl_socket.close + end + rescue OpenSSL::SSL::SSLError + end + end + + def shutdown + @thread.kill + @server.close + end + + def handle_request(socket) + request_line = socket.gets + return if request_line.nil? || request_line.strip.empty? + + _, path, _ = request_line.split + headers = {} + while (line = socket.gets) + break if line.strip.empty? + key, value = line.split(': ', 2) + headers[key] = value.strip + end + + response = case path + when '/data' + "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 3\r\n\r\nddd" + when "/proxy" + "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 5\r\n\r\nproxy" + else + "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n" + end + + socket.print(response) + end +end + +module TestOpenURIUtils + class Unauthorized < StandardError; end + class ProxyAuthenticationRequired < StandardError; end + + def with_http(log_tester=lambda {|log| assert_equal([], log) }) + log = [] + host = "127.0.0.1" + srv = SimpleHTTPServer.new(host, 0, log) + + server_thread = srv.start + server_thread2 = Thread.new { + server_thread.join + if log_tester + log_tester.call(log) + end + } + + port = srv.instance_variable_get(:@server).addr[1] + + client_thread = Thread.new { + begin + yield srv, "http://#{host}:#{port}", server_thread, log + ensure + srv.shutdown + end + } + assert_join_threads([client_thread, server_thread2]) + end + + def with_https_proxy(proxy_log_tester=lambda {|proxy_log, proxy_access_log| assert_equal([], proxy_log) }) + proxy_log = [] + proxy_access_log = [] + with_https {|srv, dr, url| + srv.instance_variable_get(:@server).setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true) + cacert_filename = "#{dr}/cacert.pem" + open(cacert_filename, "w") {|f| f << CA_CERT } + cacert_directory = "#{dr}/certs" + Dir.mkdir cacert_directory + hashed_name = "%08x.0" % OpenSSL::X509::Certificate.new(CA_CERT).subject.hash + open("#{cacert_directory}/#{hashed_name}", "w") {|f| f << CA_CERT } + proxy_host = '127.0.0.1' + proxy = SimpleHTTPProxyServer.new(proxy_host, 0, proxy_log, proxy_access_log) + proxy_port = proxy.instance_variable_get(:@server).addr[1] + proxy_thread = proxy.start + thread = Thread.new { + proxy_thread.join + if proxy_log_tester + proxy_log_tester.call(proxy_log, proxy_access_log) + end + } + begin + yield srv, dr, url, cacert_filename, cacert_directory, proxy_host, proxy_port + sleep 1 + ensure + proxy.shutdown + end + assert_join_threads([thread]) + } + end + + if defined?(OpenSSL::SSL) + def with_https(log_tester=lambda {|log| assert_equal([], log) }) + log = [] + Dir.mktmpdir {|dr| + cert = OpenSSL::X509::Certificate.new(SERVER_CERT) + key = OpenSSL::PKey::RSA.new(SERVER_KEY) + dh = OpenSSL::PKey::DH.new(DHPARAMS) + host = '127.0.0.1' + srv = SimpleHTTPSServer.new(cert, key, dh, host, 0, log) + port = srv.instance_variable_get(:@server).addr[1] + threads = [] + server_thread = srv.start + threads << Thread.new { + server_thread.join + if log_tester + log_tester.call(log) + end + } + threads << Thread.new { + begin + yield srv, dr, "https://#{host}:#{port}" + ensure + srv.shutdown + end + } + assert_join_threads(threads) + } + end + + # cp /etc/ssl/openssl.cnf . # I copied from OpenSSL 1.1.1b source + + # mkdir demoCA demoCA/private demoCA/newcerts + # touch demoCA/index.txt + # echo 00 > demoCA/serial + # openssl genrsa -des3 -out demoCA/private/cakey.pem 2048 + # openssl req -new -key demoCA/private/cakey.pem -out demoCA/careq.pem -subj "/C=JP/ST=Tokyo/O=RubyTest/CN=Ruby Test CA" + # # basicConstraints=CA:TRUE is required; the default openssl.cnf has it in [v3_ca] + # openssl ca -config openssl.cnf -extensions v3_ca -out demoCA/cacert.pem -startdate 090101000000Z -enddate 491231235959Z -batch -keyfile demoCA/private/cakey.pem -selfsign -infiles demoCA/careq.pem + + # mkdir server + # openssl genrsa -des3 -out server/server.key 2048 + # openssl req -new -key server/server.key -out server/csr.pem -subj "/C=JP/ST=Tokyo/O=RubyTest/CN=127.0.0.1" + # openssl ca -config openssl.cnf -startdate 090101000000Z -enddate 491231235959Z -in server/csr.pem -keyfile demoCA/private/cakey.pem -cert demoCA/cacert.pem -out server/cert.pem + + # demoCA/cacert.pem => TestOpenURISSL::CA_CERT + # server/cert.pem => TestOpenURISSL::SERVER_CERT + # `openssl rsa -in server/server.key -text` => TestOpenURISSL::SERVER_KEY + + CA_CERT = <<'End' +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 0 (0x0) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=JP, ST=Tokyo, O=RubyTest, CN=Ruby Test CA + Validity + Not Before: Jan 1 00:00:00 2009 GMT + Not After : Dec 31 23:59:59 2049 GMT + Subject: C=JP, ST=Tokyo, O=RubyTest, CN=Ruby Test CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:ad:f3:4d:5b:0b:01:54:cc:86:36:d1:93:6b:33: + 56:25:90:61:d6:9a:a0:f4:24:20:ee:c8:14:ab:0f: + 4b:89:d8:7c:bb:c0:f8:7f:fb:e9:a2:d5:1c:6b:6f: + dc:5c:23:b1:49:aa:2c:e8:ca:43:48:64:69:4b:8a: + bd:44:57:9b:14:d9:7a:b2:49:00:d6:c2:74:67:62: + 52:1d:a9:32:df:fe:7a:22:20:49:83:e1:cb:3d:dc: + 1a:2a:f0:36:20:c1:e8:c8:89:d4:51:1a:68:91:20: + e0:ba:67:0a:b2:6b:f8:e3:8c:f5:ee:a1:36:b1:89: + ec:23:b6:f2:39:a9:b9:2e:ea:de:d9:86:e5:42:11: + 46:ed:10:9a:90:76:44:4e:4d:49:2d:49:e8:e3:cb: + ff:7a:7d:80:cb:bf:c4:c3:69:ba:9c:60:4a:de:af: + bf:26:78:b8:fb:46:d1:37:d0:89:ba:78:93:6a:37: + a5:e9:58:e7:e2:e3:7d:7c:95:20:79:41:56:15:cd: + b2:c6:3b:e1:b7:e7:ba:47:60:9a:05:b1:07:f3:26: + 72:9d:3b:1b:02:18:3d:d5:de:e6:e9:30:a9:b5:8f: + 15:1b:40:f9:64:61:54:d3:53:e8:c4:29:4a:89:f3: + e5:0d:fd:16:61:ee:f2:6d:8a:45:a8:34:7e:53:46: + 8e:87 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + A0:7E:0B:AD:A3:AD:37:D7:21:0B:75:6F:8A:90:5F:8C:C9:69:DF:98 + X509v3 Authority Key Identifier: + keyid:A0:7E:0B:AD:A3:AD:37:D7:21:0B:75:6F:8A:90:5F:8C:C9:69:DF:98 + + X509v3 Basic Constraints: critical + CA:TRUE + Signature Algorithm: sha256WithRSAEncryption + 06:ea:06:02:19:9a:cb:94:a2:7e:c0:86:71:66:e7:a5:71:46: + a2:25:55:f5:e5:58:df:d1:91:58:e6:8a:0e:91:b3:22:4c:88: + 4d:5f:02:af:0f:73:65:0d:af:9a:f2:e4:36:f3:1f:e8:28:1d: + 9c:74:72:5b:f7:12:e8:fa:45:d6:df:e5:f1:d3:91:f4:0e:db: + e2:56:63:ee:82:57:6f:12:ad:d7:0d:de:5a:8c:3d:76:d2:87: + c9:48:1c:c4:f3:89:63:3c:c2:25:e0:dd:63:a6:4c:6c:5a:07: + 7b:86:78:62:86:02:a1:ef:0e:41:75:c5:d4:61:ab:c3:3b:9b: + 51:0b:e6:34:6d:0b:14:5a:2d:aa:d3:58:26:43:8f:4c:d7:45: + 73:1e:67:66:5e:f3:0c:69:70:27:a1:d5:70:f3:5a:10:98:c8: + 4f:8a:3b:9f:ad:8e:8d:49:8f:fb:f6:36:5d:4f:70:f9:4f:54: + 33:cf:a2:a6:1d:8c:61:b9:30:42:f2:49:d1:3d:a1:f1:eb:1e: + 78:a6:30:f8:8a:48:89:c7:3e:bd:0d:d8:72:04:a6:00:e5:62: + a4:13:3f:9e:b6:86:25:dc:d1:ff:3a:fc:f5:0e:e4:0e:f7:b8: + 66:90:fe:4f:c2:54:2a:7f:61:6e:e7:4b:bf:40:7e:75:30:02: + 5b:bb:91:1b +-----BEGIN CERTIFICATE----- +MIIDXDCCAkSgAwIBAgIBADANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJKUDEO +MAwGA1UECAwFVG9reW8xETAPBgNVBAoMCFJ1YnlUZXN0MRUwEwYDVQQDDAxSdWJ5 +IFRlc3QgQ0EwHhcNMDkwMTAxMDAwMDAwWhcNNDkxMjMxMjM1OTU5WjBHMQswCQYD +VQQGEwJKUDEOMAwGA1UECAwFVG9reW8xETAPBgNVBAoMCFJ1YnlUZXN0MRUwEwYD +VQQDDAxSdWJ5IFRlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCt801bCwFUzIY20ZNrM1YlkGHWmqD0JCDuyBSrD0uJ2Hy7wPh/++mi1Rxrb9xc +I7FJqizoykNIZGlLir1EV5sU2XqySQDWwnRnYlIdqTLf/noiIEmD4cs93Boq8DYg +wejIidRRGmiRIOC6Zwqya/jjjPXuoTaxiewjtvI5qbku6t7ZhuVCEUbtEJqQdkRO +TUktSejjy/96fYDLv8TDabqcYErer78meLj7RtE30Im6eJNqN6XpWOfi4318lSB5 +QVYVzbLGO+G357pHYJoFsQfzJnKdOxsCGD3V3ubpMKm1jxUbQPlkYVTTU+jEKUqJ +8+UN/RZh7vJtikWoNH5TRo6HAgMBAAGjUzBRMB0GA1UdDgQWBBSgfguto6031yEL +dW+KkF+MyWnfmDAfBgNVHSMEGDAWgBSgfguto6031yELdW+KkF+MyWnfmDAPBgNV +HRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAG6gYCGZrLlKJ+wIZxZuel +cUaiJVX15Vjf0ZFY5ooOkbMiTIhNXwKvD3NlDa+a8uQ28x/oKB2cdHJb9xLo+kXW +3+Xx05H0DtviVmPugldvEq3XDd5ajD120ofJSBzE84ljPMIl4N1jpkxsWgd7hnhi +hgKh7w5BdcXUYavDO5tRC+Y0bQsUWi2q01gmQ49M10VzHmdmXvMMaXAnodVw81oQ +mMhPijufrY6NSY/79jZdT3D5T1Qzz6KmHYxhuTBC8knRPaHx6x54pjD4ikiJxz69 +DdhyBKYA5WKkEz+etoYl3NH/Ovz1DuQO97hmkP5PwlQqf2Fu50u/QH51MAJbu5Eb +-----END CERTIFICATE----- +End + + SERVER_CERT = <<'End' +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=JP, ST=Tokyo, O=RubyTest, CN=Ruby Test CA + Validity + Not Before: Jan 1 00:00:00 2009 GMT + Not After : Dec 31 23:59:59 2049 GMT + Subject: C=JP, ST=Tokyo, O=RubyTest, CN=127.0.0.1 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:cb:b3:71:95:12:70:fc:db:d4:a9:a7:66:d6:d3: + 09:dd:06:80:19:e1:f2:d6:1e:31:b6:6b:20:75:51: + dc:a7:37:a9:ac:5b:57:5d:69:36:b6:de:1d:2c:f6: + 44:64:f8:e8:d6:f0:da:38:6a:ba:c2:b1:9e:dc:bb: + 79:94:e0:25:0c:ce:76:87:17:5d:79:9e:14:9e:bd: + 4c:0d:aa:74:10:3a:96:ef:76:82:d5:72:16:b5:c1: + ac:17:2d:90:83:73:5c:d7:a6:f5:36:0f:4c:55:f3: + 30:5d:19:dc:01:0e:f8:e6:fe:a5:ad:52:88:59:dc: + 4a:07:ed:a2:eb:a1:01:63:c4:8a:92:ba:06:80:9b: + 0d:85:f2:9f:f9:70:ac:d7:ad:f0:7a:3f:b8:92:2a: + 33:ca:69:d0:01:65:5d:31:38:1d:f6:1f:b2:17:07: + 7e:ac:88:67:a6:c4:5f:3e:93:94:61:e6:e4:49:9d: + ba:d4:d2:e8:e3:93:d1:66:79:c5:e3:1d:f8:5a:50: + 54:58:3d:04:b0:fd:65:d1:b3:8a:b5:8a:30:5f:b2: + dc:34:1a:14:f7:74:4c:03:29:97:63:5a:d7:de:bb: + eb:7f:4a:2a:90:59:c0:2b:47:09:82:8f:75:de:14: + 3f:bc:78:9a:69:25:80:5b:6c:a0:65:12:0d:29:61: + ac:f9 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Netscape Comment: + OpenSSL Generated Certificate + X509v3 Subject Key Identifier: + EC:6B:7C:79:B8:3B:11:1D:42:F3:9A:2A:CF:9A:15:59:D7:F9:D8:C6 + X509v3 Authority Key Identifier: + keyid:A0:7E:0B:AD:A3:AD:37:D7:21:0B:75:6F:8A:90:5F:8C:C9:69:DF:98 + + Signature Algorithm: sha256WithRSAEncryption + 29:14:db:71:e9:a0:86:f8:cc:4d:e4:8a:76:78:a7:ff:4e:94: + b4:4d:92:dc:57:9a:52:64:46:27:15:8b:4f:2a:18:a7:0d:fc: + d2:75:ce:4e:49:97:0b:46:71:57:23:e3:a5:c0:c5:71:94:fc: + f2:1d:3b:06:93:82:03:59:56:d4:fb:09:06:08:b4:97:50:33: + cf:58:89:dd:91:31:07:26:9a:7e:7f:8d:71:de:09:dc:4f:e5: + 6b:a3:10:71:d4:50:24:43:a0:1c:f5:2a:d9:1a:fb:e3:d6:f1: + bc:6b:42:67:16:b4:3b:31:f4:ec:03:7d:78:e2:64:16:57:6d: + ba:7c:0c:e1:14:b2:7c:75:4e:2b:09:3e:86:e4:aa:cc:7e:5c: + 2b:bd:8d:26:4d:49:36:74:86:fe:c5:a6:15:4a:af:e8:b4:4e: + d5:f2:e1:59:c2:fb:7e:c3:c4:f1:63:d8:c2:b0:9a:ae:31:96: + 90:c3:09:d0:ce:2e:31:90:d7:83:dd:ac:31:cc:f7:87:41:08: + 92:33:28:52:fa:2d:9e:ad:ae:6a:9f:c3:be:ce:c1:a6:e4:16: + 2f:69:34:40:86:b6:10:21:0e:31:69:81:9e:fc:fd:c3:06:25: + 65:37:d3:d9:4a:20:84:aa:e7:0e:60:7c:bf:3f:88:67:ac:e5: + 8c:e0:61:d6 +-----BEGIN CERTIFICATE----- +MIIDgTCCAmmgAwIBAgIBATANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJKUDEO +MAwGA1UECAwFVG9reW8xETAPBgNVBAoMCFJ1YnlUZXN0MRUwEwYDVQQDDAxSdWJ5 +IFRlc3QgQ0EwHhcNMDkwMTAxMDAwMDAwWhcNNDkxMjMxMjM1OTU5WjBEMQswCQYD +VQQGEwJKUDEOMAwGA1UECAwFVG9reW8xETAPBgNVBAoMCFJ1YnlUZXN0MRIwEAYD +VQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDL +s3GVEnD829Spp2bW0wndBoAZ4fLWHjG2ayB1UdynN6msW1ddaTa23h0s9kRk+OjW +8No4arrCsZ7cu3mU4CUMznaHF115nhSevUwNqnQQOpbvdoLVcha1wawXLZCDc1zX +pvU2D0xV8zBdGdwBDvjm/qWtUohZ3EoH7aLroQFjxIqSugaAmw2F8p/5cKzXrfB6 +P7iSKjPKadABZV0xOB32H7IXB36siGemxF8+k5Rh5uRJnbrU0ujjk9FmecXjHfha +UFRYPQSw/WXRs4q1ijBfstw0GhT3dEwDKZdjWtfeu+t/SiqQWcArRwmCj3XeFD+8 +eJppJYBbbKBlEg0pYaz5AgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgEN +BB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBTsa3x5 +uDsRHULzmirPmhVZ1/nYxjAfBgNVHSMEGDAWgBSgfguto6031yELdW+KkF+MyWnf +mDANBgkqhkiG9w0BAQsFAAOCAQEAKRTbcemghvjMTeSKdnin/06UtE2S3FeaUmRG +JxWLTyoYpw380nXOTkmXC0ZxVyPjpcDFcZT88h07BpOCA1lW1PsJBgi0l1Azz1iJ +3ZExByaafn+Ncd4J3E/la6MQcdRQJEOgHPUq2Rr749bxvGtCZxa0OzH07AN9eOJk +FldtunwM4RSyfHVOKwk+huSqzH5cK72NJk1JNnSG/sWmFUqv6LRO1fLhWcL7fsPE +8WPYwrCarjGWkMMJ0M4uMZDXg92sMcz3h0EIkjMoUvotnq2uap/Dvs7BpuQWL2k0 +QIa2ECEOMWmBnvz9wwYlZTfT2UoghKrnDmB8vz+IZ6zljOBh1g== +-----END CERTIFICATE----- +End + + SERVER_KEY = <<'End' +RSA Private-Key: (2048 bit, 2 primes) +modulus: + 00:cb:b3:71:95:12:70:fc:db:d4:a9:a7:66:d6:d3: + 09:dd:06:80:19:e1:f2:d6:1e:31:b6:6b:20:75:51: + dc:a7:37:a9:ac:5b:57:5d:69:36:b6:de:1d:2c:f6: + 44:64:f8:e8:d6:f0:da:38:6a:ba:c2:b1:9e:dc:bb: + 79:94:e0:25:0c:ce:76:87:17:5d:79:9e:14:9e:bd: + 4c:0d:aa:74:10:3a:96:ef:76:82:d5:72:16:b5:c1: + ac:17:2d:90:83:73:5c:d7:a6:f5:36:0f:4c:55:f3: + 30:5d:19:dc:01:0e:f8:e6:fe:a5:ad:52:88:59:dc: + 4a:07:ed:a2:eb:a1:01:63:c4:8a:92:ba:06:80:9b: + 0d:85:f2:9f:f9:70:ac:d7:ad:f0:7a:3f:b8:92:2a: + 33:ca:69:d0:01:65:5d:31:38:1d:f6:1f:b2:17:07: + 7e:ac:88:67:a6:c4:5f:3e:93:94:61:e6:e4:49:9d: + ba:d4:d2:e8:e3:93:d1:66:79:c5:e3:1d:f8:5a:50: + 54:58:3d:04:b0:fd:65:d1:b3:8a:b5:8a:30:5f:b2: + dc:34:1a:14:f7:74:4c:03:29:97:63:5a:d7:de:bb: + eb:7f:4a:2a:90:59:c0:2b:47:09:82:8f:75:de:14: + 3f:bc:78:9a:69:25:80:5b:6c:a0:65:12:0d:29:61: + ac:f9 +publicExponent: 65537 (0x10001) +privateExponent: + 12:be:d5:b2:01:3b:72:99:8c:4d:7c:81:43:3d:b2: + 87:ab:84:78:5d:49:aa:98:a6:bc:81:c9:3f:e2:a3: + aa:a3:bd:b2:85:c9:59:68:48:47:b5:d2:fb:83:42: + 32:04:91:f0:cd:c3:57:33:c3:32:0d:84:70:0d:b4: + 97:95:b4:f3:23:c0:d6:97:b8:db:6b:47:bc:7f:f1: + 12:c4:df:df:6a:74:df:5e:89:95:b8:e5:0c:1e:e1: + 86:54:84:1b:04:af:c3:8c:b2:be:21:d4:45:88:96: + a7:ca:ac:6b:50:84:69:45:7f:db:9e:5f:bb:dd:40: + d6:cf:f0:91:3c:84:d3:38:65:c9:15:f7:9e:37:aa: + 1a:2e:bc:16:b6:95:be:bc:af:45:76:ba:ad:99:f6: + ef:6a:e8:fd:f0:31:89:19:c4:04:67:a1:ec:c4:79: + 59:08:77:ab:0b:65:88:88:02:b1:38:5c:80:4e:27: + 78:b2:a5:bd:b5:ad:d5:9c:4c:ea:ad:db:05:56:25: + 70:28:da:22:fb:d8:de:8c:3b:78:fe:3e:cf:ed:1b: + f9:97:c6:b6:4a:bf:60:08:8f:dc:85:5e:b1:49:ab: + 87:8b:68:72:f4:6a:3f:bc:db:a3:6c:f7:e8:b0:15: + bb:4b:ba:37:49:a2:d1:7c:f8:4f:1b:05:11:22:d9: + 81 +prime1: + 00:fb:d2:cb:14:61:00:c1:7a:83:ba:fe:79:97:a2: + 4d:5a:ea:40:78:96:6e:d2:be:71:5b:c6:2c:1f:c9: + 18:48:6b:ae:20:86:87:b5:08:0b:17:69:ca:93:cd: + 00:36:22:51:7b:d5:2d:8c:0c:0e:de:bc:86:a8:07: + 0e:c5:57:e4:df:be:ed:7d:cc:b1:a4:d6:a8:2b:00: + 65:2a:69:30:5e:dc:6d:6d:c4:c8:7e:20:34:eb:6f: + 5e:cf:b3:b8:2e:8d:56:31:44:a8:17:ea:be:65:19: + ff:da:14:e0:0c:73:56:14:08:47:4c:5b:79:51:74: + 5d:bc:e7:fe:01:2f:55:27:69 +prime2: + 00:cf:14:54:47:bb:5f:5d:d6:2b:2d:ed:a6:8a:6f: + 36:fc:47:5e:9f:84:ae:aa:1f:f8:44:50:91:15:f5: + ed:9d:29:d9:2b:2a:19:66:56:2e:96:15:b5:8e:a9: + 7f:89:27:21:b5:57:55:7e:2a:c5:8c:93:fe:f6:0a: + a5:17:15:91:91:b3:7d:35:1a:d5:9a:2e:b8:0d:ad: + e6:97:6d:83:a3:27:29:ee:00:74:ef:57:34:f3:07: + ad:12:43:37:0c:5c:b7:26:34:bc:4e:3a:43:65:6b: + 0c:b8:23:ac:77:fd:b2:23:eb:7b:65:70:f6:96:c4: + 17:2c:aa:24:b8:a5:5e:b7:11 +exponent1: + 00:92:32:ae:f4:05:dd:0a:76:b6:43:b9:b9:9d:ee: + fc:39:ec:05:c1:fc:94:1a:85:b6:0a:31:e3:2c:10: + f3:a8:17:db:df:c6:3a:c3:3f:08:31:6f:99:cc:75: + 17:ca:55:e2:38:a2:6a:ef:03:91:1e:7f:15:2e:37: + ea:bb:67:6b:d8:fa:5f:a6:c9:4f:d9:03:46:5e:b0: + bc:0b:03:46:b1:cc:07:3b:d3:23:13:16:5f:a2:cf: + e5:9b:70:1b:5d:eb:70:3e:ea:3d:2c:a5:7c:23:f6: + 14:33:e8:2a:ab:0f:ca:c9:96:84:ce:2f:cd:1f:1d: + 0f:ce:bc:61:1b:0e:ff:c1:01 +exponent2: + 00:9e:0b:f3:03:48:73:d1:e7:9a:cf:13:f9:ae:e0: + 91:03:dc:e8:d0:30:f1:2a:30:fa:48:11:81:9a:54: + 37:c5:62:e2:37:fa:8a:a6:3b:92:94:c3:fe:ec:e2: + 5a:cf:70:09:5f:21:47:c3:e2:9b:21:de:f6:92:0c: + af:d1:bd:89:7b:bd:95:0b:49:ee:cb:1d:6b:26:2d: + 9a:b7:ea:42:b4:ec:38:29:49:39:f6:4e:05:c0:93: + 14:39:c3:09:29:ab:3d:b1:b0:40:24:28:7d:b5:d3: + 0d:43:21:1f:09:f9:9b:d3:a4:6f:6a:8d:db:f6:57: + b5:24:46:bb:7e:1d:e0:fb:31 +coefficient: + 10:93:1d:c8:33:a5:c1:d3:84:6a:22:68:e5:60:cc: + 9c:27:0a:52:0b:58:a3:0c:83:f4:f4:46:09:0c:a1: + 41:a6:ea:bf:80:9d:0e:5d:d8:3d:25:00:c5:a1:35: + 7a:8c:ea:95:16:94:c3:7c:8f:2b:e0:53:ea:66:ae: + 19:be:55:04:3d:ee:e2:4b:a8:69:1b:7e:d8:09:7f: + ed:7c:ee:95:88:10:dc:4b:5b:bf:81:a4:e8:dc:7e: + 4f:e5:c3:90:c4:e5:5a:90:10:32:d6:08:b5:1f:5d: + 09:18:d8:44:28:e4:c4:c7:07:75:9b:9b:b3:80:86: + 68:9d:fe:68:f3:4d:db:66 +writing RSA key +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAy7NxlRJw/NvUqadm1tMJ3QaAGeHy1h4xtmsgdVHcpzeprFtX +XWk2tt4dLPZEZPjo1vDaOGq6wrGe3Lt5lOAlDM52hxddeZ4Unr1MDap0EDqW73aC +1XIWtcGsFy2Qg3Nc16b1Ng9MVfMwXRncAQ745v6lrVKIWdxKB+2i66EBY8SKkroG +gJsNhfKf+XCs163wej+4kiozymnQAWVdMTgd9h+yFwd+rIhnpsRfPpOUYebkSZ26 +1NLo45PRZnnF4x34WlBUWD0EsP1l0bOKtYowX7LcNBoU93RMAymXY1rX3rvrf0oq +kFnAK0cJgo913hQ/vHiaaSWAW2ygZRINKWGs+QIDAQABAoIBABK+1bIBO3KZjE18 +gUM9soerhHhdSaqYpryByT/io6qjvbKFyVloSEe10vuDQjIEkfDNw1czwzINhHAN +tJeVtPMjwNaXuNtrR7x/8RLE399qdN9eiZW45Qwe4YZUhBsEr8OMsr4h1EWIlqfK +rGtQhGlFf9ueX7vdQNbP8JE8hNM4ZckV9543qhouvBa2lb68r0V2uq2Z9u9q6P3w +MYkZxARnoezEeVkId6sLZYiIArE4XIBOJ3iypb21rdWcTOqt2wVWJXAo2iL72N6M +O3j+Ps/tG/mXxrZKv2AIj9yFXrFJq4eLaHL0aj+826Ns9+iwFbtLujdJotF8+E8b +BREi2YECgYEA+9LLFGEAwXqDuv55l6JNWupAeJZu0r5xW8YsH8kYSGuuIIaHtQgL +F2nKk80ANiJRe9UtjAwO3ryGqAcOxVfk377tfcyxpNaoKwBlKmkwXtxtbcTIfiA0 +629ez7O4Lo1WMUSoF+q+ZRn/2hTgDHNWFAhHTFt5UXRdvOf+AS9VJ2kCgYEAzxRU +R7tfXdYrLe2mim82/Eden4Suqh/4RFCRFfXtnSnZKyoZZlYulhW1jql/iSchtVdV +firFjJP+9gqlFxWRkbN9NRrVmi64Da3ml22Doycp7gB071c08wetEkM3DFy3JjS8 +TjpDZWsMuCOsd/2yI+t7ZXD2lsQXLKokuKVetxECgYEAkjKu9AXdCna2Q7m5ne78 +OewFwfyUGoW2CjHjLBDzqBfb38Y6wz8IMW+ZzHUXylXiOKJq7wORHn8VLjfqu2dr +2PpfpslP2QNGXrC8CwNGscwHO9MjExZfos/lm3AbXetwPuo9LKV8I/YUM+gqqw/K +yZaEzi/NHx0PzrxhGw7/wQECgYEAngvzA0hz0eeazxP5ruCRA9zo0DDxKjD6SBGB +mlQ3xWLiN/qKpjuSlMP+7OJaz3AJXyFHw+KbId72kgyv0b2Je72VC0nuyx1rJi2a +t+pCtOw4KUk59k4FwJMUOcMJKas9sbBAJCh9tdMNQyEfCfmb06Rvao3b9le1JEa7 +fh3g+zECgYAQkx3IM6XB04RqImjlYMycJwpSC1ijDIP09EYJDKFBpuq/gJ0OXdg9 +JQDFoTV6jOqVFpTDfI8r4FPqZq4ZvlUEPe7iS6hpG37YCX/tfO6ViBDcS1u/gaTo +3H5P5cOQxOVakBAy1gi1H10JGNhEKOTExwd1m5uzgIZonf5o803bZg== +-----END RSA PRIVATE KEY----- +End + + DHPARAMS = <<'End' + DH Parameters: (2048 bit) + prime: + 00:ec:4e:a4:06:b6:22:ca:f9:8a:00:cc:d0:ee:2f: + 16:bf:05:64:f5:8f:fe:7f:c4:bb:b0:24:cd:ef:5d: + 8a:90:ad:dc:a9:dd:63:84:90:d8:25:ba:d8:78:d5: + 77:91:42:0a:84:fc:56:1e:13:9b:1c:aa:43:d5:1f: + 38:52:92:fe:b3:66:f9:e7:e8:8c:77:a1:a6:2f:b3: + 98:98:d2:13:fc:57:1c:2a:14:dc:bd:e6:9b:54:19: + 99:4f:ce:81:64:a6:32:7f:8e:61:50:5f:45:3a:e5: + 0c:f7:13:f3:b8:ad:d5:77:ca:09:42:f7:d8:30:27: + 7b:2c:f0:b4:b5:a0:04:96:34:0b:47:81:1d:7f:c1: + 3a:62:86:8e:7d:f8:13:7f:9a:b1:8b:09:23:9e:55: + 59:41:cd:f0:86:09:c4:b7:d1:69:54:cb:d0:f5:e9: + 27:c9:e1:81:e4:a1:df:6b:20:1c:df:e8:54:02:f2: + 37:fc:2a:f7:d5:b3:6f:79:7e:70:22:78:79:18:3c: + 75:14:68:4a:05:9f:ac:d4:7f:9a:79:db:9d:0a:6e: + ec:0a:04:70:bf:c9:4a:59:81:a2:1f:33:9b:4a:66: + bc:03:ce:8a:1b:e3:03:ec:ba:39:26:ab:90:dc:39: + 41:a1:d8:f7:20:3c:8f:af:12:2f:f7:a9:6f:44:f1: + 6d:03 + generator: 2 (0x2) +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY +JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab +VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6 +YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3 +1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD +7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg== +-----END DH PARAMETERS----- +End + end +end diff --git a/test/prism/api/parse_stream_test.rb b/test/prism/api/parse_stream_test.rb index 0edee74cc22385..1c068c617c44a2 100644 --- a/test/prism/api/parse_stream_test.rb +++ b/test/prism/api/parse_stream_test.rb @@ -69,5 +69,13 @@ def test_false___END___in_heredoc assert result.success? assert_equal 4, result.value.statements.body.length end + + def test_nul_bytes + io = StringIO.new("1 # \0\0\0 \n2 # \0\0\0\n3") + result = Prism.parse_stream(io) + + assert result.success? + assert_equal 3, result.value.statements.body.length + end end end diff --git a/test/prism/errors/1_2_3.txt b/test/prism/errors/1_2_3.txt new file mode 100644 index 00000000000000..345452911f38e3 --- /dev/null +++ b/test/prism/errors/1_2_3.txt @@ -0,0 +1,11 @@ +(1, 2, 3) + ^ unexpected ',', expecting end-of-input + ^ unexpected ',', ignoring it + ^ expected a matching `)` + ^ unexpected ',', expecting end-of-input + ^ unexpected ',', ignoring it + ^ unexpected ',', expecting end-of-input + ^ unexpected ',', ignoring it + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it + diff --git a/test/prism/errors/aliasing_global_variable_with_global_number_variable.txt b/test/prism/errors/aliasing_global_variable_with_global_number_variable.txt new file mode 100644 index 00000000000000..2f40a6a32886ab --- /dev/null +++ b/test/prism/errors/aliasing_global_variable_with_global_number_variable.txt @@ -0,0 +1,3 @@ +alias $a $1 + ^~ invalid argument being passed to `alias`; can't make alias for the number variables + diff --git a/test/prism/errors/aliasing_global_variable_with_non_global_variable.txt b/test/prism/errors/aliasing_global_variable_with_non_global_variable.txt new file mode 100644 index 00000000000000..b6f013bab5234c --- /dev/null +++ b/test/prism/errors/aliasing_global_variable_with_non_global_variable.txt @@ -0,0 +1,3 @@ +alias $a b + ^ invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable + diff --git a/test/prism/errors/aliasing_non_global_variable_with_global_variable.txt b/test/prism/errors/aliasing_non_global_variable_with_global_variable.txt new file mode 100644 index 00000000000000..8863f342f0f65c --- /dev/null +++ b/test/prism/errors/aliasing_non_global_variable_with_global_variable.txt @@ -0,0 +1,3 @@ +alias a $b + ^~ invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable + diff --git a/test/prism/errors/alnum_delimiters.txt b/test/prism/errors/alnum_delimiters.txt new file mode 100644 index 00000000000000..c9ed06ae51d066 --- /dev/null +++ b/test/prism/errors/alnum_delimiters.txt @@ -0,0 +1,3 @@ +%qXfooX +^ unknown type of %string + diff --git a/test/prism/errors/alnum_delimiters_2.txt b/test/prism/errors/alnum_delimiters_2.txt new file mode 100644 index 00000000000000..3f78b434d6c436 --- /dev/null +++ b/test/prism/errors/alnum_delimiters_2.txt @@ -0,0 +1,3 @@ +%QXfooX +^ unknown type of %string + diff --git a/test/prism/errors/alnum_delimiters_3.txt b/test/prism/errors/alnum_delimiters_3.txt new file mode 100644 index 00000000000000..55ef8d29a54012 --- /dev/null +++ b/test/prism/errors/alnum_delimiters_3.txt @@ -0,0 +1,3 @@ +%wXfooX +^ unknown type of %string + diff --git a/test/prism/errors/alnum_delimiters_4.txt b/test/prism/errors/alnum_delimiters_4.txt new file mode 100644 index 00000000000000..603b54debdcfa5 --- /dev/null +++ b/test/prism/errors/alnum_delimiters_4.txt @@ -0,0 +1,3 @@ +%WxfooX +^ unknown type of %string + diff --git a/test/prism/errors/alnum_delimiters_5.txt b/test/prism/errors/alnum_delimiters_5.txt new file mode 100644 index 00000000000000..31c344ea90202e --- /dev/null +++ b/test/prism/errors/alnum_delimiters_5.txt @@ -0,0 +1,3 @@ +%iXfooX +^ unknown type of %string + diff --git a/test/prism/errors/alnum_delimiters_6.txt b/test/prism/errors/alnum_delimiters_6.txt new file mode 100644 index 00000000000000..79ffbbb1b84b70 --- /dev/null +++ b/test/prism/errors/alnum_delimiters_6.txt @@ -0,0 +1,3 @@ +%IXfooX +^ unknown type of %string + diff --git a/test/prism/errors/alnum_delimiters_7.txt b/test/prism/errors/alnum_delimiters_7.txt new file mode 100644 index 00000000000000..809192e031f87e --- /dev/null +++ b/test/prism/errors/alnum_delimiters_7.txt @@ -0,0 +1,3 @@ +%xXfooX +^ unknown type of %string + diff --git a/test/prism/errors/alnum_delimiters_8.txt b/test/prism/errors/alnum_delimiters_8.txt new file mode 100644 index 00000000000000..abfcf119c005ce --- /dev/null +++ b/test/prism/errors/alnum_delimiters_8.txt @@ -0,0 +1,3 @@ +%rXfooX +^ unknown type of %string + diff --git a/test/prism/errors/alnum_delimiters_9.txt b/test/prism/errors/alnum_delimiters_9.txt new file mode 100644 index 00000000000000..ae56d7be4fb43e --- /dev/null +++ b/test/prism/errors/alnum_delimiters_9.txt @@ -0,0 +1,3 @@ +%sXfooX +^ unknown type of %string + diff --git a/test/prism/errors/argument_after_ellipsis.txt b/test/prism/errors/argument_after_ellipsis.txt new file mode 100644 index 00000000000000..3d708648a40bd1 --- /dev/null +++ b/test/prism/errors/argument_after_ellipsis.txt @@ -0,0 +1,3 @@ +def foo(...); foo(..., 1); end + ^ unexpected argument after `...` + diff --git a/test/prism/errors/argument_forwarding_only_effects_its_own_internals.txt b/test/prism/errors/argument_forwarding_only_effects_its_own_internals.txt new file mode 100644 index 00000000000000..9c3f0ae3f76fe7 --- /dev/null +++ b/test/prism/errors/argument_forwarding_only_effects_its_own_internals.txt @@ -0,0 +1,3 @@ +def a(...); b(...); end; def c(x, y, z); b(...); end + ^~~ unexpected ... when the parent method is not forwarding + diff --git a/test/prism/errors/argument_forwarding_when_parent_is_not_forwarding.txt b/test/prism/errors/argument_forwarding_when_parent_is_not_forwarding.txt new file mode 100644 index 00000000000000..017ba7eec9eb1b --- /dev/null +++ b/test/prism/errors/argument_forwarding_when_parent_is_not_forwarding.txt @@ -0,0 +1,3 @@ +def a(x, y, z); b(...); end + ^~~ unexpected ... when the parent method is not forwarding + diff --git a/test/prism/errors/arguments_after_block.txt b/test/prism/errors/arguments_after_block.txt new file mode 100644 index 00000000000000..2d5e06ff7777ed --- /dev/null +++ b/test/prism/errors/arguments_after_block.txt @@ -0,0 +1,3 @@ +a(&block, foo) + ^~~ unexpected argument after a block argument + diff --git a/test/prism/errors/arguments_binding_power_for_and.txt b/test/prism/errors/arguments_binding_power_for_and.txt new file mode 100644 index 00000000000000..0585a091f4d6ad --- /dev/null +++ b/test/prism/errors/arguments_binding_power_for_and.txt @@ -0,0 +1,5 @@ +foo(*bar and baz) + ^~~ unexpected 'and'; expected a `)` to close the arguments + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it + diff --git a/test/prism/errors/assign_to_numbered_parameter.txt b/test/prism/errors/assign_to_numbered_parameter.txt new file mode 100644 index 00000000000000..74cc0c4032428e --- /dev/null +++ b/test/prism/errors/assign_to_numbered_parameter.txt @@ -0,0 +1,11 @@ +a in _1 + ^~ _1 is reserved for numbered parameters +a => _1 + ^~ _1 is reserved for numbered parameters +1 => a, _1 + ^~ _1 is reserved for numbered parameters +1 in a, _1 + ^~ _1 is reserved for numbered parameters +/(?<_1>)/ =~ a + ^~ _1 is reserved for numbered parameters + diff --git a/test/prism/errors/bad_arguments.txt b/test/prism/errors/bad_arguments.txt new file mode 100644 index 00000000000000..ea19efd3c8fec7 --- /dev/null +++ b/test/prism/errors/bad_arguments.txt @@ -0,0 +1,6 @@ +def foo(A, @a, $A, @@a);end + ^ invalid formal argument; formal argument cannot be a constant + ^~ invalid formal argument; formal argument cannot be an instance variable + ^~ invalid formal argument; formal argument cannot be a global variable + ^~~ invalid formal argument; formal argument cannot be a class variable + diff --git a/test/prism/errors/begin_at_toplevel.txt b/test/prism/errors/begin_at_toplevel.txt new file mode 100644 index 00000000000000..ce3d3b8d002728 --- /dev/null +++ b/test/prism/errors/begin_at_toplevel.txt @@ -0,0 +1,3 @@ +def foo; BEGIN {}; end + ^~~~~ BEGIN is permitted only at toplevel + diff --git a/test/prism/errors/binary_range_with_left_unary_range.txt b/test/prism/errors/binary_range_with_left_unary_range.txt new file mode 100644 index 00000000000000..37e41f397179b9 --- /dev/null +++ b/test/prism/errors/binary_range_with_left_unary_range.txt @@ -0,0 +1,7 @@ +..1.. + ^~ unexpected range operator; .. and ... are non-associative and cannot be chained +...1.. + ^~ unexpected range operator; .. and ... are non-associative and cannot be chained + ^~ unexpected .., expecting end-of-input + ^~ unexpected .., ignoring it + diff --git a/test/prism/errors/block_arg_and_block.txt b/test/prism/errors/block_arg_and_block.txt new file mode 100644 index 00000000000000..c355c404758341 --- /dev/null +++ b/test/prism/errors/block_arg_and_block.txt @@ -0,0 +1,3 @@ +foo(&1) { } + ^~~ both block arg and actual block given; only one block is allowed + diff --git a/test/prism/errors/block_beginning_with_brace_and_ending_with_end.txt b/test/prism/errors/block_beginning_with_brace_and_ending_with_end.txt new file mode 100644 index 00000000000000..f0fa964c8a7858 --- /dev/null +++ b/test/prism/errors/block_beginning_with_brace_and_ending_with_end.txt @@ -0,0 +1,6 @@ +x.each { x end + ^~~ unexpected 'end', expecting end-of-input + ^~~ unexpected 'end', ignoring it + ^ unexpected end-of-input, assuming it is closing the parent top level context + ^ expected a block beginning with `{` to end with `}` + diff --git a/test/prism/errors/break_1.txt b/test/prism/errors/break_1.txt new file mode 100644 index 00000000000000..e7b26ad3a01ee9 --- /dev/null +++ b/test/prism/errors/break_1.txt @@ -0,0 +1,4 @@ +break 1,; + ^ expected an argument +^~~~~~~~ Invalid break + diff --git a/test/prism/errors/break_1_2_3.txt b/test/prism/errors/break_1_2_3.txt new file mode 100644 index 00000000000000..817207cbfe3d39 --- /dev/null +++ b/test/prism/errors/break_1_2_3.txt @@ -0,0 +1,8 @@ +break(1, 2, 3) + ^ unexpected ',', expecting end-of-input + ^ unexpected ',', ignoring it + ^ expected a matching `)` + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it +^~~~~~~~~~~~~ Invalid break + diff --git a/test/prism/errors/call_with_block_and_write.txt b/test/prism/errors/call_with_block_and_write.txt new file mode 100644 index 00000000000000..f63d94770eb327 --- /dev/null +++ b/test/prism/errors/call_with_block_and_write.txt @@ -0,0 +1,4 @@ +foo {} &&= 1 +^~~~~~ unexpected write target + ^~~ unexpected operator after a call with a block + diff --git a/test/prism/errors/call_with_block_operator_write.txt b/test/prism/errors/call_with_block_operator_write.txt new file mode 100644 index 00000000000000..3c36050b34f0fb --- /dev/null +++ b/test/prism/errors/call_with_block_operator_write.txt @@ -0,0 +1,4 @@ +foo {} += 1 +^~~~~~ unexpected write target + ^~ unexpected operator after a call with a block + diff --git a/test/prism/errors/call_with_block_or_write.txt b/test/prism/errors/call_with_block_or_write.txt new file mode 100644 index 00000000000000..2cced0db75d50b --- /dev/null +++ b/test/prism/errors/call_with_block_or_write.txt @@ -0,0 +1,4 @@ +foo {} ||= 1 +^~~~~~ unexpected write target + ^~~ unexpected operator after a call with a block + diff --git a/test/prism/errors/cannot_assign_to_a_reserved_numbered_parameter.txt b/test/prism/errors/cannot_assign_to_a_reserved_numbered_parameter.txt new file mode 100644 index 00000000000000..750915fb1fb398 --- /dev/null +++ b/test/prism/errors/cannot_assign_to_a_reserved_numbered_parameter.txt @@ -0,0 +1,14 @@ +begin + _1=:a;_2=:a;_3=:a;_4=:a;_5=:a + ^~ _1 is reserved for numbered parameters + ^~ _2 is reserved for numbered parameters + ^~ _3 is reserved for numbered parameters + ^~ _4 is reserved for numbered parameters + ^~ _5 is reserved for numbered parameters + _6=:a;_7=:a;_8=:a;_9=:a;_10=:a + ^~ _6 is reserved for numbered parameters + ^~ _7 is reserved for numbered parameters + ^~ _8 is reserved for numbered parameters + ^~ _9 is reserved for numbered parameters +end + diff --git a/test/prism/errors/case_without_clauses.txt b/test/prism/errors/case_without_clauses.txt new file mode 100644 index 00000000000000..3bbbfdd97fa136 --- /dev/null +++ b/test/prism/errors/case_without_clauses.txt @@ -0,0 +1,4 @@ +case :a +^~~~ expected a `when` or `in` clause after `case` +end + diff --git a/test/prism/errors/case_without_when_clauses_errors_on_else_clause.txt b/test/prism/errors/case_without_when_clauses_errors_on_else_clause.txt new file mode 100644 index 00000000000000..c5a1179fb9dbc3 --- /dev/null +++ b/test/prism/errors/case_without_when_clauses_errors_on_else_clause.txt @@ -0,0 +1,5 @@ +case :a +^~~~ expected a `when` or `in` clause after `case` +else +end + diff --git a/test/prism/errors/check_value_expression.txt b/test/prism/errors/check_value_expression.txt new file mode 100644 index 00000000000000..33a472d94c167d --- /dev/null +++ b/test/prism/errors/check_value_expression.txt @@ -0,0 +1,20 @@ +1 => ^(return) + ^~~~~~ unexpected void value expression +while true + 1 => ^(break) + ^~~~~ unexpected void value expression + 1 => ^(next) + ^~~~ unexpected void value expression + 1 => ^(redo) + ^~~~ unexpected void value expression + 1 => ^(retry) + ^~~~~ Invalid retry without rescue + ^~~~~ unexpected void value expression + 1 => ^(2 => a) + ^~~~~~ unexpected void value expression +end +1 => ^(if 1; (return) else (return) end) + ^~~~~~ unexpected void value expression +1 => ^(unless 1; (return) else (return) end) + ^~~~~~ unexpected void value expression + diff --git a/test/prism/errors/class_definition_in_method_body.txt b/test/prism/errors/class_definition_in_method_body.txt new file mode 100644 index 00000000000000..fcdc5746eecdfa --- /dev/null +++ b/test/prism/errors/class_definition_in_method_body.txt @@ -0,0 +1,3 @@ +def foo;class A;end;end + ^~~~~ unexpected class definition in method body + diff --git a/test/prism/errors/class_definition_in_method_defs.txt b/test/prism/errors/class_definition_in_method_defs.txt new file mode 100644 index 00000000000000..23bee0b6fb3342 --- /dev/null +++ b/test/prism/errors/class_definition_in_method_defs.txt @@ -0,0 +1,7 @@ +def foo(bar = class A;end);end + ^~~~~ unexpected class definition in method body +def foo;rescue;class A;end;end + ^~~~~ unexpected class definition in method body +def foo;ensure;class A;end;end + ^~~~~ unexpected class definition in method body + diff --git a/test/prism/errors/class_name.txt b/test/prism/errors/class_name.txt new file mode 100644 index 00000000000000..8b75896ddbacb7 --- /dev/null +++ b/test/prism/errors/class_name.txt @@ -0,0 +1,3 @@ +class 0.X end + ^~~ unexpected constant path after `class`; class/module name must be CONSTANT + diff --git a/test/prism/errors/command_call_in.txt b/test/prism/errors/command_call_in.txt new file mode 100644 index 00000000000000..a4357028c67768 --- /dev/null +++ b/test/prism/errors/command_call_in.txt @@ -0,0 +1,7 @@ +foo 1 in a + ^ unexpected `in` keyword in arguments + ^ unexpected local variable or method, expecting end-of-input +a = foo 2 in b + ^ unexpected `in` keyword in arguments + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/command_calls.txt b/test/prism/errors/command_calls.txt new file mode 100644 index 00000000000000..19812a1d0a6780 --- /dev/null +++ b/test/prism/errors/command_calls.txt @@ -0,0 +1,3 @@ +[a b] + ^ unexpected local variable or method; expected a `,` separator for the array elements + diff --git a/test/prism/errors/command_calls_10.txt b/test/prism/errors/command_calls_10.txt new file mode 100644 index 00000000000000..f4d9f0fabcf9ff --- /dev/null +++ b/test/prism/errors/command_calls_10.txt @@ -0,0 +1,3 @@ ++a b + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/command_calls_11.txt b/test/prism/errors/command_calls_11.txt new file mode 100644 index 00000000000000..868476c0c31935 --- /dev/null +++ b/test/prism/errors/command_calls_11.txt @@ -0,0 +1,3 @@ +a + b c + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/command_calls_12.txt b/test/prism/errors/command_calls_12.txt new file mode 100644 index 00000000000000..50c9ae88e3a4b8 --- /dev/null +++ b/test/prism/errors/command_calls_12.txt @@ -0,0 +1,3 @@ +a && b c + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/command_calls_13.txt b/test/prism/errors/command_calls_13.txt new file mode 100644 index 00000000000000..50dc4a84a02ae2 --- /dev/null +++ b/test/prism/errors/command_calls_13.txt @@ -0,0 +1,3 @@ +a =~ b c + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/command_calls_14.txt b/test/prism/errors/command_calls_14.txt new file mode 100644 index 00000000000000..1b16fd32453122 --- /dev/null +++ b/test/prism/errors/command_calls_14.txt @@ -0,0 +1,3 @@ +a = b, c d + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/command_calls_15.txt b/test/prism/errors/command_calls_15.txt new file mode 100644 index 00000000000000..d2409fd002360d --- /dev/null +++ b/test/prism/errors/command_calls_15.txt @@ -0,0 +1,3 @@ +a = *b c + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/command_calls_16.txt b/test/prism/errors/command_calls_16.txt new file mode 100644 index 00000000000000..ceb07dfe30aa11 --- /dev/null +++ b/test/prism/errors/command_calls_16.txt @@ -0,0 +1,3 @@ +a, b = c = d f + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/command_calls_17.txt b/test/prism/errors/command_calls_17.txt new file mode 100644 index 00000000000000..a78ac0985d1202 --- /dev/null +++ b/test/prism/errors/command_calls_17.txt @@ -0,0 +1,5 @@ +a ? b c : d e + ^ expected a `:` after the true expression of a ternary operator + ^ unexpected ':', expecting end-of-input + ^ unexpected ':', ignoring it + diff --git a/test/prism/errors/command_calls_18.txt b/test/prism/errors/command_calls_18.txt new file mode 100644 index 00000000000000..393e7e0ae63cc1 --- /dev/null +++ b/test/prism/errors/command_calls_18.txt @@ -0,0 +1,3 @@ +defined? a b + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/command_calls_19.txt b/test/prism/errors/command_calls_19.txt new file mode 100644 index 00000000000000..e045187f1e4734 --- /dev/null +++ b/test/prism/errors/command_calls_19.txt @@ -0,0 +1,3 @@ +! ! a b + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/command_calls_2.txt b/test/prism/errors/command_calls_2.txt new file mode 100644 index 00000000000000..b0983c015bb49d --- /dev/null +++ b/test/prism/errors/command_calls_2.txt @@ -0,0 +1,6 @@ +{a: b c} + ^ expected a `}` to close the hash literal + ^ unexpected local variable or method, expecting end-of-input + ^ unexpected '}', expecting end-of-input + ^ unexpected '}', ignoring it + diff --git a/test/prism/errors/command_calls_20.txt b/test/prism/errors/command_calls_20.txt new file mode 100644 index 00000000000000..3058ebce963cbb --- /dev/null +++ b/test/prism/errors/command_calls_20.txt @@ -0,0 +1,3 @@ +def f a = b c; end + ^ expected a delimiter to close the parameters + diff --git a/test/prism/errors/command_calls_21.txt b/test/prism/errors/command_calls_21.txt new file mode 100644 index 00000000000000..73d8f83539a468 --- /dev/null +++ b/test/prism/errors/command_calls_21.txt @@ -0,0 +1,5 @@ +def f(a = b c); end + ^ unexpected local variable or method; expected a `)` to close the parameters + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it + diff --git a/test/prism/errors/command_calls_22.txt b/test/prism/errors/command_calls_22.txt new file mode 100644 index 00000000000000..5a234e04e8b779 --- /dev/null +++ b/test/prism/errors/command_calls_22.txt @@ -0,0 +1,3 @@ +a = b rescue c d + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/command_calls_23.txt b/test/prism/errors/command_calls_23.txt new file mode 100644 index 00000000000000..db85589ffdea5d --- /dev/null +++ b/test/prism/errors/command_calls_23.txt @@ -0,0 +1,3 @@ +def a = b rescue c d + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/command_calls_24.txt b/test/prism/errors/command_calls_24.txt new file mode 100644 index 00000000000000..3046b36dc1256f --- /dev/null +++ b/test/prism/errors/command_calls_24.txt @@ -0,0 +1,5 @@ +->a=b c{} + ^ expected a `do` keyword or a `{` to open the lambda block + ^ unexpected end-of-input, assuming it is closing the parent top level context + ^ expected a lambda block beginning with `do` to end with `end` + diff --git a/test/prism/errors/command_calls_25.txt b/test/prism/errors/command_calls_25.txt new file mode 100644 index 00000000000000..5fddd90fdd09ae --- /dev/null +++ b/test/prism/errors/command_calls_25.txt @@ -0,0 +1,8 @@ +->(a=b c){} + ^ expected a matching `)` + ^ expected a `do` keyword or a `{` to open the lambda block + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it + ^ unexpected end-of-input, assuming it is closing the parent top level context + ^ expected a lambda block beginning with `do` to end with `end` + diff --git a/test/prism/errors/command_calls_26.txt b/test/prism/errors/command_calls_26.txt new file mode 100644 index 00000000000000..29ed4cb903b051 --- /dev/null +++ b/test/prism/errors/command_calls_26.txt @@ -0,0 +1,3 @@ +case; when a b; end + ^ expected a delimiter after the predicates of a `when` clause + diff --git a/test/prism/errors/command_calls_27.txt b/test/prism/errors/command_calls_27.txt new file mode 100644 index 00000000000000..8d1c3ee07791a8 --- /dev/null +++ b/test/prism/errors/command_calls_27.txt @@ -0,0 +1,3 @@ +case; in a if a b; end +^~~~ expected a predicate for a case matching statement + diff --git a/test/prism/errors/command_calls_28.txt b/test/prism/errors/command_calls_28.txt new file mode 100644 index 00000000000000..4bfe88d67ba87e --- /dev/null +++ b/test/prism/errors/command_calls_28.txt @@ -0,0 +1,3 @@ +case; in a unless a b; end +^~~~ expected a predicate for a case matching statement + diff --git a/test/prism/errors/command_calls_29.txt b/test/prism/errors/command_calls_29.txt new file mode 100644 index 00000000000000..eae012ab44c77f --- /dev/null +++ b/test/prism/errors/command_calls_29.txt @@ -0,0 +1,3 @@ +begin; rescue a b; end + ^ expected a closing delimiter for the `rescue` clause + diff --git a/test/prism/errors/command_calls_3.txt b/test/prism/errors/command_calls_3.txt new file mode 100644 index 00000000000000..77af72b904809e --- /dev/null +++ b/test/prism/errors/command_calls_3.txt @@ -0,0 +1,3 @@ +...a b + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/command_calls_30.txt b/test/prism/errors/command_calls_30.txt new file mode 100644 index 00000000000000..48e35685cbaf30 --- /dev/null +++ b/test/prism/errors/command_calls_30.txt @@ -0,0 +1,3 @@ +begin; rescue a b => c; end + ^ expected a closing delimiter for the `rescue` clause + diff --git a/test/prism/errors/command_calls_4.txt b/test/prism/errors/command_calls_4.txt new file mode 100644 index 00000000000000..4be14e57e4774e --- /dev/null +++ b/test/prism/errors/command_calls_4.txt @@ -0,0 +1,3 @@ +if ...a b; end + ^ expected `then` or `;` or '\n' + diff --git a/test/prism/errors/command_calls_5.txt b/test/prism/errors/command_calls_5.txt new file mode 100644 index 00000000000000..799a6c1136b678 --- /dev/null +++ b/test/prism/errors/command_calls_5.txt @@ -0,0 +1,3 @@ +a b, c d + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/command_calls_6.txt b/test/prism/errors/command_calls_6.txt new file mode 100644 index 00000000000000..6f09d36e942d71 --- /dev/null +++ b/test/prism/errors/command_calls_6.txt @@ -0,0 +1,6 @@ +a(b, c d) + ^ unexpected local variable or method; expected a `)` to close the arguments + ^ unexpected local variable or method, expecting end-of-input + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it + diff --git a/test/prism/errors/command_calls_7.txt b/test/prism/errors/command_calls_7.txt new file mode 100644 index 00000000000000..b5d74209fa6f2f --- /dev/null +++ b/test/prism/errors/command_calls_7.txt @@ -0,0 +1,6 @@ +a(*b c) + ^ unexpected local variable or method; expected a `)` to close the arguments + ^ unexpected local variable or method, expecting end-of-input + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it + diff --git a/test/prism/errors/command_calls_8.txt b/test/prism/errors/command_calls_8.txt new file mode 100644 index 00000000000000..e574063e72a607 --- /dev/null +++ b/test/prism/errors/command_calls_8.txt @@ -0,0 +1,6 @@ +a(**b c) + ^ unexpected local variable or method; expected a `)` to close the arguments + ^ unexpected local variable or method, expecting end-of-input + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it + diff --git a/test/prism/errors/command_calls_9.txt b/test/prism/errors/command_calls_9.txt new file mode 100644 index 00000000000000..69515d959c0698 --- /dev/null +++ b/test/prism/errors/command_calls_9.txt @@ -0,0 +1,6 @@ +a(&b c) + ^ unexpected local variable or method; expected a `)` to close the arguments + ^ unexpected local variable or method, expecting end-of-input + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it + diff --git a/test/prism/errors/conditional_predicate_closed.txt b/test/prism/errors/conditional_predicate_closed.txt new file mode 100644 index 00000000000000..6655fd2b1ceb72 --- /dev/null +++ b/test/prism/errors/conditional_predicate_closed.txt @@ -0,0 +1,6 @@ +if 0 0; elsif 0 0; end + ^ expected `then` or `;` or '\n' + ^ expected `then` or `;` or '\n' +unless 0 0; end + ^ expected `then` or `;` or '\n' + diff --git a/test/prism/errors/constant_assignment_in_method.txt b/test/prism/errors/constant_assignment_in_method.txt new file mode 100644 index 00000000000000..1ee49bffe6f57b --- /dev/null +++ b/test/prism/errors/constant_assignment_in_method.txt @@ -0,0 +1,3 @@ +def foo();A=1;end + ^~~ dynamic constant assignment + diff --git a/test/prism/errors/constant_path_with_invalid_token_after.txt b/test/prism/errors/constant_path_with_invalid_token_after.txt new file mode 100644 index 00000000000000..acb6dba30a3cec --- /dev/null +++ b/test/prism/errors/constant_path_with_invalid_token_after.txt @@ -0,0 +1,4 @@ +A::$b + ^ expected a constant after the `::` operator + ^~ unexpected global variable, expecting end-of-input + diff --git a/test/prism/errors/content_after_unterminated_heredoc.txt b/test/prism/errors/content_after_unterminated_heredoc.txt new file mode 100644 index 00000000000000..c0446a8c39fff4 --- /dev/null +++ b/test/prism/errors/content_after_unterminated_heredoc.txt @@ -0,0 +1,4 @@ +<<~FOO.foo + ^~~ unterminated heredoc; can't find string "FOO" anywhere before EOF + ^~~ unterminated heredoc; can't find string "FOO" anywhere before EOF + diff --git a/test/prism/errors/cr_without_lf_in_percent_expression.txt b/test/prism/errors/cr_without_lf_in_percent_expression.txt new file mode 100644 index 00000000000000..903f8b4b4afa7e --- /dev/null +++ b/test/prism/errors/cr_without_lf_in_percent_expression.txt @@ -0,0 +1,3 @@ +% + ^ unterminated string meets end of file + diff --git a/test/prism/errors/def_with_empty_expression_receiver.txt b/test/prism/errors/def_with_empty_expression_receiver.txt new file mode 100644 index 00000000000000..153fe8a1c6f7e9 --- /dev/null +++ b/test/prism/errors/def_with_empty_expression_receiver.txt @@ -0,0 +1,3 @@ +def ().a; end + ^ expected a receiver for the method definition + diff --git a/test/prism/errors/def_with_expression_receiver_and_no_identifier.txt b/test/prism/errors/def_with_expression_receiver_and_no_identifier.txt new file mode 100644 index 00000000000000..1aefc07f1a2b2f --- /dev/null +++ b/test/prism/errors/def_with_expression_receiver_and_no_identifier.txt @@ -0,0 +1,4 @@ +def (a); end + ^ expected a `.` or `::` after the receiver in a method definition + ^ unexpected ';'; expected a method name + diff --git a/test/prism/errors/def_with_multiple_statements_receiver.txt b/test/prism/errors/def_with_multiple_statements_receiver.txt new file mode 100644 index 00000000000000..80c9d4c19018e0 --- /dev/null +++ b/test/prism/errors/def_with_multiple_statements_receiver.txt @@ -0,0 +1,10 @@ +def ( +a +b +^ expected a matching `)` +^ expected a `.` or `::` after the receiver in a method definition + ^ expected a delimiter to close the parameters +).c; end +^ unexpected ')', ignoring it + ^ unexpected '.', ignoring it + diff --git a/test/prism/errors/defining_numbered_parameter.txt b/test/prism/errors/defining_numbered_parameter.txt new file mode 100644 index 00000000000000..2bf05d9563131b --- /dev/null +++ b/test/prism/errors/defining_numbered_parameter.txt @@ -0,0 +1,3 @@ +def _1; end + ^~ _1 is reserved for numbered parameters + diff --git a/test/prism/errors/defining_numbered_parameter_2.txt b/test/prism/errors/defining_numbered_parameter_2.txt new file mode 100644 index 00000000000000..dc4739b126abe6 --- /dev/null +++ b/test/prism/errors/defining_numbered_parameter_2.txt @@ -0,0 +1,3 @@ +def self._1; end + ^~ _1 is reserved for numbered parameters + diff --git a/test/prism/errors/do_not_allow_characters_other_than_0_9_a_f_and_A_F_in_u_Unicode_character_notation.txt b/test/prism/errors/do_not_allow_characters_other_than_0_9_a_f_and_A_F_in_u_Unicode_character_notation.txt new file mode 100644 index 00000000000000..953b9589d1ff67 --- /dev/null +++ b/test/prism/errors/do_not_allow_characters_other_than_0_9_a_f_and_A_F_in_u_Unicode_character_notation.txt @@ -0,0 +1,4 @@ +"\u{000z}" + ^ invalid Unicode escape sequence + ^ unterminated Unicode escape + diff --git a/test/prism/errors/do_not_allow_forward_arguments_in_blocks.txt b/test/prism/errors/do_not_allow_forward_arguments_in_blocks.txt new file mode 100644 index 00000000000000..df495576177796 --- /dev/null +++ b/test/prism/errors/do_not_allow_forward_arguments_in_blocks.txt @@ -0,0 +1,3 @@ +a {|...|} + ^~~ unexpected ... when the parent method is not forwarding + diff --git a/test/prism/errors/do_not_allow_forward_arguments_in_lambda_literals.txt b/test/prism/errors/do_not_allow_forward_arguments_in_lambda_literals.txt new file mode 100644 index 00000000000000..c2405a5c66fed1 --- /dev/null +++ b/test/prism/errors/do_not_allow_forward_arguments_in_lambda_literals.txt @@ -0,0 +1,3 @@ +->(...) {} + ^~~ unexpected ... when the parent method is not forwarding + diff --git a/test/prism/errors/do_not_allow_more_than_6_hexadecimal_digits_in_u_Unicode_character_notation.txt b/test/prism/errors/do_not_allow_more_than_6_hexadecimal_digits_in_u_Unicode_character_notation.txt new file mode 100644 index 00000000000000..50795c93533896 --- /dev/null +++ b/test/prism/errors/do_not_allow_more_than_6_hexadecimal_digits_in_u_Unicode_character_notation.txt @@ -0,0 +1,3 @@ +"\u{0000001}" + ^~~~~~~ invalid Unicode escape sequence; maximum length is 6 digits + diff --git a/test/prism/errors/do_not_allow_multiple_codepoints_in_a_single_character_literal.txt b/test/prism/errors/do_not_allow_multiple_codepoints_in_a_single_character_literal.txt new file mode 100644 index 00000000000000..1a93dc6c694326 --- /dev/null +++ b/test/prism/errors/do_not_allow_multiple_codepoints_in_a_single_character_literal.txt @@ -0,0 +1,3 @@ +?\u{0001 0002} + ^~~ invalid Unicode escape sequence; Multiple codepoints at single character literal are disallowed + diff --git a/test/prism/errors/do_not_allow_trailing_commas_in_lambda_parameters.txt b/test/prism/errors/do_not_allow_trailing_commas_in_lambda_parameters.txt new file mode 100644 index 00000000000000..11f23f034576b4 --- /dev/null +++ b/test/prism/errors/do_not_allow_trailing_commas_in_lambda_parameters.txt @@ -0,0 +1,3 @@ +-> (a, b, ) {} + ^ unexpected `,` in parameters + diff --git a/test/prism/errors/do_not_allow_trailing_commas_in_method_parameters.txt b/test/prism/errors/do_not_allow_trailing_commas_in_method_parameters.txt new file mode 100644 index 00000000000000..c0fec0c7048a0c --- /dev/null +++ b/test/prism/errors/do_not_allow_trailing_commas_in_method_parameters.txt @@ -0,0 +1,3 @@ +def foo(a,b,c,);end + ^ unexpected `,` in parameters + diff --git a/test/prism/errors/dont_allow_return_inside_class_body.txt b/test/prism/errors/dont_allow_return_inside_class_body.txt new file mode 100644 index 00000000000000..286eba210339b5 --- /dev/null +++ b/test/prism/errors/dont_allow_return_inside_class_body.txt @@ -0,0 +1,3 @@ +class A; return; end + ^~~~~~ Invalid return in class/module body + diff --git a/test/prism/errors/dont_allow_return_inside_module_body.txt b/test/prism/errors/dont_allow_return_inside_module_body.txt new file mode 100644 index 00000000000000..85dd619a932340 --- /dev/null +++ b/test/prism/errors/dont_allow_return_inside_module_body.txt @@ -0,0 +1,3 @@ +module A; return; end + ^~~~~~ Invalid return in class/module body + diff --git a/test/prism/errors/dont_allow_setting_to_back_and_nth_reference.txt b/test/prism/errors/dont_allow_setting_to_back_and_nth_reference.txt new file mode 100644 index 00000000000000..71b5b94589c038 --- /dev/null +++ b/test/prism/errors/dont_allow_setting_to_back_and_nth_reference.txt @@ -0,0 +1,7 @@ +begin +$+ = nil +^~ Can't set variable $+ +$1466 = nil +^~~~~ Can't set variable $1466 +end + diff --git a/test/prism/errors/double_arguments_forwarding.txt b/test/prism/errors/double_arguments_forwarding.txt new file mode 100644 index 00000000000000..29c78f8c802aa9 --- /dev/null +++ b/test/prism/errors/double_arguments_forwarding.txt @@ -0,0 +1,4 @@ +def foo(..., ...) + ^~~ unexpected parameter order +end + diff --git a/test/prism/errors/double_scope_numbered_parameters.txt b/test/prism/errors/double_scope_numbered_parameters.txt new file mode 100644 index 00000000000000..0bb9df4ede08eb --- /dev/null +++ b/test/prism/errors/double_scope_numbered_parameters.txt @@ -0,0 +1,3 @@ +-> { _1 + -> { _2 } } + ^~ numbered parameter is already used in outer block + diff --git a/test/prism/errors/double_splat_followed_by_splat_argument.txt b/test/prism/errors/double_splat_followed_by_splat_argument.txt new file mode 100644 index 00000000000000..b2aec1167e83e4 --- /dev/null +++ b/test/prism/errors/double_splat_followed_by_splat_argument.txt @@ -0,0 +1,3 @@ +a(**kwargs, *args) + ^~~~~ unexpected `*` splat argument after a `**` keyword splat argument + diff --git a/test/prism/errors/duplicate_pattern_capture.txt b/test/prism/errors/duplicate_pattern_capture.txt new file mode 100644 index 00000000000000..4b48fd31188d41 --- /dev/null +++ b/test/prism/errors/duplicate_pattern_capture.txt @@ -0,0 +1,17 @@ +case (); in [a, a]; end + ^ duplicated variable name +case (); in [a, *a]; end + ^ duplicated variable name +case (); in {a: a, b: a}; end + ^ duplicated variable name +case (); in {a: a, **a}; end + ^ duplicated variable name +case (); in [a, {a:}]; end + ^ duplicated variable name +case (); in [a, {a: {a: {a: [a]}}}]; end + ^ duplicated variable name +case (); in a => a; end + ^ duplicated variable name +case (); in [A => a, {a: b => a}]; end + ^ duplicated variable name + diff --git a/test/prism/errors/duplicate_pattern_hash_key.txt b/test/prism/errors/duplicate_pattern_hash_key.txt new file mode 100644 index 00000000000000..201b51234faffd --- /dev/null +++ b/test/prism/errors/duplicate_pattern_hash_key.txt @@ -0,0 +1,4 @@ +case (); in {a:, a:}; end + ^~ duplicated key name + ^ duplicated variable name + diff --git a/test/prism/errors/duplicate_pattern_hash_key_2.txt b/test/prism/errors/duplicate_pattern_hash_key_2.txt new file mode 100644 index 00000000000000..66756c454a50f2 --- /dev/null +++ b/test/prism/errors/duplicate_pattern_hash_key_2.txt @@ -0,0 +1,3 @@ +case (); in {a:1, a:2}; end + ^~ duplicated key name + diff --git a/test/prism/errors/duplicated_parameter_names.txt b/test/prism/errors/duplicated_parameter_names.txt new file mode 100644 index 00000000000000..7b82685ca3633e --- /dev/null +++ b/test/prism/errors/duplicated_parameter_names.txt @@ -0,0 +1,3 @@ +def foo(a,b,a);end + ^ duplicated argument name + diff --git a/test/prism/errors/duplicated_parameter_names_2.txt b/test/prism/errors/duplicated_parameter_names_2.txt new file mode 100644 index 00000000000000..8396993d5692d0 --- /dev/null +++ b/test/prism/errors/duplicated_parameter_names_2.txt @@ -0,0 +1,3 @@ +def foo(a,b,*a);end + ^ duplicated argument name + diff --git a/test/prism/errors/duplicated_parameter_names_3.txt b/test/prism/errors/duplicated_parameter_names_3.txt new file mode 100644 index 00000000000000..437a6623c3e365 --- /dev/null +++ b/test/prism/errors/duplicated_parameter_names_3.txt @@ -0,0 +1,3 @@ +def foo(a,b,**a);end + ^ duplicated argument name + diff --git a/test/prism/errors/duplicated_parameter_names_4.txt b/test/prism/errors/duplicated_parameter_names_4.txt new file mode 100644 index 00000000000000..a420dd8a695ba3 --- /dev/null +++ b/test/prism/errors/duplicated_parameter_names_4.txt @@ -0,0 +1,3 @@ +def foo(a,b,&a);end + ^ duplicated argument name + diff --git a/test/prism/errors/duplicated_parameter_names_5.txt b/test/prism/errors/duplicated_parameter_names_5.txt new file mode 100644 index 00000000000000..694d3a668c0179 --- /dev/null +++ b/test/prism/errors/duplicated_parameter_names_5.txt @@ -0,0 +1,3 @@ +def foo(a = 1,b,*c);end + ^ unexpected parameter `*` + diff --git a/test/prism/errors/ellipsis_in_no_paren_call.txt b/test/prism/errors/ellipsis_in_no_paren_call.txt new file mode 100644 index 00000000000000..87a847d19273c5 --- /dev/null +++ b/test/prism/errors/ellipsis_in_no_paren_call.txt @@ -0,0 +1,3 @@ +def foo(...); foo 1, ...; end + ^~~ unexpected `...` in an non-parenthesized call + diff --git a/test/prism/errors/for_loops_index_missing.txt b/test/prism/errors/for_loops_index_missing.txt new file mode 100644 index 00000000000000..a57c22b0449b5e --- /dev/null +++ b/test/prism/errors/for_loops_index_missing.txt @@ -0,0 +1,5 @@ +for in 1..10 +^~~ expected an index after `for` +i +end + diff --git a/test/prism/errors/for_loops_only_end.txt b/test/prism/errors/for_loops_only_end.txt new file mode 100644 index 00000000000000..94cc5270b56dd7 --- /dev/null +++ b/test/prism/errors/for_loops_only_end.txt @@ -0,0 +1,5 @@ +for end +^~~ expected an index after `for` + ^ expected an `in` after the index in a `for` statement + ^ expected a collection after the `in` in a `for` statement + diff --git a/test/prism/errors/forwarding_arg_after_keyword_rest.txt b/test/prism/errors/forwarding_arg_after_keyword_rest.txt new file mode 100644 index 00000000000000..86fe4aad93f8c2 --- /dev/null +++ b/test/prism/errors/forwarding_arg_after_keyword_rest.txt @@ -0,0 +1,3 @@ +def f(**,...);end + ^~~ unexpected parameter order + diff --git a/test/prism/errors/forwarding_arg_and_block.txt b/test/prism/errors/forwarding_arg_and_block.txt new file mode 100644 index 00000000000000..65c75a5d7cfc85 --- /dev/null +++ b/test/prism/errors/forwarding_arg_and_block.txt @@ -0,0 +1,3 @@ +def foo(...) = foo(...) { } + ^~~ both block arg and actual block given; only one block is allowed + diff --git a/test/prism/errors/incomplete_instance_var_string.txt b/test/prism/errors/incomplete_instance_var_string.txt new file mode 100644 index 00000000000000..b28947fc0e3c6a --- /dev/null +++ b/test/prism/errors/incomplete_instance_var_string.txt @@ -0,0 +1,4 @@ +%@#@@# + ^ '@' without identifiers is not allowed as an instance variable name + ^ unexpected instance variable, expecting end-of-input + diff --git a/test/prism/errors/index_call_with_block_and_write.txt b/test/prism/errors/index_call_with_block_and_write.txt new file mode 100644 index 00000000000000..3d92fbfea745c6 --- /dev/null +++ b/test/prism/errors/index_call_with_block_and_write.txt @@ -0,0 +1,5 @@ +foo[1] {} &&= 1 +^~~~~~~~~ unexpected write target + ^~~ unexpected operator after a call with arguments + ^~~ unexpected operator after a call with a block + diff --git a/test/prism/errors/index_call_with_block_operator_write.txt b/test/prism/errors/index_call_with_block_operator_write.txt new file mode 100644 index 00000000000000..96c413cd39f9e2 --- /dev/null +++ b/test/prism/errors/index_call_with_block_operator_write.txt @@ -0,0 +1,5 @@ +foo[1] {} += 1 +^~~~~~~~~ unexpected write target + ^~ unexpected operator after a call with arguments + ^~ unexpected operator after a call with a block + diff --git a/test/prism/errors/index_call_with_block_or_write.txt b/test/prism/errors/index_call_with_block_or_write.txt new file mode 100644 index 00000000000000..2d250fba063bbc --- /dev/null +++ b/test/prism/errors/index_call_with_block_or_write.txt @@ -0,0 +1,5 @@ +foo[1] {} ||= 1 +^~~~~~~~~ unexpected write target + ^~~ unexpected operator after a call with arguments + ^~~ unexpected operator after a call with a block + diff --git a/test/prism/errors/interpolated_regular_expression_with_unknown_regexp_options.txt b/test/prism/errors/interpolated_regular_expression_with_unknown_regexp_options.txt new file mode 100644 index 00000000000000..8e78753b1ccc31 --- /dev/null +++ b/test/prism/errors/interpolated_regular_expression_with_unknown_regexp_options.txt @@ -0,0 +1,3 @@ +/#{foo}/AZaz + ^~~~~ unknown regexp options - AZaz + diff --git a/test/prism/errors/invalid_global_variable_write.txt b/test/prism/errors/invalid_global_variable_write.txt new file mode 100644 index 00000000000000..9d9018bcf14ac5 --- /dev/null +++ b/test/prism/errors/invalid_global_variable_write.txt @@ -0,0 +1,4 @@ +$', +^~ Can't set variable $' +^~ unexpected write target + diff --git a/test/prism/errors/invalid_hex_escape.txt b/test/prism/errors/invalid_hex_escape.txt new file mode 100644 index 00000000000000..4fb847f6d24232 --- /dev/null +++ b/test/prism/errors/invalid_hex_escape.txt @@ -0,0 +1,3 @@ +"\xx" + ^~ invalid hex escape sequence + diff --git a/test/prism/errors/invalid_multi_target.txt b/test/prism/errors/invalid_multi_target.txt new file mode 100644 index 00000000000000..9756278b0cf6ae --- /dev/null +++ b/test/prism/errors/invalid_multi_target.txt @@ -0,0 +1,3 @@ +foo, +^~~ unexpected write target + diff --git a/test/prism/errors/invalid_multi_target_10.txt b/test/prism/errors/invalid_multi_target_10.txt new file mode 100644 index 00000000000000..0e87b67d36d817 --- /dev/null +++ b/test/prism/errors/invalid_multi_target_10.txt @@ -0,0 +1,3 @@ +Foo, +^~~ unexpected write target + diff --git a/test/prism/errors/invalid_multi_target_11.txt b/test/prism/errors/invalid_multi_target_11.txt new file mode 100644 index 00000000000000..8185cde79e9879 --- /dev/null +++ b/test/prism/errors/invalid_multi_target_11.txt @@ -0,0 +1,3 @@ +::Foo, +^~~~~ unexpected write target + diff --git a/test/prism/errors/invalid_multi_target_12.txt b/test/prism/errors/invalid_multi_target_12.txt new file mode 100644 index 00000000000000..f511a8a76f1ffd --- /dev/null +++ b/test/prism/errors/invalid_multi_target_12.txt @@ -0,0 +1,3 @@ +Foo::Foo, +^~~~~~~~ unexpected write target + diff --git a/test/prism/errors/invalid_multi_target_13.txt b/test/prism/errors/invalid_multi_target_13.txt new file mode 100644 index 00000000000000..7c9a3fb4e1e46d --- /dev/null +++ b/test/prism/errors/invalid_multi_target_13.txt @@ -0,0 +1,3 @@ +Foo::foo, +^~~~~~~~ unexpected write target + diff --git a/test/prism/errors/invalid_multi_target_14.txt b/test/prism/errors/invalid_multi_target_14.txt new file mode 100644 index 00000000000000..88dc08de922b72 --- /dev/null +++ b/test/prism/errors/invalid_multi_target_14.txt @@ -0,0 +1,3 @@ +foo[foo], +^~~~~~~~ unexpected write target + diff --git a/test/prism/errors/invalid_multi_target_15.txt b/test/prism/errors/invalid_multi_target_15.txt new file mode 100644 index 00000000000000..c140833467c54c --- /dev/null +++ b/test/prism/errors/invalid_multi_target_15.txt @@ -0,0 +1,3 @@ +(foo, bar) +^~~~~~~~~~ unexpected write target + diff --git a/test/prism/errors/invalid_multi_target_16.txt b/test/prism/errors/invalid_multi_target_16.txt new file mode 100644 index 00000000000000..20ea56331f5458 --- /dev/null +++ b/test/prism/errors/invalid_multi_target_16.txt @@ -0,0 +1,3 @@ +foo((foo, bar)) + ^~~~~~~~~~ unexpected write target + diff --git a/test/prism/errors/invalid_multi_target_17.txt b/test/prism/errors/invalid_multi_target_17.txt new file mode 100644 index 00000000000000..da1ced0c59581e --- /dev/null +++ b/test/prism/errors/invalid_multi_target_17.txt @@ -0,0 +1,3 @@ +foo((*)) + ^~~ unexpected write target + diff --git a/test/prism/errors/invalid_multi_target_18.txt b/test/prism/errors/invalid_multi_target_18.txt new file mode 100644 index 00000000000000..2beed193b44d78 --- /dev/null +++ b/test/prism/errors/invalid_multi_target_18.txt @@ -0,0 +1,3 @@ +foo(((foo, bar), *)) + ^~~~~~~~~~~~~~~ unexpected write target + diff --git a/test/prism/errors/invalid_multi_target_19.txt b/test/prism/errors/invalid_multi_target_19.txt new file mode 100644 index 00000000000000..b5e3e6999add1c --- /dev/null +++ b/test/prism/errors/invalid_multi_target_19.txt @@ -0,0 +1,3 @@ +(foo, bar) + 1 +^~~~~~~~~~ unexpected write target + diff --git a/test/prism/errors/invalid_multi_target_2.txt b/test/prism/errors/invalid_multi_target_2.txt new file mode 100644 index 00000000000000..68a7bbc305b7a2 --- /dev/null +++ b/test/prism/errors/invalid_multi_target_2.txt @@ -0,0 +1,3 @@ +foo = 1; foo, + ^~~ unexpected write target + diff --git a/test/prism/errors/invalid_multi_target_20.txt b/test/prism/errors/invalid_multi_target_20.txt new file mode 100644 index 00000000000000..e800bcf20444a9 --- /dev/null +++ b/test/prism/errors/invalid_multi_target_20.txt @@ -0,0 +1,3 @@ +(foo, bar) in baz +^~~~~~~~~~ unexpected write target + diff --git a/test/prism/errors/invalid_multi_target_3.txt b/test/prism/errors/invalid_multi_target_3.txt new file mode 100644 index 00000000000000..51e62076038d40 --- /dev/null +++ b/test/prism/errors/invalid_multi_target_3.txt @@ -0,0 +1,3 @@ +foo.bar, +^~~~~~~ unexpected write target + diff --git a/test/prism/errors/invalid_multi_target_4.txt b/test/prism/errors/invalid_multi_target_4.txt new file mode 100644 index 00000000000000..f4c3599ffea15c --- /dev/null +++ b/test/prism/errors/invalid_multi_target_4.txt @@ -0,0 +1,3 @@ +*foo, +^~~~ unexpected write target + diff --git a/test/prism/errors/invalid_multi_target_5.txt b/test/prism/errors/invalid_multi_target_5.txt new file mode 100644 index 00000000000000..5d143a3f5dc71d --- /dev/null +++ b/test/prism/errors/invalid_multi_target_5.txt @@ -0,0 +1,3 @@ +@foo, +^~~~ unexpected write target + diff --git a/test/prism/errors/invalid_multi_target_6.txt b/test/prism/errors/invalid_multi_target_6.txt new file mode 100644 index 00000000000000..6d15893f57d3ed --- /dev/null +++ b/test/prism/errors/invalid_multi_target_6.txt @@ -0,0 +1,3 @@ +@@foo, +^~~~~ unexpected write target + diff --git a/test/prism/errors/invalid_multi_target_7.txt b/test/prism/errors/invalid_multi_target_7.txt new file mode 100644 index 00000000000000..451f9f0a00e17e --- /dev/null +++ b/test/prism/errors/invalid_multi_target_7.txt @@ -0,0 +1,3 @@ +$foo, +^~~~ unexpected write target + diff --git a/test/prism/errors/invalid_multi_target_8.txt b/test/prism/errors/invalid_multi_target_8.txt new file mode 100644 index 00000000000000..fdbe272f9a0278 --- /dev/null +++ b/test/prism/errors/invalid_multi_target_8.txt @@ -0,0 +1,4 @@ +$1, +^~ Can't set variable $1 +^~ unexpected write target + diff --git a/test/prism/errors/invalid_multi_target_9.txt b/test/prism/errors/invalid_multi_target_9.txt new file mode 100644 index 00000000000000..038f355c5da459 --- /dev/null +++ b/test/prism/errors/invalid_multi_target_9.txt @@ -0,0 +1,4 @@ +$+, +^~ Can't set variable $+ +^~ unexpected write target + diff --git a/test/prism/errors/invalid_number_underscores.txt b/test/prism/errors/invalid_number_underscores.txt new file mode 100644 index 00000000000000..8fc79ed7a27067 --- /dev/null +++ b/test/prism/errors/invalid_number_underscores.txt @@ -0,0 +1,3 @@ +1__1 + ^ invalid underscore placement in number + diff --git a/test/prism/errors/invalid_number_underscores_10.txt b/test/prism/errors/invalid_number_underscores_10.txt new file mode 100644 index 00000000000000..53b0cc07196b98 --- /dev/null +++ b/test/prism/errors/invalid_number_underscores_10.txt @@ -0,0 +1,3 @@ +01_1_ + ^ trailing '_' in number + diff --git a/test/prism/errors/invalid_number_underscores_11.txt b/test/prism/errors/invalid_number_underscores_11.txt new file mode 100644 index 00000000000000..469110f86fecce --- /dev/null +++ b/test/prism/errors/invalid_number_underscores_11.txt @@ -0,0 +1,3 @@ +0d1_1_ + ^ trailing '_' in number + diff --git a/test/prism/errors/invalid_number_underscores_12.txt b/test/prism/errors/invalid_number_underscores_12.txt new file mode 100644 index 00000000000000..a9b63a4b6c2b80 --- /dev/null +++ b/test/prism/errors/invalid_number_underscores_12.txt @@ -0,0 +1,3 @@ +0x1_1_ + ^ trailing '_' in number + diff --git a/test/prism/errors/invalid_number_underscores_2.txt b/test/prism/errors/invalid_number_underscores_2.txt new file mode 100644 index 00000000000000..2762e087907c8e --- /dev/null +++ b/test/prism/errors/invalid_number_underscores_2.txt @@ -0,0 +1,3 @@ +0b1__1 + ^ invalid underscore placement in number + diff --git a/test/prism/errors/invalid_number_underscores_3.txt b/test/prism/errors/invalid_number_underscores_3.txt new file mode 100644 index 00000000000000..23f1e0b10be283 --- /dev/null +++ b/test/prism/errors/invalid_number_underscores_3.txt @@ -0,0 +1,3 @@ +0o1__1 + ^ invalid underscore placement in number + diff --git a/test/prism/errors/invalid_number_underscores_4.txt b/test/prism/errors/invalid_number_underscores_4.txt new file mode 100644 index 00000000000000..ced149752fafab --- /dev/null +++ b/test/prism/errors/invalid_number_underscores_4.txt @@ -0,0 +1,3 @@ +01__1 + ^ invalid underscore placement in number + diff --git a/test/prism/errors/invalid_number_underscores_5.txt b/test/prism/errors/invalid_number_underscores_5.txt new file mode 100644 index 00000000000000..5e3f2bf6824f6b --- /dev/null +++ b/test/prism/errors/invalid_number_underscores_5.txt @@ -0,0 +1,3 @@ +0d1__1 + ^ invalid underscore placement in number + diff --git a/test/prism/errors/invalid_number_underscores_6.txt b/test/prism/errors/invalid_number_underscores_6.txt new file mode 100644 index 00000000000000..225b654248fd59 --- /dev/null +++ b/test/prism/errors/invalid_number_underscores_6.txt @@ -0,0 +1,3 @@ +0x1__1 + ^ invalid underscore placement in number + diff --git a/test/prism/errors/invalid_number_underscores_7.txt b/test/prism/errors/invalid_number_underscores_7.txt new file mode 100644 index 00000000000000..d953b4cbc4e8a7 --- /dev/null +++ b/test/prism/errors/invalid_number_underscores_7.txt @@ -0,0 +1,3 @@ +1_1_ + ^ trailing '_' in number + diff --git a/test/prism/errors/invalid_number_underscores_8.txt b/test/prism/errors/invalid_number_underscores_8.txt new file mode 100644 index 00000000000000..cbdcd95d8f487c --- /dev/null +++ b/test/prism/errors/invalid_number_underscores_8.txt @@ -0,0 +1,3 @@ +0b1_1_ + ^ trailing '_' in number + diff --git a/test/prism/errors/invalid_number_underscores_9.txt b/test/prism/errors/invalid_number_underscores_9.txt new file mode 100644 index 00000000000000..173282ffb2ed4b --- /dev/null +++ b/test/prism/errors/invalid_number_underscores_9.txt @@ -0,0 +1,3 @@ +0o1_1_ + ^ trailing '_' in number + diff --git a/test/prism/errors/invalid_operator_write_dot.txt b/test/prism/errors/invalid_operator_write_dot.txt new file mode 100644 index 00000000000000..666817e60f0eec --- /dev/null +++ b/test/prism/errors/invalid_operator_write_dot.txt @@ -0,0 +1,3 @@ +foo.+= 1 + ^ unexpected write target + diff --git a/test/prism/errors/invalid_operator_write_fcall.txt b/test/prism/errors/invalid_operator_write_fcall.txt new file mode 100644 index 00000000000000..2748bf32918a18 --- /dev/null +++ b/test/prism/errors/invalid_operator_write_fcall.txt @@ -0,0 +1,3 @@ +foo! += 1 +^~~~ unexpected write target + diff --git a/test/prism/errors/it_with_ordinary_parameter.txt b/test/prism/errors/it_with_ordinary_parameter.txt new file mode 100644 index 00000000000000..0fc34e9cc880e0 --- /dev/null +++ b/test/prism/errors/it_with_ordinary_parameter.txt @@ -0,0 +1,3 @@ +proc { || it } + ^~ `it` is not allowed when an ordinary parameter is defined + diff --git a/test/prism/errors/keywords_parameters_before_required_parameters.txt b/test/prism/errors/keywords_parameters_before_required_parameters.txt new file mode 100644 index 00000000000000..42d036e95018cc --- /dev/null +++ b/test/prism/errors/keywords_parameters_before_required_parameters.txt @@ -0,0 +1,4 @@ +def foo(b:, a) + ^ unexpected parameter order +end + diff --git a/test/prism/errors/loop_conditional_is_closed.txt b/test/prism/errors/loop_conditional_is_closed.txt new file mode 100644 index 00000000000000..2be13533199771 --- /dev/null +++ b/test/prism/errors/loop_conditional_is_closed.txt @@ -0,0 +1,4 @@ +while 0 0; foo; end; until 0 0; foo; end + ^ expected a predicate expression for the `while` statement + ^ expected a predicate expression for the `until` statement + diff --git a/test/prism/errors/match_plus.txt b/test/prism/errors/match_plus.txt new file mode 100644 index 00000000000000..5e349a96ad67df --- /dev/null +++ b/test/prism/errors/match_plus.txt @@ -0,0 +1,7 @@ +a in b + c + ^ unexpected '+', expecting end-of-input + ^ unexpected '+', ignoring it +a => b + c + ^ unexpected '+', expecting end-of-input + ^ unexpected '+', ignoring it + diff --git a/test/prism/errors/method_parameters_after_arguments_forwarding.txt b/test/prism/errors/method_parameters_after_arguments_forwarding.txt new file mode 100644 index 00000000000000..ec2aefda1def7e --- /dev/null +++ b/test/prism/errors/method_parameters_after_arguments_forwarding.txt @@ -0,0 +1,4 @@ +def foo(..., a) + ^ unexpected parameter order +end + diff --git a/test/prism/errors/method_parameters_after_block.txt b/test/prism/errors/method_parameters_after_block.txt new file mode 100644 index 00000000000000..6e2091d5d1e5aa --- /dev/null +++ b/test/prism/errors/method_parameters_after_block.txt @@ -0,0 +1,4 @@ +def foo(&block, a) + ^ unexpected parameter order +end + diff --git a/test/prism/errors/method_with_arguments_after_anonymous_block.txt b/test/prism/errors/method_with_arguments_after_anonymous_block.txt new file mode 100644 index 00000000000000..0d986b3c0102a9 --- /dev/null +++ b/test/prism/errors/method_with_arguments_after_anonymous_block.txt @@ -0,0 +1,4 @@ +def foo(&, a) + ^ unexpected parameter order +end + diff --git a/test/prism/errors/missing_terminator_in_parentheses.txt b/test/prism/errors/missing_terminator_in_parentheses.txt new file mode 100644 index 00000000000000..af4b698f0c9bfa --- /dev/null +++ b/test/prism/errors/missing_terminator_in_parentheses.txt @@ -0,0 +1,3 @@ +(0 0) + ^ unexpected integer, expecting end-of-input + diff --git a/test/prism/errors/module_definition_in_method_body.txt b/test/prism/errors/module_definition_in_method_body.txt new file mode 100644 index 00000000000000..59900c96dde039 --- /dev/null +++ b/test/prism/errors/module_definition_in_method_body.txt @@ -0,0 +1,3 @@ +def foo;module A;end;end + ^~~~~~ unexpected module definition in method body + diff --git a/test/prism/errors/module_definition_in_method_body_within_block.txt b/test/prism/errors/module_definition_in_method_body_within_block.txt new file mode 100644 index 00000000000000..204be3560798c6 --- /dev/null +++ b/test/prism/errors/module_definition_in_method_body_within_block.txt @@ -0,0 +1,7 @@ +def foo + bar do + module Foo;end + ^~~~~~ unexpected module definition in method body + end +end + diff --git a/test/prism/errors/module_definition_in_method_defs.txt b/test/prism/errors/module_definition_in_method_defs.txt new file mode 100644 index 00000000000000..c5a6a8a2e8ab61 --- /dev/null +++ b/test/prism/errors/module_definition_in_method_defs.txt @@ -0,0 +1,7 @@ +def foo(bar = module A;end);end + ^~~~~~ unexpected module definition in method body +def foo;rescue;module A;end;end + ^~~~~~ unexpected module definition in method body +def foo;ensure;module A;end;end + ^~~~~~ unexpected module definition in method body + diff --git a/test/prism/errors/module_name_recoverable.txt b/test/prism/errors/module_name_recoverable.txt new file mode 100644 index 00000000000000..58a28a60c566e4 --- /dev/null +++ b/test/prism/errors/module_name_recoverable.txt @@ -0,0 +1,4 @@ +module Parent module end + ^~~~~~ unexpected constant path after `module`; class/module name must be CONSTANT + ^~~ unexpected 'end', assuming it is closing the parent module definition + diff --git a/test/prism/errors/multiple_error_in_parameters_order.txt b/test/prism/errors/multiple_error_in_parameters_order.txt new file mode 100644 index 00000000000000..5dae0a105ac466 --- /dev/null +++ b/test/prism/errors/multiple_error_in_parameters_order.txt @@ -0,0 +1,5 @@ +def foo(**args, a, b:) + ^ unexpected parameter order + ^~ unexpected parameter order +end + diff --git a/test/prism/errors/next_1.txt b/test/prism/errors/next_1.txt new file mode 100644 index 00000000000000..b56b7f6ae634b3 --- /dev/null +++ b/test/prism/errors/next_1.txt @@ -0,0 +1,4 @@ +next 1,; + ^ expected an argument +^~~~~~~ Invalid next + diff --git a/test/prism/errors/next_1_2_3.txt b/test/prism/errors/next_1_2_3.txt new file mode 100644 index 00000000000000..7abe577ab34505 --- /dev/null +++ b/test/prism/errors/next_1_2_3.txt @@ -0,0 +1,8 @@ +next(1, 2, 3) + ^ unexpected ',', expecting end-of-input + ^ unexpected ',', ignoring it + ^ expected a matching `)` + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it +^~~~~~~~~~~~ Invalid next + diff --git a/test/prism/errors/non_assoc_equality.txt b/test/prism/errors/non_assoc_equality.txt new file mode 100644 index 00000000000000..6ce8da88d6a72c --- /dev/null +++ b/test/prism/errors/non_assoc_equality.txt @@ -0,0 +1,19 @@ +1 == 2 == 3 + ^~ unexpected '==', expecting end-of-input + ^~ unexpected '==', ignoring it +1 != 2 != 3 + ^~ unexpected '!=', expecting end-of-input + ^~ unexpected '!=', ignoring it +1 === 2 === 3 + ^~~ unexpected '===', expecting end-of-input + ^~~ unexpected '===', ignoring it +1 =~ 2 =~ 3 + ^~ unexpected '=~', expecting end-of-input + ^~ unexpected '=~', ignoring it +1 !~ 2 !~ 3 + ^~ unexpected '!~', expecting end-of-input + ^~ unexpected '!~', ignoring it +1 <=> 2 <=> 3 + ^~~ unexpected '<=>', expecting end-of-input + ^~~ unexpected '<=>', ignoring it + diff --git a/test/prism/errors/non_assoc_range.txt b/test/prism/errors/non_assoc_range.txt new file mode 100644 index 00000000000000..072cf6d3c6e19e --- /dev/null +++ b/test/prism/errors/non_assoc_range.txt @@ -0,0 +1,4 @@ +1....2 + ^ unexpected '.', expecting end-of-input + ^ unexpected '.', ignoring it + diff --git a/test/prism/errors/numbered_parameters_in_block_arguments.txt b/test/prism/errors/numbered_parameters_in_block_arguments.txt new file mode 100644 index 00000000000000..d01999c53ea304 --- /dev/null +++ b/test/prism/errors/numbered_parameters_in_block_arguments.txt @@ -0,0 +1,3 @@ +foo { |_1| } + ^~ _1 is reserved for numbered parameters + diff --git a/test/prism/errors/optional_block_parameters_with_unary_operator.txt b/test/prism/errors/optional_block_parameters_with_unary_operator.txt new file mode 100644 index 00000000000000..fd45f126487b1c --- /dev/null +++ b/test/prism/errors/optional_block_parameters_with_unary_operator.txt @@ -0,0 +1,3 @@ +foo { |a = +b| } + ^ unexpected '+'; unary calls are not allowed in this context + diff --git a/test/prism/errors/optional_block_parameters_with_unary_operator_2.txt b/test/prism/errors/optional_block_parameters_with_unary_operator_2.txt new file mode 100644 index 00000000000000..ba98f36f84e6d4 --- /dev/null +++ b/test/prism/errors/optional_block_parameters_with_unary_operator_2.txt @@ -0,0 +1,3 @@ +foo { |a = -b| } + ^ unexpected '-'; unary calls are not allowed in this context + diff --git a/test/prism/errors/optional_block_parameters_with_unary_operator_3.txt b/test/prism/errors/optional_block_parameters_with_unary_operator_3.txt new file mode 100644 index 00000000000000..97700598918534 --- /dev/null +++ b/test/prism/errors/optional_block_parameters_with_unary_operator_3.txt @@ -0,0 +1,3 @@ +foo { |a = !b| } + ^ unexpected '!'; unary calls are not allowed in this context + diff --git a/test/prism/errors/optional_block_parameters_with_unary_operator_4.txt b/test/prism/errors/optional_block_parameters_with_unary_operator_4.txt new file mode 100644 index 00000000000000..9bec68d7b3517f --- /dev/null +++ b/test/prism/errors/optional_block_parameters_with_unary_operator_4.txt @@ -0,0 +1,3 @@ +foo { |a = ~b| } + ^ unexpected '~'; unary calls are not allowed in this context + diff --git a/test/prism/errors/parameter_name_ending_with_bang_or_question_mark.txt b/test/prism/errors/parameter_name_ending_with_bang_or_question_mark.txt new file mode 100644 index 00000000000000..db4fd6928abca9 --- /dev/null +++ b/test/prism/errors/parameter_name_ending_with_bang_or_question_mark.txt @@ -0,0 +1,4 @@ +def foo(x!,y?); end + ^~ unexpected name for a parameter + ^~ unexpected name for a parameter + diff --git a/test/prism/errors/pre_execution_context.txt b/test/prism/errors/pre_execution_context.txt new file mode 100644 index 00000000000000..18b11bae97a9b8 --- /dev/null +++ b/test/prism/errors/pre_execution_context.txt @@ -0,0 +1,4 @@ +BEGIN { 1 + } + ^ unexpected '}'; expected an expression after the operator + ^ unexpected '}', assuming it is closing the parent 'BEGIN' block + diff --git a/test/prism/errors/pre_execution_missing_brace.txt b/test/prism/errors/pre_execution_missing_brace.txt new file mode 100644 index 00000000000000..e51cd0732e2b71 --- /dev/null +++ b/test/prism/errors/pre_execution_missing_brace.txt @@ -0,0 +1,3 @@ +BEGIN 1 } + ^ expected a `{` after `BEGIN` + diff --git a/test/prism/errors/range_and_bin_op.txt b/test/prism/errors/range_and_bin_op.txt new file mode 100644 index 00000000000000..4a7a396d0d649d --- /dev/null +++ b/test/prism/errors/range_and_bin_op.txt @@ -0,0 +1,4 @@ +1..2..3 + ^~ unexpected .., expecting end-of-input + ^~ unexpected .., ignoring it + diff --git a/test/prism/errors/range_and_bin_op_2.txt b/test/prism/errors/range_and_bin_op_2.txt new file mode 100644 index 00000000000000..f2a31dcf82cf6e --- /dev/null +++ b/test/prism/errors/range_and_bin_op_2.txt @@ -0,0 +1,4 @@ +1..2.. + ^~ unexpected .., expecting end-of-input + ^~ unexpected .., ignoring it + diff --git a/test/prism/errors/range_and_bin_op_3.txt b/test/prism/errors/range_and_bin_op_3.txt new file mode 100644 index 00000000000000..34390d0776a758 --- /dev/null +++ b/test/prism/errors/range_and_bin_op_3.txt @@ -0,0 +1,3 @@ +1.. || 2 + ^ unexpected '|'; expected an expression after the operator + diff --git a/test/prism/errors/range_and_bin_op_4.txt b/test/prism/errors/range_and_bin_op_4.txt new file mode 100644 index 00000000000000..56226480cfcfe0 --- /dev/null +++ b/test/prism/errors/range_and_bin_op_4.txt @@ -0,0 +1,4 @@ +1.. & 2 + ^ unexpected '&', expecting end-of-input + ^ unexpected '&', ignoring it + diff --git a/test/prism/errors/range_and_bin_op_5.txt b/test/prism/errors/range_and_bin_op_5.txt new file mode 100644 index 00000000000000..bc8b46791474b5 --- /dev/null +++ b/test/prism/errors/range_and_bin_op_5.txt @@ -0,0 +1,5 @@ +1.. * 2 + ^ unexpected *, expecting end-of-input + ^ unexpected write target + ^~~ unexpected write target + diff --git a/test/prism/errors/range_and_bin_op_6.txt b/test/prism/errors/range_and_bin_op_6.txt new file mode 100644 index 00000000000000..5cdd7a4f44d67d --- /dev/null +++ b/test/prism/errors/range_and_bin_op_6.txt @@ -0,0 +1,3 @@ +1.. / 2 + ^ unterminated regexp meets end of file; expected a closing delimiter + diff --git a/test/prism/errors/range_and_bin_op_7.txt b/test/prism/errors/range_and_bin_op_7.txt new file mode 100644 index 00000000000000..3f91b5e97ffe9f --- /dev/null +++ b/test/prism/errors/range_and_bin_op_7.txt @@ -0,0 +1,3 @@ +1.. % 2 + ^ unterminated string meets end of file + diff --git a/test/prism/errors/range_and_bin_op_8.txt b/test/prism/errors/range_and_bin_op_8.txt new file mode 100644 index 00000000000000..afbf3719d5502c --- /dev/null +++ b/test/prism/errors/range_and_bin_op_8.txt @@ -0,0 +1,4 @@ +1.. ** 2 + ^~ unexpected **, expecting end-of-input + ^~ unexpected **, ignoring it + diff --git a/test/prism/errors/rational_number_with_exponential_portion.txt b/test/prism/errors/rational_number_with_exponential_portion.txt new file mode 100644 index 00000000000000..01a03d538f8216 --- /dev/null +++ b/test/prism/errors/rational_number_with_exponential_portion.txt @@ -0,0 +1,4 @@ +1e1r; 1e1ri + ^ unexpected local variable or method, expecting end-of-input + ^~ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/regular_expression_with_unknown_regexp_options.txt b/test/prism/errors/regular_expression_with_unknown_regexp_options.txt new file mode 100644 index 00000000000000..c37291ca40f224 --- /dev/null +++ b/test/prism/errors/regular_expression_with_unknown_regexp_options.txt @@ -0,0 +1,3 @@ +/foo/AZaz + ^~~~~ unknown regexp options - AZaz + diff --git a/test/prism/errors/repeated_parameter_name_in_destructured_params.txt b/test/prism/errors/repeated_parameter_name_in_destructured_params.txt new file mode 100644 index 00000000000000..766c235325bc19 --- /dev/null +++ b/test/prism/errors/repeated_parameter_name_in_destructured_params.txt @@ -0,0 +1,3 @@ +def f(a, (b, (a))); end + ^ duplicated argument name + diff --git a/test/prism/errors/rest_keywords_parameters_before_required_parameters.txt b/test/prism/errors/rest_keywords_parameters_before_required_parameters.txt new file mode 100644 index 00000000000000..406f326712ee7c --- /dev/null +++ b/test/prism/errors/rest_keywords_parameters_before_required_parameters.txt @@ -0,0 +1,4 @@ +def foo(**rest, b:) + ^~ unexpected parameter order +end + diff --git a/test/prism/errors/return_1.txt b/test/prism/errors/return_1.txt new file mode 100644 index 00000000000000..b4ce8f1f2a66bd --- /dev/null +++ b/test/prism/errors/return_1.txt @@ -0,0 +1,3 @@ +return 1,; + ^ expected an argument + diff --git a/test/prism/errors/return_1_2_3.txt b/test/prism/errors/return_1_2_3.txt new file mode 100644 index 00000000000000..8f6dbaf194149d --- /dev/null +++ b/test/prism/errors/return_1_2_3.txt @@ -0,0 +1,7 @@ +return(1, 2, 3) + ^ unexpected ',', expecting end-of-input + ^ unexpected ',', ignoring it + ^ expected a matching `)` + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it + diff --git a/test/prism/errors/returning_to_optional_parameters_multiple_times.txt b/test/prism/errors/returning_to_optional_parameters_multiple_times.txt new file mode 100644 index 00000000000000..83ca73185094c0 --- /dev/null +++ b/test/prism/errors/returning_to_optional_parameters_multiple_times.txt @@ -0,0 +1,4 @@ +def foo(a, b = 1, c, d = 2, e) + ^ unexpected parameter order +end + diff --git a/test/prism/errors/semicolon_after_inheritance_operator.txt b/test/prism/errors/semicolon_after_inheritance_operator.txt new file mode 100644 index 00000000000000..6b67e6048ac675 --- /dev/null +++ b/test/prism/errors/semicolon_after_inheritance_operator.txt @@ -0,0 +1,3 @@ +class Foo < Bar end + ^ unexpected `end`, expecting ';' or '\n' + diff --git a/test/prism/errors/setter_method_cannot_be_defined_in_an_endless_method_definition.txt b/test/prism/errors/setter_method_cannot_be_defined_in_an_endless_method_definition.txt new file mode 100644 index 00000000000000..c4440ccc7ec10f --- /dev/null +++ b/test/prism/errors/setter_method_cannot_be_defined_in_an_endless_method_definition.txt @@ -0,0 +1,3 @@ +def a=() = 42 + ^~ invalid method name; a setter method cannot be defined in an endless method definition + diff --git a/test/prism/errors/shadow_args_in_block.txt b/test/prism/errors/shadow_args_in_block.txt new file mode 100644 index 00000000000000..1e7d5f9cd45353 --- /dev/null +++ b/test/prism/errors/shadow_args_in_block.txt @@ -0,0 +1,3 @@ +tap{|a;a|} + ^ duplicated argument name + diff --git a/test/prism/errors/shadow_args_in_lambda.txt b/test/prism/errors/shadow_args_in_lambda.txt new file mode 100644 index 00000000000000..2399a0ebd5443c --- /dev/null +++ b/test/prism/errors/shadow_args_in_lambda.txt @@ -0,0 +1,5 @@ +->a;b{} + ^ expected a `do` keyword or a `{` to open the lambda block + ^ unexpected end-of-input, assuming it is closing the parent top level context + ^ expected a lambda block beginning with `do` to end with `end` + diff --git a/test/prism/errors/singleton_method_for_literals.txt b/test/prism/errors/singleton_method_for_literals.txt new file mode 100644 index 00000000000000..6247b4f025523f --- /dev/null +++ b/test/prism/errors/singleton_method_for_literals.txt @@ -0,0 +1,39 @@ +def (1).g; end + ^ cannot define singleton method for literals +def ((a; 1)).foo; end + ^ cannot define singleton method for literals +def ((return; 1)).bar; end + ^ cannot define singleton method for literals +def (((1))).foo; end + ^ cannot define singleton method for literals +def (__FILE__).foo; end + ^~~~~~~~ cannot define singleton method for literals +def (__ENCODING__).foo; end + ^~~~~~~~~~~~ cannot define singleton method for literals +def (__LINE__).foo; end + ^~~~~~~~ cannot define singleton method for literals +def ("foo").foo; end + ^~~~~ cannot define singleton method for literals +def (3.14).foo; end + ^~~~ cannot define singleton method for literals +def (3.14i).foo; end + ^~~~~ cannot define singleton method for literals +def (:foo).foo; end + ^~~~ cannot define singleton method for literals +def (:'foo').foo; end + ^~~~~~ cannot define singleton method for literals +def (:'f{o}').foo; end + ^~~~~~~ cannot define singleton method for literals +def ('foo').foo; end + ^~~~~ cannot define singleton method for literals +def ("foo").foo; end + ^~~~~ cannot define singleton method for literals +def ("#{fo}o").foo; end + ^~~~~~~~ cannot define singleton method for literals +def (/foo/).foo; end + ^~~~~ cannot define singleton method for literals +def (/f#{oo}/).foo; end + ^~~~~~~~ cannot define singleton method for literals +def ([1]).foo; end + ^~~ cannot define singleton method for literals + diff --git a/test/prism/errors/splat_argument_after_keyword_argument.txt b/test/prism/errors/splat_argument_after_keyword_argument.txt new file mode 100644 index 00000000000000..fd2dbd0003e469 --- /dev/null +++ b/test/prism/errors/splat_argument_after_keyword_argument.txt @@ -0,0 +1,3 @@ +a(foo: bar, *args) + ^~~~~ unexpected `*` splat argument after a `**` keyword splat argument + diff --git a/test/prism/errors/statement_at_non_statement.txt b/test/prism/errors/statement_at_non_statement.txt new file mode 100644 index 00000000000000..40fe7d862e135c --- /dev/null +++ b/test/prism/errors/statement_at_non_statement.txt @@ -0,0 +1,9 @@ +foo(alias x y) + ^~~~~ unexpected an `alias` at a non-statement position +foo(BEGIN { bar }) + ^~~~~ unexpected a `BEGIN` at a non-statement position +foo(END { bar }) + ^~~ unexpected an `END` at a non-statement position +foo(undef x) + ^~~~~ unexpected an `undef` at a non-statement position + diff --git a/test/prism/errors/statement_operators.txt b/test/prism/errors/statement_operators.txt new file mode 100644 index 00000000000000..04b7c57477382f --- /dev/null +++ b/test/prism/errors/statement_operators.txt @@ -0,0 +1,25 @@ +alias x y + 1 + ^ unexpected '+', expecting end-of-input + ^ unexpected '+', ignoring it +alias x y.z + ^ unexpected '.', expecting end-of-input + ^ unexpected '.', ignoring it +BEGIN { bar } + 1 + ^ unexpected '+', expecting end-of-input + ^ unexpected '+', ignoring it +BEGIN { bar }.z + ^ unexpected '.', expecting end-of-input + ^ unexpected '.', ignoring it +END { bar } + 1 + ^ unexpected '+', expecting end-of-input + ^ unexpected '+', ignoring it +END { bar }.z + ^ unexpected '.', expecting end-of-input + ^ unexpected '.', ignoring it +undef x + 1 + ^ unexpected '+', expecting end-of-input + ^ unexpected '+', ignoring it +undef x.z + ^ unexpected '.', expecting end-of-input + ^ unexpected '.', ignoring it + diff --git a/test/prism/errors/switching_to_named_arguments_twice.txt b/test/prism/errors/switching_to_named_arguments_twice.txt new file mode 100644 index 00000000000000..5dae0a105ac466 --- /dev/null +++ b/test/prism/errors/switching_to_named_arguments_twice.txt @@ -0,0 +1,5 @@ +def foo(**args, a, b:) + ^ unexpected parameter order + ^~ unexpected parameter order +end + diff --git a/test/prism/errors/switching_to_optional_arguments_twice.txt b/test/prism/errors/switching_to_optional_arguments_twice.txt new file mode 100644 index 00000000000000..5dae0a105ac466 --- /dev/null +++ b/test/prism/errors/switching_to_optional_arguments_twice.txt @@ -0,0 +1,5 @@ +def foo(**args, a, b:) + ^ unexpected parameter order + ^~ unexpected parameter order +end + diff --git a/test/prism/errors/symbol_in_hash.txt b/test/prism/errors/symbol_in_hash.txt new file mode 100644 index 00000000000000..148040aa61070c --- /dev/null +++ b/test/prism/errors/symbol_in_hash.txt @@ -0,0 +1,3 @@ +{x:'y':} + ^~ unexpected label terminator, expected a string literal terminator + diff --git a/test/prism/errors/symbol_in_keyword_parameter.txt b/test/prism/errors/symbol_in_keyword_parameter.txt new file mode 100644 index 00000000000000..22d03cccb212d7 --- /dev/null +++ b/test/prism/errors/symbol_in_keyword_parameter.txt @@ -0,0 +1,3 @@ +def foo(x:'y':); end + ^~ unexpected label terminator, expected a string literal terminator + diff --git a/test/prism/errors/targeting_numbered_parameter.txt b/test/prism/errors/targeting_numbered_parameter.txt new file mode 100644 index 00000000000000..39c40ad7b96444 --- /dev/null +++ b/test/prism/errors/targeting_numbered_parameter.txt @@ -0,0 +1,3 @@ +-> { _1, = 0 } + ^~ _1 is reserved for numbered parameters + diff --git a/test/prism/errors/top_level_constant_starting_with_downcased_identifier.txt b/test/prism/errors/top_level_constant_starting_with_downcased_identifier.txt new file mode 100644 index 00000000000000..b7b54dd74e0c67 --- /dev/null +++ b/test/prism/errors/top_level_constant_starting_with_downcased_identifier.txt @@ -0,0 +1,4 @@ +::foo::A + ^ expected a constant after the `::` operator + ^~~ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/top_level_constant_with_downcased_identifier.txt b/test/prism/errors/top_level_constant_with_downcased_identifier.txt new file mode 100644 index 00000000000000..032bcfaebb7542 --- /dev/null +++ b/test/prism/errors/top_level_constant_with_downcased_identifier.txt @@ -0,0 +1,4 @@ +::foo + ^ expected a constant after the `::` operator + ^~~ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/trailing_comma_in_calls.txt b/test/prism/errors/trailing_comma_in_calls.txt new file mode 100644 index 00000000000000..44e8a1f0b3bf72 --- /dev/null +++ b/test/prism/errors/trailing_comma_in_calls.txt @@ -0,0 +1,3 @@ +foo 1, + ^ expected an argument + diff --git a/test/prism/errors/unexpected_block.txt b/test/prism/errors/unexpected_block.txt new file mode 100644 index 00000000000000..2c0741cd30665e --- /dev/null +++ b/test/prism/errors/unexpected_block.txt @@ -0,0 +1,3 @@ +def foo = yield(&:+) + ^~~ block argument should not be given + diff --git a/test/prism/errors/unterminated_W_list.txt b/test/prism/errors/unterminated_W_list.txt new file mode 100644 index 00000000000000..89cab68abb8377 --- /dev/null +++ b/test/prism/errors/unterminated_W_list.txt @@ -0,0 +1,3 @@ +%w[ +^~~ unterminated list; expected a closing delimiter for the `%w` + diff --git a/test/prism/errors/unterminated_argument_expression.txt b/test/prism/errors/unterminated_argument_expression.txt new file mode 100644 index 00000000000000..c250a94becbe83 --- /dev/null +++ b/test/prism/errors/unterminated_argument_expression.txt @@ -0,0 +1,5 @@ +a % + ^ unterminated quoted string meets end of file + ^ unexpected end-of-input; expected an expression after the operator + ^ unexpected end-of-input, assuming it is closing the parent top level context + diff --git a/test/prism/errors/unterminated_embdoc.txt b/test/prism/errors/unterminated_embdoc.txt new file mode 100644 index 00000000000000..1dd9ea3ac498eb --- /dev/null +++ b/test/prism/errors/unterminated_embdoc.txt @@ -0,0 +1,3 @@ +=begin +^~~~~~ embedded document meets end of file + diff --git a/test/prism/errors/unterminated_embdoc_2.txt b/test/prism/errors/unterminated_embdoc_2.txt new file mode 100644 index 00000000000000..1dd9ea3ac498eb --- /dev/null +++ b/test/prism/errors/unterminated_embdoc_2.txt @@ -0,0 +1,3 @@ +=begin +^~~~~~ embedded document meets end of file + diff --git a/test/prism/errors/unterminated_empty_string.txt b/test/prism/errors/unterminated_empty_string.txt new file mode 100644 index 00000000000000..597102f7ee74e2 --- /dev/null +++ b/test/prism/errors/unterminated_empty_string.txt @@ -0,0 +1,3 @@ +" + ^ unterminated string meets end of file + diff --git a/test/prism/errors/unterminated_global_variable.txt b/test/prism/errors/unterminated_global_variable.txt new file mode 100644 index 00000000000000..ce3e960b2e2b40 --- /dev/null +++ b/test/prism/errors/unterminated_global_variable.txt @@ -0,0 +1,3 @@ +$ +^ '$' without identifiers is not allowed as a global variable name + diff --git a/test/prism/errors/unterminated_global_variable_2.txt b/test/prism/errors/unterminated_global_variable_2.txt new file mode 100644 index 00000000000000..302293b538d3ad --- /dev/null +++ b/test/prism/errors/unterminated_global_variable_2.txt @@ -0,0 +1,3 @@ +$ +^ '$' without identifiers is not allowed as a global variable name + diff --git a/test/prism/errors/unterminated_i_list.txt b/test/prism/errors/unterminated_i_list.txt new file mode 100644 index 00000000000000..c48be9971d5c8e --- /dev/null +++ b/test/prism/errors/unterminated_i_list.txt @@ -0,0 +1,3 @@ +%i[ +^~~ unterminated list; expected a closing delimiter for the `%i` + diff --git a/test/prism/errors/unterminated_interpolated_string.txt b/test/prism/errors/unterminated_interpolated_string.txt new file mode 100644 index 00000000000000..e74a4c9e2048ca --- /dev/null +++ b/test/prism/errors/unterminated_interpolated_string.txt @@ -0,0 +1,3 @@ +"hello + ^ unterminated string meets end of file + diff --git a/test/prism/errors/unterminated_interpolated_symbol.txt b/test/prism/errors/unterminated_interpolated_symbol.txt new file mode 100644 index 00000000000000..faa75972809fc7 --- /dev/null +++ b/test/prism/errors/unterminated_interpolated_symbol.txt @@ -0,0 +1,3 @@ +:"# + ^ unterminated symbol; expected a closing delimiter for the interpolated symbol + diff --git a/test/prism/errors/unterminated_parenthesized_expression.txt b/test/prism/errors/unterminated_parenthesized_expression.txt new file mode 100644 index 00000000000000..9025eec453de92 --- /dev/null +++ b/test/prism/errors/unterminated_parenthesized_expression.txt @@ -0,0 +1,4 @@ +(1 + 2 + ^ unexpected end-of-input, assuming it is closing the parent top level context + ^ expected a matching `)` + diff --git a/test/prism/errors/unterminated_regular_expression.txt b/test/prism/errors/unterminated_regular_expression.txt new file mode 100644 index 00000000000000..48f3a3081081ae --- /dev/null +++ b/test/prism/errors/unterminated_regular_expression.txt @@ -0,0 +1,3 @@ +/hello +^ unterminated regexp meets end of file; expected a closing delimiter + diff --git a/test/prism/errors/unterminated_regular_expression_with_heredoc.txt b/test/prism/errors/unterminated_regular_expression_with_heredoc.txt new file mode 100644 index 00000000000000..d4688d6c9e660b --- /dev/null +++ b/test/prism/errors/unterminated_regular_expression_with_heredoc.txt @@ -0,0 +1,4 @@ +<<-END + /b + ^ unterminated regexp meets end of file; expected a closing delimiter +END + diff --git a/test/prism/errors/unterminated_s_symbol.txt b/test/prism/errors/unterminated_s_symbol.txt new file mode 100644 index 00000000000000..0f4be932b3bb12 --- /dev/null +++ b/test/prism/errors/unterminated_s_symbol.txt @@ -0,0 +1,3 @@ +%s[abc +^~~ unterminated quoted string; expected a closing delimiter for the dynamic symbol + diff --git a/test/prism/errors/unterminated_string.txt b/test/prism/errors/unterminated_string.txt new file mode 100644 index 00000000000000..89c0a08b3e8fb4 --- /dev/null +++ b/test/prism/errors/unterminated_string.txt @@ -0,0 +1,3 @@ +'hello +^ unterminated string meets end of file + diff --git a/test/prism/errors/unterminated_unicode_brackets_should_be_a_syntax_error.txt b/test/prism/errors/unterminated_unicode_brackets_should_be_a_syntax_error.txt new file mode 100644 index 00000000000000..1a65c6149aca3e --- /dev/null +++ b/test/prism/errors/unterminated_unicode_brackets_should_be_a_syntax_error.txt @@ -0,0 +1,3 @@ +?\u{3 + ^~~~ unterminated Unicode escape + diff --git a/test/prism/errors/unterminated_xstring.txt b/test/prism/errors/unterminated_xstring.txt new file mode 100644 index 00000000000000..ccd529774c3967 --- /dev/null +++ b/test/prism/errors/unterminated_xstring.txt @@ -0,0 +1,3 @@ +`hello +^ expected a closing delimiter for the `%x` or backtick string + diff --git a/test/prism/errors/void_value_expression_in_arguments.txt b/test/prism/errors/void_value_expression_in_arguments.txt new file mode 100644 index 00000000000000..f57aee14543131 --- /dev/null +++ b/test/prism/errors/void_value_expression_in_arguments.txt @@ -0,0 +1,17 @@ +foo(return) + ^~~~~~ unexpected void value expression +foo(1, return) + ^~~~~~ unexpected void value expression +foo(*return) + ^~~~~~ unexpected void value expression +foo(**return) + ^~~~~~ unexpected void value expression +foo(&return) + ^~~~~~ unexpected void value expression +foo(return => 1) + ^~~~~~ unexpected void value expression +foo(:a => return) + ^~~~~~ unexpected void value expression +foo(a: return) + ^~~~~~ unexpected void value expression + diff --git a/test/prism/errors/void_value_expression_in_array.txt b/test/prism/errors/void_value_expression_in_array.txt new file mode 100644 index 00000000000000..a0e86fb13569d3 --- /dev/null +++ b/test/prism/errors/void_value_expression_in_array.txt @@ -0,0 +1,15 @@ +[return] + ^~~~~~ unexpected void value expression +[1, return] + ^~~~~~ unexpected void value expression +[ return => 1 ] + ^~~~~~ unexpected void value expression +[ 1 => return ] + ^~~~~~ unexpected void value expression +[ a: return ] + ^~~~~~ unexpected void value expression +[ *return ] + ^~~~~~ unexpected void value expression +[ **return ] + ^~~~~~ unexpected void value expression + diff --git a/test/prism/errors/void_value_expression_in_assignment.txt b/test/prism/errors/void_value_expression_in_assignment.txt new file mode 100644 index 00000000000000..c651d7f39ee62a --- /dev/null +++ b/test/prism/errors/void_value_expression_in_assignment.txt @@ -0,0 +1,9 @@ +a = return + ^~~~~~ unexpected void value expression +a = 1, return + ^~~~~~ unexpected void value expression +a, b = return, 1 + ^~~~~~ unexpected void value expression +a, b = 1, *return + ^~~~~~ unexpected void value expression + diff --git a/test/prism/errors/void_value_expression_in_begin_statement.txt b/test/prism/errors/void_value_expression_in_begin_statement.txt new file mode 100644 index 00000000000000..4ad40b684dc22f --- /dev/null +++ b/test/prism/errors/void_value_expression_in_begin_statement.txt @@ -0,0 +1,21 @@ +x = return 1 + ^~~~~~~~ unexpected void value expression +x = return, 1 + ^~~~~~ unexpected void value expression +x = 1, return + ^~~~~~ unexpected void value expression +x, y = return + ^~~~~~ unexpected void value expression +x = begin return ensure end + ^~~~~~ unexpected void value expression +x = begin ensure return end + ^~~~~~ unexpected void value expression +x = begin return ensure return end + ^~~~~~ unexpected void value expression +x = begin return; rescue; return end + ^~~~~~ unexpected void value expression +x = begin return; rescue; return; else return end + ^~~~~~ unexpected void value expression +x = begin; return; rescue; retry; end + ^~~~~~ unexpected void value expression + diff --git a/test/prism/errors/void_value_expression_in_binary_call.txt b/test/prism/errors/void_value_expression_in_binary_call.txt new file mode 100644 index 00000000000000..096b42be4deae2 --- /dev/null +++ b/test/prism/errors/void_value_expression_in_binary_call.txt @@ -0,0 +1,11 @@ +1 + (return) + ^~~~~~ unexpected void value expression +(return) + 1 + ^~~~~~ unexpected void value expression +1 and (return) +(return) and 1 + ^~~~~~ unexpected void value expression +1 or (return) +(return) or 1 + ^~~~~~ unexpected void value expression + diff --git a/test/prism/errors/void_value_expression_in_call.txt b/test/prism/errors/void_value_expression_in_call.txt new file mode 100644 index 00000000000000..90e6481c4ceba7 --- /dev/null +++ b/test/prism/errors/void_value_expression_in_call.txt @@ -0,0 +1,11 @@ +(return).foo + ^~~~~~ unexpected void value expression +(return).(1) + ^~~~~~ unexpected void value expression +(return)[1] + ^~~~~~ unexpected void value expression +(return)[1] = 2 + ^~~~~~ unexpected void value expression +(return)::foo + ^~~~~~ unexpected void value expression + diff --git a/test/prism/errors/void_value_expression_in_constant_path.txt b/test/prism/errors/void_value_expression_in_constant_path.txt new file mode 100644 index 00000000000000..1dab6902a2785e --- /dev/null +++ b/test/prism/errors/void_value_expression_in_constant_path.txt @@ -0,0 +1,5 @@ +(return)::A + ^~~~~~ unexpected void value expression +class (return)::A; end + ^~~~~~ unexpected void value expression + diff --git a/test/prism/errors/void_value_expression_in_def.txt b/test/prism/errors/void_value_expression_in_def.txt new file mode 100644 index 00000000000000..fed52a667762e1 --- /dev/null +++ b/test/prism/errors/void_value_expression_in_def.txt @@ -0,0 +1,10 @@ +def (return).x + ^~~~~~ unexpected void value expression +end +def x(a = return) + ^~~~~~ unexpected void value expression +end +def x(a: return) + ^~~~~~ unexpected void value expression +end + diff --git a/test/prism/errors/void_value_expression_in_expression.txt b/test/prism/errors/void_value_expression_in_expression.txt new file mode 100644 index 00000000000000..f6165a7ba63fd8 --- /dev/null +++ b/test/prism/errors/void_value_expression_in_expression.txt @@ -0,0 +1,19 @@ +(return) ? 1 : 1 + ^~~~~~ unexpected void value expression +(return)..1 + ^~~~~~ unexpected void value expression +1..(return) + ^~~~~~ unexpected void value expression +(return)...1 + ^~~~~~ unexpected void value expression +1...(return) + ^~~~~~ unexpected void value expression +(..(return)) + ^~~~~~ unexpected void value expression +(...(return)) + ^~~~~~ unexpected void value expression +((return)..) + ^~~~~~ unexpected void value expression +((return)...) + ^~~~~~ unexpected void value expression + diff --git a/test/prism/errors/void_value_expression_in_hash.txt b/test/prism/errors/void_value_expression_in_hash.txt new file mode 100644 index 00000000000000..7795511443792e --- /dev/null +++ b/test/prism/errors/void_value_expression_in_hash.txt @@ -0,0 +1,9 @@ +{ return => 1 } + ^~~~~~ unexpected void value expression +{ 1 => return } + ^~~~~~ unexpected void value expression +{ a: return } + ^~~~~~ unexpected void value expression +{ **return } + ^~~~~~ unexpected void value expression + diff --git a/test/prism/errors/void_value_expression_in_modifier.txt b/test/prism/errors/void_value_expression_in_modifier.txt new file mode 100644 index 00000000000000..7d7b444e331d9f --- /dev/null +++ b/test/prism/errors/void_value_expression_in_modifier.txt @@ -0,0 +1,13 @@ +1 if (return) + ^~~~~~ unexpected void value expression +1 unless (return) + ^~~~~~ unexpected void value expression +1 while (return) + ^~~~~~ unexpected void value expression +1 until (return) + ^~~~~~ unexpected void value expression +(return) => a + ^~~~~~ unexpected void value expression +(return) in a + ^~~~~~ unexpected void value expression + diff --git a/test/prism/errors/void_value_expression_in_statement.txt b/test/prism/errors/void_value_expression_in_statement.txt new file mode 100644 index 00000000000000..87dbfa5cc92da5 --- /dev/null +++ b/test/prism/errors/void_value_expression_in_statement.txt @@ -0,0 +1,26 @@ +if (return) + ^~~~~~ unexpected void value expression +end +unless (return) + ^~~~~~ unexpected void value expression +end +while (return) + ^~~~~~ unexpected void value expression +end +until (return) + ^~~~~~ unexpected void value expression +end +case (return) + ^~~~~~ unexpected void value expression +when 1 +end +class A < (return) + ^~~~~~ unexpected void value expression +end +class << (return) + ^~~~~~ unexpected void value expression +end +for x in (return) + ^~~~~~ unexpected void value expression +end + diff --git a/test/prism/errors/void_value_expression_in_unary_call.txt b/test/prism/errors/void_value_expression_in_unary_call.txt new file mode 100644 index 00000000000000..61e849255c7616 --- /dev/null +++ b/test/prism/errors/void_value_expression_in_unary_call.txt @@ -0,0 +1,5 @@ ++(return) + ^~~~~~ unexpected void value expression +not return + ^~~~~~ unexpected void value expression + diff --git a/test/prism/errors/while_endless_method.txt b/test/prism/errors/while_endless_method.txt new file mode 100644 index 00000000000000..6f062d89d0f850 --- /dev/null +++ b/test/prism/errors/while_endless_method.txt @@ -0,0 +1,5 @@ +while def f = g do end + ^ expected a predicate expression for the `while` statement + ^ unexpected end-of-input, assuming it is closing the parent top level context + ^ expected an `end` to close the `while` statement + diff --git a/test/prism/errors/writing_numbered_parameter.txt b/test/prism/errors/writing_numbered_parameter.txt new file mode 100644 index 00000000000000..17dcc6e8f03a27 --- /dev/null +++ b/test/prism/errors/writing_numbered_parameter.txt @@ -0,0 +1,3 @@ +-> { _1 = 0 } + ^~ _1 is reserved for numbered parameters + diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb index 4e900e37f4fdc9..9a108ceca7cb10 100644 --- a/test/prism/errors_test.rb +++ b/test/prism/errors_test.rb @@ -4,1985 +4,69 @@ module Prism class ErrorsTest < TestCase - include DSL + base = File.expand_path("errors", __dir__) + filepaths = Dir["*.txt", base: base] - def test_constant_path_with_invalid_token_after - assert_error_messages "A::$b", [ - "expected a constant after the `::` operator", - "unexpected global variable, expecting end-of-input" + if RUBY_VERSION < "3.0" + filepaths -= [ + "cannot_assign_to_a_reserved_numbered_parameter.txt", + "writing_numbered_parameter.txt", + "targeting_numbered_parameter.txt", + "defining_numbered_parameter.txt", + "defining_numbered_parameter_2.txt", + "numbered_parameters_in_block_arguments.txt" ] end - def test_module_name_recoverable - expected = ModuleNode( - [], - Location(), - ConstantReadNode(:Parent), - StatementsNode( - [ModuleNode([], Location(), MissingNode(), nil, Location(), :"")] - ), - Location(), - :Parent - ) - - assert_errors expected, "module Parent module end", [ - ["unexpected constant path after `module`; class/module name must be CONSTANT", 14..20], - ["unexpected 'end', assuming it is closing the parent module definition", 21..24] - ] - end - - def test_for_loops_index_missing - expected = ForNode( - MissingNode(), - expression("1..10"), - StatementsNode([expression("i")]), - Location(), - Location(), - nil, - Location() - ) - - assert_errors expected, "for in 1..10\ni\nend", [ - ["expected an index after `for`", 0..3] - ] - end - - def test_for_loops_only_end - expected = ForNode( - MissingNode(), - MissingNode(), - nil, - Location(), - Location(), - nil, - Location() - ) - - assert_errors expected, "for end", [ - ["expected an index after `for`", 0..3], - ["expected an `in` after the index in a `for` statement", 3..3], - ["expected a collection after the `in` in a `for` statement", 3..3] - ] - end - - def test_pre_execution_missing_brace - expected = PreExecutionNode( - StatementsNode([expression("1")]), - Location(), - Location(), - Location() - ) - - assert_errors expected, "BEGIN 1 }", [ - ["expected a `{` after `BEGIN`", 5..5] - ] - end - - def test_pre_execution_context - expected = PreExecutionNode( - StatementsNode([ - CallNode( - 0, - expression("1"), - nil, - :+, - Location(), - nil, - ArgumentsNode(0, [MissingNode()]), - nil, - nil - ) - ]), - Location(), - Location(), - Location() - ) - - assert_errors expected, "BEGIN { 1 + }", [ - ["unexpected '}'; expected an expression after the operator", 12..13], - ["unexpected '}', assuming it is closing the parent 'BEGIN' block", 12..13] - ] - end - - def test_unterminated_embdoc - message = "embedded document meets end of file" - assert_error_messages "=begin", [message] - assert_error_messages "=begin\n", [message] - - refute_error_messages "=begin\n=end" - refute_error_messages "=begin\n=end\0" - refute_error_messages "=begin\n=end\C-d" - refute_error_messages "=begin\n=end\C-z" - end - - def test_unterminated_i_list - assert_errors expression("%i["), "%i[", [ - ["unterminated list; expected a closing delimiter for the `%i`", 0..3] - ] - end - - def test_unterminated_w_list - assert_errors expression("%w["), "%w[", [ - ["unterminated list; expected a closing delimiter for the `%w`", 0..3] - ] - end - - def test_unterminated_W_list - assert_errors expression("%W["), "%W[", [ - ["unterminated list; expected a closing delimiter for the `%W`", 0..3] - ] - end - - def test_unterminated_regular_expression - assert_errors expression("/hello"), "/hello", [ - ["unterminated regexp meets end of file; expected a closing delimiter", 0..1] - ] - end - - def test_unterminated_regular_expression_with_heredoc - source = "<<-END + /b\nEND\n" - - assert_errors expression(source), source, [ - ["unterminated regexp meets end of file; expected a closing delimiter", 9..10] - ] - end - - def test_unterminated_xstring - assert_errors expression("`hello"), "`hello", [ - ["expected a closing delimiter for the `%x` or backtick string", 0..1] - ] - end - - def test_unterminated_interpolated_string - expr = expression('"hello') - assert_errors expr, '"hello', [ - ["unterminated string meets end of file", 6..6] - ] - assert_equal expr.unescaped, "hello" - assert_equal expr.closing, "" - end - - def test_unterminated_string - expr = expression("'hello") - assert_errors expr, "'hello", [ - ["unterminated string meets end of file", 0..1] - ] - assert_equal expr.unescaped, "hello" - assert_equal expr.closing, "" - end - - def test_unterminated_empty_string - expr = expression('"') - assert_errors expr, '"', [ - ["unterminated string meets end of file", 1..1] - ] - assert_equal expr.unescaped, "" - assert_equal expr.closing, "" - end - - def test_incomplete_instance_var_string - assert_errors expression('%@#@@#'), '%@#@@#', [ - ["'@' without identifiers is not allowed as an instance variable name", 4..5], - ["unexpected instance variable, expecting end-of-input", 4..5] - ] - end - - def test_unterminated_s_symbol - assert_errors expression("%s[abc"), "%s[abc", [ - ["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-input, assuming it is closing the parent top level context", 6..6], - ["expected a matching `)`", 6..6] - ] - end - - def test_missing_terminator_in_parentheses - assert_error_messages "(0 0)", [ - "unexpected integer, expecting end-of-input" - ] - end - - def test_unterminated_argument_expression - assert_errors expression('a %'), 'a %', [ - ["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 ":\"#", [ - "unterminated symbol; expected a closing delimiter for the interpolated symbol" - ] - end - - def test_cr_without_lf_in_percent_expression - assert_errors expression("%\r"), "%\r", [ - ["unterminated string meets end of file", 2..2], - ] - end - - def test_1_2_3 - assert_errors expression("(1, 2, 3)"), "(1, 2, 3)", [ - ["unexpected ',', expecting end-of-input", 2..3], - ["unexpected ',', ignoring it", 2..3], - ["expected a matching `)`", 2..2], - ["unexpected ',', expecting end-of-input", 2..3], - ["unexpected ',', ignoring it", 2..3], - ["unexpected ',', expecting end-of-input", 5..6], - ["unexpected ',', ignoring it", 5..6], - ["unexpected ')', expecting end-of-input", 8..9], - ["unexpected ')', ignoring it", 8..9] - ] - end - - def test_return_1_2_3 - assert_error_messages "return(1, 2, 3)", [ - "unexpected ',', expecting end-of-input", - "unexpected ',', ignoring it", - "expected a matching `)`", - "unexpected ')', expecting end-of-input", - "unexpected ')', ignoring it" - ] - end - - def test_return_1 - assert_errors expression("return 1,;"), "return 1,;", [ - ["expected an argument", 8..9] - ] - end - - def test_next_1_2_3 - assert_errors expression("next(1, 2, 3)"), "next(1, 2, 3)", [ - ["unexpected ',', expecting end-of-input", 6..7], - ["unexpected ',', ignoring it", 6..7], - ["expected a matching `)`", 6..6], - ["unexpected ')', expecting end-of-input", 12..13], - ["unexpected ')', ignoring it", 12..13], - ["Invalid next", 0..12] - ] - end - - def test_next_1 - assert_errors expression("next 1,;"), "next 1,;", [ - ["expected an argument", 6..7], - ["Invalid next", 0..7] - ] - end - - def test_break_1_2_3 - assert_errors expression("break(1, 2, 3)"), "break(1, 2, 3)", [ - ["unexpected ',', expecting end-of-input", 7..8], - ["unexpected ',', ignoring it", 7..8], - ["expected a matching `)`", 7..7], - ["unexpected ')', expecting end-of-input", 13..14], - ["unexpected ')', ignoring it", 13..14], - ["Invalid break", 0..13] - ] - end - - def test_break_1 - assert_errors expression("break 1,;"), "break 1,;", [ - ["expected an argument", 7..8], - ["Invalid break", 0..8] - ] - end - - def test_argument_forwarding_when_parent_is_not_forwarding - assert_errors expression('def a(x, y, z); b(...); end'), 'def a(x, y, z); b(...); end', [ - ["unexpected ... when the parent method is not forwarding", 18..21] - ] - end - - def test_argument_forwarding_only_effects_its_own_internals - assert_errors expression('def a(...); b(...); end; def c(x, y, z); b(...); end'), - 'def a(...); b(...); end; def c(x, y, z); b(...); end', [ - ["unexpected ... when the parent method is not forwarding", 43..46] - ] - end - - def test_top_level_constant_with_downcased_identifier - assert_error_messages "::foo", [ - "expected a constant after the `::` operator", - "unexpected local variable or method, expecting end-of-input" - ] - end - - def test_top_level_constant_starting_with_downcased_identifier - assert_error_messages "::foo::A", [ - "expected a constant after the `::` operator", - "unexpected local variable or method, expecting end-of-input" - ] - end - - def test_aliasing_global_variable_with_non_global_variable - assert_errors expression("alias $a b"), "alias $a b", [ - ["invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable", 9..10] - ] - end - - def test_aliasing_non_global_variable_with_global_variable - assert_errors expression("alias a $b"), "alias a $b", [ - ["invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable", 8..10] - ] - end - - def test_aliasing_global_variable_with_global_number_variable - assert_errors expression("alias $a $1"), "alias $a $1", [ - ["invalid argument being passed to `alias`; can't make alias for the number variables", 9..11] - ] - end - - def test_def_with_expression_receiver_and_no_identifier - assert_errors expression("def (a); end"), "def (a); end", [ - ["expected a `.` or `::` after the receiver in a method definition", 7..7], - ["unexpected ';'; expected a method name", 7..8] - ] - end - - def test_def_with_multiple_statements_receiver - assert_errors expression("def (\na\nb\n).c; end"), "def (\na\nb\n).c; end", [ - ["expected a matching `)`", 8..8], - ["expected a `.` or `::` after the receiver in a method definition", 8..8], - ["expected a delimiter to close the parameters", 9..9], - ["unexpected ')', ignoring it", 10..11], - ["unexpected '.', ignoring it", 11..12] - ] - end - - def test_def_with_empty_expression_receiver - assert_errors expression("def ().a; end"), "def ().a; end", [ - ["expected a receiver for the method definition", 4..5] - ] - end - - def test_block_beginning_with_brace_and_ending_with_end - assert_error_messages "x.each { x end", [ - "unexpected 'end', expecting end-of-input", - "unexpected 'end', ignoring it", - "unexpected end-of-input, assuming it is closing the parent top level context", - "expected a block beginning with `{` to end with `}`" - ] - end - - def test_double_splat_followed_by_splat_argument - expected = CallNode( - CallNodeFlags::IGNORE_VISIBILITY, - nil, - nil, - :a, - Location(), - Location(), - ArgumentsNode( - ArgumentsNodeFlags::CONTAINS_KEYWORDS | ArgumentsNodeFlags::CONTAINS_KEYWORD_SPLAT, - [ - KeywordHashNode(0, [AssocSplatNode(expression("kwargs"), Location())]), - SplatNode(Location(), expression("args")) - ] - ), - Location(), - nil - ) - - assert_errors expected, "a(**kwargs, *args)", [ - ["unexpected `*` splat argument after a `**` keyword splat argument", 12..17] - ] - end - - def test_arguments_after_block - expected = CallNode( - CallNodeFlags::IGNORE_VISIBILITY, - nil, - nil, - :a, - Location(), - Location(), - ArgumentsNode(0, [expression("foo")]), - Location(), - BlockArgumentNode(expression("block"), Location()) - ) - - assert_errors expected, "a(&block, foo)", [ - ["unexpected argument after a block argument", 10..13] - ] - end - - def test_arguments_binding_power_for_and - assert_error_messages "foo(*bar and baz)", [ - "unexpected 'and'; expected a `)` to close the arguments", - "unexpected ')', expecting end-of-input", - "unexpected ')', ignoring it" - ] - end - - def test_splat_argument_after_keyword_argument - expected = CallNode( - CallNodeFlags::IGNORE_VISIBILITY, - nil, - nil, - :a, - Location(), - Location(), - ArgumentsNode(ArgumentsNodeFlags::CONTAINS_KEYWORDS, [ - KeywordHashNode(1, [ - AssocNode( - SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, nil, Location(), Location(), "foo"), - expression("bar"), - nil - ) - ]), - SplatNode(Location(), expression("args")) - ]), - Location(), - nil - ) - - assert_errors expected, "a(foo: bar, *args)", [ - ["unexpected `*` splat argument after a `**` keyword splat argument", 12..17] - ] - end - - def test_module_definition_in_method_body - expected = DefNode( - :foo, - Location(), - nil, - nil, - StatementsNode([ModuleNode([], Location(), ConstantReadNode(:A), nil, Location(), :A)]), - [], - Location(), - nil, - nil, - nil, - nil, - Location() - ) - - assert_errors expected, "def foo;module A;end;end", [ - ["unexpected module definition in method body", 8..14] - ] - end - - def test_module_definition_in_method_body_within_block - expected = DefNode( - :foo, - Location(), - nil, - nil, - StatementsNode( - [CallNode( - CallNodeFlags::IGNORE_VISIBILITY, - nil, - nil, - :bar, - Location(), - nil, - nil, - nil, - BlockNode( - [], - nil, - StatementsNode([ModuleNode([], Location(), ConstantReadNode(:Foo), nil, Location(), :Foo)]), - Location(), - Location() - ) - )] - ), - [], - Location(), - nil, - nil, - nil, - nil, - Location() - ) - - assert_errors expected, <<~RUBY, [["unexpected module definition in method body", 21..27]] - def foo - bar do - module Foo;end - end - end - RUBY - end - - def test_module_definition_in_method_defs - source = <<~RUBY - def foo(bar = module A;end);end - def foo;rescue;module A;end;end - def foo;ensure;module A;end;end - RUBY - message = "unexpected module definition in method body" - assert_errors expression(source), source, [ - [message, 14..20], - [message, 47..53], - [message, 79..85], - ] - end - - def test_class_definition_in_method_body - expected = DefNode( - :foo, - Location(), - nil, - nil, - StatementsNode( - [ClassNode( - [], - Location(), - ConstantReadNode(:A), - nil, - nil, - nil, - Location(), - :A - )] - ), - [], - Location(), - nil, - nil, - nil, - nil, - Location() - ) - - assert_errors expected, "def foo;class A;end;end", [ - ["unexpected class definition in method body", 8..13] - ] - end - - def test_class_definition_in_method_defs - source = <<~RUBY - def foo(bar = class A;end);end - def foo;rescue;class A;end;end - def foo;ensure;class A;end;end - RUBY - message = "unexpected class definition in method body" - assert_errors expression(source), source, [ - [message, 14..19], - [message, 46..51], - [message, 77..82], - ] - end - - def test_bad_arguments - expected = DefNode( - :foo, - Location(), - nil, - ParametersNode([ - RequiredParameterNode(0, :A), - RequiredParameterNode(0, :@a), - RequiredParameterNode(0, :$A), - RequiredParameterNode(0, :@@a), - ], [], nil, [], [], nil, nil), - nil, - [:A, :@a, :$A, :@@a], - Location(), - nil, - Location(), - Location(), - nil, - Location() - ) - - assert_errors expected, "def foo(A, @a, $A, @@a);end", [ - ["invalid formal argument; formal argument cannot be a constant", 8..9], - ["invalid formal argument; formal argument cannot be an instance variable", 11..13], - ["invalid formal argument; formal argument cannot be a global variable", 15..17], - ["invalid formal argument; formal argument cannot be a class variable", 19..22], - ] - end - - if RUBY_VERSION >= "3.0" - def test_cannot_assign_to_a_reserved_numbered_parameter - expected = BeginNode( - Location(), - StatementsNode([ - LocalVariableWriteNode(:_1, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()), - LocalVariableWriteNode(:_2, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()), - LocalVariableWriteNode(:_3, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()), - LocalVariableWriteNode(:_4, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()), - LocalVariableWriteNode(:_5, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()), - LocalVariableWriteNode(:_6, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()), - LocalVariableWriteNode(:_7, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()), - LocalVariableWriteNode(:_8, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()), - LocalVariableWriteNode(:_9, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()), - LocalVariableWriteNode(:_10, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()) - ]), - nil, - nil, - nil, - Location() - ) - source = <<~RUBY - begin - _1=:a;_2=:a;_3=:a;_4=:a;_5=:a - _6=:a;_7=:a;_8=:a;_9=:a;_10=:a - end - RUBY - assert_errors expected, source, [ - ["_1 is reserved for numbered parameters", 8..10], - ["_2 is reserved for numbered parameters", 14..16], - ["_3 is reserved for numbered parameters", 20..22], - ["_4 is reserved for numbered parameters", 26..28], - ["_5 is reserved for numbered parameters", 32..34], - ["_6 is reserved for numbered parameters", 40..42], - ["_7 is reserved for numbered parameters", 46..48], - ["_8 is reserved for numbered parameters", 52..54], - ["_9 is reserved for numbered parameters", 58..60], - ] - end - end - - def test_do_not_allow_trailing_commas_in_method_parameters - expected = DefNode( - :foo, - Location(), - nil, - ParametersNode( - [RequiredParameterNode(0, :a), RequiredParameterNode(0, :b), RequiredParameterNode(0, :c)], - [], - nil, - [], - [], - nil, - nil - ), - nil, - [:a, :b, :c], - Location(), - nil, - Location(), - Location(), - nil, - Location() - ) - - assert_errors expected, "def foo(a,b,c,);end", [ - ["unexpected `,` in parameters", 13..14] - ] - end - - def test_do_not_allow_trailing_commas_in_lambda_parameters - expected = LambdaNode( - [:a, :b], - Location(), - Location(), - Location(), - BlockParametersNode( - ParametersNode([RequiredParameterNode(0, :a), RequiredParameterNode(0, :b)], [], nil, [], [], nil, nil), - [], - Location(), - Location() - ), - nil - ) - assert_errors expected, "-> (a, b, ) {}", [ - ["unexpected `,` in parameters", 8..9] - ] - end - - def test_do_not_allow_multiple_codepoints_in_a_single_character_literal - expected = StringNode(StringFlags::FORCED_UTF8_ENCODING, Location(), Location(), nil, "\u0001\u0002") - - assert_errors expected, '?\u{0001 0002}', [ - ["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 hex escape sequence", 1..3], - ] - end - - def test_do_not_allow_more_than_6_hexadecimal_digits_in_u_Unicode_character_notation - expected = StringNode(0, Location(), Location(), Location(), "\u0001") - - assert_errors expected, '"\u{0000001}"', [ - ["invalid Unicode escape sequence; maximum length is 6 digits", 4..11], - ] - end - - def test_do_not_allow_characters_other_than_0_9_a_f_and_A_F_in_u_Unicode_character_notation - expected = StringNode(0, Location(), Location(), Location(), "\u0000z}") - - 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', [ - ["unterminated Unicode escape", 1..5], - ] - end - - def test_method_parameters_after_block - expected = DefNode( - :foo, - Location(), - nil, - ParametersNode( - [], - [], - nil, - [RequiredParameterNode(0, :a)], - [], - nil, - BlockParameterNode(0, :block, Location(), Location()) - ), - nil, - [:block, :a], - Location(), - nil, - Location(), - Location(), - nil, - Location() - ) - assert_errors expected, "def foo(&block, a)\nend", [ - ["unexpected parameter order", 16..17] - ] - end - - def test_method_with_arguments_after_anonymous_block - expected = DefNode( - :foo, - Location(), - nil, - ParametersNode([], [], nil, [RequiredParameterNode(0, :a)], [], nil, BlockParameterNode(0, nil, nil, Location())), - nil, - [:a], - Location(), - nil, - Location(), - Location(), - nil, - Location() - ) - - assert_errors expected, "def foo(&, a)\nend", [ - ["unexpected parameter order", 11..12] - ] - end - - def test_method_parameters_after_arguments_forwarding - expected = DefNode( - :foo, - Location(), - nil, - ParametersNode( - [], - [], - nil, - [RequiredParameterNode(0, :a)], - [], - ForwardingParameterNode(), - nil - ), - nil, - [:a], - Location(), - nil, - Location(), - Location(), - nil, - Location() - ) - assert_errors expected, "def foo(..., a)\nend", [ - ["unexpected parameter order", 13..14] - ] - end - - def test_keywords_parameters_before_required_parameters - expected = DefNode( - :foo, - Location(), - nil, - ParametersNode( - [], - [], - nil, - [RequiredParameterNode(0, :a)], - [RequiredKeywordParameterNode(0, :b, Location())], - nil, - nil - ), - nil, - [:b, :a], - Location(), - nil, - Location(), - Location(), - nil, - Location() - ) - assert_errors expected, "def foo(b:, a)\nend", [ - ["unexpected parameter order", 12..13] - ] - end - - def test_rest_keywords_parameters_before_required_parameters - expected = DefNode( - :foo, - Location(), - nil, - ParametersNode( - [], - [], - nil, - [], - [RequiredKeywordParameterNode(0, :b, Location())], - KeywordRestParameterNode(0, :rest, Location(), Location()), - nil - ), - nil, - [:rest, :b], - Location(), - nil, - Location(), - Location(), - nil, - Location() - ) - - assert_errors expected, "def foo(**rest, b:)\nend", [ - ["unexpected parameter order", 16..18] - ] - end - - def test_double_arguments_forwarding - expected = DefNode( - :foo, - Location(), - nil, - ParametersNode([], [], nil, [ForwardingParameterNode()], [], ForwardingParameterNode(), nil), - nil, - [], - Location(), - nil, - Location(), - Location(), - nil, - Location() - ) - - assert_errors expected, "def foo(..., ...)\nend", [ - ["unexpected parameter order", 13..16] - ] - end - - def test_multiple_error_in_parameters_order - expected = DefNode( - :foo, - Location(), - nil, - ParametersNode( - [], - [], - nil, - [RequiredParameterNode(0, :a)], - [RequiredKeywordParameterNode(0, :b, Location())], - KeywordRestParameterNode(0, :args, Location(), Location()), - nil - ), - nil, - [:args, :a, :b], - Location(), - nil, - Location(), - Location(), - nil, - Location() - ) - - assert_errors expected, "def foo(**args, a, b:)\nend", [ - ["unexpected parameter order", 16..17], - ["unexpected parameter order", 19..21] - ] - end - - def test_switching_to_optional_arguments_twice - expected = DefNode( - :foo, - Location(), - nil, - ParametersNode( - [], - [], - nil, - [RequiredParameterNode(0, :a)], - [RequiredKeywordParameterNode(0, :b, Location())], - KeywordRestParameterNode(0, :args, Location(), Location()), - nil - ), - nil, - [:args, :a, :b], - Location(), - nil, - Location(), - Location(), - nil, - Location(), - ) - - assert_errors expected, "def foo(**args, a, b:)\nend", [ - ["unexpected parameter order", 16..17], - ["unexpected parameter order", 19..21] - ] - end - - def test_switching_to_named_arguments_twice - expected = DefNode( - :foo, - Location(), - nil, - ParametersNode( - [], - [], - nil, - [RequiredParameterNode(0, :a)], - [RequiredKeywordParameterNode(0, :b, Location())], - KeywordRestParameterNode(0, :args, Location(), Location()), - nil - ), - nil, - [:args, :a, :b], - Location(), - nil, - Location(), - Location(), - nil, - Location(), - ) - - assert_errors expected, "def foo(**args, a, b:)\nend", [ - ["unexpected parameter order", 16..17], - ["unexpected parameter order", 19..21] - ] - end - - def test_returning_to_optional_parameters_multiple_times - expected = DefNode( - :foo, - Location(), - nil, - ParametersNode( - [RequiredParameterNode(0, :a)], - [ - OptionalParameterNode(0, :b, Location(), Location(), IntegerNode(IntegerBaseFlags::DECIMAL, 1)), - OptionalParameterNode(0, :d, Location(), Location(), IntegerNode(IntegerBaseFlags::DECIMAL, 2)) - ], - nil, - [RequiredParameterNode(0, :c), RequiredParameterNode(0, :e)], - [], - nil, - nil - ), - nil, - [:a, :b, :c, :d, :e], - Location(), - nil, - Location(), - Location(), - nil, - Location(), - ) - - assert_errors expected, "def foo(a, b = 1, c, d = 2, e)\nend", [ - ["unexpected parameter order", 23..24] - ] - end - - def test_case_without_when_clauses_errors_on_else_clause - expected = CaseMatchNode( - SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), - [], - ElseNode(Location(), nil, Location()), - Location(), - Location() - ) - - assert_errors expected, "case :a\nelse\nend", [ - ["expected a `when` or `in` clause after `case`", 0..4] - ] - end - - def test_case_without_clauses - expected = CaseNode( - SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), - [], - nil, - Location(), - Location() - ) - - assert_errors expected, "case :a\nend", [ - ["expected a `when` or `in` clause after `case`", 0..4] - ] - end - - def test_setter_method_cannot_be_defined_in_an_endless_method_definition - expected = DefNode( - :a=, - Location(), - nil, - nil, - StatementsNode([IntegerNode(IntegerBaseFlags::DECIMAL, 42)]), - [], - Location(), - nil, - Location(), - Location(), - Location(), - nil - ) - - assert_errors expected, "def a=() = 42", [ - ["invalid method name; a setter method cannot be defined in an endless method definition", 4..6] - ] - end - - def test_do_not_allow_forward_arguments_in_lambda_literals - expected = LambdaNode( - [], - Location(), - Location(), - Location(), - BlockParametersNode(ParametersNode([], [], nil, [], [], ForwardingParameterNode(), nil), [], Location(), Location()), - nil - ) - - assert_errors expected, "->(...) {}", [ - ["unexpected ... when the parent method is not forwarding", 3..6] - ] - end - - def test_do_not_allow_forward_arguments_in_blocks - expected = CallNode( - CallNodeFlags::IGNORE_VISIBILITY, - nil, - nil, - :a, - Location(), - nil, - nil, - nil, - BlockNode( - [], - BlockParametersNode(ParametersNode([], [], nil, [], [], ForwardingParameterNode(), nil), [], Location(), Location()), - nil, - Location(), - Location() - ) - ) - - assert_errors expected, "a {|...|}", [ - ["unexpected ... when the parent method is not forwarding", 4..7] - ] - end - - def test_dont_allow_return_inside_class_body - expected = ClassNode( - [], - Location(), - ConstantReadNode(:A), - nil, - nil, - StatementsNode([ReturnNode(0, Location(), nil)]), - Location(), - :A - ) - - assert_errors expected, "class A; return; end", [ - ["Invalid return in class/module body", 9..15] - ] - end - - def test_dont_allow_return_inside_module_body - expected = ModuleNode( - [], - Location(), - ConstantReadNode(:A), - StatementsNode([ReturnNode(0, Location(), nil)]), - Location(), - :A - ) - - assert_errors expected, "module A; return; end", [ - ["Invalid return in class/module body", 10..16] - ] - end - - def test_dont_allow_setting_to_back_and_nth_reference - expected = BeginNode( - Location(), - StatementsNode([ - GlobalVariableWriteNode(:$+, Location(), NilNode(), Location()), - GlobalVariableWriteNode(:$1466, Location(), NilNode(), Location()) - ]), - nil, - nil, - nil, - Location() - ) - - assert_errors expected, "begin\n$+ = nil\n$1466 = nil\nend", [ - ["Can't set variable $+", 6..8], - ["Can't set variable $1466", 15..20] - ] - end - - def test_duplicated_parameter_names - expected = DefNode( - :foo, - Location(), - nil, - ParametersNode([RequiredParameterNode(0, :a), RequiredParameterNode(0, :b), RequiredParameterNode(ParameterFlags::REPEATED_PARAMETER, :a)], [], nil, [], [], nil, nil), - nil, - [:a, :b], - Location(), - nil, - Location(), - Location(), - nil, - Location() - ) - - assert_errors expected, "def foo(a,b,a);end", [ - ["duplicated argument name", 12..13] - ] - - expected = DefNode( - :foo, - Location(), - nil, - ParametersNode([RequiredParameterNode(0, :a), RequiredParameterNode(0, :b)], [], RestParameterNode(ParameterFlags::REPEATED_PARAMETER, :a, Location(), Location()), [], [], nil, nil), - nil, - [:a, :b], - Location(), - nil, - Location(), - Location(), - nil, - Location() - ) - - assert_errors expected, "def foo(a,b,*a);end", [ - ["duplicated argument name", 13..14] - ] - - expected = DefNode( - :foo, - Location(), - nil, - ParametersNode([RequiredParameterNode(0, :a), RequiredParameterNode(0, :b)], [], nil, [], [], KeywordRestParameterNode(ParameterFlags::REPEATED_PARAMETER, :a, Location(), Location()), nil), - nil, - [:a, :b], - Location(), - nil, - Location(), - Location(), - nil, - Location() - ) - - assert_errors expected, "def foo(a,b,**a);end", [ - ["duplicated argument name", 14..15] - ] - - expected = DefNode( - :foo, - Location(), - nil, - ParametersNode([RequiredParameterNode(0, :a), RequiredParameterNode(0, :b)], [], nil, [], [], nil, BlockParameterNode(ParameterFlags::REPEATED_PARAMETER, :a, Location(), Location())), - nil, - [:a, :b], - Location(), - nil, - Location(), - Location(), - nil, - Location() - ) - - assert_errors expected, "def foo(a,b,&a);end", [ - ["duplicated argument name", 13..14] - ] - - expected = DefNode( - :foo, - Location(), - nil, - ParametersNode([], [OptionalParameterNode(0, :a, Location(), Location(), IntegerNode(IntegerBaseFlags::DECIMAL, 1))], RestParameterNode(0, :c, Location(), Location()), [RequiredParameterNode(0, :b)], [], nil, nil), - nil, - [:a, :b, :c], - Location(), - nil, - Location(), - Location(), - nil, - Location() - ) - - assert_errors expected, "def foo(a = 1,b,*c);end", [["unexpected parameter `*`", 16..17]] - end - - def test_content_after_unterminated_heredoc - receiver = StringNode(0, Location(), Location(), Location(), "") - expected = CallNode(0, receiver, Location(), :foo, Location(), nil, nil, nil, nil) - - assert_errors expected, "<<~FOO.foo\n", [ - ["unterminated heredoc; can't find string \"FOO\" anywhere before EOF", 3..6] - ] - end - - def test_invalid_message_name - assert_equal :"", Prism.parse_statement("+.@foo,+=foo").write_name - end - - def test_invalid_operator_write_fcall - source = "foo! += 1" - assert_errors expression(source), source, [ - ["unexpected write target", 0..4] - ] - end - - def test_invalid_operator_write_dot - source = "foo.+= 1" - assert_errors expression(source), source, [ - ["unexpected write target", 5..6] - ] - end - - def test_unterminated_global_variable - message = "'$' without identifiers is not allowed as a global variable name" - - assert_errors expression("$"), "$", [[message, 0..1]] - assert_errors expression("$ "), "$ ", [[message, 0..1]] - end - - def test_invalid_global_variable_write - assert_errors expression("$',"), "$',", [ - ["Can't set variable $'", 0..2], - ["unexpected write target", 0..2] - ] - end - - def test_invalid_multi_target - error_messages = ["unexpected write target"] - - assert_error_messages "foo,", error_messages - assert_error_messages "foo = 1; foo,", error_messages - assert_error_messages "foo.bar,", error_messages - assert_error_messages "*foo,", error_messages - assert_error_messages "@foo,", error_messages - assert_error_messages "@@foo,", error_messages - assert_error_messages "$foo,", error_messages - assert_error_messages "$1,", ["Can't set variable $1", *error_messages] - assert_error_messages "$+,", ["Can't set variable $+", *error_messages] - assert_error_messages "Foo,", error_messages - assert_error_messages "::Foo,", error_messages - assert_error_messages "Foo::Foo,", error_messages - assert_error_messages "Foo::foo,", error_messages - assert_error_messages "foo[foo],", error_messages - assert_error_messages "(foo, bar)", error_messages - assert_error_messages "foo((foo, bar))", error_messages - assert_error_messages "foo((*))", error_messages - assert_error_messages "foo(((foo, bar), *))", error_messages - assert_error_messages "(foo, bar) + 1", error_messages - assert_error_messages "(foo, bar) in baz", error_messages - end - - def test_call_with_block_and_write - source = "foo {} &&= 1" - assert_errors expression(source), source, [ - ["unexpected write target", 0..6], - ["unexpected operator after a call with a block", 7..10] - ] - end - - def test_call_with_block_or_write - source = "foo {} ||= 1" - assert_errors expression(source), source, [ - ["unexpected write target", 0..6], - ["unexpected operator after a call with a block", 7..10] - ] - end - - def test_call_with_block_operator_write - source = "foo {} += 1" - assert_errors expression(source), source, [ - ["unexpected write target", 0..6], - ["unexpected operator after a call with a block", 7..9] - ] - end - - def test_index_call_with_block_and_write - source = "foo[1] {} &&= 1" - assert_errors expression(source), source, [ - ["unexpected write target", 0..9], - ["unexpected operator after a call with arguments", 10..13], - ["unexpected operator after a call with a block", 10..13] - ] - end - - def test_index_call_with_block_or_write - source = "foo[1] {} ||= 1" - assert_errors expression(source), source, [ - ["unexpected write target", 0..9], - ["unexpected operator after a call with arguments", 10..13], - ["unexpected operator after a call with a block", 10..13] - ] - end - - def test_index_call_with_block_operator_write - source = "foo[1] {} += 1" - assert_errors expression(source), source, [ - ["unexpected write target", 0..9], - ["unexpected operator after a call with arguments", 10..12], - ["unexpected operator after a call with a block", 10..12] - ] - end - - if RUBY_VERSION >= "3.0" - def test_writing_numbered_parameter - assert_error_messages "-> { _1 = 0 }", [ - "_1 is reserved for numbered parameters" - ] - end - - def test_targeting_numbered_parameter - assert_error_messages "-> { _1, = 0 }", [ - "_1 is reserved for numbered parameters" - ] - end - - def test_defining_numbered_parameter - error_messages = ["_1 is reserved for numbered parameters"] - - assert_error_messages "def _1; end", error_messages - assert_error_messages "def self._1; end", error_messages - end - end - - def test_double_scope_numbered_parameters - source = "-> { _1 + -> { _2 } }" - errors = [["numbered parameter is already used in outer block", 15..17]] - - assert_errors expression(source), source, errors - end - - 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 - assert_error_messages "01__1", error_messages - 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 - assert_error_messages "01_1_", error_messages - assert_error_messages "0d1_1_", error_messages - assert_error_messages "0x1_1_", error_messages - end - - def test_alnum_delimiters - error_messages = ["unknown type of %string"] - - assert_error_messages "%qXfooX", error_messages - assert_error_messages "%QXfooX", error_messages - assert_error_messages "%wXfooX", error_messages - assert_error_messages "%WxfooX", error_messages - assert_error_messages "%iXfooX", error_messages - assert_error_messages "%IXfooX", error_messages - assert_error_messages "%xXfooX", error_messages - assert_error_messages "%rXfooX", error_messages - assert_error_messages "%sXfooX", error_messages - end - - def test_begin_at_toplevel - source = "def foo; BEGIN {}; end" - assert_errors expression(source), source, [ - ["BEGIN is permitted only at toplevel", 9..14], + if RUBY_VERSION < "3.4" + filepaths -= [ + "it_with_ordinary_parameter.txt" ] end - if RUBY_VERSION >= "3.0" - def test_numbered_parameters_in_block_arguments - source = "foo { |_1| }" - assert_errors expression(source), source, [ - ["_1 is reserved for numbered parameters", 7..9], - ] + filepaths.each do |filepath| + define_method(:"test_#{File.basename(filepath, ".txt")}") do + assert_errors(File.join(base, filepath)) end end - def test_conditional_predicate_closed - source = "if 0 0; elsif 0 0; end\nunless 0 0; end" - assert_errors expression(source), source, [ - ["expected `then` or `;` or '\\n" + "'", 5..6], - ["expected `then` or `;` or '\\n" + "'", 16..17], - ["expected `then` or `;` or '\\n" + "'", 32..33], - ] - end - - def test_parameter_name_ending_with_bang_or_question_mark - source = "def foo(x!,y?); end" - errors = [ - ["unexpected name for a parameter", 8..10], - ["unexpected name for a parameter", 11..13] - ] - assert_errors expression(source), source, errors - end - - def test_class_name - source = "class 0.X end" - assert_errors expression(source), source, [ - ["unexpected constant path after `class`; class/module name must be CONSTANT", 6..9], - ] - end - - def test_loop_conditional_is_closed - source = "while 0 0; foo; end; until 0 0; foo; end" - assert_errors expression(source), source, [ - ["expected a predicate expression for the `while` statement", 7..7], - ["expected a predicate expression for the `until` statement", 28..28], - ] - end - - def test_forwarding_arg_after_keyword_rest - source = "def f(**,...);end" - assert_errors expression(source), source, [ - ["unexpected parameter order", 9..12] - ] - end - - def test_semicolon_after_inheritance_operator - source = "class Foo < Bar end" - assert_errors expression(source), source, [ - ["unexpected `end`, expecting ';' or '\\n'", 15..15], - ] - end - - def test_shadow_args_in_lambda - source = "->a;b{}" - - assert_errors expression(source), source, [ - ["expected a `do` keyword or a `{` to open the lambda block", 3..3], - ["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 - - def test_shadow_args_in_block - source = "tap{|a;a|}" - assert_errors expression(source), source, [ - ["duplicated argument name", 7..8], - ] - end - - def test_repeated_parameter_name_in_destructured_params - source = "def f(a, (b, (a))); end" - - assert_errors expression(source), source, [ - ["duplicated argument name", 14..15], - ] - end - - def test_assign_to_numbered_parameter - source = <<~RUBY - a in _1 - a => _1 - 1 => a, _1 - 1 in a, _1 - /(?<_1>)/ =~ a - RUBY - - message = "_1 is reserved for numbered parameters" - assert_errors expression(source), source, [ - [message, 5..7], - [message, 13..15], - [message, 24..26], - [message, 35..37], - [message, 42..44] - ] - end - - def test_symbol_in_keyword_parameter - source = "def foo(x:'y':); end" - assert_errors expression(source), source, [ - ["unexpected label terminator, expected a string literal terminator", 12..14] - ] - end - - def test_symbol_in_hash - source = "{x:'y':}" - assert_errors expression(source), source, [ - ["unexpected label terminator, expected a string literal terminator", 5..7] - ] - end - - def test_while_endless_method - source = "while def f = g do end" - - assert_errors expression(source), source, [ - ["expected a predicate expression for the `while` statement", 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 - - def test_match_plus - source = <<~RUBY - a in b + c - a => b + c - RUBY - - assert_errors expression(source), source, [ - ["unexpected '+', expecting end-of-input", 7..8], - ["unexpected '+', ignoring it", 7..8], - ["unexpected '+', expecting end-of-input", 18..19], - ["unexpected '+', ignoring it", 18..19] - ] - end - - def test_rational_number_with_exponential_portion - source = '1e1r; 1e1ri' - - assert_errors expression(source), source, [ - ["unexpected local variable or method, expecting end-of-input", 3..4], - ["unexpected local variable or method, expecting end-of-input", 9..11] - ] - end - - def test_check_value_expression - source = <<~RUBY - 1 => ^(return) - while true - 1 => ^(break) - 1 => ^(next) - 1 => ^(redo) - 1 => ^(retry) - 1 => ^(2 => a) - end - 1 => ^(if 1; (return) else (return) end) - 1 => ^(unless 1; (return) else (return) end) - RUBY - - message = "unexpected void value expression" - assert_errors expression(source), source, [ - [message, 7..13], - [message, 35..40], - [message, 51..55], - [message, 66..70], - ["Invalid retry without rescue", 81..86], - [message, 81..86], - [message, 97..103], - [message, 123..129], - [message, 168..174], - ] - end - - def test_void_value_expression_in_statement - source = <<~RUBY - if (return) - end - unless (return) - end - while (return) - end - until (return) - end - case (return) - when 1 - end - class A < (return) - end - class << (return) - end - for x in (return) - end - RUBY - - message = 'unexpected void value expression' - assert_errors expression(source), source, [ - [message, 4..10], - [message, 24..30], - [message, 43..49], - [message, 62..68], - [message, 80..86], - [message, 110..116], - [message, 132..138], - [message, 154..160], - ] - end - - def test_void_value_expression_in_begin_statement - source = <<~RUBY - x = return 1 - x = return, 1 - x = 1, return - x, y = return - x = begin return ensure end - x = begin ensure return end - x = begin return ensure return end - x = begin return; rescue; return end - x = begin return; rescue; return; else return end - x = begin; return; rescue; retry; end - RUBY - - message = 'unexpected void value expression' - assert_errors expression(source), source, [ - [message, 4..12], - [message, 17..23], - [message, 34..40], - [message, 48..54], - [message, 65..71], - [message, 100..106], - [message, 121..127], - [message, 156..162], - [message, 222..228], - [message, 244..250], - ] - - refute_error_messages("x = begin return; rescue; end") - refute_error_messages("x = begin return; rescue; return; else end") - refute_error_messages("x = begin; rescue; retry; end") - refute_error_messages("x = begin 1; rescue; retry; ensure; end") - refute_error_messages("x = begin 1; rescue; return; end") - end - - def test_void_value_expression_in_def - source = <<~RUBY - def (return).x - end - def x(a = return) - end - def x(a: return) - end - RUBY - - message = 'unexpected void value expression' - assert_errors expression(source), source, [ - [message, 5..11], - [message, 29..35], - [message, 50..56], - ] - end - - def test_void_value_expression_in_assignment - source = <<~RUBY - a = return - a = 1, return - a, b = return, 1 - a, b = 1, *return - RUBY - - message = 'unexpected void value expression' - assert_errors expression(source), source, [ - [message, 4..10], - [message, 18..24], - [message, 32..38], - [message, 53..59], - ] - end - - def test_void_value_expression_in_modifier - source = <<~RUBY - 1 if (return) - 1 unless (return) - 1 while (return) - 1 until (return) - (return) => a - (return) in a - RUBY - - message = 'unexpected void value expression' - assert_errors expression(source), source, [ - [message, 6..12], - [message, 24..30], - [message, 41..47], - [message, 58..64], - [message, 67..73], - [message, 81..87] - ] - end - - def test_void_value_expression_in_expression - source = <<~RUBY - (return) ? 1 : 1 - (return)..1 - 1..(return) - (return)...1 - 1...(return) - (..(return)) - (...(return)) - ((return)..) - ((return)...) - RUBY - - message = 'unexpected void value expression' - assert_errors expression(source), source, [ - [message, 1..7], - [message, 18..24], - [message, 33..39], - [message, 42..48], - [message, 59..65], - [message, 71..77], - [message, 85..91], - [message, 96..102], - [message, 109..115] - ] - end - - def test_void_value_expression_in_array - source = <<~RUBY - [return] - [1, return] - [ return => 1 ] - [ 1 => return ] - [ a: return ] - [ *return ] - [ **return ] - RUBY - - message = 'unexpected void value expression' - assert_errors expression(source), source, [ - [message, 1..7], - [message, 13..19], - [message, 23..29], - [message, 44..50], - [message, 58..64], - [message, 70..76], - [message, 83..89], - ] - end - - def test_void_value_expression_in_hash - source = <<~RUBY - { return => 1 } - { 1 => return } - { a: return } - { **return } - RUBY - - message = 'unexpected void value expression' - assert_errors expression(source), source, [ - [message, 2..8], - [message, 23..29], - [message, 37..43], - [message, 50..56], - ] - end - - def test_void_value_expression_in_call - source = <<~RUBY - (return).foo - (return).(1) - (return)[1] - (return)[1] = 2 - (return)::foo - RUBY - - message = 'unexpected void value expression' - assert_errors expression(source), source, [ - [message, 1..7], - [message, 14..20], - [message, 27..33], - [message, 39..45], - [message, 55..61], - ] - end - - def test_void_value_expression_in_constant_path - source = <<~RUBY - (return)::A - class (return)::A; end - RUBY - - message = 'unexpected void value expression' - assert_errors expression(source), source, [ - [message, 1..7], - [message, 19..25], - ] - end - - def test_void_value_expression_in_arguments - source = <<~RUBY - foo(return) - foo(1, return) - foo(*return) - foo(**return) - foo(&return) - foo(return => 1) - foo(:a => return) - foo(a: return) - RUBY - - message = 'unexpected void value expression' - assert_errors expression(source), source, [ - [message, 4..10], - [message, 19..25], - [message, 32..38], - [message, 46..52], - [message, 59..65], - [message, 71..77], - [message, 94..100], - [message, 109..115], - ] - end - - def test_void_value_expression_in_unary_call - source = <<~RUBY - +(return) - not return - RUBY - - message = 'unexpected void value expression' - assert_errors expression(source), source, [ - [message, 2..8], - [message, 14..20], - ] - end - - def test_void_value_expression_in_binary_call + def test_embdoc_ending source = <<~RUBY - 1 + (return) - (return) + 1 - 1 and (return) - (return) and 1 - 1 or (return) - (return) or 1 + =begin\n=end + =begin\n=end\0 + =begin\n=end\C-d + =begin\n=end\C-z RUBY - message = 'unexpected void value expression' - assert_errors expression(source), source, [ - [message, 5..11], - [message, 14..20], - [message, 42..48], - [message, 71..77], - ] - end - - def test_trailing_comma_in_calls - assert_errors expression("foo 1,"), "foo 1,", [ - ["expected an argument", 5..6] - ] - end - - def test_argument_after_ellipsis - source = 'def foo(...); foo(..., 1); end' - assert_errors expression(source), source, [ - ['unexpected argument after `...`', 23..24] - ] - end - - def test_ellipsis_in_no_paren_call - source = 'def foo(...); foo 1, ...; end' - assert_errors expression(source), source, [ - ['unexpected `...` in an non-parenthesized call', 21..24] - ] - end - - def test_non_assoc_range - source = '1....2' - - assert_errors expression(source), source, [ - ["unexpected '.', expecting end-of-input", 4..5], - ["unexpected '.', ignoring it", 4..5] - ] - end - - def test_upcase_end_in_def - assert_warning_messages "def foo; END { }; end", [ - "END in method; use at_exit" - ] + source.each_line do |line| + assert_valid_syntax(source) + assert_predicate Prism.parse(source), :success? + end end - def test_warnings_verbosity - warning = Prism.parse("def foo; END { }; end").warnings[0] - assert_equal "END in method; use at_exit", warning.message - assert_equal :default, warning.level - - warning = Prism.parse("foo /regexp/").warnings[0] - assert_equal "ambiguous `/`; wrap regexp in parentheses or add a space after `/` operator", warning.message - assert_equal :verbose, warning.level + def test_unterminated_string_closing + statement = Prism.parse_statement("'hello") + assert_equal statement.unescaped, "hello" + assert_empty statement.closing end - def test_statement_operators - source = <<~RUBY - alias x y + 1 - alias x y.z - BEGIN { bar } + 1 - BEGIN { bar }.z - END { bar } + 1 - END { bar }.z - undef x + 1 - undef x.z - RUBY - - assert_errors expression(source), source, [ - ["unexpected '+', expecting end-of-input", 10..11], - ["unexpected '+', ignoring it", 10..11], - ["unexpected '.', expecting end-of-input", 23..24], - ["unexpected '.', ignoring it", 23..24], - ["unexpected '+', expecting end-of-input", 40..41], - ["unexpected '+', ignoring it", 40..41], - ["unexpected '.', expecting end-of-input", 57..58], - ["unexpected '.', ignoring it", 57..58], - ["unexpected '+', expecting end-of-input", 72..73], - ["unexpected '+', ignoring it", 72..73], - ["unexpected '.', expecting end-of-input", 87..88], - ["unexpected '.', ignoring it", 87..88], - ["unexpected '+', expecting end-of-input", 98..99], - ["unexpected '+', ignoring it", 98..99], - ["unexpected '.', expecting end-of-input", 109..110], - ["unexpected '.', ignoring it", 109..110] - ] + def test_unterminated_interpolated_string_closing + statement = Prism.parse_statement('"hello') + assert_equal statement.unescaped, "hello" + assert_empty statement.closing end - def test_statement_at_non_statement - source = <<~RUBY - foo(alias x y) - foo(BEGIN { bar }) - foo(END { bar }) - foo(undef x) - RUBY - assert_errors expression(source), source, [ - ['unexpected an `alias` at a non-statement position', 4..9], - ['unexpected a `BEGIN` at a non-statement position', 19..24], - ['unexpected an `END` at a non-statement position', 38..41], - ['unexpected an `undef` at a non-statement position', 55..60], - ] + def test_unterminated_empty_string_closing + statement = Prism.parse_statement('"') + assert_empty statement.unescaped + assert_empty statement.closing end - def test_binary_range_with_left_unary_range - source = <<~RUBY - ..1.. - ...1.. - RUBY - - assert_errors expression(source), source, [ - ["unexpected range operator; .. and ... are non-associative and cannot be chained", 3..5], - ["unexpected range operator; .. and ... are non-associative and cannot be chained", 10..12], - ["unexpected .., expecting end-of-input", 10..12], - ["unexpected .., ignoring it", 10..12] - ] + def test_invalid_message_name + assert_equal :"", Prism.parse_statement("+.@foo,+=foo").write_name end - def test_circular_param + def test_circular_parameters source = <<~RUBY def foo(bar = bar) = 42 def foo(bar: bar) = 42 @@ -1990,294 +74,26 @@ def foo(bar: bar) = 42 proc { |foo: foo| } RUBY - assert_errors( - expression(source), - source, - [ - ["circular argument reference - bar", 8..11], - ["circular argument reference - bar", 32..35], - ["circular argument reference - foo", 55..58], - ["circular argument reference - foo", 76..79] - ], - check_valid_syntax: false, - version: "3.3.0" - ) - - refute_error_messages("def foo(bar: bar = 1); end") - end - - def test_command_calls - sources = <<~RUBY.lines - [a b] - {a: b c} - ...a b - if ...a b; end - a b, c d - a(b, c d) - a(*b c) - a(**b c) - a(&b c) - +a b - a + b c - a && b c - a =~ b c - a = b, c d - a = *b c - a, b = c = d f - a ? b c : d e - defined? a b - ! ! a b - def f a = b c; end - def f(a = b c); end - a = b rescue c d - def a = b rescue c d - ->a=b c{} - ->(a=b c){} - case; when a b; end - case; in a if a b; end - case; in a unless a b; end - begin; rescue a b; end - begin; rescue a b => c; end - RUBY - - sources.each do |source| - refute_valid_syntax(source) - assert_false(Prism.parse(source).success?) - end - end - - def test_range_and_bin_op - sources = <<~RUBY.lines - 1..2..3 - 1..2.. - 1.. || 2 - 1.. & 2 - 1.. * 2 - 1.. / 2 - 1.. % 2 - 1.. ** 2 - RUBY - - sources.each do |source| - refute_valid_syntax(source) - assert_false(Prism.parse(source).success?) + source.each_line do |line| + assert_predicate Prism.parse(line, version: "3.3.0"), :failure? + assert_predicate Prism.parse(line), :success? end end - def test_command_call_in - source = <<~RUBY - foo 1 in a - a = foo 2 in b - RUBY - - assert_errors expression(source), source, [ - ["unexpected `in` keyword in arguments", 9..10], - ["unexpected local variable or method, expecting end-of-input", 9..10], - ["unexpected `in` keyword in arguments", 24..25], - ["unexpected local variable or method, expecting end-of-input", 24..25] - ] - end - - def test_constant_assignment_in_method - source = 'def foo();A=1;end' - assert_errors expression(source), source, [ - ['dynamic constant assignment', 10..13] - ] - end - - def test_non_assoc_equality - source = <<~RUBY - 1 == 2 == 3 - 1 != 2 != 3 - 1 === 2 === 3 - 1 =~ 2 =~ 3 - 1 !~ 2 !~ 3 - 1 <=> 2 <=> 3 - RUBY - - assert_errors expression(source), source, [ - ["unexpected '==', expecting end-of-input", 7..9], - ["unexpected '==', ignoring it", 7..9], - ["unexpected '!=', expecting end-of-input", 19..21], - ["unexpected '!=', ignoring it", 19..21], - ["unexpected '===', expecting end-of-input", 32..35], - ["unexpected '===', ignoring it", 32..35], - ["unexpected '=~', expecting end-of-input", 45..47], - ["unexpected '=~', ignoring it", 45..47], - ["unexpected '!~', expecting end-of-input", 57..59], - ["unexpected '!~', ignoring it", 57..59], - ["unexpected '<=>', expecting end-of-input", 70..73], - ["unexpected '<=>', ignoring it", 70..73] - ] - end - - def test_block_arg_and_block - source = 'foo(&1) { }' - assert_errors expression(source), source, [ - ["both block arg and actual block given; only one block is allowed", 8..11] - ] - end - - def test_forwarding_arg_and_block - source = 'def foo(...) = foo(...) { }' - assert_errors expression(source), source, [ - ['both block arg and actual block given; only one block is allowed', 24..27] - ] - end - - def test_it_with_ordinary_parameter - source = "proc { || it }" - errors = [["`it` is not allowed when an ordinary parameter is defined", 10..12]] - - assert_errors expression(source), source, errors, check_valid_syntax: RUBY_VERSION >= "3.4.0" - end - - def test_regular_expression_with_unknown_regexp_options - source = "/foo/AZaz" - errors = [["unknown regexp options - AZaz", 4..9]] - - assert_errors expression(source), source, errors - end - - def test_interpolated_regular_expression_with_unknown_regexp_options - source = "/\#{foo}/AZaz" - errors = [["unknown regexp options - AZaz", 7..12]] - - assert_errors expression(source), source, errors - end - - def test_singleton_method_for_literals - source = <<~'RUBY' - def (1).g; end - def ((a; 1)).foo; end - def ((return; 1)).bar; end - def (((1))).foo; end - def (__FILE__).foo; end - def (__ENCODING__).foo; end - def (__LINE__).foo; end - def ("foo").foo; end - def (3.14).foo; end - def (3.14i).foo; end - def (:foo).foo; end - def (:'foo').foo; end - def (:'f{o}').foo; end - def ('foo').foo; end - def ("foo").foo; end - def ("#{fo}o").foo; end - def (/foo/).foo; end - def (/f#{oo}/).foo; end - def ([1]).foo; end - RUBY - errors = [ - ["cannot define singleton method for literals", 5..6], - ["cannot define singleton method for literals", 24..25], - ["cannot define singleton method for literals", 51..52], - ["cannot define singleton method for literals", 71..72], - ["cannot define singleton method for literals", 90..98], - ["cannot define singleton method for literals", 114..126], - ["cannot define singleton method for literals", 142..150], - ["cannot define singleton method for literals", 166..171], - ["cannot define singleton method for literals", 187..191], - ["cannot define singleton method for literals", 207..212], - ["cannot define singleton method for literals", 228..232], - ["cannot define singleton method for literals", 248..254], - ["cannot define singleton method for literals", 270..277], - ["cannot define singleton method for literals", 293..298], - ["cannot define singleton method for literals", 314..319], - ["cannot define singleton method for literals", 335..343], - ["cannot define singleton method for literals", 359..364], - ["cannot define singleton method for literals", 380..388], - ["cannot define singleton method for literals", 404..407] - ] - assert_errors expression(source), source, errors - end - - def test_assignment_to_literal_in_conditionals - source = <<~RUBY - if (a = 2); end - if ($a = 2); end - if (@a = 2); end - if (@@a = 2); end - if a elsif b = 2; end - unless (a = 2); end - unless ($a = 2); end - unless (@a = 2); end - unless (@@a = 2); end - while (a = 2); end - while ($a = 2); end - while (@a = 2); end - while (@@a = 2); end - until (a = 2); end - until ($a = 2); end - until (@a = 2); end - until (@@a = 2); end - foo if a = 2 - foo if (a, b = 2) - (@foo = 1) ? a : b - !(a = 2) - not a = 2 - RUBY - assert_warning_messages source, [ - "found '= literal' in conditional, should be ==" - ] * source.lines.count - end - - def test_duplicate_pattern_capture - source = <<~RUBY - case (); in [a, a]; end - case (); in [a, *a]; end - case (); in {a: a, b: a}; end - case (); in {a: a, **a}; end - case (); in [a, {a:}]; end - case (); in [a, {a: {a: {a: [a]}}}]; end - case (); in a => a; end - case (); in [A => a, {a: b => a}]; end - RUBY - - assert_error_messages source, Array.new(source.lines.length, "duplicated variable name") - refute_error_messages "case (); in [_a, _a]; end" - end - - def test_duplicate_pattern_hash_key - assert_error_messages "case (); in {a:, a:}; end", ["duplicated key name", "duplicated variable name"] - assert_error_messages "case (); in {a:1, a:2}; end", ["duplicated key name"] - refute_error_messages "case (); in [{a:1}, {a:2}]; end" - end - - def test_unexpected_block - assert_error_messages "def foo = yield(&:+)", ["block argument should not be given"] - end - private - def assert_errors(expected, source, errors, check_valid_syntax: true, **options) - refute_valid_syntax(source) if check_valid_syntax + def assert_errors(filepath) + expected = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8) - result = Prism.parse(source, **options) - node = result.value.statements.body.last - - assert_equal_nodes(expected, node, compare_location: false) - assert_equal(errors, result.errors.map { |e| [e.message, e.location.start_offset..e.location.end_offset] }) - end - - def assert_error_messages(source, errors) + source = expected.lines.grep_v(/^\s*\^/).join.gsub(/\n*\z/, "") refute_valid_syntax(source) - result = Prism.parse(source) - assert_equal(errors, result.errors.map(&:message)) - end - - def refute_error_messages(source) - assert_valid_syntax(source) - assert Prism.parse_success?(source), "Expected #{source.inspect} to parse successfully" - end - def assert_warning_messages(source, warnings) result = Prism.parse(source) - assert_equal(warnings, result.warnings.map(&:message)) - end + errors = result.errors + refute_empty errors, "Expected errors in #{filepath}" - def expression(source) - Prism.parse(source).value.statements.body.last + actual = result.errors_format + assert_equal expected, actual, "Expected errors to match for #{filepath}" end end end diff --git a/test/prism/fixtures/patterns.txt b/test/prism/fixtures/patterns.txt index 7ce3b9e4a8ee31..29f8b4cf808332 100644 --- a/test/prism/fixtures/patterns.txt +++ b/test/prism/fixtures/patterns.txt @@ -215,3 +215,6 @@ foo => Object[{x:}] a, b ) = c + +case (); in [_a, _a]; end +case (); in [{a:1}, {a:2}]; end diff --git a/test/prism/locals_test.rb b/test/prism/locals_test.rb index 3c45d8b08b11b4..ea61fd94996ec3 100644 --- a/test/prism/locals_test.rb +++ b/test/prism/locals_test.rb @@ -9,6 +9,11 @@ # to test on the most recent versions. return if !defined?(RubyVM::InstructionSequence) || RUBY_VERSION < "3.4.0" +# In Ruby 3.4.0, the local table for method forwarding changed. But 3.4.0 can +# refer to the dev version, so while 3.4.0 still isn't released, we need to +# check if we have a high enough revision. +return if RubyVM::InstructionSequence.compile("def foo(...); end").to_a[13][2][2][10].length != 1 + # Omit tests if running on a 32-bit machine because there is a bug with how # Ruby is handling large ISeqs on 32-bit machines return if RUBY_PLATFORM =~ /i686/ diff --git a/test/prism/newline_test.rb b/test/prism/newline_test.rb index 03d7df4c97f586..fefe9def91f1e6 100644 --- a/test/prism/newline_test.rb +++ b/test/prism/newline_test.rb @@ -14,6 +14,7 @@ class NewlineTest < TestCase unescape_test.rb encoding/regular_expression_encoding_test.rb encoding/string_encoding_test.rb + result/breadth_first_search_test.rb result/static_literals_test.rb result/warnings_test.rb ruby/parser_test.rb @@ -88,7 +89,7 @@ def prism_lines(result) while node = queue.shift queue.concat(node.compact_child_nodes) - newlines << result.source.line(node.location.start_offset) if node&.newline? + newlines << result.source.line(node.location.start_offset) if node&.newline_flag? end newlines.sort diff --git a/test/prism/result/breadth_first_search_test.rb b/test/prism/result/breadth_first_search_test.rb new file mode 100644 index 00000000000000..e2e043a902102e --- /dev/null +++ b/test/prism/result/breadth_first_search_test.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require_relative "../test_helper" + +module Prism + class BreadthFirstSearchTest < TestCase + def test_breadth_first_search + result = Prism.parse("[1 + 2, 2]") + found = + result.value.breadth_first_search do |node| + node.is_a?(IntegerNode) && node.value == 2 + end + + refute_nil found + assert_equal 8, found.start_offset + end + end +end diff --git a/test/prism/result/integer_base_flags_test.rb b/test/prism/result/integer_base_flags_test.rb index ef15fb437c0f01..e3ab8c691096af 100644 --- a/test/prism/result/integer_base_flags_test.rb +++ b/test/prism/result/integer_base_flags_test.rb @@ -11,7 +11,7 @@ class IntegerBaseFlagsTest < TestCase # # In C, this would look something like: # - # ((flags & ~DECIMAL) << 1) || 10 + # ((flags & ~DECIMAL) >> 1) || 10 # # We have to do some other work in Ruby because 0 is truthy and ~ on an # integer doesn't have a fixed width. @@ -26,7 +26,7 @@ def test_flags def base(source) node = Prism.parse_statement(source) - value = (node.send(:flags) & (0b1111 - IntegerBaseFlags::DECIMAL)) << 1 + value = (node.send(:flags) & (0b111100 - IntegerBaseFlags::DECIMAL)) >> 1 value == 0 ? 10 : value end end diff --git a/test/prism/result/node_id_test.rb b/test/prism/result/node_id_test.rb new file mode 100644 index 00000000000000..59b79bc5746693 --- /dev/null +++ b/test/prism/result/node_id_test.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require_relative "../test_helper" + +module Prism + class NodeIdTest < TestCase + Fixture.each do |fixture| + define_method(fixture.test_name) { assert_node_ids(fixture.read) } + end + + private + + def assert_node_ids(source) + queue = [Prism.parse(source).value] + node_ids = [] + + while (node = queue.shift) + node_ids << node.node_id + queue.concat(node.compact_child_nodes) + end + + node_ids.sort! + refute_includes node_ids, 0 + assert_equal node_ids, node_ids.uniq + end + end +end diff --git a/test/prism/result/redundant_return_test.rb b/test/prism/result/redundant_return_test.rb deleted file mode 100644 index 3b20aeba00f817..00000000000000 --- a/test/prism/result/redundant_return_test.rb +++ /dev/null @@ -1,73 +0,0 @@ -# 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/result/warnings_test.rb b/test/prism/result/warnings_test.rb index ea062d42215279..c147616a6a4c28 100644 --- a/test/prism/result/warnings_test.rb +++ b/test/prism/result/warnings_test.rb @@ -75,6 +75,42 @@ def test_integer_in_flip_flop assert_warning("1 if 2..foo", "integer") end + def test_literal_in_conditionals + sources = [ + "if (a = 2); a; end", + "if ($a = 2); end", + "if (@a = 2); end", + "if a; elsif b = 2; b end", + "unless (a = 2); a; end", + "unless ($a = 2); end", + "unless (@a = 2); end", + "while (a = 2); a; end", + "while ($a = 2); end", + "while (@a = 2); end", + "until (a = 2); a; end", + "until ($a = 2); end", + "until (@a = 2); end", + "foo if (a, b = 2); [a, b]", + "foo if a = 2 and a", + "(@foo = 1) ? a : b", + "!(a = 2) and a", + "not a = 2 and a" + ] + + if RUBY_VERSION >= "3.3" + sources.push( + "if (@@a = 2); end", + "unless (@@a = 2); end", + "while (@@a = 2); end", + "until (@@a = 2); end" + ) + end + + sources.each do |source| + assert_warning(source, "= literal' in conditional, should be ==") + end + end + def test_keyword_eol assert_warning("if\ntrue; end", "end of line") assert_warning("if true\nelsif\nfalse; end", "end of line") @@ -227,12 +263,22 @@ def test_unreachable_statement assert_warning("tap { redo; foo }", "statement not reached") end + def test_warnings_verbosity + warning = Prism.parse("def foo; END { }; end").warnings.first + assert_equal "END in method; use at_exit", warning.message + assert_equal :default, warning.level + + warning = Prism.parse("foo /regexp/").warnings.first + assert_equal "ambiguous `/`; wrap regexp in parentheses or add a space after `/` operator", warning.message + assert_equal :verbose, warning.level + end + private def assert_warning(source, message) warnings = Prism.parse(source).warnings - assert_equal 1, warnings.length + assert_equal 1, warnings.length, "Expected only one warning in #{source.inspect}, got #{warnings.map(&:message).inspect}" assert_include warnings.first.message, message if defined?(RubyVM::AbstractSyntaxTree) diff --git a/test/prism/snapshots/alias.txt b/test/prism/snapshots/alias.txt index a952e96f67721f..0d52c14f1a851f 100644 --- a/test/prism/snapshots/alias.txt +++ b/test/prism/snapshots/alias.txt @@ -1,192 +1,213 @@ @ ProgramNode (location: (1,0)-(23,11)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(23,11)) + ├── flags: ∅ └── body: (length: 12) ├── @ AliasMethodNode (location: (1,0)-(1,15)) + │ ├── flags: newline │ ├── new_name: │ │ @ SymbolNode (location: (1,6)-(1,10)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,6)-(1,7) = ":" │ │ ├── value_loc: (1,7)-(1,10) = "foo" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "foo" │ ├── old_name: │ │ @ SymbolNode (location: (1,11)-(1,15)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,11)-(1,12) = ":" │ │ ├── value_loc: (1,12)-(1,15) = "bar" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "bar" │ └── keyword_loc: (1,0)-(1,5) = "alias" ├── @ AliasMethodNode (location: (3,0)-(3,21)) + │ ├── flags: newline │ ├── new_name: │ │ @ SymbolNode (location: (3,6)-(3,13)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (3,6)-(3,9) = "%s[" │ │ ├── value_loc: (3,9)-(3,12) = "abc" │ │ ├── closing_loc: (3,12)-(3,13) = "]" │ │ └── unescaped: "abc" │ ├── old_name: │ │ @ SymbolNode (location: (3,14)-(3,21)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (3,14)-(3,17) = "%s[" │ │ ├── value_loc: (3,17)-(3,20) = "def" │ │ ├── closing_loc: (3,20)-(3,21) = "]" │ │ └── unescaped: "def" │ └── keyword_loc: (3,0)-(3,5) = "alias" ├── @ AliasMethodNode (location: (5,0)-(5,19)) + │ ├── flags: newline │ ├── new_name: │ │ @ SymbolNode (location: (5,6)-(5,12)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (5,6)-(5,8) = ":'" │ │ ├── value_loc: (5,8)-(5,11) = "abc" │ │ ├── closing_loc: (5,11)-(5,12) = "'" │ │ └── unescaped: "abc" │ ├── old_name: │ │ @ SymbolNode (location: (5,13)-(5,19)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (5,13)-(5,15) = ":'" │ │ ├── value_loc: (5,15)-(5,18) = "def" │ │ ├── closing_loc: (5,18)-(5,19) = "'" │ │ └── unescaped: "def" │ └── keyword_loc: (5,0)-(5,5) = "alias" ├── @ AliasMethodNode (location: (7,0)-(7,23)) + │ ├── flags: newline │ ├── new_name: │ │ @ InterpolatedSymbolNode (location: (7,6)-(7,16)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (7,6)-(7,8) = ":\"" │ │ ├── parts: (length: 2) │ │ │ ├── @ StringNode (location: (7,8)-(7,11)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── content_loc: (7,8)-(7,11) = "abc" │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: "abc" │ │ │ └── @ EmbeddedStatementsNode (location: (7,11)-(7,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (7,11)-(7,13) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (7,13)-(7,14)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ IntegerNode (location: (7,13)-(7,14)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── closing_loc: (7,14)-(7,15) = "}" │ │ └── closing_loc: (7,15)-(7,16) = "\"" │ ├── old_name: │ │ @ SymbolNode (location: (7,17)-(7,23)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (7,17)-(7,19) = ":'" │ │ ├── value_loc: (7,19)-(7,22) = "def" │ │ ├── closing_loc: (7,22)-(7,23) = "'" │ │ └── unescaped: "def" │ └── keyword_loc: (7,0)-(7,5) = "alias" ├── @ AliasGlobalVariableNode (location: (9,0)-(9,11)) + │ ├── flags: newline │ ├── new_name: │ │ @ GlobalVariableReadNode (location: (9,6)-(9,8)) + │ │ ├── flags: ∅ │ │ └── name: :$a │ ├── old_name: │ │ @ BackReferenceReadNode (location: (9,9)-(9,11)) + │ │ ├── flags: ∅ │ │ └── name: :$' │ └── keyword_loc: (9,0)-(9,5) = "alias" ├── @ AliasMethodNode (location: (11,0)-(11,13)) + │ ├── flags: newline │ ├── new_name: │ │ @ SymbolNode (location: (11,6)-(11,9)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (11,6)-(11,9) = "foo" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "foo" │ ├── old_name: │ │ @ SymbolNode (location: (11,10)-(11,13)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (11,10)-(11,13) = "bar" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "bar" │ └── keyword_loc: (11,0)-(11,5) = "alias" ├── @ AliasGlobalVariableNode (location: (13,0)-(13,15)) + │ ├── flags: newline │ ├── new_name: │ │ @ GlobalVariableReadNode (location: (13,6)-(13,10)) + │ │ ├── flags: ∅ │ │ └── name: :$foo │ ├── old_name: │ │ @ GlobalVariableReadNode (location: (13,11)-(13,15)) + │ │ ├── flags: ∅ │ │ └── name: :$bar │ └── keyword_loc: (13,0)-(13,5) = "alias" ├── @ AliasMethodNode (location: (15,0)-(15,12)) + │ ├── flags: newline │ ├── new_name: │ │ @ SymbolNode (location: (15,6)-(15,9)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (15,6)-(15,9) = "foo" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "foo" │ ├── old_name: │ │ @ SymbolNode (location: (15,10)-(15,12)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (15,10)-(15,12) = "if" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "if" │ └── keyword_loc: (15,0)-(15,5) = "alias" ├── @ AliasMethodNode (location: (17,0)-(17,13)) + │ ├── flags: newline │ ├── new_name: │ │ @ SymbolNode (location: (17,6)-(17,9)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (17,6)-(17,9) = "foo" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "foo" │ ├── old_name: │ │ @ SymbolNode (location: (17,10)-(17,13)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (17,10)-(17,13) = "<=>" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "<=>" │ └── keyword_loc: (17,0)-(17,5) = "alias" ├── @ AliasMethodNode (location: (19,0)-(19,15)) + │ ├── flags: newline │ ├── new_name: │ │ @ SymbolNode (location: (19,6)-(19,9)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (19,6)-(19,7) = ":" │ │ ├── value_loc: (19,7)-(19,9) = "==" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "==" │ ├── old_name: │ │ @ SymbolNode (location: (19,10)-(19,15)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (19,10)-(19,11) = ":" │ │ ├── value_loc: (19,11)-(19,15) = "eql?" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "eql?" │ └── keyword_loc: (19,0)-(19,5) = "alias" ├── @ AliasMethodNode (location: (21,0)-(21,9)) + │ ├── flags: newline │ ├── new_name: │ │ @ SymbolNode (location: (21,6)-(21,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (21,6)-(21,7) = "A" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "A" │ ├── old_name: │ │ @ SymbolNode (location: (21,8)-(21,9)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (21,8)-(21,9) = "B" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "B" │ └── keyword_loc: (21,0)-(21,5) = "alias" └── @ AliasMethodNode (location: (23,0)-(23,11)) + ├── flags: newline ├── new_name: │ @ SymbolNode (location: (23,6)-(23,8)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (23,6)-(23,7) = ":" │ ├── value_loc: (23,7)-(23,8) = "A" │ ├── closing_loc: ∅ │ └── unescaped: "A" ├── old_name: │ @ SymbolNode (location: (23,9)-(23,11)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (23,9)-(23,10) = ":" │ ├── value_loc: (23,10)-(23,11) = "B" │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/arithmetic.txt b/test/prism/snapshots/arithmetic.txt index c8a31c3d70e54e..963080e0d96b14 100644 --- a/test/prism/snapshots/arithmetic.txt +++ b/test/prism/snapshots/arithmetic.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(13,8)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(13,8)) + ├── flags: ∅ └── body: (length: 7) ├── @ CallNode (location: (1,0)-(1,8)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -37,7 +39,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (3,0)-(3,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (3,0)-(3,4)) │ │ ├── flags: ∅ @@ -80,7 +82,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (5,0)-(5,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (5,0)-(5,4)) │ │ ├── flags: ∅ @@ -123,7 +125,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (7,0)-(7,8)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -156,7 +158,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (9,0)-(9,17)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (9,0)-(9,10)) │ │ ├── flags: ∅ @@ -212,13 +214,13 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (11,0)-(11,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (11,1)-(11,5)) │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ IntegerNode (location: (11,1)-(11,2)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── call_operator_loc: ∅ │ │ ├── name: :** @@ -229,7 +231,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (11,4)-(11,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ @@ -241,10 +243,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ └── @ CallNode (location: (13,0)-(13,8)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (13,0)-(13,2)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: -1 ├── call_operator_loc: (13,2)-(13,3) = "." ├── name: :zero? diff --git a/test/prism/snapshots/arrays.txt b/test/prism/snapshots/arrays.txt index 90a4d8f3bbcce4..a73c56ac80861c 100644 --- a/test/prism/snapshots/arrays.txt +++ b/test/prism/snapshots/arrays.txt @@ -1,12 +1,15 @@ @ ProgramNode (location: (1,0)-(122,32)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(122,32)) + ├── flags: ∅ └── body: (length: 50) ├── @ ArrayNode (location: (1,0)-(1,4)) - │ ├── flags: contains_splat + │ ├── flags: newline, contains_splat │ ├── elements: (length: 1) │ │ └── @ SplatNode (location: (1,1)-(1,3)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (1,1)-(1,2) = "*" │ │ └── expression: │ │ @ CallNode (location: (1,2)-(1,3)) @@ -22,7 +25,7 @@ │ ├── opening_loc: (1,0)-(1,1) = "[" │ └── closing_loc: (1,3)-(1,4) = "]" ├── @ CallNode (location: (3,0)-(3,23)) - │ ├── flags: attribute_write + │ ├── flags: newline, attribute_write │ ├── receiver: │ │ @ CallNode (location: (3,0)-(3,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -63,47 +66,48 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ └── @ ArrayNode (location: (3,16)-(3,23)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── elements: (length: 3) │ │ │ ├── @ IntegerNode (location: (3,16)-(3,17)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── @ IntegerNode (location: (3,19)-(3,20)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ └── @ IntegerNode (location: (3,22)-(3,23)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 3 │ │ ├── opening_loc: ∅ │ │ └── closing_loc: ∅ │ ├── closing_loc: (3,12)-(3,13) = "]" │ └── block: ∅ ├── @ ArrayNode (location: (5,0)-(5,13)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 1) │ │ └── @ KeywordHashNode (location: (5,1)-(5,12)) │ │ ├── flags: symbol_keys │ │ └── elements: (length: 1) │ │ └── @ AssocNode (location: (5,1)-(5,12)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ SymbolNode (location: (5,1)-(5,3)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (5,1)-(5,2) = "a" │ │ │ ├── closing_loc: (5,2)-(5,3) = ":" │ │ │ └── unescaped: "a" │ │ ├── value: │ │ │ @ ArrayNode (location: (5,4)-(5,12)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: static_literal │ │ │ ├── elements: (length: 2) │ │ │ │ ├── @ SymbolNode (location: (5,5)-(5,7)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: (5,5)-(5,6) = ":" │ │ │ │ │ ├── value_loc: (5,6)-(5,7) = "b" │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── unescaped: "b" │ │ │ │ └── @ SymbolNode (location: (5,9)-(5,11)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (5,9)-(5,10) = ":" │ │ │ │ ├── value_loc: (5,10)-(5,11) = "c" │ │ │ │ ├── closing_loc: ∅ @@ -114,31 +118,31 @@ │ ├── opening_loc: (5,0)-(5,1) = "[" │ └── closing_loc: (5,12)-(5,13) = "]" ├── @ ArrayNode (location: (9,0)-(15,1)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── elements: (length: 5) │ │ ├── @ SymbolNode (location: (9,1)-(9,3)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (9,1)-(9,2) = ":" │ │ │ ├── value_loc: (9,2)-(9,3) = "a" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a" │ │ ├── @ SymbolNode (location: (9,5)-(9,7)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (9,5)-(9,6) = ":" │ │ │ ├── value_loc: (9,6)-(9,7) = "b" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "b" │ │ ├── @ SymbolNode (location: (10,0)-(10,2)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (10,0)-(10,1) = ":" │ │ │ ├── value_loc: (10,1)-(10,2) = "c" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "c" │ │ ├── @ IntegerNode (location: (10,3)-(10,4)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ SymbolNode (location: (14,0)-(14,2)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (14,0)-(14,1) = ":" │ │ ├── value_loc: (14,1)-(14,2) = "d" │ │ ├── closing_loc: ∅ @@ -146,31 +150,31 @@ │ ├── opening_loc: (9,0)-(9,1) = "[" │ └── closing_loc: (15,0)-(15,1) = "]" ├── @ ArrayNode (location: (18,0)-(26,1)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── elements: (length: 5) │ │ ├── @ SymbolNode (location: (18,1)-(18,3)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (18,1)-(18,2) = ":" │ │ │ ├── value_loc: (18,2)-(18,3) = "a" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a" │ │ ├── @ SymbolNode (location: (18,5)-(18,7)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (18,5)-(18,6) = ":" │ │ │ ├── value_loc: (18,6)-(18,7) = "b" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "b" │ │ ├── @ SymbolNode (location: (19,0)-(19,2)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (19,0)-(19,1) = ":" │ │ │ ├── value_loc: (19,1)-(19,2) = "c" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "c" │ │ ├── @ IntegerNode (location: (19,3)-(19,4)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ SymbolNode (location: (23,0)-(23,2)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (23,0)-(23,1) = ":" │ │ ├── value_loc: (23,1)-(23,2) = "d" │ │ ├── closing_loc: ∅ @@ -178,12 +182,13 @@ │ ├── opening_loc: (18,0)-(18,1) = "[" │ └── closing_loc: (26,0)-(26,1) = "]" ├── @ ArrayNode (location: (28,0)-(28,12)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 1) │ │ └── @ KeywordHashNode (location: (28,1)-(28,11)) │ │ ├── flags: ∅ │ │ └── elements: (length: 1) │ │ └── @ AssocNode (location: (28,1)-(28,11)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ CallNode (location: (28,1)-(28,4)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -210,7 +215,7 @@ │ ├── opening_loc: (28,0)-(28,1) = "[" │ └── closing_loc: (28,11)-(28,12) = "]" ├── @ CallNode (location: (30,0)-(30,19)) - │ ├── flags: attribute_write + │ ├── flags: newline, attribute_write │ ├── receiver: │ │ @ CallNode (location: (30,0)-(30,8)) │ │ ├── flags: ∅ @@ -276,7 +281,7 @@ │ ├── closing_loc: (30,12)-(30,13) = "]" │ └── block: ∅ ├── @ CallNode (location: (32,0)-(32,13)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (32,0)-(32,8)) │ │ ├── flags: ∅ @@ -332,12 +337,12 @@ │ ├── closing_loc: (32,12)-(32,13) = "]" │ └── block: ∅ ├── @ ArrayNode (location: (34,0)-(35,1)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── elements: (length: 0) │ ├── opening_loc: (34,0)-(34,1) = "[" │ └── closing_loc: (35,0)-(35,1) = "]" ├── @ CallNode (location: (37,0)-(37,13)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (37,0)-(37,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -380,7 +385,7 @@ │ ├── closing_loc: (37,12)-(37,13) = "]" │ └── block: ∅ ├── @ CallNode (location: (39,0)-(39,19)) - │ ├── flags: attribute_write + │ ├── flags: newline, attribute_write │ ├── receiver: │ │ @ CallNode (location: (39,0)-(39,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -433,6 +438,7 @@ │ ├── closing_loc: (39,12)-(39,13) = "]" │ └── block: ∅ ├── @ MultiWriteNode (location: (41,0)-(41,21)) + │ ├── flags: newline │ ├── lefts: (length: 2) │ │ ├── @ IndexTargetNode (location: (41,0)-(41,6)) │ │ │ ├── flags: attribute_write @@ -453,7 +459,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ IntegerNode (location: (41,4)-(41,5)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 0 │ │ │ ├── closing_loc: (41,5)-(41,6) = "]" │ │ │ └── block: ∅ @@ -476,7 +482,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (41,12)-(41,13)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 0 │ │ ├── closing_loc: (41,13)-(41,14) = "]" │ │ └── block: ∅ @@ -487,18 +493,18 @@ │ ├── operator_loc: (41,15)-(41,16) = "=" │ └── value: │ @ ArrayNode (location: (41,17)-(41,21)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 2) │ │ ├── @ IntegerNode (location: (41,17)-(41,18)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ IntegerNode (location: (41,20)-(41,21)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── opening_loc: ∅ │ └── closing_loc: ∅ ├── @ CallNode (location: (43,0)-(43,19)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (43,0)-(43,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -564,7 +570,7 @@ │ ├── closing_loc: (43,18)-(43,19) = "]" │ └── block: ∅ ├── @ CallNode (location: (45,0)-(45,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (45,0)-(45,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -597,7 +603,7 @@ │ ├── closing_loc: (45,7)-(45,8) = "]" │ └── block: ∅ ├── @ CallNode (location: (47,0)-(47,14)) - │ ├── flags: attribute_write + │ ├── flags: newline, attribute_write │ ├── receiver: │ │ @ CallNode (location: (47,0)-(47,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -640,14 +646,16 @@ │ ├── closing_loc: (47,7)-(47,8) = "]" │ └── block: ∅ ├── @ ArrayNode (location: (49,0)-(49,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 1) │ │ └── @ KeywordHashNode (location: (49,1)-(49,5)) │ │ ├── flags: ∅ │ │ └── elements: (length: 1) │ │ └── @ AssocSplatNode (location: (49,1)-(49,5)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ HashNode (location: (49,3)-(49,5)) + │ │ │ ├── flags: static_literal │ │ │ ├── opening_loc: (49,3)-(49,4) = "{" │ │ │ ├── elements: (length: 0) │ │ │ └── closing_loc: (49,4)-(49,5) = "}" @@ -655,12 +663,13 @@ │ ├── opening_loc: (49,0)-(49,1) = "[" │ └── closing_loc: (49,5)-(49,6) = "]" ├── @ ArrayNode (location: (51,0)-(51,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 1) │ │ └── @ KeywordHashNode (location: (51,1)-(51,5)) │ │ ├── flags: ∅ │ │ └── elements: (length: 1) │ │ └── @ AssocSplatNode (location: (51,1)-(51,5)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ CallNode (location: (51,3)-(51,5)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -676,15 +685,16 @@ │ ├── opening_loc: (51,0)-(51,1) = "[" │ └── closing_loc: (51,5)-(51,6) = "]" ├── @ ArrayNode (location: (53,0)-(53,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 2) │ │ ├── @ IntegerNode (location: (53,1)-(53,2)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ KeywordHashNode (location: (53,4)-(53,8)) │ │ ├── flags: ∅ │ │ └── elements: (length: 1) │ │ └── @ AssocSplatNode (location: (53,4)-(53,8)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ CallNode (location: (53,6)-(53,8)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -700,15 +710,16 @@ │ ├── opening_loc: (53,0)-(53,1) = "[" │ └── closing_loc: (53,8)-(53,9) = "]" ├── @ ArrayNode (location: (55,0)-(55,21)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 2) │ │ ├── @ IntegerNode (location: (55,1)-(55,2)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ KeywordHashNode (location: (55,4)-(55,20)) │ │ ├── flags: ∅ │ │ └── elements: (length: 3) │ │ ├── @ AssocSplatNode (location: (55,4)-(55,8)) + │ │ │ ├── flags: ∅ │ │ │ ├── value: │ │ │ │ @ CallNode (location: (55,6)-(55,8)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -722,13 +733,16 @@ │ │ │ │ └── block: ∅ │ │ │ └── operator_loc: (55,4)-(55,6) = "**" │ │ ├── @ AssocSplatNode (location: (55,10)-(55,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── value: │ │ │ │ @ HashNode (location: (55,12)-(55,14)) + │ │ │ │ ├── flags: static_literal │ │ │ │ ├── opening_loc: (55,12)-(55,13) = "{" │ │ │ │ ├── elements: (length: 0) │ │ │ │ └── closing_loc: (55,13)-(55,14) = "}" │ │ │ └── operator_loc: (55,10)-(55,12) = "**" │ │ └── @ AssocSplatNode (location: (55,16)-(55,20)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ CallNode (location: (55,18)-(55,20)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -744,12 +758,13 @@ │ ├── opening_loc: (55,0)-(55,1) = "[" │ └── closing_loc: (55,20)-(55,21) = "]" ├── @ ArrayNode (location: (57,0)-(59,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 1) │ │ └── @ KeywordHashNode (location: (58,2)-(58,12)) │ │ ├── flags: ∅ │ │ └── elements: (length: 1) │ │ └── @ AssocNode (location: (58,2)-(58,12)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ CallNode (location: (58,2)-(58,5)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -776,22 +791,22 @@ │ ├── opening_loc: (57,0)-(57,1) = "[" │ └── closing_loc: (59,0)-(59,1) = "]" ├── @ ArrayNode (location: (62,0)-(62,17)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── elements: (length: 3) │ │ ├── @ SymbolNode (location: (62,3)-(62,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (62,3)-(62,6) = "one" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "one" │ │ ├── @ SymbolNode (location: (62,7)-(62,10)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (62,7)-(62,10) = "two" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "two" │ │ └── @ SymbolNode (location: (62,11)-(62,16)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (62,11)-(62,16) = "three" │ │ ├── closing_loc: ∅ @@ -799,7 +814,7 @@ │ ├── opening_loc: (62,0)-(62,3) = "%i#" │ └── closing_loc: (62,16)-(62,17) = "#" ├── @ ArrayNode (location: (64,0)-(64,17)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 3) │ │ ├── @ StringNode (location: (64,3)-(64,6)) │ │ │ ├── flags: ∅ @@ -822,28 +837,28 @@ │ ├── opening_loc: (64,0)-(64,3) = "%w#" │ └── closing_loc: (64,16)-(64,17) = "#" ├── @ XStringNode (location: (66,0)-(66,17)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (66,0)-(66,3) = "%x#" │ ├── content_loc: (66,3)-(66,16) = "one two three" │ ├── closing_loc: (66,16)-(66,17) = "#" │ └── unescaped: "one two three" ├── @ ArrayNode (location: (69,0)-(69,17)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── elements: (length: 3) │ │ ├── @ SymbolNode (location: (69,3)-(69,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (69,3)-(69,6) = "one" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "one" │ │ ├── @ SymbolNode (location: (69,7)-(69,10)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (69,7)-(69,10) = "two" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "two" │ │ └── @ SymbolNode (location: (69,11)-(69,16)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (69,11)-(69,16) = "three" │ │ ├── closing_loc: ∅ @@ -851,7 +866,7 @@ │ ├── opening_loc: (69,0)-(69,3) = "%i@" │ └── closing_loc: (69,16)-(69,17) = "@" ├── @ ArrayNode (location: (71,0)-(71,17)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 3) │ │ ├── @ StringNode (location: (71,3)-(71,6)) │ │ │ ├── flags: ∅ @@ -874,28 +889,28 @@ │ ├── opening_loc: (71,0)-(71,3) = "%w@" │ └── closing_loc: (71,16)-(71,17) = "@" ├── @ XStringNode (location: (73,0)-(73,17)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (73,0)-(73,3) = "%x@" │ ├── content_loc: (73,3)-(73,16) = "one two three" │ ├── closing_loc: (73,16)-(73,17) = "@" │ └── unescaped: "one two three" ├── @ ArrayNode (location: (76,0)-(76,17)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── elements: (length: 3) │ │ ├── @ SymbolNode (location: (76,3)-(76,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (76,3)-(76,6) = "one" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "one" │ │ ├── @ SymbolNode (location: (76,7)-(76,10)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (76,7)-(76,10) = "two" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "two" │ │ └── @ SymbolNode (location: (76,11)-(76,16)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (76,11)-(76,16) = "three" │ │ ├── closing_loc: ∅ @@ -903,7 +918,7 @@ │ ├── opening_loc: (76,0)-(76,3) = "%i{" │ └── closing_loc: (76,16)-(76,17) = "}" ├── @ ArrayNode (location: (78,0)-(78,17)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 3) │ │ ├── @ StringNode (location: (78,3)-(78,6)) │ │ │ ├── flags: ∅ @@ -926,13 +941,13 @@ │ ├── opening_loc: (78,0)-(78,3) = "%w{" │ └── closing_loc: (78,16)-(78,17) = "}" ├── @ XStringNode (location: (80,0)-(80,17)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (80,0)-(80,3) = "%x{" │ ├── content_loc: (80,3)-(80,16) = "one two three" │ ├── closing_loc: (80,16)-(80,17) = "}" │ └── unescaped: "one two three" ├── @ ArrayNode (location: (82,0)-(82,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 1) │ │ └── @ StringNode (location: (82,3)-(82,6)) │ │ ├── flags: ∅ @@ -943,7 +958,7 @@ │ ├── opening_loc: (82,0)-(82,3) = "%w[" │ └── closing_loc: (82,6)-(82,7) = "]" ├── @ IndexOperatorWriteNode (location: (84,0)-(84,10)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (84,0)-(84,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -964,10 +979,10 @@ │ ├── binary_operator_loc: (84,6)-(84,8) = "+=" │ └── value: │ @ IntegerNode (location: (84,9)-(84,10)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ IndexOrWriteNode (location: (86,0)-(86,11)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (86,0)-(86,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -987,10 +1002,10 @@ │ ├── operator_loc: (86,6)-(86,9) = "||=" │ └── value: │ @ IntegerNode (location: (86,10)-(86,11)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ IndexAndWriteNode (location: (88,0)-(88,11)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (88,0)-(88,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1010,10 +1025,10 @@ │ ├── operator_loc: (88,6)-(88,9) = "&&=" │ └── value: │ @ IntegerNode (location: (88,10)-(88,11)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ IndexOperatorWriteNode (location: (90,0)-(90,14)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (90,0)-(90,7)) │ │ ├── flags: ∅ @@ -1044,10 +1059,10 @@ │ ├── binary_operator_loc: (90,10)-(90,12) = "+=" │ └── value: │ @ IntegerNode (location: (90,13)-(90,14)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ IndexOrWriteNode (location: (92,0)-(92,15)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (92,0)-(92,7)) │ │ ├── flags: ∅ @@ -1077,10 +1092,10 @@ │ ├── operator_loc: (92,10)-(92,13) = "||=" │ └── value: │ @ IntegerNode (location: (92,14)-(92,15)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ IndexAndWriteNode (location: (94,0)-(94,15)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (94,0)-(94,7)) │ │ ├── flags: ∅ @@ -1110,10 +1125,10 @@ │ ├── operator_loc: (94,10)-(94,13) = "&&=" │ └── value: │ @ IntegerNode (location: (94,14)-(94,15)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ IndexOperatorWriteNode (location: (96,0)-(96,13)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (96,0)-(96,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1147,10 +1162,10 @@ │ ├── binary_operator_loc: (96,9)-(96,11) = "+=" │ └── value: │ @ IntegerNode (location: (96,12)-(96,13)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ IndexOrWriteNode (location: (98,0)-(98,14)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (98,0)-(98,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1183,10 +1198,10 @@ │ ├── operator_loc: (98,9)-(98,12) = "||=" │ └── value: │ @ IntegerNode (location: (98,13)-(98,14)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ IndexAndWriteNode (location: (100,0)-(100,14)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (100,0)-(100,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1219,10 +1234,10 @@ │ ├── operator_loc: (100,9)-(100,12) = "&&=" │ └── value: │ @ IntegerNode (location: (100,13)-(100,14)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ IndexOperatorWriteNode (location: (102,0)-(102,17)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (102,0)-(102,7)) │ │ ├── flags: ∅ @@ -1266,10 +1281,10 @@ │ ├── binary_operator_loc: (102,13)-(102,15) = "+=" │ └── value: │ @ IntegerNode (location: (102,16)-(102,17)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ IndexOrWriteNode (location: (104,0)-(104,18)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (104,0)-(104,7)) │ │ ├── flags: ∅ @@ -1312,10 +1327,10 @@ │ ├── operator_loc: (104,13)-(104,16) = "||=" │ └── value: │ @ IntegerNode (location: (104,17)-(104,18)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ IndexAndWriteNode (location: (106,0)-(106,18)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (106,0)-(106,7)) │ │ ├── flags: ∅ @@ -1358,14 +1373,16 @@ │ ├── operator_loc: (106,13)-(106,16) = "&&=" │ └── value: │ @ IntegerNode (location: (106,17)-(106,18)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ DefNode (location: (108,0)-(108,19)) + │ ├── flags: newline │ ├── name: :f │ ├── name_loc: (108,4)-(108,5) = "f" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (108,6)-(108,7)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: @@ -1380,9 +1397,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (108,10)-(108,14)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (108,10)-(108,14)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ CallNode (location: (108,10)-(108,11)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -1403,6 +1421,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ SplatNode (location: (108,12)-(108,13)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (108,12)-(108,13) = "*" │ │ │ └── expression: ∅ │ │ ├── closing_loc: (108,13)-(108,14) = "]" @@ -1415,11 +1434,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (108,16)-(108,19) = "end" ├── @ DefNode (location: (110,0)-(110,22)) + │ ├── flags: newline │ ├── name: :f │ ├── name_loc: (110,4)-(110,5) = "f" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (110,6)-(110,7)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: @@ -1434,9 +1455,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (110,10)-(110,17)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (110,10)-(110,17)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ CallNode (location: (110,10)-(110,11)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -1457,9 +1479,10 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 2) │ │ │ ├── @ IntegerNode (location: (110,12)-(110,13)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── @ SplatNode (location: (110,15)-(110,16)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (110,15)-(110,16) = "*" │ │ │ └── expression: ∅ │ │ ├── closing_loc: (110,16)-(110,17) = "]" @@ -1472,11 +1495,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (110,19)-(110,22) = "end" ├── @ DefNode (location: (112,0)-(112,23)) + │ ├── flags: newline │ ├── name: :f │ ├── name_loc: (112,4)-(112,5) = "f" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (112,6)-(112,7)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: @@ -1491,9 +1516,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (112,10)-(112,18)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (112,10)-(112,18)) - │ │ ├── flags: attribute_write + │ │ ├── flags: newline, attribute_write │ │ ├── receiver: │ │ │ @ CallNode (location: (112,10)-(112,11)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -1514,10 +1540,11 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 2) │ │ │ ├── @ SplatNode (location: (112,12)-(112,13)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (112,12)-(112,13) = "*" │ │ │ │ └── expression: ∅ │ │ │ └── @ IntegerNode (location: (112,17)-(112,18)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── closing_loc: (112,13)-(112,14) = "]" │ │ └── block: ∅ @@ -1529,11 +1556,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (112,20)-(112,23) = "end" ├── @ DefNode (location: (114,0)-(114,26)) + │ ├── flags: newline │ ├── name: :f │ ├── name_loc: (114,4)-(114,5) = "f" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (114,6)-(114,7)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: @@ -1548,9 +1577,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (114,10)-(114,21)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (114,10)-(114,21)) - │ │ ├── flags: attribute_write + │ │ ├── flags: newline, attribute_write │ │ ├── receiver: │ │ │ @ CallNode (location: (114,10)-(114,11)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -1571,13 +1601,14 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 3) │ │ │ ├── @ IntegerNode (location: (114,12)-(114,13)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── @ SplatNode (location: (114,15)-(114,16)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (114,15)-(114,16) = "*" │ │ │ │ └── expression: ∅ │ │ │ └── @ IntegerNode (location: (114,20)-(114,21)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── closing_loc: (114,16)-(114,17) = "]" │ │ └── block: ∅ @@ -1589,11 +1620,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (114,23)-(114,26) = "end" ├── @ DefNode (location: (116,0)-(116,24)) + │ ├── flags: newline │ ├── name: :f │ ├── name_loc: (116,4)-(116,5) = "f" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (116,6)-(116,7)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: @@ -1608,9 +1641,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (116,10)-(116,19)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IndexOperatorWriteNode (location: (116,10)-(116,19)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ CallNode (location: (116,10)-(116,11)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -1629,6 +1663,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ SplatNode (location: (116,12)-(116,13)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (116,12)-(116,13) = "*" │ │ │ └── expression: ∅ │ │ ├── closing_loc: (116,13)-(116,14) = "]" @@ -1637,7 +1672,7 @@ │ │ ├── binary_operator_loc: (116,15)-(116,17) = "+=" │ │ └── value: │ │ @ IntegerNode (location: (116,18)-(116,19)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── locals: [] │ ├── def_keyword_loc: (116,0)-(116,3) = "def" @@ -1647,11 +1682,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (116,21)-(116,24) = "end" ├── @ DefNode (location: (118,0)-(118,28)) + │ ├── flags: newline │ ├── name: :f │ ├── name_loc: (118,4)-(118,5) = "f" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (118,6)-(118,7)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: @@ -1666,9 +1703,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (118,10)-(118,23)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IndexAndWriteNode (location: (118,10)-(118,23)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ CallNode (location: (118,10)-(118,11)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -1687,9 +1725,10 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 2) │ │ │ ├── @ IntegerNode (location: (118,12)-(118,13)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── @ SplatNode (location: (118,15)-(118,16)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (118,15)-(118,16) = "*" │ │ │ └── expression: ∅ │ │ ├── closing_loc: (118,16)-(118,17) = "]" @@ -1697,7 +1736,7 @@ │ │ ├── operator_loc: (118,18)-(118,21) = "&&=" │ │ └── value: │ │ @ IntegerNode (location: (118,22)-(118,23)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── locals: [] │ ├── def_keyword_loc: (118,0)-(118,3) = "def" @@ -1707,11 +1746,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (118,25)-(118,28) = "end" ├── @ DefNode (location: (120,0)-(120,29)) + │ ├── flags: newline │ ├── name: :f │ ├── name_loc: (120,4)-(120,5) = "f" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (120,6)-(120,7)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: @@ -1726,10 +1767,12 @@ │ │ └── block: ∅ │ ├── body: │ │ @ BeginNode (location: (120,0)-(120,29)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (120,10)-(120,24)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (120,10)-(120,16) = "rescue" │ │ │ ├── exceptions: (length: 0) │ │ │ ├── operator_loc: (120,17)-(120,19) = "=>" @@ -1753,6 +1796,7 @@ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── arguments: (length: 1) │ │ │ │ │ └── @ SplatNode (location: (120,22)-(120,23)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── operator_loc: (120,22)-(120,23) = "*" │ │ │ │ │ └── expression: ∅ │ │ │ │ ├── closing_loc: (120,23)-(120,24) = "]" @@ -1770,11 +1814,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (120,26)-(120,29) = "end" └── @ DefNode (location: (122,0)-(122,32)) + ├── flags: newline ├── name: :f ├── name_loc: (122,4)-(122,5) = "f" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (122,6)-(122,7)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: @@ -1789,10 +1835,12 @@ │ └── block: ∅ ├── body: │ @ BeginNode (location: (122,0)-(122,32)) + │ ├── flags: ∅ │ ├── begin_keyword_loc: ∅ │ ├── statements: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (122,10)-(122,27)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (122,10)-(122,16) = "rescue" │ │ ├── exceptions: (length: 0) │ │ ├── operator_loc: (122,17)-(122,19) = "=>" @@ -1816,9 +1864,10 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 2) │ │ │ │ ├── @ IntegerNode (location: (122,22)-(122,23)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ └── @ SplatNode (location: (122,25)-(122,26)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (122,25)-(122,26) = "*" │ │ │ │ └── expression: ∅ │ │ │ ├── closing_loc: (122,26)-(122,27) = "]" diff --git a/test/prism/snapshots/begin_ensure.txt b/test/prism/snapshots/begin_ensure.txt index 9af9b9e57365cc..2f127cd11f8e75 100644 --- a/test/prism/snapshots/begin_ensure.txt +++ b/test/prism/snapshots/begin_ensure.txt @@ -1,15 +1,19 @@ @ ProgramNode (location: (1,0)-(21,15)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(21,15)) + ├── flags: ∅ └── body: (length: 5) ├── @ BeginNode (location: (1,0)-(5,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (1,0)-(1,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (2,0)-(2,1)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (2,0)-(2,1)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -22,12 +26,14 @@ │ ├── else_clause: ∅ │ ├── ensure_clause: │ │ @ EnsureNode (location: (3,0)-(5,3)) + │ │ ├── flags: ∅ │ │ ├── ensure_keyword_loc: (3,0)-(3,6) = "ensure" │ │ ├── statements: │ │ │ @ StatementsNode (location: (4,0)-(4,1)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (4,0)-(4,1)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -39,12 +45,14 @@ │ │ └── end_keyword_loc: (5,0)-(5,3) = "end" │ └── end_keyword_loc: (5,0)-(5,3) = "end" ├── @ BeginNode (location: (7,0)-(7,24)) + │ ├── flags: newline │ ├── begin_keyword_loc: (7,0)-(7,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (7,7)-(7,8)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (7,7)-(7,8)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -57,12 +65,14 @@ │ ├── else_clause: ∅ │ ├── ensure_clause: │ │ @ EnsureNode (location: (7,10)-(7,24)) + │ │ ├── flags: ∅ │ │ ├── ensure_keyword_loc: (7,10)-(7,16) = "ensure" │ │ ├── statements: │ │ │ @ StatementsNode (location: (7,18)-(7,19)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (7,18)-(7,19)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -74,12 +84,14 @@ │ │ └── end_keyword_loc: (7,21)-(7,24) = "end" │ └── end_keyword_loc: (7,21)-(7,24) = "end" ├── @ BeginNode (location: (9,0)-(11,4)) + │ ├── flags: newline │ ├── begin_keyword_loc: (9,0)-(9,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (9,6)-(9,7)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (9,6)-(9,7)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -92,12 +104,14 @@ │ ├── else_clause: ∅ │ ├── ensure_clause: │ │ @ EnsureNode (location: (10,1)-(11,4)) + │ │ ├── flags: ∅ │ │ ├── ensure_keyword_loc: (10,1)-(10,7) = "ensure" │ │ ├── statements: │ │ │ @ StatementsNode (location: (10,8)-(10,9)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (10,8)-(10,9)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -109,12 +123,14 @@ │ │ └── end_keyword_loc: (11,1)-(11,4) = "end" │ └── end_keyword_loc: (11,1)-(11,4) = "end" ├── @ BeginNode (location: (13,0)-(13,22)) + │ ├── flags: newline │ ├── begin_keyword_loc: (13,0)-(13,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (13,6)-(13,7)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (13,6)-(13,7)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -127,12 +143,14 @@ │ ├── else_clause: ∅ │ ├── ensure_clause: │ │ @ EnsureNode (location: (13,9)-(13,22)) + │ │ ├── flags: ∅ │ │ ├── ensure_keyword_loc: (13,9)-(13,15) = "ensure" │ │ ├── statements: │ │ │ @ StatementsNode (location: (13,16)-(13,17)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (13,16)-(13,17)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -144,20 +162,24 @@ │ │ └── end_keyword_loc: (13,19)-(13,22) = "end" │ └── end_keyword_loc: (13,19)-(13,22) = "end" └── @ BeginNode (location: (15,0)-(21,15)) + ├── flags: newline ├── begin_keyword_loc: (15,0)-(15,5) = "begin" ├── statements: │ @ StatementsNode (location: (15,6)-(21,11)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ BeginNode (location: (15,6)-(21,11)) + │ ├── flags: newline │ ├── begin_keyword_loc: (15,6)-(15,11) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (15,11)-(21,7)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (15,11)-(21,7)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ SymbolNode (location: (15,11)-(15,13)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (15,11)-(15,12) = ":" │ │ │ ├── value_loc: (15,12)-(15,13) = "s" │ │ │ ├── closing_loc: ∅ @@ -171,20 +193,24 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ BeginNode (location: (15,16)-(21,7)) + │ │ │ ├── flags: ∅ │ │ │ ├── begin_keyword_loc: (15,16)-(15,21) = "begin" │ │ │ ├── statements: ∅ │ │ │ ├── rescue_clause: ∅ │ │ │ ├── else_clause: ∅ │ │ │ ├── ensure_clause: │ │ │ │ @ EnsureNode (location: (15,22)-(21,7)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── ensure_keyword_loc: (15,22)-(15,28) = "ensure" │ │ │ │ ├── statements: │ │ │ │ │ @ StatementsNode (location: (15,29)-(21,3)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ CallNode (location: (15,29)-(21,3)) - │ │ │ │ │ ├── flags: ∅ + │ │ │ │ │ ├── flags: newline │ │ │ │ │ ├── receiver: │ │ │ │ │ │ @ ConstantReadNode (location: (15,29)-(15,35)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ └── name: :Module │ │ │ │ │ ├── call_operator_loc: (15,35)-(15,36) = "." │ │ │ │ │ ├── name: :new @@ -194,31 +220,39 @@ │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── block: │ │ │ │ │ @ BlockNode (location: (15,40)-(21,3)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── locals: [] │ │ │ │ │ ├── parameters: ∅ │ │ │ │ │ ├── body: │ │ │ │ │ │ @ StatementsNode (location: (16,2)-(20,5)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ │ └── @ BeginNode (location: (16,2)-(20,5)) + │ │ │ │ │ │ ├── flags: newline │ │ │ │ │ │ ├── begin_keyword_loc: (16,2)-(16,7) = "begin" │ │ │ │ │ │ ├── statements: │ │ │ │ │ │ │ @ StatementsNode (location: (17,4)-(17,9)) + │ │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ │ │ └── @ BreakNode (location: (17,4)-(17,9)) + │ │ │ │ │ │ │ ├── flags: newline │ │ │ │ │ │ │ ├── arguments: ∅ │ │ │ │ │ │ │ └── keyword_loc: (17,4)-(17,9) = "break" │ │ │ │ │ │ ├── rescue_clause: ∅ │ │ │ │ │ │ ├── else_clause: ∅ │ │ │ │ │ │ ├── ensure_clause: │ │ │ │ │ │ │ @ EnsureNode (location: (18,4)-(20,5)) + │ │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ │ ├── ensure_keyword_loc: (18,4)-(18,10) = "ensure" │ │ │ │ │ │ │ ├── statements: │ │ │ │ │ │ │ │ @ StatementsNode (location: (18,11)-(19,7)) + │ │ │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ │ │ │ └── @ CallNode (location: (18,11)-(19,7)) - │ │ │ │ │ │ │ │ ├── flags: ∅ + │ │ │ │ │ │ │ │ ├── flags: newline │ │ │ │ │ │ │ │ ├── receiver: │ │ │ │ │ │ │ │ │ @ ConstantReadNode (location: (18,11)-(18,17)) + │ │ │ │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ │ │ │ └── name: :Module │ │ │ │ │ │ │ │ ├── call_operator_loc: (18,17)-(18,18) = "." │ │ │ │ │ │ │ │ ├── name: :new @@ -228,6 +262,7 @@ │ │ │ │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ │ │ │ └── block: │ │ │ │ │ │ │ │ @ BlockNode (location: (18,22)-(19,7)) + │ │ │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ │ │ ├── locals: [] │ │ │ │ │ │ │ │ ├── parameters: ∅ │ │ │ │ │ │ │ │ ├── body: ∅ diff --git a/test/prism/snapshots/begin_rescue.txt b/test/prism/snapshots/begin_rescue.txt index f624f85c072a73..087d7409e71781 100644 --- a/test/prism/snapshots/begin_rescue.txt +++ b/test/prism/snapshots/begin_rescue.txt @@ -1,15 +1,19 @@ @ ProgramNode (location: (1,0)-(78,3)) +├── flags: ∅ ├── locals: [:ex] └── statements: @ StatementsNode (location: (1,0)-(78,3)) + ├── flags: ∅ └── body: (length: 17) ├── @ BeginNode (location: (1,0)-(1,33)) + │ ├── flags: newline │ ├── begin_keyword_loc: (1,0)-(1,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (1,7)-(1,8)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (1,7)-(1,8)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -20,15 +24,17 @@ │ │ └── block: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (1,10)-(1,19)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (1,10)-(1,16) = "rescue" │ │ ├── exceptions: (length: 0) │ │ ├── operator_loc: ∅ │ │ ├── reference: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (1,18)-(1,19)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (1,18)-(1,19)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -40,12 +46,14 @@ │ │ └── consequent: ∅ │ ├── else_clause: │ │ @ ElseNode (location: (1,21)-(1,33)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (1,21)-(1,25) = "else" │ │ ├── statements: │ │ │ @ StatementsNode (location: (1,27)-(1,28)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (1,27)-(1,28)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :c @@ -58,12 +66,14 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (1,30)-(1,33) = "end" ├── @ BeginNode (location: (3,0)-(3,44)) + │ ├── flags: newline │ ├── begin_keyword_loc: (3,0)-(3,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (3,7)-(3,8)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (3,7)-(3,8)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -74,15 +84,17 @@ │ │ └── block: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (3,10)-(3,19)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (3,10)-(3,16) = "rescue" │ │ ├── exceptions: (length: 0) │ │ ├── operator_loc: ∅ │ │ ├── reference: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (3,18)-(3,19)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (3,18)-(3,19)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -94,12 +106,14 @@ │ │ └── consequent: ∅ │ ├── else_clause: │ │ @ ElseNode (location: (3,21)-(3,36)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (3,21)-(3,25) = "else" │ │ ├── statements: │ │ │ @ StatementsNode (location: (3,27)-(3,28)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (3,27)-(3,28)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :c @@ -111,12 +125,14 @@ │ │ └── end_keyword_loc: (3,30)-(3,36) = "ensure" │ ├── ensure_clause: │ │ @ EnsureNode (location: (3,30)-(3,44)) + │ │ ├── flags: ∅ │ │ ├── ensure_keyword_loc: (3,30)-(3,36) = "ensure" │ │ ├── statements: │ │ │ @ StatementsNode (location: (3,38)-(3,39)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (3,38)-(3,39)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :d @@ -128,12 +144,14 @@ │ │ └── end_keyword_loc: (3,41)-(3,44) = "end" │ └── end_keyword_loc: (3,41)-(3,44) = "end" ├── @ BeginNode (location: (5,0)-(7,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (5,0)-(5,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (6,0)-(6,1)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (6,0)-(6,1)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -147,12 +165,14 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (7,0)-(7,3) = "end" ├── @ BeginNode (location: (9,0)-(9,13)) + │ ├── flags: newline │ ├── begin_keyword_loc: (9,0)-(9,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (9,7)-(9,8)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (9,7)-(9,8)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -166,12 +186,14 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (9,10)-(9,13) = "end" ├── @ BeginNode (location: (11,0)-(12,4)) + │ ├── flags: newline │ ├── begin_keyword_loc: (11,0)-(11,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (11,6)-(11,7)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (11,6)-(11,7)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -185,12 +207,14 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (12,1)-(12,4) = "end" ├── @ BeginNode (location: (14,0)-(14,12)) + │ ├── flags: newline │ ├── begin_keyword_loc: (14,0)-(14,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (14,6)-(14,7)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (14,6)-(14,7)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -204,12 +228,14 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (14,9)-(14,12) = "end" ├── @ BeginNode (location: (16,0)-(24,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (16,0)-(16,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (17,0)-(17,1)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (17,0)-(17,1)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -220,15 +246,17 @@ │ │ └── block: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (18,0)-(23,1)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (18,0)-(18,6) = "rescue" │ │ ├── exceptions: (length: 0) │ │ ├── operator_loc: ∅ │ │ ├── reference: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (19,0)-(19,1)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (19,0)-(19,1)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -239,15 +267,17 @@ │ │ │ └── block: ∅ │ │ └── consequent: │ │ @ RescueNode (location: (20,0)-(23,1)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (20,0)-(20,6) = "rescue" │ │ ├── exceptions: (length: 0) │ │ ├── operator_loc: ∅ │ │ ├── reference: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (21,0)-(21,1)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (21,0)-(21,1)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :c @@ -258,15 +288,17 @@ │ │ │ └── block: ∅ │ │ └── consequent: │ │ @ RescueNode (location: (22,0)-(23,1)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (22,0)-(22,6) = "rescue" │ │ ├── exceptions: (length: 0) │ │ ├── operator_loc: ∅ │ │ ├── reference: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (23,0)-(23,1)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (23,0)-(23,1)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :d @@ -280,12 +312,14 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (24,0)-(24,3) = "end" ├── @ BeginNode (location: (26,0)-(32,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (26,0)-(26,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (27,2)-(27,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (27,2)-(27,3)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -296,20 +330,24 @@ │ │ └── block: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (28,0)-(31,3)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (28,0)-(28,6) = "rescue" │ │ ├── exceptions: (length: 1) │ │ │ └── @ ConstantReadNode (location: (28,7)-(28,16)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Exception │ │ ├── operator_loc: (28,17)-(28,19) = "=>" │ │ ├── reference: │ │ │ @ LocalVariableTargetNode (location: (28,20)-(28,22)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :ex │ │ │ └── depth: 0 │ │ ├── statements: │ │ │ @ StatementsNode (location: (29,2)-(29,3)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (29,2)-(29,3)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -320,22 +358,27 @@ │ │ │ └── block: ∅ │ │ └── consequent: │ │ @ RescueNode (location: (30,0)-(31,3)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (30,0)-(30,6) = "rescue" │ │ ├── exceptions: (length: 2) │ │ │ ├── @ ConstantReadNode (location: (30,7)-(30,23)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :AnotherException │ │ │ └── @ ConstantReadNode (location: (30,25)-(30,41)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :OneMoreException │ │ ├── operator_loc: (30,42)-(30,44) = "=>" │ │ ├── reference: │ │ │ @ LocalVariableTargetNode (location: (30,45)-(30,47)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :ex │ │ │ └── depth: 0 │ │ ├── statements: │ │ │ @ StatementsNode (location: (31,2)-(31,3)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (31,2)-(31,3)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :c @@ -349,12 +392,14 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (32,0)-(32,3) = "end" ├── @ BeginNode (location: (34,0)-(40,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (34,0)-(34,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (35,2)-(35,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (35,2)-(35,3)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -365,20 +410,24 @@ │ │ └── block: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (36,0)-(37,3)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (36,0)-(36,6) = "rescue" │ │ ├── exceptions: (length: 1) │ │ │ └── @ ConstantReadNode (location: (36,7)-(36,16)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Exception │ │ ├── operator_loc: (36,17)-(36,19) = "=>" │ │ ├── reference: │ │ │ @ LocalVariableTargetNode (location: (36,20)-(36,22)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :ex │ │ │ └── depth: 0 │ │ ├── statements: │ │ │ @ StatementsNode (location: (37,2)-(37,3)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (37,2)-(37,3)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -391,12 +440,14 @@ │ ├── else_clause: ∅ │ ├── ensure_clause: │ │ @ EnsureNode (location: (38,0)-(40,3)) + │ │ ├── flags: ∅ │ │ ├── ensure_keyword_loc: (38,0)-(38,6) = "ensure" │ │ ├── statements: │ │ │ @ StatementsNode (location: (39,2)-(39,3)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (39,2)-(39,3)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -408,18 +459,20 @@ │ │ └── end_keyword_loc: (40,0)-(40,3) = "end" │ └── end_keyword_loc: (40,0)-(40,3) = "end" ├── @ StringNode (location: (42,0)-(42,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (42,0)-(42,2) = "%!" │ ├── content_loc: (42,2)-(42,5) = "abc" │ ├── closing_loc: (42,5)-(42,6) = "!" │ └── unescaped: "abc" ├── @ BeginNode (location: (44,0)-(48,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (44,0)-(44,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (45,0)-(45,1)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (45,0)-(45,1)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -430,15 +483,17 @@ │ │ └── block: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (46,0)-(47,1)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (46,0)-(46,6) = "rescue" │ │ ├── exceptions: (length: 0) │ │ ├── operator_loc: ∅ │ │ ├── reference: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (47,0)-(47,1)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (47,0)-(47,1)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -452,12 +507,14 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (48,0)-(48,3) = "end" ├── @ BeginNode (location: (50,0)-(50,20)) + │ ├── flags: newline │ ├── begin_keyword_loc: (50,0)-(50,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (50,6)-(50,7)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (50,6)-(50,7)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -468,15 +525,17 @@ │ │ └── block: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (50,8)-(50,16)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (50,8)-(50,14) = "rescue" │ │ ├── exceptions: (length: 0) │ │ ├── operator_loc: ∅ │ │ ├── reference: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (50,15)-(50,16)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (50,15)-(50,16)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -490,12 +549,14 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (50,17)-(50,20) = "end" ├── @ BeginNode (location: (52,0)-(54,5)) + │ ├── flags: newline │ ├── begin_keyword_loc: (52,0)-(52,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (53,0)-(53,1)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (53,0)-(53,1)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -506,15 +567,17 @@ │ │ └── block: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (53,2)-(54,1)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (53,2)-(53,8) = "rescue" │ │ ├── exceptions: (length: 0) │ │ ├── operator_loc: ∅ │ │ ├── reference: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (54,0)-(54,1)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (54,0)-(54,1)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -528,12 +591,14 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (54,2)-(54,5) = "end" ├── @ BeginNode (location: (56,0)-(60,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (56,0)-(56,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (57,0)-(57,1)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (57,0)-(57,1)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -544,17 +609,20 @@ │ │ └── block: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (58,0)-(59,1)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (58,0)-(58,6) = "rescue" │ │ ├── exceptions: (length: 1) │ │ │ └── @ ConstantReadNode (location: (58,7)-(58,16)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Exception │ │ ├── operator_loc: ∅ │ │ ├── reference: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (59,0)-(59,1)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (59,0)-(59,1)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -568,12 +636,14 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (60,0)-(60,3) = "end" ├── @ BeginNode (location: (62,0)-(66,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (62,0)-(62,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (63,0)-(63,1)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (63,0)-(63,1)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -584,19 +654,23 @@ │ │ └── block: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (64,0)-(65,1)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (64,0)-(64,6) = "rescue" │ │ ├── exceptions: (length: 2) │ │ │ ├── @ ConstantReadNode (location: (64,7)-(64,16)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :Exception │ │ │ └── @ ConstantReadNode (location: (64,18)-(64,33)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :CustomException │ │ ├── operator_loc: ∅ │ │ ├── reference: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (65,0)-(65,1)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (65,0)-(65,1)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -610,12 +684,14 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (66,0)-(66,3) = "end" ├── @ BeginNode (location: (68,0)-(72,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (68,0)-(68,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (69,2)-(69,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (69,2)-(69,3)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -626,22 +702,27 @@ │ │ └── block: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (70,0)-(71,3)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (70,0)-(70,6) = "rescue" │ │ ├── exceptions: (length: 2) │ │ │ ├── @ ConstantReadNode (location: (70,7)-(70,16)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :Exception │ │ │ └── @ ConstantReadNode (location: (70,18)-(70,33)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :CustomException │ │ ├── operator_loc: (70,34)-(70,36) = "=>" │ │ ├── reference: │ │ │ @ LocalVariableTargetNode (location: (70,37)-(70,39)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :ex │ │ │ └── depth: 0 │ │ ├── statements: │ │ │ @ StatementsNode (location: (71,2)-(71,3)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (71,2)-(71,3)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -655,12 +736,14 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (72,0)-(72,3) = "end" └── @ BeginNode (location: (74,0)-(78,3)) + ├── flags: newline ├── begin_keyword_loc: (74,0)-(74,5) = "begin" ├── statements: │ @ StatementsNode (location: (75,2)-(75,3)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (75,2)-(75,3)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :a @@ -671,20 +754,24 @@ │ └── block: ∅ ├── rescue_clause: │ @ RescueNode (location: (76,0)-(77,3)) + │ ├── flags: ∅ │ ├── keyword_loc: (76,0)-(76,6) = "rescue" │ ├── exceptions: (length: 1) │ │ └── @ ConstantReadNode (location: (76,7)-(76,16)) + │ │ ├── flags: ∅ │ │ └── name: :Exception │ ├── operator_loc: (76,17)-(76,19) = "=>" │ ├── reference: │ │ @ LocalVariableTargetNode (location: (76,20)-(76,22)) + │ │ ├── flags: ∅ │ │ ├── name: :ex │ │ └── depth: 0 │ ├── statements: │ │ @ StatementsNode (location: (77,2)-(77,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (77,2)-(77,3)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :b diff --git a/test/prism/snapshots/blocks.txt b/test/prism/snapshots/blocks.txt index 1c996ebd09ce07..933de3e7be2e57 100644 --- a/test/prism/snapshots/blocks.txt +++ b/test/prism/snapshots/blocks.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(54,17)) +├── flags: ∅ ├── locals: [:fork] └── statements: @ StatementsNode (location: (1,0)-(54,17)) + ├── flags: ∅ └── body: (length: 20) ├── @ CallNode (location: (1,0)-(1,16)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (1,0)-(1,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -37,13 +39,15 @@ │ ├── closing_loc: (1,7)-(1,8) = "]" │ └── block: │ @ BlockNode (location: (1,9)-(1,16)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (1,11)-(1,14)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (1,11)-(1,14)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :baz @@ -55,7 +59,7 @@ │ ├── opening_loc: (1,9)-(1,10) = "{" │ └── closing_loc: (1,15)-(1,16) = "}" ├── @ CallNode (location: (3,0)-(5,3)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (3,0)-(3,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -88,13 +92,15 @@ │ ├── closing_loc: (3,7)-(3,8) = "]" │ └── block: │ @ BlockNode (location: (3,9)-(5,3)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (4,0)-(4,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (4,0)-(4,3)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :baz @@ -106,7 +112,7 @@ │ ├── opening_loc: (3,9)-(3,11) = "do" │ └── closing_loc: (5,0)-(5,3) = "end" ├── @ CallNode (location: (7,0)-(7,35)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (7,0)-(7,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -127,16 +133,19 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (7,9)-(7,10)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 0 │ ├── closing_loc: (7,10)-(7,11) = ")" │ └── block: │ @ BlockNode (location: (7,12)-(7,35)) + │ ├── flags: ∅ │ ├── locals: [:x, :memo] │ ├── parameters: │ │ @ BlockParametersNode (location: (7,14)-(7,23)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (7,15)-(7,22)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 2) │ │ │ │ ├── @ RequiredParameterNode (location: (7,15)-(7,16)) │ │ │ │ │ ├── flags: ∅ @@ -155,12 +164,15 @@ │ │ └── closing_loc: (7,22)-(7,23) = "|" │ ├── body: │ │ @ StatementsNode (location: (7,24)-(7,33)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableOperatorWriteNode (location: (7,24)-(7,33)) + │ │ ├── flags: newline │ │ ├── name_loc: (7,24)-(7,28) = "memo" │ │ ├── binary_operator_loc: (7,29)-(7,31) = "+=" │ │ ├── value: │ │ │ @ LocalVariableReadNode (location: (7,32)-(7,33)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :x │ │ │ └── depth: 0 │ │ ├── name: :memo @@ -169,7 +181,7 @@ │ ├── opening_loc: (7,12)-(7,13) = "{" │ └── closing_loc: (7,34)-(7,35) = "}" ├── @ CallNode (location: (9,0)-(9,10)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -179,13 +191,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (9,4)-(9,10)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: ∅ │ ├── opening_loc: (9,4)-(9,6) = "do" │ └── closing_loc: (9,7)-(9,10) = "end" ├── @ CallNode (location: (11,0)-(11,21)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -206,11 +219,13 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ └── @ ParenthesesNode (location: (11,9)-(11,21)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (11,10)-(11,20)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (11,10)-(11,20)) - │ │ │ ├── flags: ignore_visibility + │ │ │ ├── flags: newline, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :baz @@ -220,6 +235,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: │ │ │ @ BlockNode (location: (11,14)-(11,20)) + │ │ │ ├── flags: ∅ │ │ │ ├── locals: [] │ │ │ ├── parameters: ∅ │ │ │ ├── body: ∅ @@ -230,7 +246,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (13,0)-(13,14)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -253,13 +269,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (13,8)-(13,14)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: ∅ │ ├── opening_loc: (13,8)-(13,10) = "do" │ └── closing_loc: (13,11)-(13,14) = "end" ├── @ CallNode (location: (15,0)-(15,18)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -295,13 +312,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (15,12)-(15,18)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: ∅ │ ├── opening_loc: (15,12)-(15,14) = "do" │ └── closing_loc: (15,15)-(15,18) = "end" ├── @ CallNode (location: (17,0)-(18,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -311,11 +329,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (17,4)-(18,3)) + │ ├── flags: ∅ │ ├── locals: [:a] │ ├── parameters: │ │ @ BlockParametersNode (location: (17,7)-(17,17)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (17,8)-(17,16)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 0) │ │ │ ├── optionals: (length: 1) │ │ │ │ └── @ OptionalParameterNode (location: (17,8)-(17,16)) @@ -346,7 +367,7 @@ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── arguments: (length: 1) │ │ │ │ │ └── @ IntegerNode (location: (17,14)-(17,15)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ ├── closing_loc: (17,15)-(17,16) = "]" │ │ │ │ └── block: ∅ @@ -362,7 +383,7 @@ │ ├── opening_loc: (17,4)-(17,6) = "do" │ └── closing_loc: (18,0)-(18,3) = "end" ├── @ CallNode (location: (20,0)-(22,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -372,14 +393,17 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (20,4)-(22,3)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (20,4)-(22,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (21,0)-(21,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (21,0)-(21,6) = "rescue" │ │ │ ├── exceptions: (length: 0) │ │ │ ├── operator_loc: ∅ @@ -392,7 +416,7 @@ │ ├── opening_loc: (20,4)-(20,6) = "do" │ └── closing_loc: (22,0)-(22,3) = "end" ├── @ CallNode (location: (24,0)-(29,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -402,13 +426,15 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (24,4)-(29,3)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (25,2)-(28,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (25,2)-(28,5)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -418,13 +444,15 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (25,6)-(28,5)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── parameters: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (26,4)-(27,7)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (26,4)-(27,7)) - │ │ │ ├── flags: ignore_visibility + │ │ │ ├── flags: newline, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :baz @@ -434,6 +462,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: │ │ │ @ BlockNode (location: (26,8)-(27,7)) + │ │ │ ├── flags: ∅ │ │ │ ├── locals: [] │ │ │ ├── parameters: ∅ │ │ │ ├── body: ∅ @@ -444,7 +473,7 @@ │ ├── opening_loc: (24,4)-(24,6) = "do" │ └── closing_loc: (29,0)-(29,3) = "end" ├── @ CallNode (location: (31,0)-(31,16)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (31,0)-(31,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -477,13 +506,15 @@ │ ├── closing_loc: (31,7)-(31,8) = "]" │ └── block: │ @ BlockNode (location: (31,9)-(31,16)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (31,11)-(31,14)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (31,11)-(31,14)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :baz @@ -495,7 +526,7 @@ │ ├── opening_loc: (31,9)-(31,10) = "{" │ └── closing_loc: (31,15)-(31,16) = "}" ├── @ CallNode (location: (33,0)-(33,24)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -505,11 +536,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (33,4)-(33,24)) + │ ├── flags: ∅ │ ├── locals: [:x, :y, :z] │ ├── parameters: │ │ @ BlockParametersNode (location: (33,6)-(33,20)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (33,7)-(33,19)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (33,7)-(33,8)) │ │ │ │ ├── flags: ∅ @@ -522,7 +556,7 @@ │ │ │ │ ├── operator_loc: (33,12)-(33,13) = "=" │ │ │ │ └── value: │ │ │ │ @ IntegerNode (location: (33,14)-(33,15)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ ├── rest: ∅ │ │ │ ├── posts: (length: 0) @@ -538,14 +572,16 @@ │ │ └── closing_loc: (33,19)-(33,20) = "|" │ ├── body: │ │ @ StatementsNode (location: (33,21)-(33,22)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (33,21)-(33,22)) + │ │ ├── flags: newline │ │ ├── name: :x │ │ └── depth: 0 │ ├── opening_loc: (33,4)-(33,5) = "{" │ └── closing_loc: (33,23)-(33,24) = "}" ├── @ CallNode (location: (35,0)-(35,11)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -555,11 +591,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (35,4)-(35,11)) + │ ├── flags: ∅ │ ├── locals: [:x] │ ├── parameters: │ │ @ BlockParametersNode (location: (35,6)-(35,9)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (35,7)-(35,8)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (35,7)-(35,8)) │ │ │ │ ├── flags: ∅ @@ -577,16 +616,17 @@ │ ├── opening_loc: (35,4)-(35,5) = "{" │ └── closing_loc: (35,10)-(35,11) = "}" ├── @ LocalVariableWriteNode (location: (37,0)-(37,8)) + │ ├── flags: newline │ ├── name: :fork │ ├── depth: 0 │ ├── name_loc: (37,0)-(37,4) = "fork" │ ├── value: │ │ @ IntegerNode (location: (37,7)-(37,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (37,5)-(37,6) = "=" ├── @ CallNode (location: (38,0)-(39,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :fork @@ -596,11 +636,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (38,5)-(39,3)) + │ ├── flags: ∅ │ ├── locals: [:a] │ ├── parameters: │ │ @ BlockParametersNode (location: (38,8)-(38,11)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (38,9)-(38,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (38,9)-(38,10)) │ │ │ │ ├── flags: ∅ @@ -618,7 +661,7 @@ │ ├── opening_loc: (38,5)-(38,7) = "do" │ └── closing_loc: (39,0)-(39,3) = "end" ├── @ CallNode (location: (41,0)-(41,12)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :fork @@ -628,11 +671,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (41,5)-(41,12)) + │ ├── flags: ∅ │ ├── locals: [:a] │ ├── parameters: │ │ @ BlockParametersNode (location: (41,7)-(41,10)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (41,8)-(41,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (41,8)-(41,9)) │ │ │ │ ├── flags: ∅ @@ -650,7 +696,7 @@ │ ├── opening_loc: (41,5)-(41,6) = "{" │ └── closing_loc: (41,11)-(41,12) = "}" ├── @ CallNode (location: (43,0)-(44,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :C @@ -660,13 +706,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (43,2)-(44,3)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: ∅ │ ├── opening_loc: (43,2)-(43,4) = "do" │ └── closing_loc: (44,0)-(44,3) = "end" ├── @ CallNode (location: (46,0)-(46,4)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :C @@ -676,13 +723,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (46,2)-(46,4)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: ∅ │ ├── opening_loc: (46,2)-(46,3) = "{" │ └── closing_loc: (46,3)-(46,4) = "}" ├── @ CallNode (location: (48,0)-(52,1)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -703,11 +751,14 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (48,11)-(52,1)) + │ │ ├── flags: ∅ │ │ ├── locals: [:a, :b] │ │ ├── parameters: │ │ │ @ BlockParametersNode (location: (48,13)-(51,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── parameters: │ │ │ │ @ ParametersNode (location: (49,2)-(50,6)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── requireds: (length: 0) │ │ │ │ ├── optionals: (length: 0) │ │ │ │ ├── rest: ∅ @@ -719,7 +770,7 @@ │ │ │ │ │ │ ├── name_loc: (49,2)-(49,4) = "a:" │ │ │ │ │ │ └── value: │ │ │ │ │ │ @ IntegerNode (location: (49,5)-(49,6)) - │ │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ │ └── value: 1 │ │ │ │ │ └── @ OptionalKeywordParameterNode (location: (50,2)-(50,6)) │ │ │ │ │ ├── flags: ∅ @@ -727,7 +778,7 @@ │ │ │ │ │ ├── name_loc: (50,2)-(50,4) = "b:" │ │ │ │ │ └── value: │ │ │ │ │ @ IntegerNode (location: (50,5)-(50,6)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 2 │ │ │ │ ├── keyword_rest: ∅ │ │ │ │ └── block: ∅ @@ -740,7 +791,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ └── @ CallNode (location: (54,0)-(54,17)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :foo @@ -750,11 +801,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (54,4)-(54,17)) + ├── flags: ∅ ├── locals: [:bar] ├── parameters: │ @ BlockParametersNode (location: (54,7)-(54,13)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (54,8)-(54,12)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (54,8)-(54,11)) │ │ │ ├── flags: ∅ @@ -762,6 +816,7 @@ │ │ ├── optionals: (length: 0) │ │ ├── rest: │ │ │ @ ImplicitRestNode (location: (54,11)-(54,12)) + │ │ │ └── flags: ∅ │ │ ├── posts: (length: 0) │ │ ├── keywords: (length: 0) │ │ ├── keyword_rest: ∅ diff --git a/test/prism/snapshots/boolean_operators.txt b/test/prism/snapshots/boolean_operators.txt index 3bf33430c9b145..bde70d650901f1 100644 --- a/test/prism/snapshots/boolean_operators.txt +++ b/test/prism/snapshots/boolean_operators.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(5,7)) +├── flags: ∅ ├── locals: [:a] └── statements: @ StatementsNode (location: (1,0)-(5,7)) + ├── flags: ∅ └── body: (length: 3) ├── @ LocalVariableAndWriteNode (location: (1,0)-(1,7)) + │ ├── flags: newline │ ├── name_loc: (1,0)-(1,1) = "a" │ ├── operator_loc: (1,2)-(1,5) = "&&=" │ ├── value: @@ -20,6 +23,7 @@ │ ├── name: :a │ └── depth: 0 ├── @ LocalVariableOperatorWriteNode (location: (3,0)-(3,6)) + │ ├── flags: newline │ ├── name_loc: (3,0)-(3,1) = "a" │ ├── binary_operator_loc: (3,2)-(3,4) = "+=" │ ├── value: @@ -37,6 +41,7 @@ │ ├── binary_operator: :+ │ └── depth: 0 └── @ LocalVariableOrWriteNode (location: (5,0)-(5,7)) + ├── flags: newline ├── name_loc: (5,0)-(5,1) = "a" ├── operator_loc: (5,2)-(5,5) = "||=" ├── value: diff --git a/test/prism/snapshots/booleans.txt b/test/prism/snapshots/booleans.txt index 47319662439221..47ce80217a06cc 100644 --- a/test/prism/snapshots/booleans.txt +++ b/test/prism/snapshots/booleans.txt @@ -1,7 +1,11 @@ @ ProgramNode (location: (1,0)-(3,4)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,4)) + ├── flags: ∅ └── body: (length: 2) ├── @ FalseNode (location: (1,0)-(1,5)) + │ └── flags: newline, static_literal └── @ TrueNode (location: (3,0)-(3,4)) + └── flags: newline, static_literal diff --git a/test/prism/snapshots/break.txt b/test/prism/snapshots/break.txt index 7d5bf5e69a6cf3..469b603f849f21 100644 --- a/test/prism/snapshots/break.txt +++ b/test/prism/snapshots/break.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(29,21)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(29,21)) + ├── flags: ∅ └── body: (length: 13) ├── @ CallNode (location: (1,0)-(1,13)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -14,18 +16,21 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (1,4)-(1,13)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (1,6)-(1,11)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ BreakNode (location: (1,6)-(1,11)) + │ │ ├── flags: newline │ │ ├── arguments: ∅ │ │ └── keyword_loc: (1,6)-(1,11) = "break" │ ├── opening_loc: (1,4)-(1,5) = "{" │ └── closing_loc: (1,12)-(1,13) = "}" ├── @ CallNode (location: (3,0)-(3,27)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -35,40 +40,49 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (3,4)-(3,27)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (3,6)-(3,25)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ BreakNode (location: (3,6)-(3,25)) + │ │ ├── flags: newline │ │ ├── arguments: │ │ │ @ ArgumentsNode (location: (3,12)-(3,25)) │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 3) │ │ │ ├── @ ParenthesesNode (location: (3,12)-(3,15)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── body: │ │ │ │ │ @ StatementsNode (location: (3,13)-(3,14)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ IntegerNode (location: (3,13)-(3,14)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: newline, static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ ├── opening_loc: (3,12)-(3,13) = "(" │ │ │ │ └── closing_loc: (3,14)-(3,15) = ")" │ │ │ ├── @ ParenthesesNode (location: (3,17)-(3,20)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── body: │ │ │ │ │ @ StatementsNode (location: (3,18)-(3,19)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ IntegerNode (location: (3,18)-(3,19)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: newline, static_literal, decimal │ │ │ │ │ └── value: 2 │ │ │ │ ├── opening_loc: (3,17)-(3,18) = "(" │ │ │ │ └── closing_loc: (3,19)-(3,20) = ")" │ │ │ └── @ ParenthesesNode (location: (3,22)-(3,25)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (3,23)-(3,24)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ IntegerNode (location: (3,23)-(3,24)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: newline, static_literal, decimal │ │ │ │ └── value: 3 │ │ │ ├── opening_loc: (3,22)-(3,23) = "(" │ │ │ └── closing_loc: (3,24)-(3,25) = ")" @@ -76,7 +90,7 @@ │ ├── opening_loc: (3,4)-(3,5) = "{" │ └── closing_loc: (3,26)-(3,27) = "}" ├── @ CallNode (location: (5,0)-(5,15)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -86,24 +100,27 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (5,4)-(5,15)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (5,6)-(5,13)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ BreakNode (location: (5,6)-(5,13)) + │ │ ├── flags: newline │ │ ├── arguments: │ │ │ @ ArgumentsNode (location: (5,12)-(5,13)) │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (5,12)-(5,13)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── keyword_loc: (5,6)-(5,11) = "break" │ ├── opening_loc: (5,4)-(5,5) = "{" │ └── closing_loc: (5,14)-(5,15) = "}" ├── @ CallNode (location: (7,0)-(8,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -113,30 +130,33 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (7,4)-(8,3)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (7,6)-(8,1)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ BreakNode (location: (7,6)-(8,1)) + │ │ ├── flags: newline │ │ ├── arguments: │ │ │ @ ArgumentsNode (location: (7,12)-(8,1)) │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 3) │ │ │ ├── @ IntegerNode (location: (7,12)-(7,13)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── @ IntegerNode (location: (7,15)-(7,16)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ └── @ IntegerNode (location: (8,0)-(8,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 3 │ │ └── keyword_loc: (7,6)-(7,11) = "break" │ ├── opening_loc: (7,4)-(7,5) = "{" │ └── closing_loc: (8,2)-(8,3) = "}" ├── @ CallNode (location: (10,0)-(10,21)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -146,30 +166,33 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (10,4)-(10,21)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (10,6)-(10,19)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ BreakNode (location: (10,6)-(10,19)) + │ │ ├── flags: newline │ │ ├── arguments: │ │ │ @ ArgumentsNode (location: (10,12)-(10,19)) │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 3) │ │ │ ├── @ IntegerNode (location: (10,12)-(10,13)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── @ IntegerNode (location: (10,15)-(10,16)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ └── @ IntegerNode (location: (10,18)-(10,19)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 3 │ │ └── keyword_loc: (10,6)-(10,11) = "break" │ ├── opening_loc: (10,4)-(10,5) = "{" │ └── closing_loc: (10,20)-(10,21) = "}" ├── @ CallNode (location: (12,0)-(12,23)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -179,27 +202,30 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (12,4)-(12,23)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (12,6)-(12,21)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ BreakNode (location: (12,6)-(12,21)) + │ │ ├── flags: newline │ │ ├── arguments: │ │ │ @ ArgumentsNode (location: (12,12)-(12,21)) │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ ArrayNode (location: (12,12)-(12,21)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: static_literal │ │ │ ├── elements: (length: 3) │ │ │ │ ├── @ IntegerNode (location: (12,13)-(12,14)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ ├── @ IntegerNode (location: (12,16)-(12,17)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 2 │ │ │ │ └── @ IntegerNode (location: (12,19)-(12,20)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 3 │ │ │ ├── opening_loc: (12,12)-(12,13) = "[" │ │ │ └── closing_loc: (12,20)-(12,21) = "]" @@ -207,7 +233,7 @@ │ ├── opening_loc: (12,4)-(12,5) = "{" │ └── closing_loc: (12,22)-(12,23) = "}" ├── @ CallNode (location: (14,0)-(17,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -217,25 +243,30 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (14,4)-(17,3)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (14,6)-(17,1)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ BreakNode (location: (14,6)-(17,1)) + │ │ ├── flags: newline │ │ ├── arguments: │ │ │ @ ArgumentsNode (location: (14,11)-(17,1)) │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ ParenthesesNode (location: (14,11)-(17,1)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (15,2)-(16,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 2) │ │ │ │ ├── @ IntegerNode (location: (15,2)-(15,3)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: newline, static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ └── @ IntegerNode (location: (16,2)-(16,3)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: newline, static_literal, decimal │ │ │ │ └── value: 2 │ │ │ ├── opening_loc: (14,11)-(14,12) = "(" │ │ │ └── closing_loc: (17,0)-(17,1) = ")" @@ -243,7 +274,7 @@ │ ├── opening_loc: (14,4)-(14,5) = "{" │ └── closing_loc: (17,2)-(17,3) = "}" ├── @ CallNode (location: (19,0)-(19,15)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -253,17 +284,21 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (19,4)-(19,15)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (19,6)-(19,13)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ BreakNode (location: (19,6)-(19,13)) + │ │ ├── flags: newline │ │ ├── arguments: │ │ │ @ ArgumentsNode (location: (19,11)-(19,13)) │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ ParenthesesNode (location: (19,11)-(19,13)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: ∅ │ │ │ ├── opening_loc: (19,11)-(19,12) = "(" │ │ │ └── closing_loc: (19,12)-(19,13) = ")" @@ -271,7 +306,7 @@ │ ├── opening_loc: (19,4)-(19,5) = "{" │ └── closing_loc: (19,14)-(19,15) = "}" ├── @ CallNode (location: (21,0)-(21,16)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -281,22 +316,27 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (21,4)-(21,16)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (21,6)-(21,14)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ BreakNode (location: (21,6)-(21,14)) + │ │ ├── flags: newline │ │ ├── arguments: │ │ │ @ ArgumentsNode (location: (21,11)-(21,14)) │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ ParenthesesNode (location: (21,11)-(21,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (21,12)-(21,13)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ IntegerNode (location: (21,12)-(21,13)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: newline, static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── opening_loc: (21,11)-(21,12) = "(" │ │ │ └── closing_loc: (21,13)-(21,14) = ")" @@ -304,7 +344,7 @@ │ ├── opening_loc: (21,4)-(21,5) = "{" │ └── closing_loc: (21,15)-(21,16) = "}" ├── @ CallNode (location: (23,0)-(23,22)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (23,0)-(23,16)) │ │ ├── flags: ignore_visibility @@ -317,18 +357,21 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (23,4)-(23,16)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── parameters: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (23,6)-(23,14)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ BreakNode (location: (23,6)-(23,14)) + │ │ │ ├── flags: newline │ │ │ ├── arguments: │ │ │ │ @ ArgumentsNode (location: (23,12)-(23,14)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ IntegerNode (location: (23,12)-(23,14)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 42 │ │ │ └── keyword_loc: (23,6)-(23,11) = "break" │ │ ├── opening_loc: (23,4)-(23,5) = "{" @@ -342,12 +385,12 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (23,20)-(23,22)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 42 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (25,0)-(25,23)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (25,0)-(25,17)) │ │ ├── flags: ignore_visibility @@ -360,11 +403,14 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (25,4)-(25,17)) + │ │ ├── flags: ∅ │ │ ├── locals: [:a] │ │ ├── parameters: │ │ │ @ BlockParametersNode (location: (25,6)-(25,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── parameters: │ │ │ │ @ ParametersNode (location: (25,7)-(25,8)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── requireds: (length: 1) │ │ │ │ │ └── @ RequiredParameterNode (location: (25,7)-(25,8)) │ │ │ │ │ ├── flags: ∅ @@ -380,8 +426,10 @@ │ │ │ └── closing_loc: (25,8)-(25,9) = "|" │ │ ├── body: │ │ │ @ StatementsNode (location: (25,10)-(25,15)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ BreakNode (location: (25,10)-(25,15)) + │ │ │ ├── flags: newline │ │ │ ├── arguments: ∅ │ │ │ └── keyword_loc: (25,10)-(25,15) = "break" │ │ ├── opening_loc: (25,4)-(25,5) = "{" @@ -395,16 +443,17 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (25,21)-(25,23)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 42 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ WhileNode (location: (27,0)-(27,21)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── keyword_loc: (27,0)-(27,5) = "while" │ ├── closing_loc: (27,18)-(27,21) = "end" │ ├── predicate: │ │ @ AndNode (location: (27,6)-(27,16)) + │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ CallNode (location: (27,6)-(27,7)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -418,16 +467,18 @@ │ │ │ └── block: ∅ │ │ ├── right: │ │ │ @ BreakNode (location: (27,11)-(27,16)) + │ │ │ ├── flags: ∅ │ │ │ ├── arguments: ∅ │ │ │ └── keyword_loc: (27,11)-(27,16) = "break" │ │ └── operator_loc: (27,8)-(27,10) = "&&" │ └── statements: ∅ └── @ UntilNode (location: (29,0)-(29,21)) - ├── flags: ∅ + ├── flags: newline ├── keyword_loc: (29,0)-(29,5) = "until" ├── closing_loc: (29,18)-(29,21) = "end" ├── predicate: │ @ AndNode (location: (29,6)-(29,16)) + │ ├── flags: ∅ │ ├── left: │ │ @ CallNode (location: (29,6)-(29,7)) │ │ ├── flags: variable_call, ignore_visibility @@ -441,6 +492,7 @@ │ │ └── block: ∅ │ ├── right: │ │ @ BreakNode (location: (29,11)-(29,16)) + │ │ ├── flags: ∅ │ │ ├── arguments: ∅ │ │ └── keyword_loc: (29,11)-(29,16) = "break" │ └── operator_loc: (29,8)-(29,10) = "&&" diff --git a/test/prism/snapshots/case.txt b/test/prism/snapshots/case.txt index 417bf9492a4d29..14a2f79e45f2e8 100644 --- a/test/prism/snapshots/case.txt +++ b/test/prism/snapshots/case.txt @@ -1,22 +1,26 @@ @ ProgramNode (location: (1,0)-(55,3)) +├── flags: ∅ ├── locals: [:b] └── statements: @ StatementsNode (location: (1,0)-(55,3)) + ├── flags: ∅ └── body: (length: 15) ├── @ CaseNode (location: (1,0)-(3,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (1,5)-(1,8)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,5)-(1,6) = ":" │ │ ├── value_loc: (1,6)-(1,8) = "hi" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "hi" │ ├── conditions: (length: 1) │ │ └── @ WhenNode (location: (2,0)-(2,8)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (2,0)-(2,4) = "when" │ │ ├── conditions: (length: 1) │ │ │ └── @ SymbolNode (location: (2,5)-(2,8)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (2,5)-(2,6) = ":" │ │ │ ├── value_loc: (2,6)-(2,8) = "hi" │ │ │ ├── closing_loc: ∅ @@ -27,19 +31,24 @@ │ ├── case_keyword_loc: (1,0)-(1,4) = "case" │ └── end_keyword_loc: (3,0)-(3,3) = "end" ├── @ CaseNode (location: (5,0)-(5,58)) + │ ├── flags: newline │ ├── predicate: │ │ @ TrueNode (location: (5,5)-(5,9)) + │ │ └── flags: static_literal │ ├── conditions: (length: 2) │ │ ├── @ WhenNode (location: (5,11)-(5,30)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (5,11)-(5,15) = "when" │ │ │ ├── conditions: (length: 1) │ │ │ │ └── @ TrueNode (location: (5,16)-(5,20)) + │ │ │ │ └── flags: static_literal │ │ │ ├── then_keyword_loc: ∅ │ │ │ └── statements: │ │ │ @ StatementsNode (location: (5,22)-(5,30)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (5,22)-(5,30)) - │ │ │ ├── flags: ignore_visibility + │ │ │ ├── flags: newline, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :puts @@ -50,7 +59,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ SymbolNode (location: (5,27)-(5,30)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (5,27)-(5,28) = ":" │ │ │ │ ├── value_loc: (5,28)-(5,30) = "hi" │ │ │ │ ├── closing_loc: ∅ @@ -58,15 +67,18 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ └── @ WhenNode (location: (5,32)-(5,53)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (5,32)-(5,36) = "when" │ │ ├── conditions: (length: 1) │ │ │ └── @ FalseNode (location: (5,37)-(5,42)) + │ │ │ └── flags: static_literal │ │ ├── then_keyword_loc: ∅ │ │ └── statements: │ │ @ StatementsNode (location: (5,44)-(5,53)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (5,44)-(5,53)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :puts @@ -77,7 +89,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ SymbolNode (location: (5,49)-(5,53)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (5,49)-(5,50) = ":" │ │ │ ├── value_loc: (5,50)-(5,53) = "bye" │ │ │ ├── closing_loc: ∅ @@ -88,12 +100,15 @@ │ ├── case_keyword_loc: (5,0)-(5,4) = "case" │ └── end_keyword_loc: (5,55)-(5,58) = "end" ├── @ CaseNode (location: (7,0)-(7,20)) + │ ├── flags: newline │ ├── predicate: ∅ │ ├── conditions: (length: 1) │ │ └── @ WhenNode (location: (7,6)-(7,15)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (7,6)-(7,10) = "when" │ │ ├── conditions: (length: 1) │ │ │ └── @ SplatNode (location: (7,11)-(7,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (7,11)-(7,12) = "*" │ │ │ └── expression: │ │ │ @ CallNode (location: (7,12)-(7,15)) @@ -112,19 +127,21 @@ │ ├── case_keyword_loc: (7,0)-(7,4) = "case" │ └── end_keyword_loc: (7,17)-(7,20) = "end" ├── @ CaseNode (location: (9,0)-(13,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (9,5)-(9,8)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (9,5)-(9,6) = ":" │ │ ├── value_loc: (9,6)-(9,8) = "hi" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "hi" │ ├── conditions: (length: 1) │ │ └── @ WhenNode (location: (10,0)-(10,8)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (10,0)-(10,4) = "when" │ │ ├── conditions: (length: 1) │ │ │ └── @ SymbolNode (location: (10,5)-(10,8)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (10,5)-(10,6) = ":" │ │ │ ├── value_loc: (10,6)-(10,8) = "hi" │ │ │ ├── closing_loc: ∅ @@ -133,12 +150,14 @@ │ │ └── statements: ∅ │ ├── consequent: │ │ @ ElseNode (location: (11,0)-(13,3)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (11,0)-(11,4) = "else" │ │ ├── statements: │ │ │ @ StatementsNode (location: (12,0)-(12,2)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ SymbolNode (location: (12,0)-(12,2)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (12,0)-(12,1) = ":" │ │ │ ├── value_loc: (12,1)-(12,2) = "b" │ │ │ ├── closing_loc: ∅ @@ -147,6 +166,7 @@ │ ├── case_keyword_loc: (9,0)-(9,4) = "case" │ └── end_keyword_loc: (13,0)-(13,3) = "end" ├── @ CaseNode (location: (15,0)-(15,36)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (15,5)-(15,9)) │ │ ├── flags: variable_call, ignore_visibility @@ -160,11 +180,14 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ WhenNode (location: (15,11)-(15,31)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (15,11)-(15,15) = "when" │ │ ├── conditions: (length: 2) │ │ │ ├── @ ConstantReadNode (location: (15,16)-(15,22)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :FooBar │ │ │ └── @ ConstantReadNode (location: (15,24)-(15,31)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :BazBonk │ │ ├── then_keyword_loc: ∅ │ │ └── statements: ∅ @@ -172,9 +195,11 @@ │ ├── case_keyword_loc: (15,0)-(15,4) = "case" │ └── end_keyword_loc: (15,33)-(15,36) = "end" ├── @ CaseNode (location: (17,0)-(19,3)) + │ ├── flags: newline │ ├── predicate: ∅ │ ├── conditions: (length: 1) │ │ └── @ WhenNode (location: (18,0)-(18,15)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (18,0)-(18,4) = "when" │ │ ├── conditions: (length: 1) │ │ │ └── @ CallNode (location: (18,5)-(18,15)) @@ -216,9 +241,11 @@ │ ├── case_keyword_loc: (17,0)-(17,4) = "case" │ └── end_keyword_loc: (19,0)-(19,3) = "end" ├── @ CaseNode (location: (21,0)-(25,3)) + │ ├── flags: newline │ ├── predicate: ∅ │ ├── conditions: (length: 1) │ │ └── @ WhenNode (location: (22,0)-(22,6)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (22,0)-(22,4) = "when" │ │ ├── conditions: (length: 1) │ │ │ └── @ CallNode (location: (22,5)-(22,6)) @@ -235,12 +262,14 @@ │ │ └── statements: ∅ │ ├── consequent: │ │ @ ElseNode (location: (23,0)-(25,3)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (23,0)-(23,4) = "else" │ │ ├── statements: ∅ │ │ └── end_keyword_loc: (25,0)-(25,3) = "end" │ ├── case_keyword_loc: (21,0)-(21,4) = "case" │ └── end_keyword_loc: (25,0)-(25,3) = "end" ├── @ CaseNode (location: (27,0)-(30,6)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (27,5)-(27,9)) │ │ ├── flags: variable_call, ignore_visibility @@ -254,10 +283,11 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ WhenNode (location: (28,3)-(28,10)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (28,3)-(28,7) = "when" │ │ ├── conditions: (length: 1) │ │ │ └── @ SymbolNode (location: (28,8)-(28,10)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (28,8)-(28,9) = ":" │ │ │ ├── value_loc: (28,9)-(28,10) = "b" │ │ │ ├── closing_loc: ∅ @@ -266,19 +296,22 @@ │ │ └── statements: ∅ │ ├── consequent: │ │ @ ElseNode (location: (29,5)-(30,6)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (29,5)-(29,9) = "else" │ │ ├── statements: ∅ │ │ └── end_keyword_loc: (30,3)-(30,6) = "end" │ ├── case_keyword_loc: (27,0)-(27,4) = "case" │ └── end_keyword_loc: (30,3)-(30,6) = "end" ├── @ CaseNode (location: (32,0)-(32,25)) + │ ├── flags: newline │ ├── predicate: ∅ │ ├── conditions: (length: 1) │ │ └── @ WhenNode (location: (32,14)-(32,20)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (32,14)-(32,18) = "when" │ │ ├── conditions: (length: 1) │ │ │ └── @ IntegerNode (location: (32,19)-(32,20)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── then_keyword_loc: ∅ │ │ └── statements: ∅ @@ -286,23 +319,26 @@ │ ├── case_keyword_loc: (32,0)-(32,4) = "case" │ └── end_keyword_loc: (32,22)-(32,25) = "end" ├── @ CaseNode (location: (34,0)-(36,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ MatchPredicateNode (location: (34,5)-(34,11)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ IntegerNode (location: (34,5)-(34,6)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── pattern: │ │ │ @ IntegerNode (location: (34,10)-(34,11)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── operator_loc: (34,7)-(34,9) = "in" │ ├── conditions: (length: 1) │ │ └── @ WhenNode (location: (35,0)-(35,6)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (35,0)-(35,4) = "when" │ │ ├── conditions: (length: 1) │ │ │ └── @ IntegerNode (location: (35,5)-(35,6)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 3 │ │ ├── then_keyword_loc: ∅ │ │ └── statements: ∅ @@ -310,23 +346,26 @@ │ ├── case_keyword_loc: (34,0)-(34,4) = "case" │ └── end_keyword_loc: (36,0)-(36,3) = "end" ├── @ CaseNode (location: (38,0)-(38,24)) + │ ├── flags: newline │ ├── predicate: │ │ @ MatchPredicateNode (location: (38,5)-(38,11)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ IntegerNode (location: (38,5)-(38,6)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── pattern: │ │ │ @ IntegerNode (location: (38,10)-(38,11)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── operator_loc: (38,7)-(38,9) = "in" │ ├── conditions: (length: 1) │ │ └── @ WhenNode (location: (38,13)-(38,19)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (38,13)-(38,17) = "when" │ │ ├── conditions: (length: 1) │ │ │ └── @ IntegerNode (location: (38,18)-(38,19)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 3 │ │ ├── then_keyword_loc: ∅ │ │ └── statements: ∅ @@ -334,22 +373,25 @@ │ ├── case_keyword_loc: (38,0)-(38,4) = "case" │ └── end_keyword_loc: (38,21)-(38,24) = "end" ├── @ CaseMatchNode (location: (40,0)-(42,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ MatchPredicateNode (location: (40,5)-(40,11)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ IntegerNode (location: (40,5)-(40,6)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── pattern: │ │ │ @ IntegerNode (location: (40,10)-(40,11)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── operator_loc: (40,7)-(40,9) = "in" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (41,0)-(41,4)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IntegerNode (location: (41,3)-(41,4)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 3 │ │ ├── statements: ∅ │ │ ├── in_loc: (41,0)-(41,2) = "in" @@ -358,22 +400,25 @@ │ ├── case_keyword_loc: (40,0)-(40,4) = "case" │ └── end_keyword_loc: (42,0)-(42,3) = "end" ├── @ CaseMatchNode (location: (44,0)-(44,22)) + │ ├── flags: newline │ ├── predicate: │ │ @ MatchPredicateNode (location: (44,5)-(44,11)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ IntegerNode (location: (44,5)-(44,6)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── pattern: │ │ │ @ IntegerNode (location: (44,10)-(44,11)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── operator_loc: (44,7)-(44,9) = "in" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (44,13)-(44,17)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IntegerNode (location: (44,16)-(44,17)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 3 │ │ ├── statements: ∅ │ │ ├── in_loc: (44,13)-(44,15) = "in" @@ -382,6 +427,7 @@ │ ├── case_keyword_loc: (44,0)-(44,4) = "case" │ └── end_keyword_loc: (44,19)-(44,22) = "end" ├── @ CaseMatchNode (location: (46,0)-(49,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (46,5)-(46,6)) │ │ ├── flags: variable_call, ignore_visibility @@ -395,11 +441,14 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (47,0)-(48,3)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (47,3)-(47,15)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (47,5)-(47,7) = "if" │ │ │ ├── predicate: │ │ │ │ @ AndNode (location: (47,8)-(47,15)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── left: │ │ │ │ │ @ CallNode (location: (47,8)-(47,9)) │ │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -426,17 +475,20 @@ │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (47,3)-(47,4)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ LocalVariableTargetNode (location: (47,3)-(47,4)) + │ │ │ │ ├── flags: newline │ │ │ │ ├── name: :b │ │ │ │ └── depth: 0 │ │ │ ├── consequent: ∅ │ │ │ └── end_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (48,2)-(48,3)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (48,2)-(48,3)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :e @@ -451,10 +503,10 @@ │ ├── case_keyword_loc: (46,0)-(46,4) = "case" │ └── end_keyword_loc: (49,0)-(49,3) = "end" └── @ CallNode (location: (51,0)-(55,3)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (51,0)-(51,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── call_operator_loc: (51,1)-(51,2) = "." ├── name: :then @@ -464,24 +516,31 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (51,7)-(55,3)) + ├── flags: ∅ ├── locals: [:_1] ├── parameters: │ @ NumberedParametersNode (location: (51,7)-(55,3)) + │ ├── flags: ∅ │ └── maximum: 1 ├── body: │ @ StatementsNode (location: (52,2)-(54,5)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CaseMatchNode (location: (52,2)-(54,5)) + │ ├── flags: newline │ ├── predicate: │ │ @ IntegerNode (location: (52,7)-(52,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (53,2)-(53,8)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ PinnedVariableNode (location: (53,5)-(53,8)) + │ │ │ ├── flags: ∅ │ │ │ ├── variable: │ │ │ │ @ LocalVariableReadNode (location: (53,6)-(53,8)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :_1 │ │ │ │ └── depth: 0 │ │ │ └── operator_loc: (53,5)-(53,6) = "^" diff --git a/test/prism/snapshots/classes.txt b/test/prism/snapshots/classes.txt index 4a36bd5cdc49e5..6e30ba03898c7b 100644 --- a/test/prism/snapshots/classes.txt +++ b/test/prism/snapshots/classes.txt @@ -1,46 +1,56 @@ @ ProgramNode (location: (1,0)-(35,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(35,3)) + ├── flags: ∅ └── body: (length: 14) ├── @ ClassNode (location: (1,0)-(1,17)) + │ ├── flags: newline │ ├── locals: [:a] │ ├── class_keyword_loc: (1,0)-(1,5) = "class" │ ├── constant_path: │ │ @ ConstantReadNode (location: (1,6)-(1,7)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── inheritance_operator_loc: ∅ │ ├── superclass: ∅ │ ├── body: │ │ @ StatementsNode (location: (1,8)-(1,13)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableWriteNode (location: (1,8)-(1,13)) + │ │ ├── flags: newline │ │ ├── name: :a │ │ ├── depth: 0 │ │ ├── name_loc: (1,8)-(1,9) = "a" │ │ ├── value: │ │ │ @ IntegerNode (location: (1,12)-(1,13)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── operator_loc: (1,10)-(1,11) = "=" │ ├── end_keyword_loc: (1,14)-(1,17) = "end" │ └── name: :A ├── @ ClassNode (location: (3,0)-(3,20)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (3,0)-(3,5) = "class" │ ├── constant_path: │ │ @ ConstantReadNode (location: (3,6)-(3,7)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── inheritance_operator_loc: ∅ │ ├── superclass: ∅ │ ├── body: │ │ @ BeginNode (location: (3,0)-(3,20)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: ∅ │ │ ├── rescue_clause: ∅ │ │ ├── else_clause: ∅ │ │ ├── ensure_clause: │ │ │ @ EnsureNode (location: (3,9)-(3,20)) + │ │ │ ├── flags: ∅ │ │ │ ├── ensure_keyword_loc: (3,9)-(3,15) = "ensure" │ │ │ ├── statements: ∅ │ │ │ └── end_keyword_loc: (3,17)-(3,20) = "end" @@ -48,19 +58,23 @@ │ ├── end_keyword_loc: (3,17)-(3,20) = "end" │ └── name: :A ├── @ ClassNode (location: (5,0)-(5,34)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (5,0)-(5,5) = "class" │ ├── constant_path: │ │ @ ConstantReadNode (location: (5,6)-(5,7)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── inheritance_operator_loc: ∅ │ ├── superclass: ∅ │ ├── body: │ │ @ BeginNode (location: (5,0)-(5,34)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (5,9)-(5,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (5,9)-(5,15) = "rescue" │ │ │ ├── exceptions: (length: 0) │ │ │ ├── operator_loc: ∅ @@ -69,11 +83,13 @@ │ │ │ └── consequent: ∅ │ │ ├── else_clause: │ │ │ @ ElseNode (location: (5,17)-(5,29)) + │ │ │ ├── flags: ∅ │ │ │ ├── else_keyword_loc: (5,17)-(5,21) = "else" │ │ │ ├── statements: ∅ │ │ │ └── end_keyword_loc: (5,23)-(5,29) = "ensure" │ │ ├── ensure_clause: │ │ │ @ EnsureNode (location: (5,23)-(5,34)) + │ │ │ ├── flags: ∅ │ │ │ ├── ensure_keyword_loc: (5,23)-(5,29) = "ensure" │ │ │ ├── statements: ∅ │ │ │ └── end_keyword_loc: (5,31)-(5,34) = "end" @@ -81,30 +97,36 @@ │ ├── end_keyword_loc: (5,31)-(5,34) = "end" │ └── name: :A ├── @ ClassNode (location: (7,0)-(9,3)) + │ ├── flags: newline │ ├── locals: [:a] │ ├── class_keyword_loc: (7,0)-(7,5) = "class" │ ├── constant_path: │ │ @ ConstantReadNode (location: (7,6)-(7,7)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── inheritance_operator_loc: (7,8)-(7,9) = "<" │ ├── superclass: │ │ @ ConstantReadNode (location: (7,10)-(7,11)) + │ │ ├── flags: ∅ │ │ └── name: :B │ ├── body: │ │ @ StatementsNode (location: (8,0)-(8,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableWriteNode (location: (8,0)-(8,5)) + │ │ ├── flags: newline │ │ ├── name: :a │ │ ├── depth: 0 │ │ ├── name_loc: (8,0)-(8,1) = "a" │ │ ├── value: │ │ │ @ IntegerNode (location: (8,4)-(8,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── operator_loc: (8,2)-(8,3) = "=" │ ├── end_keyword_loc: (9,0)-(9,3) = "end" │ └── name: :A ├── @ SingletonClassNode (location: (11,0)-(12,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (11,0)-(11,5) = "class" │ ├── operator_loc: (11,6)-(11,8) = "<<" @@ -132,30 +154,37 @@ │ ├── body: ∅ │ └── end_keyword_loc: (12,0)-(12,3) = "end" ├── @ ClassNode (location: (14,0)-(14,40)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (14,0)-(14,5) = "class" │ ├── constant_path: │ │ @ ConstantReadNode (location: (14,6)-(14,7)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── inheritance_operator_loc: ∅ │ ├── superclass: ∅ │ ├── body: │ │ @ StatementsNode (location: (14,9)-(14,35)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SingletonClassNode (location: (14,9)-(14,35)) + │ │ ├── flags: newline │ │ ├── locals: [] │ │ ├── class_keyword_loc: (14,9)-(14,14) = "class" │ │ ├── operator_loc: (14,15)-(14,17) = "<<" │ │ ├── expression: │ │ │ @ SelfNode (location: (14,18)-(14,22)) + │ │ │ └── flags: ∅ │ │ ├── body: │ │ │ @ BeginNode (location: (14,9)-(14,35)) + │ │ │ ├── flags: ∅ │ │ │ ├── begin_keyword_loc: ∅ │ │ │ ├── statements: ∅ │ │ │ ├── rescue_clause: ∅ │ │ │ ├── else_clause: ∅ │ │ │ ├── ensure_clause: │ │ │ │ @ EnsureNode (location: (14,24)-(14,35)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── ensure_keyword_loc: (14,24)-(14,30) = "ensure" │ │ │ │ ├── statements: ∅ │ │ │ │ └── end_keyword_loc: (14,32)-(14,35) = "end" @@ -164,28 +193,35 @@ │ ├── end_keyword_loc: (14,37)-(14,40) = "end" │ └── name: :A ├── @ ClassNode (location: (16,0)-(16,54)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (16,0)-(16,5) = "class" │ ├── constant_path: │ │ @ ConstantReadNode (location: (16,6)-(16,7)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── inheritance_operator_loc: ∅ │ ├── superclass: ∅ │ ├── body: │ │ @ StatementsNode (location: (16,9)-(16,49)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SingletonClassNode (location: (16,9)-(16,49)) + │ │ ├── flags: newline │ │ ├── locals: [] │ │ ├── class_keyword_loc: (16,9)-(16,14) = "class" │ │ ├── operator_loc: (16,15)-(16,17) = "<<" │ │ ├── expression: │ │ │ @ SelfNode (location: (16,18)-(16,22)) + │ │ │ └── flags: ∅ │ │ ├── body: │ │ │ @ BeginNode (location: (16,9)-(16,49)) + │ │ │ ├── flags: ∅ │ │ │ ├── begin_keyword_loc: ∅ │ │ │ ├── statements: ∅ │ │ │ ├── rescue_clause: │ │ │ │ @ RescueNode (location: (16,24)-(16,30)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── keyword_loc: (16,24)-(16,30) = "rescue" │ │ │ │ ├── exceptions: (length: 0) │ │ │ │ ├── operator_loc: ∅ @@ -194,11 +230,13 @@ │ │ │ │ └── consequent: ∅ │ │ │ ├── else_clause: │ │ │ │ @ ElseNode (location: (16,32)-(16,44)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── else_keyword_loc: (16,32)-(16,36) = "else" │ │ │ │ ├── statements: ∅ │ │ │ │ └── end_keyword_loc: (16,38)-(16,44) = "ensure" │ │ │ ├── ensure_clause: │ │ │ │ @ EnsureNode (location: (16,38)-(16,49)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── ensure_keyword_loc: (16,38)-(16,44) = "ensure" │ │ │ │ ├── statements: ∅ │ │ │ │ └── end_keyword_loc: (16,46)-(16,49) = "end" @@ -207,6 +245,7 @@ │ ├── end_keyword_loc: (16,51)-(16,54) = "end" │ └── name: :A ├── @ SingletonClassNode (location: (18,0)-(19,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (18,0)-(18,5) = "class" │ ├── operator_loc: (18,6)-(18,8) = "<<" @@ -234,6 +273,7 @@ │ ├── body: ∅ │ └── end_keyword_loc: (19,0)-(19,3) = "end" ├── @ SingletonClassNode (location: (21,0)-(21,20)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (21,0)-(21,5) = "class" │ ├── operator_loc: (21,6)-(21,8) = "<<" @@ -261,35 +301,42 @@ │ ├── body: ∅ │ └── end_keyword_loc: (21,17)-(21,20) = "end" ├── @ SingletonClassNode (location: (23,0)-(24,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (23,0)-(23,5) = "class" │ ├── operator_loc: (23,6)-(23,8) = "<<" │ ├── expression: │ │ @ SelfNode (location: (23,9)-(23,13)) + │ │ └── flags: ∅ │ ├── body: ∅ │ └── end_keyword_loc: (24,0)-(24,3) = "end" ├── @ SingletonClassNode (location: (26,0)-(26,17)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (26,0)-(26,5) = "class" │ ├── operator_loc: (26,6)-(26,8) = "<<" │ ├── expression: │ │ @ SelfNode (location: (26,9)-(26,13)) + │ │ └── flags: ∅ │ ├── body: ∅ │ └── end_keyword_loc: (26,14)-(26,17) = "end" ├── @ SingletonClassNode (location: (28,0)-(30,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (28,0)-(28,5) = "class" │ ├── operator_loc: (28,6)-(28,8) = "<<" │ ├── expression: │ │ @ SelfNode (location: (28,9)-(28,13)) + │ │ └── flags: ∅ │ ├── body: │ │ @ StatementsNode (location: (29,0)-(29,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (29,0)-(29,5)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ IntegerNode (location: (29,0)-(29,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── call_operator_loc: ∅ │ │ ├── name: :+ @@ -300,25 +347,28 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (29,4)-(29,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ └── end_keyword_loc: (30,0)-(30,3) = "end" ├── @ SingletonClassNode (location: (32,0)-(32,23)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (32,0)-(32,5) = "class" │ ├── operator_loc: (32,6)-(32,8) = "<<" │ ├── expression: │ │ @ SelfNode (location: (32,9)-(32,13)) + │ │ └── flags: ∅ │ ├── body: │ │ @ StatementsNode (location: (32,14)-(32,19)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (32,14)-(32,19)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ IntegerNode (location: (32,14)-(32,15)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── call_operator_loc: ∅ │ │ ├── name: :+ @@ -329,16 +379,18 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (32,18)-(32,19)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ └── end_keyword_loc: (32,20)-(32,23) = "end" └── @ ClassNode (location: (34,0)-(35,3)) + ├── flags: newline ├── locals: [] ├── class_keyword_loc: (34,0)-(34,5) = "class" ├── constant_path: │ @ ConstantReadNode (location: (34,6)-(34,7)) + │ ├── flags: ∅ │ └── name: :A ├── inheritance_operator_loc: (34,8)-(34,9) = "<" ├── superclass: @@ -346,6 +398,7 @@ │ ├── flags: ∅ │ ├── receiver: │ │ @ ConstantReadNode (location: (34,10)-(34,11)) + │ │ ├── flags: ∅ │ │ └── name: :B │ ├── call_operator_loc: ∅ │ ├── name: :[] @@ -356,7 +409,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (34,12)-(34,13)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── closing_loc: (34,13)-(34,14) = "]" │ └── block: ∅ diff --git a/test/prism/snapshots/command_method_call.txt b/test/prism/snapshots/command_method_call.txt index 7fd6341304c950..2c0f44e52b4b06 100644 --- a/test/prism/snapshots/command_method_call.txt +++ b/test/prism/snapshots/command_method_call.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(41,10)) +├── flags: ∅ ├── locals: [:foo, :bar] └── statements: @ StatementsNode (location: (1,0)-(41,10)) + ├── flags: ∅ └── body: (length: 21) ├── @ CallNode (location: (1,0)-(1,5)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -15,12 +17,12 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (1,4)-(1,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (3,0)-(3,9)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -42,13 +44,14 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (3,8)-(3,9)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ IfNode (location: (5,0)-(5,14)) + │ ├── flags: newline │ ├── if_keyword_loc: (5,6)-(5,8) = "if" │ ├── predicate: │ │ @ CallNode (location: (5,9)-(5,14)) @@ -63,16 +66,17 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (5,13)-(5,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ ├── then_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (5,0)-(5,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (5,0)-(5,5)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :foo @@ -83,13 +87,14 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (5,4)-(5,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ ├── consequent: ∅ │ └── end_keyword_loc: ∅ ├── @ UnlessNode (location: (7,0)-(7,18)) + │ ├── flags: newline │ ├── keyword_loc: (7,6)-(7,12) = "unless" │ ├── predicate: │ │ @ CallNode (location: (7,13)-(7,18)) @@ -104,16 +109,17 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (7,17)-(7,18)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ ├── then_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (7,0)-(7,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (7,0)-(7,5)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :foo @@ -124,14 +130,14 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (7,4)-(7,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ ├── consequent: ∅ │ └── end_keyword_loc: ∅ ├── @ WhileNode (location: (9,0)-(9,17)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── keyword_loc: (9,6)-(9,11) = "while" │ ├── closing_loc: ∅ │ ├── predicate: @@ -147,15 +153,16 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (9,16)-(9,17)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ └── statements: │ @ StatementsNode (location: (9,0)-(9,5)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (9,0)-(9,5)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -166,12 +173,12 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (9,4)-(9,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ UntilNode (location: (11,0)-(11,17)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── keyword_loc: (11,6)-(11,11) = "until" │ ├── closing_loc: ∅ │ ├── predicate: @@ -187,15 +194,16 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (11,16)-(11,17)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ └── statements: │ @ StatementsNode (location: (11,0)-(11,5)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (11,0)-(11,5)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -206,11 +214,12 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (11,4)-(11,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ RescueModifierNode (location: (13,0)-(13,18)) + │ ├── flags: newline │ ├── expression: │ │ @ CallNode (location: (13,0)-(13,5)) │ │ ├── flags: ignore_visibility @@ -224,7 +233,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (13,4)-(13,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ @@ -242,12 +251,12 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (13,17)-(13,18)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (15,0)-(15,10)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (15,0)-(15,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -279,13 +288,14 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (15,8)-(15,9)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ ├── closing_loc: (15,9)-(15,10) = "]" │ └── block: ∅ ├── @ AndNode (location: (17,0)-(17,15)) + │ ├── flags: newline │ ├── left: │ │ @ CallNode (location: (17,0)-(17,5)) │ │ ├── flags: ignore_visibility @@ -299,7 +309,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (17,4)-(17,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ @@ -316,12 +326,13 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (17,14)-(17,15)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ └── operator_loc: (17,6)-(17,9) = "and" ├── @ OrNode (location: (19,0)-(19,14)) + │ ├── flags: newline │ ├── left: │ │ @ CallNode (location: (19,0)-(19,5)) │ │ ├── flags: ignore_visibility @@ -335,7 +346,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (19,4)-(19,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ @@ -352,13 +363,13 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (19,13)-(19,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ └── operator_loc: (19,6)-(19,8) = "or" ├── @ CallNode (location: (21,0)-(21,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (21,4)-(21,9)) │ │ ├── flags: ignore_visibility @@ -372,7 +383,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (21,8)-(21,9)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ @@ -384,11 +395,13 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ LocalVariableWriteNode (location: (23,0)-(23,17)) + │ ├── flags: newline │ ├── name: :foo │ ├── depth: 0 │ ├── name_loc: (23,0)-(23,3) = "foo" │ ├── value: │ │ @ LocalVariableWriteNode (location: (23,6)-(23,17)) + │ │ ├── flags: ∅ │ │ ├── name: :bar │ │ ├── depth: 0 │ │ ├── name_loc: (23,6)-(23,9) = "bar" @@ -405,22 +418,24 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ IntegerNode (location: (23,16)-(23,17)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ └── operator_loc: (23,10)-(23,11) = "=" │ └── operator_loc: (23,4)-(23,5) = "=" ├── @ DefNode (location: (25,0)-(25,15)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (25,4)-(25,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (25,10)-(25,15)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (25,10)-(25,15)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -431,7 +446,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (25,14)-(25,15)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ @@ -443,10 +458,10 @@ │ ├── equal_loc: (25,8)-(25,9) = "=" │ └── end_keyword_loc: ∅ ├── @ CallNode (location: (27,0)-(27,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ IntegerNode (location: (27,0)-(27,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── call_operator_loc: (27,1)-(27,2) = "." │ ├── name: :foo @@ -457,18 +472,18 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (27,6)-(27,7)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (29,0)-(29,11)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (29,0)-(29,5)) │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ IntegerNode (location: (29,0)-(29,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── call_operator_loc: (29,1)-(29,2) = "." │ │ ├── name: :foo @@ -486,12 +501,12 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (29,10)-(29,11)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (31,0)-(31,14)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (31,0)-(31,8)) │ │ ├── flags: ∅ @@ -500,7 +515,7 @@ │ │ │ ├── flags: ∅ │ │ │ ├── receiver: │ │ │ │ @ IntegerNode (location: (31,0)-(31,1)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── call_operator_loc: (31,1)-(31,2) = "." │ │ │ ├── name: :foo @@ -518,7 +533,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (31,6)-(31,7)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── closing_loc: (31,7)-(31,8) = "]" │ │ └── block: ∅ @@ -531,18 +546,18 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (31,13)-(31,14)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 3 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (33,0)-(33,14)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (33,0)-(33,8)) │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ IntegerNode (location: (33,0)-(33,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── call_operator_loc: (33,1)-(33,2) = "." │ │ ├── name: :foo @@ -553,7 +568,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (33,6)-(33,7)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── closing_loc: (33,7)-(33,8) = ")" │ │ └── block: ∅ @@ -566,18 +581,18 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (33,13)-(33,14)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 3 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (35,0)-(35,15)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (35,0)-(35,9)) │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ IntegerNode (location: (35,0)-(35,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── call_operator_loc: (35,1)-(35,2) = "." │ │ ├── name: :foo @@ -587,9 +602,10 @@ │ │ ├── closing_loc: (35,8)-(35,9) = ")" │ │ └── block: │ │ @ BlockArgumentNode (location: (35,6)-(35,8)) + │ │ ├── flags: ∅ │ │ ├── expression: │ │ │ @ IntegerNode (location: (35,7)-(35,8)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── operator_loc: (35,6)-(35,7) = "&" │ ├── call_operator_loc: (35,9)-(35,10) = "." @@ -601,11 +617,12 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (35,14)-(35,15)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 3 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ AndNode (location: (37,0)-(37,17)) + │ ├── flags: newline │ ├── left: │ │ @ CallNode (location: (37,0)-(37,6)) │ │ ├── flags: ∅ @@ -622,7 +639,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ IntegerNode (location: (37,5)-(37,6)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ @@ -649,7 +666,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ IntegerNode (location: (37,16)-(37,17)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ @@ -662,6 +679,7 @@ │ │ └── block: ∅ │ └── operator_loc: (37,7)-(37,10) = "and" ├── @ OrNode (location: (39,0)-(39,16)) + │ ├── flags: newline │ ├── left: │ │ @ CallNode (location: (39,0)-(39,6)) │ │ ├── flags: ∅ @@ -678,7 +696,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ IntegerNode (location: (39,5)-(39,6)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ @@ -705,7 +723,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ IntegerNode (location: (39,15)-(39,16)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ @@ -718,7 +736,7 @@ │ │ └── block: ∅ │ └── operator_loc: (39,7)-(39,9) = "or" └── @ CallNode (location: (41,0)-(41,10)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (41,4)-(41,10)) │ ├── flags: ∅ @@ -735,7 +753,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (41,9)-(41,10)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ diff --git a/test/prism/snapshots/comments.txt b/test/prism/snapshots/comments.txt index b7088adcd504b6..66869bc2a99a54 100644 --- a/test/prism/snapshots/comments.txt +++ b/test/prism/snapshots/comments.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(24,5)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(24,5)) + ├── flags: ∅ └── body: (length: 9) ├── @ CallNode (location: (1,0)-(1,1)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :a @@ -14,7 +16,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (3,0)-(3,1)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :b @@ -24,7 +26,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (5,0)-(5,1)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :c @@ -34,7 +36,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (6,0)-(6,1)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :d @@ -44,7 +46,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (8,0)-(10,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (8,0)-(8,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -64,7 +66,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (12,0)-(14,2)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (12,0)-(12,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -84,7 +86,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (16,0)-(17,2)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (16,0)-(16,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -104,7 +106,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (19,0)-(20,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (19,0)-(19,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -124,7 +126,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ └── @ CallNode (location: (22,0)-(24,5)) - ├── flags: safe_navigation + ├── flags: newline, safe_navigation ├── receiver: │ @ CallNode (location: (22,0)-(22,1)) │ ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/constants.txt b/test/prism/snapshots/constants.txt index 12518336634381..3246ff80c334bd 100644 --- a/test/prism/snapshots/constants.txt +++ b/test/prism/snapshots/constants.txt @@ -1,20 +1,27 @@ @ ProgramNode (location: (1,0)-(184,10)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(184,10)) + ├── flags: ∅ └── body: (length: 90) ├── @ ConstantPathNode (location: (1,0)-(1,4)) + │ ├── flags: newline │ ├── parent: │ │ @ ConstantReadNode (location: (1,0)-(1,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── name: :B │ ├── delimiter_loc: (1,1)-(1,3) = "::" │ └── name_loc: (1,3)-(1,4) = "B" ├── @ ConstantPathNode (location: (3,0)-(3,7)) + │ ├── flags: newline │ ├── parent: │ │ @ ConstantPathNode (location: (3,0)-(3,4)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (3,0)-(3,1)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :A │ │ ├── name: :B │ │ ├── delimiter_loc: (3,1)-(3,3) = "::" @@ -23,6 +30,7 @@ │ ├── delimiter_loc: (3,4)-(3,6) = "::" │ └── name_loc: (3,6)-(3,7) = "C" ├── @ ConstantPathNode (location: (5,0)-(5,4)) + │ ├── flags: newline │ ├── parent: │ │ @ CallNode (location: (5,0)-(5,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -38,10 +46,13 @@ │ ├── delimiter_loc: (5,1)-(5,3) = "::" │ └── name_loc: (5,3)-(5,4) = "B" ├── @ ConstantPathWriteNode (location: (7,0)-(7,8)) + │ ├── flags: newline │ ├── target: │ │ @ ConstantPathNode (location: (7,0)-(7,4)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (7,0)-(7,1)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :A │ │ ├── name: :B │ │ ├── delimiter_loc: (7,1)-(7,3) = "::" @@ -49,20 +60,22 @@ │ ├── operator_loc: (7,5)-(7,6) = "=" │ └── value: │ @ IntegerNode (location: (7,7)-(7,8)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ ConstantWriteNode (location: (9,0)-(9,5)) + │ ├── flags: newline │ ├── name: :A │ ├── name_loc: (9,0)-(9,1) = "A" │ ├── value: │ │ @ IntegerNode (location: (9,4)-(9,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (9,2)-(9,3) = "=" ├── @ ConstantReadNode (location: (11,0)-(11,3)) + │ ├── flags: newline │ └── name: :ABC ├── @ CallNode (location: (13,0)-(13,5)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :Foo @@ -73,12 +86,12 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (13,4)-(13,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (15,0)-(15,8)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :Foo @@ -89,6 +102,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ SplatNode (location: (15,4)-(15,8)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (15,4)-(15,5) = "*" │ │ └── expression: │ │ @ CallNode (location: (15,5)-(15,8)) @@ -104,7 +118,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (17,0)-(17,9)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :Foo @@ -118,6 +132,7 @@ │ │ ├── flags: ∅ │ │ └── elements: (length: 1) │ │ └── @ AssocSplatNode (location: (17,4)-(17,9)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ CallNode (location: (17,6)-(17,9)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -133,7 +148,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (19,0)-(19,8)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :Foo @@ -143,6 +158,7 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockArgumentNode (location: (19,4)-(19,8)) + │ ├── flags: ∅ │ ├── expression: │ │ @ CallNode (location: (19,5)-(19,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -156,9 +172,10 @@ │ │ └── block: ∅ │ └── operator_loc: (19,4)-(19,5) = "&" ├── @ CallNode (location: (21,0)-(21,13)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (21,0)-(21,3)) + │ │ ├── flags: ∅ │ │ └── name: :Foo │ ├── call_operator_loc: (21,3)-(21,5) = "::" │ ├── name: :Bar @@ -169,6 +186,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ SplatNode (location: (21,9)-(21,13)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (21,9)-(21,10) = "*" │ │ └── expression: │ │ @ CallNode (location: (21,10)-(21,13)) @@ -184,9 +202,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (23,0)-(23,14)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (23,0)-(23,3)) + │ │ ├── flags: ∅ │ │ └── name: :Foo │ ├── call_operator_loc: (23,3)-(23,5) = "::" │ ├── name: :Bar @@ -200,6 +219,7 @@ │ │ ├── flags: ∅ │ │ └── elements: (length: 1) │ │ └── @ AssocSplatNode (location: (23,9)-(23,14)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ CallNode (location: (23,11)-(23,14)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -215,9 +235,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (25,0)-(25,13)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (25,0)-(25,3)) + │ │ ├── flags: ∅ │ │ └── name: :Foo │ ├── call_operator_loc: (25,3)-(25,5) = "::" │ ├── name: :Bar @@ -227,6 +248,7 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockArgumentNode (location: (25,9)-(25,13)) + │ ├── flags: ∅ │ ├── expression: │ │ @ CallNode (location: (25,10)-(25,13)) │ │ ├── flags: variable_call, ignore_visibility @@ -240,9 +262,10 @@ │ │ └── block: ∅ │ └── operator_loc: (25,9)-(25,10) = "&" ├── @ CallNode (location: (27,0)-(27,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantPathNode (location: (27,0)-(27,3)) + │ │ ├── flags: ∅ │ │ ├── parent: ∅ │ │ ├── name: :A │ │ ├── delimiter_loc: (27,0)-(27,2) = "::" @@ -255,8 +278,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ ConstantPathWriteNode (location: (29,0)-(29,7)) + │ ├── flags: newline │ ├── target: │ │ @ ConstantPathNode (location: (29,0)-(29,3)) + │ │ ├── flags: ∅ │ │ ├── parent: ∅ │ │ ├── name: :A │ │ ├── delimiter_loc: (29,0)-(29,2) = "::" @@ -264,13 +289,16 @@ │ ├── operator_loc: (29,4)-(29,5) = "=" │ └── value: │ @ IntegerNode (location: (29,6)-(29,7)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ ConstantPathWriteNode (location: (31,0)-(31,10)) + │ ├── flags: newline │ ├── target: │ │ @ ConstantPathNode (location: (31,0)-(31,6)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ ConstantPathNode (location: (31,0)-(31,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── parent: ∅ │ │ │ ├── name: :A │ │ │ ├── delimiter_loc: (31,0)-(31,2) = "::" @@ -281,11 +309,13 @@ │ ├── operator_loc: (31,7)-(31,8) = "=" │ └── value: │ @ IntegerNode (location: (31,9)-(31,10)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ ConstantPathNode (location: (33,0)-(33,6)) + │ ├── flags: newline │ ├── parent: │ │ @ ConstantPathNode (location: (33,0)-(33,3)) + │ │ ├── flags: ∅ │ │ ├── parent: ∅ │ │ ├── name: :A │ │ ├── delimiter_loc: (33,0)-(33,2) = "::" @@ -294,14 +324,16 @@ │ ├── delimiter_loc: (33,3)-(33,5) = "::" │ └── name_loc: (33,5)-(33,6) = "B" ├── @ ConstantPathNode (location: (35,0)-(35,3)) + │ ├── flags: newline │ ├── parent: ∅ │ ├── name: :A │ ├── delimiter_loc: (35,0)-(35,2) = "::" │ └── name_loc: (35,2)-(35,3) = "A" ├── @ CallNode (location: (37,0)-(37,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (37,0)-(37,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (37,1)-(37,3) = "::" │ ├── name: :false @@ -311,11 +343,13 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (39,0)-(39,10)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantPathNode (location: (39,0)-(39,4)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (39,0)-(39,1)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :A │ │ ├── name: :B │ │ ├── delimiter_loc: (39,1)-(39,3) = "::" @@ -328,9 +362,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (41,0)-(41,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (41,0)-(41,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (41,1)-(41,3) = "::" │ ├── name: :& @@ -340,9 +375,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (43,0)-(43,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (43,0)-(43,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (43,1)-(43,3) = "::" │ ├── name: :` @@ -352,9 +388,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (45,0)-(45,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (45,0)-(45,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (45,1)-(45,3) = "::" │ ├── name: :! @@ -364,9 +401,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (47,0)-(47,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (47,0)-(47,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (47,1)-(47,3) = "::" │ ├── name: :!= @@ -376,9 +414,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (49,0)-(49,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (49,0)-(49,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (49,1)-(49,3) = "::" │ ├── name: :^ @@ -388,9 +427,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (51,0)-(51,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (51,0)-(51,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (51,1)-(51,3) = "::" │ ├── name: :== @@ -400,9 +440,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (53,0)-(53,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (53,0)-(53,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (53,1)-(53,3) = "::" │ ├── name: :=== @@ -412,9 +453,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (55,0)-(55,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (55,0)-(55,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (55,1)-(55,3) = "::" │ ├── name: :=~ @@ -424,9 +466,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (57,0)-(57,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (57,0)-(57,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (57,1)-(57,3) = "::" │ ├── name: :> @@ -436,9 +479,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (59,0)-(59,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (59,0)-(59,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (59,1)-(59,3) = "::" │ ├── name: :>= @@ -448,9 +492,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (61,0)-(61,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (61,0)-(61,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (61,1)-(61,3) = "::" │ ├── name: :>> @@ -460,9 +505,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (63,0)-(63,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (63,0)-(63,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (63,1)-(63,3) = "::" │ ├── name: :<< @@ -472,16 +518,19 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ ConstantPathNode (location: (65,0)-(67,1)) + │ ├── flags: newline │ ├── parent: │ │ @ ConstantReadNode (location: (65,0)-(65,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── name: :C │ ├── delimiter_loc: (65,1)-(65,3) = "::" │ └── name_loc: (67,0)-(67,1) = "C" ├── @ CallNode (location: (69,0)-(69,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (69,0)-(69,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (69,1)-(69,3) = "::" │ ├── name: :alias @@ -491,9 +540,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (71,0)-(71,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (71,0)-(71,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (71,1)-(71,3) = "::" │ ├── name: :and @@ -503,9 +553,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (73,0)-(73,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (73,0)-(73,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (73,1)-(73,3) = "::" │ ├── name: :begin @@ -515,16 +566,19 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ ConstantPathNode (location: (75,0)-(75,8)) + │ ├── flags: newline │ ├── parent: │ │ @ ConstantReadNode (location: (75,0)-(75,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── name: :BEGIN │ ├── delimiter_loc: (75,1)-(75,3) = "::" │ └── name_loc: (75,3)-(75,8) = "BEGIN" ├── @ CallNode (location: (77,0)-(77,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (77,0)-(77,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (77,1)-(77,3) = "::" │ ├── name: :break @@ -534,9 +588,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (79,0)-(79,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (79,0)-(79,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (79,1)-(79,3) = "::" │ ├── name: :class @@ -546,9 +601,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (81,0)-(81,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (81,0)-(81,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (81,1)-(81,3) = "::" │ ├── name: :def @@ -558,9 +614,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (83,0)-(83,10)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (83,0)-(83,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (83,1)-(83,3) = "::" │ ├── name: :defined @@ -570,9 +627,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (85,0)-(85,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (85,0)-(85,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (85,1)-(85,3) = "::" │ ├── name: :do @@ -582,9 +640,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (87,0)-(87,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (87,0)-(87,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (87,1)-(87,3) = "::" │ ├── name: :else @@ -594,9 +653,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (89,0)-(89,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (89,0)-(89,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (89,1)-(89,3) = "::" │ ├── name: :elsif @@ -606,9 +666,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (91,0)-(91,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (91,0)-(91,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (91,1)-(91,3) = "::" │ ├── name: :end @@ -618,16 +679,19 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ ConstantPathNode (location: (93,0)-(93,6)) + │ ├── flags: newline │ ├── parent: │ │ @ ConstantReadNode (location: (93,0)-(93,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── name: :END │ ├── delimiter_loc: (93,1)-(93,3) = "::" │ └── name_loc: (93,3)-(93,6) = "END" ├── @ CallNode (location: (95,0)-(95,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (95,0)-(95,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (95,1)-(95,3) = "::" │ ├── name: :ensure @@ -637,9 +701,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (97,0)-(97,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (97,0)-(97,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (97,1)-(97,3) = "::" │ ├── name: :false @@ -649,9 +714,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (99,0)-(99,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (99,0)-(99,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (99,1)-(99,3) = "::" │ ├── name: :for @@ -661,9 +727,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (101,0)-(101,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (101,0)-(101,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (101,1)-(101,3) = "::" │ ├── name: :if @@ -673,9 +740,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (103,0)-(103,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (103,0)-(103,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (103,1)-(103,3) = "::" │ ├── name: :in @@ -685,9 +753,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (105,0)-(105,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (105,0)-(105,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (105,1)-(105,3) = "::" │ ├── name: :next @@ -697,9 +766,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (107,0)-(107,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (107,0)-(107,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (107,1)-(107,3) = "::" │ ├── name: :nil @@ -709,9 +779,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (109,0)-(109,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (109,0)-(109,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (109,1)-(109,3) = "::" │ ├── name: :not @@ -721,9 +792,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (111,0)-(111,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (111,0)-(111,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (111,1)-(111,3) = "::" │ ├── name: :or @@ -733,9 +805,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (113,0)-(113,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (113,0)-(113,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (113,1)-(113,3) = "::" │ ├── name: :redo @@ -745,9 +818,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (115,0)-(115,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (115,0)-(115,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (115,1)-(115,3) = "::" │ ├── name: :rescue @@ -757,9 +831,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (117,0)-(117,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (117,0)-(117,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (117,1)-(117,3) = "::" │ ├── name: :retry @@ -769,9 +844,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (119,0)-(119,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (119,0)-(119,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (119,1)-(119,3) = "::" │ ├── name: :return @@ -781,9 +857,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (121,0)-(121,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (121,0)-(121,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (121,1)-(121,3) = "::" │ ├── name: :self @@ -793,9 +870,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (123,0)-(123,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (123,0)-(123,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (123,1)-(123,3) = "::" │ ├── name: :super @@ -805,9 +883,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (125,0)-(125,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (125,0)-(125,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (125,1)-(125,3) = "::" │ ├── name: :then @@ -817,9 +896,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (127,0)-(127,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (127,0)-(127,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (127,1)-(127,3) = "::" │ ├── name: :true @@ -829,9 +909,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (129,0)-(129,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (129,0)-(129,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (129,1)-(129,3) = "::" │ ├── name: :undef @@ -841,9 +922,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (131,0)-(131,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (131,0)-(131,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (131,1)-(131,3) = "::" │ ├── name: :unless @@ -853,9 +935,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (133,0)-(133,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (133,0)-(133,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (133,1)-(133,3) = "::" │ ├── name: :until @@ -865,9 +948,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (135,0)-(135,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (135,0)-(135,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (135,1)-(135,3) = "::" │ ├── name: :when @@ -877,9 +961,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (137,0)-(137,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (137,0)-(137,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (137,1)-(137,3) = "::" │ ├── name: :while @@ -889,9 +974,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (139,0)-(139,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (139,0)-(139,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (139,1)-(139,3) = "::" │ ├── name: :yield @@ -901,9 +987,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (141,0)-(141,15)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (141,0)-(141,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (141,1)-(141,3) = "::" │ ├── name: :__ENCODING__ @@ -913,9 +1000,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (143,0)-(143,11)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (143,0)-(143,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (143,1)-(143,3) = "::" │ ├── name: :__FILE__ @@ -925,9 +1013,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (145,0)-(145,11)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (145,0)-(145,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (145,1)-(145,3) = "::" │ ├── name: :__LINE__ @@ -937,9 +1026,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (147,0)-(147,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (147,0)-(147,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (147,1)-(147,3) = "::" │ ├── name: :< @@ -949,9 +1039,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (149,0)-(149,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (149,0)-(149,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (149,1)-(149,3) = "::" │ ├── name: :<=> @@ -961,9 +1052,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (151,0)-(151,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (151,0)-(151,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (151,1)-(151,3) = "::" │ ├── name: :<< @@ -973,9 +1065,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (153,0)-(153,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (153,0)-(153,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (153,1)-(153,3) = "::" │ ├── name: :- @@ -985,9 +1078,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (155,0)-(155,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (155,0)-(155,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (155,1)-(155,3) = "::" │ ├── name: :% @@ -997,9 +1091,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (157,0)-(157,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (157,0)-(157,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (157,1)-(157,3) = "::" │ ├── name: :% @@ -1022,9 +1117,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (159,0)-(159,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (159,0)-(159,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (159,1)-(159,3) = "::" │ ├── name: :% @@ -1047,9 +1143,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (161,0)-(161,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (161,0)-(161,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (161,1)-(161,3) = "::" │ ├── name: :% @@ -1072,9 +1169,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (163,0)-(163,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (163,0)-(163,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (163,1)-(163,3) = "::" │ ├── name: :% @@ -1085,13 +1183,15 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ ConstantReadNode (location: (163,4)-(163,5)) + │ │ ├── flags: ∅ │ │ └── name: :I │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (165,0)-(165,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (165,0)-(165,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (165,1)-(165,3) = "::" │ ├── name: :% @@ -1102,13 +1202,15 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ ConstantReadNode (location: (165,4)-(165,5)) + │ │ ├── flags: ∅ │ │ └── name: :W │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (167,0)-(167,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (167,0)-(167,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (167,1)-(167,3) = "::" │ ├── name: :| @@ -1118,9 +1220,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (169,0)-(169,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (169,0)-(169,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (169,1)-(169,3) = "::" │ ├── name: :+ @@ -1130,9 +1233,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (171,0)-(171,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (171,0)-(171,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (171,1)-(171,3) = "::" │ ├── name: :/ @@ -1142,9 +1246,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (173,0)-(173,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (173,0)-(173,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (173,1)-(173,3) = "::" │ ├── name: :* @@ -1154,9 +1259,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (175,0)-(175,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (175,0)-(175,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (175,1)-(175,3) = "::" │ ├── name: :** @@ -1166,9 +1272,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (177,0)-(177,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (177,0)-(177,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (177,1)-(177,3) = "::" │ ├── name: :~ @@ -1178,11 +1285,13 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ ConstantPathNode (location: (179,0)-(180,1)) + │ ├── flags: newline │ ├── parent: │ │ @ CallNode (location: (179,0)-(179,4)) │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ ConstantReadNode (location: (179,0)-(179,1)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :A │ │ ├── call_operator_loc: (179,1)-(179,3) = "::" │ │ ├── name: :_ @@ -1195,12 +1304,13 @@ │ ├── delimiter_loc: (179,4)-(179,6) = "::" │ └── name_loc: (180,0)-(180,1) = "C" └── @ RangeNode (location: (182,0)-(184,10)) - ├── flags: ∅ + ├── flags: newline ├── left: │ @ CallNode (location: (182,0)-(182,4)) │ ├── flags: ∅ │ ├── receiver: │ │ @ ConstantReadNode (location: (182,0)-(182,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (182,1)-(182,3) = "::" │ ├── name: :_ @@ -1214,6 +1324,7 @@ │ ├── flags: ∅ │ ├── receiver: │ │ @ ConstantReadNode (location: (184,0)-(184,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (184,1)-(184,3) = "::" │ ├── name: :__END__ diff --git a/test/prism/snapshots/dash_heredocs.txt b/test/prism/snapshots/dash_heredocs.txt index bd2b05ea0d0717..1ca8b4f73dcdbb 100644 --- a/test/prism/snapshots/dash_heredocs.txt +++ b/test/prism/snapshots/dash_heredocs.txt @@ -1,16 +1,18 @@ @ ProgramNode (location: (1,0)-(57,11)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(57,11)) + ├── flags: ∅ └── body: (length: 13) ├── @ StringNode (location: (1,0)-(1,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (1,0)-(1,6) = "<<-EOF" │ ├── content_loc: (2,0)-(3,0) = " a\n" │ ├── closing_loc: (3,0)-(4,0) = "EOF\n" │ └── unescaped: " a\n" ├── @ CallNode (location: (5,0)-(5,20)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ StringNode (location: (5,0)-(5,8)) │ │ ├── flags: ∅ @@ -35,6 +37,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ InterpolatedXStringNode (location: (11,0)-(11,8)) + │ ├── flags: newline │ ├── opening_loc: (11,0)-(11,8) = "<<-`EOF`" │ ├── parts: (length: 3) │ │ ├── @ StringNode (location: (12,0)-(13,0)) @@ -44,9 +47,11 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: " a\n" │ │ ├── @ EmbeddedStatementsNode (location: (13,0)-(13,4)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (13,0)-(13,2) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (13,2)-(13,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (13,2)-(13,3)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -67,31 +72,33 @@ │ │ └── unescaped: "\n" │ └── closing_loc: (14,0)-(15,0) = "EOF\n" ├── @ StringNode (location: (16,0)-(16,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (16,0)-(16,6) = "<<-EOF" │ ├── content_loc: (17,0)-(18,0) = " a\n" │ ├── closing_loc: (18,0)-(19,0) = "EOF\n" │ └── unescaped: " a\n" ├── @ StringNode (location: (20,0)-(20,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (20,0)-(20,6) = "<<-EOF" │ ├── content_loc: (21,0)-(23,0) = " a\n b\n" │ ├── closing_loc: (23,0)-(24,0) = " EOF\n" │ └── unescaped: " a\n b\n" ├── @ InterpolatedStringNode (location: (25,0)-(25,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (25,0)-(25,8) = "<<-\"EOF\"" │ ├── parts: (length: 3) │ │ ├── @ StringNode (location: (26,0)-(27,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (26,0)-(27,0) = " a\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: " a\n" │ │ ├── @ EmbeddedStatementsNode (location: (27,0)-(27,4)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (27,0)-(27,2) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (27,2)-(27,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (27,2)-(27,3)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -105,26 +112,28 @@ │ │ │ │ └── block: ∅ │ │ │ └── closing_loc: (27,3)-(27,4) = "}" │ │ └── @ StringNode (location: (27,4)-(28,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (27,4)-(28,0) = "\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "\n" │ └── closing_loc: (28,0)-(29,0) = "EOF\n" ├── @ InterpolatedStringNode (location: (30,0)-(30,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (30,0)-(30,6) = "<<-EOF" │ ├── parts: (length: 3) │ │ ├── @ StringNode (location: (31,0)-(32,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (31,0)-(32,0) = " a\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: " a\n" │ │ ├── @ EmbeddedStatementsNode (location: (32,0)-(32,4)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (32,0)-(32,2) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (32,2)-(32,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (32,2)-(32,3)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -138,38 +147,38 @@ │ │ │ │ └── block: ∅ │ │ │ └── closing_loc: (32,3)-(32,4) = "}" │ │ └── @ StringNode (location: (32,4)-(33,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (32,4)-(33,0) = "\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "\n" │ └── closing_loc: (33,0)-(34,0) = "EOF\n" ├── @ StringNode (location: (35,0)-(35,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (35,0)-(35,2) = "%#" │ ├── content_loc: (35,2)-(35,5) = "abc" │ ├── closing_loc: (35,5)-(35,6) = "#" │ └── unescaped: "abc" ├── @ StringNode (location: (37,0)-(37,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (37,0)-(37,6) = "<<-EOF" │ ├── content_loc: (38,0)-(40,0) = " a\n b\n" │ ├── closing_loc: (40,0)-(41,0) = "EOF\n" │ └── unescaped: " a\n b\n" ├── @ StringNode (location: (42,0)-(42,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (42,0)-(42,5) = "<<-''" │ ├── content_loc: (43,0)-(43,0) = "" │ ├── closing_loc: (43,0)-(44,0) = "\n" │ └── unescaped: "" ├── @ StringNode (location: (45,0)-(45,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (45,0)-(45,8) = "<<-'EOF'" │ ├── content_loc: (46,0)-(47,0) = " a \#{1}\n" │ ├── closing_loc: (47,0)-(48,0) = "EOF\n" │ └── unescaped: " a \#{1}\n" ├── @ CallNode (location: (49,0)-(49,11)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ StringNode (location: (49,0)-(49,4)) │ │ ├── flags: ∅ @@ -190,22 +199,24 @@ │ │ ├── opening_loc: (49,7)-(49,11) = "<<-B" │ │ ├── parts: (length: 3) │ │ │ ├── @ StringNode (location: (52,0)-(53,2)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── content_loc: (52,0)-(53,2) = " b\n " │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: " b\n " │ │ │ ├── @ EmbeddedStatementsNode (location: (53,2)-(54,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── opening_loc: (53,2)-(53,4) = "\#{" │ │ │ │ ├── statements: │ │ │ │ │ @ StatementsNode (location: (53,4)-(53,5)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ IntegerNode (location: (53,4)-(53,5)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 2 │ │ │ │ └── closing_loc: (54,2)-(54,3) = "}" │ │ │ └── @ StringNode (location: (54,3)-(55,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (54,3)-(55,0) = "\n" │ │ │ ├── closing_loc: ∅ @@ -214,7 +225,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ └── @ CallNode (location: (57,0)-(57,11)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ StringNode (location: (57,0)-(57,4)) │ ├── flags: ∅ @@ -235,22 +246,24 @@ │ ├── opening_loc: (57,7)-(57,11) = "<<-B" │ ├── parts: (length: 3) │ │ ├── @ StringNode (location: (60,0)-(61,2)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (60,0)-(61,2) = " b\n " │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: " b\n " │ │ ├── @ EmbeddedStatementsNode (location: (61,2)-(62,4)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (61,2)-(61,4) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (62,2)-(62,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ IntegerNode (location: (62,2)-(62,3)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ └── closing_loc: (62,3)-(62,4) = "}" │ │ └── @ StringNode (location: (62,4)-(63,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (62,4)-(63,0) = "\n" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/defined.txt b/test/prism/snapshots/defined.txt index c60173ff37146f..761c7b2ce79a99 100644 --- a/test/prism/snapshots/defined.txt +++ b/test/prism/snapshots/defined.txt @@ -1,37 +1,44 @@ @ ProgramNode (location: (1,0)-(10,1)) +├── flags: ∅ ├── locals: [:x] └── statements: @ StatementsNode (location: (1,0)-(10,1)) + ├── flags: ∅ └── body: (length: 5) ├── @ AndNode (location: (1,0)-(1,25)) + │ ├── flags: newline │ ├── left: │ │ @ DefinedNode (location: (1,0)-(1,10)) + │ │ ├── flags: ∅ │ │ ├── lparen_loc: ∅ │ │ ├── value: │ │ │ @ IntegerNode (location: (1,9)-(1,10)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── rparen_loc: ∅ │ │ └── keyword_loc: (1,0)-(1,8) = "defined?" │ ├── right: │ │ @ DefinedNode (location: (1,15)-(1,25)) + │ │ ├── flags: ∅ │ │ ├── lparen_loc: ∅ │ │ ├── value: │ │ │ @ IntegerNode (location: (1,24)-(1,25)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── rparen_loc: ∅ │ │ └── keyword_loc: (1,15)-(1,23) = "defined?" │ └── operator_loc: (1,11)-(1,14) = "and" ├── @ DefinedNode (location: (3,0)-(3,16)) + │ ├── flags: newline │ ├── lparen_loc: (3,8)-(3,9) = "(" │ ├── value: │ │ @ LocalVariableOperatorWriteNode (location: (3,9)-(3,15)) + │ │ ├── flags: ∅ │ │ ├── name_loc: (3,9)-(3,10) = "x" │ │ ├── binary_operator_loc: (3,11)-(3,13) = "%=" │ │ ├── value: │ │ │ @ IntegerNode (location: (3,14)-(3,15)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── name: :x │ │ ├── binary_operator: :% @@ -39,9 +46,11 @@ │ ├── rparen_loc: (3,15)-(3,16) = ")" │ └── keyword_loc: (3,0)-(3,8) = "defined?" ├── @ DefinedNode (location: (5,0)-(5,21)) + │ ├── flags: newline │ ├── lparen_loc: (5,8)-(5,9) = "(" │ ├── value: │ │ @ AndNode (location: (5,9)-(5,20)) + │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ CallNode (location: (5,9)-(5,12)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -68,14 +77,16 @@ │ ├── rparen_loc: (5,20)-(5,21) = ")" │ └── keyword_loc: (5,0)-(5,8) = "defined?" ├── @ DefinedNode (location: (7,0)-(7,10)) + │ ├── flags: newline │ ├── lparen_loc: ∅ │ ├── value: │ │ @ IntegerNode (location: (7,9)-(7,10)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── rparen_loc: ∅ │ └── keyword_loc: (7,0)-(7,8) = "defined?" └── @ DefinedNode (location: (9,0)-(10,1)) + ├── flags: newline ├── lparen_loc: (9,8)-(9,9) = "(" ├── value: │ @ StringNode (location: (9,9)-(9,14)) diff --git a/test/prism/snapshots/dos_endings.txt b/test/prism/snapshots/dos_endings.txt index 1ae15e1e87c081..69d6b7cd7e5f2b 100644 --- a/test/prism/snapshots/dos_endings.txt +++ b/test/prism/snapshots/dos_endings.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(17,20)) +├── flags: ∅ ├── locals: [:x, :a] └── statements: @ StatementsNode (location: (1,0)-(17,20)) + ├── flags: ∅ └── body: (length: 5) ├── @ CallNode (location: (1,0)-(2,12)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :puts @@ -15,17 +17,17 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ InterpolatedStringNode (location: (1,5)-(2,12)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── opening_loc: ∅ │ │ ├── parts: (length: 2) │ │ │ ├── @ StringNode (location: (1,5)-(1,9)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: (1,5)-(1,6) = "\"" │ │ │ │ ├── content_loc: (1,6)-(1,8) = "hi" │ │ │ │ ├── closing_loc: (1,8)-(1,9) = "\"" │ │ │ │ └── unescaped: "hi" │ │ │ └── @ StringNode (location: (2,5)-(2,12)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: (2,5)-(2,6) = "\"" │ │ │ ├── content_loc: (2,6)-(2,11) = "there" │ │ │ ├── closing_loc: (2,11)-(2,12) = "\"" @@ -34,10 +36,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ ArrayNode (location: (4,0)-(5,2)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── elements: (length: 1) │ │ └── @ SymbolNode (location: (4,3)-(5,1)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (4,3)-(5,1) = "a\\\r\nb" │ │ ├── closing_loc: ∅ @@ -45,12 +47,13 @@ │ ├── opening_loc: (4,0)-(4,3) = "%I{" │ └── closing_loc: (5,1)-(5,2) = "}" ├── @ StringNode (location: (7,0)-(7,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (7,0)-(7,4) = "<<-E" │ ├── content_loc: (8,0)-(11,0) = " 1 \\\r\n 2\r\n 3\r\n" │ ├── closing_loc: (11,0)-(12,0) = "E\r\n" │ └── unescaped: " 1 2\n 3\n" ├── @ LocalVariableWriteNode (location: (13,0)-(15,0)) + │ ├── flags: newline │ ├── name: :x │ ├── depth: 0 │ ├── name_loc: (13,0)-(13,1) = "x" @@ -63,6 +66,7 @@ │ │ └── unescaped: "" │ └── operator_loc: (13,2)-(13,3) = "=" └── @ LocalVariableWriteNode (location: (17,0)-(17,20)) + ├── flags: newline ├── name: :a ├── depth: 0 ├── name_loc: (17,0)-(17,1) = "a" @@ -82,17 +86,17 @@ │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ InterpolatedStringNode (location: (17,8)-(17,14)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: static_literal │ │ │ ├── opening_loc: (17,8)-(17,14) = "<<~EOF" │ │ │ ├── parts: (length: 2) │ │ │ │ ├── @ StringNode (location: (18,0)-(19,0)) - │ │ │ │ │ ├── flags: frozen + │ │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── content_loc: (18,0)-(19,0) = "\r\n" │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── unescaped: "\n" │ │ │ │ └── @ StringNode (location: (19,0)-(20,0)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── content_loc: (19,0)-(20,0) = " baz\r\n" │ │ │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/dstring.txt b/test/prism/snapshots/dstring.txt index 3978a2f0879e50..b3ece4051372b4 100644 --- a/test/prism/snapshots/dstring.txt +++ b/test/prism/snapshots/dstring.txt @@ -1,28 +1,32 @@ @ ProgramNode (location: (1,0)-(29,1)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(29,1)) + ├── flags: ∅ └── body: (length: 8) ├── @ StringNode (location: (1,0)-(2,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (1,0)-(1,1) = "\"" │ ├── content_loc: (1,1)-(2,5) = "foo\n bar" │ ├── closing_loc: (2,5)-(2,6) = "\"" │ └── unescaped: "foo\n bar" ├── @ InterpolatedStringNode (location: (4,0)-(5,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (4,0)-(4,1) = "\"" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (4,1)-(5,2)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (4,1)-(5,2) = "foo\n " │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "foo\n " │ │ └── @ EmbeddedStatementsNode (location: (5,2)-(5,8)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (5,2)-(5,4) = "\#{" │ │ ├── statements: │ │ │ @ StatementsNode (location: (5,4)-(5,7)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (5,4)-(5,7)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -37,48 +41,48 @@ │ │ └── closing_loc: (5,7)-(5,8) = "}" │ └── closing_loc: (5,8)-(5,9) = "\"" ├── @ InterpolatedStringNode (location: (7,0)-(9,2)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── opening_loc: ∅ │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (7,0)-(8,2)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: (7,0)-(7,1) = "\"" │ │ │ ├── content_loc: (7,1)-(8,1) = "fo\no" │ │ │ ├── closing_loc: (8,1)-(8,2) = "\"" │ │ │ └── unescaped: "fo\no" │ │ └── @ StringNode (location: (8,3)-(9,2)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: (8,3)-(8,4) = "\"" │ │ ├── content_loc: (8,4)-(9,1) = "ba\nr" │ │ ├── closing_loc: (9,1)-(9,2) = "\"" │ │ └── unescaped: "ba\nr" │ └── closing_loc: ∅ ├── @ StringNode (location: (11,0)-(13,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (11,0)-(11,1) = "\"" │ ├── content_loc: (11,1)-(13,0) = "\nfoo\\\n" │ ├── closing_loc: (13,0)-(13,1) = "\"" │ └── unescaped: "\nfoo" ├── @ StringNode (location: (15,0)-(17,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (15,0)-(15,1) = "\"" │ ├── content_loc: (15,1)-(17,0) = "\nfoo\\\\\n" │ ├── closing_loc: (17,0)-(17,1) = "\"" │ └── unescaped: "\nfoo\\\n" ├── @ StringNode (location: (19,0)-(21,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (19,0)-(19,1) = "\"" │ ├── content_loc: (19,1)-(21,0) = "\nfoo\\\\\\\n" │ ├── closing_loc: (21,0)-(21,1) = "\"" │ └── unescaped: "\nfoo\\" ├── @ StringNode (location: (23,0)-(25,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (23,0)-(23,1) = "\"" │ ├── content_loc: (23,1)-(25,0) = "\nfoo\\\\\\\\\n" │ ├── closing_loc: (25,0)-(25,1) = "\"" │ └── unescaped: "\nfoo\\\\\n" └── @ StringNode (location: (27,0)-(29,1)) - ├── flags: ∅ + ├── flags: newline ├── opening_loc: (27,0)-(27,1) = "\"" ├── content_loc: (27,1)-(29,0) = "\nfoo\\\\\\\\\\\n" ├── closing_loc: (29,0)-(29,1) = "\"" diff --git a/test/prism/snapshots/dsym_str.txt b/test/prism/snapshots/dsym_str.txt index 33a5e2da21c3b7..835cbbdc8a753e 100644 --- a/test/prism/snapshots/dsym_str.txt +++ b/test/prism/snapshots/dsym_str.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(2,6)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(2,6)) + ├── flags: ∅ └── body: (length: 1) └── @ SymbolNode (location: (1,0)-(2,6)) - ├── flags: forced_us_ascii_encoding + ├── flags: newline, static_literal, forced_us_ascii_encoding ├── opening_loc: (1,0)-(1,2) = ":\"" ├── value_loc: (1,2)-(2,5) = "foo\n bar" ├── closing_loc: (2,5)-(2,6) = "\"" diff --git a/test/prism/snapshots/embdoc_no_newline_at_end.txt b/test/prism/snapshots/embdoc_no_newline_at_end.txt index 3a21ce5559c982..5756285aaf2bd2 100644 --- a/test/prism/snapshots/embdoc_no_newline_at_end.txt +++ b/test/prism/snapshots/embdoc_no_newline_at_end.txt @@ -1,5 +1,7 @@ @ ProgramNode (location: (1,0)-(1,0)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,0)) + ├── flags: ∅ └── body: (length: 0) diff --git a/test/prism/snapshots/emoji_method_calls.txt b/test/prism/snapshots/emoji_method_calls.txt index 8f71181ac1544a..f6f1bc4162aca6 100644 --- a/test/prism/snapshots/emoji_method_calls.txt +++ b/test/prism/snapshots/emoji_method_calls.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,12)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,12)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,12)) - ├── flags: attribute_write + ├── flags: newline, attribute_write ├── receiver: │ @ CallNode (location: (1,0)-(1,3)) │ ├── flags: variable_call, ignore_visibility @@ -25,7 +27,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (1,11)-(1,12)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── closing_loc: ∅ └── block: ∅ diff --git a/test/prism/snapshots/endless_methods.txt b/test/prism/snapshots/endless_methods.txt index 6e20c0deec6140..50a0f41a67609b 100644 --- a/test/prism/snapshots/endless_methods.txt +++ b/test/prism/snapshots/endless_methods.txt @@ -1,18 +1,22 @@ @ ProgramNode (location: (1,0)-(5,22)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(5,22)) + ├── flags: ∅ └── body: (length: 3) ├── @ DefNode (location: (1,0)-(1,11)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (1,4)-(1,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (1,10)-(1,11)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (1,10)-(1,11)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 1 │ ├── locals: [] │ ├── def_keyword_loc: (1,0)-(1,3) = "def" @@ -22,15 +26,17 @@ │ ├── equal_loc: (1,8)-(1,9) = "=" │ └── end_keyword_loc: ∅ ├── @ DefNode (location: (3,0)-(3,14)) + │ ├── flags: newline │ ├── name: :bar │ ├── name_loc: (3,4)-(3,7) = "bar" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (3,10)-(3,14)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (3,10)-(3,14)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :A @@ -56,21 +62,23 @@ │ ├── equal_loc: (3,8)-(3,9) = "=" │ └── end_keyword_loc: ∅ └── @ DefNode (location: (5,0)-(5,22)) + ├── flags: newline ├── name: :method ├── name_loc: (5,4)-(5,10) = "method" ├── receiver: ∅ ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (5,13)-(5,22)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (5,13)-(5,22)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (5,13)-(5,18)) │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ IntegerNode (location: (5,13)-(5,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── call_operator_loc: ∅ │ │ ├── name: :+ @@ -81,7 +89,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (5,17)-(5,18)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ @@ -94,7 +102,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (5,21)-(5,22)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 3 │ ├── closing_loc: ∅ │ └── block: ∅ diff --git a/test/prism/snapshots/endless_range_in_conditional.txt b/test/prism/snapshots/endless_range_in_conditional.txt index 1802c4faed6cb8..518e96ed5318dc 100644 --- a/test/prism/snapshots/endless_range_in_conditional.txt +++ b/test/prism/snapshots/endless_range_in_conditional.txt @@ -1,20 +1,23 @@ @ ProgramNode (location: (1,0)-(3,12)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,12)) + ├── flags: ∅ └── body: (length: 3) ├── @ IfNode (location: (1,0)-(1,13)) + │ ├── flags: newline │ ├── if_keyword_loc: (1,0)-(1,2) = "if" │ ├── predicate: │ │ @ FlipFlopNode (location: (1,3)-(1,7)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (1,3)-(1,4)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: │ │ │ @ IntegerNode (location: (1,6)-(1,7)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── operator_loc: (1,4)-(1,6) = ".." │ ├── then_keyword_loc: ∅ @@ -22,14 +25,15 @@ │ ├── consequent: ∅ │ └── end_keyword_loc: (1,10)-(1,13) = "end" ├── @ IfNode (location: (2,0)-(2,12)) + │ ├── flags: newline │ ├── if_keyword_loc: (2,0)-(2,2) = "if" │ ├── predicate: │ │ @ FlipFlopNode (location: (2,3)-(2,6)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: ∅ │ │ ├── right: │ │ │ @ IntegerNode (location: (2,5)-(2,6)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── operator_loc: (2,3)-(2,5) = ".." │ ├── then_keyword_loc: ∅ @@ -37,13 +41,14 @@ │ ├── consequent: ∅ │ └── end_keyword_loc: (2,9)-(2,12) = "end" └── @ IfNode (location: (3,0)-(3,12)) + ├── flags: newline ├── if_keyword_loc: (3,0)-(3,2) = "if" ├── predicate: │ @ FlipFlopNode (location: (3,3)-(3,6)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── left: │ │ @ IntegerNode (location: (3,3)-(3,4)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── right: ∅ │ └── operator_loc: (3,4)-(3,6) = ".." diff --git a/test/prism/snapshots/for.txt b/test/prism/snapshots/for.txt index cc4bbc1166fd03..5558209826f117 100644 --- a/test/prism/snapshots/for.txt +++ b/test/prism/snapshots/for.txt @@ -1,29 +1,35 @@ @ ProgramNode (location: (1,0)-(19,22)) +├── flags: ∅ ├── locals: [:i, :j, :k] └── statements: @ StatementsNode (location: (1,0)-(19,22)) + ├── flags: ∅ └── body: (length: 6) ├── @ ForNode (location: (1,0)-(3,3)) + │ ├── flags: newline │ ├── index: │ │ @ LocalVariableTargetNode (location: (1,4)-(1,5)) + │ │ ├── flags: ∅ │ │ ├── name: :i │ │ └── depth: 0 │ ├── collection: │ │ @ RangeNode (location: (1,9)-(1,14)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (1,9)-(1,10)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: │ │ │ @ IntegerNode (location: (1,12)-(1,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 10 │ │ └── operator_loc: (1,10)-(1,12) = ".." │ ├── statements: │ │ @ StatementsNode (location: (2,0)-(2,1)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (2,0)-(2,1)) + │ │ ├── flags: newline │ │ ├── name: :i │ │ └── depth: 0 │ ├── for_keyword_loc: (1,0)-(1,3) = "for" @@ -31,26 +37,30 @@ │ ├── do_keyword_loc: ∅ │ └── end_keyword_loc: (3,0)-(3,3) = "end" ├── @ ForNode (location: (5,0)-(5,22)) + │ ├── flags: newline │ ├── index: │ │ @ LocalVariableTargetNode (location: (5,4)-(5,5)) + │ │ ├── flags: ∅ │ │ ├── name: :i │ │ └── depth: 0 │ ├── collection: │ │ @ RangeNode (location: (5,9)-(5,14)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (5,9)-(5,10)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: │ │ │ @ IntegerNode (location: (5,12)-(5,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 10 │ │ └── operator_loc: (5,10)-(5,12) = ".." │ ├── statements: │ │ @ StatementsNode (location: (5,16)-(5,17)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (5,16)-(5,17)) + │ │ ├── flags: newline │ │ ├── name: :i │ │ └── depth: 0 │ ├── for_keyword_loc: (5,0)-(5,3) = "for" @@ -58,13 +68,17 @@ │ ├── do_keyword_loc: ∅ │ └── end_keyword_loc: (5,19)-(5,22) = "end" ├── @ ForNode (location: (7,0)-(9,3)) + │ ├── flags: newline │ ├── index: │ │ @ MultiTargetNode (location: (7,4)-(7,7)) + │ │ ├── flags: ∅ │ │ ├── lefts: (length: 2) │ │ │ ├── @ LocalVariableTargetNode (location: (7,4)-(7,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :i │ │ │ │ └── depth: 0 │ │ │ └── @ LocalVariableTargetNode (location: (7,6)-(7,7)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :j │ │ │ └── depth: 0 │ │ ├── rest: ∅ @@ -73,20 +87,22 @@ │ │ └── rparen_loc: ∅ │ ├── collection: │ │ @ RangeNode (location: (7,11)-(7,16)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (7,11)-(7,12)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: │ │ │ @ IntegerNode (location: (7,14)-(7,16)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 10 │ │ └── operator_loc: (7,12)-(7,14) = ".." │ ├── statements: │ │ @ StatementsNode (location: (8,0)-(8,1)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (8,0)-(8,1)) + │ │ ├── flags: newline │ │ ├── name: :i │ │ └── depth: 0 │ ├── for_keyword_loc: (7,0)-(7,3) = "for" @@ -94,16 +110,21 @@ │ ├── do_keyword_loc: ∅ │ └── end_keyword_loc: (9,0)-(9,3) = "end" ├── @ ForNode (location: (11,0)-(13,3)) + │ ├── flags: newline │ ├── index: │ │ @ MultiTargetNode (location: (11,4)-(11,9)) + │ │ ├── flags: ∅ │ │ ├── lefts: (length: 3) │ │ │ ├── @ LocalVariableTargetNode (location: (11,4)-(11,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :i │ │ │ │ └── depth: 0 │ │ │ ├── @ LocalVariableTargetNode (location: (11,6)-(11,7)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :j │ │ │ │ └── depth: 0 │ │ │ └── @ LocalVariableTargetNode (location: (11,8)-(11,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :k │ │ │ └── depth: 0 │ │ ├── rest: ∅ @@ -112,20 +133,22 @@ │ │ └── rparen_loc: ∅ │ ├── collection: │ │ @ RangeNode (location: (11,13)-(11,18)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (11,13)-(11,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: │ │ │ @ IntegerNode (location: (11,16)-(11,18)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 10 │ │ └── operator_loc: (11,14)-(11,16) = ".." │ ├── statements: │ │ @ StatementsNode (location: (12,0)-(12,1)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (12,0)-(12,1)) + │ │ ├── flags: newline │ │ ├── name: :i │ │ └── depth: 0 │ ├── for_keyword_loc: (11,0)-(11,3) = "for" @@ -133,26 +156,30 @@ │ ├── do_keyword_loc: ∅ │ └── end_keyword_loc: (13,0)-(13,3) = "end" ├── @ ForNode (location: (15,0)-(17,3)) + │ ├── flags: newline │ ├── index: │ │ @ LocalVariableTargetNode (location: (15,4)-(15,5)) + │ │ ├── flags: ∅ │ │ ├── name: :i │ │ └── depth: 0 │ ├── collection: │ │ @ RangeNode (location: (15,9)-(15,14)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (15,9)-(15,10)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: │ │ │ @ IntegerNode (location: (15,12)-(15,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 10 │ │ └── operator_loc: (15,10)-(15,12) = ".." │ ├── statements: │ │ @ StatementsNode (location: (16,0)-(16,1)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (16,0)-(16,1)) + │ │ ├── flags: newline │ │ ├── name: :i │ │ └── depth: 0 │ ├── for_keyword_loc: (15,0)-(15,3) = "for" @@ -160,26 +187,30 @@ │ ├── do_keyword_loc: (15,15)-(15,17) = "do" │ └── end_keyword_loc: (17,0)-(17,3) = "end" └── @ ForNode (location: (19,0)-(19,22)) + ├── flags: newline ├── index: │ @ LocalVariableTargetNode (location: (19,4)-(19,5)) + │ ├── flags: ∅ │ ├── name: :i │ └── depth: 0 ├── collection: │ @ RangeNode (location: (19,9)-(19,14)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── left: │ │ @ IntegerNode (location: (19,9)-(19,10)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── right: │ │ @ IntegerNode (location: (19,12)-(19,14)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 10 │ └── operator_loc: (19,10)-(19,12) = ".." ├── statements: │ @ StatementsNode (location: (19,16)-(19,17)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ LocalVariableReadNode (location: (19,16)-(19,17)) + │ ├── flags: newline │ ├── name: :i │ └── depth: 0 ├── for_keyword_loc: (19,0)-(19,3) = "for" diff --git a/test/prism/snapshots/global_variables.txt b/test/prism/snapshots/global_variables.txt index 9f775ed80d399a..17b7728a32d3c2 100644 --- a/test/prism/snapshots/global_variables.txt +++ b/test/prism/snapshots/global_variables.txt @@ -1,190 +1,216 @@ @ ProgramNode (location: (1,0)-(93,4)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(93,4)) + ├── flags: ∅ └── body: (length: 47) ├── @ GlobalVariableReadNode (location: (1,0)-(1,16)) + │ ├── flags: newline │ └── name: :$global_variable ├── @ GlobalVariableReadNode (location: (3,0)-(3,2)) + │ ├── flags: newline │ └── name: :$_ ├── @ GlobalVariableReadNode (location: (5,0)-(5,3)) + │ ├── flags: newline │ └── name: :$-w ├── @ GlobalVariableReadNode (location: (7,0)-(7,10)) + │ ├── flags: newline │ └── name: :$LOAD_PATH ├── @ GlobalVariableReadNode (location: (9,0)-(9,6)) + │ ├── flags: newline │ └── name: :$stdin ├── @ GlobalVariableReadNode (location: (11,0)-(11,7)) + │ ├── flags: newline │ └── name: :$stdout ├── @ GlobalVariableReadNode (location: (13,0)-(13,7)) + │ ├── flags: newline │ └── name: :$stderr ├── @ GlobalVariableReadNode (location: (15,0)-(15,2)) + │ ├── flags: newline │ └── name: :$! ├── @ GlobalVariableReadNode (location: (17,0)-(17,2)) + │ ├── flags: newline │ └── name: :$? ├── @ GlobalVariableReadNode (location: (19,0)-(19,2)) + │ ├── flags: newline │ └── name: :$~ ├── @ BackReferenceReadNode (location: (21,0)-(21,2)) + │ ├── flags: newline │ └── name: :$& ├── @ BackReferenceReadNode (location: (23,0)-(23,2)) + │ ├── flags: newline │ └── name: :$` ├── @ BackReferenceReadNode (location: (25,0)-(25,2)) + │ ├── flags: newline │ └── name: :$' ├── @ BackReferenceReadNode (location: (27,0)-(27,2)) + │ ├── flags: newline │ └── name: :$+ ├── @ GlobalVariableReadNode (location: (29,0)-(29,2)) + │ ├── flags: newline │ └── name: :$: ├── @ GlobalVariableReadNode (location: (31,0)-(31,2)) + │ ├── flags: newline │ └── name: :$; ├── @ GlobalVariableReadNode (location: (33,0)-(33,2)) + │ ├── flags: newline │ └── name: :$, ├── @ GlobalVariableReadNode (location: (35,0)-(35,6)) + │ ├── flags: newline │ └── name: :$DEBUG ├── @ GlobalVariableReadNode (location: (37,0)-(37,9)) + │ ├── flags: newline │ └── name: :$FILENAME ├── @ GlobalVariableReadNode (location: (39,0)-(39,2)) + │ ├── flags: newline │ └── name: :$0 ├── @ GlobalVariableReadNode (location: (41,0)-(41,3)) + │ ├── flags: newline │ └── name: :$-0 ├── @ GlobalVariableReadNode (location: (43,0)-(43,16)) + │ ├── flags: newline │ └── name: :$LOADED_FEATURES ├── @ GlobalVariableReadNode (location: (45,0)-(45,8)) + │ ├── flags: newline │ └── name: :$VERBOSE ├── @ GlobalVariableReadNode (location: (47,0)-(47,3)) + │ ├── flags: newline │ └── name: :$-K ├── @ SymbolNode (location: (49,0)-(49,17)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (49,0)-(49,1) = ":" │ ├── value_loc: (49,1)-(49,17) = "$global_variable" │ ├── closing_loc: ∅ │ └── unescaped: "$global_variable" ├── @ SymbolNode (location: (51,0)-(51,3)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (51,0)-(51,1) = ":" │ ├── value_loc: (51,1)-(51,3) = "$_" │ ├── closing_loc: ∅ │ └── unescaped: "$_" ├── @ SymbolNode (location: (53,0)-(53,4)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (53,0)-(53,1) = ":" │ ├── value_loc: (53,1)-(53,4) = "$-w" │ ├── closing_loc: ∅ │ └── unescaped: "$-w" ├── @ SymbolNode (location: (55,0)-(55,11)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (55,0)-(55,1) = ":" │ ├── value_loc: (55,1)-(55,11) = "$LOAD_PATH" │ ├── closing_loc: ∅ │ └── unescaped: "$LOAD_PATH" ├── @ SymbolNode (location: (57,0)-(57,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (57,0)-(57,1) = ":" │ ├── value_loc: (57,1)-(57,7) = "$stdin" │ ├── closing_loc: ∅ │ └── unescaped: "$stdin" ├── @ SymbolNode (location: (59,0)-(59,8)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (59,0)-(59,1) = ":" │ ├── value_loc: (59,1)-(59,8) = "$stdout" │ ├── closing_loc: ∅ │ └── unescaped: "$stdout" ├── @ SymbolNode (location: (61,0)-(61,8)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (61,0)-(61,1) = ":" │ ├── value_loc: (61,1)-(61,8) = "$stderr" │ ├── closing_loc: ∅ │ └── unescaped: "$stderr" ├── @ SymbolNode (location: (63,0)-(63,3)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (63,0)-(63,1) = ":" │ ├── value_loc: (63,1)-(63,3) = "$!" │ ├── closing_loc: ∅ │ └── unescaped: "$!" ├── @ SymbolNode (location: (65,0)-(65,3)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (65,0)-(65,1) = ":" │ ├── value_loc: (65,1)-(65,3) = "$?" │ ├── closing_loc: ∅ │ └── unescaped: "$?" ├── @ SymbolNode (location: (67,0)-(67,3)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (67,0)-(67,1) = ":" │ ├── value_loc: (67,1)-(67,3) = "$~" │ ├── closing_loc: ∅ │ └── unescaped: "$~" ├── @ SymbolNode (location: (69,0)-(69,3)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (69,0)-(69,1) = ":" │ ├── value_loc: (69,1)-(69,3) = "$&" │ ├── closing_loc: ∅ │ └── unescaped: "$&" ├── @ SymbolNode (location: (71,0)-(71,3)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (71,0)-(71,1) = ":" │ ├── value_loc: (71,1)-(71,3) = "$`" │ ├── closing_loc: ∅ │ └── unescaped: "$`" ├── @ SymbolNode (location: (73,0)-(73,3)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (73,0)-(73,1) = ":" │ ├── value_loc: (73,1)-(73,3) = "$'" │ ├── closing_loc: ∅ │ └── unescaped: "$'" ├── @ SymbolNode (location: (75,0)-(75,3)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (75,0)-(75,1) = ":" │ ├── value_loc: (75,1)-(75,3) = "$+" │ ├── closing_loc: ∅ │ └── unescaped: "$+" ├── @ SymbolNode (location: (77,0)-(77,3)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (77,0)-(77,1) = ":" │ ├── value_loc: (77,1)-(77,3) = "$:" │ ├── closing_loc: ∅ │ └── unescaped: "$:" ├── @ SymbolNode (location: (79,0)-(79,3)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (79,0)-(79,1) = ":" │ ├── value_loc: (79,1)-(79,3) = "$;" │ ├── closing_loc: ∅ │ └── unescaped: "$;" ├── @ SymbolNode (location: (81,0)-(81,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (81,0)-(81,1) = ":" │ ├── value_loc: (81,1)-(81,7) = "$DEBUG" │ ├── closing_loc: ∅ │ └── unescaped: "$DEBUG" ├── @ SymbolNode (location: (83,0)-(83,10)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (83,0)-(83,1) = ":" │ ├── value_loc: (83,1)-(83,10) = "$FILENAME" │ ├── closing_loc: ∅ │ └── unescaped: "$FILENAME" ├── @ SymbolNode (location: (85,0)-(85,3)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (85,0)-(85,1) = ":" │ ├── value_loc: (85,1)-(85,3) = "$0" │ ├── closing_loc: ∅ │ └── unescaped: "$0" ├── @ SymbolNode (location: (87,0)-(87,4)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (87,0)-(87,1) = ":" │ ├── value_loc: (87,1)-(87,4) = "$-0" │ ├── closing_loc: ∅ │ └── unescaped: "$-0" ├── @ SymbolNode (location: (89,0)-(89,17)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (89,0)-(89,1) = ":" │ ├── value_loc: (89,1)-(89,17) = "$LOADED_FEATURES" │ ├── closing_loc: ∅ │ └── unescaped: "$LOADED_FEATURES" ├── @ SymbolNode (location: (91,0)-(91,9)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (91,0)-(91,1) = ":" │ ├── value_loc: (91,1)-(91,9) = "$VERBOSE" │ ├── closing_loc: ∅ │ └── unescaped: "$VERBOSE" └── @ SymbolNode (location: (93,0)-(93,4)) - ├── flags: forced_us_ascii_encoding + ├── flags: newline, static_literal, forced_us_ascii_encoding ├── opening_loc: (93,0)-(93,1) = ":" ├── value_loc: (93,1)-(93,4) = "$-K" ├── closing_loc: ∅ diff --git a/test/prism/snapshots/hashes.txt b/test/prism/snapshots/hashes.txt index 7a3ac4b0eaaf22..8462c28994ee46 100644 --- a/test/prism/snapshots/hashes.txt +++ b/test/prism/snapshots/hashes.txt @@ -1,20 +1,26 @@ @ ProgramNode (location: (1,0)-(28,9)) +├── flags: ∅ ├── locals: [:a] └── statements: @ StatementsNode (location: (1,0)-(28,9)) + ├── flags: ∅ └── body: (length: 10) ├── @ HashNode (location: (1,0)-(1,2)) + │ ├── flags: newline, static_literal │ ├── opening_loc: (1,0)-(1,1) = "{" │ ├── elements: (length: 0) │ └── closing_loc: (1,1)-(1,2) = "}" ├── @ HashNode (location: (3,0)-(4,1)) + │ ├── flags: newline, static_literal │ ├── opening_loc: (3,0)-(3,1) = "{" │ ├── elements: (length: 0) │ └── closing_loc: (4,0)-(4,1) = "}" ├── @ HashNode (location: (6,0)-(6,18)) + │ ├── flags: newline │ ├── opening_loc: (6,0)-(6,1) = "{" │ ├── elements: (length: 2) │ │ ├── @ AssocNode (location: (6,2)-(6,8)) + │ │ │ ├── flags: ∅ │ │ │ ├── key: │ │ │ │ @ CallNode (location: (6,2)-(6,3)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -39,6 +45,7 @@ │ │ │ │ └── block: ∅ │ │ │ └── operator_loc: (6,4)-(6,6) = "=>" │ │ └── @ AssocNode (location: (6,10)-(6,16)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ CallNode (location: (6,10)-(6,11)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -64,9 +71,11 @@ │ │ └── operator_loc: (6,12)-(6,14) = "=>" │ └── closing_loc: (6,17)-(6,18) = "}" ├── @ HashNode (location: (8,0)-(8,15)) + │ ├── flags: newline │ ├── opening_loc: (8,0)-(8,1) = "{" │ ├── elements: (length: 2) │ │ ├── @ AssocNode (location: (8,2)-(8,8)) + │ │ │ ├── flags: ∅ │ │ │ ├── key: │ │ │ │ @ CallNode (location: (8,2)-(8,3)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -91,6 +100,7 @@ │ │ │ │ └── block: ∅ │ │ │ └── operator_loc: (8,4)-(8,6) = "=>" │ │ └── @ AssocSplatNode (location: (8,10)-(8,13)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ CallNode (location: (8,12)-(8,13)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -105,12 +115,14 @@ │ │ └── operator_loc: (8,10)-(8,12) = "**" │ └── closing_loc: (8,14)-(8,15) = "}" ├── @ HashNode (location: (10,0)-(16,5)) + │ ├── flags: newline │ ├── opening_loc: (10,0)-(10,1) = "{" │ ├── elements: (length: 2) │ │ ├── @ AssocNode (location: (11,6)-(11,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (11,6)-(11,8)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (11,6)-(11,7) = "a" │ │ │ │ ├── closing_loc: (11,7)-(11,8) = ":" @@ -128,9 +140,10 @@ │ │ │ │ └── block: ∅ │ │ │ └── operator_loc: ∅ │ │ └── @ AssocNode (location: (12,6)-(12,10)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ SymbolNode (location: (12,6)-(12,8)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (12,6)-(12,7) = "c" │ │ │ ├── closing_loc: (12,7)-(12,8) = ":" @@ -149,12 +162,14 @@ │ │ └── operator_loc: ∅ │ └── closing_loc: (16,4)-(16,5) = "}" ├── @ HashNode (location: (18,0)-(18,25)) + │ ├── flags: newline │ ├── opening_loc: (18,0)-(18,1) = "{" │ ├── elements: (length: 4) │ │ ├── @ AssocNode (location: (18,2)-(18,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (18,2)-(18,4)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (18,2)-(18,3) = "a" │ │ │ │ ├── closing_loc: (18,3)-(18,4) = ":" @@ -172,9 +187,10 @@ │ │ │ │ └── block: ∅ │ │ │ └── operator_loc: ∅ │ │ ├── @ AssocNode (location: (18,8)-(18,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (18,8)-(18,10)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (18,8)-(18,9) = "c" │ │ │ │ ├── closing_loc: (18,9)-(18,10) = ":" @@ -192,6 +208,7 @@ │ │ │ │ └── block: ∅ │ │ │ └── operator_loc: ∅ │ │ ├── @ AssocSplatNode (location: (18,14)-(18,17)) + │ │ │ ├── flags: ∅ │ │ │ ├── value: │ │ │ │ @ CallNode (location: (18,16)-(18,17)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -205,9 +222,10 @@ │ │ │ │ └── block: ∅ │ │ │ └── operator_loc: (18,14)-(18,16) = "**" │ │ └── @ AssocNode (location: (18,19)-(18,23)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ SymbolNode (location: (18,19)-(18,21)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (18,19)-(18,20) = "f" │ │ │ ├── closing_loc: (18,20)-(18,21) = ":" @@ -226,12 +244,14 @@ │ │ └── operator_loc: ∅ │ └── closing_loc: (18,24)-(18,25) = "}" ├── @ HashNode (location: (20,0)-(20,12)) + │ ├── flags: newline │ ├── opening_loc: (20,0)-(20,1) = "{" │ ├── elements: (length: 1) │ │ └── @ AssocNode (location: (20,2)-(20,10)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ SymbolNode (location: (20,2)-(20,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (20,2)-(20,3) = "\"" │ │ │ ├── value_loc: (20,3)-(20,4) = "a" │ │ │ ├── closing_loc: (20,4)-(20,6) = "\":" @@ -260,16 +280,17 @@ │ │ └── operator_loc: ∅ │ └── closing_loc: (20,11)-(20,12) = "}" ├── @ LocalVariableWriteNode (location: (22,0)-(22,5)) + │ ├── flags: newline │ ├── name: :a │ ├── depth: 0 │ ├── name_loc: (22,0)-(22,1) = "a" │ ├── value: │ │ @ IntegerNode (location: (22,4)-(22,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (22,2)-(22,3) = "=" ├── @ CallNode (location: (23,0)-(26,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -279,63 +300,75 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (23,4)-(26,3)) + │ ├── flags: ∅ │ ├── locals: [:b] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (24,2)-(25,20)) + │ │ ├── flags: ∅ │ │ └── body: (length: 2) │ │ ├── @ LocalVariableWriteNode (location: (24,2)-(24,7)) + │ │ │ ├── flags: newline │ │ │ ├── name: :b │ │ │ ├── depth: 0 │ │ │ ├── name_loc: (24,2)-(24,3) = "b" │ │ │ ├── value: │ │ │ │ @ IntegerNode (location: (24,6)-(24,7)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── operator_loc: (24,4)-(24,5) = "=" │ │ └── @ HashNode (location: (25,2)-(25,20)) + │ │ ├── flags: newline │ │ ├── opening_loc: (25,2)-(25,3) = "{" │ │ ├── elements: (length: 4) │ │ │ ├── @ AssocNode (location: (25,4)-(25,6)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── key: │ │ │ │ │ @ SymbolNode (location: (25,4)-(25,6)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── value_loc: (25,4)-(25,5) = "a" │ │ │ │ │ ├── closing_loc: (25,5)-(25,6) = ":" │ │ │ │ │ └── unescaped: "a" │ │ │ │ ├── value: │ │ │ │ │ @ ImplicitNode (location: (25,4)-(25,6)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── value: │ │ │ │ │ @ LocalVariableReadNode (location: (25,4)-(25,6)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── name: :a │ │ │ │ │ └── depth: 1 │ │ │ │ └── operator_loc: ∅ │ │ │ ├── @ AssocNode (location: (25,8)-(25,10)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── key: │ │ │ │ │ @ SymbolNode (location: (25,8)-(25,10)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── value_loc: (25,8)-(25,9) = "b" │ │ │ │ │ ├── closing_loc: (25,9)-(25,10) = ":" │ │ │ │ │ └── unescaped: "b" │ │ │ │ ├── value: │ │ │ │ │ @ ImplicitNode (location: (25,8)-(25,10)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── value: │ │ │ │ │ @ LocalVariableReadNode (location: (25,8)-(25,10)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── name: :b │ │ │ │ │ └── depth: 0 │ │ │ │ └── operator_loc: ∅ │ │ │ ├── @ AssocNode (location: (25,12)-(25,14)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── key: │ │ │ │ │ @ SymbolNode (location: (25,12)-(25,14)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── value_loc: (25,12)-(25,13) = "c" │ │ │ │ │ ├── closing_loc: (25,13)-(25,14) = ":" │ │ │ │ │ └── unescaped: "c" │ │ │ │ ├── value: │ │ │ │ │ @ ImplicitNode (location: (25,12)-(25,14)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── value: │ │ │ │ │ @ CallNode (location: (25,12)-(25,14)) │ │ │ │ │ ├── flags: ignore_visibility @@ -349,36 +382,41 @@ │ │ │ │ │ └── block: ∅ │ │ │ │ └── operator_loc: ∅ │ │ │ └── @ AssocNode (location: (25,16)-(25,18)) + │ │ │ ├── flags: ∅ │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (25,16)-(25,18)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (25,16)-(25,17) = "D" │ │ │ │ ├── closing_loc: (25,17)-(25,18) = ":" │ │ │ │ └── unescaped: "D" │ │ │ ├── value: │ │ │ │ @ ImplicitNode (location: (25,16)-(25,18)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── value: │ │ │ │ @ ConstantReadNode (location: (25,16)-(25,18)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :D │ │ │ └── operator_loc: ∅ │ │ └── closing_loc: (25,19)-(25,20) = "}" │ ├── opening_loc: (23,4)-(23,6) = "do" │ └── closing_loc: (26,0)-(26,3) = "end" └── @ HashNode (location: (28,0)-(28,9)) + ├── flags: newline, static_literal ├── opening_loc: (28,0)-(28,1) = "{" ├── elements: (length: 1) │ └── @ AssocNode (location: (28,2)-(28,7)) + │ ├── flags: static_literal │ ├── key: │ │ @ SymbolNode (location: (28,2)-(28,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (28,2)-(28,3) = "a" │ │ ├── closing_loc: (28,3)-(28,4) = ":" │ │ └── unescaped: "a" │ ├── value: │ │ @ IntegerNode (location: (28,5)-(28,7)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: -1 │ └── operator_loc: ∅ └── closing_loc: (28,8)-(28,9) = "}" diff --git a/test/prism/snapshots/heredoc.txt b/test/prism/snapshots/heredoc.txt index 3ca66dd9897baf..7145f0f7527c27 100644 --- a/test/prism/snapshots/heredoc.txt +++ b/test/prism/snapshots/heredoc.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,6)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,6)) + ├── flags: ∅ └── body: (length: 1) └── @ StringNode (location: (1,0)-(1,6)) - ├── flags: ∅ + ├── flags: newline ├── opening_loc: (1,0)-(1,6) = "< @@ -307,15 +309,15 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (31,6)-(31,7)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (33,0)-(33,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ IntegerNode (location: (33,0)-(33,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── call_operator_loc: ∅ │ ├── name: :== @@ -326,15 +328,15 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (33,5)-(33,6)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (35,0)-(35,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ IntegerNode (location: (35,0)-(35,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── call_operator_loc: ∅ │ ├── name: :=== @@ -345,15 +347,15 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (35,6)-(35,7)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (37,0)-(37,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ IntegerNode (location: (37,0)-(37,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── call_operator_loc: ∅ │ ├── name: :=~ @@ -364,15 +366,15 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (37,5)-(37,6)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (39,0)-(39,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ IntegerNode (location: (39,0)-(39,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── call_operator_loc: ∅ │ ├── name: :> @@ -383,15 +385,15 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (39,4)-(39,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (41,0)-(41,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ IntegerNode (location: (41,0)-(41,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── call_operator_loc: ∅ │ ├── name: :>= @@ -402,15 +404,15 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (41,5)-(41,6)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (43,0)-(43,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ IntegerNode (location: (43,0)-(43,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── call_operator_loc: ∅ │ ├── name: :>> @@ -421,15 +423,15 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (43,5)-(43,6)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (45,0)-(45,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ IntegerNode (location: (45,0)-(45,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── call_operator_loc: ∅ │ ├── name: :^ @@ -440,15 +442,15 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (45,4)-(45,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (47,0)-(47,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ IntegerNode (location: (47,0)-(47,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── call_operator_loc: ∅ │ ├── name: :| @@ -459,35 +461,37 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (47,4)-(47,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ AndNode (location: (49,0)-(49,6)) + │ ├── flags: newline │ ├── left: │ │ @ IntegerNode (location: (49,0)-(49,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── right: │ │ @ IntegerNode (location: (49,5)-(49,6)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ └── operator_loc: (49,2)-(49,4) = "&&" ├── @ AndNode (location: (51,0)-(51,7)) + │ ├── flags: newline │ ├── left: │ │ @ IntegerNode (location: (51,0)-(51,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── right: │ │ @ IntegerNode (location: (51,6)-(51,7)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ └── operator_loc: (51,2)-(51,5) = "and" ├── @ CallNode (location: (53,0)-(53,10)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ IntegerNode (location: (53,0)-(53,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── call_operator_loc: ∅ │ ├── name: :* @@ -501,7 +505,7 @@ │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ IntegerNode (location: (53,4)-(53,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── call_operator_loc: ∅ │ │ ├── name: :** @@ -512,20 +516,20 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (53,9)-(53,10)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 3 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (55,0)-(55,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (55,0)-(55,5)) │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ IntegerNode (location: (55,0)-(55,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── call_operator_loc: ∅ │ │ ├── name: :* @@ -536,7 +540,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (55,4)-(55,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ @@ -549,35 +553,37 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (55,8)-(55,9)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 3 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ OrNode (location: (57,0)-(57,6)) + │ ├── flags: newline │ ├── left: │ │ @ IntegerNode (location: (57,0)-(57,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── right: │ │ @ IntegerNode (location: (57,5)-(57,6)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ └── operator_loc: (57,2)-(57,4) = "or" ├── @ OrNode (location: (59,0)-(59,6)) + │ ├── flags: newline │ ├── left: │ │ @ IntegerNode (location: (59,0)-(59,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── right: │ │ @ IntegerNode (location: (59,5)-(59,6)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ └── operator_loc: (59,2)-(59,4) = "||" ├── @ CallNode (location: (61,0)-(61,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ IntegerNode (location: (61,0)-(61,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── call_operator_loc: ∅ │ ├── name: :+ @@ -591,7 +597,7 @@ │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ IntegerNode (location: (61,4)-(61,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── call_operator_loc: ∅ │ │ ├── name: :* @@ -602,21 +608,23 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (61,8)-(61,9)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 3 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ ├── closing_loc: ∅ │ └── block: ∅ └── @ ParenthesesNode (location: (63,0)-(63,7)) + ├── flags: newline ├── body: │ @ StatementsNode (location: (63,1)-(63,6)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (63,1)-(63,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ IntegerNode (location: (63,1)-(63,2)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── call_operator_loc: ∅ │ ├── name: :+ @@ -627,7 +635,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (63,5)-(63,6)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── closing_loc: ∅ │ └── block: ∅ diff --git a/test/prism/snapshots/keyword_method_names.txt b/test/prism/snapshots/keyword_method_names.txt index 6d59790b070065..6bb4d970845292 100644 --- a/test/prism/snapshots/keyword_method_names.txt +++ b/test/prism/snapshots/keyword_method_names.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(29,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(29,3)) + ├── flags: ∅ └── body: (length: 10) ├── @ DefNode (location: (1,0)-(2,3)) + │ ├── flags: newline │ ├── name: :def │ ├── name_loc: (1,4)-(1,7) = "def" │ ├── receiver: ∅ @@ -17,10 +20,12 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (2,0)-(2,3) = "end" ├── @ DefNode (location: (4,0)-(5,3)) + │ ├── flags: newline │ ├── name: :ensure │ ├── name_loc: (4,9)-(4,15) = "ensure" │ ├── receiver: │ │ @ SelfNode (location: (4,4)-(4,8)) + │ │ └── flags: ∅ │ ├── parameters: ∅ │ ├── body: ∅ │ ├── locals: [] @@ -31,7 +36,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (5,0)-(5,3) = "end" ├── @ CallNode (location: (7,0)-(10,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :private @@ -42,15 +47,17 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ DefNode (location: (7,8)-(10,3)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ ├── name_loc: (7,12)-(7,15) = "foo" │ │ ├── receiver: ∅ │ │ ├── parameters: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (8,2)-(9,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (8,2)-(9,5)) - │ │ │ ├── flags: ignore_visibility + │ │ │ ├── flags: newline, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :bar @@ -60,6 +67,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: │ │ │ @ BlockNode (location: (8,6)-(9,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── locals: [] │ │ │ ├── parameters: ∅ │ │ │ ├── body: ∅ @@ -75,11 +83,13 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ DefNode (location: (12,0)-(13,3)) + │ ├── flags: newline │ ├── name: :m │ ├── name_loc: (12,4)-(12,5) = "m" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (12,6)-(12,14)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (12,6)-(12,7)) │ │ │ ├── flags: ∅ @@ -90,6 +100,7 @@ │ │ ├── keywords: (length: 0) │ │ ├── keyword_rest: │ │ │ @ NoKeywordsParameterNode (location: (12,9)-(12,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (12,9)-(12,11) = "**" │ │ │ └── keyword_loc: (12,11)-(12,14) = "nil" │ │ └── block: ∅ @@ -102,10 +113,12 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (13,0)-(13,3) = "end" ├── @ DefNode (location: (15,0)-(16,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (15,17)-(15,18) = "a" │ ├── receiver: │ │ @ SourceEncodingNode (location: (15,4)-(15,16)) + │ │ └── flags: static_literal │ ├── parameters: ∅ │ ├── body: ∅ │ ├── locals: [] @@ -116,18 +129,19 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (16,0)-(16,3) = "end" ├── @ StringNode (location: (18,0)-(18,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (18,0)-(18,2) = "%{" │ ├── content_loc: (18,2)-(18,5) = "abc" │ ├── closing_loc: (18,5)-(18,6) = "}" │ └── unescaped: "abc" ├── @ StringNode (location: (20,0)-(20,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (20,0)-(20,2) = "%\"" │ ├── content_loc: (20,2)-(20,5) = "abc" │ ├── closing_loc: (20,5)-(20,6) = "\"" │ └── unescaped: "abc" ├── @ DefNode (location: (22,0)-(23,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (22,13)-(22,14) = "a" │ ├── receiver: @@ -144,10 +158,12 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (23,0)-(23,3) = "end" ├── @ DefNode (location: (25,0)-(26,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (25,13)-(25,14) = "a" │ ├── receiver: │ │ @ SourceLineNode (location: (25,4)-(25,12)) + │ │ └── flags: static_literal │ ├── parameters: ∅ │ ├── body: ∅ │ ├── locals: [] @@ -158,10 +174,12 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (26,0)-(26,3) = "end" └── @ DefNode (location: (28,0)-(29,3)) + ├── flags: newline ├── name: :a ├── name_loc: (28,9)-(28,10) = "a" ├── receiver: │ @ NilNode (location: (28,4)-(28,7)) + │ └── flags: static_literal ├── parameters: ∅ ├── body: ∅ ├── locals: [] diff --git a/test/prism/snapshots/keywords.txt b/test/prism/snapshots/keywords.txt index b3d5c5e1c382c1..afe1bad4835557 100644 --- a/test/prism/snapshots/keywords.txt +++ b/test/prism/snapshots/keywords.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(11,8)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(11,8)) + ├── flags: ∅ └── body: (length: 6) ├── @ CallNode (location: (1,0)-(1,12)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -14,34 +16,44 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (1,4)-(1,12)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (1,6)-(1,10)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ RedoNode (location: (1,6)-(1,10)) + │ │ └── flags: newline │ ├── opening_loc: (1,4)-(1,5) = "{" │ └── closing_loc: (1,11)-(1,12) = "}" ├── @ BeginNode (location: (3,0)-(3,25)) + │ ├── flags: newline │ ├── begin_keyword_loc: (3,0)-(3,5) = "begin" │ ├── statements: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (3,7)-(3,20)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (3,7)-(3,13) = "rescue" │ │ ├── exceptions: (length: 0) │ │ ├── operator_loc: ∅ │ │ ├── reference: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (3,15)-(3,20)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ RetryNode (location: (3,15)-(3,20)) + │ │ │ └── flags: newline │ │ └── consequent: ∅ │ ├── else_clause: ∅ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (3,22)-(3,25) = "end" ├── @ SelfNode (location: (5,0)-(5,4)) + │ └── flags: newline ├── @ SourceEncodingNode (location: (7,0)-(7,12)) + │ └── flags: newline, static_literal ├── @ SourceFileNode (location: (9,0)-(9,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ └── filepath: "keywords.txt" └── @ SourceLineNode (location: (11,0)-(11,8)) + └── flags: newline, static_literal diff --git a/test/prism/snapshots/lambda.txt b/test/prism/snapshots/lambda.txt index 0864b103695d7a..90b0b523aa9e83 100644 --- a/test/prism/snapshots/lambda.txt +++ b/test/prism/snapshots/lambda.txt @@ -1,17 +1,22 @@ @ ProgramNode (location: (1,0)-(11,18)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(11,18)) + ├── flags: ∅ └── body: (length: 5) ├── @ LambdaNode (location: (1,0)-(3,4)) + │ ├── flags: newline │ ├── locals: [:foo] │ ├── operator_loc: (1,0)-(1,2) = "->" │ ├── opening_loc: (3,2)-(3,3) = "{" │ ├── closing_loc: (3,3)-(3,4) = "}" │ ├── parameters: │ │ @ BlockParametersNode (location: (1,2)-(3,1)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (2,2)-(2,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (2,2)-(2,5)) │ │ │ │ ├── flags: ∅ @@ -27,14 +32,17 @@ │ │ └── closing_loc: (3,0)-(3,1) = ")" │ └── body: ∅ ├── @ LambdaNode (location: (5,0)-(5,18)) + │ ├── flags: newline │ ├── locals: [:x] │ ├── operator_loc: (5,0)-(5,2) = "->" │ ├── opening_loc: (5,15)-(5,16) = "{" │ ├── closing_loc: (5,17)-(5,18) = "}" │ ├── parameters: │ │ @ BlockParametersNode (location: (5,2)-(5,14)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (5,3)-(5,13)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 0) │ │ │ ├── optionals: (length: 0) │ │ │ ├── rest: ∅ @@ -50,15 +58,17 @@ │ │ │ │ ├── opening_loc: (5,6)-(5,7) = "\"" │ │ │ │ ├── parts: (length: 2) │ │ │ │ │ ├── @ StringNode (location: (5,7)-(5,8)) - │ │ │ │ │ │ ├── flags: frozen + │ │ │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ │ ├── content_loc: (5,7)-(5,8) = "b" │ │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ │ └── unescaped: "b" │ │ │ │ │ └── @ EmbeddedStatementsNode (location: (5,8)-(5,12)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── opening_loc: (5,8)-(5,10) = "\#{" │ │ │ │ │ ├── statements: │ │ │ │ │ │ @ StatementsNode (location: (5,10)-(5,11)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ │ └── @ CallNode (location: (5,10)-(5,11)) │ │ │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -79,14 +89,17 @@ │ │ └── closing_loc: (5,13)-(5,14) = ")" │ └── body: ∅ ├── @ LambdaNode (location: (7,0)-(7,15)) + │ ├── flags: newline │ ├── locals: [:a] │ ├── operator_loc: (7,0)-(7,2) = "->" │ ├── opening_loc: (7,13)-(7,14) = "{" │ ├── closing_loc: (7,14)-(7,15) = "}" │ ├── parameters: │ │ @ BlockParametersNode (location: (7,2)-(7,12)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (7,3)-(7,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 0) │ │ │ ├── optionals: (length: 0) │ │ │ ├── rest: ∅ @@ -119,7 +132,7 @@ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── arguments: (length: 1) │ │ │ │ │ └── @ IntegerNode (location: (7,10)-(7,11)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 3 │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── block: ∅ @@ -130,14 +143,17 @@ │ │ └── closing_loc: (7,11)-(7,12) = ")" │ └── body: ∅ ├── @ LambdaNode (location: (9,0)-(9,19)) + │ ├── flags: newline │ ├── locals: [:foo] │ ├── operator_loc: (9,0)-(9,2) = "->" │ ├── opening_loc: (9,13)-(9,15) = "do" │ ├── closing_loc: (9,16)-(9,19) = "end" │ ├── parameters: │ │ @ BlockParametersNode (location: (9,3)-(9,12)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (9,3)-(9,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 0) │ │ │ ├── optionals: (length: 1) │ │ │ │ └── @ OptionalParameterNode (location: (9,3)-(9,12)) @@ -166,14 +182,17 @@ │ │ └── closing_loc: ∅ │ └── body: ∅ └── @ LambdaNode (location: (11,0)-(11,18)) + ├── flags: newline ├── locals: [:foo] ├── operator_loc: (11,0)-(11,2) = "->" ├── opening_loc: (11,12)-(11,14) = "do" ├── closing_loc: (11,15)-(11,18) = "end" ├── parameters: │ @ BlockParametersNode (location: (11,3)-(11,11)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (11,3)-(11,11)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ diff --git a/test/prism/snapshots/method_calls.txt b/test/prism/snapshots/method_calls.txt index 6082b567f7b4d5..4bf8f3c710b4d7 100644 --- a/test/prism/snapshots/method_calls.txt +++ b/test/prism/snapshots/method_calls.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(156,19)) +├── flags: ∅ ├── locals: [:foo] └── statements: @ StatementsNode (location: (1,0)-(156,19)) + ├── flags: ∅ └── body: (length: 67) ├── @ CallNode (location: (1,0)-(1,14)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (1,0)-(1,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -33,7 +35,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (3,0)-(3,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (3,0)-(3,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -76,7 +78,7 @@ │ ├── closing_loc: (3,8)-(3,9) = ")" │ └── block: ∅ ├── @ CallNode (location: (5,0)-(5,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (5,0)-(5,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -96,7 +98,7 @@ │ ├── closing_loc: (5,4)-(5,5) = ")" │ └── block: ∅ ├── @ CallNode (location: (7,0)-(9,7)) - │ ├── flags: safe_navigation + │ ├── flags: newline, safe_navigation │ ├── receiver: │ │ @ CallNode (location: (7,0)-(8,6)) │ │ ├── flags: ∅ @@ -126,7 +128,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (11,0)-(11,2)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :a! @@ -136,7 +138,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (13,0)-(13,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (13,0)-(13,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -156,7 +158,7 @@ │ ├── closing_loc: (13,3)-(13,4) = ")" │ └── block: ∅ ├── @ CallNode (location: (15,0)-(15,11)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (15,0)-(15,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -177,18 +179,18 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 3) │ │ ├── @ IntegerNode (location: (15,3)-(15,4)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── @ IntegerNode (location: (15,6)-(15,7)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── @ IntegerNode (location: (15,9)-(15,10)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 3 │ ├── closing_loc: (15,10)-(15,11) = ")" │ └── block: ∅ ├── @ CallNode (location: (17,0)-(17,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (17,0)-(17,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -208,7 +210,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (19,0)-(19,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (19,0)-(19,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -241,7 +243,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (21,0)-(21,11)) - │ ├── flags: attribute_write + │ ├── flags: newline, attribute_write │ ├── receiver: │ │ @ CallNode (location: (21,0)-(21,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -262,12 +264,12 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (21,10)-(21,11)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (23,0)-(23,2)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :a? @@ -277,7 +279,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (25,0)-(25,8)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :a @@ -287,6 +289,7 @@ │ ├── closing_loc: (25,8)-(25,9) = ")" │ └── block: │ @ BlockArgumentNode (location: (25,2)-(25,8)) + │ ├── flags: ∅ │ ├── expression: │ │ @ CallNode (location: (25,3)-(25,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -300,7 +303,7 @@ │ │ └── block: ∅ │ └── operator_loc: (25,2)-(25,3) = "&" ├── @ CallNode (location: (27,0)-(27,11)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :a @@ -314,6 +317,7 @@ │ │ ├── flags: ∅ │ │ └── elements: (length: 1) │ │ └── @ AssocSplatNode (location: (27,2)-(27,10)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ CallNode (location: (27,4)-(27,10)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -329,7 +333,7 @@ │ ├── closing_loc: (27,10)-(27,11) = ")" │ └── block: ∅ ├── @ CallNode (location: (29,0)-(29,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (29,0)-(29,3)) │ │ ├── flags: ∅ @@ -359,7 +363,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (31,0)-(31,7)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :a @@ -392,7 +396,7 @@ │ ├── closing_loc: (31,6)-(31,7) = ")" │ └── block: ∅ ├── @ CallNode (location: (33,0)-(33,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :a @@ -402,7 +406,7 @@ │ ├── closing_loc: (33,2)-(33,3) = ")" │ └── block: ∅ ├── @ CallNode (location: (35,0)-(35,8)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :a @@ -413,6 +417,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ SplatNode (location: (35,2)-(35,7)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (35,2)-(35,3) = "*" │ │ └── expression: │ │ @ CallNode (location: (35,3)-(35,7)) @@ -428,7 +433,7 @@ │ ├── closing_loc: (35,7)-(35,8) = ")" │ └── block: ∅ ├── @ CallNode (location: (37,0)-(37,6)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :a @@ -461,7 +466,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (39,0)-(39,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (39,0)-(39,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -504,6 +509,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ MultiWriteNode (location: (41,0)-(41,23)) + │ ├── flags: newline │ ├── lefts: (length: 2) │ │ ├── @ CallTargetNode (location: (41,0)-(41,7)) │ │ │ ├── flags: ∅ @@ -544,18 +550,18 @@ │ ├── operator_loc: (41,17)-(41,18) = "=" │ └── value: │ @ ArrayNode (location: (41,19)-(41,23)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 2) │ │ ├── @ IntegerNode (location: (41,19)-(41,20)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ IntegerNode (location: (41,22)-(41,23)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── opening_loc: ∅ │ └── closing_loc: ∅ ├── @ CallNode (location: (43,0)-(43,4)) - │ ├── flags: safe_navigation + │ ├── flags: newline, safe_navigation │ ├── receiver: │ │ @ CallNode (location: (43,0)-(43,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -575,7 +581,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (45,0)-(45,5)) - │ ├── flags: safe_navigation + │ ├── flags: newline, safe_navigation │ ├── receiver: │ │ @ CallNode (location: (45,0)-(45,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -595,7 +601,7 @@ │ ├── closing_loc: (45,4)-(45,5) = ")" │ └── block: ∅ ├── @ CallNode (location: (47,0)-(47,7)) - │ ├── flags: safe_navigation + │ ├── flags: newline, safe_navigation │ ├── receiver: │ │ @ CallNode (location: (47,0)-(47,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -628,7 +634,7 @@ │ ├── closing_loc: (47,6)-(47,7) = ")" │ └── block: ∅ ├── @ CallNode (location: (49,0)-(49,6)) - │ ├── flags: safe_navigation + │ ├── flags: newline, safe_navigation │ ├── receiver: │ │ @ CallNode (location: (49,0)-(49,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -648,11 +654,14 @@ │ ├── closing_loc: (49,5)-(49,6) = ")" │ └── block: ∅ ├── @ IfNode (location: (51,0)-(51,33)) + │ ├── flags: newline │ ├── if_keyword_loc: (51,11)-(51,13) = "if" │ ├── predicate: │ │ @ AndNode (location: (51,14)-(51,33)) + │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ OrNode (location: (51,14)-(51,25)) + │ │ │ ├── flags: ∅ │ │ │ ├── left: │ │ │ │ @ CallNode (location: (51,14)-(51,18)) │ │ │ │ ├── flags: ignore_visibility @@ -691,9 +700,10 @@ │ ├── then_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (51,0)-(51,10)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (51,0)-(51,10)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :foo @@ -704,13 +714,13 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 2) │ │ │ ├── @ SymbolNode (location: (51,4)-(51,6)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (51,4)-(51,5) = ":" │ │ │ │ ├── value_loc: (51,5)-(51,6) = "a" │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: "a" │ │ │ └── @ SymbolNode (location: (51,8)-(51,10)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (51,8)-(51,9) = ":" │ │ │ ├── value_loc: (51,9)-(51,10) = "b" │ │ │ ├── closing_loc: ∅ @@ -720,7 +730,7 @@ │ ├── consequent: ∅ │ └── end_keyword_loc: ∅ ├── @ CallNode (location: (53,0)-(56,1)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -731,13 +741,13 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 2) │ │ ├── @ SymbolNode (location: (53,4)-(53,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (53,4)-(53,5) = ":" │ │ │ ├── value_loc: (53,5)-(53,6) = "a" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a" │ │ └── @ SymbolNode (location: (55,2)-(55,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (55,2)-(55,3) = ":" │ │ ├── value_loc: (55,3)-(55,4) = "b" │ │ ├── closing_loc: ∅ @@ -745,7 +755,7 @@ │ ├── closing_loc: (56,0)-(56,1) = ")" │ └── block: ∅ ├── @ CallNode (location: (58,0)-(58,10)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -756,6 +766,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ SplatNode (location: (58,4)-(58,9)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (58,4)-(58,5) = "*" │ │ └── expression: │ │ @ CallNode (location: (58,5)-(58,9)) @@ -771,7 +782,7 @@ │ ├── closing_loc: (58,9)-(58,10) = ")" │ └── block: ∅ ├── @ CallNode (location: (60,0)-(60,39)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -782,7 +793,7 @@ │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 2) │ │ ├── @ SymbolNode (location: (60,4)-(60,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (60,4)-(60,5) = ":" │ │ │ ├── value_loc: (60,5)-(60,6) = "a" │ │ │ ├── closing_loc: ∅ @@ -791,25 +802,26 @@ │ │ ├── flags: symbol_keys │ │ └── elements: (length: 2) │ │ ├── @ AssocNode (location: (60,8)-(60,22)) + │ │ │ ├── flags: ∅ │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (60,8)-(60,10)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (60,8)-(60,9) = ":" │ │ │ │ ├── value_loc: (60,9)-(60,10) = "h" │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: "h" │ │ │ ├── value: │ │ │ │ @ ArrayNode (location: (60,14)-(60,22)) - │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── flags: static_literal │ │ │ │ ├── elements: (length: 2) │ │ │ │ │ ├── @ SymbolNode (location: (60,15)-(60,17)) - │ │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ │ ├── opening_loc: (60,15)-(60,16) = ":" │ │ │ │ │ │ ├── value_loc: (60,16)-(60,17) = "x" │ │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ │ └── unescaped: "x" │ │ │ │ │ └── @ SymbolNode (location: (60,19)-(60,21)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: (60,19)-(60,20) = ":" │ │ │ │ │ ├── value_loc: (60,20)-(60,21) = "y" │ │ │ │ │ ├── closing_loc: ∅ @@ -818,16 +830,17 @@ │ │ │ │ └── closing_loc: (60,21)-(60,22) = "]" │ │ │ └── operator_loc: (60,11)-(60,13) = "=>" │ │ └── @ AssocNode (location: (60,24)-(60,32)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (60,24)-(60,26)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (60,24)-(60,25) = ":" │ │ │ ├── value_loc: (60,25)-(60,26) = "a" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a" │ │ ├── value: │ │ │ @ SymbolNode (location: (60,30)-(60,32)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (60,30)-(60,31) = ":" │ │ │ ├── value_loc: (60,31)-(60,32) = "b" │ │ │ ├── closing_loc: ∅ @@ -836,16 +849,17 @@ │ ├── closing_loc: (60,39)-(60,40) = ")" │ └── block: │ @ BlockArgumentNode (location: (60,34)-(60,39)) + │ ├── flags: ∅ │ ├── expression: │ │ @ SymbolNode (location: (60,35)-(60,39)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (60,35)-(60,36) = ":" │ │ ├── value_loc: (60,36)-(60,39) = "bar" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "bar" │ └── operator_loc: (60,34)-(60,35) = "&" ├── @ CallNode (location: (62,0)-(62,49)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :hi @@ -856,45 +870,50 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 2) │ │ ├── @ IntegerNode (location: (62,3)-(62,6)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 123 │ │ └── @ HashNode (location: (62,8)-(62,49)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (62,8)-(62,9) = "{" │ │ ├── elements: (length: 3) │ │ │ ├── @ AssocNode (location: (62,10)-(62,27)) + │ │ │ │ ├── flags: static_literal │ │ │ │ ├── key: │ │ │ │ │ @ SymbolNode (location: (62,10)-(62,16)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: (62,10)-(62,11) = ":" │ │ │ │ │ ├── value_loc: (62,11)-(62,16) = "there" │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── unescaped: "there" │ │ │ │ ├── value: │ │ │ │ │ @ SymbolNode (location: (62,20)-(62,27)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: (62,20)-(62,21) = ":" │ │ │ │ │ ├── value_loc: (62,21)-(62,27) = "friend" │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── unescaped: "friend" │ │ │ │ └── operator_loc: (62,17)-(62,19) = "=>" │ │ │ ├── @ AssocSplatNode (location: (62,29)-(62,33)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── value: │ │ │ │ │ @ HashNode (location: (62,31)-(62,33)) + │ │ │ │ │ ├── flags: static_literal │ │ │ │ │ ├── opening_loc: (62,31)-(62,32) = "{" │ │ │ │ │ ├── elements: (length: 0) │ │ │ │ │ └── closing_loc: (62,32)-(62,33) = "}" │ │ │ │ └── operator_loc: (62,29)-(62,31) = "**" │ │ │ └── @ AssocNode (location: (62,35)-(62,47)) + │ │ │ ├── flags: static_literal │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (62,35)-(62,42)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (62,35)-(62,41) = "whatup" │ │ │ │ ├── closing_loc: (62,41)-(62,42) = ":" │ │ │ │ └── unescaped: "whatup" │ │ │ ├── value: │ │ │ │ @ SymbolNode (location: (62,43)-(62,47)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (62,43)-(62,44) = ":" │ │ │ │ ├── value_loc: (62,44)-(62,47) = "dog" │ │ │ │ ├── closing_loc: ∅ @@ -904,7 +923,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (64,0)-(64,36)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -915,7 +934,7 @@ │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 2) │ │ ├── @ SymbolNode (location: (64,4)-(64,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (64,4)-(64,5) = ":" │ │ │ ├── value_loc: (64,5)-(64,6) = "a" │ │ │ ├── closing_loc: ∅ @@ -924,24 +943,29 @@ │ │ ├── flags: symbol_keys │ │ └── elements: (length: 1) │ │ └── @ AssocNode (location: (64,8)-(64,15)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (64,8)-(64,10)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (64,8)-(64,9) = "b" │ │ │ ├── closing_loc: (64,9)-(64,10) = ":" │ │ │ └── unescaped: "b" │ │ ├── value: │ │ │ @ TrueNode (location: (64,11)-(64,15)) + │ │ │ └── flags: static_literal │ │ └── operator_loc: ∅ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (64,16)-(64,36)) + │ ├── flags: ∅ │ ├── locals: [:a, :b] │ ├── parameters: │ │ @ BlockParametersNode (location: (64,19)-(64,25)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (64,20)-(64,24)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 2) │ │ │ │ ├── @ RequiredParameterNode (location: (64,20)-(64,21)) │ │ │ │ │ ├── flags: ∅ @@ -960,9 +984,10 @@ │ │ └── closing_loc: (64,24)-(64,25) = "|" │ ├── body: │ │ @ StatementsNode (location: (64,26)-(64,32)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (64,26)-(64,32)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :puts @@ -973,6 +998,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ LocalVariableReadNode (location: (64,31)-(64,32)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ ├── closing_loc: ∅ @@ -980,7 +1006,7 @@ │ ├── opening_loc: (64,16)-(64,18) = "do" │ └── closing_loc: (64,33)-(64,36) = "end" ├── @ CallNode (location: (66,0)-(66,17)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :hi @@ -994,16 +1020,17 @@ │ │ ├── flags: symbol_keys │ │ └── elements: (length: 1) │ │ └── @ AssocNode (location: (66,3)-(66,17)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (66,3)-(66,9)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (66,3)-(66,8) = "there" │ │ │ ├── closing_loc: (66,8)-(66,9) = ":" │ │ │ └── unescaped: "there" │ │ ├── value: │ │ │ @ SymbolNode (location: (66,10)-(66,17)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (66,10)-(66,11) = ":" │ │ │ ├── value_loc: (66,11)-(66,17) = "friend" │ │ │ ├── closing_loc: ∅ @@ -1012,7 +1039,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (68,0)-(68,40)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :hi @@ -1026,39 +1053,43 @@ │ │ ├── flags: ∅ │ │ └── elements: (length: 3) │ │ ├── @ AssocNode (location: (68,3)-(68,20)) + │ │ │ ├── flags: static_literal │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (68,3)-(68,9)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (68,3)-(68,4) = ":" │ │ │ │ ├── value_loc: (68,4)-(68,9) = "there" │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: "there" │ │ │ ├── value: │ │ │ │ @ SymbolNode (location: (68,13)-(68,20)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (68,13)-(68,14) = ":" │ │ │ │ ├── value_loc: (68,14)-(68,20) = "friend" │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: "friend" │ │ │ └── operator_loc: (68,10)-(68,12) = "=>" │ │ ├── @ AssocSplatNode (location: (68,22)-(68,26)) + │ │ │ ├── flags: ∅ │ │ │ ├── value: │ │ │ │ @ HashNode (location: (68,24)-(68,26)) + │ │ │ │ ├── flags: static_literal │ │ │ │ ├── opening_loc: (68,24)-(68,25) = "{" │ │ │ │ ├── elements: (length: 0) │ │ │ │ └── closing_loc: (68,25)-(68,26) = "}" │ │ │ └── operator_loc: (68,22)-(68,24) = "**" │ │ └── @ AssocNode (location: (68,28)-(68,40)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (68,28)-(68,35)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (68,28)-(68,34) = "whatup" │ │ │ ├── closing_loc: (68,34)-(68,35) = ":" │ │ │ └── unescaped: "whatup" │ │ ├── value: │ │ │ @ SymbolNode (location: (68,36)-(68,40)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (68,36)-(68,37) = ":" │ │ │ ├── value_loc: (68,37)-(68,40) = "dog" │ │ │ ├── closing_loc: ∅ @@ -1067,7 +1098,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (70,0)-(70,41)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :hi @@ -1081,39 +1112,43 @@ │ │ ├── flags: ∅ │ │ └── elements: (length: 3) │ │ ├── @ AssocNode (location: (70,3)-(70,20)) + │ │ │ ├── flags: static_literal │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (70,3)-(70,9)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (70,3)-(70,4) = ":" │ │ │ │ ├── value_loc: (70,4)-(70,9) = "there" │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: "there" │ │ │ ├── value: │ │ │ │ @ SymbolNode (location: (70,13)-(70,20)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (70,13)-(70,14) = ":" │ │ │ │ ├── value_loc: (70,14)-(70,20) = "friend" │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: "friend" │ │ │ └── operator_loc: (70,10)-(70,12) = "=>" │ │ ├── @ AssocSplatNode (location: (70,22)-(70,26)) + │ │ │ ├── flags: ∅ │ │ │ ├── value: │ │ │ │ @ HashNode (location: (70,24)-(70,26)) + │ │ │ │ ├── flags: static_literal │ │ │ │ ├── opening_loc: (70,24)-(70,25) = "{" │ │ │ │ ├── elements: (length: 0) │ │ │ │ └── closing_loc: (70,25)-(70,26) = "}" │ │ │ └── operator_loc: (70,22)-(70,24) = "**" │ │ └── @ AssocNode (location: (70,28)-(70,40)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (70,28)-(70,35)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (70,28)-(70,34) = "whatup" │ │ │ ├── closing_loc: (70,34)-(70,35) = ":" │ │ │ └── unescaped: "whatup" │ │ ├── value: │ │ │ @ SymbolNode (location: (70,36)-(70,40)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (70,36)-(70,37) = ":" │ │ │ ├── value_loc: (70,37)-(70,40) = "dog" │ │ │ ├── closing_loc: ∅ @@ -1122,7 +1157,7 @@ │ ├── closing_loc: (70,40)-(70,41) = ")" │ └── block: ∅ ├── @ CallNode (location: (72,0)-(72,35)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -1133,44 +1168,50 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ HashNode (location: (72,4)-(72,26)) + │ │ ├── flags: static_literal │ │ ├── opening_loc: (72,4)-(72,5) = "{" │ │ ├── elements: (length: 2) │ │ │ ├── @ AssocNode (location: (72,6)-(72,13)) + │ │ │ │ ├── flags: static_literal │ │ │ │ ├── key: │ │ │ │ │ @ SymbolNode (location: (72,6)-(72,8)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── value_loc: (72,6)-(72,7) = "a" │ │ │ │ │ ├── closing_loc: (72,7)-(72,8) = ":" │ │ │ │ │ └── unescaped: "a" │ │ │ │ ├── value: │ │ │ │ │ @ TrueNode (location: (72,9)-(72,13)) + │ │ │ │ │ └── flags: static_literal │ │ │ │ └── operator_loc: ∅ │ │ │ └── @ AssocNode (location: (72,15)-(72,23)) + │ │ │ ├── flags: static_literal │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (72,15)-(72,17)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (72,15)-(72,16) = "b" │ │ │ │ ├── closing_loc: (72,16)-(72,17) = ":" │ │ │ │ └── unescaped: "b" │ │ │ ├── value: │ │ │ │ @ FalseNode (location: (72,18)-(72,23)) + │ │ │ │ └── flags: static_literal │ │ │ └── operator_loc: ∅ │ │ └── closing_loc: (72,25)-(72,26) = "}" │ ├── closing_loc: (72,35)-(72,36) = ")" │ └── block: │ @ BlockArgumentNode (location: (72,28)-(72,35)) + │ ├── flags: ∅ │ ├── expression: │ │ @ SymbolNode (location: (72,29)-(72,35)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (72,29)-(72,30) = ":" │ │ ├── value_loc: (72,30)-(72,35) = "block" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "block" │ └── operator_loc: (72,28)-(72,29) = "&" ├── @ CallNode (location: (74,0)-(74,20)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :hi @@ -1184,16 +1225,17 @@ │ │ ├── flags: symbol_keys │ │ └── elements: (length: 1) │ │ └── @ AssocNode (location: (74,3)-(74,20)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (74,3)-(74,9)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (74,3)-(74,4) = ":" │ │ │ ├── value_loc: (74,4)-(74,9) = "there" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "there" │ │ ├── value: │ │ │ @ SymbolNode (location: (74,13)-(74,20)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (74,13)-(74,14) = ":" │ │ │ ├── value_loc: (74,14)-(74,20) = "friend" │ │ │ ├── closing_loc: ∅ @@ -1202,7 +1244,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (76,0)-(78,1)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -1213,13 +1255,13 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 2) │ │ ├── @ SymbolNode (location: (76,4)-(76,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (76,4)-(76,5) = ":" │ │ │ ├── value_loc: (76,5)-(76,6) = "a" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a" │ │ └── @ SymbolNode (location: (77,0)-(77,2)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (77,0)-(77,1) = ":" │ │ ├── value_loc: (77,1)-(77,2) = "b" │ │ ├── closing_loc: ∅ @@ -1227,7 +1269,7 @@ │ ├── closing_loc: (78,0)-(78,1) = ")" │ └── block: ∅ ├── @ CallNode (location: (80,0)-(83,1)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -1238,7 +1280,7 @@ │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 2) │ │ ├── @ SymbolNode (location: (81,0)-(81,2)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (81,0)-(81,1) = ":" │ │ │ ├── value_loc: (81,1)-(81,2) = "a" │ │ │ ├── closing_loc: ∅ @@ -1247,16 +1289,17 @@ │ │ ├── flags: symbol_keys │ │ └── elements: (length: 1) │ │ └── @ AssocNode (location: (82,0)-(82,5)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (82,0)-(82,2)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (82,0)-(82,1) = "b" │ │ │ ├── closing_loc: (82,1)-(82,2) = ":" │ │ │ └── unescaped: "b" │ │ ├── value: │ │ │ @ SymbolNode (location: (82,3)-(82,5)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (82,3)-(82,4) = ":" │ │ │ ├── value_loc: (82,4)-(82,5) = "c" │ │ │ ├── closing_loc: ∅ @@ -1265,7 +1308,7 @@ │ ├── closing_loc: (83,0)-(83,1) = ")" │ └── block: ∅ ├── @ CallNode (location: (85,0)-(85,11)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -1275,16 +1318,17 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockArgumentNode (location: (85,4)-(85,11)) + │ ├── flags: ∅ │ ├── expression: │ │ @ SymbolNode (location: (85,5)-(85,11)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (85,5)-(85,6) = ":" │ │ ├── value_loc: (85,6)-(85,11) = "block" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "block" │ └── operator_loc: (85,4)-(85,5) = "&" ├── @ CallNode (location: (87,0)-(87,30)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -1298,40 +1342,45 @@ │ │ ├── flags: symbol_keys │ │ └── elements: (length: 2) │ │ ├── @ AssocNode (location: (87,4)-(87,11)) + │ │ │ ├── flags: static_literal │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (87,4)-(87,6)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (87,4)-(87,5) = "a" │ │ │ │ ├── closing_loc: (87,5)-(87,6) = ":" │ │ │ │ └── unescaped: "a" │ │ │ ├── value: │ │ │ │ @ TrueNode (location: (87,7)-(87,11)) + │ │ │ │ └── flags: static_literal │ │ │ └── operator_loc: ∅ │ │ └── @ AssocNode (location: (87,13)-(87,21)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (87,13)-(87,15)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (87,13)-(87,14) = "b" │ │ │ ├── closing_loc: (87,14)-(87,15) = ":" │ │ │ └── unescaped: "b" │ │ ├── value: │ │ │ @ FalseNode (location: (87,16)-(87,21)) + │ │ │ └── flags: static_literal │ │ └── operator_loc: ∅ │ ├── closing_loc: ∅ │ └── block: │ @ BlockArgumentNode (location: (87,23)-(87,30)) + │ ├── flags: ∅ │ ├── expression: │ │ @ SymbolNode (location: (87,24)-(87,30)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (87,24)-(87,25) = ":" │ │ ├── value_loc: (87,25)-(87,30) = "block" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "block" │ └── operator_loc: (87,23)-(87,24) = "&" ├── @ CallNode (location: (89,0)-(89,21)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :some_func @@ -1342,30 +1391,32 @@ │ │ ├── flags: contains_keywords │ │ └── arguments: (length: 2) │ │ ├── @ IntegerNode (location: (89,10)-(89,11)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ KeywordHashNode (location: (89,13)-(89,21)) │ │ ├── flags: symbol_keys │ │ └── elements: (length: 1) │ │ └── @ AssocNode (location: (89,13)-(89,21)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (89,13)-(89,19)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (89,13)-(89,18) = "kwarg" │ │ │ ├── closing_loc: (89,18)-(89,19) = ":" │ │ │ └── unescaped: "kwarg" │ │ ├── value: │ │ │ @ IntegerNode (location: (89,20)-(89,21)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── operator_loc: ∅ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (91,0)-(91,18)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (91,0)-(91,6)) + │ │ ├── flags: ∅ │ │ └── name: :Kernel │ ├── call_operator_loc: (91,6)-(91,7) = "." │ ├── name: :Integer @@ -1376,12 +1427,12 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (91,15)-(91,17)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 10 │ ├── closing_loc: (91,17)-(91,18) = ")" │ └── block: ∅ ├── @ CallNode (location: (93,0)-(93,10)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (93,0)-(93,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -1401,13 +1452,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (93,7)-(93,10)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: ∅ │ ├── opening_loc: (93,7)-(93,8) = "{" │ └── closing_loc: (93,9)-(93,10) = "}" ├── @ CallNode (location: (95,0)-(95,14)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (95,0)-(95,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1427,21 +1479,26 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (95,8)-(95,14)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (95,10)-(95,12)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ BackReferenceReadNode (location: (95,10)-(95,12)) + │ │ ├── flags: newline │ │ └── name: :$& │ ├── opening_loc: (95,8)-(95,9) = "{" │ └── closing_loc: (95,13)-(95,14) = "}" ├── @ CallNode (location: (97,0)-(97,12)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantPathNode (location: (97,0)-(97,4)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (97,0)-(97,1)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :A │ │ ├── name: :B │ │ ├── delimiter_loc: (97,1)-(97,3) = "::" @@ -1455,7 +1512,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ SymbolNode (location: (97,8)-(97,12)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (97,8)-(97,9) = ":" │ │ ├── value_loc: (97,9)-(97,12) = "foo" │ │ ├── closing_loc: ∅ @@ -1463,11 +1520,13 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (99,0)-(99,13)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantPathNode (location: (99,0)-(99,4)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (99,0)-(99,1)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :A │ │ ├── name: :B │ │ ├── delimiter_loc: (99,1)-(99,3) = "::" @@ -1481,7 +1540,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ SymbolNode (location: (99,8)-(99,12)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (99,8)-(99,9) = ":" │ │ ├── value_loc: (99,9)-(99,12) = "foo" │ │ ├── closing_loc: ∅ @@ -1489,11 +1548,13 @@ │ ├── closing_loc: (99,12)-(99,13) = ")" │ └── block: ∅ ├── @ CallNode (location: (101,0)-(101,17)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantPathNode (location: (101,0)-(101,4)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (101,0)-(101,1)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :A │ │ ├── name: :B │ │ ├── delimiter_loc: (101,1)-(101,3) = "::" @@ -1507,7 +1568,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ SymbolNode (location: (101,8)-(101,12)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (101,8)-(101,9) = ":" │ │ ├── value_loc: (101,9)-(101,12) = "foo" │ │ ├── closing_loc: ∅ @@ -1515,13 +1576,14 @@ │ ├── closing_loc: (101,12)-(101,13) = ")" │ └── block: │ @ BlockNode (location: (101,14)-(101,17)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: ∅ │ ├── opening_loc: (101,14)-(101,15) = "{" │ └── closing_loc: (101,16)-(101,17) = "}" ├── @ CallNode (location: (103,0)-(103,12)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -1535,22 +1597,23 @@ │ │ ├── flags: symbol_keys │ │ └── elements: (length: 1) │ │ └── @ AssocNode (location: (103,4)-(103,11)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (103,4)-(103,8)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (103,4)-(103,5) = "\"" │ │ │ ├── value_loc: (103,5)-(103,6) = "a" │ │ │ ├── closing_loc: (103,6)-(103,8) = "\":" │ │ │ └── unescaped: "a" │ │ ├── value: │ │ │ @ IntegerNode (location: (103,9)-(103,11)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: -1 │ │ └── operator_loc: ∅ │ ├── closing_loc: (103,11)-(103,12) = ")" │ └── block: ∅ ├── @ CallNode (location: (105,0)-(105,28)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -1564,21 +1627,24 @@ │ │ ├── flags: symbol_keys │ │ └── elements: (length: 1) │ │ └── @ AssocNode (location: (105,4)-(105,28)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ SymbolNode (location: (105,4)-(105,8)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (105,4)-(105,7) = "bar" │ │ │ ├── closing_loc: (105,7)-(105,8) = ":" │ │ │ └── unescaped: "bar" │ │ ├── value: │ │ │ @ HashNode (location: (105,9)-(105,28)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (105,9)-(105,10) = "{" │ │ │ ├── elements: (length: 1) │ │ │ │ └── @ AssocNode (location: (105,11)-(105,26)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── key: │ │ │ │ │ @ SymbolNode (location: (105,11)-(105,15)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── value_loc: (105,11)-(105,14) = "baz" │ │ │ │ │ ├── closing_loc: (105,14)-(105,15) = ":" @@ -1595,6 +1661,7 @@ │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── block: │ │ │ │ │ @ BlockNode (location: (105,20)-(105,26)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── locals: [] │ │ │ │ │ ├── parameters: ∅ │ │ │ │ │ ├── body: ∅ @@ -1606,7 +1673,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (107,0)-(107,24)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -1620,18 +1687,21 @@ │ │ ├── flags: symbol_keys │ │ └── elements: (length: 1) │ │ └── @ AssocNode (location: (107,4)-(107,24)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ SymbolNode (location: (107,4)-(107,8)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (107,4)-(107,7) = "bar" │ │ │ ├── closing_loc: (107,7)-(107,8) = ":" │ │ │ └── unescaped: "bar" │ │ ├── value: │ │ │ @ HashNode (location: (107,9)-(107,24)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (107,9)-(107,10) = "{" │ │ │ ├── elements: (length: 1) │ │ │ │ └── @ AssocSplatNode (location: (107,11)-(107,22)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── value: │ │ │ │ │ @ CallNode (location: (107,13)-(107,22)) │ │ │ │ │ ├── flags: ignore_visibility @@ -1644,6 +1714,7 @@ │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── block: │ │ │ │ │ @ BlockNode (location: (107,16)-(107,22)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── locals: [] │ │ │ │ │ ├── parameters: ∅ │ │ │ │ │ ├── body: ∅ @@ -1655,7 +1726,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (109,0)-(109,36)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -1670,9 +1741,11 @@ │ │ ├── opening_loc: (109,4)-(109,5) = "\"" │ │ ├── parts: (length: 1) │ │ │ └── @ EmbeddedStatementsNode (location: (109,5)-(109,28)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (109,5)-(109,7) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (109,7)-(109,27)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (109,7)-(109,27)) │ │ │ │ ├── flags: ∅ @@ -1695,13 +1768,15 @@ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── block: │ │ │ │ @ BlockNode (location: (109,15)-(109,27)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── locals: [] │ │ │ │ ├── parameters: ∅ │ │ │ │ ├── body: │ │ │ │ │ @ StatementsNode (location: (109,18)-(109,23)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ StringNode (location: (109,18)-(109,23)) - │ │ │ │ │ ├── flags: ∅ + │ │ │ │ │ ├── flags: newline │ │ │ │ │ ├── opening_loc: (109,18)-(109,19) = "\"" │ │ │ │ │ ├── content_loc: (109,19)-(109,22) = "baz" │ │ │ │ │ ├── closing_loc: (109,22)-(109,23) = "\"" @@ -1713,13 +1788,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (109,30)-(109,36)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: ∅ │ ├── opening_loc: (109,30)-(109,32) = "do" │ └── closing_loc: (109,33)-(109,36) = "end" ├── @ CallNode (location: (111,0)-(111,28)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -1730,18 +1806,21 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ ClassNode (location: (111,4)-(111,28)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── class_keyword_loc: (111,4)-(111,9) = "class" │ │ ├── constant_path: │ │ │ @ ConstantReadNode (location: (111,10)-(111,13)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Bar │ │ ├── inheritance_operator_loc: ∅ │ │ ├── superclass: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (111,14)-(111,24)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (111,14)-(111,24)) - │ │ │ ├── flags: ignore_visibility + │ │ │ ├── flags: newline, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :baz @@ -1751,6 +1830,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: │ │ │ @ BlockNode (location: (111,18)-(111,24)) + │ │ │ ├── flags: ∅ │ │ │ ├── locals: [] │ │ │ ├── parameters: ∅ │ │ │ ├── body: ∅ @@ -1761,7 +1841,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (113,0)-(113,29)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -1772,16 +1852,19 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ ModuleNode (location: (113,4)-(113,29)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── module_keyword_loc: (113,4)-(113,10) = "module" │ │ ├── constant_path: │ │ │ @ ConstantReadNode (location: (113,11)-(113,14)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Bar │ │ ├── body: │ │ │ @ StatementsNode (location: (113,15)-(113,25)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (113,15)-(113,25)) - │ │ │ ├── flags: ignore_visibility + │ │ │ ├── flags: newline, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :baz @@ -1791,6 +1874,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: │ │ │ @ BlockNode (location: (113,19)-(113,25)) + │ │ │ ├── flags: ∅ │ │ │ ├── locals: [] │ │ │ ├── parameters: ∅ │ │ │ ├── body: ∅ @@ -1801,7 +1885,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (115,0)-(115,16)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -1825,6 +1909,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: │ │ │ @ BlockNode (location: (115,9)-(115,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── locals: [] │ │ │ ├── parameters: ∅ │ │ │ ├── body: ∅ @@ -1835,7 +1920,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (117,0)-(117,28)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :p @@ -1846,15 +1931,17 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ BeginNode (location: (117,2)-(117,28)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: (117,2)-(117,7) = "begin" │ │ ├── statements: │ │ │ @ StatementsNode (location: (117,8)-(117,24)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (117,8)-(117,24)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── receiver: │ │ │ │ @ IntegerNode (location: (117,8)-(117,9)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── call_operator_loc: (117,9)-(117,10) = "." │ │ │ ├── name: :times @@ -1864,13 +1951,15 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: │ │ │ @ BlockNode (location: (117,16)-(117,24)) + │ │ │ ├── flags: ∅ │ │ │ ├── locals: [] │ │ │ ├── parameters: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (117,19)-(117,20)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ IntegerNode (location: (117,19)-(117,20)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: newline, static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── opening_loc: (117,16)-(117,18) = "do" │ │ │ └── closing_loc: (117,21)-(117,24) = "end" @@ -1881,7 +1970,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (119,0)-(124,5)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -1892,12 +1981,13 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 2) │ │ ├── @ SymbolNode (location: (119,4)-(119,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (119,4)-(119,5) = ":" │ │ │ ├── value_loc: (119,5)-(119,6) = "a" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a" │ │ └── @ IfNode (location: (120,2)-(124,5)) + │ │ ├── flags: newline │ │ ├── if_keyword_loc: (120,2)-(120,4) = "if" │ │ ├── predicate: │ │ │ @ CallNode (location: (120,5)-(120,6)) @@ -1913,9 +2003,10 @@ │ │ ├── then_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (121,4)-(123,7)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (121,4)-(123,7)) - │ │ │ ├── flags: ignore_visibility + │ │ │ ├── flags: newline, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :bar @@ -1925,11 +2016,14 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: │ │ │ @ BlockNode (location: (121,8)-(123,7)) + │ │ │ ├── flags: ∅ │ │ │ ├── locals: [:a] │ │ │ ├── parameters: │ │ │ │ @ BlockParametersNode (location: (121,11)-(121,14)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── parameters: │ │ │ │ │ @ ParametersNode (location: (121,12)-(121,13)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── requireds: (length: 1) │ │ │ │ │ │ └── @ RequiredParameterNode (location: (121,12)-(121,13)) │ │ │ │ │ │ ├── flags: ∅ @@ -1945,8 +2039,10 @@ │ │ │ │ └── closing_loc: (121,13)-(121,14) = "|" │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (122,6)-(122,7)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ LocalVariableReadNode (location: (122,6)-(122,7)) + │ │ │ │ ├── flags: newline │ │ │ │ ├── name: :a │ │ │ │ └── depth: 0 │ │ │ ├── opening_loc: (121,8)-(121,10) = "do" @@ -1956,7 +2052,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (126,0)-(135,5)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -1967,7 +2063,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 3) │ │ ├── @ SymbolNode (location: (126,4)-(126,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (126,4)-(126,5) = ":" │ │ │ ├── value_loc: (126,5)-(126,6) = "a" │ │ │ ├── closing_loc: ∅ @@ -1989,9 +2085,10 @@ │ │ │ │ └── block: ∅ │ │ │ └── statements: │ │ │ @ StatementsNode (location: (128,4)-(130,7)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (128,4)-(130,7)) - │ │ │ ├── flags: ignore_visibility + │ │ │ ├── flags: newline, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :bar @@ -2001,11 +2098,14 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: │ │ │ @ BlockNode (location: (128,8)-(130,7)) + │ │ │ ├── flags: ∅ │ │ │ ├── locals: [:a] │ │ │ ├── parameters: │ │ │ │ @ BlockParametersNode (location: (128,11)-(128,14)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── parameters: │ │ │ │ │ @ ParametersNode (location: (128,12)-(128,13)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── requireds: (length: 1) │ │ │ │ │ │ └── @ RequiredParameterNode (location: (128,12)-(128,13)) │ │ │ │ │ │ ├── flags: ∅ @@ -2021,8 +2121,10 @@ │ │ │ │ └── closing_loc: (128,13)-(128,14) = "|" │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (129,6)-(129,7)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ LocalVariableReadNode (location: (129,6)-(129,7)) + │ │ │ │ ├── flags: newline │ │ │ │ ├── name: :a │ │ │ │ └── depth: 0 │ │ │ ├── opening_loc: (128,8)-(128,10) = "do" @@ -2044,9 +2146,10 @@ │ │ │ └── block: ∅ │ │ └── statements: │ │ @ StatementsNode (location: (133,4)-(134,7)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (133,4)-(134,7)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :baz @@ -2056,6 +2159,7 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (133,8)-(134,7)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── parameters: ∅ │ │ ├── body: ∅ @@ -2064,9 +2168,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (137,0)-(137,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ HashNode (location: (137,0)-(137,2)) + │ │ ├── flags: static_literal │ │ ├── opening_loc: (137,0)-(137,1) = "{" │ │ ├── elements: (length: 0) │ │ └── closing_loc: (137,1)-(137,2) = "}" @@ -2089,6 +2194,7 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (137,7)-(137,9)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── parameters: ∅ │ │ ├── body: ∅ @@ -2097,9 +2203,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (139,0)-(139,16)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ HashNode (location: (139,0)-(139,2)) + │ │ ├── flags: static_literal │ │ ├── opening_loc: (139,0)-(139,1) = "{" │ │ ├── elements: (length: 0) │ │ └── closing_loc: (139,1)-(139,2) = "}" @@ -2122,11 +2229,14 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (139,7)-(139,16)) + │ │ ├── flags: ∅ │ │ ├── locals: [:a] │ │ ├── parameters: │ │ │ @ BlockParametersNode (location: (139,9)-(139,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── parameters: │ │ │ │ @ ParametersNode (location: (139,10)-(139,11)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── requireds: (length: 1) │ │ │ │ │ └── @ RequiredParameterNode (location: (139,10)-(139,11)) │ │ │ │ │ ├── flags: ∅ @@ -2142,8 +2252,10 @@ │ │ │ └── closing_loc: (139,11)-(139,12) = "|" │ │ ├── body: │ │ │ @ StatementsNode (location: (139,13)-(139,14)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ LocalVariableReadNode (location: (139,13)-(139,14)) + │ │ │ ├── flags: newline │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ ├── opening_loc: (139,7)-(139,8) = "{" @@ -2151,7 +2263,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (141,0)-(141,11)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (141,0)-(141,4)) │ │ ├── flags: ignore_visibility @@ -2164,6 +2276,7 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (141,2)-(141,4)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── parameters: ∅ │ │ ├── body: ∅ @@ -2188,6 +2301,7 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (141,9)-(141,11)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── parameters: ∅ │ │ ├── body: ∅ @@ -2196,7 +2310,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (143,0)-(143,11)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (143,0)-(143,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2227,6 +2341,7 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (143,9)-(143,11)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── parameters: ∅ │ │ ├── body: ∅ @@ -2235,13 +2350,15 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ InterpolatedStringNode (location: (145,0)-(145,17)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (145,0)-(145,1) = "\"" │ ├── parts: (length: 1) │ │ └── @ EmbeddedStatementsNode (location: (145,1)-(145,16)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (145,1)-(145,3) = "\#{" │ │ ├── statements: │ │ │ @ StatementsNode (location: (145,4)-(145,14)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (145,4)-(145,14)) │ │ │ ├── flags: ignore_visibility @@ -2255,11 +2372,13 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ ParenthesesNode (location: (145,9)-(145,14)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── body: │ │ │ │ │ @ StatementsNode (location: (145,10)-(145,13)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ StringNode (location: (145,10)-(145,13)) - │ │ │ │ │ ├── flags: ∅ + │ │ │ │ │ ├── flags: newline │ │ │ │ │ ├── opening_loc: (145,10)-(145,11) = "\"" │ │ │ │ │ ├── content_loc: (145,11)-(145,12) = " " │ │ │ │ │ ├── closing_loc: (145,12)-(145,13) = "\"" @@ -2271,20 +2390,24 @@ │ │ └── closing_loc: (145,15)-(145,16) = "}" │ └── closing_loc: (145,16)-(145,17) = "\"" ├── @ InterpolatedStringNode (location: (147,0)-(147,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (147,0)-(147,1) = "\"" │ ├── parts: (length: 1) │ │ └── @ EmbeddedStatementsNode (location: (147,1)-(147,7)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (147,1)-(147,3) = "\#{" │ │ ├── statements: │ │ │ @ StatementsNode (location: (147,3)-(147,6)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ ParenthesesNode (location: (147,3)-(147,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (147,4)-(147,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (147,4)-(147,5)) - │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ ├── receiver: ∅ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :v @@ -2298,11 +2421,13 @@ │ │ └── closing_loc: (147,6)-(147,7) = "}" │ └── closing_loc: (147,7)-(147,8) = "\"" ├── @ DefNode (location: (149,0)-(149,18)) + │ ├── flags: newline │ ├── name: :f │ ├── name_loc: (149,4)-(149,5) = "f" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (149,6)-(149,7)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: @@ -2317,9 +2442,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (149,10)-(149,13)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (149,10)-(149,13)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :p @@ -2330,6 +2456,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ SplatNode (location: (149,12)-(149,13)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (149,12)-(149,13) = "*" │ │ │ └── expression: ∅ │ │ ├── closing_loc: ∅ @@ -2342,7 +2469,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (149,15)-(149,18) = "end" ├── @ CallNode (location: (151,0)-(151,16)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -2353,7 +2480,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 2) │ │ ├── @ IntegerNode (location: (151,4)-(151,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ CallNode (location: (151,7)-(151,16)) │ │ ├── flags: ignore_visibility @@ -2366,29 +2493,32 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (151,11)-(151,16)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── parameters: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (151,13)-(151,14)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ IntegerNode (location: (151,13)-(151,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: newline, static_literal, decimal │ │ │ └── value: 1 │ │ ├── opening_loc: (151,11)-(151,12) = "{" │ │ └── closing_loc: (151,15)-(151,16) = "}" │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ LocalVariableWriteNode (location: (153,0)-(153,7)) + │ ├── flags: newline │ ├── name: :foo │ ├── depth: 0 │ ├── name_loc: (153,0)-(153,3) = "foo" │ ├── value: │ │ @ IntegerNode (location: (153,6)-(153,7)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (153,4)-(153,5) = "=" ├── @ CallNode (location: (154,0)-(154,6)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -2398,15 +2528,17 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (154,4)-(154,6)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: ∅ │ ├── opening_loc: (154,4)-(154,5) = "{" │ └── closing_loc: (154,5)-(154,6) = "}" └── @ CallNode (location: (156,0)-(156,19)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ InstanceVariableReadNode (location: (156,0)-(156,2)) + │ ├── flags: ∅ │ └── name: :@a ├── call_operator_loc: (156,2)-(156,3) = "." ├── name: :b @@ -2420,20 +2552,24 @@ │ ├── flags: ∅ │ └── elements: (length: 1) │ └── @ AssocNode (location: (156,5)-(156,19)) + │ ├── flags: ∅ │ ├── key: │ │ @ InterpolatedSymbolNode (location: (156,5)-(156,16)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (156,5)-(156,6) = "\"" │ │ ├── parts: (length: 2) │ │ │ ├── @ StringNode (location: (156,6)-(156,7)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── content_loc: (156,6)-(156,7) = "c" │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: "c" │ │ │ └── @ EmbeddedStatementsNode (location: (156,7)-(156,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (156,7)-(156,9) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (156,9)-(156,13)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (156,9)-(156,13)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -2449,7 +2585,7 @@ │ │ └── closing_loc: (156,14)-(156,16) = "\":" │ ├── value: │ │ @ IntegerNode (location: (156,17)-(156,19)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 42 │ └── operator_loc: ∅ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/methods.txt b/test/prism/snapshots/methods.txt index b38640399b0937..0fd1a7f43aa38e 100644 --- a/test/prism/snapshots/methods.txt +++ b/test/prism/snapshots/methods.txt @@ -1,16 +1,21 @@ @ ProgramNode (location: (1,0)-(183,37)) +├── flags: ∅ ├── locals: [:a, :c, :foo] └── statements: @ StatementsNode (location: (1,0)-(183,37)) + ├── flags: ∅ └── body: (length: 69) ├── @ DefNode (location: (1,0)-(2,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (1,4)-(1,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,8)-(1,18)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,18)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 2) │ │ │ │ ├── @ RequiredParameterNode (location: (1,9)-(1,12)) │ │ │ │ │ ├── flags: ∅ @@ -37,13 +42,16 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (2,0)-(2,3) = "end" ├── @ DefNode (location: (4,0)-(5,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (4,4)-(4,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (4,8)-(4,44)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ MultiTargetNode (location: (4,8)-(4,18)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 2) │ │ │ │ ├── @ RequiredParameterNode (location: (4,9)-(4,12)) │ │ │ │ │ ├── flags: ∅ @@ -63,11 +71,12 @@ │ │ │ ├── operator_loc: (4,29)-(4,30) = "=" │ │ │ └── value: │ │ │ @ IntegerNode (location: (4,31)-(4,32)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── rest: ∅ │ │ ├── posts: (length: 1) │ │ │ └── @ MultiTargetNode (location: (4,34)-(4,44)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 2) │ │ │ │ ├── @ RequiredParameterNode (location: (4,35)-(4,38)) │ │ │ │ │ ├── flags: ∅ @@ -91,18 +100,21 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (5,0)-(5,3) = "end" ├── @ DefNode (location: (8,0)-(8,18)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (8,4)-(8,5) = "a" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (8,0)-(8,18)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: ∅ │ │ ├── rescue_clause: ∅ │ │ ├── else_clause: ∅ │ │ ├── ensure_clause: │ │ │ @ EnsureNode (location: (8,7)-(8,18)) + │ │ │ ├── flags: ∅ │ │ │ ├── ensure_keyword_loc: (8,7)-(8,13) = "ensure" │ │ │ ├── statements: ∅ │ │ │ └── end_keyword_loc: (8,15)-(8,18) = "end" @@ -115,10 +127,12 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (8,15)-(8,18) = "end" ├── @ DefNode (location: (10,0)-(11,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (10,8)-(10,9) = "a" │ ├── receiver: │ │ @ ParenthesesNode (location: (10,4)-(10,7)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ CallNode (location: (10,5)-(10,6)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -142,10 +156,12 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (11,0)-(11,3) = "end" ├── @ DefNode (location: (13,0)-(14,3)) + │ ├── flags: newline │ ├── name: :b │ ├── name_loc: (13,9)-(13,10) = "b" │ ├── receiver: │ │ @ ParenthesesNode (location: (13,4)-(13,7)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ CallNode (location: (13,5)-(13,6)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -169,10 +185,12 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (14,0)-(14,3) = "end" ├── @ DefNode (location: (16,0)-(17,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (16,10)-(16,11) = "a" │ ├── receiver: │ │ @ FalseNode (location: (16,4)-(16,9)) + │ │ └── flags: static_literal │ ├── parameters: ∅ │ ├── body: ∅ │ ├── locals: [] @@ -183,11 +201,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (17,0)-(17,3) = "end" ├── @ DefNode (location: (19,0)-(20,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (19,4)-(19,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (19,6)-(19,9)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -195,6 +215,7 @@ │ │ ├── keywords: (length: 0) │ │ ├── keyword_rest: │ │ │ @ ForwardingParameterNode (location: (19,6)-(19,9)) + │ │ │ └── flags: ∅ │ │ └── block: ∅ │ ├── body: ∅ │ ├── locals: [] @@ -205,10 +226,12 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (20,0)-(20,3) = "end" ├── @ DefNode (location: (22,0)-(23,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (22,9)-(22,10) = "a" │ ├── receiver: │ │ @ GlobalVariableReadNode (location: (22,4)-(22,8)) + │ │ ├── flags: ∅ │ │ └── name: :$var │ ├── parameters: ∅ │ ├── body: ∅ @@ -220,6 +243,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (23,0)-(23,3) = "end" ├── @ DefNode (location: (25,0)-(26,3)) + │ ├── flags: newline │ ├── name: :b │ ├── name_loc: (25,6)-(25,7) = "b" │ ├── receiver: @@ -243,10 +267,12 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (26,0)-(26,3) = "end" ├── @ DefNode (location: (28,0)-(29,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (28,9)-(28,10) = "a" │ ├── receiver: │ │ @ InstanceVariableReadNode (location: (28,4)-(28,8)) + │ │ ├── flags: ∅ │ │ └── name: :@var │ ├── parameters: ∅ │ ├── body: ∅ @@ -258,11 +284,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (29,0)-(29,3) = "end" ├── @ DefNode (location: (31,0)-(31,13)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (31,4)-(31,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (31,6)-(31,8)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -283,17 +311,19 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (31,10)-(31,13) = "end" ├── @ StringNode (location: (33,0)-(33,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (33,0)-(33,2) = "%," │ ├── content_loc: (33,2)-(33,5) = "abc" │ ├── closing_loc: (33,5)-(33,6) = "," │ └── unescaped: "abc" ├── @ DefNode (location: (35,0)-(36,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (35,4)-(35,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (35,6)-(35,8)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -314,11 +344,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (36,0)-(36,3) = "end" ├── @ DefNode (location: (38,0)-(39,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (38,4)-(38,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (38,6)-(38,9)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -340,11 +372,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (39,0)-(39,3) = "end" ├── @ DefNode (location: (41,0)-(42,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (41,4)-(41,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (41,6)-(41,8)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -366,15 +400,17 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (42,0)-(42,3) = "end" ├── @ LocalVariableWriteNode (location: (44,0)-(44,5)) + │ ├── flags: newline │ ├── name: :a │ ├── depth: 0 │ ├── name_loc: (44,0)-(44,1) = "a" │ ├── value: │ │ @ IntegerNode (location: (44,4)-(44,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (44,2)-(44,3) = "=" ├── @ DefNode (location: (44,7)-(45,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (44,11)-(44,12) = "a" │ ├── receiver: ∅ @@ -388,11 +424,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (45,0)-(45,3) = "end" ├── @ DefNode (location: (47,0)-(48,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (47,4)-(47,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (47,6)-(47,13)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 3) │ │ │ ├── @ RequiredParameterNode (location: (47,6)-(47,7)) │ │ │ │ ├── flags: ∅ @@ -418,10 +456,12 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (48,0)-(48,3) = "end" ├── @ DefNode (location: (50,0)-(51,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (50,8)-(50,9) = "a" │ ├── receiver: │ │ @ NilNode (location: (50,4)-(50,7)) + │ │ └── flags: static_literal │ ├── parameters: ∅ │ ├── body: ∅ │ ├── locals: [] @@ -432,11 +472,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (51,0)-(51,3) = "end" ├── @ DefNode (location: (53,0)-(54,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (53,4)-(53,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (53,6)-(53,14)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -452,7 +494,7 @@ │ │ │ ├── name_loc: (53,10)-(53,12) = "c:" │ │ │ └── value: │ │ │ @ IntegerNode (location: (53,13)-(53,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── keyword_rest: ∅ │ │ └── block: ∅ @@ -465,11 +507,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (54,0)-(54,3) = "end" ├── @ DefNode (location: (56,0)-(57,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (56,4)-(56,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (56,6)-(56,14)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -485,7 +529,7 @@ │ │ │ ├── name_loc: (56,10)-(56,12) = "c:" │ │ │ └── value: │ │ │ @ IntegerNode (location: (56,13)-(56,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── keyword_rest: ∅ │ │ └── block: ∅ @@ -498,11 +542,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (57,0)-(57,3) = "end" ├── @ DefNode (location: (59,0)-(61,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (59,4)-(59,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (59,6)-(60,7)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -514,7 +560,7 @@ │ │ │ │ ├── name_loc: (59,6)-(59,8) = "b:" │ │ │ │ └── value: │ │ │ │ @ IntegerNode (location: (60,2)-(60,3)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── @ RequiredKeywordParameterNode (location: (60,5)-(60,7)) │ │ │ ├── flags: ∅ @@ -531,17 +577,19 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (61,0)-(61,3) = "end" ├── @ StringNode (location: (63,0)-(63,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (63,0)-(63,2) = "%." │ ├── content_loc: (63,2)-(63,5) = "abc" │ ├── closing_loc: (63,5)-(63,6) = "." │ └── unescaped: "abc" ├── @ DefNode (location: (65,0)-(66,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (65,4)-(65,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (65,6)-(65,18)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 2) │ │ │ ├── @ OptionalParameterNode (location: (65,6)-(65,11)) @@ -551,7 +599,7 @@ │ │ │ │ ├── operator_loc: (65,8)-(65,9) = "=" │ │ │ │ └── value: │ │ │ │ @ IntegerNode (location: (65,10)-(65,11)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── @ OptionalParameterNode (location: (65,13)-(65,18)) │ │ │ ├── flags: ∅ @@ -560,7 +608,7 @@ │ │ │ ├── operator_loc: (65,15)-(65,16) = "=" │ │ │ └── value: │ │ │ @ IntegerNode (location: (65,17)-(65,18)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) @@ -576,6 +624,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (66,0)-(66,3) = "end" ├── @ DefNode (location: (68,0)-(69,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (68,4)-(68,5) = "a" │ ├── receiver: ∅ @@ -589,11 +638,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (69,0)-(69,3) = "end" ├── @ DefNode (location: (71,0)-(72,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (71,4)-(71,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (71,6)-(71,14)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (71,6)-(71,7)) │ │ │ ├── flags: ∅ @@ -606,7 +657,7 @@ │ │ │ ├── operator_loc: (71,11)-(71,12) = "=" │ │ │ └── value: │ │ │ @ IntegerNode (location: (71,13)-(71,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) @@ -622,11 +673,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (72,0)-(72,3) = "end" ├── @ DefNode (location: (74,0)-(75,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (74,4)-(74,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (74,6)-(74,7)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (74,6)-(74,7)) │ │ │ ├── flags: ∅ @@ -646,16 +699,19 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (75,0)-(75,3) = "end" ├── @ DefNode (location: (77,0)-(77,32)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (77,4)-(77,5) = "a" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (77,0)-(77,32)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (77,7)-(77,13)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (77,7)-(77,13) = "rescue" │ │ │ ├── exceptions: (length: 0) │ │ │ ├── operator_loc: ∅ @@ -664,11 +720,13 @@ │ │ │ └── consequent: ∅ │ │ ├── else_clause: │ │ │ @ ElseNode (location: (77,15)-(77,27)) + │ │ │ ├── flags: ∅ │ │ │ ├── else_keyword_loc: (77,15)-(77,19) = "else" │ │ │ ├── statements: ∅ │ │ │ └── end_keyword_loc: (77,21)-(77,27) = "ensure" │ │ ├── ensure_clause: │ │ │ @ EnsureNode (location: (77,21)-(77,32)) + │ │ │ ├── flags: ∅ │ │ │ ├── ensure_keyword_loc: (77,21)-(77,27) = "ensure" │ │ │ ├── statements: ∅ │ │ │ └── end_keyword_loc: (77,29)-(77,32) = "end" @@ -681,11 +739,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (77,29)-(77,32) = "end" ├── @ DefNode (location: (79,0)-(80,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (79,4)-(79,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (79,6)-(79,8)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: @@ -707,11 +767,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (80,0)-(80,3) = "end" ├── @ DefNode (location: (82,0)-(83,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (82,4)-(82,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (82,6)-(82,7)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: @@ -733,20 +795,23 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (83,0)-(83,3) = "end" ├── @ DefNode (location: (85,0)-(87,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (85,4)-(85,5) = "a" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (86,0)-(86,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableWriteNode (location: (86,0)-(86,5)) + │ │ ├── flags: newline │ │ ├── name: :b │ │ ├── depth: 0 │ │ ├── name_loc: (86,0)-(86,1) = "b" │ │ ├── value: │ │ │ @ IntegerNode (location: (86,4)-(86,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── operator_loc: (86,2)-(86,3) = "=" │ ├── locals: [:b] @@ -757,10 +822,12 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (87,0)-(87,3) = "end" ├── @ DefNode (location: (89,0)-(90,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (89,9)-(89,10) = "a" │ ├── receiver: │ │ @ SelfNode (location: (89,4)-(89,8)) + │ │ └── flags: ∅ │ ├── parameters: ∅ │ ├── body: ∅ │ ├── locals: [] @@ -771,10 +838,12 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (90,0)-(90,3) = "end" ├── @ DefNode (location: (92,0)-(93,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (92,9)-(92,10) = "a" │ ├── receiver: │ │ @ TrueNode (location: (92,4)-(92,8)) + │ │ └── flags: static_literal │ ├── parameters: ∅ │ ├── body: ∅ │ ├── locals: [] @@ -785,6 +854,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (93,0)-(93,3) = "end" ├── @ DefNode (location: (95,0)-(96,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (95,4)-(95,5) = "a" │ ├── receiver: ∅ @@ -798,30 +868,35 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (96,0)-(96,3) = "end" ├── @ DefNode (location: (98,0)-(101,3)) + │ ├── flags: newline │ ├── name: :hi │ ├── name_loc: (98,4)-(98,6) = "hi" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (99,0)-(100,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 2) │ │ ├── @ IfNode (location: (99,0)-(99,18)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (99,11)-(99,13) = "if" │ │ │ ├── predicate: │ │ │ │ @ TrueNode (location: (99,14)-(99,18)) + │ │ │ │ └── flags: static_literal │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (99,0)-(99,10)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ ReturnNode (location: (99,0)-(99,10)) - │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── flags: newline │ │ │ │ ├── keyword_loc: (99,0)-(99,6) = "return" │ │ │ │ └── arguments: │ │ │ │ @ ArgumentsNode (location: (99,7)-(99,10)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ SymbolNode (location: (99,7)-(99,10)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (99,7)-(99,8) = ":" │ │ │ │ ├── value_loc: (99,8)-(99,10) = "hi" │ │ │ │ ├── closing_loc: ∅ @@ -829,7 +904,7 @@ │ │ │ ├── consequent: ∅ │ │ │ └── end_keyword_loc: ∅ │ │ └── @ SymbolNode (location: (100,0)-(100,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (100,0)-(100,1) = ":" │ │ ├── value_loc: (100,1)-(100,4) = "bye" │ │ ├── closing_loc: ∅ @@ -842,15 +917,17 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (101,0)-(101,3) = "end" ├── @ DefNode (location: (103,0)-(103,11)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (103,4)-(103,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (103,10)-(103,11)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (103,10)-(103,11)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 1 │ ├── locals: [] │ ├── def_keyword_loc: (103,0)-(103,3) = "def" @@ -860,15 +937,17 @@ │ ├── equal_loc: (103,8)-(103,9) = "=" │ └── end_keyword_loc: ∅ ├── @ DefNode (location: (104,0)-(104,11)) + │ ├── flags: newline │ ├── name: :bar │ ├── name_loc: (104,4)-(104,7) = "bar" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (104,10)-(104,11)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (104,10)-(104,11)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 2 │ ├── locals: [] │ ├── def_keyword_loc: (104,0)-(104,3) = "def" @@ -878,11 +957,13 @@ │ ├── equal_loc: (104,8)-(104,9) = "=" │ └── end_keyword_loc: ∅ ├── @ DefNode (location: (106,0)-(106,18)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (106,4)-(106,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (106,8)-(106,11)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (106,8)-(106,11)) │ │ │ ├── flags: ∅ @@ -895,9 +976,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (106,15)-(106,18)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (106,15)-(106,18)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 123 │ ├── locals: [:bar] │ ├── def_keyword_loc: (106,0)-(106,3) = "def" @@ -907,15 +989,17 @@ │ ├── equal_loc: (106,13)-(106,14) = "=" │ └── end_keyword_loc: ∅ ├── @ DefNode (location: (108,0)-(108,13)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (108,4)-(108,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (108,10)-(108,13)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (108,10)-(108,13)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 123 │ ├── locals: [] │ ├── def_keyword_loc: (108,0)-(108,3) = "def" @@ -925,11 +1009,13 @@ │ ├── equal_loc: (108,8)-(108,9) = "=" │ └── end_keyword_loc: ∅ ├── @ DefNode (location: (110,0)-(110,19)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (110,4)-(110,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (110,6)-(110,7)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: @@ -944,9 +1030,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (110,10)-(110,14)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (110,10)-(110,14)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :b @@ -957,6 +1044,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ SplatNode (location: (110,12)-(110,13)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (110,12)-(110,13) = "*" │ │ │ └── expression: ∅ │ │ ├── closing_loc: (110,13)-(110,14) = ")" @@ -969,11 +1057,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (110,16)-(110,19) = "end" ├── @ DefNode (location: (112,0)-(112,23)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (112,4)-(112,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (112,6)-(112,9)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -981,12 +1071,14 @@ │ │ ├── keywords: (length: 0) │ │ ├── keyword_rest: │ │ │ @ ForwardingParameterNode (location: (112,6)-(112,9)) + │ │ │ └── flags: ∅ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (112,12)-(112,18)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (112,12)-(112,18)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :b @@ -997,6 +1089,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ ForwardingArgumentsNode (location: (112,14)-(112,17)) + │ │ │ └── flags: ∅ │ │ ├── closing_loc: (112,17)-(112,18) = ")" │ │ └── block: ∅ │ ├── locals: [] @@ -1007,11 +1100,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (112,20)-(112,23) = "end" ├── @ DefNode (location: (114,0)-(114,29)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (114,4)-(114,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (114,6)-(114,9)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -1019,12 +1114,14 @@ │ │ ├── keywords: (length: 0) │ │ ├── keyword_rest: │ │ │ @ ForwardingParameterNode (location: (114,6)-(114,9)) + │ │ │ └── flags: ∅ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (114,12)-(114,24)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (114,12)-(114,24)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :b @@ -1035,12 +1132,13 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 3) │ │ │ ├── @ IntegerNode (location: (114,14)-(114,15)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── @ IntegerNode (location: (114,17)-(114,18)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ └── @ ForwardingArgumentsNode (location: (114,20)-(114,23)) + │ │ │ └── flags: ∅ │ │ ├── closing_loc: (114,23)-(114,24) = ")" │ │ └── block: ∅ │ ├── locals: [] @@ -1051,12 +1149,15 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (114,26)-(114,29) = "end" ├── @ DefNode (location: (116,0)-(117,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (116,12)-(116,13) = "a" │ ├── receiver: │ │ @ ParenthesesNode (location: (116,4)-(116,11)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ LocalVariableWriteNode (location: (116,5)-(116,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :c │ │ │ ├── depth: 0 │ │ │ ├── name_loc: (116,5)-(116,6) = "c" @@ -1084,11 +1185,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (117,0)-(117,3) = "end" ├── @ DefNode (location: (119,0)-(120,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (119,4)-(119,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (119,6)-(119,8)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -1110,11 +1213,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (120,0)-(120,3) = "end" ├── @ DefNode (location: (122,0)-(123,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (122,4)-(122,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (122,6)-(122,7)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -1136,10 +1241,12 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (123,0)-(123,3) = "end" ├── @ DefNode (location: (125,0)-(126,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (125,10)-(125,11) = "a" │ ├── receiver: │ │ @ ClassVariableReadNode (location: (125,4)-(125,9)) + │ │ ├── flags: ∅ │ │ └── name: :@@var │ ├── parameters: ∅ │ ├── body: ∅ @@ -1151,12 +1258,15 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (126,0)-(126,3) = "end" ├── @ DefNode (location: (128,0)-(129,3)) + │ ├── flags: newline │ ├── name: :C │ ├── name_loc: (128,12)-(128,13) = "C" │ ├── receiver: │ │ @ ParenthesesNode (location: (128,4)-(128,11)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ LocalVariableWriteNode (location: (128,5)-(128,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ ├── depth: 0 │ │ │ ├── name_loc: (128,5)-(128,6) = "a" @@ -1184,10 +1294,12 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (129,0)-(129,3) = "end" ├── @ DefNode (location: (131,0)-(131,28)) + │ ├── flags: newline │ ├── name: :Array_function │ ├── name_loc: (131,9)-(131,23) = "Array_function" │ ├── receiver: │ │ @ SelfNode (location: (131,4)-(131,8)) + │ │ └── flags: ∅ │ ├── parameters: ∅ │ ├── body: ∅ │ ├── locals: [] @@ -1198,18 +1310,21 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (131,25)-(131,28) = "end" ├── @ ConstantWriteNode (location: (133,0)-(133,9)) + │ ├── flags: newline │ ├── name: :Const │ ├── name_loc: (133,0)-(133,5) = "Const" │ ├── value: │ │ @ IntegerNode (location: (133,8)-(133,9)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (133,6)-(133,7) = "=" ├── @ DefNode (location: (133,11)-(134,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (133,21)-(133,22) = "a" │ ├── receiver: │ │ @ ConstantReadNode (location: (133,15)-(133,20)) + │ │ ├── flags: ∅ │ │ └── name: :Const │ ├── parameters: ∅ │ ├── body: ∅ @@ -1221,11 +1336,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (134,0)-(134,3) = "end" ├── @ DefNode (location: (136,0)-(136,31)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (136,4)-(136,5) = "a" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (136,6)-(136,9)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -1233,24 +1350,28 @@ │ │ ├── keywords: (length: 0) │ │ ├── keyword_rest: │ │ │ @ ForwardingParameterNode (location: (136,6)-(136,9)) + │ │ │ └── flags: ∅ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (136,12)-(136,26)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ InterpolatedStringNode (location: (136,12)-(136,26)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── opening_loc: (136,12)-(136,13) = "\"" │ │ ├── parts: (length: 2) │ │ │ ├── @ StringNode (location: (136,13)-(136,16)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── content_loc: (136,13)-(136,16) = "foo" │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: "foo" │ │ │ └── @ EmbeddedStatementsNode (location: (136,16)-(136,25)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (136,16)-(136,18) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (136,18)-(136,24)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (136,18)-(136,24)) │ │ │ │ ├── flags: ignore_visibility @@ -1264,6 +1385,7 @@ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── arguments: (length: 1) │ │ │ │ │ └── @ ForwardingArgumentsNode (location: (136,20)-(136,23)) + │ │ │ │ │ └── flags: ∅ │ │ │ │ ├── closing_loc: (136,23)-(136,24) = ")" │ │ │ │ └── block: ∅ │ │ │ └── closing_loc: (136,24)-(136,25) = "}" @@ -1276,17 +1398,20 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (136,28)-(136,31) = "end" ├── @ DefNode (location: (138,0)-(140,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (138,4)-(138,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (139,2)-(139,30)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (139,2)-(139,30)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ HashNode (location: (139,2)-(139,4)) + │ │ │ ├── flags: static_literal │ │ │ ├── opening_loc: (139,2)-(139,3) = "{" │ │ │ ├── elements: (length: 0) │ │ │ └── closing_loc: (139,3)-(139,4) = "}" @@ -1302,6 +1427,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── elements: (length: 3) │ │ │ ├── @ AssocSplatNode (location: (139,11)-(139,16)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── value: │ │ │ │ │ @ CallNode (location: (139,13)-(139,16)) │ │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -1315,6 +1441,7 @@ │ │ │ │ │ └── block: ∅ │ │ │ │ └── operator_loc: (139,11)-(139,13) = "**" │ │ │ ├── @ AssocSplatNode (location: (139,18)-(139,23)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── value: │ │ │ │ │ @ CallNode (location: (139,20)-(139,23)) │ │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -1328,6 +1455,7 @@ │ │ │ │ │ └── block: ∅ │ │ │ │ └── operator_loc: (139,18)-(139,20) = "**" │ │ │ └── @ AssocSplatNode (location: (139,25)-(139,30)) + │ │ │ ├── flags: ∅ │ │ │ ├── value: │ │ │ │ @ CallNode (location: (139,27)-(139,30)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -1350,11 +1478,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (140,0)-(140,3) = "end" ├── @ DefNode (location: (142,0)-(143,3)) + │ ├── flags: newline │ ├── name: :bar │ ├── name_loc: (142,4)-(142,7) = "bar" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (142,8)-(142,19)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -1366,18 +1496,20 @@ │ │ │ ├── name_loc: (142,8)-(142,10) = "a:" │ │ │ └── value: │ │ │ @ ParenthesesNode (location: (142,11)-(142,19)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (142,12)-(142,18)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ RangeNode (location: (142,12)-(142,18)) - │ │ │ │ ├── flags: exclude_end + │ │ │ │ ├── flags: newline, static_literal, exclude_end │ │ │ │ ├── left: │ │ │ │ │ @ IntegerNode (location: (142,12)-(142,13)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ ├── right: │ │ │ │ │ @ IntegerNode (location: (142,16)-(142,18)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 10 │ │ │ │ └── operator_loc: (142,13)-(142,16) = "..." │ │ │ ├── opening_loc: (142,11)-(142,12) = "(" @@ -1393,11 +1525,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (143,0)-(143,3) = "end" ├── @ DefNode (location: (145,0)-(146,3)) + │ ├── flags: newline │ ├── name: :bar │ ├── name_loc: (145,4)-(145,7) = "bar" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (145,8)-(145,18)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -1409,15 +1543,17 @@ │ │ │ ├── name_loc: (145,8)-(145,10) = "a:" │ │ │ └── value: │ │ │ @ ParenthesesNode (location: (145,11)-(145,18)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (145,12)-(145,17)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ RangeNode (location: (145,12)-(145,17)) - │ │ │ │ ├── flags: exclude_end + │ │ │ │ ├── flags: newline, static_literal, exclude_end │ │ │ │ ├── left: ∅ │ │ │ │ ├── right: │ │ │ │ │ @ IntegerNode (location: (145,15)-(145,17)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 10 │ │ │ │ └── operator_loc: (145,12)-(145,15) = "..." │ │ │ ├── opening_loc: (145,11)-(145,12) = "(" @@ -1433,11 +1569,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (146,0)-(146,3) = "end" ├── @ DefNode (location: (148,0)-(149,3)) + │ ├── flags: newline │ ├── name: :bar │ ├── name_loc: (148,4)-(148,7) = "bar" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (148,8)-(148,17)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -1449,14 +1587,16 @@ │ │ │ ├── name_loc: (148,8)-(148,10) = "a:" │ │ │ └── value: │ │ │ @ ParenthesesNode (location: (148,11)-(148,17)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (148,12)-(148,16)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ RangeNode (location: (148,12)-(148,16)) - │ │ │ │ ├── flags: exclude_end + │ │ │ │ ├── flags: newline, static_literal, exclude_end │ │ │ │ ├── left: │ │ │ │ │ @ IntegerNode (location: (148,12)-(148,13)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ ├── right: ∅ │ │ │ │ └── operator_loc: (148,13)-(148,16) = "..." @@ -1473,11 +1613,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (149,0)-(149,3) = "end" ├── @ DefNode (location: (151,0)-(152,3)) + │ ├── flags: newline │ ├── name: :bar │ ├── name_loc: (151,4)-(151,7) = "bar" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (151,8)-(151,20)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 1) │ │ │ └── @ OptionalParameterNode (location: (151,8)-(151,20)) @@ -1487,18 +1629,20 @@ │ │ │ ├── operator_loc: (151,10)-(151,11) = "=" │ │ │ └── value: │ │ │ @ ParenthesesNode (location: (151,12)-(151,20)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (151,13)-(151,19)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ RangeNode (location: (151,13)-(151,19)) - │ │ │ │ ├── flags: exclude_end + │ │ │ │ ├── flags: newline, static_literal, exclude_end │ │ │ │ ├── left: │ │ │ │ │ @ IntegerNode (location: (151,13)-(151,14)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ ├── right: │ │ │ │ │ @ IntegerNode (location: (151,17)-(151,19)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 10 │ │ │ │ └── operator_loc: (151,14)-(151,17) = "..." │ │ │ ├── opening_loc: (151,12)-(151,13) = "(" @@ -1517,11 +1661,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (152,0)-(152,3) = "end" ├── @ DefNode (location: (154,0)-(155,3)) + │ ├── flags: newline │ ├── name: :bar │ ├── name_loc: (154,4)-(154,7) = "bar" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (154,8)-(154,19)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 1) │ │ │ └── @ OptionalParameterNode (location: (154,8)-(154,19)) @@ -1531,15 +1677,17 @@ │ │ │ ├── operator_loc: (154,10)-(154,11) = "=" │ │ │ └── value: │ │ │ @ ParenthesesNode (location: (154,12)-(154,19)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (154,13)-(154,18)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ RangeNode (location: (154,13)-(154,18)) - │ │ │ │ ├── flags: exclude_end + │ │ │ │ ├── flags: newline, static_literal, exclude_end │ │ │ │ ├── left: ∅ │ │ │ │ ├── right: │ │ │ │ │ @ IntegerNode (location: (154,16)-(154,18)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 10 │ │ │ │ └── operator_loc: (154,13)-(154,16) = "..." │ │ │ ├── opening_loc: (154,12)-(154,13) = "(" @@ -1558,11 +1706,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (155,0)-(155,3) = "end" ├── @ DefNode (location: (157,0)-(158,3)) + │ ├── flags: newline │ ├── name: :bar │ ├── name_loc: (157,4)-(157,7) = "bar" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (157,8)-(157,18)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 1) │ │ │ └── @ OptionalParameterNode (location: (157,8)-(157,18)) @@ -1572,14 +1722,16 @@ │ │ │ ├── operator_loc: (157,10)-(157,11) = "=" │ │ │ └── value: │ │ │ @ ParenthesesNode (location: (157,12)-(157,18)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (157,13)-(157,17)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ RangeNode (location: (157,13)-(157,17)) - │ │ │ │ ├── flags: exclude_end + │ │ │ │ ├── flags: newline, static_literal, exclude_end │ │ │ │ ├── left: │ │ │ │ │ @ IntegerNode (location: (157,13)-(157,14)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ ├── right: ∅ │ │ │ │ └── operator_loc: (157,14)-(157,17) = "..." @@ -1599,11 +1751,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (158,0)-(158,3) = "end" ├── @ DefNode (location: (160,0)-(162,3)) + │ ├── flags: newline │ ├── name: :method │ ├── name_loc: (160,4)-(160,10) = "method" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (160,11)-(160,12)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (160,11)-(160,12)) │ │ │ ├── flags: ∅ @@ -1616,9 +1770,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (161,2)-(161,14)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (161,2)-(161,14)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ CallNode (location: (161,2)-(161,6)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -1649,6 +1804,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: │ │ │ @ BlockNode (location: (161,12)-(161,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── locals: [] │ │ │ ├── parameters: ∅ │ │ │ ├── body: ∅ @@ -1664,19 +1820,22 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (162,0)-(162,3) = "end" ├── @ LocalVariableWriteNode (location: (164,0)-(164,7)) + │ ├── flags: newline │ ├── name: :foo │ ├── depth: 0 │ ├── name_loc: (164,0)-(164,3) = "foo" │ ├── value: │ │ @ IntegerNode (location: (164,6)-(164,7)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (164,4)-(164,5) = "=" ├── @ DefNode (location: (165,0)-(165,16)) + │ ├── flags: newline │ ├── name: :bar │ ├── name_loc: (165,8)-(165,11) = "bar" │ ├── receiver: │ │ @ LocalVariableReadNode (location: (165,4)-(165,7)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ └── depth: 0 │ ├── parameters: ∅ @@ -1689,11 +1848,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (165,13)-(165,16) = "end" ├── @ DefNode (location: (167,0)-(167,18)) + │ ├── flags: newline │ ├── name: :f │ ├── name_loc: (167,4)-(167,5) = "f" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (167,6)-(167,7)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: @@ -1708,11 +1869,13 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (167,10)-(167,13)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ ArrayNode (location: (167,10)-(167,13)) - │ │ ├── flags: contains_splat + │ │ ├── flags: newline, contains_splat │ │ ├── elements: (length: 1) │ │ │ └── @ SplatNode (location: (167,11)-(167,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (167,11)-(167,12) = "*" │ │ │ └── expression: ∅ │ │ ├── opening_loc: (167,10)-(167,11) = "[" @@ -1725,11 +1888,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (167,15)-(167,18) = "end" ├── @ DefNode (location: (169,0)-(169,15)) + │ ├── flags: newline │ ├── name: :f │ ├── name_loc: (169,4)-(169,5) = "f" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (169,6)-(169,10)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -1771,11 +1936,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (169,12)-(169,15) = "end" ├── @ DefNode (location: (171,0)-(171,15)) + │ ├── flags: newline │ ├── name: :f │ ├── name_loc: (171,4)-(171,5) = "f" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (171,6)-(171,10)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -1817,11 +1984,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (171,12)-(171,15) = "end" ├── @ DefNode (location: (173,0)-(173,15)) + │ ├── flags: newline │ ├── name: :f │ ├── name_loc: (173,4)-(173,5) = "f" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (173,6)-(173,10)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -1863,11 +2032,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (173,12)-(173,15) = "end" ├── @ DefNode (location: (175,0)-(175,20)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (175,4)-(175,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (175,8)-(175,15)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -1895,11 +2066,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (175,17)-(175,20) = "end" ├── @ DefNode (location: (177,0)-(179,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (177,4)-(177,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (177,8)-(177,11)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -1907,12 +2080,14 @@ │ │ ├── keywords: (length: 0) │ │ ├── keyword_rest: │ │ │ @ ForwardingParameterNode (location: (177,8)-(177,11)) + │ │ │ └── flags: ∅ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (178,2)-(178,10)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (178,2)-(178,10)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -1923,6 +2098,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ ForwardingArgumentsNode (location: (178,6)-(178,9)) + │ │ │ └── flags: ∅ │ │ ├── closing_loc: (178,9)-(178,10) = ")" │ │ └── block: ∅ │ ├── locals: [] @@ -1933,11 +2109,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (179,0)-(179,3) = "end" ├── @ DefNode (location: (181,0)-(181,42)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (181,4)-(181,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (181,8)-(181,37)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 1) │ │ │ └── @ OptionalParameterNode (location: (181,8)-(181,37)) @@ -1947,15 +2125,19 @@ │ │ │ ├── operator_loc: (181,12)-(181,13) = "=" │ │ │ └── value: │ │ │ @ ParenthesesNode (location: (181,14)-(181,37)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (181,15)-(181,36)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 2) │ │ │ │ ├── @ DefNode (location: (181,15)-(181,33)) + │ │ │ │ │ ├── flags: newline │ │ │ │ │ ├── name: :baz │ │ │ │ │ ├── name_loc: (181,19)-(181,22) = "baz" │ │ │ │ │ ├── receiver: ∅ │ │ │ │ │ ├── parameters: │ │ │ │ │ │ @ ParametersNode (location: (181,23)-(181,26)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ ├── requireds: (length: 1) │ │ │ │ │ │ │ └── @ RequiredParameterNode (location: (181,23)-(181,26)) │ │ │ │ │ │ │ ├── flags: ∅ @@ -1968,8 +2150,10 @@ │ │ │ │ │ │ └── block: ∅ │ │ │ │ │ ├── body: │ │ │ │ │ │ @ StatementsNode (location: (181,30)-(181,33)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ │ └── @ LocalVariableReadNode (location: (181,30)-(181,33)) + │ │ │ │ │ │ ├── flags: newline │ │ │ │ │ │ ├── name: :bar │ │ │ │ │ │ └── depth: 0 │ │ │ │ │ ├── locals: [:bar] @@ -1980,7 +2164,7 @@ │ │ │ │ │ ├── equal_loc: (181,28)-(181,29) = "=" │ │ │ │ │ └── end_keyword_loc: ∅ │ │ │ │ └── @ IntegerNode (location: (181,35)-(181,36)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: newline, static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── opening_loc: (181,14)-(181,15) = "(" │ │ │ └── closing_loc: (181,36)-(181,37) = ")" @@ -1991,9 +2175,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (181,41)-(181,42)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (181,41)-(181,42)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 2 │ ├── locals: [:bar] │ ├── def_keyword_loc: (181,0)-(181,3) = "def" @@ -2003,16 +2188,20 @@ │ ├── equal_loc: (181,39)-(181,40) = "=" │ └── end_keyword_loc: ∅ └── @ DefNode (location: (183,0)-(183,37)) + ├── flags: newline ├── name: :foo ├── name_loc: (183,21)-(183,24) = "foo" ├── receiver: │ @ ParenthesesNode (location: (183,4)-(183,20)) + │ ├── flags: ∅ │ ├── body: │ │ @ ClassNode (location: (183,5)-(183,19)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── class_keyword_loc: (183,5)-(183,10) = "class" │ │ ├── constant_path: │ │ │ @ ConstantReadNode (location: (183,11)-(183,14)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Foo │ │ ├── inheritance_operator_loc: ∅ │ │ ├── superclass: ∅ @@ -2023,6 +2212,7 @@ │ └── closing_loc: (183,19)-(183,20) = ")" ├── parameters: │ @ ParametersNode (location: (183,25)-(183,32)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 1) │ │ └── @ OptionalParameterNode (location: (183,25)-(183,32)) @@ -2032,7 +2222,7 @@ │ │ ├── operator_loc: (183,29)-(183,30) = "=" │ │ └── value: │ │ @ IntegerNode (location: (183,31)-(183,32)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── rest: ∅ │ ├── posts: (length: 0) @@ -2041,9 +2231,10 @@ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (183,36)-(183,37)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ IntegerNode (location: (183,36)-(183,37)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 2 ├── locals: [:bar] ├── def_keyword_loc: (183,0)-(183,3) = "def" diff --git a/test/prism/snapshots/modules.txt b/test/prism/snapshots/modules.txt index de1ea8feebc951..d889d855af4678 100644 --- a/test/prism/snapshots/modules.txt +++ b/test/prism/snapshots/modules.txt @@ -1,42 +1,50 @@ @ ProgramNode (location: (1,0)-(18,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(18,3)) + ├── flags: ∅ └── body: (length: 7) ├── @ ModuleNode (location: (1,0)-(1,18)) + │ ├── flags: newline │ ├── locals: [:a] │ ├── module_keyword_loc: (1,0)-(1,6) = "module" │ ├── constant_path: │ │ @ ConstantReadNode (location: (1,7)-(1,8)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── body: │ │ @ StatementsNode (location: (1,9)-(1,14)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableWriteNode (location: (1,9)-(1,14)) + │ │ ├── flags: newline │ │ ├── name: :a │ │ ├── depth: 0 │ │ ├── name_loc: (1,9)-(1,10) = "a" │ │ ├── value: │ │ │ @ IntegerNode (location: (1,13)-(1,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── operator_loc: (1,11)-(1,12) = "=" │ ├── end_keyword_loc: (1,15)-(1,18) = "end" │ └── name: :A ├── @ InterpolatedStringNode (location: (3,0)-(3,18)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (3,0)-(3,3) = "%Q{" │ ├── parts: (length: 3) │ │ ├── @ StringNode (location: (3,3)-(3,7)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (3,3)-(3,7) = "aaa " │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "aaa " │ │ ├── @ EmbeddedStatementsNode (location: (3,7)-(3,13)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (3,7)-(3,9) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (3,9)-(3,12)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (3,9)-(3,12)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -50,17 +58,19 @@ │ │ │ │ └── block: ∅ │ │ │ └── closing_loc: (3,12)-(3,13) = "}" │ │ └── @ StringNode (location: (3,13)-(3,17)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (3,13)-(3,17) = " ccc" │ │ ├── closing_loc: ∅ │ │ └── unescaped: " ccc" │ └── closing_loc: (3,17)-(3,18) = "}" ├── @ ModuleNode (location: (5,0)-(6,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── module_keyword_loc: (5,0)-(5,6) = "module" │ ├── constant_path: │ │ @ ConstantPathNode (location: (5,7)-(5,11)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ CallNode (location: (5,7)-(5,8)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -79,28 +89,34 @@ │ ├── end_keyword_loc: (6,0)-(6,3) = "end" │ └── name: :M ├── @ ModuleNode (location: (8,0)-(9,19)) + │ ├── flags: newline │ ├── locals: [:x] │ ├── module_keyword_loc: (8,0)-(8,6) = "module" │ ├── constant_path: │ │ @ ConstantReadNode (location: (8,7)-(8,8)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── body: │ │ @ BeginNode (location: (8,0)-(9,19)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (9,1)-(9,6)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ LocalVariableWriteNode (location: (9,1)-(9,6)) + │ │ │ ├── flags: newline │ │ │ ├── name: :x │ │ │ ├── depth: 0 │ │ │ ├── name_loc: (9,1)-(9,2) = "x" │ │ │ ├── value: │ │ │ │ @ IntegerNode (location: (9,5)-(9,6)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── operator_loc: (9,3)-(9,4) = "=" │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (9,8)-(9,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (9,8)-(9,14) = "rescue" │ │ │ ├── exceptions: (length: 0) │ │ │ ├── operator_loc: ∅ @@ -113,10 +129,12 @@ │ ├── end_keyword_loc: (9,16)-(9,19) = "end" │ └── name: :A ├── @ ModuleNode (location: (11,0)-(12,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── module_keyword_loc: (11,0)-(11,6) = "module" │ ├── constant_path: │ │ @ ConstantPathNode (location: (11,7)-(11,10)) + │ │ ├── flags: ∅ │ │ ├── parent: ∅ │ │ ├── name: :A │ │ ├── delimiter_loc: (11,7)-(11,9) = "::" @@ -125,15 +143,18 @@ │ ├── end_keyword_loc: (12,0)-(12,3) = "end" │ └── name: :A ├── @ ModuleNode (location: (14,0)-(15,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── module_keyword_loc: (14,0)-(14,6) = "module" │ ├── constant_path: │ │ @ ConstantPathNode (location: (14,7)-(14,13)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ CallNode (location: (14,7)-(14,10)) │ │ │ ├── flags: ∅ │ │ │ ├── receiver: │ │ │ │ @ ConstantReadNode (location: (14,7)-(14,8)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :A │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :[] @@ -149,15 +170,18 @@ │ ├── end_keyword_loc: (15,0)-(15,3) = "end" │ └── name: :B └── @ ModuleNode (location: (17,0)-(18,3)) + ├── flags: newline ├── locals: [] ├── module_keyword_loc: (17,0)-(17,6) = "module" ├── constant_path: │ @ ConstantPathNode (location: (17,7)-(17,14)) + │ ├── flags: ∅ │ ├── parent: │ │ @ CallNode (location: (17,7)-(17,11)) │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ ConstantReadNode (location: (17,7)-(17,8)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :A │ │ ├── call_operator_loc: ∅ │ │ ├── name: :[] @@ -168,7 +192,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (17,9)-(17,10)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── closing_loc: (17,10)-(17,11) = "]" │ │ └── block: ∅ diff --git a/test/prism/snapshots/multi_write.txt b/test/prism/snapshots/multi_write.txt index d313801fdb2b89..fa36f50423f762 100644 --- a/test/prism/snapshots/multi_write.txt +++ b/test/prism/snapshots/multi_write.txt @@ -1,28 +1,36 @@ @ ProgramNode (location: (1,0)-(4,26)) +├── flags: ∅ ├── locals: [:foo, :bar] └── statements: @ StatementsNode (location: (1,0)-(4,26)) + ├── flags: ∅ └── body: (length: 4) ├── @ LocalVariableWriteNode (location: (1,0)-(1,18)) + │ ├── flags: newline │ ├── name: :foo │ ├── depth: 0 │ ├── name_loc: (1,0)-(1,3) = "foo" │ ├── value: │ │ @ RescueModifierNode (location: (1,6)-(1,18)) + │ │ ├── flags: ∅ │ │ ├── expression: │ │ │ @ IntegerNode (location: (1,6)-(1,7)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── keyword_loc: (1,8)-(1,14) = "rescue" │ │ └── rescue_expression: │ │ @ NilNode (location: (1,15)-(1,18)) + │ │ └── flags: static_literal │ └── operator_loc: (1,4)-(1,5) = "=" ├── @ MultiWriteNode (location: (2,0)-(2,23)) + │ ├── flags: newline │ ├── lefts: (length: 2) │ │ ├── @ LocalVariableTargetNode (location: (2,0)-(2,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :foo │ │ │ └── depth: 0 │ │ └── @ LocalVariableTargetNode (location: (2,5)-(2,8)) + │ │ ├── flags: ∅ │ │ ├── name: :bar │ │ └── depth: 0 │ ├── rest: ∅ @@ -32,28 +40,32 @@ │ ├── operator_loc: (2,9)-(2,10) = "=" │ └── value: │ @ RescueModifierNode (location: (2,11)-(2,23)) + │ ├── flags: ∅ │ ├── expression: │ │ @ IntegerNode (location: (2,11)-(2,12)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── keyword_loc: (2,13)-(2,19) = "rescue" │ └── rescue_expression: │ @ NilNode (location: (2,20)-(2,23)) + │ └── flags: static_literal ├── @ RescueModifierNode (location: (3,0)-(3,21)) + │ ├── flags: newline │ ├── expression: │ │ @ LocalVariableWriteNode (location: (3,0)-(3,10)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ ├── depth: 0 │ │ ├── name_loc: (3,0)-(3,3) = "foo" │ │ ├── value: │ │ │ @ ArrayNode (location: (3,6)-(3,10)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: static_literal │ │ │ ├── elements: (length: 2) │ │ │ │ ├── @ IntegerNode (location: (3,6)-(3,7)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ └── @ IntegerNode (location: (3,9)-(3,10)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ ├── opening_loc: ∅ │ │ │ └── closing_loc: ∅ @@ -61,12 +73,16 @@ │ ├── keyword_loc: (3,11)-(3,17) = "rescue" │ └── rescue_expression: │ @ NilNode (location: (3,18)-(3,21)) + │ └── flags: static_literal └── @ MultiWriteNode (location: (4,0)-(4,26)) + ├── flags: newline ├── lefts: (length: 2) │ ├── @ LocalVariableTargetNode (location: (4,0)-(4,3)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ └── depth: 0 │ └── @ LocalVariableTargetNode (location: (4,5)-(4,8)) + │ ├── flags: ∅ │ ├── name: :bar │ └── depth: 0 ├── rest: ∅ @@ -76,18 +92,20 @@ ├── operator_loc: (4,9)-(4,10) = "=" └── value: @ RescueModifierNode (location: (4,11)-(4,26)) + ├── flags: ∅ ├── expression: │ @ ArrayNode (location: (4,11)-(4,15)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 2) │ │ ├── @ IntegerNode (location: (4,11)-(4,12)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ IntegerNode (location: (4,14)-(4,15)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── opening_loc: ∅ │ └── closing_loc: ∅ ├── keyword_loc: (4,16)-(4,22) = "rescue" └── rescue_expression: @ NilNode (location: (4,23)-(4,26)) + └── flags: static_literal diff --git a/test/prism/snapshots/newline_terminated.txt b/test/prism/snapshots/newline_terminated.txt index 6a3b28dba98d3a..85e996fa5a2377 100644 --- a/test/prism/snapshots/newline_terminated.txt +++ b/test/prism/snapshots/newline_terminated.txt @@ -1,106 +1,108 @@ @ ProgramNode (location: (3,0)-(41,0)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (3,0)-(41,0)) + ├── flags: ∅ └── body: (length: 17) ├── @ StringNode (location: (3,0)-(3,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (3,0)-(3,2) = "% " │ ├── content_loc: (3,2)-(3,5) = "abc" │ ├── closing_loc: (3,5)-(3,6) = " " │ └── unescaped: "abc" ├── @ StringNode (location: (4,0)-(4,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (4,0)-(4,2) = "%\t" │ ├── content_loc: (4,2)-(4,5) = "abc" │ ├── closing_loc: (4,5)-(4,6) = "\t" │ └── unescaped: "abc" ├── @ StringNode (location: (5,0)-(5,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (5,0)-(5,2) = "%\v" │ ├── content_loc: (5,2)-(5,5) = "abc" │ ├── closing_loc: (5,5)-(5,6) = "\v" │ └── unescaped: "abc" ├── @ StringNode (location: (6,0)-(6,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (6,0)-(6,2) = "%\r" │ ├── content_loc: (6,2)-(6,5) = "abc" │ ├── closing_loc: (6,5)-(6,6) = "\r" │ └── unescaped: "abc" ├── @ StringNode (location: (7,0)-(9,0)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (7,0)-(8,0) = "%\n" │ ├── content_loc: (8,0)-(8,3) = "abc" │ ├── closing_loc: (8,3)-(9,0) = "\n" │ └── unescaped: "abc" ├── @ StringNode (location: (10,0)-(10,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (10,0)-(10,2) = "%\u0000" │ ├── content_loc: (10,2)-(10,5) = "abc" │ ├── closing_loc: (10,5)-(10,6) = "\u0000" │ └── unescaped: "abc" ├── @ StringNode (location: (11,0)-(13,0)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (11,0)-(12,0) = "%\n" │ ├── content_loc: (12,0)-(12,3) = "abc" │ ├── closing_loc: (12,3)-(13,0) = "\n" │ └── unescaped: "abc" ├── @ StringNode (location: (14,0)-(16,0)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (14,0)-(15,0) = "%\n" │ ├── content_loc: (15,0)-(15,4) = "\rabc" │ ├── closing_loc: (15,4)-(16,0) = "\n" │ └── unescaped: "\rabc" ├── @ StringNode (location: (17,0)-(19,0)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (17,0)-(18,0) = "%\n" │ ├── content_loc: (18,0)-(18,4) = "\rabc" │ ├── closing_loc: (18,4)-(19,0) = "\n" │ └── unescaped: "\rabc" ├── @ StringNode (location: (20,0)-(22,0)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (20,0)-(21,0) = "%\n" │ ├── content_loc: (21,0)-(21,3) = "abc" │ ├── closing_loc: (21,3)-(22,0) = "\n" │ └── unescaped: "abc" ├── @ StringNode (location: (23,0)-(23,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (23,0)-(23,2) = "%\r" │ ├── content_loc: (23,2)-(23,5) = "abc" │ ├── closing_loc: (23,5)-(23,6) = "\r" │ └── unescaped: "abc" ├── @ StringNode (location: (24,0)-(26,0)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (24,0)-(25,0) = "%\n" │ ├── content_loc: (25,0)-(25,3) = "abc" │ ├── closing_loc: (25,3)-(26,0) = "\n" │ └── unescaped: "abc" ├── @ StringNode (location: (27,0)-(29,0)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (27,0)-(28,0) = "%\n" │ ├── content_loc: (28,0)-(28,3) = "abc" │ ├── closing_loc: (28,3)-(29,0) = "\n" │ └── unescaped: "abc" ├── @ StringNode (location: (30,0)-(32,0)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (30,0)-(31,0) = "%\n" │ ├── content_loc: (31,0)-(31,3) = "foo" │ ├── closing_loc: (31,3)-(32,0) = "\n" │ └── unescaped: "foo" ├── @ StringNode (location: (33,0)-(35,0)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (33,0)-(34,0) = "%q\n" │ ├── content_loc: (34,0)-(34,3) = "foo" │ ├── closing_loc: (34,3)-(35,0) = "\n" │ └── unescaped: "foo" ├── @ StringNode (location: (36,0)-(38,0)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (36,0)-(37,0) = "%Q\n" │ ├── content_loc: (37,0)-(37,3) = "foo" │ ├── closing_loc: (37,3)-(38,0) = "\n" │ └── unescaped: "foo" └── @ RegularExpressionNode (location: (39,0)-(41,0)) - ├── flags: forced_us_ascii_encoding + ├── flags: newline, static_literal, forced_us_ascii_encoding ├── opening_loc: (39,0)-(40,0) = "%r\n" ├── content_loc: (40,0)-(40,3) = "foo" ├── closing_loc: (40,3)-(41,0) = "\n" diff --git a/test/prism/snapshots/next.txt b/test/prism/snapshots/next.txt index ce2e497de93003..5b0becea1c81dd 100644 --- a/test/prism/snapshots/next.txt +++ b/test/prism/snapshots/next.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(24,15)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(24,15)) + ├── flags: ∅ └── body: (length: 10) ├── @ CallNode (location: (1,0)-(1,12)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -14,18 +16,21 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (1,4)-(1,12)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (1,6)-(1,10)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ NextNode (location: (1,6)-(1,10)) + │ │ ├── flags: newline │ │ ├── arguments: ∅ │ │ └── keyword_loc: (1,6)-(1,10) = "next" │ ├── opening_loc: (1,4)-(1,5) = "{" │ └── closing_loc: (1,11)-(1,12) = "}" ├── @ CallNode (location: (3,0)-(3,26)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -35,40 +40,49 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (3,4)-(3,26)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (3,6)-(3,24)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ NextNode (location: (3,6)-(3,24)) + │ │ ├── flags: newline │ │ ├── arguments: │ │ │ @ ArgumentsNode (location: (3,11)-(3,24)) │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 3) │ │ │ ├── @ ParenthesesNode (location: (3,11)-(3,14)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── body: │ │ │ │ │ @ StatementsNode (location: (3,12)-(3,13)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ IntegerNode (location: (3,12)-(3,13)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: newline, static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ ├── opening_loc: (3,11)-(3,12) = "(" │ │ │ │ └── closing_loc: (3,13)-(3,14) = ")" │ │ │ ├── @ ParenthesesNode (location: (3,16)-(3,19)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── body: │ │ │ │ │ @ StatementsNode (location: (3,17)-(3,18)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ IntegerNode (location: (3,17)-(3,18)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: newline, static_literal, decimal │ │ │ │ │ └── value: 2 │ │ │ │ ├── opening_loc: (3,16)-(3,17) = "(" │ │ │ │ └── closing_loc: (3,18)-(3,19) = ")" │ │ │ └── @ ParenthesesNode (location: (3,21)-(3,24)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (3,22)-(3,23)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ IntegerNode (location: (3,22)-(3,23)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: newline, static_literal, decimal │ │ │ │ └── value: 3 │ │ │ ├── opening_loc: (3,21)-(3,22) = "(" │ │ │ └── closing_loc: (3,23)-(3,24) = ")" @@ -76,7 +90,7 @@ │ ├── opening_loc: (3,4)-(3,5) = "{" │ └── closing_loc: (3,25)-(3,26) = "}" ├── @ CallNode (location: (5,0)-(5,14)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -86,24 +100,27 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (5,4)-(5,14)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (5,6)-(5,12)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ NextNode (location: (5,6)-(5,12)) + │ │ ├── flags: newline │ │ ├── arguments: │ │ │ @ ArgumentsNode (location: (5,11)-(5,12)) │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (5,11)-(5,12)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── keyword_loc: (5,6)-(5,10) = "next" │ ├── opening_loc: (5,4)-(5,5) = "{" │ └── closing_loc: (5,13)-(5,14) = "}" ├── @ CallNode (location: (7,0)-(8,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -113,30 +130,33 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (7,4)-(8,3)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (7,6)-(8,1)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ NextNode (location: (7,6)-(8,1)) + │ │ ├── flags: newline │ │ ├── arguments: │ │ │ @ ArgumentsNode (location: (7,11)-(8,1)) │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 3) │ │ │ ├── @ IntegerNode (location: (7,11)-(7,12)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── @ IntegerNode (location: (7,14)-(7,15)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ └── @ IntegerNode (location: (8,0)-(8,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 3 │ │ └── keyword_loc: (7,6)-(7,10) = "next" │ ├── opening_loc: (7,4)-(7,5) = "{" │ └── closing_loc: (8,2)-(8,3) = "}" ├── @ CallNode (location: (10,0)-(10,20)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -146,30 +166,33 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (10,4)-(10,20)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (10,6)-(10,18)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ NextNode (location: (10,6)-(10,18)) + │ │ ├── flags: newline │ │ ├── arguments: │ │ │ @ ArgumentsNode (location: (10,11)-(10,18)) │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 3) │ │ │ ├── @ IntegerNode (location: (10,11)-(10,12)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── @ IntegerNode (location: (10,14)-(10,15)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ └── @ IntegerNode (location: (10,17)-(10,18)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 3 │ │ └── keyword_loc: (10,6)-(10,10) = "next" │ ├── opening_loc: (10,4)-(10,5) = "{" │ └── closing_loc: (10,19)-(10,20) = "}" ├── @ CallNode (location: (12,0)-(12,22)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -179,27 +202,30 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (12,4)-(12,22)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (12,6)-(12,20)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ NextNode (location: (12,6)-(12,20)) + │ │ ├── flags: newline │ │ ├── arguments: │ │ │ @ ArgumentsNode (location: (12,11)-(12,20)) │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ ArrayNode (location: (12,11)-(12,20)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: static_literal │ │ │ ├── elements: (length: 3) │ │ │ │ ├── @ IntegerNode (location: (12,12)-(12,13)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ ├── @ IntegerNode (location: (12,15)-(12,16)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 2 │ │ │ │ └── @ IntegerNode (location: (12,18)-(12,19)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 3 │ │ │ ├── opening_loc: (12,11)-(12,12) = "[" │ │ │ └── closing_loc: (12,19)-(12,20) = "]" @@ -207,7 +233,7 @@ │ ├── opening_loc: (12,4)-(12,5) = "{" │ └── closing_loc: (12,21)-(12,22) = "}" ├── @ CallNode (location: (14,0)-(17,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -217,25 +243,30 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (14,4)-(17,3)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (14,6)-(17,1)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ NextNode (location: (14,6)-(17,1)) + │ │ ├── flags: newline │ │ ├── arguments: │ │ │ @ ArgumentsNode (location: (14,10)-(17,1)) │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ ParenthesesNode (location: (14,10)-(17,1)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (15,2)-(16,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 2) │ │ │ │ ├── @ IntegerNode (location: (15,2)-(15,3)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: newline, static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ └── @ IntegerNode (location: (16,2)-(16,3)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: newline, static_literal, decimal │ │ │ │ └── value: 2 │ │ │ ├── opening_loc: (14,10)-(14,11) = "(" │ │ │ └── closing_loc: (17,0)-(17,1) = ")" @@ -243,7 +274,7 @@ │ ├── opening_loc: (14,4)-(14,5) = "{" │ └── closing_loc: (17,2)-(17,3) = "}" ├── @ CallNode (location: (19,0)-(20,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -253,21 +284,24 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (19,4)-(20,3)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (19,6)-(20,1)) + │ │ ├── flags: ∅ │ │ └── body: (length: 2) │ │ ├── @ NextNode (location: (19,6)-(19,10)) + │ │ │ ├── flags: newline │ │ │ ├── arguments: ∅ │ │ │ └── keyword_loc: (19,6)-(19,10) = "next" │ │ └── @ IntegerNode (location: (20,0)-(20,1)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 1 │ ├── opening_loc: (19,4)-(19,5) = "{" │ └── closing_loc: (20,2)-(20,3) = "}" ├── @ CallNode (location: (22,0)-(22,14)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -277,17 +311,21 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (22,4)-(22,14)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (22,6)-(22,12)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ NextNode (location: (22,6)-(22,12)) + │ │ ├── flags: newline │ │ ├── arguments: │ │ │ @ ArgumentsNode (location: (22,10)-(22,12)) │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ ParenthesesNode (location: (22,10)-(22,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: ∅ │ │ │ ├── opening_loc: (22,10)-(22,11) = "(" │ │ │ └── closing_loc: (22,11)-(22,12) = ")" @@ -295,7 +333,7 @@ │ ├── opening_loc: (22,4)-(22,5) = "{" │ └── closing_loc: (22,13)-(22,14) = "}" └── @ CallNode (location: (24,0)-(24,15)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :tap @@ -305,22 +343,27 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (24,4)-(24,15)) + ├── flags: ∅ ├── locals: [] ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (24,6)-(24,13)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ NextNode (location: (24,6)-(24,13)) + │ ├── flags: newline │ ├── arguments: │ │ @ ArgumentsNode (location: (24,10)-(24,13)) │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ ParenthesesNode (location: (24,10)-(24,13)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (24,11)-(24,12)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ IntegerNode (location: (24,11)-(24,12)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: newline, static_literal, decimal │ │ │ └── value: 1 │ │ ├── opening_loc: (24,10)-(24,11) = "(" │ │ └── closing_loc: (24,12)-(24,13) = ")" diff --git a/test/prism/snapshots/nils.txt b/test/prism/snapshots/nils.txt index f72745560fee7e..275bc373a534c7 100644 --- a/test/prism/snapshots/nils.txt +++ b/test/prism/snapshots/nils.txt @@ -1,33 +1,42 @@ @ ProgramNode (location: (1,0)-(12,11)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(12,11)) + ├── flags: ∅ └── body: (length: 5) ├── @ NilNode (location: (1,0)-(1,3)) + │ └── flags: newline, static_literal ├── @ ParenthesesNode (location: (3,0)-(3,2)) + │ ├── flags: newline │ ├── body: ∅ │ ├── opening_loc: (3,0)-(3,1) = "(" │ └── closing_loc: (3,1)-(3,2) = ")" ├── @ ParenthesesNode (location: (5,0)-(8,1)) + │ ├── flags: newline │ ├── body: ∅ │ ├── opening_loc: (5,0)-(5,1) = "(" │ └── closing_loc: (8,0)-(8,1) = ")" ├── @ PostExecutionNode (location: (10,0)-(10,9)) + │ ├── flags: newline │ ├── statements: │ │ @ StatementsNode (location: (10,6)-(10,7)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (10,6)-(10,7)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 1 │ ├── keyword_loc: (10,0)-(10,3) = "END" │ ├── opening_loc: (10,4)-(10,5) = "{" │ └── closing_loc: (10,8)-(10,9) = "}" └── @ PreExecutionNode (location: (12,0)-(12,11)) + ├── flags: newline ├── statements: │ @ StatementsNode (location: (12,8)-(12,9)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ IntegerNode (location: (12,8)-(12,9)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 1 ├── keyword_loc: (12,0)-(12,5) = "BEGIN" ├── opening_loc: (12,6)-(12,7) = "{" diff --git a/test/prism/snapshots/non_alphanumeric_methods.txt b/test/prism/snapshots/non_alphanumeric_methods.txt index 2ed66fe0e2e0d6..2d29d365e467a8 100644 --- a/test/prism/snapshots/non_alphanumeric_methods.txt +++ b/test/prism/snapshots/non_alphanumeric_methods.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(105,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(105,3)) + ├── flags: ∅ └── body: (length: 36) ├── @ DefNode (location: (1,0)-(2,3)) + │ ├── flags: newline │ ├── name: :! │ ├── name_loc: (1,4)-(1,5) = "!" │ ├── receiver: ∅ @@ -17,6 +20,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (2,0)-(2,3) = "end" ├── @ DefNode (location: (4,0)-(5,3)) + │ ├── flags: newline │ ├── name: :!= │ ├── name_loc: (4,4)-(4,6) = "!=" │ ├── receiver: ∅ @@ -30,6 +34,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (5,0)-(5,3) = "end" ├── @ DefNode (location: (7,0)-(8,3)) + │ ├── flags: newline │ ├── name: :!~ │ ├── name_loc: (7,4)-(7,6) = "!~" │ ├── receiver: ∅ @@ -43,6 +48,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (8,0)-(8,3) = "end" ├── @ DefNode (location: (10,0)-(11,3)) + │ ├── flags: newline │ ├── name: :% │ ├── name_loc: (10,4)-(10,5) = "%" │ ├── receiver: ∅ @@ -56,10 +62,12 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (11,0)-(11,3) = "end" ├── @ DefNode (location: (13,0)-(14,3)) + │ ├── flags: newline │ ├── name: :+ │ ├── name_loc: (13,9)-(13,10) = "+" │ ├── receiver: │ │ @ SelfNode (location: (13,4)-(13,8)) + │ │ └── flags: ∅ │ ├── parameters: ∅ │ ├── body: ∅ │ ├── locals: [] @@ -70,6 +78,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (14,0)-(14,3) = "end" ├── @ DefNode (location: (16,0)-(17,3)) + │ ├── flags: newline │ ├── name: :& │ ├── name_loc: (16,4)-(16,5) = "&" │ ├── receiver: ∅ @@ -83,6 +92,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (17,0)-(17,3) = "end" ├── @ DefNode (location: (19,0)-(20,3)) + │ ├── flags: newline │ ├── name: :* │ ├── name_loc: (19,4)-(19,5) = "*" │ ├── receiver: ∅ @@ -96,6 +106,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (20,0)-(20,3) = "end" ├── @ DefNode (location: (22,0)-(23,3)) + │ ├── flags: newline │ ├── name: :** │ ├── name_loc: (22,4)-(22,6) = "**" │ ├── receiver: ∅ @@ -109,17 +120,19 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (23,0)-(23,3) = "end" ├── @ StringNode (location: (25,0)-(25,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (25,0)-(25,2) = "%|" │ ├── content_loc: (25,2)-(25,5) = "abc" │ ├── closing_loc: (25,5)-(25,6) = "|" │ └── unescaped: "abc" ├── @ DefNode (location: (27,0)-(28,3)) + │ ├── flags: newline │ ├── name: :+ │ ├── name_loc: (27,4)-(27,5) = "+" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (27,6)-(27,9)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -141,6 +154,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (28,0)-(28,3) = "end" ├── @ DefNode (location: (30,0)-(31,3)) + │ ├── flags: newline │ ├── name: :+ │ ├── name_loc: (30,4)-(30,5) = "+" │ ├── receiver: ∅ @@ -154,11 +168,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (31,0)-(31,3) = "end" ├── @ DefNode (location: (33,0)-(34,3)) + │ ├── flags: newline │ ├── name: :+ │ ├── name_loc: (33,4)-(33,5) = "+" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (33,6)-(33,7)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (33,6)-(33,7)) │ │ │ ├── flags: ∅ @@ -178,10 +194,12 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (34,0)-(34,3) = "end" ├── @ DefNode (location: (36,0)-(37,3)) + │ ├── flags: newline │ ├── name: :+ │ ├── name_loc: (36,9)-(36,10) = "+" │ ├── receiver: │ │ @ SelfNode (location: (36,4)-(36,8)) + │ │ └── flags: ∅ │ ├── parameters: ∅ │ ├── body: ∅ │ ├── locals: [] @@ -192,6 +210,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (37,0)-(37,3) = "end" ├── @ DefNode (location: (39,0)-(40,3)) + │ ├── flags: newline │ ├── name: :+ │ ├── name_loc: (39,4)-(39,5) = "+" │ ├── receiver: ∅ @@ -205,6 +224,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (40,0)-(40,3) = "end" ├── @ DefNode (location: (42,0)-(43,3)) + │ ├── flags: newline │ ├── name: :+@ │ ├── name_loc: (42,4)-(42,6) = "+@" │ ├── receiver: ∅ @@ -218,6 +238,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (43,0)-(43,3) = "end" ├── @ DefNode (location: (45,0)-(46,3)) + │ ├── flags: newline │ ├── name: :- │ ├── name_loc: (45,4)-(45,5) = "-" │ ├── receiver: ∅ @@ -231,6 +252,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (46,0)-(46,3) = "end" ├── @ DefNode (location: (48,0)-(48,11)) + │ ├── flags: newline │ ├── name: :- │ ├── name_loc: (48,6)-(48,7) = "-" │ ├── receiver: @@ -254,6 +276,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (48,8)-(48,11) = "end" ├── @ DefNode (location: (50,0)-(51,3)) + │ ├── flags: newline │ ├── name: :-@ │ ├── name_loc: (50,4)-(50,6) = "-@" │ ├── receiver: ∅ @@ -267,6 +290,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (51,0)-(51,3) = "end" ├── @ DefNode (location: (53,0)-(54,3)) + │ ├── flags: newline │ ├── name: :/ │ ├── name_loc: (53,4)-(53,5) = "/" │ ├── receiver: ∅ @@ -280,6 +304,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (54,0)-(54,3) = "end" ├── @ DefNode (location: (56,0)-(57,3)) + │ ├── flags: newline │ ├── name: :< │ ├── name_loc: (56,4)-(56,5) = "<" │ ├── receiver: ∅ @@ -293,6 +318,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (57,0)-(57,3) = "end" ├── @ DefNode (location: (59,0)-(60,3)) + │ ├── flags: newline │ ├── name: :<< │ ├── name_loc: (59,4)-(59,6) = "<<" │ ├── receiver: ∅ @@ -306,6 +332,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (60,0)-(60,3) = "end" ├── @ DefNode (location: (62,0)-(63,3)) + │ ├── flags: newline │ ├── name: :<= │ ├── name_loc: (62,4)-(62,6) = "<=" │ ├── receiver: ∅ @@ -319,6 +346,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (63,0)-(63,3) = "end" ├── @ DefNode (location: (65,0)-(66,3)) + │ ├── flags: newline │ ├── name: :<=> │ ├── name_loc: (65,4)-(65,7) = "<=>" │ ├── receiver: ∅ @@ -332,6 +360,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (66,0)-(66,3) = "end" ├── @ DefNode (location: (68,0)-(69,3)) + │ ├── flags: newline │ ├── name: :== │ ├── name_loc: (68,4)-(68,6) = "==" │ ├── receiver: ∅ @@ -345,6 +374,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (69,0)-(69,3) = "end" ├── @ DefNode (location: (71,0)-(72,3)) + │ ├── flags: newline │ ├── name: :=== │ ├── name_loc: (71,4)-(71,7) = "===" │ ├── receiver: ∅ @@ -358,6 +388,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (72,0)-(72,3) = "end" ├── @ DefNode (location: (74,0)-(75,3)) + │ ├── flags: newline │ ├── name: :=~ │ ├── name_loc: (74,4)-(74,6) = "=~" │ ├── receiver: ∅ @@ -371,6 +402,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (75,0)-(75,3) = "end" ├── @ DefNode (location: (77,0)-(78,3)) + │ ├── flags: newline │ ├── name: :> │ ├── name_loc: (77,4)-(77,5) = ">" │ ├── receiver: ∅ @@ -384,6 +416,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (78,0)-(78,3) = "end" ├── @ DefNode (location: (80,0)-(81,3)) + │ ├── flags: newline │ ├── name: :>= │ ├── name_loc: (80,4)-(80,6) = ">=" │ ├── receiver: ∅ @@ -397,6 +430,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (81,0)-(81,3) = "end" ├── @ DefNode (location: (83,0)-(84,3)) + │ ├── flags: newline │ ├── name: :>> │ ├── name_loc: (83,4)-(83,6) = ">>" │ ├── receiver: ∅ @@ -410,6 +444,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (84,0)-(84,3) = "end" ├── @ DefNode (location: (86,0)-(87,3)) + │ ├── flags: newline │ ├── name: :[] │ ├── name_loc: (86,4)-(86,6) = "[]" │ ├── receiver: ∅ @@ -423,6 +458,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (87,0)-(87,3) = "end" ├── @ DefNode (location: (89,0)-(90,3)) + │ ├── flags: newline │ ├── name: :[]= │ ├── name_loc: (89,4)-(89,7) = "[]=" │ ├── receiver: ∅ @@ -436,6 +472,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (90,0)-(90,3) = "end" ├── @ DefNode (location: (92,0)-(93,3)) + │ ├── flags: newline │ ├── name: :^ │ ├── name_loc: (92,4)-(92,5) = "^" │ ├── receiver: ∅ @@ -449,6 +486,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (93,0)-(93,3) = "end" ├── @ DefNode (location: (95,0)-(96,3)) + │ ├── flags: newline │ ├── name: :` │ ├── name_loc: (95,4)-(95,5) = "`" │ ├── receiver: ∅ @@ -462,10 +500,12 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (96,0)-(96,3) = "end" ├── @ DefNode (location: (98,0)-(99,3)) + │ ├── flags: newline │ ├── name: :` │ ├── name_loc: (98,9)-(98,10) = "`" │ ├── receiver: │ │ @ SelfNode (location: (98,4)-(98,8)) + │ │ └── flags: ∅ │ ├── parameters: ∅ │ ├── body: ∅ │ ├── locals: [] @@ -476,6 +516,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (99,0)-(99,3) = "end" ├── @ DefNode (location: (101,0)-(102,3)) + │ ├── flags: newline │ ├── name: :| │ ├── name_loc: (101,4)-(101,5) = "|" │ ├── receiver: ∅ @@ -489,6 +530,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (102,0)-(102,3) = "end" └── @ DefNode (location: (104,0)-(105,3)) + ├── flags: newline ├── name: :~ ├── name_loc: (104,4)-(104,5) = "~" ├── receiver: ∅ diff --git a/test/prism/snapshots/not.txt b/test/prism/snapshots/not.txt index fda61bb4b585ca..e164b18813a44d 100644 --- a/test/prism/snapshots/not.txt +++ b/test/prism/snapshots/not.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(37,16)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(37,16)) + ├── flags: ∅ └── body: (length: 10) ├── @ AndNode (location: (1,0)-(1,19)) + │ ├── flags: newline │ ├── left: │ │ @ CallNode (location: (1,0)-(1,7)) │ │ ├── flags: ∅ @@ -48,9 +51,10 @@ │ │ └── block: ∅ │ └── operator_loc: (1,8)-(1,11) = "and" ├── @ CallNode (location: (3,0)-(3,16)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ AndNode (location: (3,4)-(3,15)) + │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ CallNode (location: (3,4)-(3,7)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -82,7 +86,7 @@ │ ├── closing_loc: (3,15)-(3,16) = ")" │ └── block: ∅ ├── @ CallNode (location: (5,0)-(5,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (5,4)-(5,7)) │ │ ├── flags: variable_call, ignore_visibility @@ -102,6 +106,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ AndNode (location: (7,0)-(8,5)) + │ ├── flags: newline │ ├── left: │ │ @ CallNode (location: (7,0)-(7,7)) │ │ ├── flags: ∅ @@ -146,6 +151,7 @@ │ │ └── block: ∅ │ └── operator_loc: (7,8)-(7,11) = "and" ├── @ AndNode (location: (11,0)-(13,5)) + │ ├── flags: newline │ ├── left: │ │ @ CallNode (location: (11,0)-(11,7)) │ │ ├── flags: ∅ @@ -190,6 +196,7 @@ │ │ └── block: ∅ │ └── operator_loc: (11,8)-(11,11) = "and" ├── @ AndNode (location: (16,0)-(20,5)) + │ ├── flags: newline │ ├── left: │ │ @ CallNode (location: (16,0)-(16,7)) │ │ ├── flags: ∅ @@ -234,7 +241,7 @@ │ │ └── block: ∅ │ └── operator_loc: (16,8)-(16,11) = "and" ├── @ CallNode (location: (22,0)-(25,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (22,4)-(22,7)) │ │ ├── flags: variable_call, ignore_visibility @@ -254,7 +261,7 @@ │ ├── closing_loc: (25,0)-(25,1) = ")" │ └── block: ∅ ├── @ CallNode (location: (27,0)-(33,3)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (30,0)-(30,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -274,7 +281,7 @@ │ ├── closing_loc: (33,2)-(33,3) = ")" │ └── block: ∅ ├── @ CallNode (location: (35,0)-(35,14)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ FlipFlopNode (location: (35,4)-(35,14)) │ │ ├── flags: ∅ @@ -309,14 +316,16 @@ │ ├── closing_loc: ∅ │ └── block: ∅ └── @ CallNode (location: (37,0)-(37,16)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ ParenthesesNode (location: (37,4)-(37,16)) + │ ├── flags: ∅ │ ├── body: │ │ @ StatementsNode (location: (37,5)-(37,15)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ FlipFlopNode (location: (37,5)-(37,15)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── left: │ │ │ @ CallNode (location: (37,5)-(37,8)) │ │ │ ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/numbers.txt b/test/prism/snapshots/numbers.txt index 58aea454fa0ae0..4512427ef320ad 100644 --- a/test/prism/snapshots/numbers.txt +++ b/test/prism/snapshots/numbers.txt @@ -1,135 +1,147 @@ @ ProgramNode (location: (1,0)-(67,5)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(67,5)) + ├── flags: ∅ └── body: (length: 34) ├── @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 0 ├── @ IntegerNode (location: (3,0)-(3,1)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 1 ├── @ FloatNode (location: (5,0)-(5,3)) + │ ├── flags: newline, static_literal │ └── value: 1.0 ├── @ IntegerNode (location: (7,0)-(7,1)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 2 ├── @ IntegerNode (location: (9,0)-(9,3)) - │ ├── flags: binary + │ ├── flags: newline, static_literal, binary │ └── value: 0 ├── @ IntegerNode (location: (11,0)-(11,3)) - │ ├── flags: binary + │ ├── flags: newline, static_literal, binary │ └── value: 1 ├── @ IntegerNode (location: (13,0)-(13,4)) - │ ├── flags: binary + │ ├── flags: newline, static_literal, binary │ └── value: 2 ├── @ IntegerNode (location: (15,0)-(15,3)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 0 ├── @ IntegerNode (location: (17,0)-(17,3)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 1 ├── @ IntegerNode (location: (19,0)-(19,3)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 2 ├── @ IntegerNode (location: (21,0)-(21,2)) - │ ├── flags: octal + │ ├── flags: newline, static_literal, octal │ └── value: 0 ├── @ IntegerNode (location: (23,0)-(23,2)) - │ ├── flags: octal + │ ├── flags: newline, static_literal, octal │ └── value: 1 ├── @ IntegerNode (location: (25,0)-(25,2)) - │ ├── flags: octal + │ ├── flags: newline, static_literal, octal │ └── value: 2 ├── @ IntegerNode (location: (27,0)-(27,3)) - │ ├── flags: octal + │ ├── flags: newline, static_literal, octal │ └── value: 0 ├── @ IntegerNode (location: (29,0)-(29,3)) - │ ├── flags: octal + │ ├── flags: newline, static_literal, octal │ └── value: 1 ├── @ IntegerNode (location: (31,0)-(31,3)) - │ ├── flags: octal + │ ├── flags: newline, static_literal, octal │ └── value: 2 ├── @ IntegerNode (location: (33,0)-(33,3)) - │ ├── flags: hexadecimal + │ ├── flags: newline, static_literal, hexadecimal │ └── value: 0 ├── @ IntegerNode (location: (35,0)-(35,3)) - │ ├── flags: hexadecimal + │ ├── flags: newline, static_literal, hexadecimal │ └── value: 1 ├── @ IntegerNode (location: (37,0)-(37,3)) - │ ├── flags: hexadecimal + │ ├── flags: newline, static_literal, hexadecimal │ └── value: 2 ├── @ ImaginaryNode (location: (39,0)-(39,2)) + │ ├── flags: newline, static_literal │ └── numeric: │ @ IntegerNode (location: (39,0)-(39,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ RationalNode (location: (41,0)-(41,2)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ ├── numerator: 1 │ └── denominator: 1 ├── @ IntegerNode (location: (43,0)-(43,2)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: -1 ├── @ ImaginaryNode (location: (45,0)-(45,3)) + │ ├── flags: newline, static_literal │ └── numeric: │ @ RationalNode (location: (45,0)-(45,2)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ ├── numerator: 1 │ └── denominator: 1 ├── @ RationalNode (location: (47,0)-(47,4)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ ├── numerator: 6 │ └── denominator: 5 ├── @ ImaginaryNode (location: (49,0)-(49,5)) + │ ├── flags: newline, static_literal │ └── numeric: │ @ RationalNode (location: (49,0)-(49,4)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ ├── numerator: 6 │ └── denominator: 5 ├── @ ImaginaryNode (location: (51,0)-(51,4)) + │ ├── flags: newline, static_literal │ └── numeric: │ @ RationalNode (location: (51,0)-(51,3)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ ├── numerator: -1 │ └── denominator: 1 ├── @ RationalNode (location: (53,0)-(53,5)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ ├── numerator: -6 │ └── denominator: 5 ├── @ ImaginaryNode (location: (55,0)-(55,6)) + │ ├── flags: newline, static_literal │ └── numeric: │ @ RationalNode (location: (55,0)-(55,5)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ ├── numerator: -6 │ └── denominator: 5 ├── @ RationalNode (location: (57,0)-(57,4)) - │ ├── flags: octal + │ ├── flags: newline, static_literal, octal │ ├── numerator: 1 │ └── denominator: 1 ├── @ ImaginaryNode (location: (59,0)-(59,4)) + │ ├── flags: newline, static_literal │ └── numeric: │ @ IntegerNode (location: (59,0)-(59,3)) - │ ├── flags: octal + │ ├── flags: static_literal, octal │ └── value: 1 ├── @ ImaginaryNode (location: (61,0)-(61,5)) + │ ├── flags: newline, static_literal │ └── numeric: │ @ RationalNode (location: (61,0)-(61,4)) - │ ├── flags: octal + │ ├── flags: static_literal, octal │ ├── numerator: 1 │ └── denominator: 1 ├── @ RationalNode (location: (63,0)-(63,4)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ ├── numerator: 1 │ └── denominator: 1 ├── @ ImaginaryNode (location: (65,0)-(65,4)) + │ ├── flags: newline, static_literal │ └── numeric: │ @ IntegerNode (location: (65,0)-(65,3)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 └── @ ImaginaryNode (location: (67,0)-(67,5)) + ├── flags: newline, static_literal └── numeric: @ RationalNode (location: (67,0)-(67,4)) - ├── flags: binary + ├── flags: static_literal, binary ├── numerator: 1 └── denominator: 1 diff --git a/test/prism/snapshots/patterns.txt b/test/prism/snapshots/patterns.txt index 17aa23b4b9434e..72209345855a46 100644 --- a/test/prism/snapshots/patterns.txt +++ b/test/prism/snapshots/patterns.txt @@ -1,9 +1,12 @@ -@ ProgramNode (location: (1,0)-(217,5)) -├── locals: [:bar, :baz, :qux, :b, :a, :foo, :x] +@ ProgramNode (location: (1,0)-(220,31)) +├── flags: ∅ +├── locals: [:bar, :baz, :qux, :b, :a, :foo, :x, :_a] └── statements: - @ StatementsNode (location: (1,0)-(217,5)) - └── body: (length: 180) + @ StatementsNode (location: (1,0)-(220,31)) + ├── flags: ∅ + └── body: (length: 182) ├── @ MatchRequiredNode (location: (1,0)-(1,10)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (1,0)-(1,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -17,10 +20,12 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ LocalVariableTargetNode (location: (1,7)-(1,10)) + │ │ ├── flags: ∅ │ │ ├── name: :bar │ │ └── depth: 0 │ └── operator_loc: (1,4)-(1,6) = "=>" ├── @ MatchRequiredNode (location: (2,0)-(2,8)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (2,0)-(2,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -34,10 +39,11 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ IntegerNode (location: (2,7)-(2,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (2,4)-(2,6) = "=>" ├── @ MatchRequiredNode (location: (3,0)-(3,10)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (3,0)-(3,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -51,9 +57,11 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ FloatNode (location: (3,7)-(3,10)) + │ │ ├── flags: static_literal │ │ └── value: 1.0 │ └── operator_loc: (3,4)-(3,6) = "=>" ├── @ MatchRequiredNode (location: (4,0)-(4,9)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (4,0)-(4,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -67,12 +75,14 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ImaginaryNode (location: (4,7)-(4,9)) + │ │ ├── flags: static_literal │ │ └── numeric: │ │ @ IntegerNode (location: (4,7)-(4,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (4,4)-(4,6) = "=>" ├── @ MatchRequiredNode (location: (5,0)-(5,9)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (5,0)-(5,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -86,11 +96,12 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ RationalNode (location: (5,7)-(5,9)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ ├── numerator: 1 │ │ └── denominator: 1 │ └── operator_loc: (5,4)-(5,6) = "=>" ├── @ MatchRequiredNode (location: (6,0)-(6,11)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (6,0)-(6,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -104,13 +115,14 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ SymbolNode (location: (6,7)-(6,11)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (6,7)-(6,8) = ":" │ │ ├── value_loc: (6,8)-(6,11) = "foo" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "foo" │ └── operator_loc: (6,4)-(6,6) = "=>" ├── @ MatchRequiredNode (location: (7,0)-(7,14)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (7,0)-(7,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -124,13 +136,14 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ SymbolNode (location: (7,7)-(7,14)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (7,7)-(7,10) = "%s[" │ │ ├── value_loc: (7,10)-(7,13) = "foo" │ │ ├── closing_loc: (7,13)-(7,14) = "]" │ │ └── unescaped: "foo" │ └── operator_loc: (7,4)-(7,6) = "=>" ├── @ MatchRequiredNode (location: (8,0)-(8,13)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (8,0)-(8,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -144,13 +157,14 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ SymbolNode (location: (8,7)-(8,13)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (8,7)-(8,9) = ":\"" │ │ ├── value_loc: (8,9)-(8,12) = "foo" │ │ ├── closing_loc: (8,12)-(8,13) = "\"" │ │ └── unescaped: "foo" │ └── operator_loc: (8,4)-(8,6) = "=>" ├── @ MatchRequiredNode (location: (9,0)-(9,12)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (9,0)-(9,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -164,13 +178,14 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ RegularExpressionNode (location: (9,7)-(9,12)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (9,7)-(9,8) = "/" │ │ ├── content_loc: (9,8)-(9,11) = "foo" │ │ ├── closing_loc: (9,11)-(9,12) = "/" │ │ └── unescaped: "foo" │ └── operator_loc: (9,4)-(9,6) = "=>" ├── @ MatchRequiredNode (location: (10,0)-(10,12)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (10,0)-(10,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -191,6 +206,7 @@ │ │ └── unescaped: "foo" │ └── operator_loc: (10,4)-(10,6) = "=>" ├── @ MatchRequiredNode (location: (11,0)-(11,14)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (11,0)-(11,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -211,6 +227,7 @@ │ │ └── unescaped: "foo" │ └── operator_loc: (11,4)-(11,6) = "=>" ├── @ MatchRequiredNode (location: (12,0)-(12,14)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (12,0)-(12,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -224,10 +241,10 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayNode (location: (12,7)-(12,14)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── elements: (length: 1) │ │ │ └── @ SymbolNode (location: (12,10)-(12,13)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (12,10)-(12,13) = "foo" │ │ │ ├── closing_loc: ∅ @@ -236,6 +253,7 @@ │ │ └── closing_loc: (12,13)-(12,14) = "]" │ └── operator_loc: (12,4)-(12,6) = "=>" ├── @ MatchRequiredNode (location: (13,0)-(13,14)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (13,0)-(13,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -249,10 +267,10 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayNode (location: (13,7)-(13,14)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── elements: (length: 1) │ │ │ └── @ SymbolNode (location: (13,10)-(13,13)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (13,10)-(13,13) = "foo" │ │ │ ├── closing_loc: ∅ @@ -261,6 +279,7 @@ │ │ └── closing_loc: (13,13)-(13,14) = "]" │ └── operator_loc: (13,4)-(13,6) = "=>" ├── @ MatchRequiredNode (location: (14,0)-(14,14)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (14,0)-(14,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -286,6 +305,7 @@ │ │ └── closing_loc: (14,13)-(14,14) = "]" │ └── operator_loc: (14,4)-(14,6) = "=>" ├── @ MatchRequiredNode (location: (15,0)-(15,14)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (15,0)-(15,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -311,6 +331,7 @@ │ │ └── closing_loc: (15,13)-(15,14) = "]" │ └── operator_loc: (15,4)-(15,6) = "=>" ├── @ MatchRequiredNode (location: (16,0)-(16,14)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (16,0)-(16,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -331,6 +352,7 @@ │ │ └── unescaped: "foo" │ └── operator_loc: (16,4)-(16,6) = "=>" ├── @ MatchRequiredNode (location: (17,0)-(17,14)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (17,0)-(17,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -351,6 +373,7 @@ │ │ └── unescaped: "foo" │ └── operator_loc: (17,4)-(17,6) = "=>" ├── @ MatchRequiredNode (location: (18,0)-(18,12)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (18,0)-(18,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -371,6 +394,7 @@ │ │ └── unescaped: "foo" │ └── operator_loc: (18,4)-(18,6) = "=>" ├── @ MatchRequiredNode (location: (19,0)-(19,10)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (19,0)-(19,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -384,8 +408,10 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ NilNode (location: (19,7)-(19,10)) + │ │ └── flags: static_literal │ └── operator_loc: (19,4)-(19,6) = "=>" ├── @ MatchRequiredNode (location: (20,0)-(20,11)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (20,0)-(20,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -399,8 +425,10 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ SelfNode (location: (20,7)-(20,11)) + │ │ └── flags: ∅ │ └── operator_loc: (20,4)-(20,6) = "=>" ├── @ MatchRequiredNode (location: (21,0)-(21,11)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (21,0)-(21,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -414,8 +442,10 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ TrueNode (location: (21,7)-(21,11)) + │ │ └── flags: static_literal │ └── operator_loc: (21,4)-(21,6) = "=>" ├── @ MatchRequiredNode (location: (22,0)-(22,12)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (22,0)-(22,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -429,8 +459,10 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ FalseNode (location: (22,7)-(22,12)) + │ │ └── flags: static_literal │ └── operator_loc: (22,4)-(22,6) = "=>" ├── @ MatchRequiredNode (location: (23,0)-(23,15)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (23,0)-(23,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -448,6 +480,7 @@ │ │ └── filepath: "patterns.txt" │ └── operator_loc: (23,4)-(23,6) = "=>" ├── @ MatchRequiredNode (location: (24,0)-(24,15)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (24,0)-(24,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -461,8 +494,10 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ SourceLineNode (location: (24,7)-(24,15)) + │ │ └── flags: static_literal │ └── operator_loc: (24,4)-(24,6) = "=>" ├── @ MatchRequiredNode (location: (25,0)-(25,19)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (25,0)-(25,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -476,8 +511,10 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ SourceEncodingNode (location: (25,7)-(25,19)) + │ │ └── flags: static_literal │ └── operator_loc: (25,4)-(25,6) = "=>" ├── @ MatchRequiredNode (location: (26,0)-(26,17)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (26,0)-(26,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -491,6 +528,7 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ LambdaNode (location: (26,7)-(26,17)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── operator_loc: (26,7)-(26,9) = "->" │ │ ├── opening_loc: (26,10)-(26,11) = "{" @@ -498,12 +536,15 @@ │ │ ├── parameters: ∅ │ │ └── body: │ │ @ StatementsNode (location: (26,12)-(26,15)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (26,12)-(26,15)) + │ │ ├── flags: newline │ │ ├── name: :bar │ │ └── depth: 1 │ └── operator_loc: (26,4)-(26,6) = "=>" ├── @ MatchRequiredNode (location: (28,0)-(28,13)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (28,0)-(28,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -517,18 +558,19 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ RangeNode (location: (28,7)-(28,13)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (28,7)-(28,8)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: │ │ │ @ IntegerNode (location: (28,12)-(28,13)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── operator_loc: (28,9)-(28,11) = ".." │ └── operator_loc: (28,4)-(28,6) = "=>" ├── @ MatchRequiredNode (location: (29,0)-(29,17)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (29,0)-(29,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -545,13 +587,16 @@ │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ FloatNode (location: (29,7)-(29,10)) + │ │ │ ├── flags: static_literal │ │ │ └── value: 1.0 │ │ ├── right: │ │ │ @ FloatNode (location: (29,14)-(29,17)) + │ │ │ ├── flags: static_literal │ │ │ └── value: 1.0 │ │ └── operator_loc: (29,11)-(29,13) = ".." │ └── operator_loc: (29,4)-(29,6) = "=>" ├── @ MatchRequiredNode (location: (30,0)-(30,15)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (30,0)-(30,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -568,19 +613,22 @@ │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ ImaginaryNode (location: (30,7)-(30,9)) + │ │ │ ├── flags: static_literal │ │ │ └── numeric: │ │ │ @ IntegerNode (location: (30,7)-(30,8)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: │ │ │ @ ImaginaryNode (location: (30,13)-(30,15)) + │ │ │ ├── flags: static_literal │ │ │ └── numeric: │ │ │ @ IntegerNode (location: (30,13)-(30,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── operator_loc: (30,10)-(30,12) = ".." │ └── operator_loc: (30,4)-(30,6) = "=>" ├── @ MatchRequiredNode (location: (31,0)-(31,15)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (31,0)-(31,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -597,17 +645,18 @@ │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ RationalNode (location: (31,7)-(31,9)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ ├── numerator: 1 │ │ │ └── denominator: 1 │ │ ├── right: │ │ │ @ RationalNode (location: (31,13)-(31,15)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ ├── numerator: 1 │ │ │ └── denominator: 1 │ │ └── operator_loc: (31,10)-(31,12) = ".." │ └── operator_loc: (31,4)-(31,6) = "=>" ├── @ MatchRequiredNode (location: (32,0)-(32,19)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (32,0)-(32,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -624,14 +673,14 @@ │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ SymbolNode (location: (32,7)-(32,11)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (32,7)-(32,8) = ":" │ │ │ ├── value_loc: (32,8)-(32,11) = "foo" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "foo" │ │ ├── right: │ │ │ @ SymbolNode (location: (32,15)-(32,19)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (32,15)-(32,16) = ":" │ │ │ ├── value_loc: (32,16)-(32,19) = "foo" │ │ │ ├── closing_loc: ∅ @@ -639,6 +688,7 @@ │ │ └── operator_loc: (32,12)-(32,14) = ".." │ └── operator_loc: (32,4)-(32,6) = "=>" ├── @ MatchRequiredNode (location: (33,0)-(33,25)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (33,0)-(33,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -655,14 +705,14 @@ │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ SymbolNode (location: (33,7)-(33,14)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (33,7)-(33,10) = "%s[" │ │ │ ├── value_loc: (33,10)-(33,13) = "foo" │ │ │ ├── closing_loc: (33,13)-(33,14) = "]" │ │ │ └── unescaped: "foo" │ │ ├── right: │ │ │ @ SymbolNode (location: (33,18)-(33,25)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (33,18)-(33,21) = "%s[" │ │ │ ├── value_loc: (33,21)-(33,24) = "foo" │ │ │ ├── closing_loc: (33,24)-(33,25) = "]" @@ -670,6 +720,7 @@ │ │ └── operator_loc: (33,15)-(33,17) = ".." │ └── operator_loc: (33,4)-(33,6) = "=>" ├── @ MatchRequiredNode (location: (34,0)-(34,23)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (34,0)-(34,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -686,14 +737,14 @@ │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ SymbolNode (location: (34,7)-(34,13)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (34,7)-(34,9) = ":\"" │ │ │ ├── value_loc: (34,9)-(34,12) = "foo" │ │ │ ├── closing_loc: (34,12)-(34,13) = "\"" │ │ │ └── unescaped: "foo" │ │ ├── right: │ │ │ @ SymbolNode (location: (34,17)-(34,23)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (34,17)-(34,19) = ":\"" │ │ │ ├── value_loc: (34,19)-(34,22) = "foo" │ │ │ ├── closing_loc: (34,22)-(34,23) = "\"" @@ -701,6 +752,7 @@ │ │ └── operator_loc: (34,14)-(34,16) = ".." │ └── operator_loc: (34,4)-(34,6) = "=>" ├── @ MatchRequiredNode (location: (35,0)-(35,21)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (35,0)-(35,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -717,14 +769,14 @@ │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ RegularExpressionNode (location: (35,7)-(35,12)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (35,7)-(35,8) = "/" │ │ │ ├── content_loc: (35,8)-(35,11) = "foo" │ │ │ ├── closing_loc: (35,11)-(35,12) = "/" │ │ │ └── unescaped: "foo" │ │ ├── right: │ │ │ @ RegularExpressionNode (location: (35,16)-(35,21)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (35,16)-(35,17) = "/" │ │ │ ├── content_loc: (35,17)-(35,20) = "foo" │ │ │ ├── closing_loc: (35,20)-(35,21) = "/" @@ -732,6 +784,7 @@ │ │ └── operator_loc: (35,13)-(35,15) = ".." │ └── operator_loc: (35,4)-(35,6) = "=>" ├── @ MatchRequiredNode (location: (36,0)-(36,21)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (36,0)-(36,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -763,6 +816,7 @@ │ │ └── operator_loc: (36,13)-(36,15) = ".." │ └── operator_loc: (36,4)-(36,6) = "=>" ├── @ MatchRequiredNode (location: (37,0)-(37,25)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (37,0)-(37,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -794,6 +848,7 @@ │ │ └── operator_loc: (37,15)-(37,17) = ".." │ └── operator_loc: (37,4)-(37,6) = "=>" ├── @ MatchRequiredNode (location: (38,0)-(38,25)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (38,0)-(38,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -810,10 +865,10 @@ │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ ArrayNode (location: (38,7)-(38,14)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: static_literal │ │ │ ├── elements: (length: 1) │ │ │ │ └── @ SymbolNode (location: (38,10)-(38,13)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (38,10)-(38,13) = "foo" │ │ │ │ ├── closing_loc: ∅ @@ -822,10 +877,10 @@ │ │ │ └── closing_loc: (38,13)-(38,14) = "]" │ │ ├── right: │ │ │ @ ArrayNode (location: (38,18)-(38,25)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: static_literal │ │ │ ├── elements: (length: 1) │ │ │ │ └── @ SymbolNode (location: (38,21)-(38,24)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (38,21)-(38,24) = "foo" │ │ │ │ ├── closing_loc: ∅ @@ -835,6 +890,7 @@ │ │ └── operator_loc: (38,15)-(38,17) = ".." │ └── operator_loc: (38,4)-(38,6) = "=>" ├── @ MatchRequiredNode (location: (39,0)-(39,25)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (39,0)-(39,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -851,10 +907,10 @@ │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ ArrayNode (location: (39,7)-(39,14)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: static_literal │ │ │ ├── elements: (length: 1) │ │ │ │ └── @ SymbolNode (location: (39,10)-(39,13)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (39,10)-(39,13) = "foo" │ │ │ │ ├── closing_loc: ∅ @@ -863,10 +919,10 @@ │ │ │ └── closing_loc: (39,13)-(39,14) = "]" │ │ ├── right: │ │ │ @ ArrayNode (location: (39,18)-(39,25)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: static_literal │ │ │ ├── elements: (length: 1) │ │ │ │ └── @ SymbolNode (location: (39,21)-(39,24)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (39,21)-(39,24) = "foo" │ │ │ │ ├── closing_loc: ∅ @@ -876,6 +932,7 @@ │ │ └── operator_loc: (39,15)-(39,17) = ".." │ └── operator_loc: (39,4)-(39,6) = "=>" ├── @ MatchRequiredNode (location: (40,0)-(40,25)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (40,0)-(40,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -917,6 +974,7 @@ │ │ └── operator_loc: (40,15)-(40,17) = ".." │ └── operator_loc: (40,4)-(40,6) = "=>" ├── @ MatchRequiredNode (location: (41,0)-(41,25)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (41,0)-(41,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -958,6 +1016,7 @@ │ │ └── operator_loc: (41,15)-(41,17) = ".." │ └── operator_loc: (41,4)-(41,6) = "=>" ├── @ MatchRequiredNode (location: (42,0)-(42,25)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (42,0)-(42,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -989,6 +1048,7 @@ │ │ └── operator_loc: (42,15)-(42,17) = ".." │ └── operator_loc: (42,4)-(42,6) = "=>" ├── @ MatchRequiredNode (location: (43,0)-(43,25)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (43,0)-(43,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1020,6 +1080,7 @@ │ │ └── operator_loc: (43,15)-(43,17) = ".." │ └── operator_loc: (43,4)-(43,6) = "=>" ├── @ MatchRequiredNode (location: (44,0)-(44,21)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (44,0)-(44,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1051,6 +1112,7 @@ │ │ └── operator_loc: (44,13)-(44,15) = ".." │ └── operator_loc: (44,4)-(44,6) = "=>" ├── @ MatchRequiredNode (location: (45,0)-(45,17)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (45,0)-(45,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1064,14 +1126,17 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ RangeNode (location: (45,7)-(45,17)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ NilNode (location: (45,7)-(45,10)) + │ │ │ └── flags: static_literal │ │ ├── right: │ │ │ @ NilNode (location: (45,14)-(45,17)) + │ │ │ └── flags: static_literal │ │ └── operator_loc: (45,11)-(45,13) = ".." │ └── operator_loc: (45,4)-(45,6) = "=>" ├── @ MatchRequiredNode (location: (46,0)-(46,19)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (46,0)-(46,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1088,11 +1153,14 @@ │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ SelfNode (location: (46,7)-(46,11)) + │ │ │ └── flags: ∅ │ │ ├── right: │ │ │ @ SelfNode (location: (46,15)-(46,19)) + │ │ │ └── flags: ∅ │ │ └── operator_loc: (46,12)-(46,14) = ".." │ └── operator_loc: (46,4)-(46,6) = "=>" ├── @ MatchRequiredNode (location: (47,0)-(47,19)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (47,0)-(47,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1109,11 +1177,14 @@ │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ TrueNode (location: (47,7)-(47,11)) + │ │ │ └── flags: static_literal │ │ ├── right: │ │ │ @ TrueNode (location: (47,15)-(47,19)) + │ │ │ └── flags: static_literal │ │ └── operator_loc: (47,12)-(47,14) = ".." │ └── operator_loc: (47,4)-(47,6) = "=>" ├── @ MatchRequiredNode (location: (48,0)-(48,21)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (48,0)-(48,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1130,11 +1201,14 @@ │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ FalseNode (location: (48,7)-(48,12)) + │ │ │ └── flags: static_literal │ │ ├── right: │ │ │ @ FalseNode (location: (48,16)-(48,21)) + │ │ │ └── flags: static_literal │ │ └── operator_loc: (48,13)-(48,15) = ".." │ └── operator_loc: (48,4)-(48,6) = "=>" ├── @ MatchRequiredNode (location: (49,0)-(49,27)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (49,0)-(49,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1160,6 +1234,7 @@ │ │ └── operator_loc: (49,16)-(49,18) = ".." │ └── operator_loc: (49,4)-(49,6) = "=>" ├── @ MatchRequiredNode (location: (50,0)-(50,27)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (50,0)-(50,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1176,11 +1251,14 @@ │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ SourceLineNode (location: (50,7)-(50,15)) + │ │ │ └── flags: static_literal │ │ ├── right: │ │ │ @ SourceLineNode (location: (50,19)-(50,27)) + │ │ │ └── flags: static_literal │ │ └── operator_loc: (50,16)-(50,18) = ".." │ └── operator_loc: (50,4)-(50,6) = "=>" ├── @ MatchRequiredNode (location: (51,0)-(51,35)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (51,0)-(51,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1197,11 +1275,14 @@ │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ SourceEncodingNode (location: (51,7)-(51,19)) + │ │ │ └── flags: static_literal │ │ ├── right: │ │ │ @ SourceEncodingNode (location: (51,23)-(51,35)) + │ │ │ └── flags: static_literal │ │ └── operator_loc: (51,20)-(51,22) = ".." │ └── operator_loc: (51,4)-(51,6) = "=>" ├── @ MatchRequiredNode (location: (52,0)-(52,31)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (52,0)-(52,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1218,6 +1299,7 @@ │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ LambdaNode (location: (52,7)-(52,17)) + │ │ │ ├── flags: ∅ │ │ │ ├── locals: [] │ │ │ ├── operator_loc: (52,7)-(52,9) = "->" │ │ │ ├── opening_loc: (52,10)-(52,11) = "{" @@ -1225,12 +1307,15 @@ │ │ │ ├── parameters: ∅ │ │ │ └── body: │ │ │ @ StatementsNode (location: (52,12)-(52,15)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ LocalVariableReadNode (location: (52,12)-(52,15)) + │ │ │ ├── flags: newline │ │ │ ├── name: :bar │ │ │ └── depth: 1 │ │ ├── right: │ │ │ @ LambdaNode (location: (52,21)-(52,31)) + │ │ │ ├── flags: ∅ │ │ │ ├── locals: [] │ │ │ ├── operator_loc: (52,21)-(52,23) = "->" │ │ │ ├── opening_loc: (52,24)-(52,25) = "{" @@ -1238,22 +1323,26 @@ │ │ │ ├── parameters: ∅ │ │ │ └── body: │ │ │ @ StatementsNode (location: (52,26)-(52,29)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ LocalVariableReadNode (location: (52,26)-(52,29)) + │ │ │ ├── flags: newline │ │ │ ├── name: :bar │ │ │ └── depth: 1 │ │ └── operator_loc: (52,18)-(52,20) = ".." │ └── operator_loc: (52,4)-(52,6) = "=>" ├── @ LocalVariableWriteNode (location: (54,0)-(54,7)) + │ ├── flags: newline │ ├── name: :bar │ ├── depth: 0 │ ├── name_loc: (54,0)-(54,3) = "bar" │ ├── value: │ │ @ IntegerNode (location: (54,6)-(54,7)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (54,4)-(54,5) = "=" ├── @ MatchRequiredNode (location: (54,9)-(54,20)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (54,9)-(54,12)) │ │ ├── flags: variable_call, ignore_visibility @@ -1267,13 +1356,16 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ PinnedVariableNode (location: (54,16)-(54,20)) + │ │ ├── flags: ∅ │ │ ├── variable: │ │ │ @ LocalVariableReadNode (location: (54,17)-(54,20)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ └── operator_loc: (54,16)-(54,17) = "^" │ └── operator_loc: (54,13)-(54,15) = "=>" ├── @ MatchRequiredNode (location: (55,0)-(55,12)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (55,0)-(55,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1287,12 +1379,15 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ PinnedVariableNode (location: (55,7)-(55,12)) + │ │ ├── flags: ∅ │ │ ├── variable: │ │ │ @ InstanceVariableReadNode (location: (55,8)-(55,12)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :@bar │ │ └── operator_loc: (55,7)-(55,8) = "^" │ └── operator_loc: (55,4)-(55,6) = "=>" ├── @ MatchRequiredNode (location: (56,0)-(56,13)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (56,0)-(56,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1306,12 +1401,15 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ PinnedVariableNode (location: (56,7)-(56,13)) + │ │ ├── flags: ∅ │ │ ├── variable: │ │ │ @ ClassVariableReadNode (location: (56,8)-(56,13)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :@@bar │ │ └── operator_loc: (56,7)-(56,8) = "^" │ └── operator_loc: (56,4)-(56,6) = "=>" ├── @ MatchRequiredNode (location: (57,0)-(57,12)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (57,0)-(57,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1325,12 +1423,15 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ PinnedVariableNode (location: (57,7)-(57,12)) + │ │ ├── flags: ∅ │ │ ├── variable: │ │ │ @ GlobalVariableReadNode (location: (57,8)-(57,12)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :$bar │ │ └── operator_loc: (57,7)-(57,8) = "^" │ └── operator_loc: (57,4)-(57,6) = "=>" ├── @ MatchRequiredNode (location: (59,0)-(59,11)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (59,0)-(59,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1344,15 +1445,17 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ PinnedExpressionNode (location: (59,7)-(59,11)) + │ │ ├── flags: ∅ │ │ ├── expression: │ │ │ @ IntegerNode (location: (59,9)-(59,10)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── operator_loc: (59,7)-(59,8) = "^" │ │ ├── lparen_loc: (59,8)-(59,9) = "(" │ │ └── rparen_loc: (59,10)-(59,11) = ")" │ └── operator_loc: (59,4)-(59,6) = "=>" ├── @ MatchRequiredNode (location: (60,0)-(60,13)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (60,0)-(60,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1366,13 +1469,16 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ PinnedExpressionNode (location: (60,7)-(60,13)) + │ │ ├── flags: ∅ │ │ ├── expression: │ │ │ @ NilNode (location: (60,9)-(60,12)) + │ │ │ └── flags: static_literal │ │ ├── operator_loc: (60,7)-(60,8) = "^" │ │ ├── lparen_loc: (60,8)-(60,9) = "(" │ │ └── rparen_loc: (60,12)-(60,13) = ")" │ └── operator_loc: (60,4)-(60,6) = "=>" ├── @ MatchRequiredNode (location: (61,0)-(61,23)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (61,0)-(61,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1386,6 +1492,7 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ PinnedExpressionNode (location: (61,7)-(61,23)) + │ │ ├── flags: ∅ │ │ ├── expression: │ │ │ @ CallNode (location: (61,9)-(61,22)) │ │ │ ├── flags: ∅ @@ -1417,6 +1524,7 @@ │ │ └── rparen_loc: (61,22)-(61,23) = ")" │ └── operator_loc: (61,4)-(61,6) = "=>" ├── @ MatchRequiredNode (location: (63,0)-(63,10)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (63,0)-(63,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1430,9 +1538,11 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ConstantReadNode (location: (63,7)-(63,10)) + │ │ ├── flags: ∅ │ │ └── name: :Foo │ └── operator_loc: (63,4)-(63,6) = "=>" ├── @ MatchRequiredNode (location: (64,0)-(64,20)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (64,0)-(64,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1446,10 +1556,13 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ConstantPathNode (location: (64,7)-(64,20)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ ConstantPathNode (location: (64,7)-(64,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── parent: │ │ │ │ @ ConstantReadNode (location: (64,7)-(64,10)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :Foo │ │ │ ├── name: :Bar │ │ │ ├── delimiter_loc: (64,10)-(64,12) = "::" @@ -1459,6 +1572,7 @@ │ │ └── name_loc: (64,17)-(64,20) = "Baz" │ └── operator_loc: (64,4)-(64,6) = "=>" ├── @ MatchRequiredNode (location: (65,0)-(65,12)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (65,0)-(65,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1472,12 +1586,14 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ConstantPathNode (location: (65,7)-(65,12)) + │ │ ├── flags: ∅ │ │ ├── parent: ∅ │ │ ├── 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)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (66,0)-(66,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1491,10 +1607,13 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ConstantPathNode (location: (66,7)-(66,22)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ ConstantPathNode (location: (66,7)-(66,17)) + │ │ │ ├── flags: ∅ │ │ │ ├── parent: │ │ │ │ @ ConstantPathNode (location: (66,7)-(66,12)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── parent: ∅ │ │ │ │ ├── name: :Foo │ │ │ │ ├── delimiter_loc: (66,7)-(66,9) = "::" @@ -1507,6 +1626,7 @@ │ │ └── name_loc: (66,19)-(66,22) = "Baz" │ └── operator_loc: (66,4)-(66,6) = "=>" ├── @ MatchRequiredNode (location: (68,0)-(68,12)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (68,0)-(68,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1520,8 +1640,10 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (68,7)-(68,12)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (68,7)-(68,10)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Foo │ │ ├── requireds: (length: 0) │ │ ├── rest: ∅ @@ -1530,6 +1652,7 @@ │ │ └── closing_loc: (68,11)-(68,12) = ")" │ └── operator_loc: (68,4)-(68,6) = "=>" ├── @ MatchRequiredNode (location: (69,0)-(69,13)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (69,0)-(69,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1543,12 +1666,14 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (69,7)-(69,13)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (69,7)-(69,10)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Foo │ │ ├── requireds: (length: 1) │ │ │ └── @ IntegerNode (location: (69,11)-(69,12)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) @@ -1556,6 +1681,7 @@ │ │ └── closing_loc: (69,12)-(69,13) = ")" │ └── operator_loc: (69,4)-(69,6) = "=>" ├── @ MatchRequiredNode (location: (70,0)-(70,19)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (70,0)-(70,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1569,18 +1695,20 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (70,7)-(70,19)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (70,7)-(70,10)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Foo │ │ ├── requireds: (length: 3) │ │ │ ├── @ IntegerNode (location: (70,11)-(70,12)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── @ IntegerNode (location: (70,14)-(70,15)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ └── @ IntegerNode (location: (70,17)-(70,18)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 3 │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) @@ -1588,6 +1716,7 @@ │ │ └── closing_loc: (70,18)-(70,19) = ")" │ └── operator_loc: (70,4)-(70,6) = "=>" ├── @ MatchRequiredNode (location: (71,0)-(71,15)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (71,0)-(71,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1601,11 +1730,14 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (71,7)-(71,15)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (71,7)-(71,10)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Foo │ │ ├── requireds: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (71,11)-(71,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── rest: ∅ @@ -1614,6 +1746,7 @@ │ │ └── closing_loc: (71,14)-(71,15) = ")" │ └── operator_loc: (71,4)-(71,6) = "=>" ├── @ MatchRequiredNode (location: (72,0)-(72,21)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (72,0)-(72,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1627,25 +1760,31 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (72,7)-(72,21)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (72,7)-(72,10)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Foo │ │ ├── requireds: (length: 0) │ │ ├── rest: │ │ │ @ SplatNode (location: (72,11)-(72,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (72,11)-(72,12) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (72,12)-(72,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── posts: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (72,17)-(72,20)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :baz │ │ │ └── depth: 0 │ │ ├── opening_loc: (72,10)-(72,11) = "(" │ │ └── closing_loc: (72,20)-(72,21) = ")" │ └── operator_loc: (72,4)-(72,6) = "=>" ├── @ MatchRequiredNode (location: (73,0)-(73,21)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (73,0)-(73,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1659,18 +1798,23 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (73,7)-(73,21)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (73,7)-(73,10)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Foo │ │ ├── requireds: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (73,11)-(73,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── rest: │ │ │ @ SplatNode (location: (73,16)-(73,20)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (73,16)-(73,17) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (73,17)-(73,20)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :baz │ │ │ └── depth: 0 │ │ ├── posts: (length: 0) @@ -1678,6 +1822,7 @@ │ │ └── closing_loc: (73,20)-(73,21) = ")" │ └── operator_loc: (73,4)-(73,6) = "=>" ├── @ MatchRequiredNode (location: (74,0)-(74,27)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (74,0)-(74,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1691,31 +1836,39 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ FindPatternNode (location: (74,7)-(74,27)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (74,7)-(74,10)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Foo │ │ ├── left: │ │ │ @ SplatNode (location: (74,11)-(74,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (74,11)-(74,12) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (74,12)-(74,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── requireds: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (74,17)-(74,20)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :baz │ │ │ └── depth: 0 │ │ ├── right: │ │ │ @ SplatNode (location: (74,22)-(74,26)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (74,22)-(74,23) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (74,23)-(74,26)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :qux │ │ │ └── depth: 0 │ │ ├── opening_loc: (74,10)-(74,11) = "(" │ │ └── closing_loc: (74,26)-(74,27) = ")" │ └── operator_loc: (74,4)-(74,6) = "=>" ├── @ MatchRequiredNode (location: (76,0)-(76,12)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (76,0)-(76,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1729,8 +1882,10 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (76,7)-(76,12)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (76,7)-(76,10)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Foo │ │ ├── requireds: (length: 0) │ │ ├── rest: ∅ @@ -1739,6 +1894,7 @@ │ │ └── closing_loc: (76,11)-(76,12) = "]" │ └── operator_loc: (76,4)-(76,6) = "=>" ├── @ MatchRequiredNode (location: (77,0)-(77,13)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (77,0)-(77,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1752,12 +1908,14 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (77,7)-(77,13)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (77,7)-(77,10)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Foo │ │ ├── requireds: (length: 1) │ │ │ └── @ IntegerNode (location: (77,11)-(77,12)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) @@ -1765,6 +1923,7 @@ │ │ └── closing_loc: (77,12)-(77,13) = "]" │ └── operator_loc: (77,4)-(77,6) = "=>" ├── @ MatchRequiredNode (location: (78,0)-(78,19)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (78,0)-(78,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1778,18 +1937,20 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (78,7)-(78,19)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (78,7)-(78,10)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Foo │ │ ├── requireds: (length: 3) │ │ │ ├── @ IntegerNode (location: (78,11)-(78,12)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── @ IntegerNode (location: (78,14)-(78,15)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ └── @ IntegerNode (location: (78,17)-(78,18)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 3 │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) @@ -1797,6 +1958,7 @@ │ │ └── closing_loc: (78,18)-(78,19) = "]" │ └── operator_loc: (78,4)-(78,6) = "=>" ├── @ MatchRequiredNode (location: (79,0)-(79,17)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (79,0)-(79,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1810,13 +1972,17 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (79,7)-(79,17)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (79,7)-(79,10)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Foo │ │ ├── requireds: (length: 1) │ │ │ └── @ ArrayPatternNode (location: (79,11)-(79,16)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: │ │ │ │ @ ConstantReadNode (location: (79,11)-(79,14)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :Foo │ │ │ ├── requireds: (length: 0) │ │ │ ├── rest: ∅ @@ -1829,6 +1995,7 @@ │ │ └── closing_loc: (79,16)-(79,17) = "]" │ └── operator_loc: (79,4)-(79,6) = "=>" ├── @ MatchRequiredNode (location: (80,0)-(80,15)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (80,0)-(80,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1842,11 +2009,14 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (80,7)-(80,15)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (80,7)-(80,10)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Foo │ │ ├── requireds: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (80,11)-(80,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── rest: ∅ @@ -1855,6 +2025,7 @@ │ │ └── closing_loc: (80,14)-(80,15) = "]" │ └── operator_loc: (80,4)-(80,6) = "=>" ├── @ MatchRequiredNode (location: (81,0)-(81,21)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (81,0)-(81,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1868,25 +2039,31 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (81,7)-(81,21)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (81,7)-(81,10)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Foo │ │ ├── requireds: (length: 0) │ │ ├── rest: │ │ │ @ SplatNode (location: (81,11)-(81,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (81,11)-(81,12) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (81,12)-(81,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── posts: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (81,17)-(81,20)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :baz │ │ │ └── depth: 0 │ │ ├── opening_loc: (81,10)-(81,11) = "[" │ │ └── closing_loc: (81,20)-(81,21) = "]" │ └── operator_loc: (81,4)-(81,6) = "=>" ├── @ MatchRequiredNode (location: (82,0)-(82,21)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (82,0)-(82,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1900,18 +2077,23 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (82,7)-(82,21)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (82,7)-(82,10)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Foo │ │ ├── requireds: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (82,11)-(82,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── rest: │ │ │ @ SplatNode (location: (82,16)-(82,20)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (82,16)-(82,17) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (82,17)-(82,20)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :baz │ │ │ └── depth: 0 │ │ ├── posts: (length: 0) @@ -1919,6 +2101,7 @@ │ │ └── closing_loc: (82,20)-(82,21) = "]" │ └── operator_loc: (82,4)-(82,6) = "=>" ├── @ MatchRequiredNode (location: (83,0)-(83,27)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (83,0)-(83,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1932,31 +2115,39 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ FindPatternNode (location: (83,7)-(83,27)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (83,7)-(83,10)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Foo │ │ ├── left: │ │ │ @ SplatNode (location: (83,11)-(83,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (83,11)-(83,12) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (83,12)-(83,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── requireds: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (83,17)-(83,20)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :baz │ │ │ └── depth: 0 │ │ ├── right: │ │ │ @ SplatNode (location: (83,22)-(83,26)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (83,22)-(83,23) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (83,23)-(83,26)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :qux │ │ │ └── depth: 0 │ │ ├── opening_loc: (83,10)-(83,11) = "[" │ │ └── closing_loc: (83,26)-(83,27) = "]" │ └── operator_loc: (83,4)-(83,6) = "=>" ├── @ MatchRequiredNode (location: (85,0)-(85,11)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (85,0)-(85,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1970,13 +2161,16 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (85,7)-(85,11)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 0) │ │ ├── rest: │ │ │ @ SplatNode (location: (85,7)-(85,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (85,7)-(85,8) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (85,8)-(85,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── posts: (length: 0) @@ -1984,6 +2178,7 @@ │ │ └── closing_loc: ∅ │ └── operator_loc: (85,4)-(85,6) = "=>" ├── @ MatchRequiredNode (location: (86,0)-(86,21)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (86,0)-(86,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1997,26 +2192,32 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (86,7)-(86,21)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 0) │ │ ├── rest: │ │ │ @ SplatNode (location: (86,7)-(86,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (86,7)-(86,8) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (86,8)-(86,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── posts: (length: 2) │ │ │ ├── @ LocalVariableTargetNode (location: (86,13)-(86,16)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ └── @ LocalVariableTargetNode (location: (86,18)-(86,21)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :qux │ │ │ └── depth: 0 │ │ ├── opening_loc: ∅ │ │ └── closing_loc: ∅ │ └── operator_loc: (86,4)-(86,6) = "=>" ├── @ MatchRequiredNode (location: (87,0)-(87,21)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (87,0)-(87,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2030,26 +2231,32 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (87,7)-(87,21)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (87,7)-(87,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── rest: │ │ │ @ SplatNode (location: (87,12)-(87,16)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (87,12)-(87,13) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (87,13)-(87,16)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :baz │ │ │ └── depth: 0 │ │ ├── posts: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (87,18)-(87,21)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :qux │ │ │ └── depth: 0 │ │ ├── opening_loc: ∅ │ │ └── closing_loc: ∅ │ └── operator_loc: (87,4)-(87,6) = "=>" ├── @ MatchRequiredNode (location: (88,0)-(88,21)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (88,0)-(88,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2063,19 +2270,24 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (88,7)-(88,21)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 2) │ │ │ ├── @ LocalVariableTargetNode (location: (88,7)-(88,10)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :bar │ │ │ │ └── depth: 0 │ │ │ └── @ LocalVariableTargetNode (location: (88,12)-(88,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :baz │ │ │ └── depth: 0 │ │ ├── rest: │ │ │ @ SplatNode (location: (88,17)-(88,21)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (88,17)-(88,18) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (88,18)-(88,21)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :qux │ │ │ └── depth: 0 │ │ ├── posts: (length: 0) @@ -2083,6 +2295,7 @@ │ │ └── closing_loc: ∅ │ └── operator_loc: (88,4)-(88,6) = "=>" ├── @ MatchRequiredNode (location: (89,0)-(89,22)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (89,0)-(89,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2096,29 +2309,36 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ FindPatternNode (location: (89,7)-(89,22)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── left: │ │ │ @ SplatNode (location: (89,7)-(89,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (89,7)-(89,8) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (89,8)-(89,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── requireds: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (89,13)-(89,16)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :baz │ │ │ └── depth: 0 │ │ ├── right: │ │ │ @ SplatNode (location: (89,18)-(89,22)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (89,18)-(89,19) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (89,19)-(89,22)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :qux │ │ │ └── depth: 0 │ │ ├── opening_loc: ∅ │ │ └── closing_loc: ∅ │ └── operator_loc: (89,4)-(89,6) = "=>" ├── @ MatchRequiredNode (location: (91,0)-(91,11)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (91,0)-(91,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2132,18 +2352,22 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (91,7)-(91,11)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (91,7)-(91,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── rest: │ │ │ @ ImplicitRestNode (location: (91,10)-(91,11)) + │ │ │ └── flags: ∅ │ │ ├── posts: (length: 0) │ │ ├── opening_loc: ∅ │ │ └── closing_loc: ∅ │ └── operator_loc: (91,4)-(91,6) = "=>" ├── @ MatchRequiredNode (location: (95,0)-(95,9)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (95,0)-(95,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2157,6 +2381,7 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (95,7)-(95,9)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 0) │ │ ├── rest: ∅ @@ -2165,6 +2390,7 @@ │ │ └── closing_loc: (95,8)-(95,9) = "]" │ └── operator_loc: (95,4)-(95,6) = "=>" ├── @ MatchRequiredNode (location: (96,0)-(96,17)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (96,0)-(96,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2178,18 +2404,23 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (96,7)-(96,17)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ ArrayPatternNode (location: (96,8)-(96,16)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ ArrayPatternNode (location: (96,9)-(96,15)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── constant: ∅ │ │ │ │ ├── requireds: (length: 1) │ │ │ │ │ └── @ ArrayPatternNode (location: (96,10)-(96,14)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── constant: ∅ │ │ │ │ │ ├── requireds: (length: 1) │ │ │ │ │ │ └── @ ArrayPatternNode (location: (96,11)-(96,13)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ ├── constant: ∅ │ │ │ │ │ │ ├── requireds: (length: 0) │ │ │ │ │ │ ├── rest: ∅ @@ -2214,6 +2445,7 @@ │ │ └── closing_loc: (96,16)-(96,17) = "]" │ └── operator_loc: (96,4)-(96,6) = "=>" ├── @ MatchRequiredNode (location: (98,0)-(98,13)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (98,0)-(98,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2227,13 +2459,16 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (98,7)-(98,13)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 0) │ │ ├── rest: │ │ │ @ SplatNode (location: (98,8)-(98,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (98,8)-(98,9) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (98,9)-(98,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── posts: (length: 0) @@ -2241,6 +2476,7 @@ │ │ └── closing_loc: (98,12)-(98,13) = "]" │ └── operator_loc: (98,4)-(98,6) = "=>" ├── @ MatchRequiredNode (location: (99,0)-(99,23)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (99,0)-(99,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2254,26 +2490,32 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (99,7)-(99,23)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 0) │ │ ├── rest: │ │ │ @ SplatNode (location: (99,8)-(99,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (99,8)-(99,9) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (99,9)-(99,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── posts: (length: 2) │ │ │ ├── @ LocalVariableTargetNode (location: (99,14)-(99,17)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ └── @ LocalVariableTargetNode (location: (99,19)-(99,22)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :qux │ │ │ └── depth: 0 │ │ ├── opening_loc: (99,7)-(99,8) = "[" │ │ └── closing_loc: (99,22)-(99,23) = "]" │ └── operator_loc: (99,4)-(99,6) = "=>" ├── @ MatchRequiredNode (location: (100,0)-(100,23)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (100,0)-(100,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2287,26 +2529,32 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (100,7)-(100,23)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (100,8)-(100,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── rest: │ │ │ @ SplatNode (location: (100,13)-(100,17)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (100,13)-(100,14) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (100,14)-(100,17)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :baz │ │ │ └── depth: 0 │ │ ├── posts: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (100,19)-(100,22)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :qux │ │ │ └── depth: 0 │ │ ├── opening_loc: (100,7)-(100,8) = "[" │ │ └── closing_loc: (100,22)-(100,23) = "]" │ └── operator_loc: (100,4)-(100,6) = "=>" ├── @ MatchRequiredNode (location: (101,0)-(101,23)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (101,0)-(101,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2320,19 +2568,24 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (101,7)-(101,23)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 2) │ │ │ ├── @ LocalVariableTargetNode (location: (101,8)-(101,11)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :bar │ │ │ │ └── depth: 0 │ │ │ └── @ LocalVariableTargetNode (location: (101,13)-(101,16)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :baz │ │ │ └── depth: 0 │ │ ├── rest: │ │ │ @ SplatNode (location: (101,18)-(101,22)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (101,18)-(101,19) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (101,19)-(101,22)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :qux │ │ │ └── depth: 0 │ │ ├── posts: (length: 0) @@ -2340,6 +2593,7 @@ │ │ └── closing_loc: (101,22)-(101,23) = "]" │ └── operator_loc: (101,4)-(101,6) = "=>" ├── @ MatchRequiredNode (location: (102,0)-(102,24)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (102,0)-(102,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2353,29 +2607,36 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ FindPatternNode (location: (102,7)-(102,24)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── left: │ │ │ @ SplatNode (location: (102,8)-(102,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (102,8)-(102,9) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (102,9)-(102,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── requireds: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (102,14)-(102,17)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :baz │ │ │ └── depth: 0 │ │ ├── right: │ │ │ @ SplatNode (location: (102,19)-(102,23)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (102,19)-(102,20) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (102,20)-(102,23)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :qux │ │ │ └── depth: 0 │ │ ├── opening_loc: (102,7)-(102,8) = "[" │ │ └── closing_loc: (102,23)-(102,24) = "]" │ └── operator_loc: (102,4)-(102,6) = "=>" ├── @ MatchPredicateNode (location: (104,0)-(104,10)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (104,0)-(104,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2389,10 +2650,12 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ LocalVariableTargetNode (location: (104,7)-(104,10)) + │ │ ├── flags: ∅ │ │ ├── name: :bar │ │ └── depth: 0 │ └── operator_loc: (104,4)-(104,6) = "in" ├── @ MatchPredicateNode (location: (105,0)-(105,8)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (105,0)-(105,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2406,10 +2669,11 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ IntegerNode (location: (105,7)-(105,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (105,4)-(105,6) = "in" ├── @ MatchPredicateNode (location: (106,0)-(106,10)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (106,0)-(106,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2423,9 +2687,11 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ FloatNode (location: (106,7)-(106,10)) + │ │ ├── flags: static_literal │ │ └── value: 1.0 │ └── operator_loc: (106,4)-(106,6) = "in" ├── @ MatchPredicateNode (location: (107,0)-(107,9)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (107,0)-(107,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2439,12 +2705,14 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ImaginaryNode (location: (107,7)-(107,9)) + │ │ ├── flags: static_literal │ │ └── numeric: │ │ @ IntegerNode (location: (107,7)-(107,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (107,4)-(107,6) = "in" ├── @ MatchPredicateNode (location: (108,0)-(108,9)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (108,0)-(108,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2458,11 +2726,12 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ RationalNode (location: (108,7)-(108,9)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ ├── numerator: 1 │ │ └── denominator: 1 │ └── operator_loc: (108,4)-(108,6) = "in" ├── @ MatchPredicateNode (location: (109,0)-(109,11)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (109,0)-(109,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2476,13 +2745,14 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ SymbolNode (location: (109,7)-(109,11)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (109,7)-(109,8) = ":" │ │ ├── value_loc: (109,8)-(109,11) = "foo" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "foo" │ └── operator_loc: (109,4)-(109,6) = "in" ├── @ MatchPredicateNode (location: (110,0)-(110,14)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (110,0)-(110,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2496,13 +2766,14 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ SymbolNode (location: (110,7)-(110,14)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (110,7)-(110,10) = "%s[" │ │ ├── value_loc: (110,10)-(110,13) = "foo" │ │ ├── closing_loc: (110,13)-(110,14) = "]" │ │ └── unescaped: "foo" │ └── operator_loc: (110,4)-(110,6) = "in" ├── @ MatchPredicateNode (location: (111,0)-(111,13)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (111,0)-(111,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2516,13 +2787,14 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ SymbolNode (location: (111,7)-(111,13)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (111,7)-(111,9) = ":\"" │ │ ├── value_loc: (111,9)-(111,12) = "foo" │ │ ├── closing_loc: (111,12)-(111,13) = "\"" │ │ └── unescaped: "foo" │ └── operator_loc: (111,4)-(111,6) = "in" ├── @ MatchPredicateNode (location: (112,0)-(112,12)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (112,0)-(112,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2536,13 +2808,14 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ RegularExpressionNode (location: (112,7)-(112,12)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (112,7)-(112,8) = "/" │ │ ├── content_loc: (112,8)-(112,11) = "foo" │ │ ├── closing_loc: (112,11)-(112,12) = "/" │ │ └── unescaped: "foo" │ └── operator_loc: (112,4)-(112,6) = "in" ├── @ MatchPredicateNode (location: (113,0)-(113,12)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (113,0)-(113,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2563,6 +2836,7 @@ │ │ └── unescaped: "foo" │ └── operator_loc: (113,4)-(113,6) = "in" ├── @ MatchPredicateNode (location: (114,0)-(114,14)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (114,0)-(114,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2583,6 +2857,7 @@ │ │ └── unescaped: "foo" │ └── operator_loc: (114,4)-(114,6) = "in" ├── @ MatchPredicateNode (location: (115,0)-(115,14)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (115,0)-(115,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2596,10 +2871,10 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayNode (location: (115,7)-(115,14)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── elements: (length: 1) │ │ │ └── @ SymbolNode (location: (115,10)-(115,13)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (115,10)-(115,13) = "foo" │ │ │ ├── closing_loc: ∅ @@ -2608,6 +2883,7 @@ │ │ └── closing_loc: (115,13)-(115,14) = "]" │ └── operator_loc: (115,4)-(115,6) = "in" ├── @ MatchPredicateNode (location: (116,0)-(116,14)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (116,0)-(116,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2621,10 +2897,10 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayNode (location: (116,7)-(116,14)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── elements: (length: 1) │ │ │ └── @ SymbolNode (location: (116,10)-(116,13)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (116,10)-(116,13) = "foo" │ │ │ ├── closing_loc: ∅ @@ -2633,6 +2909,7 @@ │ │ └── closing_loc: (116,13)-(116,14) = "]" │ └── operator_loc: (116,4)-(116,6) = "in" ├── @ MatchPredicateNode (location: (117,0)-(117,14)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (117,0)-(117,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2658,6 +2935,7 @@ │ │ └── closing_loc: (117,13)-(117,14) = "]" │ └── operator_loc: (117,4)-(117,6) = "in" ├── @ MatchPredicateNode (location: (118,0)-(118,14)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (118,0)-(118,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2683,6 +2961,7 @@ │ │ └── closing_loc: (118,13)-(118,14) = "]" │ └── operator_loc: (118,4)-(118,6) = "in" ├── @ MatchPredicateNode (location: (119,0)-(119,14)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (119,0)-(119,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2703,6 +2982,7 @@ │ │ └── unescaped: "foo" │ └── operator_loc: (119,4)-(119,6) = "in" ├── @ MatchPredicateNode (location: (120,0)-(120,14)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (120,0)-(120,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2723,6 +3003,7 @@ │ │ └── unescaped: "foo" │ └── operator_loc: (120,4)-(120,6) = "in" ├── @ MatchPredicateNode (location: (121,0)-(121,12)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (121,0)-(121,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2743,6 +3024,7 @@ │ │ └── unescaped: "foo" │ └── operator_loc: (121,4)-(121,6) = "in" ├── @ MatchPredicateNode (location: (122,0)-(122,10)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (122,0)-(122,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2756,8 +3038,10 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ NilNode (location: (122,7)-(122,10)) + │ │ └── flags: static_literal │ └── operator_loc: (122,4)-(122,6) = "in" ├── @ MatchPredicateNode (location: (123,0)-(123,11)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (123,0)-(123,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2771,8 +3055,10 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ SelfNode (location: (123,7)-(123,11)) + │ │ └── flags: ∅ │ └── operator_loc: (123,4)-(123,6) = "in" ├── @ MatchPredicateNode (location: (124,0)-(124,11)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (124,0)-(124,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2786,8 +3072,10 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ TrueNode (location: (124,7)-(124,11)) + │ │ └── flags: static_literal │ └── operator_loc: (124,4)-(124,6) = "in" ├── @ MatchPredicateNode (location: (125,0)-(125,12)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (125,0)-(125,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2801,8 +3089,10 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ FalseNode (location: (125,7)-(125,12)) + │ │ └── flags: static_literal │ └── operator_loc: (125,4)-(125,6) = "in" ├── @ MatchPredicateNode (location: (126,0)-(126,15)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (126,0)-(126,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2820,6 +3110,7 @@ │ │ └── filepath: "patterns.txt" │ └── operator_loc: (126,4)-(126,6) = "in" ├── @ MatchPredicateNode (location: (127,0)-(127,15)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (127,0)-(127,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2833,8 +3124,10 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ SourceLineNode (location: (127,7)-(127,15)) + │ │ └── flags: static_literal │ └── operator_loc: (127,4)-(127,6) = "in" ├── @ MatchPredicateNode (location: (128,0)-(128,19)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (128,0)-(128,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2848,8 +3141,10 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ SourceEncodingNode (location: (128,7)-(128,19)) + │ │ └── flags: static_literal │ └── operator_loc: (128,4)-(128,6) = "in" ├── @ MatchPredicateNode (location: (129,0)-(129,17)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (129,0)-(129,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2863,6 +3158,7 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ LambdaNode (location: (129,7)-(129,17)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── operator_loc: (129,7)-(129,9) = "->" │ │ ├── opening_loc: (129,10)-(129,11) = "{" @@ -2870,12 +3166,15 @@ │ │ ├── parameters: ∅ │ │ └── body: │ │ @ StatementsNode (location: (129,12)-(129,15)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (129,12)-(129,15)) + │ │ ├── flags: newline │ │ ├── name: :bar │ │ └── depth: 1 │ └── operator_loc: (129,4)-(129,6) = "in" ├── @ MatchPredicateNode (location: (131,0)-(131,11)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (131,0)-(131,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2889,18 +3188,22 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (131,7)-(131,11)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (131,7)-(131,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── rest: │ │ │ @ ImplicitRestNode (location: (131,10)-(131,11)) + │ │ │ └── flags: ∅ │ │ ├── posts: (length: 0) │ │ ├── opening_loc: ∅ │ │ └── closing_loc: ∅ │ └── operator_loc: (131,4)-(131,6) = "in" ├── @ CaseMatchNode (location: (135,0)-(135,25)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (135,5)-(135,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -2914,8 +3217,10 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (135,10)-(135,21)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ LocalVariableTargetNode (location: (135,13)-(135,16)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── statements: ∅ @@ -2925,6 +3230,7 @@ │ ├── case_keyword_loc: (135,0)-(135,4) = "case" │ └── end_keyword_loc: (135,22)-(135,25) = "end" ├── @ CaseMatchNode (location: (136,0)-(136,23)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (136,5)-(136,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -2938,9 +3244,10 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (136,10)-(136,19)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IntegerNode (location: (136,13)-(136,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── statements: ∅ │ │ ├── in_loc: (136,10)-(136,12) = "in" @@ -2949,6 +3256,7 @@ │ ├── case_keyword_loc: (136,0)-(136,4) = "case" │ └── end_keyword_loc: (136,20)-(136,23) = "end" ├── @ CaseMatchNode (location: (137,0)-(137,25)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (137,5)-(137,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -2962,8 +3270,10 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (137,10)-(137,21)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ FloatNode (location: (137,13)-(137,16)) + │ │ │ ├── flags: static_literal │ │ │ └── value: 1.0 │ │ ├── statements: ∅ │ │ ├── in_loc: (137,10)-(137,12) = "in" @@ -2972,6 +3282,7 @@ │ ├── case_keyword_loc: (137,0)-(137,4) = "case" │ └── end_keyword_loc: (137,22)-(137,25) = "end" ├── @ CaseMatchNode (location: (138,0)-(138,24)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (138,5)-(138,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -2985,11 +3296,13 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (138,10)-(138,20)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ImaginaryNode (location: (138,13)-(138,15)) + │ │ │ ├── flags: static_literal │ │ │ └── numeric: │ │ │ @ IntegerNode (location: (138,13)-(138,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── statements: ∅ │ │ ├── in_loc: (138,10)-(138,12) = "in" @@ -2998,6 +3311,7 @@ │ ├── case_keyword_loc: (138,0)-(138,4) = "case" │ └── end_keyword_loc: (138,21)-(138,24) = "end" ├── @ CaseMatchNode (location: (139,0)-(139,24)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (139,5)-(139,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3011,9 +3325,10 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (139,10)-(139,20)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ RationalNode (location: (139,13)-(139,15)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ ├── numerator: 1 │ │ │ └── denominator: 1 │ │ ├── statements: ∅ @@ -3023,6 +3338,7 @@ │ ├── case_keyword_loc: (139,0)-(139,4) = "case" │ └── end_keyword_loc: (139,21)-(139,24) = "end" ├── @ CaseMatchNode (location: (140,0)-(140,26)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (140,5)-(140,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3036,9 +3352,10 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (140,10)-(140,22)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ SymbolNode (location: (140,13)-(140,17)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (140,13)-(140,14) = ":" │ │ │ ├── value_loc: (140,14)-(140,17) = "foo" │ │ │ ├── closing_loc: ∅ @@ -3050,6 +3367,7 @@ │ ├── case_keyword_loc: (140,0)-(140,4) = "case" │ └── end_keyword_loc: (140,23)-(140,26) = "end" ├── @ CaseMatchNode (location: (141,0)-(141,29)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (141,5)-(141,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3063,9 +3381,10 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (141,10)-(141,25)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ SymbolNode (location: (141,13)-(141,20)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (141,13)-(141,16) = "%s[" │ │ │ ├── value_loc: (141,16)-(141,19) = "foo" │ │ │ ├── closing_loc: (141,19)-(141,20) = "]" @@ -3077,6 +3396,7 @@ │ ├── case_keyword_loc: (141,0)-(141,4) = "case" │ └── end_keyword_loc: (141,26)-(141,29) = "end" ├── @ CaseMatchNode (location: (142,0)-(142,28)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (142,5)-(142,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3090,9 +3410,10 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (142,10)-(142,24)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ SymbolNode (location: (142,13)-(142,19)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (142,13)-(142,15) = ":\"" │ │ │ ├── value_loc: (142,15)-(142,18) = "foo" │ │ │ ├── closing_loc: (142,18)-(142,19) = "\"" @@ -3104,6 +3425,7 @@ │ ├── case_keyword_loc: (142,0)-(142,4) = "case" │ └── end_keyword_loc: (142,25)-(142,28) = "end" ├── @ CaseMatchNode (location: (143,0)-(143,27)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (143,5)-(143,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3117,9 +3439,10 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (143,10)-(143,23)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ RegularExpressionNode (location: (143,13)-(143,18)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (143,13)-(143,14) = "/" │ │ │ ├── content_loc: (143,14)-(143,17) = "foo" │ │ │ ├── closing_loc: (143,17)-(143,18) = "/" @@ -3131,6 +3454,7 @@ │ ├── case_keyword_loc: (143,0)-(143,4) = "case" │ └── end_keyword_loc: (143,24)-(143,27) = "end" ├── @ CaseMatchNode (location: (144,0)-(144,27)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (144,5)-(144,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3144,6 +3468,7 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (144,10)-(144,23)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ XStringNode (location: (144,13)-(144,18)) │ │ │ ├── flags: ∅ @@ -3158,6 +3483,7 @@ │ ├── case_keyword_loc: (144,0)-(144,4) = "case" │ └── end_keyword_loc: (144,24)-(144,27) = "end" ├── @ CaseMatchNode (location: (145,0)-(145,29)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (145,5)-(145,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3171,6 +3497,7 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (145,10)-(145,25)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ XStringNode (location: (145,13)-(145,20)) │ │ │ ├── flags: ∅ @@ -3185,6 +3512,7 @@ │ ├── case_keyword_loc: (145,0)-(145,4) = "case" │ └── end_keyword_loc: (145,26)-(145,29) = "end" ├── @ CaseMatchNode (location: (146,0)-(146,29)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (146,5)-(146,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3198,12 +3526,13 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (146,10)-(146,25)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayNode (location: (146,13)-(146,20)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: static_literal │ │ │ ├── elements: (length: 1) │ │ │ │ └── @ SymbolNode (location: (146,16)-(146,19)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (146,16)-(146,19) = "foo" │ │ │ │ ├── closing_loc: ∅ @@ -3217,6 +3546,7 @@ │ ├── case_keyword_loc: (146,0)-(146,4) = "case" │ └── end_keyword_loc: (146,26)-(146,29) = "end" ├── @ CaseMatchNode (location: (147,0)-(147,29)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (147,5)-(147,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3230,12 +3560,13 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (147,10)-(147,25)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayNode (location: (147,13)-(147,20)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: static_literal │ │ │ ├── elements: (length: 1) │ │ │ │ └── @ SymbolNode (location: (147,16)-(147,19)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (147,16)-(147,19) = "foo" │ │ │ │ ├── closing_loc: ∅ @@ -3249,6 +3580,7 @@ │ ├── case_keyword_loc: (147,0)-(147,4) = "case" │ └── end_keyword_loc: (147,26)-(147,29) = "end" ├── @ CaseMatchNode (location: (148,0)-(148,29)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (148,5)-(148,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3262,6 +3594,7 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (148,10)-(148,25)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayNode (location: (148,13)-(148,20)) │ │ │ ├── flags: ∅ @@ -3281,6 +3614,7 @@ │ ├── case_keyword_loc: (148,0)-(148,4) = "case" │ └── end_keyword_loc: (148,26)-(148,29) = "end" ├── @ CaseMatchNode (location: (149,0)-(149,29)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (149,5)-(149,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3294,6 +3628,7 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (149,10)-(149,25)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayNode (location: (149,13)-(149,20)) │ │ │ ├── flags: ∅ @@ -3313,6 +3648,7 @@ │ ├── case_keyword_loc: (149,0)-(149,4) = "case" │ └── end_keyword_loc: (149,26)-(149,29) = "end" ├── @ CaseMatchNode (location: (150,0)-(150,29)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (150,5)-(150,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3326,6 +3662,7 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (150,10)-(150,25)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ StringNode (location: (150,13)-(150,20)) │ │ │ ├── flags: ∅ @@ -3340,6 +3677,7 @@ │ ├── case_keyword_loc: (150,0)-(150,4) = "case" │ └── end_keyword_loc: (150,26)-(150,29) = "end" ├── @ CaseMatchNode (location: (151,0)-(151,29)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (151,5)-(151,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3353,6 +3691,7 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (151,10)-(151,25)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ StringNode (location: (151,13)-(151,20)) │ │ │ ├── flags: ∅ @@ -3367,6 +3706,7 @@ │ ├── case_keyword_loc: (151,0)-(151,4) = "case" │ └── end_keyword_loc: (151,26)-(151,29) = "end" ├── @ CaseMatchNode (location: (152,0)-(152,27)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (152,5)-(152,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3380,6 +3720,7 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (152,10)-(152,23)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ StringNode (location: (152,13)-(152,18)) │ │ │ ├── flags: ∅ @@ -3394,6 +3735,7 @@ │ ├── case_keyword_loc: (152,0)-(152,4) = "case" │ └── end_keyword_loc: (152,24)-(152,27) = "end" ├── @ CaseMatchNode (location: (153,0)-(153,25)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (153,5)-(153,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3407,8 +3749,10 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (153,10)-(153,21)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ NilNode (location: (153,13)-(153,16)) + │ │ │ └── flags: static_literal │ │ ├── statements: ∅ │ │ ├── in_loc: (153,10)-(153,12) = "in" │ │ └── then_loc: (153,17)-(153,21) = "then" @@ -3416,6 +3760,7 @@ │ ├── case_keyword_loc: (153,0)-(153,4) = "case" │ └── end_keyword_loc: (153,22)-(153,25) = "end" ├── @ CaseMatchNode (location: (154,0)-(154,26)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (154,5)-(154,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3429,8 +3774,10 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (154,10)-(154,22)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ SelfNode (location: (154,13)-(154,17)) + │ │ │ └── flags: ∅ │ │ ├── statements: ∅ │ │ ├── in_loc: (154,10)-(154,12) = "in" │ │ └── then_loc: (154,18)-(154,22) = "then" @@ -3438,6 +3785,7 @@ │ ├── case_keyword_loc: (154,0)-(154,4) = "case" │ └── end_keyword_loc: (154,23)-(154,26) = "end" ├── @ CaseMatchNode (location: (155,0)-(155,26)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (155,5)-(155,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3451,8 +3799,10 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (155,10)-(155,22)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ TrueNode (location: (155,13)-(155,17)) + │ │ │ └── flags: static_literal │ │ ├── statements: ∅ │ │ ├── in_loc: (155,10)-(155,12) = "in" │ │ └── then_loc: (155,18)-(155,22) = "then" @@ -3460,6 +3810,7 @@ │ ├── case_keyword_loc: (155,0)-(155,4) = "case" │ └── end_keyword_loc: (155,23)-(155,26) = "end" ├── @ CaseMatchNode (location: (156,0)-(156,27)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (156,5)-(156,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3473,8 +3824,10 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (156,10)-(156,23)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ FalseNode (location: (156,13)-(156,18)) + │ │ │ └── flags: static_literal │ │ ├── statements: ∅ │ │ ├── in_loc: (156,10)-(156,12) = "in" │ │ └── then_loc: (156,19)-(156,23) = "then" @@ -3482,6 +3835,7 @@ │ ├── case_keyword_loc: (156,0)-(156,4) = "case" │ └── end_keyword_loc: (156,24)-(156,27) = "end" ├── @ CaseMatchNode (location: (157,0)-(157,30)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (157,5)-(157,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3495,6 +3849,7 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (157,10)-(157,26)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ SourceFileNode (location: (157,13)-(157,21)) │ │ │ ├── flags: ∅ @@ -3506,6 +3861,7 @@ │ ├── case_keyword_loc: (157,0)-(157,4) = "case" │ └── end_keyword_loc: (157,27)-(157,30) = "end" ├── @ CaseMatchNode (location: (158,0)-(158,30)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (158,5)-(158,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3519,8 +3875,10 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (158,10)-(158,26)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ SourceLineNode (location: (158,13)-(158,21)) + │ │ │ └── flags: static_literal │ │ ├── statements: ∅ │ │ ├── in_loc: (158,10)-(158,12) = "in" │ │ └── then_loc: (158,22)-(158,26) = "then" @@ -3528,6 +3886,7 @@ │ ├── case_keyword_loc: (158,0)-(158,4) = "case" │ └── end_keyword_loc: (158,27)-(158,30) = "end" ├── @ CaseMatchNode (location: (159,0)-(159,34)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (159,5)-(159,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3541,8 +3900,10 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (159,10)-(159,30)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ SourceEncodingNode (location: (159,13)-(159,25)) + │ │ │ └── flags: static_literal │ │ ├── statements: ∅ │ │ ├── in_loc: (159,10)-(159,12) = "in" │ │ └── then_loc: (159,26)-(159,30) = "then" @@ -3550,6 +3911,7 @@ │ ├── case_keyword_loc: (159,0)-(159,4) = "case" │ └── end_keyword_loc: (159,31)-(159,34) = "end" ├── @ CaseMatchNode (location: (160,0)-(160,32)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (160,5)-(160,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3563,8 +3925,10 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (160,10)-(160,28)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ LambdaNode (location: (160,13)-(160,23)) + │ │ │ ├── flags: ∅ │ │ │ ├── locals: [] │ │ │ ├── operator_loc: (160,13)-(160,15) = "->" │ │ │ ├── opening_loc: (160,16)-(160,17) = "{" @@ -3572,8 +3936,10 @@ │ │ │ ├── parameters: ∅ │ │ │ └── body: │ │ │ @ StatementsNode (location: (160,18)-(160,21)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ LocalVariableReadNode (location: (160,18)-(160,21)) + │ │ │ ├── flags: newline │ │ │ ├── name: :bar │ │ │ └── depth: 1 │ │ ├── statements: ∅ @@ -3583,6 +3949,7 @@ │ ├── case_keyword_loc: (160,0)-(160,4) = "case" │ └── end_keyword_loc: (160,29)-(160,32) = "end" ├── @ CaseMatchNode (location: (162,0)-(162,32)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (162,5)-(162,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3596,18 +3963,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (162,10)-(162,28)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (162,13)-(162,23)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (162,17)-(162,19) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (162,20)-(162,23)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (162,13)-(162,16)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ LocalVariableTargetNode (location: (162,13)-(162,16)) + │ │ │ │ ├── flags: newline │ │ │ │ ├── name: :bar │ │ │ │ └── depth: 0 │ │ │ ├── consequent: ∅ @@ -3619,6 +3991,7 @@ │ ├── case_keyword_loc: (162,0)-(162,4) = "case" │ └── end_keyword_loc: (162,29)-(162,32) = "end" ├── @ CaseMatchNode (location: (163,0)-(163,30)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (163,5)-(163,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3632,19 +4005,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (163,10)-(163,26)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (163,13)-(163,21)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (163,15)-(163,17) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (163,18)-(163,21)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (163,13)-(163,14)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ IntegerNode (location: (163,13)-(163,14)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: newline, static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── consequent: ∅ │ │ │ └── end_keyword_loc: ∅ @@ -3655,6 +4032,7 @@ │ ├── case_keyword_loc: (163,0)-(163,4) = "case" │ └── end_keyword_loc: (163,27)-(163,30) = "end" ├── @ CaseMatchNode (location: (164,0)-(164,32)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (164,5)-(164,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3668,18 +4046,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (164,10)-(164,28)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (164,13)-(164,23)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (164,17)-(164,19) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (164,20)-(164,23)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (164,13)-(164,16)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ FloatNode (location: (164,13)-(164,16)) + │ │ │ │ ├── flags: newline, static_literal │ │ │ │ └── value: 1.0 │ │ │ ├── consequent: ∅ │ │ │ └── end_keyword_loc: ∅ @@ -3690,6 +4073,7 @@ │ ├── case_keyword_loc: (164,0)-(164,4) = "case" │ └── end_keyword_loc: (164,29)-(164,32) = "end" ├── @ CaseMatchNode (location: (165,0)-(165,31)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (165,5)-(165,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3703,21 +4087,26 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (165,10)-(165,27)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (165,13)-(165,22)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (165,16)-(165,18) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (165,19)-(165,22)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (165,13)-(165,15)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ ImaginaryNode (location: (165,13)-(165,15)) + │ │ │ │ ├── flags: newline, static_literal │ │ │ │ └── numeric: │ │ │ │ @ IntegerNode (location: (165,13)-(165,14)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── consequent: ∅ │ │ │ └── end_keyword_loc: ∅ @@ -3728,6 +4117,7 @@ │ ├── case_keyword_loc: (165,0)-(165,4) = "case" │ └── end_keyword_loc: (165,28)-(165,31) = "end" ├── @ CaseMatchNode (location: (166,0)-(166,31)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (166,5)-(166,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3741,19 +4131,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (166,10)-(166,27)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (166,13)-(166,22)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (166,16)-(166,18) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (166,19)-(166,22)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (166,13)-(166,15)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ RationalNode (location: (166,13)-(166,15)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: newline, static_literal, decimal │ │ │ │ ├── numerator: 1 │ │ │ │ └── denominator: 1 │ │ │ ├── consequent: ∅ @@ -3765,6 +4159,7 @@ │ ├── case_keyword_loc: (166,0)-(166,4) = "case" │ └── end_keyword_loc: (166,28)-(166,31) = "end" ├── @ CaseMatchNode (location: (167,0)-(167,33)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (167,5)-(167,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3778,19 +4173,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (167,10)-(167,29)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (167,13)-(167,24)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (167,18)-(167,20) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (167,21)-(167,24)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (167,13)-(167,17)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ SymbolNode (location: (167,13)-(167,17)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (167,13)-(167,14) = ":" │ │ │ │ ├── value_loc: (167,14)-(167,17) = "foo" │ │ │ │ ├── closing_loc: ∅ @@ -3804,6 +4203,7 @@ │ ├── case_keyword_loc: (167,0)-(167,4) = "case" │ └── end_keyword_loc: (167,30)-(167,33) = "end" ├── @ CaseMatchNode (location: (168,0)-(168,36)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (168,5)-(168,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3817,19 +4217,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (168,10)-(168,32)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (168,13)-(168,27)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (168,21)-(168,23) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (168,24)-(168,27)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (168,13)-(168,20)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ SymbolNode (location: (168,13)-(168,20)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (168,13)-(168,16) = "%s[" │ │ │ │ ├── value_loc: (168,16)-(168,19) = "foo" │ │ │ │ ├── closing_loc: (168,19)-(168,20) = "]" @@ -3843,6 +4247,7 @@ │ ├── case_keyword_loc: (168,0)-(168,4) = "case" │ └── end_keyword_loc: (168,33)-(168,36) = "end" ├── @ CaseMatchNode (location: (169,0)-(169,35)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (169,5)-(169,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3856,19 +4261,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (169,10)-(169,31)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (169,13)-(169,26)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (169,20)-(169,22) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (169,23)-(169,26)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (169,13)-(169,19)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ SymbolNode (location: (169,13)-(169,19)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (169,13)-(169,15) = ":\"" │ │ │ │ ├── value_loc: (169,15)-(169,18) = "foo" │ │ │ │ ├── closing_loc: (169,18)-(169,19) = "\"" @@ -3882,6 +4291,7 @@ │ ├── case_keyword_loc: (169,0)-(169,4) = "case" │ └── end_keyword_loc: (169,32)-(169,35) = "end" ├── @ CaseMatchNode (location: (170,0)-(170,34)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (170,5)-(170,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3895,19 +4305,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (170,10)-(170,30)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (170,13)-(170,25)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (170,19)-(170,21) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (170,22)-(170,25)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (170,13)-(170,18)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ RegularExpressionNode (location: (170,13)-(170,18)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (170,13)-(170,14) = "/" │ │ │ │ ├── content_loc: (170,14)-(170,17) = "foo" │ │ │ │ ├── closing_loc: (170,17)-(170,18) = "/" @@ -3921,6 +4335,7 @@ │ ├── case_keyword_loc: (170,0)-(170,4) = "case" │ └── end_keyword_loc: (170,31)-(170,34) = "end" ├── @ CaseMatchNode (location: (171,0)-(171,34)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (171,5)-(171,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3934,19 +4349,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (171,10)-(171,30)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (171,13)-(171,25)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (171,19)-(171,21) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (171,22)-(171,25)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (171,13)-(171,18)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ XStringNode (location: (171,13)-(171,18)) - │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── flags: newline │ │ │ │ ├── opening_loc: (171,13)-(171,14) = "`" │ │ │ │ ├── content_loc: (171,14)-(171,17) = "foo" │ │ │ │ ├── closing_loc: (171,17)-(171,18) = "`" @@ -3960,6 +4379,7 @@ │ ├── case_keyword_loc: (171,0)-(171,4) = "case" │ └── end_keyword_loc: (171,31)-(171,34) = "end" ├── @ CaseMatchNode (location: (172,0)-(172,36)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (172,5)-(172,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -3973,19 +4393,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (172,10)-(172,32)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (172,13)-(172,27)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (172,21)-(172,23) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (172,24)-(172,27)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (172,13)-(172,20)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ XStringNode (location: (172,13)-(172,20)) - │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── flags: newline │ │ │ │ ├── opening_loc: (172,13)-(172,16) = "%x[" │ │ │ │ ├── content_loc: (172,16)-(172,19) = "foo" │ │ │ │ ├── closing_loc: (172,19)-(172,20) = "]" @@ -3999,6 +4423,7 @@ │ ├── case_keyword_loc: (172,0)-(172,4) = "case" │ └── end_keyword_loc: (172,33)-(172,36) = "end" ├── @ CaseMatchNode (location: (173,0)-(173,36)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (173,5)-(173,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -4012,22 +4437,26 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (173,10)-(173,32)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (173,13)-(173,27)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (173,21)-(173,23) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (173,24)-(173,27)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (173,13)-(173,20)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ ArrayNode (location: (173,13)-(173,20)) - │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── flags: newline, static_literal │ │ │ │ ├── elements: (length: 1) │ │ │ │ │ └── @ SymbolNode (location: (173,16)-(173,19)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── value_loc: (173,16)-(173,19) = "foo" │ │ │ │ │ ├── closing_loc: ∅ @@ -4043,6 +4472,7 @@ │ ├── case_keyword_loc: (173,0)-(173,4) = "case" │ └── end_keyword_loc: (173,33)-(173,36) = "end" ├── @ CaseMatchNode (location: (174,0)-(174,36)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (174,5)-(174,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -4056,22 +4486,26 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (174,10)-(174,32)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (174,13)-(174,27)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (174,21)-(174,23) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (174,24)-(174,27)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (174,13)-(174,20)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ ArrayNode (location: (174,13)-(174,20)) - │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── flags: newline, static_literal │ │ │ │ ├── elements: (length: 1) │ │ │ │ │ └── @ SymbolNode (location: (174,16)-(174,19)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── value_loc: (174,16)-(174,19) = "foo" │ │ │ │ │ ├── closing_loc: ∅ @@ -4087,6 +4521,7 @@ │ ├── case_keyword_loc: (174,0)-(174,4) = "case" │ └── end_keyword_loc: (174,33)-(174,36) = "end" ├── @ CaseMatchNode (location: (175,0)-(175,36)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (175,5)-(175,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -4100,19 +4535,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (175,10)-(175,32)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (175,13)-(175,27)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (175,21)-(175,23) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (175,24)-(175,27)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (175,13)-(175,20)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ ArrayNode (location: (175,13)-(175,20)) - │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── flags: newline │ │ │ │ ├── elements: (length: 1) │ │ │ │ │ └── @ StringNode (location: (175,16)-(175,19)) │ │ │ │ │ ├── flags: ∅ @@ -4131,6 +4570,7 @@ │ ├── case_keyword_loc: (175,0)-(175,4) = "case" │ └── end_keyword_loc: (175,33)-(175,36) = "end" ├── @ CaseMatchNode (location: (176,0)-(176,36)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (176,5)-(176,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -4144,19 +4584,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (176,10)-(176,32)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (176,13)-(176,27)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (176,21)-(176,23) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (176,24)-(176,27)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (176,13)-(176,20)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ ArrayNode (location: (176,13)-(176,20)) - │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── flags: newline │ │ │ │ ├── elements: (length: 1) │ │ │ │ │ └── @ StringNode (location: (176,16)-(176,19)) │ │ │ │ │ ├── flags: ∅ @@ -4175,6 +4619,7 @@ │ ├── case_keyword_loc: (176,0)-(176,4) = "case" │ └── end_keyword_loc: (176,33)-(176,36) = "end" ├── @ CaseMatchNode (location: (177,0)-(177,36)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (177,5)-(177,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -4188,19 +4633,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (177,10)-(177,32)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (177,13)-(177,27)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (177,21)-(177,23) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (177,24)-(177,27)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (177,13)-(177,20)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ StringNode (location: (177,13)-(177,20)) - │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── flags: newline │ │ │ │ ├── opening_loc: (177,13)-(177,16) = "%q[" │ │ │ │ ├── content_loc: (177,16)-(177,19) = "foo" │ │ │ │ ├── closing_loc: (177,19)-(177,20) = "]" @@ -4214,6 +4663,7 @@ │ ├── case_keyword_loc: (177,0)-(177,4) = "case" │ └── end_keyword_loc: (177,33)-(177,36) = "end" ├── @ CaseMatchNode (location: (178,0)-(178,36)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (178,5)-(178,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -4227,19 +4677,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (178,10)-(178,32)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (178,13)-(178,27)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (178,21)-(178,23) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (178,24)-(178,27)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (178,13)-(178,20)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ StringNode (location: (178,13)-(178,20)) - │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── flags: newline │ │ │ │ ├── opening_loc: (178,13)-(178,16) = "%Q[" │ │ │ │ ├── content_loc: (178,16)-(178,19) = "foo" │ │ │ │ ├── closing_loc: (178,19)-(178,20) = "]" @@ -4253,6 +4707,7 @@ │ ├── case_keyword_loc: (178,0)-(178,4) = "case" │ └── end_keyword_loc: (178,33)-(178,36) = "end" ├── @ CaseMatchNode (location: (179,0)-(179,34)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (179,5)-(179,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -4266,19 +4721,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (179,10)-(179,30)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (179,13)-(179,25)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (179,19)-(179,21) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (179,22)-(179,25)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (179,13)-(179,18)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ StringNode (location: (179,13)-(179,18)) - │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── flags: newline │ │ │ │ ├── opening_loc: (179,13)-(179,14) = "\"" │ │ │ │ ├── content_loc: (179,14)-(179,17) = "foo" │ │ │ │ ├── closing_loc: (179,17)-(179,18) = "\"" @@ -4292,6 +4751,7 @@ │ ├── case_keyword_loc: (179,0)-(179,4) = "case" │ └── end_keyword_loc: (179,31)-(179,34) = "end" ├── @ CaseMatchNode (location: (180,0)-(180,32)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (180,5)-(180,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -4305,18 +4765,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (180,10)-(180,28)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (180,13)-(180,23)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (180,17)-(180,19) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (180,20)-(180,23)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (180,13)-(180,16)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ NilNode (location: (180,13)-(180,16)) + │ │ │ │ └── flags: newline, static_literal │ │ │ ├── consequent: ∅ │ │ │ └── end_keyword_loc: ∅ │ │ ├── statements: ∅ @@ -4326,6 +4791,7 @@ │ ├── case_keyword_loc: (180,0)-(180,4) = "case" │ └── end_keyword_loc: (180,29)-(180,32) = "end" ├── @ CaseMatchNode (location: (181,0)-(181,33)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (181,5)-(181,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -4339,18 +4805,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (181,10)-(181,29)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (181,13)-(181,24)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (181,18)-(181,20) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (181,21)-(181,24)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (181,13)-(181,17)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ SelfNode (location: (181,13)-(181,17)) + │ │ │ │ └── flags: newline │ │ │ ├── consequent: ∅ │ │ │ └── end_keyword_loc: ∅ │ │ ├── statements: ∅ @@ -4360,6 +4831,7 @@ │ ├── case_keyword_loc: (181,0)-(181,4) = "case" │ └── end_keyword_loc: (181,30)-(181,33) = "end" ├── @ CaseMatchNode (location: (182,0)-(182,33)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (182,5)-(182,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -4373,18 +4845,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (182,10)-(182,29)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (182,13)-(182,24)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (182,18)-(182,20) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (182,21)-(182,24)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (182,13)-(182,17)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ TrueNode (location: (182,13)-(182,17)) + │ │ │ │ └── flags: newline, static_literal │ │ │ ├── consequent: ∅ │ │ │ └── end_keyword_loc: ∅ │ │ ├── statements: ∅ @@ -4394,6 +4871,7 @@ │ ├── case_keyword_loc: (182,0)-(182,4) = "case" │ └── end_keyword_loc: (182,30)-(182,33) = "end" ├── @ CaseMatchNode (location: (183,0)-(183,34)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (183,5)-(183,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -4407,18 +4885,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (183,10)-(183,30)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (183,13)-(183,25)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (183,19)-(183,21) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (183,22)-(183,25)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (183,13)-(183,18)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ FalseNode (location: (183,13)-(183,18)) + │ │ │ │ └── flags: newline, static_literal │ │ │ ├── consequent: ∅ │ │ │ └── end_keyword_loc: ∅ │ │ ├── statements: ∅ @@ -4428,6 +4911,7 @@ │ ├── case_keyword_loc: (183,0)-(183,4) = "case" │ └── end_keyword_loc: (183,31)-(183,34) = "end" ├── @ CaseMatchNode (location: (184,0)-(184,37)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (184,5)-(184,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -4441,19 +4925,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (184,10)-(184,33)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (184,13)-(184,28)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (184,22)-(184,24) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (184,25)-(184,28)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (184,13)-(184,21)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ SourceFileNode (location: (184,13)-(184,21)) - │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── flags: newline │ │ │ │ └── filepath: "patterns.txt" │ │ │ ├── consequent: ∅ │ │ │ └── end_keyword_loc: ∅ @@ -4464,6 +4952,7 @@ │ ├── case_keyword_loc: (184,0)-(184,4) = "case" │ └── end_keyword_loc: (184,34)-(184,37) = "end" ├── @ CaseMatchNode (location: (185,0)-(185,37)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (185,5)-(185,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -4477,18 +4966,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (185,10)-(185,33)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (185,13)-(185,28)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (185,22)-(185,24) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (185,25)-(185,28)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (185,13)-(185,21)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ SourceLineNode (location: (185,13)-(185,21)) + │ │ │ │ └── flags: newline, static_literal │ │ │ ├── consequent: ∅ │ │ │ └── end_keyword_loc: ∅ │ │ ├── statements: ∅ @@ -4498,6 +4992,7 @@ │ ├── case_keyword_loc: (185,0)-(185,4) = "case" │ └── end_keyword_loc: (185,34)-(185,37) = "end" ├── @ CaseMatchNode (location: (186,0)-(186,41)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (186,5)-(186,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -4511,18 +5006,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (186,10)-(186,37)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (186,13)-(186,32)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (186,26)-(186,28) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (186,29)-(186,32)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (186,13)-(186,25)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ SourceEncodingNode (location: (186,13)-(186,25)) + │ │ │ │ └── flags: newline, static_literal │ │ │ ├── consequent: ∅ │ │ │ └── end_keyword_loc: ∅ │ │ ├── statements: ∅ @@ -4532,6 +5032,7 @@ │ ├── case_keyword_loc: (186,0)-(186,4) = "case" │ └── end_keyword_loc: (186,38)-(186,41) = "end" ├── @ CaseMatchNode (location: (187,0)-(187,39)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (187,5)-(187,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -4545,18 +5046,23 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (187,10)-(187,35)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (187,13)-(187,30)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (187,24)-(187,26) = "if" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (187,27)-(187,30)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 0 │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (187,13)-(187,23)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ LambdaNode (location: (187,13)-(187,23)) + │ │ │ │ ├── flags: newline │ │ │ │ ├── locals: [] │ │ │ │ ├── operator_loc: (187,13)-(187,15) = "->" │ │ │ │ ├── opening_loc: (187,16)-(187,17) = "{" @@ -4564,8 +5070,10 @@ │ │ │ │ ├── parameters: ∅ │ │ │ │ └── body: │ │ │ │ @ StatementsNode (location: (187,18)-(187,21)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ LocalVariableReadNode (location: (187,18)-(187,21)) + │ │ │ │ ├── flags: newline │ │ │ │ ├── name: :bar │ │ │ │ └── depth: 1 │ │ │ ├── consequent: ∅ @@ -4577,9 +5085,11 @@ │ ├── case_keyword_loc: (187,0)-(187,4) = "case" │ └── end_keyword_loc: (187,36)-(187,39) = "end" ├── @ IfNode (location: (189,0)-(190,3)) + │ ├── flags: newline │ ├── if_keyword_loc: (189,0)-(189,2) = "if" │ ├── predicate: │ │ @ MatchPredicateNode (location: (189,3)-(189,10)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ CallNode (location: (189,3)-(189,4)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -4593,6 +5103,7 @@ │ │ │ └── block: ∅ │ │ ├── pattern: │ │ │ @ ArrayPatternNode (location: (189,8)-(189,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: ∅ │ │ │ ├── requireds: (length: 0) │ │ │ ├── rest: ∅ @@ -4605,6 +5116,7 @@ │ ├── consequent: ∅ │ └── end_keyword_loc: (190,0)-(190,3) = "end" ├── @ MatchRequiredNode (location: (192,0)-(194,1)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (192,0)-(192,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -4618,9 +5130,11 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (192,5)-(194,1)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (193,2)-(193,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :b │ │ │ └── depth: 0 │ │ ├── rest: ∅ @@ -4629,6 +5143,7 @@ │ │ └── closing_loc: (194,0)-(194,1) = "]" │ └── operator_loc: (192,2)-(192,4) = "=>" ├── @ MatchPredicateNode (location: (196,0)-(200,1)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (196,0)-(196,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -4642,34 +5157,41 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ HashPatternNode (location: (196,7)-(200,1)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (196,7)-(196,8)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :A │ │ ├── elements: (length: 1) │ │ │ └── @ AssocNode (location: (197,2)-(199,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (197,2)-(197,6)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (197,2)-(197,5) = "bar" │ │ │ │ ├── closing_loc: (197,5)-(197,6) = ":" │ │ │ │ └── unescaped: "bar" │ │ │ ├── value: │ │ │ │ @ HashPatternNode (location: (197,7)-(199,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── constant: │ │ │ │ │ @ ConstantReadNode (location: (197,7)-(197,8)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── name: :B │ │ │ │ ├── elements: (length: 1) │ │ │ │ │ └── @ AssocNode (location: (198,4)-(198,12)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── key: │ │ │ │ │ │ @ SymbolNode (location: (198,4)-(198,10)) - │ │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ │ ├── value_loc: (198,4)-(198,9) = "value" │ │ │ │ │ │ ├── closing_loc: (198,9)-(198,10) = ":" │ │ │ │ │ │ └── unescaped: "value" │ │ │ │ │ ├── value: │ │ │ │ │ │ @ LocalVariableTargetNode (location: (198,11)-(198,12)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ ├── name: :a │ │ │ │ │ │ └── depth: 0 │ │ │ │ │ └── operator_loc: ∅ @@ -4682,6 +5204,7 @@ │ │ └── closing_loc: (200,0)-(200,1) = "]" │ └── operator_loc: (196,4)-(196,6) = "in" ├── @ MatchPredicateNode (location: (202,0)-(202,17)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (202,0)-(202,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -4695,17 +5218,21 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ CapturePatternNode (location: (202,7)-(202,17)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ LocalVariableTargetNode (location: (202,7)-(202,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── target: │ │ │ @ LocalVariableTargetNode (location: (202,14)-(202,17)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :baz │ │ │ └── depth: 0 │ │ └── operator_loc: (202,11)-(202,13) = "=>" │ └── operator_loc: (202,4)-(202,6) = "in" ├── @ MatchRequiredNode (location: (203,0)-(203,17)) + │ ├── flags: newline │ ├── value: │ │ @ CallNode (location: (203,0)-(203,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -4719,25 +5246,32 @@ │ │ └── block: ∅ │ ├── pattern: │ │ @ CapturePatternNode (location: (203,7)-(203,17)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ LocalVariableTargetNode (location: (203,7)-(203,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── target: │ │ │ @ LocalVariableTargetNode (location: (203,14)-(203,17)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :baz │ │ │ └── depth: 0 │ │ └── operator_loc: (203,11)-(203,13) = "=>" │ └── operator_loc: (203,4)-(203,6) = "=>" ├── @ MultiWriteNode (location: (205,0)-(205,20)) + │ ├── flags: newline │ ├── lefts: (length: 3) │ │ ├── @ LocalVariableTargetNode (location: (205,0)-(205,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :foo │ │ │ └── depth: 0 │ │ ├── @ LocalVariableTargetNode (location: (205,5)-(205,8)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ └── @ LocalVariableTargetNode (location: (205,10)-(205,13)) + │ │ ├── flags: ∅ │ │ ├── name: :baz │ │ └── depth: 0 │ ├── rest: ∅ @@ -4747,18 +5281,18 @@ │ ├── operator_loc: (205,14)-(205,15) = "=" │ └── value: │ @ ArrayNode (location: (205,16)-(205,20)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 2) │ │ ├── @ IntegerNode (location: (205,16)-(205,17)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ IntegerNode (location: (205,19)-(205,20)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── opening_loc: ∅ │ └── closing_loc: ∅ ├── @ CallNode (location: (206,0)-(208,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -4768,34 +5302,41 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (206,4)-(208,3)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (207,2)-(207,29)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ MatchRequiredNode (location: (207,2)-(207,29)) + │ │ ├── flags: newline │ │ ├── value: │ │ │ @ ArrayNode (location: (207,2)-(207,8)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: static_literal │ │ │ ├── elements: (length: 2) │ │ │ │ ├── @ IntegerNode (location: (207,3)-(207,4)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ └── @ IntegerNode (location: (207,6)-(207,7)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ ├── opening_loc: (207,2)-(207,3) = "[" │ │ │ └── closing_loc: (207,7)-(207,8) = "]" │ │ ├── pattern: │ │ │ @ CapturePatternNode (location: (207,12)-(207,29)) + │ │ │ ├── flags: ∅ │ │ │ ├── value: │ │ │ │ @ ArrayPatternNode (location: (207,12)-(207,22)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── constant: ∅ │ │ │ │ ├── requireds: (length: 2) │ │ │ │ │ ├── @ LocalVariableTargetNode (location: (207,13)-(207,16)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ ├── name: :foo │ │ │ │ │ │ └── depth: 1 │ │ │ │ │ └── @ LocalVariableTargetNode (location: (207,18)-(207,21)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── name: :bar │ │ │ │ │ └── depth: 1 │ │ │ │ ├── rest: ∅ @@ -4804,6 +5345,7 @@ │ │ │ │ └── closing_loc: (207,21)-(207,22) = "]" │ │ │ ├── target: │ │ │ │ @ LocalVariableTargetNode (location: (207,26)-(207,29)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :baz │ │ │ │ └── depth: 1 │ │ │ └── operator_loc: (207,23)-(207,25) = "=>" @@ -4811,31 +5353,39 @@ │ ├── opening_loc: (206,4)-(206,6) = "do" │ └── closing_loc: (208,0)-(208,3) = "end" ├── @ MatchRequiredNode (location: (210,0)-(210,19)) + │ ├── flags: newline │ ├── value: │ │ @ LocalVariableReadNode (location: (210,0)-(210,3)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ └── depth: 0 │ ├── pattern: │ │ @ ArrayPatternNode (location: (210,7)-(210,19)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (210,7)-(210,13)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Object │ │ ├── requireds: (length: 1) │ │ │ └── @ HashPatternNode (location: (210,14)-(210,18)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: ∅ │ │ │ ├── elements: (length: 1) │ │ │ │ └── @ AssocNode (location: (210,15)-(210,17)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── key: │ │ │ │ │ @ SymbolNode (location: (210,15)-(210,17)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── value_loc: (210,15)-(210,16) = "x" │ │ │ │ │ ├── closing_loc: (210,16)-(210,17) = ":" │ │ │ │ │ └── unescaped: "x" │ │ │ │ ├── value: │ │ │ │ │ @ ImplicitNode (location: (210,15)-(210,16)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── value: │ │ │ │ │ @ LocalVariableTargetNode (location: (210,15)-(210,16)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── name: :x │ │ │ │ │ └── depth: 0 │ │ │ │ └── operator_loc: ∅ @@ -4848,10 +5398,10 @@ │ │ └── closing_loc: (210,18)-(210,19) = "]" │ └── operator_loc: (210,4)-(210,6) = "=>" ├── @ CallNode (location: (212,0)-(212,19)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ IntegerNode (location: (212,0)-(212,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── call_operator_loc: (212,1)-(212,2) = "." │ ├── name: :then @@ -4861,49 +5411,160 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (212,7)-(212,19)) + │ ├── flags: ∅ │ ├── locals: [:_1] │ ├── parameters: │ │ @ NumberedParametersNode (location: (212,7)-(212,19)) + │ │ ├── flags: ∅ │ │ └── maximum: 1 │ ├── body: │ │ @ StatementsNode (location: (212,9)-(212,17)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ MatchPredicateNode (location: (212,9)-(212,17)) + │ │ ├── flags: newline │ │ ├── value: │ │ │ @ IntegerNode (location: (212,9)-(212,10)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── pattern: │ │ │ @ PinnedVariableNode (location: (212,14)-(212,17)) + │ │ │ ├── flags: ∅ │ │ │ ├── variable: │ │ │ │ @ LocalVariableReadNode (location: (212,15)-(212,17)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :_1 │ │ │ │ └── depth: 0 │ │ │ └── operator_loc: (212,14)-(212,15) = "^" │ │ └── operator_loc: (212,11)-(212,13) = "in" │ ├── opening_loc: (212,7)-(212,8) = "{" │ └── closing_loc: (212,18)-(212,19) = "}" - └── @ MultiWriteNode (location: (214,0)-(217,5)) - ├── lefts: (length: 2) - │ ├── @ LocalVariableTargetNode (location: (215,2)-(215,3)) - │ │ ├── name: :a - │ │ └── depth: 0 - │ └── @ LocalVariableTargetNode (location: (216,2)-(216,3)) - │ ├── name: :b - │ └── depth: 0 - ├── rest: ∅ - ├── rights: (length: 0) - ├── lparen_loc: (214,0)-(214,1) = "(" - ├── rparen_loc: (217,0)-(217,1) = ")" - ├── operator_loc: (217,2)-(217,3) = "=" - └── value: - @ CallNode (location: (217,4)-(217,5)) - ├── flags: variable_call, ignore_visibility - ├── receiver: ∅ - ├── call_operator_loc: ∅ - ├── name: :c - ├── message_loc: (217,4)-(217,5) = "c" - ├── opening_loc: ∅ - ├── arguments: ∅ - ├── closing_loc: ∅ - └── block: ∅ + ├── @ MultiWriteNode (location: (214,0)-(217,5)) + │ ├── flags: newline + │ ├── lefts: (length: 2) + │ │ ├── @ LocalVariableTargetNode (location: (215,2)-(215,3)) + │ │ │ ├── flags: ∅ + │ │ │ ├── name: :a + │ │ │ └── depth: 0 + │ │ └── @ LocalVariableTargetNode (location: (216,2)-(216,3)) + │ │ ├── flags: ∅ + │ │ ├── name: :b + │ │ └── depth: 0 + │ ├── rest: ∅ + │ ├── rights: (length: 0) + │ ├── lparen_loc: (214,0)-(214,1) = "(" + │ ├── rparen_loc: (217,0)-(217,1) = ")" + │ ├── operator_loc: (217,2)-(217,3) = "=" + │ └── value: + │ @ CallNode (location: (217,4)-(217,5)) + │ ├── flags: variable_call, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :c + │ ├── message_loc: (217,4)-(217,5) = "c" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ └── block: ∅ + ├── @ CaseMatchNode (location: (219,0)-(219,25)) + │ ├── flags: newline + │ ├── predicate: + │ │ @ ParenthesesNode (location: (219,5)-(219,7)) + │ │ ├── flags: ∅ + │ │ ├── body: ∅ + │ │ ├── opening_loc: (219,5)-(219,6) = "(" + │ │ └── closing_loc: (219,6)-(219,7) = ")" + │ ├── conditions: (length: 1) + │ │ └── @ InNode (location: (219,9)-(219,20)) + │ │ ├── flags: ∅ + │ │ ├── pattern: + │ │ │ @ ArrayPatternNode (location: (219,12)-(219,20)) + │ │ │ ├── flags: ∅ + │ │ │ ├── constant: ∅ + │ │ │ ├── requireds: (length: 2) + │ │ │ │ ├── @ LocalVariableTargetNode (location: (219,13)-(219,15)) + │ │ │ │ │ ├── flags: ∅ + │ │ │ │ │ ├── name: :_a + │ │ │ │ │ └── depth: 0 + │ │ │ │ └── @ LocalVariableTargetNode (location: (219,17)-(219,19)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── name: :_a + │ │ │ │ └── depth: 0 + │ │ │ ├── rest: ∅ + │ │ │ ├── posts: (length: 0) + │ │ │ ├── opening_loc: (219,12)-(219,13) = "[" + │ │ │ └── closing_loc: (219,19)-(219,20) = "]" + │ │ ├── statements: ∅ + │ │ ├── in_loc: (219,9)-(219,11) = "in" + │ │ └── then_loc: ∅ + │ ├── consequent: ∅ + │ ├── case_keyword_loc: (219,0)-(219,4) = "case" + │ └── end_keyword_loc: (219,22)-(219,25) = "end" + └── @ CaseMatchNode (location: (220,0)-(220,31)) + ├── flags: newline + ├── predicate: + │ @ ParenthesesNode (location: (220,5)-(220,7)) + │ ├── flags: ∅ + │ ├── body: ∅ + │ ├── opening_loc: (220,5)-(220,6) = "(" + │ └── closing_loc: (220,6)-(220,7) = ")" + ├── conditions: (length: 1) + │ └── @ InNode (location: (220,9)-(220,26)) + │ ├── flags: ∅ + │ ├── pattern: + │ │ @ ArrayPatternNode (location: (220,12)-(220,26)) + │ │ ├── flags: ∅ + │ │ ├── constant: ∅ + │ │ ├── requireds: (length: 2) + │ │ │ ├── @ HashPatternNode (location: (220,13)-(220,18)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── constant: ∅ + │ │ │ │ ├── elements: (length: 1) + │ │ │ │ │ └── @ AssocNode (location: (220,14)-(220,17)) + │ │ │ │ │ ├── flags: static_literal + │ │ │ │ │ ├── key: + │ │ │ │ │ │ @ SymbolNode (location: (220,14)-(220,16)) + │ │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding + │ │ │ │ │ │ ├── opening_loc: ∅ + │ │ │ │ │ │ ├── value_loc: (220,14)-(220,15) = "a" + │ │ │ │ │ │ ├── closing_loc: (220,15)-(220,16) = ":" + │ │ │ │ │ │ └── unescaped: "a" + │ │ │ │ │ ├── value: + │ │ │ │ │ │ @ IntegerNode (location: (220,16)-(220,17)) + │ │ │ │ │ │ ├── flags: static_literal, decimal + │ │ │ │ │ │ └── value: 1 + │ │ │ │ │ └── operator_loc: ∅ + │ │ │ │ ├── rest: ∅ + │ │ │ │ ├── opening_loc: (220,13)-(220,14) = "{" + │ │ │ │ └── closing_loc: (220,17)-(220,18) = "}" + │ │ │ └── @ HashPatternNode (location: (220,20)-(220,25)) + │ │ │ ├── flags: ∅ + │ │ │ ├── constant: ∅ + │ │ │ ├── elements: (length: 1) + │ │ │ │ └── @ AssocNode (location: (220,21)-(220,24)) + │ │ │ │ ├── flags: static_literal + │ │ │ │ ├── key: + │ │ │ │ │ @ SymbolNode (location: (220,21)-(220,23)) + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding + │ │ │ │ │ ├── opening_loc: ∅ + │ │ │ │ │ ├── value_loc: (220,21)-(220,22) = "a" + │ │ │ │ │ ├── closing_loc: (220,22)-(220,23) = ":" + │ │ │ │ │ └── unescaped: "a" + │ │ │ │ ├── value: + │ │ │ │ │ @ IntegerNode (location: (220,23)-(220,24)) + │ │ │ │ │ ├── flags: static_literal, decimal + │ │ │ │ │ └── value: 2 + │ │ │ │ └── operator_loc: ∅ + │ │ │ ├── rest: ∅ + │ │ │ ├── opening_loc: (220,20)-(220,21) = "{" + │ │ │ └── closing_loc: (220,24)-(220,25) = "}" + │ │ ├── rest: ∅ + │ │ ├── posts: (length: 0) + │ │ ├── opening_loc: (220,12)-(220,13) = "[" + │ │ └── closing_loc: (220,25)-(220,26) = "]" + │ ├── statements: ∅ + │ ├── in_loc: (220,9)-(220,11) = "in" + │ └── then_loc: ∅ + ├── consequent: ∅ + ├── case_keyword_loc: (220,0)-(220,4) = "case" + └── end_keyword_loc: (220,28)-(220,31) = "end" diff --git a/test/prism/snapshots/procs.txt b/test/prism/snapshots/procs.txt index 1329ae6a5f210e..a7602165144950 100644 --- a/test/prism/snapshots/procs.txt +++ b/test/prism/snapshots/procs.txt @@ -1,17 +1,22 @@ @ ProgramNode (location: (1,0)-(27,19)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(27,19)) + ├── flags: ∅ └── body: (length: 10) ├── @ LambdaNode (location: (1,0)-(1,21)) + │ ├── flags: newline │ ├── locals: [:a, :b, :c, :d] │ ├── operator_loc: (1,0)-(1,2) = "->" │ ├── opening_loc: (1,16)-(1,17) = "{" │ ├── closing_loc: (1,20)-(1,21) = "}" │ ├── parameters: │ │ @ BlockParametersNode (location: (1,3)-(1,15)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (1,4)-(1,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (1,4)-(1,5)) │ │ │ │ ├── flags: ∅ @@ -36,11 +41,14 @@ │ │ └── closing_loc: (1,14)-(1,15) = ")" │ └── body: │ @ StatementsNode (location: (1,18)-(1,19)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ LocalVariableReadNode (location: (1,18)-(1,19)) + │ ├── flags: newline │ ├── name: :b │ └── depth: 0 ├── @ LambdaNode (location: (3,0)-(5,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── operator_loc: (3,0)-(3,2) = "->" │ ├── opening_loc: (3,3)-(3,5) = "do" @@ -48,17 +56,20 @@ │ ├── parameters: ∅ │ └── body: │ @ BeginNode (location: (3,3)-(5,3)) + │ ├── flags: ∅ │ ├── begin_keyword_loc: ∅ │ ├── statements: ∅ │ ├── rescue_clause: ∅ │ ├── else_clause: ∅ │ ├── ensure_clause: │ │ @ EnsureNode (location: (4,0)-(5,3)) + │ │ ├── flags: ∅ │ │ ├── ensure_keyword_loc: (4,0)-(4,6) = "ensure" │ │ ├── statements: ∅ │ │ └── end_keyword_loc: (5,0)-(5,3) = "end" │ └── end_keyword_loc: (5,0)-(5,3) = "end" ├── @ LambdaNode (location: (7,0)-(11,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── operator_loc: (7,0)-(7,2) = "->" │ ├── opening_loc: (7,3)-(7,5) = "do" @@ -66,10 +77,12 @@ │ ├── parameters: ∅ │ └── body: │ @ BeginNode (location: (7,3)-(11,3)) + │ ├── flags: ∅ │ ├── begin_keyword_loc: ∅ │ ├── statements: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (8,0)-(8,6)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (8,0)-(8,6) = "rescue" │ │ ├── exceptions: (length: 0) │ │ ├── operator_loc: ∅ @@ -78,16 +91,19 @@ │ │ └── consequent: ∅ │ ├── else_clause: │ │ @ ElseNode (location: (9,0)-(10,6)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (9,0)-(9,4) = "else" │ │ ├── statements: ∅ │ │ └── end_keyword_loc: (10,0)-(10,6) = "ensure" │ ├── ensure_clause: │ │ @ EnsureNode (location: (10,0)-(11,3)) + │ │ ├── flags: ∅ │ │ ├── ensure_keyword_loc: (10,0)-(10,6) = "ensure" │ │ ├── statements: ∅ │ │ └── end_keyword_loc: (11,0)-(11,3) = "end" │ └── end_keyword_loc: (11,0)-(11,3) = "end" ├── @ LambdaNode (location: (13,0)-(13,10)) + │ ├── flags: newline │ ├── locals: [] │ ├── operator_loc: (13,0)-(13,2) = "->" │ ├── opening_loc: (13,3)-(13,4) = "{" @@ -95,9 +111,10 @@ │ ├── parameters: ∅ │ └── body: │ @ StatementsNode (location: (13,5)-(13,8)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (13,5)-(13,8)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -107,6 +124,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ LambdaNode (location: (15,0)-(15,15)) + │ ├── flags: newline │ ├── locals: [] │ ├── operator_loc: (15,0)-(15,2) = "->" │ ├── opening_loc: (15,3)-(15,5) = "do" @@ -114,9 +132,10 @@ │ ├── parameters: ∅ │ └── body: │ @ StatementsNode (location: (15,7)-(15,10)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (15,7)-(15,10)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -126,14 +145,17 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ LambdaNode (location: (17,0)-(17,29)) + │ ├── flags: newline │ ├── locals: [:a, :b, :c, :d, :e] │ ├── operator_loc: (17,0)-(17,2) = "->" │ ├── opening_loc: (17,24)-(17,25) = "{" │ ├── closing_loc: (17,28)-(17,29) = "}" │ ├── parameters: │ │ @ BlockParametersNode (location: (17,3)-(17,23)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (17,3)-(17,23)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (17,3)-(17,4)) │ │ │ │ ├── flags: ∅ @@ -146,7 +168,7 @@ │ │ │ │ ├── operator_loc: (17,8)-(17,9) = "=" │ │ │ │ └── value: │ │ │ │ @ IntegerNode (location: (17,10)-(17,11)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── rest: ∅ │ │ │ ├── posts: (length: 0) @@ -171,19 +193,24 @@ │ │ └── closing_loc: ∅ │ └── body: │ @ StatementsNode (location: (17,26)-(17,27)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ LocalVariableReadNode (location: (17,26)-(17,27)) + │ ├── flags: newline │ ├── name: :a │ └── depth: 0 ├── @ LambdaNode (location: (19,0)-(19,40)) + │ ├── flags: newline │ ├── locals: [:a, :b, :c, :d, :e, :f, :g] │ ├── operator_loc: (19,0)-(19,2) = "->" │ ├── opening_loc: (19,35)-(19,36) = "{" │ ├── closing_loc: (19,39)-(19,40) = "}" │ ├── parameters: │ │ @ BlockParametersNode (location: (19,3)-(19,34)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (19,4)-(19,33)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (19,4)-(19,5)) │ │ │ │ ├── flags: ∅ @@ -196,7 +223,7 @@ │ │ │ │ ├── operator_loc: (19,9)-(19,10) = "=" │ │ │ │ └── value: │ │ │ │ @ IntegerNode (location: (19,11)-(19,12)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── rest: │ │ │ │ @ RestParameterNode (location: (19,14)-(19,16)) @@ -231,19 +258,24 @@ │ │ └── closing_loc: (19,33)-(19,34) = ")" │ └── body: │ @ StatementsNode (location: (19,37)-(19,38)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ LocalVariableReadNode (location: (19,37)-(19,38)) + │ ├── flags: newline │ ├── name: :a │ └── depth: 0 ├── @ LambdaNode (location: (21,0)-(23,3)) + │ ├── flags: newline │ ├── locals: [:a, :b, :c, :d, :e, :f, :g] │ ├── operator_loc: (21,0)-(21,2) = "->" │ ├── opening_loc: (21,35)-(21,37) = "do" │ ├── closing_loc: (23,0)-(23,3) = "end" │ ├── parameters: │ │ @ BlockParametersNode (location: (21,3)-(21,34)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (21,4)-(21,33)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (21,4)-(21,5)) │ │ │ │ ├── flags: ∅ @@ -256,7 +288,7 @@ │ │ │ │ ├── operator_loc: (21,9)-(21,10) = "=" │ │ │ │ └── value: │ │ │ │ @ IntegerNode (location: (21,11)-(21,12)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── rest: │ │ │ │ @ RestParameterNode (location: (21,14)-(21,16)) @@ -291,19 +323,24 @@ │ │ └── closing_loc: (21,33)-(21,34) = ")" │ └── body: │ @ StatementsNode (location: (22,2)-(22,3)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ LocalVariableReadNode (location: (22,2)-(22,3)) + │ ├── flags: newline │ ├── name: :a │ └── depth: 0 ├── @ LambdaNode (location: (25,0)-(25,25)) + │ ├── flags: newline │ ├── locals: [:a] │ ├── operator_loc: (25,0)-(25,2) = "->" │ ├── opening_loc: (25,7)-(25,8) = "{" │ ├── closing_loc: (25,24)-(25,25) = "}" │ ├── parameters: │ │ @ BlockParametersNode (location: (25,3)-(25,6)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (25,4)-(25,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (25,4)-(25,5)) │ │ │ │ ├── flags: ∅ @@ -319,16 +356,20 @@ │ │ └── closing_loc: (25,5)-(25,6) = ")" │ └── body: │ @ StatementsNode (location: (25,9)-(25,23)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ LambdaNode (location: (25,9)-(25,23)) + │ ├── flags: newline │ ├── locals: [:b] │ ├── operator_loc: (25,9)-(25,11) = "->" │ ├── opening_loc: (25,14)-(25,15) = "{" │ ├── closing_loc: (25,22)-(25,23) = "}" │ ├── parameters: │ │ @ BlockParametersNode (location: (25,12)-(25,13)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (25,12)-(25,13)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (25,12)-(25,13)) │ │ │ │ ├── flags: ∅ @@ -344,11 +385,13 @@ │ │ └── closing_loc: ∅ │ └── body: │ @ StatementsNode (location: (25,16)-(25,21)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (25,16)-(25,21)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (25,16)-(25,17)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 1 │ ├── call_operator_loc: ∅ @@ -360,21 +403,26 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ LocalVariableReadNode (location: (25,20)-(25,21)) + │ │ ├── flags: ∅ │ │ ├── name: :b │ │ └── depth: 0 │ ├── closing_loc: ∅ │ └── block: ∅ └── @ LambdaNode (location: (27,0)-(27,19)) + ├── flags: newline ├── locals: [:a, :b, :c] ├── operator_loc: (27,0)-(27,2) = "->" ├── opening_loc: (27,16)-(27,17) = "{" ├── closing_loc: (27,18)-(27,19) = "}" ├── parameters: │ @ BlockParametersNode (location: (27,3)-(27,15)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (27,4)-(27,14)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ MultiTargetNode (location: (27,4)-(27,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 2) │ │ │ │ ├── @ RequiredParameterNode (location: (27,5)-(27,6)) │ │ │ │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/range_begin_open_exclusive.txt b/test/prism/snapshots/range_begin_open_exclusive.txt index a630b01ef19d05..e4ca315fc2b031 100644 --- a/test/prism/snapshots/range_begin_open_exclusive.txt +++ b/test/prism/snapshots/range_begin_open_exclusive.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,4)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,4)) + ├── flags: ∅ └── body: (length: 1) └── @ RangeNode (location: (1,0)-(1,4)) - ├── flags: exclude_end + ├── flags: newline, static_literal, exclude_end ├── left: ∅ ├── right: │ @ IntegerNode (location: (1,3)-(1,4)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 └── operator_loc: (1,0)-(1,3) = "..." diff --git a/test/prism/snapshots/range_begin_open_inclusive.txt b/test/prism/snapshots/range_begin_open_inclusive.txt index dc8ef0d2dbc00b..45aa88a5d1007c 100644 --- a/test/prism/snapshots/range_begin_open_inclusive.txt +++ b/test/prism/snapshots/range_begin_open_inclusive.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,3)) + ├── flags: ∅ └── body: (length: 1) └── @ RangeNode (location: (1,0)-(1,3)) - ├── flags: ∅ + ├── flags: newline, static_literal ├── left: ∅ ├── right: │ @ IntegerNode (location: (1,2)-(1,3)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 └── operator_loc: (1,0)-(1,2) = ".." diff --git a/test/prism/snapshots/range_end_open_exclusive.txt b/test/prism/snapshots/range_end_open_exclusive.txt index 17a75f89456fe8..d0d3f810a3718c 100644 --- a/test/prism/snapshots/range_end_open_exclusive.txt +++ b/test/prism/snapshots/range_end_open_exclusive.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,4)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,4)) + ├── flags: ∅ └── body: (length: 1) └── @ RangeNode (location: (1,0)-(1,4)) - ├── flags: exclude_end + ├── flags: newline, static_literal, exclude_end ├── left: │ @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── right: ∅ └── operator_loc: (1,1)-(1,4) = "..." diff --git a/test/prism/snapshots/range_end_open_inclusive.txt b/test/prism/snapshots/range_end_open_inclusive.txt index b49272d8cd1eba..8bfee649a3cd2e 100644 --- a/test/prism/snapshots/range_end_open_inclusive.txt +++ b/test/prism/snapshots/range_end_open_inclusive.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,3)) + ├── flags: ∅ └── body: (length: 1) └── @ RangeNode (location: (1,0)-(1,3)) - ├── flags: ∅ + ├── flags: newline, static_literal ├── left: │ @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── right: ∅ └── operator_loc: (1,1)-(1,3) = ".." diff --git a/test/prism/snapshots/ranges.txt b/test/prism/snapshots/ranges.txt index 2fffe805376040..81dbe3d901d827 100644 --- a/test/prism/snapshots/ranges.txt +++ b/test/prism/snapshots/ranges.txt @@ -1,49 +1,55 @@ @ ProgramNode (location: (1,0)-(49,7)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(49,7)) + ├── flags: ∅ └── body: (length: 25) ├── @ ParenthesesNode (location: (1,0)-(1,6)) + │ ├── flags: newline │ ├── body: │ │ @ StatementsNode (location: (1,1)-(1,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ RangeNode (location: (1,1)-(1,5)) - │ │ ├── flags: exclude_end + │ │ ├── flags: newline, static_literal, exclude_end │ │ ├── left: ∅ │ │ ├── right: │ │ │ @ IntegerNode (location: (1,4)-(1,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── operator_loc: (1,1)-(1,4) = "..." │ ├── opening_loc: (1,0)-(1,1) = "(" │ └── closing_loc: (1,5)-(1,6) = ")" ├── @ ParenthesesNode (location: (3,0)-(3,5)) + │ ├── flags: newline │ ├── body: │ │ @ StatementsNode (location: (3,1)-(3,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ RangeNode (location: (3,1)-(3,4)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline, static_literal │ │ ├── left: ∅ │ │ ├── right: │ │ │ @ IntegerNode (location: (3,3)-(3,4)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── operator_loc: (3,1)-(3,3) = ".." │ ├── opening_loc: (3,0)-(3,1) = "(" │ └── closing_loc: (3,4)-(3,5) = ")" ├── @ RangeNode (location: (5,0)-(5,5)) - │ ├── flags: exclude_end + │ ├── flags: newline, static_literal, exclude_end │ ├── left: │ │ @ IntegerNode (location: (5,0)-(5,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── right: │ │ @ IntegerNode (location: (5,4)-(5,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ └── operator_loc: (5,1)-(5,4) = "..." ├── @ CallNode (location: (7,0)-(7,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (7,0)-(7,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -64,22 +70,24 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ RangeNode (location: (7,4)-(7,8)) - │ │ ├── flags: exclude_end + │ │ ├── flags: static_literal, exclude_end │ │ ├── left: ∅ │ │ ├── right: │ │ │ @ IntegerNode (location: (7,7)-(7,8)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── operator_loc: (7,4)-(7,7) = "..." │ ├── closing_loc: (7,8)-(7,9) = "]" │ └── block: ∅ ├── @ HashNode (location: (9,0)-(9,15)) + │ ├── flags: newline │ ├── opening_loc: (9,0)-(9,1) = "{" │ ├── elements: (length: 1) │ │ └── @ AssocNode (location: (9,2)-(9,13)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ SymbolNode (location: (9,2)-(9,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (9,2)-(9,5) = "foo" │ │ │ ├── closing_loc: (9,5)-(9,6) = ":" @@ -103,37 +111,41 @@ │ │ └── operator_loc: ∅ │ └── closing_loc: (9,14)-(9,15) = "}" ├── @ ParenthesesNode (location: (11,0)-(11,6)) + │ ├── flags: newline │ ├── body: │ │ @ StatementsNode (location: (11,1)-(11,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ RangeNode (location: (11,1)-(11,5)) - │ │ ├── flags: exclude_end + │ │ ├── flags: newline, static_literal, exclude_end │ │ ├── left: │ │ │ @ IntegerNode (location: (11,1)-(11,2)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: ∅ │ │ └── operator_loc: (11,2)-(11,5) = "..." │ ├── opening_loc: (11,0)-(11,1) = "(" │ └── closing_loc: (11,5)-(11,6) = ")" ├── @ RangeNode (location: (13,0)-(13,4)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── left: │ │ @ IntegerNode (location: (13,0)-(13,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── right: │ │ @ IntegerNode (location: (13,3)-(13,4)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ └── operator_loc: (13,1)-(13,3) = ".." ├── @ HashNode (location: (15,0)-(15,14)) + │ ├── flags: newline │ ├── opening_loc: (15,0)-(15,1) = "{" │ ├── elements: (length: 1) │ │ └── @ AssocNode (location: (15,2)-(15,12)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ SymbolNode (location: (15,2)-(15,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (15,2)-(15,5) = "foo" │ │ │ ├── closing_loc: (15,5)-(15,6) = ":" @@ -157,58 +169,61 @@ │ │ └── operator_loc: ∅ │ └── closing_loc: (15,13)-(15,14) = "}" ├── @ ParenthesesNode (location: (17,0)-(17,5)) + │ ├── flags: newline │ ├── body: │ │ @ StatementsNode (location: (17,1)-(17,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ RangeNode (location: (17,1)-(17,4)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline, static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (17,1)-(17,2)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: ∅ │ │ └── operator_loc: (17,2)-(17,4) = ".." │ ├── opening_loc: (17,0)-(17,1) = "(" │ └── closing_loc: (17,4)-(17,5) = ")" ├── @ RangeNode (location: (19,0)-(19,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── left: │ │ @ IntegerNode (location: (19,0)-(19,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── right: │ │ @ RangeNode (location: (19,5)-(19,8)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: ∅ │ │ ├── right: │ │ │ @ IntegerNode (location: (19,7)-(19,8)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── operator_loc: (19,5)-(19,7) = ".." │ └── operator_loc: (19,2)-(19,4) = ".." ├── @ AndNode (location: (21,0)-(21,8)) + │ ├── flags: newline │ ├── left: │ │ @ RangeNode (location: (21,0)-(21,3)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (21,0)-(21,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: ∅ │ │ └── operator_loc: (21,1)-(21,3) = ".." │ ├── right: │ │ @ IntegerNode (location: (21,7)-(21,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ └── operator_loc: (21,4)-(21,6) = "&&" ├── @ CallNode (location: (23,0)-(23,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ RangeNode (location: (23,0)-(23,3)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (23,0)-(23,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: ∅ │ │ └── operator_loc: (23,1)-(23,3) = ".." @@ -221,18 +236,18 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (23,7)-(23,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (25,0)-(25,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ RangeNode (location: (25,0)-(25,3)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (25,0)-(25,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: ∅ │ │ └── operator_loc: (25,1)-(25,3) = ".." @@ -245,18 +260,18 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (25,7)-(25,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (27,0)-(27,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ RangeNode (location: (27,0)-(27,3)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (27,0)-(27,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: ∅ │ │ └── operator_loc: (27,1)-(27,3) = ".." @@ -269,18 +284,18 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (27,8)-(27,9)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (29,0)-(29,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ RangeNode (location: (29,0)-(29,3)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (29,0)-(29,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: ∅ │ │ └── operator_loc: (29,1)-(29,3) = ".." @@ -293,18 +308,18 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (29,8)-(29,9)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (31,0)-(31,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ RangeNode (location: (31,0)-(31,3)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (31,0)-(31,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: ∅ │ │ └── operator_loc: (31,1)-(31,3) = ".." @@ -317,18 +332,18 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (31,7)-(31,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (33,0)-(33,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ RangeNode (location: (33,0)-(33,3)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (33,0)-(33,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: ∅ │ │ └── operator_loc: (33,1)-(33,3) = ".." @@ -341,18 +356,18 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (33,7)-(33,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (35,0)-(35,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ RangeNode (location: (35,0)-(35,3)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (35,0)-(35,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: ∅ │ │ └── operator_loc: (35,1)-(35,3) = ".." @@ -365,18 +380,18 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (35,6)-(35,7)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (37,0)-(37,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ RangeNode (location: (37,0)-(37,3)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (37,0)-(37,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: ∅ │ │ └── operator_loc: (37,1)-(37,3) = ".." @@ -389,18 +404,18 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (37,6)-(37,7)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (39,0)-(39,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ RangeNode (location: (39,0)-(39,3)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (39,0)-(39,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: ∅ │ │ └── operator_loc: (39,1)-(39,3) = ".." @@ -413,18 +428,18 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (39,7)-(39,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (41,0)-(41,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ RangeNode (location: (41,0)-(41,3)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (41,0)-(41,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: ∅ │ │ └── operator_loc: (41,1)-(41,3) = ".." @@ -437,18 +452,18 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (41,7)-(41,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (43,0)-(43,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ RangeNode (location: (43,0)-(43,3)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (43,0)-(43,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: ∅ │ │ └── operator_loc: (43,1)-(43,3) = ".." @@ -461,18 +476,18 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (43,7)-(43,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (45,0)-(45,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ RangeNode (location: (45,0)-(45,3)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (45,0)-(45,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: ∅ │ │ └── operator_loc: (45,1)-(45,3) = ".." @@ -485,22 +500,22 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (45,7)-(45,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ RangeNode (location: (47,0)-(47,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── left: │ │ @ IntegerNode (location: (47,0)-(47,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── right: │ │ @ CallNode (location: (47,4)-(47,7)) │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ IntegerNode (location: (47,6)-(47,7)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── call_operator_loc: ∅ │ │ ├── name: :+@ @@ -511,17 +526,17 @@ │ │ └── block: ∅ │ └── operator_loc: (47,1)-(47,3) = ".." └── @ RangeNode (location: (49,0)-(49,7)) - ├── flags: ∅ + ├── flags: newline ├── left: │ @ IntegerNode (location: (49,0)-(49,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── right: │ @ CallNode (location: (49,4)-(49,7)) │ ├── flags: ∅ │ ├── receiver: │ │ @ IntegerNode (location: (49,6)-(49,7)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── call_operator_loc: ∅ │ ├── name: :-@ diff --git a/test/prism/snapshots/regex.txt b/test/prism/snapshots/regex.txt index d4d153e8d516e0..2f4b986d97ff80 100644 --- a/test/prism/snapshots/regex.txt +++ b/test/prism/snapshots/regex.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(46,32)) +├── flags: ∅ ├── locals: [:foo, :ab, :abc, :a] └── statements: @ StatementsNode (location: (1,0)-(46,32)) + ├── flags: ∅ └── body: (length: 25) ├── @ CallNode (location: (1,0)-(1,9)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -15,7 +17,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ RegularExpressionNode (location: (1,4)-(1,9)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,4)-(1,5) = "/" │ │ ├── content_loc: (1,5)-(1,8) = "bar" │ │ ├── closing_loc: (1,8)-(1,9) = "/" @@ -23,47 +25,51 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ RegularExpressionNode (location: (3,0)-(3,8)) - │ ├── flags: ignore_case, forced_us_ascii_encoding + │ ├── flags: newline, static_literal, ignore_case, forced_us_ascii_encoding │ ├── opening_loc: (3,0)-(3,3) = "%r{" │ ├── content_loc: (3,3)-(3,6) = "abc" │ ├── closing_loc: (3,6)-(3,8) = "}i" │ └── unescaped: "abc" ├── @ RegularExpressionNode (location: (5,0)-(5,5)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (5,0)-(5,1) = "/" │ ├── content_loc: (5,1)-(5,4) = "a\\b" │ ├── closing_loc: (5,4)-(5,5) = "/" │ └── unescaped: "a\\b" ├── @ InterpolatedRegularExpressionNode (location: (7,0)-(7,11)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (7,0)-(7,1) = "/" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (7,1)-(7,5)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (7,1)-(7,5) = "aaa " │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "aaa " │ │ └── @ EmbeddedVariableNode (location: (7,5)-(7,10)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (7,5)-(7,6) = "#" │ │ └── variable: │ │ @ GlobalVariableReadNode (location: (7,6)-(7,10)) + │ │ ├── flags: ∅ │ │ └── name: :$bbb │ └── closing_loc: (7,10)-(7,11) = "/" ├── @ InterpolatedRegularExpressionNode (location: (9,0)-(9,16)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (9,0)-(9,1) = "/" │ ├── parts: (length: 3) │ │ ├── @ StringNode (location: (9,1)-(9,5)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (9,1)-(9,5) = "aaa " │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "aaa " │ │ ├── @ EmbeddedStatementsNode (location: (9,5)-(9,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (9,5)-(9,7) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (9,7)-(9,10)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (9,7)-(9,10)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -77,22 +83,23 @@ │ │ │ │ └── block: ∅ │ │ │ └── closing_loc: (9,10)-(9,11) = "}" │ │ └── @ StringNode (location: (9,11)-(9,15)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (9,11)-(9,15) = " ccc" │ │ ├── closing_loc: ∅ │ │ └── unescaped: " ccc" │ └── closing_loc: (9,15)-(9,16) = "/" ├── @ ArrayNode (location: (11,0)-(11,27)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 2) │ │ ├── @ MatchWriteNode (location: (11,1)-(11,21)) + │ │ │ ├── flags: ∅ │ │ │ ├── call: │ │ │ │ @ CallNode (location: (11,1)-(11,21)) │ │ │ │ ├── flags: ∅ │ │ │ │ ├── receiver: │ │ │ │ │ @ RegularExpressionNode (location: (11,1)-(11,14)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: (11,1)-(11,2) = "/" │ │ │ │ │ ├── content_loc: (11,2)-(11,13) = "(?bar)" │ │ │ │ │ ├── closing_loc: (11,13)-(11,14) = "/" @@ -119,48 +126,50 @@ │ │ │ │ └── block: ∅ │ │ │ └── targets: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (11,5)-(11,8)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :foo │ │ │ └── depth: 0 │ │ └── @ LocalVariableReadNode (location: (11,23)-(11,26)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ └── depth: 0 │ ├── opening_loc: (11,0)-(11,1) = "[" │ └── closing_loc: (11,26)-(11,27) = "]" ├── @ RegularExpressionNode (location: (13,0)-(13,6)) - │ ├── flags: ignore_case, forced_us_ascii_encoding + │ ├── flags: newline, static_literal, ignore_case, forced_us_ascii_encoding │ ├── opening_loc: (13,0)-(13,1) = "/" │ ├── content_loc: (13,1)-(13,4) = "abc" │ ├── closing_loc: (13,4)-(13,6) = "/i" │ └── unescaped: "abc" ├── @ RegularExpressionNode (location: (15,0)-(15,26)) - │ ├── flags: ignore_case, forced_us_ascii_encoding + │ ├── flags: newline, static_literal, ignore_case, forced_us_ascii_encoding │ ├── opening_loc: (15,0)-(15,3) = "%r/" │ ├── content_loc: (15,3)-(15,24) = "[a-z$._?][\\w$.?\#@~]*:" │ ├── closing_loc: (15,24)-(15,26) = "/i" │ └── unescaped: "[a-z$._?][\\w$.?\#@~]*:" ├── @ RegularExpressionNode (location: (17,0)-(17,37)) - │ ├── flags: ignore_case, forced_us_ascii_encoding + │ ├── flags: newline, static_literal, ignore_case, forced_us_ascii_encoding │ ├── opening_loc: (17,0)-(17,3) = "%r/" │ ├── content_loc: (17,3)-(17,35) = "([a-z$._?][\\w$.?\#@~]*)(\\s+)(equ)" │ ├── closing_loc: (17,35)-(17,37) = "/i" │ └── unescaped: "([a-z$._?][\\w$.?\#@~]*)(\\s+)(equ)" ├── @ RegularExpressionNode (location: (19,0)-(19,25)) - │ ├── flags: ignore_case, forced_us_ascii_encoding + │ ├── flags: newline, static_literal, ignore_case, forced_us_ascii_encoding │ ├── opening_loc: (19,0)-(19,3) = "%r/" │ ├── content_loc: (19,3)-(19,23) = "[a-z$._?][\\w$.?\#@~]*" │ ├── closing_loc: (19,23)-(19,25) = "/i" │ └── unescaped: "[a-z$._?][\\w$.?\#@~]*" ├── @ RegularExpressionNode (location: (21,0)-(24,1)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (21,0)-(21,3) = "%r(" │ ├── content_loc: (21,3)-(24,0) = "\n(?:[\#$%_']|\\(\\)|\\(,\\)|\\[\\]|[0-9])*\n (?:[\#$%_']+)\n" │ ├── closing_loc: (24,0)-(24,1) = ")" │ └── unescaped: "\n(?:[\#$%_']|\\(\\)|\\(,\\)|\\[\\]|[0-9])*\n (?:[\#$%_']+)\n" ├── @ CallNode (location: (26,0)-(26,16)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ RegularExpressionNode (location: (26,0)-(26,8)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (26,0)-(26,1) = "/" │ │ ├── content_loc: (26,1)-(26,7) = "(?#\\))" │ │ ├── closing_loc: (26,7)-(26,8) = "/" @@ -182,25 +191,27 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ RegularExpressionNode (location: (28,0)-(28,9)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (28,0)-(28,3) = "%r#" │ ├── content_loc: (28,3)-(28,8) = "pound" │ ├── closing_loc: (28,8)-(28,9) = "#" │ └── unescaped: "pound" ├── @ InterpolatedRegularExpressionNode (location: (30,0)-(30,13)) - │ ├── flags: once + │ ├── flags: newline, once │ ├── opening_loc: (30,0)-(30,1) = "/" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (30,1)-(30,5)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (30,1)-(30,5) = "aaa " │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "aaa " │ │ └── @ EmbeddedStatementsNode (location: (30,5)-(30,11)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (30,5)-(30,7) = "\#{" │ │ ├── statements: │ │ │ @ StatementsNode (location: (30,7)-(30,10)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (30,7)-(30,10)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -215,12 +226,13 @@ │ │ └── closing_loc: (30,10)-(30,11) = "}" │ └── closing_loc: (30,11)-(30,13) = "/o" ├── @ MatchWriteNode (location: (32,0)-(33,10)) + │ ├── flags: newline │ ├── call: │ │ @ CallNode (location: (32,0)-(33,10)) │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ RegularExpressionNode (location: (32,0)-(33,4)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (32,0)-(32,1) = "/" │ │ │ ├── content_loc: (32,1)-(33,3) = "(?)" │ │ │ ├── closing_loc: (33,3)-(33,4) = "/" @@ -243,18 +255,21 @@ │ │ └── block: ∅ │ └── targets: (length: 1) │ └── @ LocalVariableTargetNode (location: (32,0)-(33,4)) + │ ├── flags: ∅ │ ├── name: :ab │ └── depth: 0 ├── @ LocalVariableReadNode (location: (33,12)-(33,14)) + │ ├── flags: newline │ ├── name: :ab │ └── depth: 0 ├── @ MatchWriteNode (location: (35,0)-(35,24)) + │ ├── flags: newline │ ├── call: │ │ @ CallNode (location: (35,0)-(35,24)) │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ RegularExpressionNode (location: (35,0)-(35,18)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (35,0)-(35,1) = "/" │ │ │ ├── content_loc: (35,1)-(35,17) = "(?)(?)" │ │ │ ├── closing_loc: (35,17)-(35,18) = "/" @@ -277,16 +292,18 @@ │ │ └── block: ∅ │ └── targets: (length: 1) │ └── @ LocalVariableTargetNode (location: (35,4)-(35,7)) + │ ├── flags: ∅ │ ├── name: :abc │ └── depth: 0 ├── @ LocalVariableReadNode (location: (35,26)-(35,29)) + │ ├── flags: newline │ ├── name: :abc │ └── depth: 0 ├── @ CallNode (location: (37,0)-(37,16)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ RegularExpressionNode (location: (37,0)-(37,10)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (37,0)-(37,1) = "/" │ │ ├── content_loc: (37,1)-(37,9) = "(?)" │ │ ├── closing_loc: (37,9)-(37,10) = "/" @@ -308,16 +325,17 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ LocalVariableWriteNode (location: (39,0)-(39,5)) + │ ├── flags: newline │ ├── name: :a │ ├── depth: 0 │ ├── name_loc: (39,0)-(39,1) = "a" │ ├── value: │ │ @ IntegerNode (location: (39,4)-(39,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (39,2)-(39,3) = "=" ├── @ CallNode (location: (40,0)-(40,24)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -327,18 +345,21 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (40,4)-(40,24)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (40,6)-(40,22)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ MatchWriteNode (location: (40,6)-(40,22)) + │ │ ├── flags: newline │ │ ├── call: │ │ │ @ CallNode (location: (40,6)-(40,22)) │ │ │ ├── flags: ∅ │ │ │ ├── receiver: │ │ │ │ @ RegularExpressionNode (location: (40,6)-(40,14)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (40,6)-(40,7) = "/" │ │ │ │ ├── content_loc: (40,7)-(40,13) = "(?)" │ │ │ │ ├── closing_loc: (40,13)-(40,14) = "/" @@ -365,17 +386,19 @@ │ │ │ └── block: ∅ │ │ └── targets: (length: 1) │ │ └── @ LocalVariableTargetNode (location: (40,10)-(40,11)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 1 │ ├── opening_loc: (40,4)-(40,5) = "{" │ └── closing_loc: (40,23)-(40,24) = "}" ├── @ MatchWriteNode (location: (42,0)-(42,16)) + │ ├── flags: newline │ ├── call: │ │ @ CallNode (location: (42,0)-(42,16)) │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ RegularExpressionNode (location: (42,0)-(42,10)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (42,0)-(42,1) = "/" │ │ │ ├── content_loc: (42,1)-(42,9) = "(?)" │ │ │ ├── closing_loc: (42,9)-(42,10) = "/" @@ -398,13 +421,14 @@ │ │ └── block: ∅ │ └── targets: (length: 1) │ └── @ LocalVariableTargetNode (location: (42,4)-(42,7)) + │ ├── flags: ∅ │ ├── name: :foo │ └── depth: 0 ├── @ CallNode (location: (43,0)-(43,16)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ RegularExpressionNode (location: (43,0)-(43,10)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (43,0)-(43,1) = "/" │ │ ├── content_loc: (43,1)-(43,9) = "(?)" │ │ ├── closing_loc: (43,9)-(43,10) = "/" @@ -426,10 +450,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (45,0)-(45,16)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ RegularExpressionNode (location: (45,0)-(45,10)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (45,0)-(45,1) = "/" │ │ ├── content_loc: (45,1)-(45,9) = "(?)" │ │ ├── closing_loc: (45,9)-(45,10) = "/" @@ -451,11 +475,13 @@ │ ├── closing_loc: ∅ │ └── block: ∅ └── @ DefNode (location: (46,0)-(46,32)) + ├── flags: newline ├── name: :foo ├── name_loc: (46,4)-(46,7) = "foo" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (46,8)-(46,12)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ @@ -469,14 +495,16 @@ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (46,16)-(46,32)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ MatchWriteNode (location: (46,16)-(46,32)) + │ ├── flags: newline │ ├── call: │ │ @ CallNode (location: (46,16)-(46,32)) │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ RegularExpressionNode (location: (46,16)-(46,26)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (46,16)-(46,17) = "/" │ │ │ ├── content_loc: (46,17)-(46,25) = "(?)" │ │ │ ├── closing_loc: (46,25)-(46,26) = "/" @@ -499,6 +527,7 @@ │ │ └── block: ∅ │ └── targets: (length: 1) │ └── @ LocalVariableTargetNode (location: (46,20)-(46,23)) + │ ├── flags: ∅ │ ├── name: :nil │ └── depth: 0 ├── locals: [:nil] diff --git a/test/prism/snapshots/regex_char_width.txt b/test/prism/snapshots/regex_char_width.txt index 6bf2169b2f3e5c..664e4e8850a254 100644 --- a/test/prism/snapshots/regex_char_width.txt +++ b/test/prism/snapshots/regex_char_width.txt @@ -1,15 +1,18 @@ @ ProgramNode (location: (2,0)-(3,6)) +├── flags: ∅ ├── locals: [:a, :b] └── statements: @ StatementsNode (location: (2,0)-(3,6)) + ├── flags: ∅ └── body: (length: 2) ├── @ MatchWriteNode (location: (2,0)-(2,36)) + │ ├── flags: newline │ ├── call: │ │ @ CallNode (location: (2,0)-(2,36)) │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ RegularExpressionNode (location: (2,0)-(2,22)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: static_literal │ │ │ ├── opening_loc: (2,0)-(2,1) = "/" │ │ │ ├── content_loc: (2,1)-(2,21) = "\x{E285}\xA7(?.)\x{E285}\xA9(?.)" │ │ │ ├── closing_loc: (2,21)-(2,22) = "/" @@ -32,18 +35,22 @@ │ │ └── block: ∅ │ └── targets: (length: 2) │ ├── @ LocalVariableTargetNode (location: (2,7)-(2,8)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ └── @ LocalVariableTargetNode (location: (2,17)-(2,18)) + │ ├── flags: ∅ │ ├── name: :b │ └── depth: 0 └── @ ArrayNode (location: (3,0)-(3,6)) - ├── flags: ∅ + ├── flags: newline ├── elements: (length: 2) │ ├── @ LocalVariableReadNode (location: (3,1)-(3,2)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ └── @ LocalVariableReadNode (location: (3,4)-(3,5)) + │ ├── flags: ∅ │ ├── name: :b │ └── depth: 0 ├── opening_loc: (3,0)-(3,1) = "[" diff --git a/test/prism/snapshots/repeat_parameters.txt b/test/prism/snapshots/repeat_parameters.txt index fd98294ce6150e..1dbf120e9cbbac 100644 --- a/test/prism/snapshots/repeat_parameters.txt +++ b/test/prism/snapshots/repeat_parameters.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(38,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(38,3)) + ├── flags: ∅ └── body: (length: 13) ├── @ DefNode (location: (1,0)-(2,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (1,4)-(1,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,8)-(1,12)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 2) │ │ │ ├── @ RequiredParameterNode (location: (1,8)-(1,9)) │ │ │ │ ├── flags: ∅ @@ -31,11 +35,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (2,0)-(2,3) = "end" ├── @ DefNode (location: (4,0)-(5,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (4,4)-(4,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (4,8)-(4,15)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 3) │ │ │ ├── @ RequiredParameterNode (location: (4,8)-(4,9)) │ │ │ │ ├── flags: ∅ @@ -61,11 +67,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (5,0)-(5,3) = "end" ├── @ DefNode (location: (7,0)-(8,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (7,4)-(7,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (7,8)-(7,19)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 4) │ │ │ ├── @ RequiredParameterNode (location: (7,8)-(7,9)) │ │ │ │ ├── flags: ∅ @@ -94,11 +102,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (8,0)-(8,3) = "end" ├── @ DefNode (location: (10,0)-(11,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (10,4)-(10,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (10,8)-(10,23)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 5) │ │ │ ├── @ RequiredParameterNode (location: (10,8)-(10,9)) │ │ │ │ ├── flags: ∅ @@ -130,22 +140,26 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (11,0)-(11,3) = "end" ├── @ DefNode (location: (13,0)-(14,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (13,4)-(13,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (13,8)-(13,35)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 3) │ │ │ ├── @ RequiredParameterNode (location: (13,8)-(13,9)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :a │ │ │ ├── @ MultiTargetNode (location: (13,11)-(13,22)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── lefts: (length: 1) │ │ │ │ │ └── @ RequiredParameterNode (location: (13,12)-(13,13)) │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── name: :b │ │ │ │ ├── rest: │ │ │ │ │ @ SplatNode (location: (13,15)-(13,18)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── operator_loc: (13,15)-(13,16) = "*" │ │ │ │ │ └── expression: │ │ │ │ │ @ RequiredParameterNode (location: (13,16)-(13,18)) @@ -158,12 +172,14 @@ │ │ │ │ ├── lparen_loc: (13,11)-(13,12) = "(" │ │ │ │ └── rparen_loc: (13,21)-(13,22) = ")" │ │ │ └── @ MultiTargetNode (location: (13,24)-(13,35)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (13,25)-(13,26)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :e │ │ │ ├── rest: │ │ │ │ @ SplatNode (location: (13,28)-(13,31)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (13,28)-(13,29) = "*" │ │ │ │ └── expression: │ │ │ │ @ RequiredParameterNode (location: (13,29)-(13,31)) @@ -190,11 +206,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (14,0)-(14,3) = "end" ├── @ DefNode (location: (16,0)-(17,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (16,4)-(16,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (16,8)-(16,20)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 4) │ │ │ ├── @ RequiredParameterNode (location: (16,8)-(16,10)) │ │ │ │ ├── flags: ∅ @@ -223,19 +241,23 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (17,0)-(17,3) = "end" ├── @ DefNode (location: (19,0)-(20,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (19,4)-(19,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (19,8)-(19,32)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 2) │ │ │ ├── @ MultiTargetNode (location: (19,8)-(19,19)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── lefts: (length: 1) │ │ │ │ │ └── @ RequiredParameterNode (location: (19,9)-(19,10)) │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── name: :a │ │ │ │ ├── rest: │ │ │ │ │ @ SplatNode (location: (19,12)-(19,15)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── operator_loc: (19,12)-(19,13) = "*" │ │ │ │ │ └── expression: │ │ │ │ │ @ RequiredParameterNode (location: (19,13)-(19,15)) @@ -248,12 +270,14 @@ │ │ │ │ ├── lparen_loc: (19,8)-(19,9) = "(" │ │ │ │ └── rparen_loc: (19,18)-(19,19) = ")" │ │ │ └── @ MultiTargetNode (location: (19,21)-(19,32)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (19,22)-(19,23)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :d │ │ │ ├── rest: │ │ │ │ @ SplatNode (location: (19,25)-(19,28)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (19,25)-(19,26) = "*" │ │ │ │ └── expression: │ │ │ │ @ RequiredParameterNode (location: (19,26)-(19,28)) @@ -280,11 +304,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (20,0)-(20,3) = "end" ├── @ DefNode (location: (22,0)-(23,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (22,4)-(22,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (22,8)-(22,22)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 2) │ │ │ ├── @ OptionalParameterNode (location: (22,8)-(22,14)) @@ -294,7 +320,7 @@ │ │ │ │ ├── operator_loc: (22,11)-(22,12) = "=" │ │ │ │ └── value: │ │ │ │ @ IntegerNode (location: (22,13)-(22,14)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── @ OptionalParameterNode (location: (22,16)-(22,22)) │ │ │ ├── flags: repeated_parameter @@ -303,7 +329,7 @@ │ │ │ ├── operator_loc: (22,19)-(22,20) = "=" │ │ │ └── value: │ │ │ @ IntegerNode (location: (22,21)-(22,22)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) @@ -319,11 +345,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (23,0)-(23,3) = "end" ├── @ DefNode (location: (25,0)-(26,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (25,4)-(25,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (25,8)-(25,16)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -348,11 +376,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (26,0)-(26,3) = "end" ├── @ DefNode (location: (28,0)-(29,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (28,4)-(28,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (28,8)-(28,20)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -364,7 +394,7 @@ │ │ │ │ ├── name_loc: (28,8)-(28,11) = "_a:" │ │ │ │ └── value: │ │ │ │ @ IntegerNode (location: (28,12)-(28,13)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── @ OptionalKeywordParameterNode (location: (28,15)-(28,20)) │ │ │ ├── flags: repeated_parameter @@ -372,7 +402,7 @@ │ │ │ ├── name_loc: (28,15)-(28,18) = "_a:" │ │ │ └── value: │ │ │ @ IntegerNode (location: (28,19)-(28,20)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── keyword_rest: ∅ │ │ └── block: ∅ @@ -385,11 +415,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (29,0)-(29,3) = "end" ├── @ DefNode (location: (31,0)-(32,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (31,4)-(31,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (31,8)-(31,16)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (31,8)-(31,10)) │ │ │ ├── flags: ∅ @@ -414,11 +446,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (32,0)-(32,3) = "end" ├── @ DefNode (location: (34,0)-(35,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (34,4)-(34,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (34,8)-(34,15)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (34,8)-(34,10)) │ │ │ ├── flags: ∅ @@ -443,11 +477,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (35,0)-(35,3) = "end" └── @ DefNode (location: (37,0)-(38,3)) + ├── flags: newline ├── name: :foo ├── name_loc: (37,4)-(37,7) = "foo" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (37,8)-(37,15)) + │ ├── flags: ∅ │ ├── requireds: (length: 1) │ │ └── @ RequiredParameterNode (location: (37,8)-(37,10)) │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/rescue.txt b/test/prism/snapshots/rescue.txt index 390b08ae0efcd2..53a49fbb47063b 100644 --- a/test/prism/snapshots/rescue.txt +++ b/test/prism/snapshots/rescue.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(35,18)) +├── flags: ∅ ├── locals: [:a, :z] └── statements: @ StatementsNode (location: (1,0)-(35,18)) + ├── flags: ∅ └── body: (length: 14) ├── @ RescueModifierNode (location: (1,0)-(1,14)) + │ ├── flags: newline │ ├── expression: │ │ @ CallNode (location: (1,0)-(1,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -18,7 +21,9 @@ │ ├── keyword_loc: (1,4)-(1,10) = "rescue" │ └── rescue_expression: │ @ NilNode (location: (1,11)-(1,14)) + │ └── flags: static_literal ├── @ RescueModifierNode (location: (3,0)-(4,3)) + │ ├── flags: newline │ ├── expression: │ │ @ CallNode (location: (3,0)-(3,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -33,8 +38,9 @@ │ ├── keyword_loc: (3,4)-(3,10) = "rescue" │ └── rescue_expression: │ @ NilNode (location: (4,0)-(4,3)) + │ └── flags: static_literal ├── @ CallNode (location: (6,0)-(6,24)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -44,23 +50,28 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (6,4)-(6,24)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (6,6)-(6,22)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ RescueModifierNode (location: (6,6)-(6,22)) + │ │ ├── flags: newline │ │ ├── expression: │ │ │ @ BreakNode (location: (6,6)-(6,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── arguments: ∅ │ │ │ └── keyword_loc: (6,6)-(6,11) = "break" │ │ ├── keyword_loc: (6,12)-(6,18) = "rescue" │ │ └── rescue_expression: │ │ @ NilNode (location: (6,19)-(6,22)) + │ │ └── flags: static_literal │ ├── opening_loc: (6,4)-(6,5) = "{" │ └── closing_loc: (6,23)-(6,24) = "}" ├── @ CallNode (location: (8,0)-(8,23)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -70,22 +81,28 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (8,4)-(8,23)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (8,6)-(8,21)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ RescueModifierNode (location: (8,6)-(8,21)) + │ │ ├── flags: newline │ │ ├── expression: │ │ │ @ NextNode (location: (8,6)-(8,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── arguments: ∅ │ │ │ └── keyword_loc: (8,6)-(8,10) = "next" │ │ ├── keyword_loc: (8,11)-(8,17) = "rescue" │ │ └── rescue_expression: │ │ @ NilNode (location: (8,18)-(8,21)) + │ │ └── flags: static_literal │ ├── opening_loc: (8,4)-(8,5) = "{" │ └── closing_loc: (8,22)-(8,23) = "}" ├── @ RescueModifierNode (location: (10,0)-(10,17)) + │ ├── flags: newline │ ├── expression: │ │ @ ReturnNode (location: (10,0)-(10,6)) │ │ ├── flags: ∅ @@ -94,7 +111,9 @@ │ ├── keyword_loc: (10,7)-(10,13) = "rescue" │ └── rescue_expression: │ @ NilNode (location: (10,14)-(10,17)) + │ └── flags: static_literal ├── @ RescueModifierNode (location: (12,0)-(12,19)) + │ ├── flags: newline │ ├── expression: │ │ @ CallNode (location: (12,0)-(12,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -109,14 +128,17 @@ │ ├── keyword_loc: (12,4)-(12,10) = "rescue" │ └── rescue_expression: │ @ OrNode (location: (12,11)-(12,19)) + │ ├── flags: ∅ │ ├── left: │ │ @ NilNode (location: (12,11)-(12,14)) + │ │ └── flags: static_literal │ ├── right: │ │ @ IntegerNode (location: (12,18)-(12,19)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (12,15)-(12,17) = "||" ├── @ RescueModifierNode (location: (14,0)-(14,22)) + │ ├── flags: newline │ ├── expression: │ │ @ CallNode (location: (14,0)-(14,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -131,34 +153,41 @@ │ ├── keyword_loc: (14,4)-(14,10) = "rescue" │ └── rescue_expression: │ @ IfNode (location: (14,11)-(14,22)) + │ ├── flags: newline │ ├── if_keyword_loc: ∅ │ ├── predicate: │ │ @ NilNode (location: (14,11)-(14,14)) + │ │ └── flags: static_literal │ ├── then_keyword_loc: (14,15)-(14,16) = "?" │ ├── statements: │ │ @ StatementsNode (location: (14,17)-(14,18)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (14,17)-(14,18)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 1 │ ├── consequent: │ │ @ ElseNode (location: (14,19)-(14,22)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (14,19)-(14,20) = ":" │ │ ├── statements: │ │ │ @ StatementsNode (location: (14,21)-(14,22)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ IntegerNode (location: (14,21)-(14,22)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: newline, static_literal, decimal │ │ │ └── value: 2 │ │ └── end_keyword_loc: ∅ │ └── end_keyword_loc: ∅ ├── @ BeginNode (location: (16,0)-(16,24)) + │ ├── flags: newline │ ├── begin_keyword_loc: (16,0)-(16,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (16,7)-(16,8)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (16,7)-(16,8)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -169,9 +198,11 @@ │ │ └── block: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (16,10)-(16,19)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (16,10)-(16,16) = "rescue" │ │ ├── exceptions: (length: 1) │ │ │ └── @ SplatNode (location: (16,17)-(16,19)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (16,17)-(16,18) = "*" │ │ │ └── expression: │ │ │ @ CallNode (location: (16,18)-(16,19)) @@ -192,7 +223,7 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (16,21)-(16,24) = "end" ├── @ CallNode (location: (18,0)-(20,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -202,11 +233,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (18,4)-(20,3)) + │ ├── flags: ∅ │ ├── locals: [:x] │ ├── parameters: │ │ @ BlockParametersNode (location: (18,7)-(18,10)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (18,8)-(18,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (18,8)-(18,9)) │ │ │ │ ├── flags: ∅ @@ -222,8 +256,10 @@ │ │ └── closing_loc: (18,9)-(18,10) = "|" │ ├── body: │ │ @ StatementsNode (location: (19,2)-(19,40)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ RescueModifierNode (location: (19,2)-(19,40)) + │ │ ├── flags: newline │ │ ├── expression: │ │ │ @ CallNode (location: (19,2)-(19,8)) │ │ │ ├── flags: ignore_visibility @@ -285,14 +321,17 @@ │ ├── opening_loc: (18,4)-(18,6) = "do" │ └── closing_loc: (20,0)-(20,3) = "end" ├── @ IfNode (location: (22,0)-(24,3)) + │ ├── flags: newline │ ├── if_keyword_loc: (22,0)-(22,2) = "if" │ ├── predicate: │ │ @ LocalVariableWriteNode (location: (22,3)-(22,21)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ ├── depth: 0 │ │ ├── name_loc: (22,3)-(22,4) = "a" │ │ ├── value: │ │ │ @ RescueModifierNode (location: (22,7)-(22,21)) + │ │ │ ├── flags: ∅ │ │ │ ├── expression: │ │ │ │ @ CallNode (location: (22,7)-(22,10)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -307,13 +346,15 @@ │ │ │ ├── keyword_loc: (22,11)-(22,17) = "rescue" │ │ │ └── rescue_expression: │ │ │ @ NilNode (location: (22,18)-(22,21)) + │ │ │ └── flags: static_literal │ │ └── operator_loc: (22,5)-(22,6) = "=" │ ├── then_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (23,2)-(23,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (23,2)-(23,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -325,14 +366,17 @@ │ ├── consequent: ∅ │ └── end_keyword_loc: (24,0)-(24,3) = "end" ├── @ DefNode (location: (26,0)-(26,44)) + │ ├── flags: newline │ ├── name: :some_method │ ├── name_loc: (26,4)-(26,15) = "some_method" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (26,18)-(26,44)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ RescueModifierNode (location: (26,18)-(26,44)) + │ │ ├── flags: newline │ │ ├── expression: │ │ │ @ CallNode (location: (26,18)-(26,33)) │ │ │ ├── flags: ignore_visibility @@ -346,13 +390,14 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ IntegerNode (location: (26,31)-(26,33)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 42 │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ ├── keyword_loc: (26,34)-(26,40) = "rescue" │ │ └── rescue_expression: │ │ @ NilNode (location: (26,41)-(26,44)) + │ │ └── flags: static_literal │ ├── locals: [] │ ├── def_keyword_loc: (26,0)-(26,3) = "def" │ ├── operator_loc: ∅ @@ -361,18 +406,21 @@ │ ├── equal_loc: (26,16)-(26,17) = "=" │ └── end_keyword_loc: ∅ ├── @ DefNode (location: (28,0)-(31,3)) + │ ├── flags: newline │ ├── name: :a │ ├── name_loc: (28,4)-(28,5) = "a" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (28,0)-(31,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (29,2)-(29,6)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (29,2)-(29,6)) - │ │ │ ├── flags: ignore_visibility + │ │ │ ├── flags: newline, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :a @@ -386,15 +434,17 @@ │ │ │ │ ├── flags: symbol_keys │ │ │ │ └── elements: (length: 1) │ │ │ │ └── @ AssocNode (location: (29,4)-(29,6)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── key: │ │ │ │ │ @ SymbolNode (location: (29,4)-(29,6)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── value_loc: (29,4)-(29,5) = "b" │ │ │ │ │ ├── closing_loc: (29,5)-(29,6) = ":" │ │ │ │ │ └── unescaped: "b" │ │ │ │ ├── value: │ │ │ │ │ @ ImplicitNode (location: (29,4)-(29,6)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── value: │ │ │ │ │ @ CallNode (location: (29,4)-(29,6)) │ │ │ │ │ ├── flags: ignore_visibility @@ -411,6 +461,7 @@ │ │ │ └── block: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (30,0)-(30,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (30,0)-(30,6) = "rescue" │ │ │ ├── exceptions: (length: 0) │ │ │ ├── operator_loc: ∅ @@ -428,8 +479,10 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (31,0)-(31,3) = "end" ├── @ RescueModifierNode (location: (33,0)-(33,21)) + │ ├── flags: newline │ ├── expression: │ │ @ IfNode (location: (33,0)-(33,10)) + │ │ ├── flags: newline │ │ ├── if_keyword_loc: (33,4)-(33,6) = "if" │ │ ├── predicate: │ │ │ @ CallNode (location: (33,7)-(33,10)) @@ -445,9 +498,10 @@ │ │ ├── then_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (33,0)-(33,3)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (33,0)-(33,3)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :foo @@ -471,11 +525,13 @@ │ ├── closing_loc: ∅ │ └── block: ∅ └── @ LocalVariableWriteNode (location: (35,0)-(35,18)) + ├── flags: newline ├── name: :z ├── depth: 0 ├── name_loc: (35,0)-(35,1) = "z" ├── value: │ @ RescueModifierNode (location: (35,4)-(35,18)) + │ ├── flags: ∅ │ ├── expression: │ │ @ CallNode (location: (35,4)-(35,7)) │ │ ├── flags: ignore_visibility diff --git a/test/prism/snapshots/return.txt b/test/prism/snapshots/return.txt index 0dd26281c1d65d..8c01ee438cd792 100644 --- a/test/prism/snapshots/return.txt +++ b/test/prism/snapshots/return.txt @@ -1,165 +1,179 @@ @ ProgramNode (location: (1,0)-(23,9)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(23,9)) + ├── flags: ∅ └── body: (length: 10) ├── @ ReturnNode (location: (1,0)-(1,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── keyword_loc: (1,0)-(1,6) = "return" │ └── arguments: ∅ ├── @ ReturnNode (location: (3,0)-(3,20)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── keyword_loc: (3,0)-(3,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (3,7)-(3,20)) │ ├── flags: ∅ │ └── arguments: (length: 3) │ ├── @ ParenthesesNode (location: (3,7)-(3,10)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (3,8)-(3,9)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ IntegerNode (location: (3,8)-(3,9)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: newline, static_literal, decimal │ │ │ └── value: 1 │ │ ├── opening_loc: (3,7)-(3,8) = "(" │ │ └── closing_loc: (3,9)-(3,10) = ")" │ ├── @ ParenthesesNode (location: (3,12)-(3,15)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (3,13)-(3,14)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ IntegerNode (location: (3,13)-(3,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: newline, static_literal, decimal │ │ │ └── value: 2 │ │ ├── opening_loc: (3,12)-(3,13) = "(" │ │ └── closing_loc: (3,14)-(3,15) = ")" │ └── @ ParenthesesNode (location: (3,17)-(3,20)) + │ ├── flags: ∅ │ ├── body: │ │ @ StatementsNode (location: (3,18)-(3,19)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (3,18)-(3,19)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 3 │ ├── opening_loc: (3,17)-(3,18) = "(" │ └── closing_loc: (3,19)-(3,20) = ")" ├── @ ReturnNode (location: (5,0)-(5,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── keyword_loc: (5,0)-(5,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (5,7)-(5,9)) │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ SplatNode (location: (5,7)-(5,9)) + │ ├── flags: ∅ │ ├── operator_loc: (5,7)-(5,8) = "*" │ └── expression: │ @ IntegerNode (location: (5,8)-(5,9)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ ReturnNode (location: (7,0)-(7,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── keyword_loc: (7,0)-(7,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (7,7)-(7,8)) │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (7,7)-(7,8)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ ReturnNode (location: (9,0)-(10,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── keyword_loc: (9,0)-(9,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (9,7)-(10,1)) │ ├── flags: ∅ │ └── arguments: (length: 3) │ ├── @ IntegerNode (location: (9,7)-(9,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── @ IntegerNode (location: (9,10)-(9,11)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ └── @ IntegerNode (location: (10,0)-(10,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 3 ├── @ ReturnNode (location: (12,0)-(12,14)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── keyword_loc: (12,0)-(12,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (12,7)-(12,14)) │ ├── flags: ∅ │ └── arguments: (length: 3) │ ├── @ IntegerNode (location: (12,7)-(12,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── @ IntegerNode (location: (12,10)-(12,11)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ └── @ IntegerNode (location: (12,13)-(12,14)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 3 ├── @ ReturnNode (location: (14,0)-(14,16)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── keyword_loc: (14,0)-(14,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (14,7)-(14,16)) │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ ArrayNode (location: (14,7)-(14,16)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 3) │ │ ├── @ IntegerNode (location: (14,8)-(14,9)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── @ IntegerNode (location: (14,11)-(14,12)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── @ IntegerNode (location: (14,14)-(14,15)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 3 │ ├── opening_loc: (14,7)-(14,8) = "[" │ └── closing_loc: (14,15)-(14,16) = "]" ├── @ ReturnNode (location: (16,0)-(19,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── keyword_loc: (16,0)-(16,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (16,6)-(19,1)) │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ ParenthesesNode (location: (16,6)-(19,1)) + │ ├── flags: ∅ │ ├── body: │ │ @ StatementsNode (location: (17,2)-(18,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 2) │ │ ├── @ IntegerNode (location: (17,2)-(17,3)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: newline, static_literal, decimal │ │ │ └── value: 1 │ │ └── @ IntegerNode (location: (18,2)-(18,3)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 2 │ ├── opening_loc: (16,6)-(16,7) = "(" │ └── closing_loc: (19,0)-(19,1) = ")" ├── @ ReturnNode (location: (21,0)-(21,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── keyword_loc: (21,0)-(21,6) = "return" │ └── arguments: │ @ ArgumentsNode (location: (21,6)-(21,8)) │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ ParenthesesNode (location: (21,6)-(21,8)) + │ ├── flags: ∅ │ ├── body: ∅ │ ├── opening_loc: (21,6)-(21,7) = "(" │ └── closing_loc: (21,7)-(21,8) = ")" └── @ ReturnNode (location: (23,0)-(23,9)) - ├── flags: ∅ + ├── flags: newline ├── keyword_loc: (23,0)-(23,6) = "return" └── arguments: @ ArgumentsNode (location: (23,6)-(23,9)) ├── flags: ∅ └── arguments: (length: 1) └── @ ParenthesesNode (location: (23,6)-(23,9)) + ├── flags: ∅ ├── body: │ @ StatementsNode (location: (23,7)-(23,8)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ IntegerNode (location: (23,7)-(23,8)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 1 ├── opening_loc: (23,6)-(23,7) = "(" └── closing_loc: (23,8)-(23,9) = ")" diff --git a/test/prism/snapshots/seattlerb/BEGIN.txt b/test/prism/snapshots/seattlerb/BEGIN.txt index 246f39cbba87d9..a0d73a8039b91c 100644 --- a/test/prism/snapshots/seattlerb/BEGIN.txt +++ b/test/prism/snapshots/seattlerb/BEGIN.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,12)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,12)) + ├── flags: ∅ └── body: (length: 1) └── @ PreExecutionNode (location: (1,0)-(1,12)) + ├── flags: newline ├── statements: │ @ StatementsNode (location: (1,8)-(1,10)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ IntegerNode (location: (1,8)-(1,10)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 42 ├── keyword_loc: (1,0)-(1,5) = "BEGIN" ├── opening_loc: (1,6)-(1,7) = "{" diff --git a/test/prism/snapshots/seattlerb/TestRubyParserShared.txt b/test/prism/snapshots/seattlerb/TestRubyParserShared.txt index fabc92e47795c9..7a488a20aab3eb 100644 --- a/test/prism/snapshots/seattlerb/TestRubyParserShared.txt +++ b/test/prism/snapshots/seattlerb/TestRubyParserShared.txt @@ -1,24 +1,26 @@ @ ProgramNode (location: (1,0)-(92,1)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(92,1)) + ├── flags: ∅ └── body: (length: 16) ├── @ ArrayNode (location: (1,0)-(4,1)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── elements: (length: 0) │ ├── opening_loc: (1,0)-(1,3) = "%I[" │ └── closing_loc: (4,0)-(4,1) = "]" ├── @ ArrayNode (location: (6,0)-(9,1)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── elements: (length: 2) │ │ ├── @ SymbolNode (location: (7,0)-(7,5)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (7,0)-(7,5) = "line2" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "line2" │ │ └── @ SymbolNode (location: (8,0)-(8,5)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (8,0)-(8,5) = "line3" │ │ ├── closing_loc: ∅ @@ -26,12 +28,12 @@ │ ├── opening_loc: (6,0)-(6,3) = "%I[" │ └── closing_loc: (9,0)-(9,1) = "]" ├── @ ArrayNode (location: (11,0)-(14,1)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── elements: (length: 0) │ ├── opening_loc: (11,0)-(11,3) = "%W[" │ └── closing_loc: (14,0)-(14,1) = "]" ├── @ ArrayNode (location: (16,0)-(19,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 2) │ │ ├── @ StringNode (location: (17,0)-(17,5)) │ │ │ ├── flags: ∅ @@ -48,21 +50,21 @@ │ ├── opening_loc: (16,0)-(16,3) = "%W[" │ └── closing_loc: (19,0)-(19,1) = "]" ├── @ ArrayNode (location: (21,0)-(24,1)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── elements: (length: 0) │ ├── opening_loc: (21,0)-(21,3) = "%i[" │ └── closing_loc: (24,0)-(24,1) = "]" ├── @ ArrayNode (location: (26,0)-(29,1)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── elements: (length: 2) │ │ ├── @ SymbolNode (location: (27,0)-(27,5)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (27,0)-(27,5) = "line2" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "line2" │ │ └── @ SymbolNode (location: (28,0)-(28,5)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (28,0)-(28,5) = "line3" │ │ ├── closing_loc: ∅ @@ -70,18 +72,18 @@ │ ├── opening_loc: (26,0)-(26,3) = "%i[" │ └── closing_loc: (29,0)-(29,1) = "]" ├── @ RegularExpressionNode (location: (31,0)-(34,1)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (31,0)-(31,3) = "%r[" │ ├── content_loc: (31,3)-(34,0) = "\n\n\n" │ ├── closing_loc: (34,0)-(34,1) = "]" │ └── unescaped: "\n\n\n" ├── @ ArrayNode (location: (36,0)-(39,1)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── elements: (length: 0) │ ├── opening_loc: (36,0)-(36,3) = "%w[" │ └── closing_loc: (39,0)-(39,1) = "]" ├── @ ArrayNode (location: (41,0)-(44,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 2) │ │ ├── @ StringNode (location: (42,0)-(42,5)) │ │ │ ├── flags: ∅ @@ -98,16 +100,16 @@ │ ├── opening_loc: (41,0)-(41,3) = "%w[" │ └── closing_loc: (44,0)-(44,1) = "]" ├── @ ArrayNode (location: (46,0)-(49,1)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── elements: (length: 2) │ │ ├── @ SymbolNode (location: (47,0)-(47,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (47,0)-(47,1) = ":" │ │ │ ├── value_loc: (47,1)-(47,6) = "line2" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "line2" │ │ └── @ SymbolNode (location: (48,0)-(48,6)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (48,0)-(48,1) = ":" │ │ ├── value_loc: (48,1)-(48,6) = "line3" │ │ ├── closing_loc: ∅ @@ -115,23 +117,29 @@ │ ├── opening_loc: (46,0)-(46,1) = "[" │ └── closing_loc: (49,0)-(49,1) = "]" ├── @ ClassNode (location: (51,0)-(56,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (51,0)-(51,5) = "class" │ ├── constant_path: │ │ @ ConstantReadNode (location: (51,6)-(51,7)) + │ │ ├── flags: ∅ │ │ └── name: :X │ ├── inheritance_operator_loc: ∅ │ ├── superclass: ∅ │ ├── body: │ │ @ StatementsNode (location: (52,2)-(55,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ DefNode (location: (52,2)-(55,5)) + │ │ ├── flags: newline │ │ ├── name: :y │ │ ├── name_loc: (52,11)-(52,12) = "y" │ │ ├── receiver: │ │ │ @ SelfNode (location: (52,6)-(52,10)) + │ │ │ └── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (52,13)-(53,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 2) │ │ │ │ ├── @ RequiredParameterNode (location: (52,13)-(52,14)) │ │ │ │ │ ├── flags: ∅ @@ -147,11 +155,13 @@ │ │ │ └── block: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (54,4)-(54,9)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (54,4)-(54,9)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── receiver: │ │ │ │ @ LocalVariableReadNode (location: (54,4)-(54,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :a │ │ │ │ └── depth: 0 │ │ │ ├── call_operator_loc: ∅ @@ -163,6 +173,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ LocalVariableReadNode (location: (54,8)-(54,9)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :b │ │ │ │ └── depth: 0 │ │ │ ├── closing_loc: ∅ @@ -177,33 +188,40 @@ │ ├── end_keyword_loc: (56,0)-(56,3) = "end" │ └── name: :X ├── @ ClassNode (location: (59,0)-(63,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (59,0)-(59,5) = "class" │ ├── constant_path: │ │ @ ConstantReadNode (location: (59,6)-(59,7)) + │ │ ├── flags: ∅ │ │ └── name: :X │ ├── inheritance_operator_loc: ∅ │ ├── superclass: ∅ │ ├── body: │ │ @ StatementsNode (location: (60,2)-(62,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ ClassNode (location: (60,2)-(62,5)) + │ │ ├── flags: newline │ │ ├── locals: [] │ │ ├── class_keyword_loc: (60,2)-(60,7) = "class" │ │ ├── constant_path: │ │ │ @ ConstantReadNode (location: (60,8)-(60,9)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Y │ │ ├── inheritance_operator_loc: ∅ │ │ ├── superclass: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (61,4)-(61,10)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ ConstantWriteNode (location: (61,4)-(61,10)) + │ │ │ ├── flags: newline │ │ │ ├── name: :Z │ │ │ ├── name_loc: (61,4)-(61,5) = "Z" │ │ │ ├── value: │ │ │ │ @ IntegerNode (location: (61,8)-(61,10)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 42 │ │ │ └── operator_loc: (61,6)-(61,7) = "=" │ │ ├── end_keyword_loc: (62,2)-(62,5) = "end" @@ -211,22 +229,27 @@ │ ├── end_keyword_loc: (63,0)-(63,3) = "end" │ └── name: :X ├── @ ClassNode (location: (66,0)-(71,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (66,0)-(66,5) = "class" │ ├── constant_path: │ │ @ ConstantReadNode (location: (66,6)-(66,7)) + │ │ ├── flags: ∅ │ │ └── name: :X │ ├── inheritance_operator_loc: ∅ │ ├── superclass: ∅ │ ├── body: │ │ @ StatementsNode (location: (67,2)-(70,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ DefNode (location: (67,2)-(70,5)) + │ │ ├── flags: newline │ │ ├── name: :y │ │ ├── name_loc: (67,6)-(67,7) = "y" │ │ ├── receiver: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (67,8)-(68,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 2) │ │ │ │ ├── @ RequiredParameterNode (location: (67,8)-(67,9)) │ │ │ │ │ ├── flags: ∅ @@ -242,11 +265,13 @@ │ │ │ └── block: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (69,4)-(69,9)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (69,4)-(69,9)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── receiver: │ │ │ │ @ LocalVariableReadNode (location: (69,4)-(69,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :a │ │ │ │ └── depth: 0 │ │ │ ├── call_operator_loc: ∅ @@ -258,6 +283,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ LocalVariableReadNode (location: (69,8)-(69,9)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :b │ │ │ │ └── depth: 0 │ │ │ ├── closing_loc: ∅ @@ -272,29 +298,33 @@ │ ├── end_keyword_loc: (71,0)-(71,3) = "end" │ └── name: :X ├── @ ModuleNode (location: (74,0)-(79,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── module_keyword_loc: (74,0)-(74,6) = "module" │ ├── constant_path: │ │ @ ConstantReadNode (location: (74,7)-(74,8)) + │ │ ├── flags: ∅ │ │ └── name: :X │ ├── body: │ │ @ StatementsNode (location: (75,2)-(78,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ ConstantWriteNode (location: (75,2)-(78,3)) + │ │ ├── flags: newline │ │ ├── name: :X │ │ ├── name_loc: (75,2)-(75,3) = "X" │ │ ├── value: │ │ │ @ ArrayNode (location: (75,6)-(78,3)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: static_literal │ │ │ ├── elements: (length: 2) │ │ │ │ ├── @ SymbolNode (location: (76,4)-(76,10)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: (76,4)-(76,5) = ":" │ │ │ │ │ ├── value_loc: (76,5)-(76,10) = "line3" │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── unescaped: "line3" │ │ │ │ └── @ SymbolNode (location: (77,4)-(77,10)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (77,4)-(77,5) = ":" │ │ │ │ ├── value_loc: (77,5)-(77,10) = "line4" │ │ │ │ ├── closing_loc: ∅ @@ -305,29 +335,36 @@ │ ├── end_keyword_loc: (79,0)-(79,3) = "end" │ └── name: :X ├── @ ModuleNode (location: (82,0)-(86,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── module_keyword_loc: (82,0)-(82,6) = "module" │ ├── constant_path: │ │ @ ConstantReadNode (location: (82,7)-(82,8)) + │ │ ├── flags: ∅ │ │ └── name: :X │ ├── body: │ │ @ StatementsNode (location: (83,2)-(85,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ ModuleNode (location: (83,2)-(85,5)) + │ │ ├── flags: newline │ │ ├── locals: [] │ │ ├── module_keyword_loc: (83,2)-(83,8) = "module" │ │ ├── constant_path: │ │ │ @ ConstantReadNode (location: (83,9)-(83,10)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Y │ │ ├── body: │ │ │ @ StatementsNode (location: (84,4)-(84,10)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ ConstantWriteNode (location: (84,4)-(84,10)) + │ │ │ ├── flags: newline │ │ │ ├── name: :Z │ │ │ ├── name_loc: (84,4)-(84,5) = "Z" │ │ │ ├── value: │ │ │ │ @ IntegerNode (location: (84,8)-(84,10)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 42 │ │ │ └── operator_loc: (84,6)-(84,7) = "=" │ │ ├── end_keyword_loc: (85,2)-(85,5) = "end" @@ -335,7 +372,7 @@ │ ├── end_keyword_loc: (86,0)-(86,3) = "end" │ └── name: :X └── @ CallNode (location: (89,0)-(92,1)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :x @@ -346,13 +383,13 @@ │ ├── flags: ∅ │ └── arguments: (length: 2) │ ├── @ SymbolNode (location: (90,0)-(90,6)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (90,0)-(90,1) = ":" │ │ ├── value_loc: (90,1)-(90,6) = "line2" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "line2" │ └── @ SymbolNode (location: (91,0)-(91,6)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (91,0)-(91,1) = ":" │ ├── value_loc: (91,1)-(91,6) = "line3" │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/__ENCODING__.txt b/test/prism/snapshots/seattlerb/__ENCODING__.txt index 1b223bd8fe78dd..f04dfb6cb8694b 100644 --- a/test/prism/snapshots/seattlerb/__ENCODING__.txt +++ b/test/prism/snapshots/seattlerb/__ENCODING__.txt @@ -1,6 +1,9 @@ @ ProgramNode (location: (1,0)-(1,12)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,12)) + ├── flags: ∅ └── body: (length: 1) └── @ SourceEncodingNode (location: (1,0)-(1,12)) + └── flags: newline, static_literal diff --git a/test/prism/snapshots/seattlerb/alias_gvar_backref.txt b/test/prism/snapshots/seattlerb/alias_gvar_backref.txt index a2586423d78091..47b0e80b6447c2 100644 --- a/test/prism/snapshots/seattlerb/alias_gvar_backref.txt +++ b/test/prism/snapshots/seattlerb/alias_gvar_backref.txt @@ -1,13 +1,18 @@ @ ProgramNode (location: (1,0)-(1,15)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,15)) + ├── flags: ∅ └── body: (length: 1) └── @ AliasGlobalVariableNode (location: (1,0)-(1,15)) + ├── flags: newline ├── new_name: │ @ GlobalVariableReadNode (location: (1,6)-(1,12)) + │ ├── flags: ∅ │ └── name: :$MATCH ├── old_name: │ @ BackReferenceReadNode (location: (1,13)-(1,15)) + │ ├── flags: ∅ │ └── name: :$& └── keyword_loc: (1,0)-(1,5) = "alias" diff --git a/test/prism/snapshots/seattlerb/alias_resword.txt b/test/prism/snapshots/seattlerb/alias_resword.txt index 99ed696c6873b9..44a4727283eaa7 100644 --- a/test/prism/snapshots/seattlerb/alias_resword.txt +++ b/test/prism/snapshots/seattlerb/alias_resword.txt @@ -1,19 +1,22 @@ @ ProgramNode (location: (1,0)-(1,12)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,12)) + ├── flags: ∅ └── body: (length: 1) └── @ AliasMethodNode (location: (1,0)-(1,12)) + ├── flags: newline ├── new_name: │ @ SymbolNode (location: (1,6)-(1,8)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: ∅ │ ├── value_loc: (1,6)-(1,8) = "in" │ ├── closing_loc: ∅ │ └── unescaped: "in" ├── old_name: │ @ SymbolNode (location: (1,9)-(1,12)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: ∅ │ ├── value_loc: (1,9)-(1,12) = "out" │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/and_multi.txt b/test/prism/snapshots/seattlerb/and_multi.txt index b60b131fd2d7ec..f98ad67e1026b9 100644 --- a/test/prism/snapshots/seattlerb/and_multi.txt +++ b/test/prism/snapshots/seattlerb/and_multi.txt @@ -1,18 +1,24 @@ @ ProgramNode (location: (1,0)-(3,4)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,4)) + ├── flags: ∅ └── body: (length: 1) └── @ AndNode (location: (1,0)-(3,4)) + ├── flags: newline ├── left: │ @ AndNode (location: (1,0)-(2,9)) + │ ├── flags: ∅ │ ├── left: │ │ @ TrueNode (location: (1,0)-(1,4)) + │ │ └── flags: static_literal │ ├── right: │ │ @ CallNode (location: (2,0)-(2,9)) │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ FalseNode (location: (2,4)-(2,9)) + │ │ │ └── flags: static_literal │ │ ├── call_operator_loc: ∅ │ │ ├── name: :! │ │ ├── message_loc: (2,0)-(2,3) = "not" @@ -23,4 +29,5 @@ │ └── operator_loc: (1,5)-(1,8) = "and" ├── right: │ @ TrueNode (location: (3,0)-(3,4)) + │ └── flags: static_literal └── operator_loc: (2,10)-(2,13) = "and" diff --git a/test/prism/snapshots/seattlerb/aref_args_assocs.txt b/test/prism/snapshots/seattlerb/aref_args_assocs.txt index b729186dc5e3d4..cbd07920379988 100644 --- a/test/prism/snapshots/seattlerb/aref_args_assocs.txt +++ b/test/prism/snapshots/seattlerb/aref_args_assocs.txt @@ -1,22 +1,25 @@ @ ProgramNode (location: (1,0)-(1,8)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,8)) + ├── flags: ∅ └── body: (length: 1) └── @ ArrayNode (location: (1,0)-(1,8)) - ├── flags: ∅ + ├── flags: newline ├── elements: (length: 1) │ └── @ KeywordHashNode (location: (1,1)-(1,7)) │ ├── flags: ∅ │ └── elements: (length: 1) │ └── @ AssocNode (location: (1,1)-(1,7)) + │ ├── flags: static_literal │ ├── key: │ │ @ IntegerNode (location: (1,1)-(1,2)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── value: │ │ @ IntegerNode (location: (1,6)-(1,7)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ └── operator_loc: (1,3)-(1,5) = "=>" ├── opening_loc: (1,0)-(1,1) = "[" diff --git a/test/prism/snapshots/seattlerb/aref_args_lit_assocs.txt b/test/prism/snapshots/seattlerb/aref_args_lit_assocs.txt index 0e9454d780b22a..7302f728e3beaa 100644 --- a/test/prism/snapshots/seattlerb/aref_args_lit_assocs.txt +++ b/test/prism/snapshots/seattlerb/aref_args_lit_assocs.txt @@ -1,25 +1,28 @@ @ ProgramNode (location: (1,0)-(1,11)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,11)) + ├── flags: ∅ └── body: (length: 1) └── @ ArrayNode (location: (1,0)-(1,11)) - ├── flags: ∅ + ├── flags: newline ├── elements: (length: 2) │ ├── @ IntegerNode (location: (1,1)-(1,2)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── @ KeywordHashNode (location: (1,4)-(1,10)) │ ├── flags: ∅ │ └── elements: (length: 1) │ └── @ AssocNode (location: (1,4)-(1,10)) + │ ├── flags: static_literal │ ├── key: │ │ @ IntegerNode (location: (1,4)-(1,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── value: │ │ @ IntegerNode (location: (1,9)-(1,10)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 3 │ └── operator_loc: (1,6)-(1,8) = "=>" ├── opening_loc: (1,0)-(1,1) = "[" diff --git a/test/prism/snapshots/seattlerb/args_kw_block.txt b/test/prism/snapshots/seattlerb/args_kw_block.txt index 1ad933c74e3f4e..88081dfa1452d7 100644 --- a/test/prism/snapshots/seattlerb/args_kw_block.txt +++ b/test/prism/snapshots/seattlerb/args_kw_block.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,20)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,20)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,20)) + ├── flags: newline ├── name: :f ├── name_loc: (1,4)-(1,5) = "f" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,6)-(1,14)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ @@ -20,7 +24,7 @@ │ │ ├── name_loc: (1,6)-(1,8) = "a:" │ │ └── value: │ │ @ IntegerNode (location: (1,9)-(1,10)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── keyword_rest: ∅ │ └── block: diff --git a/test/prism/snapshots/seattlerb/array_line_breaks.txt b/test/prism/snapshots/seattlerb/array_line_breaks.txt index 880fedf93308b5..a2d21e90ad6f39 100644 --- a/test/prism/snapshots/seattlerb/array_line_breaks.txt +++ b/test/prism/snapshots/seattlerb/array_line_breaks.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(4,1)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(4,1)) + ├── flags: ∅ └── body: (length: 2) ├── @ ArrayNode (location: (1,0)-(3,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 2) │ │ ├── @ StringNode (location: (2,0)-(2,3)) │ │ │ ├── flags: ∅ @@ -21,5 +23,5 @@ │ ├── opening_loc: (1,0)-(1,1) = "[" │ └── closing_loc: (3,3)-(3,4) = "]" └── @ IntegerNode (location: (4,0)-(4,1)) - ├── flags: decimal + ├── flags: newline, static_literal, decimal └── value: 1 diff --git a/test/prism/snapshots/seattlerb/array_lits_trailing_calls.txt b/test/prism/snapshots/seattlerb/array_lits_trailing_calls.txt index d46a181fc7673c..2de5a054a66695 100644 --- a/test/prism/snapshots/seattlerb/array_lits_trailing_calls.txt +++ b/test/prism/snapshots/seattlerb/array_lits_trailing_calls.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(3,4)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,4)) + ├── flags: ∅ └── body: (length: 2) ├── @ CallNode (location: (1,0)-(1,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ArrayNode (location: (1,0)-(1,4)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── elements: (length: 0) │ │ ├── opening_loc: (1,0)-(1,3) = "%w[" │ │ └── closing_loc: (1,3)-(1,4) = "]" @@ -19,10 +21,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ └── @ CallNode (location: (3,0)-(3,4)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ ArrayNode (location: (3,0)-(3,2)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 0) │ ├── opening_loc: (3,0)-(3,1) = "[" │ └── closing_loc: (3,1)-(3,2) = "]" diff --git a/test/prism/snapshots/seattlerb/assoc__bare.txt b/test/prism/snapshots/seattlerb/assoc__bare.txt index 28e4f713c51462..530360ef66ff23 100644 --- a/test/prism/snapshots/seattlerb/assoc__bare.txt +++ b/test/prism/snapshots/seattlerb/assoc__bare.txt @@ -1,21 +1,26 @@ @ ProgramNode (location: (1,0)-(1,6)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,6)) + ├── flags: ∅ └── body: (length: 1) └── @ HashNode (location: (1,0)-(1,6)) + ├── flags: newline ├── opening_loc: (1,0)-(1,1) = "{" ├── elements: (length: 1) │ └── @ AssocNode (location: (1,2)-(1,4)) + │ ├── flags: ∅ │ ├── key: │ │ @ SymbolNode (location: (1,2)-(1,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (1,2)-(1,3) = "y" │ │ ├── closing_loc: (1,3)-(1,4) = ":" │ │ └── unescaped: "y" │ ├── value: │ │ @ ImplicitNode (location: (1,2)-(1,4)) + │ │ ├── flags: ∅ │ │ └── value: │ │ @ CallNode (location: (1,2)-(1,4)) │ │ ├── flags: ignore_visibility diff --git a/test/prism/snapshots/seattlerb/assoc_label.txt b/test/prism/snapshots/seattlerb/assoc_label.txt index 70490c0da4c687..91b94bbc1cc0a0 100644 --- a/test/prism/snapshots/seattlerb/assoc_label.txt +++ b/test/prism/snapshots/seattlerb/assoc_label.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,6)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,6)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,6)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -18,16 +20,17 @@ │ ├── flags: symbol_keys │ └── elements: (length: 1) │ └── @ AssocNode (location: (1,2)-(1,5)) + │ ├── flags: static_literal │ ├── key: │ │ @ SymbolNode (location: (1,2)-(1,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (1,2)-(1,3) = "b" │ │ ├── closing_loc: (1,3)-(1,4) = ":" │ │ └── unescaped: "b" │ ├── value: │ │ @ IntegerNode (location: (1,4)-(1,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: ∅ ├── closing_loc: (1,5)-(1,6) = ")" diff --git a/test/prism/snapshots/seattlerb/attr_asgn_colon_id.txt b/test/prism/snapshots/seattlerb/attr_asgn_colon_id.txt index 589433eda89916..67802fc8a4ea7f 100644 --- a/test/prism/snapshots/seattlerb/attr_asgn_colon_id.txt +++ b/test/prism/snapshots/seattlerb/attr_asgn_colon_id.txt @@ -1,12 +1,15 @@ @ ProgramNode (location: (1,0)-(1,8)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,8)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,8)) - ├── flags: attribute_write + ├── flags: newline, attribute_write ├── receiver: │ @ ConstantReadNode (location: (1,0)-(1,1)) + │ ├── flags: ∅ │ └── name: :A ├── call_operator_loc: (1,1)-(1,3) = "::" ├── name: :b= @@ -17,7 +20,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (1,7)-(1,8)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── closing_loc: ∅ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/attrasgn_array_arg.txt b/test/prism/snapshots/seattlerb/attrasgn_array_arg.txt index 9b04ae9656ed86..43853b4ef91d11 100644 --- a/test/prism/snapshots/seattlerb/attrasgn_array_arg.txt +++ b/test/prism/snapshots/seattlerb/attrasgn_array_arg.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,13)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,13)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,13)) - ├── flags: attribute_write + ├── flags: newline, attribute_write ├── receiver: │ @ CallNode (location: (1,0)-(1,1)) │ ├── flags: variable_call, ignore_visibility @@ -25,18 +27,18 @@ │ ├── flags: ∅ │ └── arguments: (length: 2) │ ├── @ ArrayNode (location: (1,2)-(1,8)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── elements: (length: 2) │ │ │ ├── @ IntegerNode (location: (1,3)-(1,4)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── @ IntegerNode (location: (1,6)-(1,7)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── opening_loc: (1,2)-(1,3) = "[" │ │ └── closing_loc: (1,7)-(1,8) = "]" │ └── @ IntegerNode (location: (1,12)-(1,13)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 3 ├── closing_loc: (1,8)-(1,9) = "]" └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/attrasgn_array_lhs.txt b/test/prism/snapshots/seattlerb/attrasgn_array_lhs.txt index 39544e98da2c3f..5729407edc1f17 100644 --- a/test/prism/snapshots/seattlerb/attrasgn_array_lhs.txt +++ b/test/prism/snapshots/seattlerb/attrasgn_array_lhs.txt @@ -1,25 +1,27 @@ @ ProgramNode (location: (1,0)-(1,42)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,42)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,42)) - ├── flags: attribute_write + ├── flags: newline, attribute_write ├── receiver: │ @ ArrayNode (location: (1,0)-(1,12)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 4) │ │ ├── @ IntegerNode (location: (1,1)-(1,2)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── @ IntegerNode (location: (1,4)-(1,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── @ IntegerNode (location: (1,7)-(1,8)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 3 │ │ └── @ IntegerNode (location: (1,10)-(1,11)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 4 │ ├── opening_loc: (1,0)-(1,1) = "[" │ └── closing_loc: (1,11)-(1,12) = "]" diff --git a/test/prism/snapshots/seattlerb/attrasgn_primary_dot_constant.txt b/test/prism/snapshots/seattlerb/attrasgn_primary_dot_constant.txt index d4c4fe107059c9..2c06df2609b95d 100644 --- a/test/prism/snapshots/seattlerb/attrasgn_primary_dot_constant.txt +++ b/test/prism/snapshots/seattlerb/attrasgn_primary_dot_constant.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,7)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,7)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,7)) - ├── flags: attribute_write + ├── flags: newline, attribute_write ├── receiver: │ @ CallNode (location: (1,0)-(1,1)) │ ├── flags: variable_call, ignore_visibility @@ -25,7 +27,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (1,6)-(1,7)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── closing_loc: ∅ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/backticks_interpolation_line.txt b/test/prism/snapshots/seattlerb/backticks_interpolation_line.txt index 67c8be034eb20a..8c6a163c48a6c9 100644 --- a/test/prism/snapshots/seattlerb/backticks_interpolation_line.txt +++ b/test/prism/snapshots/seattlerb/backticks_interpolation_line.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,8)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,8)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,8)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :x @@ -15,12 +17,15 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ InterpolatedXStringNode (location: (1,2)-(1,8)) + │ ├── flags: ∅ │ ├── opening_loc: (1,2)-(1,3) = "`" │ ├── parts: (length: 1) │ │ └── @ EmbeddedStatementsNode (location: (1,3)-(1,7)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (1,3)-(1,5) = "\#{" │ │ ├── statements: │ │ │ @ StatementsNode (location: (1,5)-(1,6)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (1,5)-(1,6)) │ │ │ ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/seattlerb/bang_eq.txt b/test/prism/snapshots/seattlerb/bang_eq.txt index ec3dd888b27ef8..6bbd4a6ea2205d 100644 --- a/test/prism/snapshots/seattlerb/bang_eq.txt +++ b/test/prism/snapshots/seattlerb/bang_eq.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,6)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,6)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,6)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── call_operator_loc: ∅ ├── name: :!= @@ -18,7 +20,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (1,5)-(1,6)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── closing_loc: ∅ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/bdot2.txt b/test/prism/snapshots/seattlerb/bdot2.txt index d4fb86fbe66f65..b1c05c0fc46e26 100644 --- a/test/prism/snapshots/seattlerb/bdot2.txt +++ b/test/prism/snapshots/seattlerb/bdot2.txt @@ -1,18 +1,20 @@ @ ProgramNode (location: (1,0)-(3,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,3)) + ├── flags: ∅ └── body: (length: 3) ├── @ RangeNode (location: (1,0)-(1,4)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── left: ∅ │ ├── right: │ │ @ IntegerNode (location: (1,2)-(1,4)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 10 │ └── operator_loc: (1,0)-(1,2) = ".." ├── @ RangeNode (location: (2,2)-(2,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── left: ∅ │ ├── right: │ │ @ CallNode (location: (2,4)-(2,5)) @@ -27,7 +29,7 @@ │ │ └── block: ∅ │ └── operator_loc: (2,2)-(2,4) = ".." └── @ CallNode (location: (3,2)-(3,3)) - ├── flags: variable_call, ignore_visibility + ├── flags: newline, variable_call, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :c diff --git a/test/prism/snapshots/seattlerb/bdot3.txt b/test/prism/snapshots/seattlerb/bdot3.txt index 0c960f045870a0..0b35268f19a2e8 100644 --- a/test/prism/snapshots/seattlerb/bdot3.txt +++ b/test/prism/snapshots/seattlerb/bdot3.txt @@ -1,18 +1,20 @@ @ ProgramNode (location: (1,0)-(3,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,3)) + ├── flags: ∅ └── body: (length: 3) ├── @ RangeNode (location: (1,0)-(1,5)) - │ ├── flags: exclude_end + │ ├── flags: newline, static_literal, exclude_end │ ├── left: ∅ │ ├── right: │ │ @ IntegerNode (location: (1,3)-(1,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 10 │ └── operator_loc: (1,0)-(1,3) = "..." ├── @ RangeNode (location: (2,2)-(2,6)) - │ ├── flags: exclude_end + │ ├── flags: newline, exclude_end │ ├── left: ∅ │ ├── right: │ │ @ CallNode (location: (2,5)-(2,6)) @@ -27,7 +29,7 @@ │ │ └── block: ∅ │ └── operator_loc: (2,2)-(2,5) = "..." └── @ CallNode (location: (3,2)-(3,3)) - ├── flags: variable_call, ignore_visibility + ├── flags: newline, variable_call, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :c diff --git a/test/prism/snapshots/seattlerb/begin_ensure_no_bodies.txt b/test/prism/snapshots/seattlerb/begin_ensure_no_bodies.txt index e1698d0baefcc0..6ebcc2e052736f 100644 --- a/test/prism/snapshots/seattlerb/begin_ensure_no_bodies.txt +++ b/test/prism/snapshots/seattlerb/begin_ensure_no_bodies.txt @@ -1,15 +1,19 @@ @ ProgramNode (location: (1,0)-(3,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,3)) + ├── flags: ∅ └── body: (length: 1) └── @ BeginNode (location: (1,0)-(3,3)) + ├── flags: newline ├── begin_keyword_loc: (1,0)-(1,5) = "begin" ├── statements: ∅ ├── rescue_clause: ∅ ├── else_clause: ∅ ├── ensure_clause: │ @ EnsureNode (location: (2,0)-(3,3)) + │ ├── flags: ∅ │ ├── ensure_keyword_loc: (2,0)-(2,6) = "ensure" │ ├── statements: ∅ │ └── end_keyword_loc: (3,0)-(3,3) = "end" diff --git a/test/prism/snapshots/seattlerb/begin_rescue_else_ensure_bodies.txt b/test/prism/snapshots/seattlerb/begin_rescue_else_ensure_bodies.txt index 6603e5c8945548..61a62d311eb167 100644 --- a/test/prism/snapshots/seattlerb/begin_rescue_else_ensure_bodies.txt +++ b/test/prism/snapshots/seattlerb/begin_rescue_else_ensure_bodies.txt @@ -1,47 +1,57 @@ @ ProgramNode (location: (1,0)-(9,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(9,3)) + ├── flags: ∅ └── body: (length: 1) └── @ BeginNode (location: (1,0)-(9,3)) + ├── flags: newline ├── begin_keyword_loc: (1,0)-(1,5) = "begin" ├── statements: │ @ StatementsNode (location: (2,2)-(2,3)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ IntegerNode (location: (2,2)-(2,3)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 1 ├── rescue_clause: │ @ RescueNode (location: (3,0)-(4,3)) + │ ├── flags: ∅ │ ├── keyword_loc: (3,0)-(3,6) = "rescue" │ ├── exceptions: (length: 0) │ ├── operator_loc: ∅ │ ├── reference: ∅ │ ├── statements: │ │ @ StatementsNode (location: (4,2)-(4,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (4,2)-(4,3)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 2 │ └── consequent: ∅ ├── else_clause: │ @ ElseNode (location: (5,0)-(7,6)) + │ ├── flags: ∅ │ ├── else_keyword_loc: (5,0)-(5,4) = "else" │ ├── statements: │ │ @ StatementsNode (location: (6,2)-(6,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (6,2)-(6,3)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 3 │ └── end_keyword_loc: (7,0)-(7,6) = "ensure" ├── ensure_clause: │ @ EnsureNode (location: (7,0)-(9,3)) + │ ├── flags: ∅ │ ├── ensure_keyword_loc: (7,0)-(7,6) = "ensure" │ ├── statements: │ │ @ StatementsNode (location: (8,2)-(8,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (8,2)-(8,3)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 4 │ └── end_keyword_loc: (9,0)-(9,3) = "end" └── end_keyword_loc: (9,0)-(9,3) = "end" diff --git a/test/prism/snapshots/seattlerb/begin_rescue_else_ensure_no_bodies.txt b/test/prism/snapshots/seattlerb/begin_rescue_else_ensure_no_bodies.txt index 02e1f097ac4eab..3353a4148470f1 100644 --- a/test/prism/snapshots/seattlerb/begin_rescue_else_ensure_no_bodies.txt +++ b/test/prism/snapshots/seattlerb/begin_rescue_else_ensure_no_bodies.txt @@ -1,13 +1,17 @@ @ ProgramNode (location: (1,0)-(9,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(9,3)) + ├── flags: ∅ └── body: (length: 1) └── @ BeginNode (location: (1,0)-(9,3)) + ├── flags: newline ├── begin_keyword_loc: (1,0)-(1,5) = "begin" ├── statements: ∅ ├── rescue_clause: │ @ RescueNode (location: (3,0)-(3,6)) + │ ├── flags: ∅ │ ├── keyword_loc: (3,0)-(3,6) = "rescue" │ ├── exceptions: (length: 0) │ ├── operator_loc: ∅ @@ -16,11 +20,13 @@ │ └── consequent: ∅ ├── else_clause: │ @ ElseNode (location: (5,0)-(7,6)) + │ ├── flags: ∅ │ ├── else_keyword_loc: (5,0)-(5,4) = "else" │ ├── statements: ∅ │ └── end_keyword_loc: (7,0)-(7,6) = "ensure" ├── ensure_clause: │ @ EnsureNode (location: (7,0)-(9,3)) + │ ├── flags: ∅ │ ├── ensure_keyword_loc: (7,0)-(7,6) = "ensure" │ ├── statements: ∅ │ └── end_keyword_loc: (9,0)-(9,3) = "end" diff --git a/test/prism/snapshots/seattlerb/begin_rescue_ensure_no_bodies.txt b/test/prism/snapshots/seattlerb/begin_rescue_ensure_no_bodies.txt index b36fe5c1fef513..e84fc70650899a 100644 --- a/test/prism/snapshots/seattlerb/begin_rescue_ensure_no_bodies.txt +++ b/test/prism/snapshots/seattlerb/begin_rescue_ensure_no_bodies.txt @@ -1,13 +1,17 @@ @ ProgramNode (location: (1,0)-(4,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(4,3)) + ├── flags: ∅ └── body: (length: 1) └── @ BeginNode (location: (1,0)-(4,3)) + ├── flags: newline ├── begin_keyword_loc: (1,0)-(1,5) = "begin" ├── statements: ∅ ├── rescue_clause: │ @ RescueNode (location: (2,0)-(2,6)) + │ ├── flags: ∅ │ ├── keyword_loc: (2,0)-(2,6) = "rescue" │ ├── exceptions: (length: 0) │ ├── operator_loc: ∅ @@ -17,6 +21,7 @@ ├── else_clause: ∅ ├── ensure_clause: │ @ EnsureNode (location: (3,0)-(4,3)) + │ ├── flags: ∅ │ ├── ensure_keyword_loc: (3,0)-(3,6) = "ensure" │ ├── statements: ∅ │ └── end_keyword_loc: (4,0)-(4,3) = "end" diff --git a/test/prism/snapshots/seattlerb/block_arg__bare.txt b/test/prism/snapshots/seattlerb/block_arg__bare.txt index 165c2980be3250..ceafdcebbc57f4 100644 --- a/test/prism/snapshots/seattlerb/block_arg__bare.txt +++ b/test/prism/snapshots/seattlerb/block_arg__bare.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,13)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,13)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,13)) + ├── flags: newline ├── name: :x ├── name_loc: (1,4)-(1,5) = "x" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,6)-(1,7)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ diff --git a/test/prism/snapshots/seattlerb/block_arg_kwsplat.txt b/test/prism/snapshots/seattlerb/block_arg_kwsplat.txt index 392de8559b9b02..20756c8378ca46 100644 --- a/test/prism/snapshots/seattlerb/block_arg_kwsplat.txt +++ b/test/prism/snapshots/seattlerb/block_arg_kwsplat.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,11)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,11)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,11)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,11)) + ├── flags: ∅ ├── locals: [:b] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,9)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,8)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ diff --git a/test/prism/snapshots/seattlerb/block_arg_opt_arg_block.txt b/test/prism/snapshots/seattlerb/block_arg_opt_arg_block.txt index ee9644d59d8bbc..dd89df907daad6 100644 --- a/test/prism/snapshots/seattlerb/block_arg_opt_arg_block.txt +++ b/test/prism/snapshots/seattlerb/block_arg_opt_arg_block.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,21)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,21)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,21)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,21)) + ├── flags: ∅ ├── locals: [:b, :c, :d, :e] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,19)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,18)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6)) │ │ │ ├── flags: ∅ @@ -31,7 +36,7 @@ │ │ │ ├── operator_loc: (1,9)-(1,10) = "=" │ │ │ └── value: │ │ │ @ IntegerNode (location: (1,10)-(1,11)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── rest: ∅ │ │ ├── posts: (length: 1) diff --git a/test/prism/snapshots/seattlerb/block_arg_opt_splat.txt b/test/prism/snapshots/seattlerb/block_arg_opt_splat.txt index 799bd21057aad4..cefc1a85153b95 100644 --- a/test/prism/snapshots/seattlerb/block_arg_opt_splat.txt +++ b/test/prism/snapshots/seattlerb/block_arg_opt_splat.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,20)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,20)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,20)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,20)) + ├── flags: ∅ ├── locals: [:b, :c, :d] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,18)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,17)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6)) │ │ │ ├── flags: ∅ @@ -31,7 +36,7 @@ │ │ │ ├── operator_loc: (1,10)-(1,11) = "=" │ │ │ └── value: │ │ │ @ IntegerNode (location: (1,12)-(1,13)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── rest: │ │ │ @ RestParameterNode (location: (1,15)-(1,17)) diff --git a/test/prism/snapshots/seattlerb/block_arg_opt_splat_arg_block_omfg.txt b/test/prism/snapshots/seattlerb/block_arg_opt_splat_arg_block_omfg.txt index b5df136a622987..bf66e376fd7573 100644 --- a/test/prism/snapshots/seattlerb/block_arg_opt_splat_arg_block_omfg.txt +++ b/test/prism/snapshots/seattlerb/block_arg_opt_splat_arg_block_omfg.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,25)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,25)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,25)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,25)) + ├── flags: ∅ ├── locals: [:b, :c, :d, :e, :f] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,23)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,22)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6)) │ │ │ ├── flags: ∅ @@ -31,7 +36,7 @@ │ │ │ ├── operator_loc: (1,9)-(1,10) = "=" │ │ │ └── value: │ │ │ @ IntegerNode (location: (1,10)-(1,11)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── rest: │ │ │ @ RestParameterNode (location: (1,13)-(1,15)) diff --git a/test/prism/snapshots/seattlerb/block_arg_optional.txt b/test/prism/snapshots/seattlerb/block_arg_optional.txt index 8a850e9a21a1e1..5fecfc31ab31a4 100644 --- a/test/prism/snapshots/seattlerb/block_arg_optional.txt +++ b/test/prism/snapshots/seattlerb/block_arg_optional.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,13)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,13)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,13)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,13)) + ├── flags: ∅ ├── locals: [:b] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,11)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,10)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 1) │ │ │ └── @ OptionalParameterNode (location: (1,5)-(1,10)) @@ -28,7 +33,7 @@ │ │ │ ├── operator_loc: (1,7)-(1,8) = "=" │ │ │ └── value: │ │ │ @ IntegerNode (location: (1,9)-(1,10)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) diff --git a/test/prism/snapshots/seattlerb/block_arg_scope.txt b/test/prism/snapshots/seattlerb/block_arg_scope.txt index b99cc5e45ce350..e90d6445b2db06 100644 --- a/test/prism/snapshots/seattlerb/block_arg_scope.txt +++ b/test/prism/snapshots/seattlerb/block_arg_scope.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,12)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,12)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,12)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,12)) + ├── flags: ∅ ├── locals: [:b, :c] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,10)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,6)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6)) │ │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/block_arg_scope2.txt b/test/prism/snapshots/seattlerb/block_arg_scope2.txt index 98b3a7da3aab9d..c9f7242d8a77e8 100644 --- a/test/prism/snapshots/seattlerb/block_arg_scope2.txt +++ b/test/prism/snapshots/seattlerb/block_arg_scope2.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,14)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,14)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,14)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,14)) + ├── flags: ∅ ├── locals: [:b, :c, :d] ├── parameters: │ @ BlockParametersNode (location: (1,3)-(1,12)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,4)-(1,5)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (1,4)-(1,5)) │ │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/block_arg_splat_arg.txt b/test/prism/snapshots/seattlerb/block_arg_splat_arg.txt index fd5813c983679e..6ae1b1dade72d4 100644 --- a/test/prism/snapshots/seattlerb/block_arg_splat_arg.txt +++ b/test/prism/snapshots/seattlerb/block_arg_splat_arg.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,16)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,16)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,16)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,16)) + ├── flags: ∅ ├── locals: [:b, :c, :d] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,14)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,13)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6)) │ │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/block_args_kwargs.txt b/test/prism/snapshots/seattlerb/block_args_kwargs.txt index 0975ce33679661..45876c6dc129c9 100644 --- a/test/prism/snapshots/seattlerb/block_args_kwargs.txt +++ b/test/prism/snapshots/seattlerb/block_args_kwargs.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,23)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,23)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,23)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,23)) + ├── flags: ∅ ├── locals: [:kwargs] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,14)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,13)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -36,8 +41,10 @@ │ └── closing_loc: (1,13)-(1,14) = "|" ├── body: │ @ StatementsNode (location: (1,15)-(1,21)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ LocalVariableReadNode (location: (1,15)-(1,21)) + │ ├── flags: newline │ ├── name: :kwargs │ └── depth: 0 ├── opening_loc: (1,2)-(1,3) = "{" diff --git a/test/prism/snapshots/seattlerb/block_args_no_kwargs.txt b/test/prism/snapshots/seattlerb/block_args_no_kwargs.txt index d47349defbd409..298bc26ce0ac30 100644 --- a/test/prism/snapshots/seattlerb/block_args_no_kwargs.txt +++ b/test/prism/snapshots/seattlerb/block_args_no_kwargs.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,13)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,13)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,13)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,13)) + ├── flags: ∅ ├── locals: [] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,11)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,10)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -26,6 +31,7 @@ │ │ ├── keywords: (length: 0) │ │ ├── keyword_rest: │ │ │ @ NoKeywordsParameterNode (location: (1,5)-(1,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (1,5)-(1,7) = "**" │ │ │ └── keyword_loc: (1,7)-(1,10) = "nil" │ │ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/block_args_opt1.txt b/test/prism/snapshots/seattlerb/block_args_opt1.txt index 1f21c0a4773b8c..d23bd5edce02c3 100644 --- a/test/prism/snapshots/seattlerb/block_args_opt1.txt +++ b/test/prism/snapshots/seattlerb/block_args_opt1.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,24)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,24)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,24)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,24)) + ├── flags: ∅ ├── locals: [:a, :b] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,15)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,14)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6)) │ │ │ ├── flags: ∅ @@ -31,7 +36,7 @@ │ │ │ ├── operator_loc: (1,10)-(1,11) = "=" │ │ │ └── value: │ │ │ @ IntegerNode (location: (1,12)-(1,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 42 │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) @@ -43,14 +48,17 @@ │ └── closing_loc: (1,14)-(1,15) = "|" ├── body: │ @ StatementsNode (location: (1,16)-(1,22)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ ArrayNode (location: (1,16)-(1,22)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 2) │ │ ├── @ LocalVariableReadNode (location: (1,17)-(1,18)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ └── @ LocalVariableReadNode (location: (1,20)-(1,21)) + │ │ ├── flags: ∅ │ │ ├── name: :b │ │ └── depth: 0 │ ├── opening_loc: (1,16)-(1,17) = "[" diff --git a/test/prism/snapshots/seattlerb/block_args_opt2.txt b/test/prism/snapshots/seattlerb/block_args_opt2.txt index a8d736dde73c94..7170768b1cb943 100644 --- a/test/prism/snapshots/seattlerb/block_args_opt2.txt +++ b/test/prism/snapshots/seattlerb/block_args_opt2.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,18)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,18)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,18)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,18)) + ├── flags: ∅ ├── locals: [:b, :c] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,16)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,6)-(1,14)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 2) │ │ │ ├── @ OptionalParameterNode (location: (1,6)-(1,9)) @@ -28,7 +33,7 @@ │ │ │ │ ├── operator_loc: (1,7)-(1,8) = "=" │ │ │ │ └── value: │ │ │ │ @ IntegerNode (location: (1,8)-(1,9)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── @ OptionalParameterNode (location: (1,11)-(1,14)) │ │ │ ├── flags: ∅ @@ -37,7 +42,7 @@ │ │ │ ├── operator_loc: (1,12)-(1,13) = "=" │ │ │ └── value: │ │ │ @ IntegerNode (location: (1,13)-(1,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) diff --git a/test/prism/snapshots/seattlerb/block_args_opt2_2.txt b/test/prism/snapshots/seattlerb/block_args_opt2_2.txt index 0403851ed7b7ea..34d04dbe54b4a0 100644 --- a/test/prism/snapshots/seattlerb/block_args_opt2_2.txt +++ b/test/prism/snapshots/seattlerb/block_args_opt2_2.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,35)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,35)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,35)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,35)) + ├── flags: ∅ ├── locals: [:a, :b, :c] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,23)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,22)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6)) │ │ │ ├── flags: ∅ @@ -31,7 +36,7 @@ │ │ │ │ ├── operator_loc: (1,10)-(1,11) = "=" │ │ │ │ └── value: │ │ │ │ @ IntegerNode (location: (1,12)-(1,14)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 42 │ │ │ └── @ OptionalParameterNode (location: (1,16)-(1,22)) │ │ │ ├── flags: ∅ @@ -40,7 +45,7 @@ │ │ │ ├── operator_loc: (1,18)-(1,19) = "=" │ │ │ └── value: │ │ │ @ IntegerNode (location: (1,20)-(1,22)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 24 │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) @@ -52,17 +57,21 @@ │ └── closing_loc: (1,22)-(1,23) = "|" ├── body: │ @ StatementsNode (location: (1,24)-(1,33)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ ArrayNode (location: (1,24)-(1,33)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 3) │ │ ├── @ LocalVariableReadNode (location: (1,25)-(1,26)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ ├── @ LocalVariableReadNode (location: (1,28)-(1,29)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :b │ │ │ └── depth: 0 │ │ └── @ LocalVariableReadNode (location: (1,31)-(1,32)) + │ │ ├── flags: ∅ │ │ ├── name: :c │ │ └── depth: 0 │ ├── opening_loc: (1,24)-(1,25) = "[" diff --git a/test/prism/snapshots/seattlerb/block_args_opt3.txt b/test/prism/snapshots/seattlerb/block_args_opt3.txt index ff4495019c7cea..508d062ce2e354 100644 --- a/test/prism/snapshots/seattlerb/block_args_opt3.txt +++ b/test/prism/snapshots/seattlerb/block_args_opt3.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,42)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,42)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,42)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,42)) + ├── flags: ∅ ├── locals: [:a, :b, :c, :d] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,27)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,26)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6)) │ │ │ ├── flags: ∅ @@ -31,7 +36,7 @@ │ │ │ │ ├── operator_loc: (1,10)-(1,11) = "=" │ │ │ │ └── value: │ │ │ │ @ IntegerNode (location: (1,12)-(1,14)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 42 │ │ │ └── @ OptionalParameterNode (location: (1,16)-(1,22)) │ │ │ ├── flags: ∅ @@ -40,7 +45,7 @@ │ │ │ ├── operator_loc: (1,18)-(1,19) = "=" │ │ │ └── value: │ │ │ @ IntegerNode (location: (1,20)-(1,22)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 24 │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) @@ -57,20 +62,25 @@ │ └── closing_loc: (1,26)-(1,27) = "|" ├── body: │ @ StatementsNode (location: (1,28)-(1,40)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ ArrayNode (location: (1,28)-(1,40)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 4) │ │ ├── @ LocalVariableReadNode (location: (1,29)-(1,30)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ ├── @ LocalVariableReadNode (location: (1,32)-(1,33)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :b │ │ │ └── depth: 0 │ │ ├── @ LocalVariableReadNode (location: (1,35)-(1,36)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :c │ │ │ └── depth: 0 │ │ └── @ LocalVariableReadNode (location: (1,38)-(1,39)) + │ │ ├── flags: ∅ │ │ ├── name: :d │ │ └── depth: 0 │ ├── opening_loc: (1,28)-(1,29) = "[" diff --git a/test/prism/snapshots/seattlerb/block_call_defn_call_block_call.txt b/test/prism/snapshots/seattlerb/block_call_defn_call_block_call.txt index 2e634dc937afc0..4e6ad90afd1257 100644 --- a/test/prism/snapshots/seattlerb/block_call_defn_call_block_call.txt +++ b/test/prism/snapshots/seattlerb/block_call_defn_call_block_call.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(4,11)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(4,11)) + ├── flags: ∅ └── body: (length: 2) ├── @ CallNode (location: (1,0)-(3,4)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :a @@ -15,11 +17,13 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ DefNode (location: (1,2)-(3,4)) + │ │ ├── flags: ∅ │ │ ├── name: :b │ │ ├── name_loc: (1,6)-(1,7) = "b" │ │ ├── receiver: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (1,8)-(1,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (1,8)-(1,9)) │ │ │ │ ├── flags: ∅ @@ -32,9 +36,10 @@ │ │ │ └── block: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (2,1)-(2,2)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (2,1)-(2,2)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :d @@ -53,7 +58,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ └── @ CallNode (location: (4,1)-(4,11)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (4,1)-(4,2)) │ ├── flags: variable_call, ignore_visibility @@ -73,6 +78,7 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (4,5)-(4,11)) + ├── flags: ∅ ├── locals: [] ├── parameters: ∅ ├── body: ∅ diff --git a/test/prism/snapshots/seattlerb/block_call_dot_op2_brace_block.txt b/test/prism/snapshots/seattlerb/block_call_dot_op2_brace_block.txt index e46104b868ca86..5f05b1f6ff4015 100644 --- a/test/prism/snapshots/seattlerb/block_call_dot_op2_brace_block.txt +++ b/test/prism/snapshots/seattlerb/block_call_dot_op2_brace_block.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,31)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,31)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,31)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (1,0)-(1,16)) │ ├── flags: ∅ @@ -40,13 +42,15 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (1,8)-(1,16)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (1,11)-(1,12)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (1,11)-(1,12)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :d @@ -65,11 +69,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,19)-(1,31)) + ├── flags: ∅ ├── locals: [:f] ├── parameters: │ @ BlockParametersNode (location: (1,22)-(1,25)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,23)-(1,24)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (1,23)-(1,24)) │ │ │ ├── flags: ∅ @@ -85,9 +92,10 @@ │ └── closing_loc: (1,24)-(1,25) = "|" ├── body: │ @ StatementsNode (location: (1,26)-(1,27)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,26)-(1,27)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :g diff --git a/test/prism/snapshots/seattlerb/block_call_dot_op2_cmd_args_do_block.txt b/test/prism/snapshots/seattlerb/block_call_dot_op2_cmd_args_do_block.txt index 05d076f8d6f593..ca141580b7d8fb 100644 --- a/test/prism/snapshots/seattlerb/block_call_dot_op2_cmd_args_do_block.txt +++ b/test/prism/snapshots/seattlerb/block_call_dot_op2_cmd_args_do_block.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,33)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,33)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,33)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (1,0)-(1,16)) │ ├── flags: ∅ @@ -40,13 +42,15 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (1,8)-(1,16)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (1,11)-(1,12)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (1,11)-(1,12)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :d @@ -78,11 +82,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,21)-(1,33)) + ├── flags: ∅ ├── locals: [:g] ├── parameters: │ @ BlockParametersNode (location: (1,24)-(1,27)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,25)-(1,26)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (1,25)-(1,26)) │ │ │ ├── flags: ∅ @@ -98,9 +105,10 @@ │ └── closing_loc: (1,26)-(1,27) = "|" ├── body: │ @ StatementsNode (location: (1,28)-(1,29)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,28)-(1,29)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :h diff --git a/test/prism/snapshots/seattlerb/block_call_operation_colon.txt b/test/prism/snapshots/seattlerb/block_call_operation_colon.txt index 9fd13b0dfcc830..cecd421263869f 100644 --- a/test/prism/snapshots/seattlerb/block_call_operation_colon.txt +++ b/test/prism/snapshots/seattlerb/block_call_operation_colon.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,15)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,15)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,15)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (1,0)-(1,12)) │ ├── flags: ∅ @@ -40,6 +42,7 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (1,6)-(1,12)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: ∅ diff --git a/test/prism/snapshots/seattlerb/block_call_operation_dot.txt b/test/prism/snapshots/seattlerb/block_call_operation_dot.txt index 43c19d33181013..5c661fb49a9a57 100644 --- a/test/prism/snapshots/seattlerb/block_call_operation_dot.txt +++ b/test/prism/snapshots/seattlerb/block_call_operation_dot.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,14)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,14)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,14)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (1,0)-(1,12)) │ ├── flags: ∅ @@ -40,6 +42,7 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (1,6)-(1,12)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: ∅ diff --git a/test/prism/snapshots/seattlerb/block_call_paren_call_block_call.txt b/test/prism/snapshots/seattlerb/block_call_paren_call_block_call.txt index 10c1780d374157..93c4b05f9bcddb 100644 --- a/test/prism/snapshots/seattlerb/block_call_paren_call_block_call.txt +++ b/test/prism/snapshots/seattlerb/block_call_paren_call_block_call.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(2,10)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(2,10)) + ├── flags: ∅ └── body: (length: 2) ├── @ CallNode (location: (1,0)-(1,5)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :a @@ -15,11 +17,13 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ ParenthesesNode (location: (1,2)-(1,5)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (1,3)-(1,4)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (1,3)-(1,4)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -33,7 +37,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ └── @ CallNode (location: (2,0)-(2,10)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (2,0)-(2,1)) │ ├── flags: variable_call, ignore_visibility @@ -53,6 +57,7 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (2,4)-(2,10)) + ├── flags: ∅ ├── locals: [] ├── parameters: ∅ ├── body: ∅ diff --git a/test/prism/snapshots/seattlerb/block_command_operation_colon.txt b/test/prism/snapshots/seattlerb/block_command_operation_colon.txt index 30fd6dafa00a50..c71fbe2f4a0a58 100644 --- a/test/prism/snapshots/seattlerb/block_command_operation_colon.txt +++ b/test/prism/snapshots/seattlerb/block_command_operation_colon.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,17)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,17)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,17)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (1,0)-(1,11)) │ ├── flags: ignore_visibility @@ -18,7 +20,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ SymbolNode (location: (1,2)-(1,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,2)-(1,3) = ":" │ │ ├── value_loc: (1,3)-(1,4) = "b" │ │ ├── closing_loc: ∅ @@ -26,6 +28,7 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (1,5)-(1,11)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: ∅ @@ -40,7 +43,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ SymbolNode (location: (1,15)-(1,17)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,15)-(1,16) = ":" │ ├── value_loc: (1,16)-(1,17) = "d" │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/block_command_operation_dot.txt b/test/prism/snapshots/seattlerb/block_command_operation_dot.txt index e4f69d360452b5..68d98c99b46e2c 100644 --- a/test/prism/snapshots/seattlerb/block_command_operation_dot.txt +++ b/test/prism/snapshots/seattlerb/block_command_operation_dot.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,16)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,16)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,16)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (1,0)-(1,11)) │ ├── flags: ignore_visibility @@ -18,7 +20,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ SymbolNode (location: (1,2)-(1,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,2)-(1,3) = ":" │ │ ├── value_loc: (1,3)-(1,4) = "b" │ │ ├── closing_loc: ∅ @@ -26,6 +28,7 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (1,5)-(1,11)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: ∅ @@ -40,7 +43,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ SymbolNode (location: (1,14)-(1,16)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,14)-(1,15) = ":" │ ├── value_loc: (1,15)-(1,16) = "d" │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/block_decomp_anon_splat_arg.txt b/test/prism/snapshots/seattlerb/block_decomp_anon_splat_arg.txt index e309ec1f98f49b..5628cacc97867d 100644 --- a/test/prism/snapshots/seattlerb/block_decomp_anon_splat_arg.txt +++ b/test/prism/snapshots/seattlerb/block_decomp_anon_splat_arg.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,14)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,14)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,14)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,16 +16,21 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,14)) + ├── flags: ∅ ├── locals: [:a] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,12)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,11)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ MultiTargetNode (location: (1,5)-(1,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 0) │ │ │ ├── rest: │ │ │ │ @ SplatNode (location: (1,6)-(1,7)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (1,6)-(1,7) = "*" │ │ │ │ └── expression: ∅ │ │ │ ├── rights: (length: 1) diff --git a/test/prism/snapshots/seattlerb/block_decomp_arg_splat.txt b/test/prism/snapshots/seattlerb/block_decomp_arg_splat.txt index 8d28fa7e02d391..b13e13d167ef8e 100644 --- a/test/prism/snapshots/seattlerb/block_decomp_arg_splat.txt +++ b/test/prism/snapshots/seattlerb/block_decomp_arg_splat.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,14)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,14)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,14)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -14,19 +16,24 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,14)) + ├── flags: ∅ ├── locals: [:b] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,12)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,11)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ MultiTargetNode (location: (1,5)-(1,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (1,6)-(1,7)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :b │ │ │ ├── rest: │ │ │ │ @ SplatNode (location: (1,9)-(1,10)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (1,9)-(1,10) = "*" │ │ │ │ └── expression: ∅ │ │ │ ├── rights: (length: 0) diff --git a/test/prism/snapshots/seattlerb/block_decomp_arg_splat_arg.txt b/test/prism/snapshots/seattlerb/block_decomp_arg_splat_arg.txt index 4f4a82acf594e8..ba0a6202cedc07 100644 --- a/test/prism/snapshots/seattlerb/block_decomp_arg_splat_arg.txt +++ b/test/prism/snapshots/seattlerb/block_decomp_arg_splat_arg.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,18)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,18)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,18)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,19 +16,24 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,18)) + ├── flags: ∅ ├── locals: [:a, :b, :c] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,16)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,15)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ MultiTargetNode (location: (1,5)-(1,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (1,6)-(1,7)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :a │ │ │ ├── rest: │ │ │ │ @ SplatNode (location: (1,9)-(1,11)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (1,9)-(1,10) = "*" │ │ │ │ └── expression: │ │ │ │ @ RequiredParameterNode (location: (1,10)-(1,11)) diff --git a/test/prism/snapshots/seattlerb/block_decomp_splat.txt b/test/prism/snapshots/seattlerb/block_decomp_splat.txt index 09d344012645f5..fd8ad4bda1fa46 100644 --- a/test/prism/snapshots/seattlerb/block_decomp_splat.txt +++ b/test/prism/snapshots/seattlerb/block_decomp_splat.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,12)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,12)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,12)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,16 +16,21 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,12)) + ├── flags: ∅ ├── locals: [:a] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,10)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,9)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ MultiTargetNode (location: (1,5)-(1,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 0) │ │ │ ├── rest: │ │ │ │ @ SplatNode (location: (1,6)-(1,8)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (1,6)-(1,7) = "*" │ │ │ │ └── expression: │ │ │ │ @ RequiredParameterNode (location: (1,7)-(1,8)) diff --git a/test/prism/snapshots/seattlerb/block_kw.txt b/test/prism/snapshots/seattlerb/block_kw.txt index f022637dae6dea..d76998ecb6636a 100644 --- a/test/prism/snapshots/seattlerb/block_kw.txt +++ b/test/prism/snapshots/seattlerb/block_kw.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,15)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,15)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,15)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :blah @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,5)-(1,15)) + ├── flags: ∅ ├── locals: [:k] ├── parameters: │ @ BlockParametersNode (location: (1,7)-(1,13)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,8)-(1,12)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -30,7 +35,7 @@ │ │ │ ├── name_loc: (1,8)-(1,10) = "k:" │ │ │ └── value: │ │ │ @ IntegerNode (location: (1,10)-(1,12)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 42 │ │ ├── keyword_rest: ∅ │ │ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/block_kw__required.txt b/test/prism/snapshots/seattlerb/block_kw__required.txt index 8a49c8bec7efe4..f04987d854fd10 100644 --- a/test/prism/snapshots/seattlerb/block_kw__required.txt +++ b/test/prism/snapshots/seattlerb/block_kw__required.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,16)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,16)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,16)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :blah @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,5)-(1,16)) + ├── flags: ∅ ├── locals: [:k] ├── parameters: │ @ BlockParametersNode (location: (1,8)-(1,12)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,9)-(1,11)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ diff --git a/test/prism/snapshots/seattlerb/block_kwarg_lvar.txt b/test/prism/snapshots/seattlerb/block_kwarg_lvar.txt index e77bf90a276b5b..861348f2a2a4a7 100644 --- a/test/prism/snapshots/seattlerb/block_kwarg_lvar.txt +++ b/test/prism/snapshots/seattlerb/block_kwarg_lvar.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,20)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,20)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,20)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :bl @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,3)-(1,20)) + ├── flags: ∅ ├── locals: [:kw] ├── parameters: │ @ BlockParametersNode (location: (1,5)-(1,15)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,6)-(1,14)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -30,7 +35,7 @@ │ │ │ ├── name_loc: (1,6)-(1,9) = "kw:" │ │ │ └── value: │ │ │ @ SymbolNode (location: (1,10)-(1,14)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (1,10)-(1,11) = ":" │ │ │ ├── value_loc: (1,11)-(1,14) = "val" │ │ │ ├── closing_loc: ∅ @@ -42,8 +47,10 @@ │ └── closing_loc: (1,14)-(1,15) = "|" ├── body: │ @ StatementsNode (location: (1,16)-(1,18)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ LocalVariableReadNode (location: (1,16)-(1,18)) + │ ├── flags: newline │ ├── name: :kw │ └── depth: 0 ├── opening_loc: (1,3)-(1,4) = "{" diff --git a/test/prism/snapshots/seattlerb/block_kwarg_lvar_multiple.txt b/test/prism/snapshots/seattlerb/block_kwarg_lvar_multiple.txt index a527c8c993d692..f1c1fef8a38c48 100644 --- a/test/prism/snapshots/seattlerb/block_kwarg_lvar_multiple.txt +++ b/test/prism/snapshots/seattlerb/block_kwarg_lvar_multiple.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,33)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,33)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,33)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :bl @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,3)-(1,33)) + ├── flags: ∅ ├── locals: [:kw, :kw2] ├── parameters: │ @ BlockParametersNode (location: (1,5)-(1,28)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,6)-(1,26)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -30,7 +35,7 @@ │ │ │ │ ├── name_loc: (1,6)-(1,9) = "kw:" │ │ │ │ └── value: │ │ │ │ @ SymbolNode (location: (1,10)-(1,14)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (1,10)-(1,11) = ":" │ │ │ │ ├── value_loc: (1,11)-(1,14) = "val" │ │ │ │ ├── closing_loc: ∅ @@ -41,7 +46,7 @@ │ │ │ ├── name_loc: (1,16)-(1,20) = "kw2:" │ │ │ └── value: │ │ │ @ SymbolNode (location: (1,21)-(1,26)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (1,21)-(1,22) = ":" │ │ │ ├── value_loc: (1,22)-(1,26) = "val2" │ │ │ ├── closing_loc: ∅ @@ -53,8 +58,10 @@ │ └── closing_loc: (1,27)-(1,28) = "|" ├── body: │ @ StatementsNode (location: (1,29)-(1,31)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ LocalVariableReadNode (location: (1,29)-(1,31)) + │ ├── flags: newline │ ├── name: :kw │ └── depth: 0 ├── opening_loc: (1,3)-(1,4) = "{" diff --git a/test/prism/snapshots/seattlerb/block_opt_arg.txt b/test/prism/snapshots/seattlerb/block_opt_arg.txt index 64dc928f14b326..39be11ac92d193 100644 --- a/test/prism/snapshots/seattlerb/block_opt_arg.txt +++ b/test/prism/snapshots/seattlerb/block_opt_arg.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,14)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,14)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,14)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,14)) + ├── flags: ∅ ├── locals: [:b, :c] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,12)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,11)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 1) │ │ │ └── @ OptionalParameterNode (location: (1,5)-(1,8)) @@ -28,7 +33,7 @@ │ │ │ ├── operator_loc: (1,6)-(1,7) = "=" │ │ │ └── value: │ │ │ @ IntegerNode (location: (1,7)-(1,8)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── rest: ∅ │ │ ├── posts: (length: 1) diff --git a/test/prism/snapshots/seattlerb/block_opt_splat.txt b/test/prism/snapshots/seattlerb/block_opt_splat.txt index c18df9c27d41ff..3898212dc00f64 100644 --- a/test/prism/snapshots/seattlerb/block_opt_splat.txt +++ b/test/prism/snapshots/seattlerb/block_opt_splat.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,17)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,17)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,17)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,17)) + ├── flags: ∅ ├── locals: [:b, :c] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,15)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,14)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 1) │ │ │ └── @ OptionalParameterNode (location: (1,5)-(1,10)) @@ -28,7 +33,7 @@ │ │ │ ├── operator_loc: (1,7)-(1,8) = "=" │ │ │ └── value: │ │ │ @ IntegerNode (location: (1,9)-(1,10)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── rest: │ │ │ @ RestParameterNode (location: (1,12)-(1,14)) diff --git a/test/prism/snapshots/seattlerb/block_opt_splat_arg_block_omfg.txt b/test/prism/snapshots/seattlerb/block_opt_splat_arg_block_omfg.txt index 3806809d2bd072..ed4857ad25a939 100644 --- a/test/prism/snapshots/seattlerb/block_opt_splat_arg_block_omfg.txt +++ b/test/prism/snapshots/seattlerb/block_opt_splat_arg_block_omfg.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,22)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,22)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,22)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,22)) + ├── flags: ∅ ├── locals: [:b, :c, :d, :e] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,20)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,19)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 1) │ │ │ └── @ OptionalParameterNode (location: (1,5)-(1,8)) @@ -28,7 +33,7 @@ │ │ │ ├── operator_loc: (1,6)-(1,7) = "=" │ │ │ └── value: │ │ │ @ IntegerNode (location: (1,7)-(1,8)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── rest: │ │ │ @ RestParameterNode (location: (1,10)-(1,12)) diff --git a/test/prism/snapshots/seattlerb/block_optarg.txt b/test/prism/snapshots/seattlerb/block_optarg.txt index 5da99aec799e30..2172571eb12da5 100644 --- a/test/prism/snapshots/seattlerb/block_optarg.txt +++ b/test/prism/snapshots/seattlerb/block_optarg.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,14)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,14)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,14)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,14)) + ├── flags: ∅ ├── locals: [:b] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,12)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,11)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 1) │ │ │ └── @ OptionalParameterNode (location: (1,5)-(1,11)) @@ -28,7 +33,7 @@ │ │ │ ├── operator_loc: (1,7)-(1,8) = "=" │ │ │ └── value: │ │ │ @ SymbolNode (location: (1,9)-(1,11)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (1,9)-(1,10) = ":" │ │ │ ├── value_loc: (1,10)-(1,11) = "c" │ │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/block_paren_splat.txt b/test/prism/snapshots/seattlerb/block_paren_splat.txt index ebd937904ccd4d..b6c2da679bb072 100644 --- a/test/prism/snapshots/seattlerb/block_paren_splat.txt +++ b/test/prism/snapshots/seattlerb/block_paren_splat.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,15)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,15)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,15)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -14,19 +16,24 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,15)) + ├── flags: ∅ ├── locals: [:b, :c] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,13)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,12)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ MultiTargetNode (location: (1,5)-(1,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (1,6)-(1,7)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :b │ │ │ ├── rest: │ │ │ │ @ SplatNode (location: (1,9)-(1,11)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (1,9)-(1,10) = "*" │ │ │ │ └── expression: │ │ │ │ @ RequiredParameterNode (location: (1,10)-(1,11)) diff --git a/test/prism/snapshots/seattlerb/block_reg_optarg.txt b/test/prism/snapshots/seattlerb/block_reg_optarg.txt index 53c43603a7ee5f..0173b92e8f08f6 100644 --- a/test/prism/snapshots/seattlerb/block_reg_optarg.txt +++ b/test/prism/snapshots/seattlerb/block_reg_optarg.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,17)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,17)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,17)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,17)) + ├── flags: ∅ ├── locals: [:b, :c] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,15)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,14)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (1,5)-(1,6)) │ │ │ ├── flags: ∅ @@ -31,7 +36,7 @@ │ │ │ ├── operator_loc: (1,10)-(1,11) = "=" │ │ │ └── value: │ │ │ @ SymbolNode (location: (1,12)-(1,14)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (1,12)-(1,13) = ":" │ │ │ ├── value_loc: (1,13)-(1,14) = "d" │ │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/block_return.txt b/test/prism/snapshots/seattlerb/block_return.txt index c863b28a22c902..0eee33c844e2b3 100644 --- a/test/prism/snapshots/seattlerb/block_return.txt +++ b/test/prism/snapshots/seattlerb/block_return.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,27)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,27)) + ├── flags: ∅ └── body: (length: 1) └── @ ReturnNode (location: (1,0)-(1,27)) - ├── flags: ∅ + ├── flags: newline ├── keyword_loc: (1,0)-(1,6) = "return" └── arguments: @ ArgumentsNode (location: (1,7)-(1,27)) @@ -34,11 +36,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,15)-(1,27)) + ├── flags: ∅ ├── locals: [:bar] ├── parameters: │ @ BlockParametersNode (location: (1,18)-(1,23)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,19)-(1,22)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (1,19)-(1,22)) │ │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/block_scope.txt b/test/prism/snapshots/seattlerb/block_scope.txt index a21a28b99375f8..ef659bb38ebe4f 100644 --- a/test/prism/snapshots/seattlerb/block_scope.txt +++ b/test/prism/snapshots/seattlerb/block_scope.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,10)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,10)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,10)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -14,9 +16,11 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,10)) + ├── flags: ∅ ├── locals: [:b] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,8)) + │ ├── flags: ∅ │ ├── parameters: ∅ │ ├── locals: (length: 1) │ │ └── @ BlockLocalVariableNode (location: (1,6)-(1,7)) diff --git a/test/prism/snapshots/seattlerb/block_splat_reg.txt b/test/prism/snapshots/seattlerb/block_splat_reg.txt index 617ff886225cc7..b5eb009c52dc89 100644 --- a/test/prism/snapshots/seattlerb/block_splat_reg.txt +++ b/test/prism/snapshots/seattlerb/block_splat_reg.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,13)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,13)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,13)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,13)) + ├── flags: ∅ ├── locals: [:b, :c] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,11)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,10)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: diff --git a/test/prism/snapshots/seattlerb/bug169.txt b/test/prism/snapshots/seattlerb/bug169.txt index e4fb47a6de9641..c55df4257ded69 100644 --- a/test/prism/snapshots/seattlerb/bug169.txt +++ b/test/prism/snapshots/seattlerb/bug169.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,7)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,7)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,7)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :m @@ -15,12 +17,14 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ ParenthesesNode (location: (1,2)-(1,4)) + │ ├── flags: ∅ │ ├── body: ∅ │ ├── opening_loc: (1,2)-(1,3) = "(" │ └── closing_loc: (1,3)-(1,4) = ")" ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,5)-(1,7)) + ├── flags: ∅ ├── locals: [] ├── parameters: ∅ ├── body: ∅ diff --git a/test/prism/snapshots/seattlerb/bug179.txt b/test/prism/snapshots/seattlerb/bug179.txt index d7695bc7a74f40..4392d5ec27c6cb 100644 --- a/test/prism/snapshots/seattlerb/bug179.txt +++ b/test/prism/snapshots/seattlerb/bug179.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,9)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,9)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,9)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :p @@ -18,11 +20,13 @@ │ ├── flags: ∅ │ ├── left: │ │ @ ParenthesesNode (location: (1,2)-(1,4)) + │ │ ├── flags: ∅ │ │ ├── body: ∅ │ │ ├── opening_loc: (1,2)-(1,3) = "(" │ │ └── closing_loc: (1,3)-(1,4) = ")" │ ├── right: │ │ @ NilNode (location: (1,6)-(1,9)) + │ │ └── flags: static_literal │ └── operator_loc: (1,4)-(1,6) = ".." ├── closing_loc: ∅ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/bug190.txt b/test/prism/snapshots/seattlerb/bug190.txt index b261a166cf9da3..004ca16a60c502 100644 --- a/test/prism/snapshots/seattlerb/bug190.txt +++ b/test/prism/snapshots/seattlerb/bug190.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,6)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,6)) + ├── flags: ∅ └── body: (length: 1) └── @ RegularExpressionNode (location: (1,0)-(1,6)) - ├── flags: forced_us_ascii_encoding + ├── flags: newline, static_literal, forced_us_ascii_encoding ├── opening_loc: (1,0)-(1,3) = "%r'" ├── content_loc: (1,3)-(1,5) = "\\'" ├── closing_loc: (1,5)-(1,6) = "'" diff --git a/test/prism/snapshots/seattlerb/bug191.txt b/test/prism/snapshots/seattlerb/bug191.txt index 69835ab1d033c7..3977eb95195d76 100644 --- a/test/prism/snapshots/seattlerb/bug191.txt +++ b/test/prism/snapshots/seattlerb/bug191.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(3,9)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,9)) + ├── flags: ∅ └── body: (length: 2) ├── @ IfNode (location: (1,0)-(1,9)) + │ ├── flags: newline │ ├── if_keyword_loc: ∅ │ ├── predicate: │ │ @ CallNode (location: (1,0)-(1,1)) @@ -19,21 +22,24 @@ │ ├── then_keyword_loc: (1,2)-(1,3) = "?" │ ├── statements: │ │ @ StatementsNode (location: (1,4)-(1,6)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ StringNode (location: (1,4)-(1,6)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── opening_loc: (1,4)-(1,5) = "\"" │ │ ├── content_loc: (1,5)-(1,5) = "" │ │ ├── closing_loc: (1,5)-(1,6) = "\"" │ │ └── unescaped: "" │ ├── consequent: │ │ @ ElseNode (location: (1,6)-(1,9)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (1,6)-(1,7) = ":" │ │ ├── statements: │ │ │ @ StatementsNode (location: (1,8)-(1,9)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (1,8)-(1,9)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -45,6 +51,7 @@ │ │ └── end_keyword_loc: ∅ │ └── end_keyword_loc: ∅ └── @ IfNode (location: (3,0)-(3,9)) + ├── flags: newline ├── if_keyword_loc: ∅ ├── predicate: │ @ CallNode (location: (3,0)-(3,1)) @@ -60,21 +67,24 @@ ├── then_keyword_loc: (3,2)-(3,3) = "?" ├── statements: │ @ StatementsNode (location: (3,4)-(3,6)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ StringNode (location: (3,4)-(3,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (3,4)-(3,5) = "'" │ ├── content_loc: (3,5)-(3,5) = "" │ ├── closing_loc: (3,5)-(3,6) = "'" │ └── unescaped: "" ├── consequent: │ @ ElseNode (location: (3,6)-(3,9)) + │ ├── flags: ∅ │ ├── else_keyword_loc: (3,6)-(3,7) = ":" │ ├── statements: │ │ @ StatementsNode (location: (3,8)-(3,9)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (3,8)-(3,9)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :b diff --git a/test/prism/snapshots/seattlerb/bug202.txt b/test/prism/snapshots/seattlerb/bug202.txt index 377b53727ed67e..f907081c208a28 100644 --- a/test/prism/snapshots/seattlerb/bug202.txt +++ b/test/prism/snapshots/seattlerb/bug202.txt @@ -1,22 +1,26 @@ @ ProgramNode (location: (1,0)-(2,10)) +├── flags: ∅ ├── locals: [:测试] └── statements: @ StatementsNode (location: (1,0)-(2,10)) + ├── flags: ∅ └── body: (length: 2) ├── @ GlobalVariableWriteNode (location: (1,0)-(1,11)) + │ ├── flags: newline │ ├── name: :$测试 │ ├── name_loc: (1,0)-(1,7) = "$测试" │ ├── value: │ │ @ IntegerNode (location: (1,10)-(1,11)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (1,8)-(1,9) = "=" └── @ LocalVariableWriteNode (location: (2,0)-(2,10)) + ├── flags: newline ├── name: :测试 ├── depth: 0 ├── name_loc: (2,0)-(2,6) = "测试" ├── value: │ @ IntegerNode (location: (2,9)-(2,10)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 └── operator_loc: (2,7)-(2,8) = "=" diff --git a/test/prism/snapshots/seattlerb/bug236.txt b/test/prism/snapshots/seattlerb/bug236.txt index 203a39a793a2cc..792020dc78b52f 100644 --- a/test/prism/snapshots/seattlerb/bug236.txt +++ b/test/prism/snapshots/seattlerb/bug236.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(3,6)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,6)) + ├── flags: ∅ └── body: (length: 2) ├── @ CallNode (location: (1,0)-(1,7)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :x @@ -14,11 +16,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (1,1)-(1,7)) + │ ├── flags: ∅ │ ├── locals: [:a] │ ├── parameters: │ │ @ BlockParametersNode (location: (1,2)-(1,6)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (1,3)-(1,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (1,3)-(1,4)) │ │ │ │ ├── flags: ∅ @@ -26,6 +31,7 @@ │ │ │ ├── optionals: (length: 0) │ │ │ ├── rest: │ │ │ │ @ ImplicitRestNode (location: (1,4)-(1,5)) + │ │ │ │ └── flags: ∅ │ │ │ ├── posts: (length: 0) │ │ │ ├── keywords: (length: 0) │ │ │ ├── keyword_rest: ∅ @@ -37,7 +43,7 @@ │ ├── opening_loc: (1,1)-(1,2) = "{" │ └── closing_loc: (1,6)-(1,7) = "}" └── @ CallNode (location: (3,0)-(3,6)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :x @@ -47,11 +53,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (3,1)-(3,6)) + ├── flags: ∅ ├── locals: [:a] ├── parameters: │ @ BlockParametersNode (location: (3,2)-(3,5)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (3,3)-(3,4)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (3,3)-(3,4)) │ │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/bug290.txt b/test/prism/snapshots/seattlerb/bug290.txt index 4f1e673c4b3dc1..85d75c0d16132f 100644 --- a/test/prism/snapshots/seattlerb/bug290.txt +++ b/test/prism/snapshots/seattlerb/bug290.txt @@ -1,15 +1,19 @@ @ ProgramNode (location: (1,0)-(3,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,3)) + ├── flags: ∅ └── body: (length: 1) └── @ BeginNode (location: (1,0)-(3,3)) + ├── flags: newline ├── begin_keyword_loc: (1,0)-(1,5) = "begin" ├── statements: │ @ StatementsNode (location: (2,2)-(2,5)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (2,2)-(2,5)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo diff --git a/test/prism/snapshots/seattlerb/bug_187.txt b/test/prism/snapshots/seattlerb/bug_187.txt index ae72675e5cfb1a..6a5786e34ab08b 100644 --- a/test/prism/snapshots/seattlerb/bug_187.txt +++ b/test/prism/snapshots/seattlerb/bug_187.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(3,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(3,3)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :private @@ -15,15 +17,17 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ DefNode (location: (1,8)-(3,3)) + │ ├── flags: ∅ │ ├── name: :f │ ├── name_loc: (1,12)-(1,13) = "f" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (2,0)-(2,10)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (2,0)-(2,10)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ CallNode (location: (2,0)-(2,1)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -43,6 +47,7 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (2,4)-(2,10)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── parameters: ∅ │ │ ├── body: ∅ diff --git a/test/prism/snapshots/seattlerb/bug_215.txt b/test/prism/snapshots/seattlerb/bug_215.txt index de7716335e9ffd..ee82b4f1da9982 100644 --- a/test/prism/snapshots/seattlerb/bug_215.txt +++ b/test/prism/snapshots/seattlerb/bug_215.txt @@ -1,12 +1,15 @@ @ ProgramNode (location: (1,0)-(1,13)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,13)) + ├── flags: ∅ └── body: (length: 1) └── @ UndefNode (location: (1,0)-(1,13)) + ├── flags: newline ├── names: (length: 1) │ └── @ SymbolNode (location: (1,6)-(1,13)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,6)-(1,9) = "%s(" │ ├── value_loc: (1,9)-(1,12) = "foo" │ ├── closing_loc: (1,12)-(1,13) = ")" diff --git a/test/prism/snapshots/seattlerb/bug_249.txt b/test/prism/snapshots/seattlerb/bug_249.txt index ad61501a0762ca..06daa80e41d8f0 100644 --- a/test/prism/snapshots/seattlerb/bug_249.txt +++ b/test/prism/snapshots/seattlerb/bug_249.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(4,28)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(4,28)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(4,28)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :mount @@ -18,13 +20,16 @@ │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ ParenthesesNode (location: (1,6)-(4,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (1,7)-(4,4)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (1,7)-(4,4)) - │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── flags: newline │ │ │ │ ├── receiver: │ │ │ │ │ @ ConstantReadNode (location: (1,7)-(1,12)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── name: :Class │ │ │ │ ├── call_operator_loc: (1,12)-(1,13) = "." │ │ │ │ ├── name: :new @@ -34,12 +39,15 @@ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── block: │ │ │ │ @ BlockNode (location: (1,17)-(4,4)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── locals: [] │ │ │ │ ├── parameters: ∅ │ │ │ │ ├── body: │ │ │ │ │ @ StatementsNode (location: (2,0)-(3,3)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ DefNode (location: (2,0)-(3,3)) + │ │ │ │ │ ├── flags: newline │ │ │ │ │ ├── name: :initialize │ │ │ │ │ ├── name_loc: (2,4)-(2,14) = "initialize" │ │ │ │ │ ├── receiver: ∅ @@ -67,9 +75,10 @@ │ ├── flags: symbol_keys │ └── elements: (length: 1) │ └── @ AssocNode (location: (4,11)-(4,28)) + │ ├── flags: ∅ │ ├── key: │ │ @ SymbolNode (location: (4,11)-(4,14)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (4,11)-(4,12) = ":" │ │ ├── value_loc: (4,12)-(4,14) = "at" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/bug_and.txt b/test/prism/snapshots/seattlerb/bug_and.txt index 3daf505e5f160d..89eb403b4a8061 100644 --- a/test/prism/snapshots/seattlerb/bug_and.txt +++ b/test/prism/snapshots/seattlerb/bug_and.txt @@ -1,20 +1,27 @@ @ ProgramNode (location: (1,0)-(4,11)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(4,11)) + ├── flags: ∅ └── body: (length: 2) ├── @ AndNode (location: (1,0)-(2,4)) + │ ├── flags: newline │ ├── left: │ │ @ TrueNode (location: (1,0)-(1,4)) + │ │ └── flags: static_literal │ ├── right: │ │ @ TrueNode (location: (2,0)-(2,4)) + │ │ └── flags: static_literal │ └── operator_loc: (1,5)-(1,8) = "and" └── @ AndNode (location: (4,0)-(4,11)) + ├── flags: newline ├── left: │ @ TrueNode (location: (4,0)-(4,4)) + │ └── flags: static_literal ├── right: │ @ ArrayNode (location: (4,9)-(4,11)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 0) │ ├── opening_loc: (4,9)-(4,10) = "[" │ └── closing_loc: (4,10)-(4,11) = "]" diff --git a/test/prism/snapshots/seattlerb/bug_args__19.txt b/test/prism/snapshots/seattlerb/bug_args__19.txt index f451bd01722fca..5b1d897718a323 100644 --- a/test/prism/snapshots/seattlerb/bug_args__19.txt +++ b/test/prism/snapshots/seattlerb/bug_args__19.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,16)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,16)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,16)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,13 +16,17 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,16)) + ├── flags: ∅ ├── locals: [:a, :b] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,12)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,11)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ MultiTargetNode (location: (1,5)-(1,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 2) │ │ │ │ ├── @ RequiredParameterNode (location: (1,6)-(1,7)) │ │ │ │ │ ├── flags: ∅ @@ -43,9 +49,10 @@ │ └── closing_loc: (1,11)-(1,12) = "|" ├── body: │ @ StatementsNode (location: (1,13)-(1,14)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,13)-(1,14)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :d diff --git a/test/prism/snapshots/seattlerb/bug_args_masgn.txt b/test/prism/snapshots/seattlerb/bug_args_masgn.txt index 297979c182c1a9..6456d82ecc3130 100644 --- a/test/prism/snapshots/seattlerb/bug_args_masgn.txt +++ b/test/prism/snapshots/seattlerb/bug_args_masgn.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,17)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,17)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,17)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,13 +16,17 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,17)) + ├── flags: ∅ ├── locals: [:a, :b, :c] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,15)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,14)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 2) │ │ │ ├── @ MultiTargetNode (location: (1,5)-(1,11)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── lefts: (length: 2) │ │ │ │ │ ├── @ RequiredParameterNode (location: (1,6)-(1,7)) │ │ │ │ │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/bug_args_masgn2.txt b/test/prism/snapshots/seattlerb/bug_args_masgn2.txt index 6bec9187b3b578..bd9fc6116115d0 100644 --- a/test/prism/snapshots/seattlerb/bug_args_masgn2.txt +++ b/test/prism/snapshots/seattlerb/bug_args_masgn2.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,22)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,22)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,22)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,15 +16,20 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,22)) + ├── flags: ∅ ├── locals: [:a, :b, :c, :d] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,20)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,19)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 2) │ │ │ ├── @ MultiTargetNode (location: (1,5)-(1,16)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── lefts: (length: 2) │ │ │ │ │ ├── @ MultiTargetNode (location: (1,6)-(1,12)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ ├── lefts: (length: 2) │ │ │ │ │ │ │ ├── @ RequiredParameterNode (location: (1,7)-(1,8)) │ │ │ │ │ │ │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/bug_args_masgn_outer_parens__19.txt b/test/prism/snapshots/seattlerb/bug_args_masgn_outer_parens__19.txt index 42a060d02aaf66..ad62bd4daa50db 100644 --- a/test/prism/snapshots/seattlerb/bug_args_masgn_outer_parens__19.txt +++ b/test/prism/snapshots/seattlerb/bug_args_masgn_outer_parens__19.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,19)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,19)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,19)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,15 +16,20 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,19)) + ├── flags: ∅ ├── locals: [:k, :v, :i] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,17)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,16)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ MultiTargetNode (location: (1,5)-(1,16)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 2) │ │ │ │ ├── @ MultiTargetNode (location: (1,6)-(1,12)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── lefts: (length: 2) │ │ │ │ │ │ ├── @ RequiredParameterNode (location: (1,7)-(1,8)) │ │ │ │ │ │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/bug_call_arglist_parens.txt b/test/prism/snapshots/seattlerb/bug_call_arglist_parens.txt index 53d6f9220b2c0d..6954854baeba87 100644 --- a/test/prism/snapshots/seattlerb/bug_call_arglist_parens.txt +++ b/test/prism/snapshots/seattlerb/bug_call_arglist_parens.txt @@ -1,18 +1,22 @@ @ ProgramNode (location: (1,6)-(11,9)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,6)-(11,9)) + ├── flags: ∅ └── body: (length: 3) ├── @ DefNode (location: (1,6)-(3,9)) + │ ├── flags: newline │ ├── name: :f │ ├── name_loc: (1,10)-(1,11) = "f" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (2,8)-(2,17)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (2,8)-(2,17)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :g @@ -23,16 +27,18 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 2) │ │ │ ├── @ ParenthesesNode (location: (2,10)-(2,14)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── body: │ │ │ │ │ @ StatementsNode (location: (2,12)-(2,13)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ IntegerNode (location: (2,12)-(2,13)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: newline, static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ ├── opening_loc: (2,10)-(2,11) = "(" │ │ │ │ └── closing_loc: (2,13)-(2,14) = ")" │ │ │ └── @ IntegerNode (location: (2,16)-(2,17)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ @@ -44,15 +50,17 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (3,6)-(3,9) = "end" ├── @ DefNode (location: (6,6)-(8,9)) + │ ├── flags: newline │ ├── name: :f │ ├── name_loc: (6,10)-(6,11) = "f" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (7,8)-(7,16)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (7,8)-(7,16)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :g @@ -63,16 +71,18 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 2) │ │ │ ├── @ ParenthesesNode (location: (7,10)-(7,13)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── body: │ │ │ │ │ @ StatementsNode (location: (7,11)-(7,12)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ IntegerNode (location: (7,11)-(7,12)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: newline, static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ ├── opening_loc: (7,10)-(7,11) = "(" │ │ │ │ └── closing_loc: (7,12)-(7,13) = ")" │ │ │ └── @ IntegerNode (location: (7,15)-(7,16)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ @@ -84,7 +94,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (8,6)-(8,9) = "end" └── @ CallNode (location: (11,0)-(11,9)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :g @@ -95,16 +105,18 @@ │ ├── flags: ∅ │ └── arguments: (length: 2) │ ├── @ ParenthesesNode (location: (11,2)-(11,6)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (11,4)-(11,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ IntegerNode (location: (11,4)-(11,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: newline, static_literal, decimal │ │ │ └── value: 1 │ │ ├── opening_loc: (11,2)-(11,3) = "(" │ │ └── closing_loc: (11,5)-(11,6) = ")" │ └── @ IntegerNode (location: (11,8)-(11,9)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── closing_loc: ∅ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/bug_case_when_regexp.txt b/test/prism/snapshots/seattlerb/bug_case_when_regexp.txt index 0cc1ca05e1f247..f0d3c3d23c8465 100644 --- a/test/prism/snapshots/seattlerb/bug_case_when_regexp.txt +++ b/test/prism/snapshots/seattlerb/bug_case_when_regexp.txt @@ -1,22 +1,26 @@ @ ProgramNode (location: (1,0)-(1,26)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,26)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseNode (location: (1,0)-(1,26)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "x" │ ├── closing_loc: ∅ │ └── unescaped: "x" ├── conditions: (length: 1) │ └── @ WhenNode (location: (1,9)-(1,22)) + │ ├── flags: ∅ │ ├── keyword_loc: (1,9)-(1,13) = "when" │ ├── conditions: (length: 1) │ │ └── @ RegularExpressionNode (location: (1,14)-(1,17)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,14)-(1,15) = "/" │ │ ├── content_loc: (1,15)-(1,16) = "x" │ │ ├── closing_loc: (1,16)-(1,17) = "/" diff --git a/test/prism/snapshots/seattlerb/bug_comma.txt b/test/prism/snapshots/seattlerb/bug_comma.txt index af886999b5e512..0d65d2805a5672 100644 --- a/test/prism/snapshots/seattlerb/bug_comma.txt +++ b/test/prism/snapshots/seattlerb/bug_comma.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(1,24)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,24)) + ├── flags: ∅ └── body: (length: 1) └── @ IfNode (location: (1,0)-(1,24)) + ├── flags: newline ├── if_keyword_loc: (1,0)-(1,2) = "if" ├── predicate: │ @ CallNode (location: (1,3)-(1,15)) diff --git a/test/prism/snapshots/seattlerb/bug_cond_pct.txt b/test/prism/snapshots/seattlerb/bug_cond_pct.txt index cbf3bc3ef02da1..69d9b7e1755e5e 100644 --- a/test/prism/snapshots/seattlerb/bug_cond_pct.txt +++ b/test/prism/snapshots/seattlerb/bug_cond_pct.txt @@ -1,16 +1,20 @@ @ ProgramNode (location: (1,0)-(1,28)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,28)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseNode (location: (1,0)-(1,28)) + ├── flags: newline ├── predicate: ∅ ├── conditions: (length: 1) │ └── @ WhenNode (location: (1,6)-(1,23)) + │ ├── flags: ∅ │ ├── keyword_loc: (1,6)-(1,10) = "when" │ ├── conditions: (length: 1) │ │ └── @ RegularExpressionNode (location: (1,11)-(1,23)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,11)-(1,14) = "%r%" │ │ ├── content_loc: (1,14)-(1,22) = "blahblah" │ │ ├── closing_loc: (1,22)-(1,23) = "%" diff --git a/test/prism/snapshots/seattlerb/bug_hash_args.txt b/test/prism/snapshots/seattlerb/bug_hash_args.txt index e138db4d4914d3..cd90f0ebc60a0e 100644 --- a/test/prism/snapshots/seattlerb/bug_hash_args.txt +++ b/test/prism/snapshots/seattlerb/bug_hash_args.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,19)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,19)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,19)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :foo @@ -15,7 +17,7 @@ │ ├── flags: contains_keywords │ └── arguments: (length: 2) │ ├── @ SymbolNode (location: (1,4)-(1,8)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,4)-(1,5) = ":" │ │ ├── value_loc: (1,5)-(1,8) = "bar" │ │ ├── closing_loc: ∅ @@ -24,15 +26,17 @@ │ ├── flags: symbol_keys │ └── elements: (length: 1) │ └── @ AssocNode (location: (1,10)-(1,18)) + │ ├── flags: static_literal │ ├── key: │ │ @ SymbolNode (location: (1,10)-(1,14)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (1,10)-(1,13) = "baz" │ │ ├── closing_loc: (1,13)-(1,14) = ":" │ │ └── unescaped: "baz" │ ├── value: │ │ @ NilNode (location: (1,15)-(1,18)) + │ │ └── flags: static_literal │ └── operator_loc: ∅ ├── closing_loc: (1,18)-(1,19) = ")" └── block: ∅ 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 fe2d7f73c9730c..8c06cabf1c245c 100644 --- a/test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt +++ b/test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,20)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,20)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,20)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :foo @@ -15,7 +17,7 @@ │ ├── flags: contains_keywords │ └── arguments: (length: 2) │ ├── @ SymbolNode (location: (1,4)-(1,8)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,4)-(1,5) = ":" │ │ ├── value_loc: (1,5)-(1,8) = "bar" │ │ ├── closing_loc: ∅ @@ -24,15 +26,17 @@ │ ├── flags: symbol_keys │ └── elements: (length: 1) │ └── @ AssocNode (location: (1,10)-(1,18)) + │ ├── flags: static_literal │ ├── key: │ │ @ SymbolNode (location: (1,10)-(1,14)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (1,10)-(1,13) = "baz" │ │ ├── closing_loc: (1,13)-(1,14) = ":" │ │ └── unescaped: "baz" │ ├── value: │ │ @ NilNode (location: (1,15)-(1,18)) + │ │ └── flags: static_literal │ └── operator_loc: ∅ ├── closing_loc: (1,19)-(1,20) = ")" └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/bug_hash_interp_array.txt b/test/prism/snapshots/seattlerb/bug_hash_interp_array.txt index 433fb024115ff0..3d9fc56850b08e 100644 --- a/test/prism/snapshots/seattlerb/bug_hash_interp_array.txt +++ b/test/prism/snapshots/seattlerb/bug_hash_interp_array.txt @@ -1,24 +1,30 @@ @ ProgramNode (location: (1,0)-(1,13)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,13)) + ├── flags: ∅ └── body: (length: 1) └── @ HashNode (location: (1,0)-(1,13)) + ├── flags: newline ├── opening_loc: (1,0)-(1,1) = "{" ├── elements: (length: 1) │ └── @ AssocNode (location: (1,2)-(1,11)) + │ ├── flags: ∅ │ ├── key: │ │ @ InterpolatedSymbolNode (location: (1,2)-(1,8)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (1,2)-(1,3) = "\"" │ │ ├── parts: (length: 1) │ │ │ └── @ EmbeddedStatementsNode (location: (1,3)-(1,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (1,3)-(1,5) = "\#{" │ │ │ ├── statements: ∅ │ │ │ └── closing_loc: (1,5)-(1,6) = "}" │ │ └── closing_loc: (1,6)-(1,8) = "\":" │ ├── value: │ │ @ ArrayNode (location: (1,9)-(1,11)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── elements: (length: 0) │ │ ├── opening_loc: (1,9)-(1,10) = "[" │ │ └── closing_loc: (1,10)-(1,11) = "]" diff --git a/test/prism/snapshots/seattlerb/bug_masgn_right.txt b/test/prism/snapshots/seattlerb/bug_masgn_right.txt index b4c75c4607e221..e5b635d802d3c0 100644 --- a/test/prism/snapshots/seattlerb/bug_masgn_right.txt +++ b/test/prism/snapshots/seattlerb/bug_masgn_right.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,17)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,17)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,17)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,16 +16,20 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,17)) + ├── flags: ∅ ├── locals: [:a, :b, :c] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,15)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,14)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 2) │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :a │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 2) │ │ │ │ ├── @ RequiredParameterNode (location: (1,9)-(1,10)) │ │ │ │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/bug_not_parens.txt b/test/prism/snapshots/seattlerb/bug_not_parens.txt index 9e4a416d4aa2aa..163fb79564f8c3 100644 --- a/test/prism/snapshots/seattlerb/bug_not_parens.txt +++ b/test/prism/snapshots/seattlerb/bug_not_parens.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,6)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,6)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,6)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (1,4)-(1,5)) │ ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/seattlerb/bug_op_asgn_rescue.txt b/test/prism/snapshots/seattlerb/bug_op_asgn_rescue.txt index 33016f32f86369..9da753aece04eb 100644 --- a/test/prism/snapshots/seattlerb/bug_op_asgn_rescue.txt +++ b/test/prism/snapshots/seattlerb/bug_op_asgn_rescue.txt @@ -1,13 +1,17 @@ @ ProgramNode (location: (1,0)-(1,18)) +├── flags: ∅ ├── locals: [:a] └── statements: @ StatementsNode (location: (1,0)-(1,18)) + ├── flags: ∅ └── body: (length: 1) └── @ LocalVariableOrWriteNode (location: (1,0)-(1,18)) + ├── flags: newline ├── name_loc: (1,0)-(1,1) = "a" ├── operator_loc: (1,2)-(1,5) = "||=" ├── value: │ @ RescueModifierNode (location: (1,6)-(1,18)) + │ ├── flags: ∅ │ ├── expression: │ │ @ CallNode (location: (1,6)-(1,7)) │ │ ├── flags: variable_call, ignore_visibility @@ -22,5 +26,6 @@ │ ├── keyword_loc: (1,8)-(1,14) = "rescue" │ └── rescue_expression: │ @ NilNode (location: (1,15)-(1,18)) + │ └── flags: static_literal ├── name: :a └── depth: 0 diff --git a/test/prism/snapshots/seattlerb/call_and.txt b/test/prism/snapshots/seattlerb/call_and.txt index d3e88b3f9e418c..640f355c4a1fb8 100644 --- a/test/prism/snapshots/seattlerb/call_and.txt +++ b/test/prism/snapshots/seattlerb/call_and.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,5)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,5)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,5)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── call_operator_loc: ∅ ├── name: :& @@ -18,7 +20,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (1,4)-(1,5)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── closing_loc: ∅ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/call_arg_assoc.txt b/test/prism/snapshots/seattlerb/call_arg_assoc.txt index f489bc7f191e48..b8ec9070317132 100644 --- a/test/prism/snapshots/seattlerb/call_arg_assoc.txt +++ b/test/prism/snapshots/seattlerb/call_arg_assoc.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,10)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,10)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,10)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -15,19 +17,20 @@ │ ├── flags: contains_keywords │ └── arguments: (length: 2) │ ├── @ IntegerNode (location: (1,2)-(1,3)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── @ KeywordHashNode (location: (1,5)-(1,9)) │ ├── flags: ∅ │ └── elements: (length: 1) │ └── @ AssocNode (location: (1,5)-(1,9)) + │ ├── flags: static_literal │ ├── key: │ │ @ IntegerNode (location: (1,5)-(1,6)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── value: │ │ @ IntegerNode (location: (1,8)-(1,9)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 3 │ └── operator_loc: (1,6)-(1,8) = "=>" ├── closing_loc: (1,9)-(1,10) = ")" diff --git a/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt b/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt index 5b191396de5150..11142b272191e5 100644 --- a/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt +++ b/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,16)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,16)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,16)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -15,28 +17,30 @@ │ ├── flags: contains_keywords, contains_keyword_splat │ └── arguments: (length: 2) │ ├── @ IntegerNode (location: (1,2)-(1,3)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── @ KeywordHashNode (location: (1,5)-(1,15)) │ ├── flags: ∅ │ └── elements: (length: 2) │ ├── @ AssocNode (location: (1,5)-(1,10)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (1,5)-(1,8)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (1,5)-(1,7) = "kw" │ │ │ ├── closing_loc: (1,7)-(1,8) = ":" │ │ │ └── unescaped: "kw" │ │ ├── value: │ │ │ @ IntegerNode (location: (1,9)-(1,10)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── operator_loc: ∅ │ └── @ AssocSplatNode (location: (1,12)-(1,15)) + │ ├── flags: ∅ │ ├── value: │ │ @ IntegerNode (location: (1,14)-(1,15)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 3 │ └── operator_loc: (1,12)-(1,14) = "**" ├── closing_loc: (1,15)-(1,16) = ")" diff --git a/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt b/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt index f95b80cf7db49a..853f7103e3e673 100644 --- a/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt +++ b/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,9)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,9)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,9)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -28,9 +30,10 @@ │ ├── flags: ∅ │ └── elements: (length: 1) │ └── @ AssocSplatNode (location: (1,5)-(1,8)) + │ ├── flags: ∅ │ ├── value: │ │ @ IntegerNode (location: (1,7)-(1,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (1,5)-(1,7) = "**" ├── closing_loc: (1,8)-(1,9) = ")" diff --git a/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt b/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt index 8946206a3f5bc8..615bee5b4a9681 100644 --- a/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt +++ b/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(5,8)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(5,8)) + ├── flags: ∅ └── body: (length: 3) ├── @ CallNode (location: (1,0)-(1,11)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :x @@ -18,14 +20,18 @@ │ │ ├── flags: ∅ │ │ └── elements: (length: 1) │ │ └── @ AssocNode (location: (1,2)-(1,11)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ InterpolatedSymbolNode (location: (1,2)-(1,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (1,2)-(1,3) = "\"" │ │ │ ├── parts: (length: 1) │ │ │ │ └── @ EmbeddedStatementsNode (location: (1,3)-(1,7)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── opening_loc: (1,3)-(1,5) = "\#{" │ │ │ │ ├── statements: │ │ │ │ │ @ StatementsNode (location: (1,5)-(1,6)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ CallNode (location: (1,5)-(1,6)) │ │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -41,13 +47,13 @@ │ │ │ └── closing_loc: (1,7)-(1,9) = "\":" │ │ ├── value: │ │ │ @ IntegerNode (location: (1,9)-(1,11)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 42 │ │ └── operator_loc: ∅ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (3,0)-(3,8)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :x @@ -61,22 +67,23 @@ │ │ ├── flags: symbol_keys │ │ └── elements: (length: 1) │ │ └── @ AssocNode (location: (3,2)-(3,8)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (3,2)-(3,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (3,2)-(3,3) = "\"" │ │ │ ├── value_loc: (3,3)-(3,4) = "k" │ │ │ ├── closing_loc: (3,4)-(3,6) = "\":" │ │ │ └── unescaped: "k" │ │ ├── value: │ │ │ @ IntegerNode (location: (3,6)-(3,8)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 42 │ │ └── operator_loc: ∅ │ ├── closing_loc: ∅ │ └── block: ∅ └── @ CallNode (location: (5,0)-(5,8)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :x @@ -90,16 +97,17 @@ │ ├── flags: symbol_keys │ └── elements: (length: 1) │ └── @ AssocNode (location: (5,2)-(5,8)) + │ ├── flags: static_literal │ ├── key: │ │ @ SymbolNode (location: (5,2)-(5,6)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (5,2)-(5,3) = "'" │ │ ├── value_loc: (5,3)-(5,4) = "k" │ │ ├── closing_loc: (5,4)-(5,6) = "':" │ │ └── unescaped: "k" │ ├── value: │ │ @ IntegerNode (location: (5,6)-(5,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 42 │ └── operator_loc: ∅ ├── closing_loc: ∅ 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 0ba5891cf6c8e8..00bc620f544829 100644 --- a/test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt +++ b/test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,11)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,11)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,11)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -15,19 +17,20 @@ │ ├── flags: contains_keywords │ └── arguments: (length: 2) │ ├── @ IntegerNode (location: (1,2)-(1,3)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── @ KeywordHashNode (location: (1,5)-(1,9)) │ ├── flags: ∅ │ └── elements: (length: 1) │ └── @ AssocNode (location: (1,5)-(1,9)) + │ ├── flags: static_literal │ ├── key: │ │ @ IntegerNode (location: (1,5)-(1,6)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── value: │ │ @ IntegerNode (location: (1,8)-(1,9)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 3 │ └── operator_loc: (1,6)-(1,8) = "=>" ├── closing_loc: (1,10)-(1,11) = ")" diff --git a/test/prism/snapshots/seattlerb/call_args_command.txt b/test/prism/snapshots/seattlerb/call_args_command.txt index f4a55ff58c36db..6fe112f224aa59 100644 --- a/test/prism/snapshots/seattlerb/call_args_command.txt +++ b/test/prism/snapshots/seattlerb/call_args_command.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,9)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,9)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,9)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (1,0)-(1,1)) │ ├── flags: variable_call, ignore_visibility @@ -46,7 +48,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (1,8)-(1,9)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── closing_loc: ∅ │ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/call_array_arg.txt b/test/prism/snapshots/seattlerb/call_array_arg.txt index 95d2f4859d00ee..cec613bc582e44 100644 --- a/test/prism/snapshots/seattlerb/call_array_arg.txt +++ b/test/prism/snapshots/seattlerb/call_array_arg.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,13)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,13)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,13)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── call_operator_loc: ∅ ├── name: :== @@ -18,16 +20,16 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ ArrayNode (location: (1,5)-(1,13)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 2) │ │ ├── @ SymbolNode (location: (1,6)-(1,8)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (1,6)-(1,7) = ":" │ │ │ ├── value_loc: (1,7)-(1,8) = "b" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "b" │ │ └── @ SymbolNode (location: (1,10)-(1,12)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,10)-(1,11) = ":" │ │ ├── value_loc: (1,11)-(1,12) = "c" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/call_array_block_call.txt b/test/prism/snapshots/seattlerb/call_array_block_call.txt index e02740e7f58d6c..4a044924e9f31e 100644 --- a/test/prism/snapshots/seattlerb/call_array_block_call.txt +++ b/test/prism/snapshots/seattlerb/call_array_block_call.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,19)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,19)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,19)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -18,6 +20,7 @@ │ ├── flags: ∅ │ ├── elements: (length: 2) │ │ ├── @ NilNode (location: (1,4)-(1,7)) + │ │ │ └── flags: static_literal │ │ └── @ CallNode (location: (1,9)-(1,17)) │ │ ├── flags: ignore_visibility │ │ ├── receiver: ∅ @@ -29,6 +32,7 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (1,11)-(1,17)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── parameters: ∅ │ │ ├── body: ∅ diff --git a/test/prism/snapshots/seattlerb/call_array_lambda_block_call.txt b/test/prism/snapshots/seattlerb/call_array_lambda_block_call.txt index c6aa7228123ccb..dca64e5f7b25ae 100644 --- a/test/prism/snapshots/seattlerb/call_array_lambda_block_call.txt +++ b/test/prism/snapshots/seattlerb/call_array_lambda_block_call.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(2,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(2,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(2,3)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -18,12 +20,14 @@ │ ├── flags: ∅ │ ├── elements: (length: 1) │ │ └── @ LambdaNode (location: (1,3)-(1,10)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── operator_loc: (1,3)-(1,5) = "->" │ │ ├── opening_loc: (1,8)-(1,9) = "{" │ │ ├── closing_loc: (1,9)-(1,10) = "}" │ │ ├── parameters: │ │ │ @ BlockParametersNode (location: (1,5)-(1,7)) + │ │ │ ├── flags: ∅ │ │ │ ├── parameters: ∅ │ │ │ ├── locals: (length: 0) │ │ │ ├── opening_loc: (1,5)-(1,6) = "(" @@ -34,6 +38,7 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,12)-(2,3)) + ├── flags: ∅ ├── locals: [] ├── parameters: ∅ ├── body: ∅ diff --git a/test/prism/snapshots/seattlerb/call_array_lit_inline_hash.txt b/test/prism/snapshots/seattlerb/call_array_lit_inline_hash.txt index 091e21c00a6347..f83c7a55e5b592 100644 --- a/test/prism/snapshots/seattlerb/call_array_lit_inline_hash.txt +++ b/test/prism/snapshots/seattlerb/call_array_lit_inline_hash.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,16)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,16)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,16)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -18,7 +20,7 @@ │ ├── flags: ∅ │ ├── elements: (length: 2) │ │ ├── @ SymbolNode (location: (1,3)-(1,5)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (1,3)-(1,4) = ":" │ │ │ ├── value_loc: (1,4)-(1,5) = "b" │ │ │ ├── closing_loc: ∅ @@ -27,16 +29,17 @@ │ │ ├── flags: symbol_keys │ │ └── elements: (length: 1) │ │ └── @ AssocNode (location: (1,7)-(1,14)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (1,7)-(1,9)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (1,7)-(1,8) = ":" │ │ │ ├── value_loc: (1,8)-(1,9) = "c" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "c" │ │ ├── value: │ │ │ @ IntegerNode (location: (1,13)-(1,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── operator_loc: (1,10)-(1,12) = "=>" │ ├── opening_loc: (1,2)-(1,3) = "[" diff --git a/test/prism/snapshots/seattlerb/call_assoc.txt b/test/prism/snapshots/seattlerb/call_assoc.txt index 60784e6095ac2f..a4e4512a0821cd 100644 --- a/test/prism/snapshots/seattlerb/call_assoc.txt +++ b/test/prism/snapshots/seattlerb/call_assoc.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,7)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,7)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,7)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -18,13 +20,14 @@ │ ├── flags: ∅ │ └── elements: (length: 1) │ └── @ AssocNode (location: (1,2)-(1,6)) + │ ├── flags: static_literal │ ├── key: │ │ @ IntegerNode (location: (1,2)-(1,3)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── value: │ │ @ IntegerNode (location: (1,5)-(1,6)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 3 │ └── operator_loc: (1,3)-(1,5) = "=>" ├── closing_loc: (1,6)-(1,7) = ")" diff --git a/test/prism/snapshots/seattlerb/call_assoc_new.txt b/test/prism/snapshots/seattlerb/call_assoc_new.txt index dc25fb24931931..6cbc942a6ba486 100644 --- a/test/prism/snapshots/seattlerb/call_assoc_new.txt +++ b/test/prism/snapshots/seattlerb/call_assoc_new.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,6)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,6)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,6)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -18,16 +20,17 @@ │ ├── flags: symbol_keys │ └── elements: (length: 1) │ └── @ AssocNode (location: (1,2)-(1,5)) + │ ├── flags: static_literal │ ├── key: │ │ @ SymbolNode (location: (1,2)-(1,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (1,2)-(1,3) = "a" │ │ ├── closing_loc: (1,3)-(1,4) = ":" │ │ └── unescaped: "a" │ ├── value: │ │ @ IntegerNode (location: (1,4)-(1,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 3 │ └── operator_loc: ∅ ├── closing_loc: (1,5)-(1,6) = ")" 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 b3d652e879c36d..e0236f3974fc6b 100644 --- a/test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt +++ b/test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(5,4)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(5,4)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(5,4)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -18,19 +20,21 @@ │ ├── flags: symbol_keys │ └── elements: (length: 1) │ └── @ AssocNode (location: (1,2)-(5,3)) + │ ├── flags: ∅ │ ├── key: │ │ @ SymbolNode (location: (1,2)-(1,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (1,2)-(1,3) = "b" │ │ ├── closing_loc: (1,3)-(1,4) = ":" │ │ └── unescaped: "b" │ ├── value: │ │ @ IfNode (location: (1,5)-(5,3)) + │ │ ├── flags: newline │ │ ├── if_keyword_loc: (1,5)-(1,7) = "if" │ │ ├── predicate: │ │ │ @ SymbolNode (location: (1,8)-(1,10)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (1,8)-(1,9) = ":" │ │ │ ├── value_loc: (1,9)-(1,10) = "c" │ │ │ ├── closing_loc: ∅ @@ -38,18 +42,21 @@ │ │ ├── then_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (2,0)-(2,1)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ IntegerNode (location: (2,0)-(2,1)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: newline, static_literal, decimal │ │ │ └── value: 1 │ │ ├── consequent: │ │ │ @ ElseNode (location: (3,0)-(5,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── else_keyword_loc: (3,0)-(3,4) = "else" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (4,0)-(4,1)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ IntegerNode (location: (4,0)-(4,1)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: newline, static_literal, decimal │ │ │ │ └── value: 2 │ │ │ └── end_keyword_loc: (5,0)-(5,3) = "end" │ │ └── end_keyword_loc: (5,0)-(5,3) = "end" diff --git a/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt b/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt index b2012f0f75f70d..a240775d69b8af 100644 --- a/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt +++ b/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,8)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,8)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,8)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -18,13 +20,14 @@ │ ├── flags: ∅ │ └── elements: (length: 1) │ └── @ AssocNode (location: (1,2)-(1,6)) + │ ├── flags: static_literal │ ├── key: │ │ @ IntegerNode (location: (1,2)-(1,3)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── value: │ │ @ IntegerNode (location: (1,5)-(1,6)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ └── operator_loc: (1,3)-(1,5) = "=>" ├── closing_loc: (1,7)-(1,8) = ")" diff --git a/test/prism/snapshots/seattlerb/call_bang_command_call.txt b/test/prism/snapshots/seattlerb/call_bang_command_call.txt index 5e4e10d953c798..e226d652785347 100644 --- a/test/prism/snapshots/seattlerb/call_bang_command_call.txt +++ b/test/prism/snapshots/seattlerb/call_bang_command_call.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,7)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,7)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,7)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (1,2)-(1,7)) │ ├── flags: ∅ @@ -28,7 +30,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (1,6)-(1,7)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── closing_loc: ∅ │ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/call_bang_squiggle.txt b/test/prism/snapshots/seattlerb/call_bang_squiggle.txt index bf11bc0136ff73..5c10841f73936e 100644 --- a/test/prism/snapshots/seattlerb/call_bang_squiggle.txt +++ b/test/prism/snapshots/seattlerb/call_bang_squiggle.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,6)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,6)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,6)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── call_operator_loc: ∅ ├── name: :!~ @@ -18,7 +20,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (1,5)-(1,6)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── closing_loc: ∅ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/call_begin_call_block_call.txt b/test/prism/snapshots/seattlerb/call_begin_call_block_call.txt index 1aa994c8e6dd84..240a3aaa76b5c6 100644 --- a/test/prism/snapshots/seattlerb/call_begin_call_block_call.txt +++ b/test/prism/snapshots/seattlerb/call_begin_call_block_call.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(3,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(3,3)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -15,12 +17,14 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ BeginNode (location: (1,2)-(3,3)) + │ ├── flags: ∅ │ ├── begin_keyword_loc: (1,2)-(1,7) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (2,0)-(2,10)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (2,0)-(2,10)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ CallNode (location: (2,0)-(2,1)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -40,6 +44,7 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (2,4)-(2,10)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── parameters: ∅ │ │ ├── body: ∅ diff --git a/test/prism/snapshots/seattlerb/call_block_arg_named.txt b/test/prism/snapshots/seattlerb/call_block_arg_named.txt index f87c29cf2e196c..db6fc7a059a0f0 100644 --- a/test/prism/snapshots/seattlerb/call_block_arg_named.txt +++ b/test/prism/snapshots/seattlerb/call_block_arg_named.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,6)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,6)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,6)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :x @@ -14,6 +16,7 @@ ├── closing_loc: (1,6)-(1,7) = ")" └── block: @ BlockArgumentNode (location: (1,2)-(1,6)) + ├── flags: ∅ ├── expression: │ @ CallNode (location: (1,3)-(1,6)) │ ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/seattlerb/call_carat.txt b/test/prism/snapshots/seattlerb/call_carat.txt index 856e9a7847f8f6..88ed832aca93de 100644 --- a/test/prism/snapshots/seattlerb/call_carat.txt +++ b/test/prism/snapshots/seattlerb/call_carat.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,5)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,5)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,5)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── call_operator_loc: ∅ ├── name: :^ @@ -18,7 +20,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (1,4)-(1,5)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── closing_loc: ∅ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/call_colon2.txt b/test/prism/snapshots/seattlerb/call_colon2.txt index 98bfc6312601f3..85e39cd3636104 100644 --- a/test/prism/snapshots/seattlerb/call_colon2.txt +++ b/test/prism/snapshots/seattlerb/call_colon2.txt @@ -1,12 +1,15 @@ @ ProgramNode (location: (1,0)-(1,4)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,4)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,4)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ ConstantReadNode (location: (1,0)-(1,1)) + │ ├── flags: ∅ │ └── name: :A ├── call_operator_loc: (1,1)-(1,3) = "::" ├── name: :b diff --git a/test/prism/snapshots/seattlerb/call_colon_parens.txt b/test/prism/snapshots/seattlerb/call_colon_parens.txt index 6d10171a2b19c9..19141c34133911 100644 --- a/test/prism/snapshots/seattlerb/call_colon_parens.txt +++ b/test/prism/snapshots/seattlerb/call_colon_parens.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,5)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,5)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,5)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── call_operator_loc: (1,1)-(1,3) = "::" ├── name: :call diff --git a/test/prism/snapshots/seattlerb/call_div.txt b/test/prism/snapshots/seattlerb/call_div.txt index ba62fb87bd1b62..55a410977c3baa 100644 --- a/test/prism/snapshots/seattlerb/call_div.txt +++ b/test/prism/snapshots/seattlerb/call_div.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,5)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,5)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,5)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── call_operator_loc: ∅ ├── name: :/ @@ -18,7 +20,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (1,4)-(1,5)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── closing_loc: ∅ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/call_dot_parens.txt b/test/prism/snapshots/seattlerb/call_dot_parens.txt index c9b708469967f6..29b592a960dc18 100644 --- a/test/prism/snapshots/seattlerb/call_dot_parens.txt +++ b/test/prism/snapshots/seattlerb/call_dot_parens.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,4)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,4)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,4)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── call_operator_loc: (1,1)-(1,2) = "." ├── name: :call diff --git a/test/prism/snapshots/seattlerb/call_env.txt b/test/prism/snapshots/seattlerb/call_env.txt index fd1f95388edda6..933621594f850c 100644 --- a/test/prism/snapshots/seattlerb/call_env.txt +++ b/test/prism/snapshots/seattlerb/call_env.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,7)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,7)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,7)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (1,0)-(1,1)) │ ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/seattlerb/call_eq3.txt b/test/prism/snapshots/seattlerb/call_eq3.txt index e636e1572572e8..52e4b00c5ce7b7 100644 --- a/test/prism/snapshots/seattlerb/call_eq3.txt +++ b/test/prism/snapshots/seattlerb/call_eq3.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,7)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,7)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,7)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── call_operator_loc: ∅ ├── name: :=== @@ -18,7 +20,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (1,6)-(1,7)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── closing_loc: ∅ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/call_gt.txt b/test/prism/snapshots/seattlerb/call_gt.txt index a6f19e5adfe4c0..22371dd8ca0e47 100644 --- a/test/prism/snapshots/seattlerb/call_gt.txt +++ b/test/prism/snapshots/seattlerb/call_gt.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,5)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,5)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,5)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── call_operator_loc: ∅ ├── name: :> @@ -18,7 +20,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (1,4)-(1,5)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── closing_loc: ∅ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/call_kwsplat.txt b/test/prism/snapshots/seattlerb/call_kwsplat.txt index e0620dc5f0ba54..17773e769335a6 100644 --- a/test/prism/snapshots/seattlerb/call_kwsplat.txt +++ b/test/prism/snapshots/seattlerb/call_kwsplat.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,6)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,6)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,6)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -18,9 +20,10 @@ │ ├── flags: ∅ │ └── elements: (length: 1) │ └── @ AssocSplatNode (location: (1,2)-(1,5)) + │ ├── flags: ∅ │ ├── value: │ │ @ IntegerNode (location: (1,4)-(1,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (1,2)-(1,4) = "**" ├── closing_loc: (1,5)-(1,6) = ")" diff --git a/test/prism/snapshots/seattlerb/call_leading_dots.txt b/test/prism/snapshots/seattlerb/call_leading_dots.txt index e8435d7e7a14bc..5562afcd7020f5 100644 --- a/test/prism/snapshots/seattlerb/call_leading_dots.txt +++ b/test/prism/snapshots/seattlerb/call_leading_dots.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(3,2)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,2)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(3,2)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (1,0)-(2,2)) │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/call_leading_dots_comment.txt b/test/prism/snapshots/seattlerb/call_leading_dots_comment.txt index e5dfb723723e17..f285e42cbf4b67 100644 --- a/test/prism/snapshots/seattlerb/call_leading_dots_comment.txt +++ b/test/prism/snapshots/seattlerb/call_leading_dots_comment.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(4,2)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(4,2)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(4,2)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (1,0)-(2,2)) │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/call_lt.txt b/test/prism/snapshots/seattlerb/call_lt.txt index 14f50585d935d5..bec3deddd083b5 100644 --- a/test/prism/snapshots/seattlerb/call_lt.txt +++ b/test/prism/snapshots/seattlerb/call_lt.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,5)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,5)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,5)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── call_operator_loc: ∅ ├── name: :< @@ -18,7 +20,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (1,4)-(1,5)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── closing_loc: ∅ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/call_lte.txt b/test/prism/snapshots/seattlerb/call_lte.txt index 665a99d60a8191..a71e03ee1283ec 100644 --- a/test/prism/snapshots/seattlerb/call_lte.txt +++ b/test/prism/snapshots/seattlerb/call_lte.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,6)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,6)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,6)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── call_operator_loc: ∅ ├── name: :<= @@ -18,7 +20,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (1,5)-(1,6)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── closing_loc: ∅ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/call_not.txt b/test/prism/snapshots/seattlerb/call_not.txt index 86c68923031764..e9df80fb9f2a84 100644 --- a/test/prism/snapshots/seattlerb/call_not.txt +++ b/test/prism/snapshots/seattlerb/call_not.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,6)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,6)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,6)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (1,4)-(1,6)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 42 ├── call_operator_loc: ∅ ├── name: :! diff --git a/test/prism/snapshots/seattlerb/call_pipe.txt b/test/prism/snapshots/seattlerb/call_pipe.txt index 855e986ef6f30a..8cf68211a99ea7 100644 --- a/test/prism/snapshots/seattlerb/call_pipe.txt +++ b/test/prism/snapshots/seattlerb/call_pipe.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,5)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,5)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,5)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── call_operator_loc: ∅ ├── name: :| @@ -18,7 +20,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (1,4)-(1,5)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── closing_loc: ∅ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/call_rshift.txt b/test/prism/snapshots/seattlerb/call_rshift.txt index 26e593db187a6f..28948a044f9da0 100644 --- a/test/prism/snapshots/seattlerb/call_rshift.txt +++ b/test/prism/snapshots/seattlerb/call_rshift.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,6)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,6)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,6)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── call_operator_loc: ∅ ├── name: :>> @@ -18,7 +20,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (1,5)-(1,6)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── closing_loc: ∅ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/call_self_brackets.txt b/test/prism/snapshots/seattlerb/call_self_brackets.txt index 16ca69b5c29d38..e4f5e2c41313aa 100644 --- a/test/prism/snapshots/seattlerb/call_self_brackets.txt +++ b/test/prism/snapshots/seattlerb/call_self_brackets.txt @@ -1,12 +1,15 @@ @ ProgramNode (location: (1,0)-(1,7)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,7)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,7)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: │ @ SelfNode (location: (1,0)-(1,4)) + │ └── flags: ∅ ├── call_operator_loc: ∅ ├── name: :[] ├── message_loc: (1,4)-(1,7) = "[1]" @@ -16,7 +19,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (1,5)-(1,6)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── closing_loc: (1,6)-(1,7) = "]" └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/call_spaceship.txt b/test/prism/snapshots/seattlerb/call_spaceship.txt index 8d43c3f971049f..4ea67f2e00bb5b 100644 --- a/test/prism/snapshots/seattlerb/call_spaceship.txt +++ b/test/prism/snapshots/seattlerb/call_spaceship.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,7)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,7)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,7)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── call_operator_loc: ∅ ├── name: :<=> @@ -18,7 +20,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (1,6)-(1,7)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── closing_loc: ∅ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/call_stabby_do_end_with_block.txt b/test/prism/snapshots/seattlerb/call_stabby_do_end_with_block.txt index 242db9e9cb59e4..0e83d334cacf2b 100644 --- a/test/prism/snapshots/seattlerb/call_stabby_do_end_with_block.txt +++ b/test/prism/snapshots/seattlerb/call_stabby_do_end_with_block.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,22)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,22)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,22)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -15,6 +17,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ LambdaNode (location: (1,2)-(1,13)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── operator_loc: (1,2)-(1,4) = "->" │ ├── opening_loc: (1,5)-(1,7) = "do" @@ -22,20 +25,23 @@ │ ├── parameters: ∅ │ └── body: │ @ StatementsNode (location: (1,8)-(1,9)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ IntegerNode (location: (1,8)-(1,9)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 1 ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,14)-(1,22)) + ├── flags: ∅ ├── locals: [] ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (1,17)-(1,18)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ IntegerNode (location: (1,17)-(1,18)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 2 ├── opening_loc: (1,14)-(1,16) = "do" └── closing_loc: (1,19)-(1,22) = "end" diff --git a/test/prism/snapshots/seattlerb/call_stabby_with_braces_block.txt b/test/prism/snapshots/seattlerb/call_stabby_with_braces_block.txt index 7c3ab8dad8fdee..705f8b8d0224c6 100644 --- a/test/prism/snapshots/seattlerb/call_stabby_with_braces_block.txt +++ b/test/prism/snapshots/seattlerb/call_stabby_with_braces_block.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,19)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,19)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,19)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -15,6 +17,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ LambdaNode (location: (1,2)-(1,10)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── operator_loc: (1,2)-(1,4) = "->" │ ├── opening_loc: (1,5)-(1,6) = "{" @@ -22,20 +25,23 @@ │ ├── parameters: ∅ │ └── body: │ @ StatementsNode (location: (1,7)-(1,8)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ IntegerNode (location: (1,7)-(1,8)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 1 ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,11)-(1,19)) + ├── flags: ∅ ├── locals: [] ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (1,14)-(1,15)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ IntegerNode (location: (1,14)-(1,15)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 2 ├── opening_loc: (1,11)-(1,13) = "do" └── closing_loc: (1,16)-(1,19) = "end" diff --git a/test/prism/snapshots/seattlerb/call_star.txt b/test/prism/snapshots/seattlerb/call_star.txt index 49aee1672c7228..06bee81f30da40 100644 --- a/test/prism/snapshots/seattlerb/call_star.txt +++ b/test/prism/snapshots/seattlerb/call_star.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,5)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,5)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,5)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── call_operator_loc: ∅ ├── name: :* @@ -18,7 +20,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (1,4)-(1,5)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── closing_loc: ∅ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/call_star2.txt b/test/prism/snapshots/seattlerb/call_star2.txt index cc2532cc7c8490..bd367d2e758fc8 100644 --- a/test/prism/snapshots/seattlerb/call_star2.txt +++ b/test/prism/snapshots/seattlerb/call_star2.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,6)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,6)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,6)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── call_operator_loc: ∅ ├── name: :** @@ -18,7 +20,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (1,5)-(1,6)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── closing_loc: ∅ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/call_trailing_comma.txt b/test/prism/snapshots/seattlerb/call_trailing_comma.txt index fe28a3ad3eb6fb..01cb77a25a060f 100644 --- a/test/prism/snapshots/seattlerb/call_trailing_comma.txt +++ b/test/prism/snapshots/seattlerb/call_trailing_comma.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,5)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,5)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,5)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -15,7 +17,7 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ IntegerNode (location: (1,2)-(1,3)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── closing_loc: (1,4)-(1,5) = ")" └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/call_trailing_dots.txt b/test/prism/snapshots/seattlerb/call_trailing_dots.txt index b0e23eb27b95ac..73ffd855ad8839 100644 --- a/test/prism/snapshots/seattlerb/call_trailing_dots.txt +++ b/test/prism/snapshots/seattlerb/call_trailing_dots.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(3,1)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,1)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(3,1)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (1,0)-(2,1)) │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/call_unary_bang.txt b/test/prism/snapshots/seattlerb/call_unary_bang.txt index 782cc83b1037ec..b08f0706930f40 100644 --- a/test/prism/snapshots/seattlerb/call_unary_bang.txt +++ b/test/prism/snapshots/seattlerb/call_unary_bang.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(1,2)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,2)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,2)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ IntegerNode (location: (1,1)-(1,2)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── call_operator_loc: ∅ ├── name: :! diff --git a/test/prism/snapshots/seattlerb/case_in.txt b/test/prism/snapshots/seattlerb/case_in.txt index 950d66647e6305..6df3b407a68374 100644 --- a/test/prism/snapshots/seattlerb/case_in.txt +++ b/test/prism/snapshots/seattlerb/case_in.txt @@ -1,34 +1,42 @@ @ ProgramNode (location: (1,0)-(111,3)) +├── flags: ∅ ├── locals: [:b, :_, :lhs, :x, :rhs, :c, :e] └── statements: @ StatementsNode (location: (1,0)-(111,3)) + ├── flags: ∅ └── body: (length: 28) ├── @ CaseMatchNode (location: (1,0)-(3,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (1,5)-(1,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,5)-(1,6) = ":" │ │ ├── value_loc: (1,6)-(1,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (2,0)-(2,8)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ HashPatternNode (location: (2,4)-(2,8)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: ∅ │ │ │ ├── elements: (length: 1) │ │ │ │ └── @ AssocNode (location: (2,4)-(2,8)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── key: │ │ │ │ │ @ SymbolNode (location: (2,4)-(2,8)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: (2,4)-(2,5) = "\"" │ │ │ │ │ ├── value_loc: (2,5)-(2,6) = "b" │ │ │ │ │ ├── closing_loc: (2,6)-(2,8) = "\":" │ │ │ │ │ └── unescaped: "b" │ │ │ │ ├── value: │ │ │ │ │ @ ImplicitNode (location: (2,5)-(2,6)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── value: │ │ │ │ │ @ LocalVariableTargetNode (location: (2,5)-(2,6)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── name: :b │ │ │ │ │ └── depth: 0 │ │ │ │ └── operator_loc: ∅ @@ -42,27 +50,29 @@ │ ├── case_keyword_loc: (1,0)-(1,4) = "case" │ └── end_keyword_loc: (3,0)-(3,3) = "end" ├── @ CaseMatchNode (location: (5,0)-(7,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (5,5)-(5,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (5,5)-(5,6) = ":" │ │ ├── value_loc: (5,6)-(5,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (6,0)-(6,10)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayNode (location: (6,3)-(6,10)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: static_literal │ │ │ ├── elements: (length: 2) │ │ │ │ ├── @ SymbolNode (location: (6,6)-(6,7)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── value_loc: (6,6)-(6,7) = "a" │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── unescaped: "a" │ │ │ │ └── @ SymbolNode (location: (6,8)-(6,9)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (6,8)-(6,9) = "b" │ │ │ │ ├── closing_loc: ∅ @@ -76,15 +86,17 @@ │ ├── case_keyword_loc: (5,0)-(5,4) = "case" │ └── end_keyword_loc: (7,0)-(7,3) = "end" ├── @ CaseMatchNode (location: (9,0)-(11,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (9,5)-(9,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (9,5)-(9,6) = ":" │ │ ├── value_loc: (9,6)-(9,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (10,0)-(10,10)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayNode (location: (10,3)-(10,10)) │ │ │ ├── flags: ∅ @@ -110,27 +122,29 @@ │ ├── case_keyword_loc: (9,0)-(9,4) = "case" │ └── end_keyword_loc: (11,0)-(11,3) = "end" ├── @ CaseMatchNode (location: (13,0)-(15,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (13,5)-(13,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (13,5)-(13,6) = ":" │ │ ├── value_loc: (13,6)-(13,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (14,0)-(14,10)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayNode (location: (14,3)-(14,10)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: static_literal │ │ │ ├── elements: (length: 2) │ │ │ │ ├── @ SymbolNode (location: (14,6)-(14,7)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── value_loc: (14,6)-(14,7) = "a" │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── unescaped: "a" │ │ │ │ └── @ SymbolNode (location: (14,8)-(14,9)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (14,8)-(14,9) = "b" │ │ │ │ ├── closing_loc: ∅ @@ -144,15 +158,17 @@ │ ├── case_keyword_loc: (13,0)-(13,4) = "case" │ └── end_keyword_loc: (15,0)-(15,3) = "end" ├── @ CaseMatchNode (location: (17,0)-(19,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (17,5)-(17,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (17,5)-(17,6) = ":" │ │ ├── value_loc: (17,6)-(17,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (18,0)-(18,10)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayNode (location: (18,3)-(18,10)) │ │ │ ├── flags: ∅ @@ -178,24 +194,27 @@ │ ├── case_keyword_loc: (17,0)-(17,4) = "case" │ └── end_keyword_loc: (19,0)-(19,3) = "end" ├── @ CaseMatchNode (location: (21,0)-(23,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (21,5)-(21,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (21,5)-(21,6) = ":" │ │ ├── value_loc: (21,6)-(21,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (22,0)-(22,10)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ParenthesesNode (location: (22,3)-(22,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ RangeNode (location: (22,4)-(22,9)) - │ │ │ │ ├── flags: exclude_end + │ │ │ │ ├── flags: static_literal, exclude_end │ │ │ │ ├── left: ∅ │ │ │ │ ├── right: │ │ │ │ │ @ IntegerNode (location: (22,7)-(22,9)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 10 │ │ │ │ └── operator_loc: (22,4)-(22,7) = "..." │ │ │ ├── opening_loc: (22,3)-(22,4) = "(" @@ -207,24 +226,27 @@ │ ├── case_keyword_loc: (21,0)-(21,4) = "case" │ └── end_keyword_loc: (23,0)-(23,3) = "end" ├── @ CaseMatchNode (location: (25,0)-(27,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (25,5)-(25,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (25,5)-(25,6) = ":" │ │ ├── value_loc: (25,6)-(25,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (26,0)-(26,9)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ParenthesesNode (location: (26,3)-(26,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ RangeNode (location: (26,4)-(26,8)) - │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── flags: static_literal │ │ │ │ ├── left: ∅ │ │ │ │ ├── right: │ │ │ │ │ @ IntegerNode (location: (26,6)-(26,8)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 10 │ │ │ │ └── operator_loc: (26,4)-(26,6) = ".." │ │ │ ├── opening_loc: (26,3)-(26,4) = "(" @@ -236,23 +258,26 @@ │ ├── case_keyword_loc: (25,0)-(25,4) = "case" │ └── end_keyword_loc: (27,0)-(27,3) = "end" ├── @ CaseMatchNode (location: (29,0)-(31,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (29,5)-(29,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (29,5)-(29,6) = ":" │ │ ├── value_loc: (29,6)-(29,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (30,0)-(30,9)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ParenthesesNode (location: (30,3)-(30,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ RangeNode (location: (30,4)-(30,8)) - │ │ │ │ ├── flags: exclude_end + │ │ │ │ ├── flags: static_literal, exclude_end │ │ │ │ ├── left: │ │ │ │ │ @ IntegerNode (location: (30,4)-(30,5)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ ├── right: ∅ │ │ │ │ └── operator_loc: (30,5)-(30,8) = "..." @@ -265,27 +290,30 @@ │ ├── case_keyword_loc: (29,0)-(29,4) = "case" │ └── end_keyword_loc: (31,0)-(31,3) = "end" ├── @ CaseMatchNode (location: (33,0)-(35,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (33,5)-(33,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (33,5)-(33,6) = ":" │ │ ├── value_loc: (33,6)-(33,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (34,0)-(34,10)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ParenthesesNode (location: (34,3)-(34,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ RangeNode (location: (34,4)-(34,9)) - │ │ │ │ ├── flags: exclude_end + │ │ │ │ ├── flags: static_literal, exclude_end │ │ │ │ ├── left: │ │ │ │ │ @ IntegerNode (location: (34,4)-(34,5)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ ├── right: │ │ │ │ │ @ IntegerNode (location: (34,8)-(34,9)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 3 │ │ │ │ └── operator_loc: (34,5)-(34,8) = "..." │ │ │ ├── opening_loc: (34,3)-(34,4) = "(" @@ -297,20 +325,23 @@ │ ├── case_keyword_loc: (33,0)-(33,4) = "case" │ └── end_keyword_loc: (35,0)-(35,3) = "end" ├── @ CaseMatchNode (location: (37,0)-(39,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (37,5)-(37,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (37,5)-(37,6) = ":" │ │ ├── value_loc: (37,6)-(37,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (38,0)-(38,7)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ParenthesesNode (location: (38,3)-(38,7)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ IntegerNode (location: (38,4)-(38,6)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 42 │ │ │ ├── opening_loc: (38,3)-(38,4) = "(" │ │ │ └── closing_loc: (38,6)-(38,7) = ")" @@ -321,21 +352,25 @@ │ ├── case_keyword_loc: (37,0)-(37,4) = "case" │ └── end_keyword_loc: (39,0)-(39,3) = "end" ├── @ CaseMatchNode (location: (41,0)-(43,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (41,5)-(41,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (41,5)-(41,6) = ":" │ │ ├── value_loc: (41,6)-(41,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (42,0)-(42,8)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ HashPatternNode (location: (42,3)-(42,8)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: ∅ │ │ │ ├── elements: (length: 0) │ │ │ ├── rest: │ │ │ │ @ NoKeywordsParameterNode (location: (42,3)-(42,8)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (42,3)-(42,5) = "**" │ │ │ │ └── keyword_loc: (42,5)-(42,8) = "nil" │ │ │ ├── opening_loc: ∅ @@ -347,18 +382,20 @@ │ ├── case_keyword_loc: (41,0)-(41,4) = "case" │ └── end_keyword_loc: (43,0)-(43,3) = "end" ├── @ CaseMatchNode (location: (45,0)-(47,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (45,5)-(45,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (45,5)-(45,6) = ":" │ │ ├── value_loc: (45,6)-(45,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (46,0)-(46,11)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ RegularExpressionNode (location: (46,3)-(46,11)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (46,3)-(46,4) = "/" │ │ │ ├── content_loc: (46,4)-(46,10) = "regexp" │ │ │ ├── closing_loc: (46,10)-(46,11) = "/" @@ -370,35 +407,40 @@ │ ├── case_keyword_loc: (45,0)-(45,4) = "case" │ └── end_keyword_loc: (47,0)-(47,3) = "end" ├── @ CaseMatchNode (location: (49,0)-(51,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (49,5)-(49,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (49,5)-(49,6) = ":" │ │ ├── value_loc: (49,6)-(49,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (50,0)-(50,13)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayPatternNode (location: (50,3)-(50,13)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ SymbolNode (location: (50,3)-(50,5)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (50,3)-(50,4) = ":" │ │ │ │ ├── value_loc: (50,4)-(50,5) = "b" │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: "b" │ │ │ ├── rest: │ │ │ │ @ SplatNode (location: (50,7)-(50,9)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (50,7)-(50,8) = "*" │ │ │ │ └── expression: │ │ │ │ @ LocalVariableTargetNode (location: (50,8)-(50,9)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :_ │ │ │ │ └── depth: 0 │ │ │ ├── posts: (length: 1) │ │ │ │ └── @ SymbolNode (location: (50,11)-(50,13)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (50,11)-(50,12) = ":" │ │ │ │ ├── value_loc: (50,12)-(50,13) = "c" │ │ │ │ ├── closing_loc: ∅ @@ -412,30 +454,34 @@ │ ├── case_keyword_loc: (49,0)-(49,4) = "case" │ └── end_keyword_loc: (51,0)-(51,3) = "end" ├── @ CaseMatchNode (location: (53,0)-(55,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (53,5)-(53,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (53,5)-(53,6) = ":" │ │ ├── value_loc: (53,6)-(53,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (54,0)-(54,11)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayPatternNode (location: (54,3)-(54,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: ∅ │ │ │ ├── requireds: (length: 2) │ │ │ │ ├── @ SymbolNode (location: (54,3)-(54,5)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: (54,3)-(54,4) = ":" │ │ │ │ │ ├── value_loc: (54,4)-(54,5) = "b" │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── unescaped: "b" │ │ │ │ └── @ ArrayPatternNode (location: (54,7)-(54,11)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── constant: ∅ │ │ │ │ ├── requireds: (length: 1) │ │ │ │ │ └── @ SymbolNode (location: (54,8)-(54,10)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: (54,8)-(54,9) = ":" │ │ │ │ │ ├── value_loc: (54,9)-(54,10) = "c" │ │ │ │ │ ├── closing_loc: ∅ @@ -455,19 +501,23 @@ │ ├── case_keyword_loc: (53,0)-(53,4) = "case" │ └── end_keyword_loc: (55,0)-(55,3) = "end" ├── @ CaseMatchNode (location: (57,0)-(59,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (57,5)-(57,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (57,5)-(57,6) = ":" │ │ ├── value_loc: (57,6)-(57,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (58,0)-(58,11)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayPatternNode (location: (58,3)-(58,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: │ │ │ │ @ ConstantReadNode (location: (58,3)-(58,9)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :Symbol │ │ │ ├── requireds: (length: 0) │ │ │ ├── rest: ∅ @@ -481,36 +531,45 @@ │ ├── case_keyword_loc: (57,0)-(57,4) = "case" │ └── end_keyword_loc: (59,0)-(59,3) = "end" ├── @ CaseMatchNode (location: (61,0)-(63,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (61,5)-(61,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (61,5)-(61,6) = ":" │ │ ├── value_loc: (61,6)-(61,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (62,0)-(62,24)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ FindPatternNode (location: (62,3)-(62,24)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: │ │ │ │ @ ConstantReadNode (location: (62,3)-(62,9)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :Symbol │ │ │ ├── left: │ │ │ │ @ SplatNode (location: (62,10)-(62,14)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (62,10)-(62,11) = "*" │ │ │ │ └── expression: │ │ │ │ @ LocalVariableTargetNode (location: (62,11)-(62,14)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :lhs │ │ │ │ └── depth: 0 │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ LocalVariableTargetNode (location: (62,16)-(62,17)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :x │ │ │ │ └── depth: 0 │ │ │ ├── right: │ │ │ │ @ SplatNode (location: (62,19)-(62,23)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (62,19)-(62,20) = "*" │ │ │ │ └── expression: │ │ │ │ @ LocalVariableTargetNode (location: (62,20)-(62,23)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :rhs │ │ │ │ └── depth: 0 │ │ │ ├── opening_loc: (62,9)-(62,10) = "(" @@ -522,36 +581,45 @@ │ ├── case_keyword_loc: (61,0)-(61,4) = "case" │ └── end_keyword_loc: (63,0)-(63,3) = "end" ├── @ CaseMatchNode (location: (65,0)-(67,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (65,5)-(65,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (65,5)-(65,6) = ":" │ │ ├── value_loc: (65,6)-(65,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (66,0)-(66,24)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ FindPatternNode (location: (66,3)-(66,24)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: │ │ │ │ @ ConstantReadNode (location: (66,3)-(66,9)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :Symbol │ │ │ ├── left: │ │ │ │ @ SplatNode (location: (66,10)-(66,14)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (66,10)-(66,11) = "*" │ │ │ │ └── expression: │ │ │ │ @ LocalVariableTargetNode (location: (66,11)-(66,14)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :lhs │ │ │ │ └── depth: 0 │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ LocalVariableTargetNode (location: (66,16)-(66,17)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :x │ │ │ │ └── depth: 0 │ │ │ ├── right: │ │ │ │ @ SplatNode (location: (66,19)-(66,23)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (66,19)-(66,20) = "*" │ │ │ │ └── expression: │ │ │ │ @ LocalVariableTargetNode (location: (66,20)-(66,23)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :rhs │ │ │ │ └── depth: 0 │ │ │ ├── opening_loc: (66,9)-(66,10) = "[" @@ -563,28 +631,34 @@ │ ├── case_keyword_loc: (65,0)-(65,4) = "case" │ └── end_keyword_loc: (67,0)-(67,3) = "end" ├── @ CaseMatchNode (location: (69,0)-(71,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (69,5)-(69,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (69,5)-(69,6) = ":" │ │ ├── value_loc: (69,6)-(69,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (70,0)-(70,22)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayPatternNode (location: (70,3)-(70,22)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: ∅ │ │ │ ├── requireds: (length: 2) │ │ │ │ ├── @ LambdaNode (location: (70,4)-(70,18)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── locals: [:b] │ │ │ │ │ ├── operator_loc: (70,4)-(70,6) = "->" │ │ │ │ │ ├── opening_loc: (70,10)-(70,11) = "{" │ │ │ │ │ ├── closing_loc: (70,17)-(70,18) = "}" │ │ │ │ │ ├── parameters: │ │ │ │ │ │ @ BlockParametersNode (location: (70,6)-(70,9)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ ├── parameters: │ │ │ │ │ │ │ @ ParametersNode (location: (70,7)-(70,8)) + │ │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ │ ├── requireds: (length: 1) │ │ │ │ │ │ │ │ └── @ RequiredParameterNode (location: (70,7)-(70,8)) │ │ │ │ │ │ │ │ ├── flags: ∅ @@ -600,9 +674,12 @@ │ │ │ │ │ │ └── closing_loc: (70,8)-(70,9) = ")" │ │ │ │ │ └── body: │ │ │ │ │ @ StatementsNode (location: (70,12)-(70,16)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ TrueNode (location: (70,12)-(70,16)) + │ │ │ │ │ └── flags: newline, static_literal │ │ │ │ └── @ LocalVariableTargetNode (location: (70,20)-(70,21)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :c │ │ │ │ └── depth: 0 │ │ │ ├── rest: ∅ @@ -616,49 +693,58 @@ │ ├── case_keyword_loc: (69,0)-(69,4) = "case" │ └── end_keyword_loc: (71,0)-(71,3) = "end" ├── @ CaseMatchNode (location: (73,0)-(75,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (73,5)-(73,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (73,5)-(73,6) = ":" │ │ ├── value_loc: (73,6)-(73,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (74,0)-(74,28)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayPatternNode (location: (74,3)-(74,28)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: ∅ │ │ │ ├── requireds: (length: 4) │ │ │ │ ├── @ SymbolNode (location: (74,4)-(74,6)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: (74,4)-(74,5) = ":" │ │ │ │ │ ├── value_loc: (74,5)-(74,6) = "a" │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── unescaped: "a" │ │ │ │ ├── @ LocalVariableTargetNode (location: (74,8)-(74,9)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── name: :b │ │ │ │ │ └── depth: 0 │ │ │ │ ├── @ LocalVariableTargetNode (location: (74,11)-(74,12)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── name: :c │ │ │ │ │ └── depth: 0 │ │ │ │ └── @ ArrayPatternNode (location: (74,14)-(74,27)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── constant: ∅ │ │ │ │ ├── requireds: (length: 1) │ │ │ │ │ └── @ SymbolNode (location: (74,15)-(74,17)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: (74,15)-(74,16) = ":" │ │ │ │ │ ├── value_loc: (74,16)-(74,17) = "d" │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── unescaped: "d" │ │ │ │ ├── rest: │ │ │ │ │ @ SplatNode (location: (74,19)-(74,21)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── operator_loc: (74,19)-(74,20) = "*" │ │ │ │ │ └── expression: │ │ │ │ │ @ LocalVariableTargetNode (location: (74,20)-(74,21)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── name: :e │ │ │ │ │ └── depth: 0 │ │ │ │ ├── posts: (length: 1) │ │ │ │ │ └── @ NilNode (location: (74,23)-(74,26)) + │ │ │ │ │ └── flags: static_literal │ │ │ │ ├── opening_loc: (74,14)-(74,15) = "[" │ │ │ │ └── closing_loc: (74,26)-(74,27) = "]" │ │ │ ├── rest: ∅ @@ -672,27 +758,33 @@ │ ├── case_keyword_loc: (73,0)-(73,4) = "case" │ └── end_keyword_loc: (75,0)-(75,3) = "end" ├── @ CaseMatchNode (location: (77,0)-(79,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (77,5)-(77,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (77,5)-(77,6) = ":" │ │ ├── value_loc: (77,6)-(77,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (78,0)-(78,12)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayPatternNode (location: (78,3)-(78,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ ConstantReadNode (location: (78,4)-(78,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :A │ │ │ ├── rest: │ │ │ │ @ SplatNode (location: (78,7)-(78,8)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (78,7)-(78,8) = "*" │ │ │ │ └── expression: ∅ │ │ │ ├── posts: (length: 1) │ │ │ │ └── @ ConstantReadNode (location: (78,10)-(78,11)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :B │ │ │ ├── opening_loc: (78,3)-(78,4) = "[" │ │ │ └── closing_loc: (78,11)-(78,12) = "]" @@ -703,29 +795,34 @@ │ ├── case_keyword_loc: (77,0)-(77,4) = "case" │ └── end_keyword_loc: (79,0)-(79,3) = "end" ├── @ CaseMatchNode (location: (81,0)-(83,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (81,5)-(81,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (81,5)-(81,6) = ":" │ │ ├── value_loc: (81,6)-(81,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (82,0)-(82,22)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayPatternNode (location: (82,3)-(82,22)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: ∅ │ │ │ ├── requireds: (length: 2) │ │ │ │ ├── @ ArrayPatternNode (location: (82,4)-(82,11)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── constant: ∅ │ │ │ │ │ ├── requireds: (length: 2) │ │ │ │ │ │ ├── @ SymbolNode (location: (82,5)-(82,7)) - │ │ │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ │ │ ├── opening_loc: (82,5)-(82,6) = ":" │ │ │ │ │ │ │ ├── value_loc: (82,6)-(82,7) = "b" │ │ │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ │ │ └── unescaped: "b" │ │ │ │ │ │ └── @ LocalVariableTargetNode (location: (82,9)-(82,10)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ ├── name: :c │ │ │ │ │ │ └── depth: 0 │ │ │ │ │ ├── rest: ∅ @@ -733,17 +830,20 @@ │ │ │ │ │ ├── opening_loc: (82,4)-(82,5) = "[" │ │ │ │ │ └── closing_loc: (82,10)-(82,11) = "]" │ │ │ │ └── @ ArrayPatternNode (location: (82,13)-(82,21)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── constant: ∅ │ │ │ │ ├── requireds: (length: 2) │ │ │ │ │ ├── @ SymbolNode (location: (82,14)-(82,16)) - │ │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ │ ├── opening_loc: (82,14)-(82,15) = ":" │ │ │ │ │ │ ├── value_loc: (82,15)-(82,16) = "d" │ │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ │ └── unescaped: "d" │ │ │ │ │ └── @ PinnedVariableNode (location: (82,18)-(82,20)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── variable: │ │ │ │ │ │ @ LocalVariableReadNode (location: (82,19)-(82,20)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ ├── name: :e │ │ │ │ │ │ └── depth: 0 │ │ │ │ │ └── operator_loc: (82,18)-(82,19) = "^" @@ -762,17 +862,20 @@ │ ├── case_keyword_loc: (81,0)-(81,4) = "case" │ └── end_keyword_loc: (83,0)-(83,3) = "end" ├── @ CaseMatchNode (location: (85,0)-(87,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (85,5)-(85,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (85,5)-(85,6) = ":" │ │ ├── value_loc: (85,6)-(85,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (86,0)-(86,5)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayPatternNode (location: (86,3)-(86,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: ∅ │ │ │ ├── requireds: (length: 0) │ │ │ ├── rest: ∅ @@ -786,20 +889,24 @@ │ ├── case_keyword_loc: (85,0)-(85,4) = "case" │ └── end_keyword_loc: (87,0)-(87,3) = "end" ├── @ CaseMatchNode (location: (89,0)-(91,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (89,5)-(89,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (89,5)-(89,6) = ":" │ │ ├── value_loc: (89,6)-(89,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (90,0)-(90,9)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayPatternNode (location: (90,3)-(90,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ PinnedExpressionNode (location: (90,4)-(90,8)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── expression: │ │ │ │ │ @ CallNode (location: (90,6)-(90,7)) │ │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -825,32 +932,41 @@ │ ├── case_keyword_loc: (89,0)-(89,4) = "case" │ └── end_keyword_loc: (91,0)-(91,3) = "end" ├── @ CaseMatchNode (location: (93,0)-(95,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (93,5)-(93,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (93,5)-(93,6) = ":" │ │ ├── value_loc: (93,6)-(93,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (94,0)-(94,19)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayPatternNode (location: (94,3)-(94,19)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: ∅ │ │ │ ├── requireds: (length: 3) │ │ │ │ ├── @ PinnedVariableNode (location: (94,4)-(94,7)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── variable: │ │ │ │ │ │ @ InstanceVariableReadNode (location: (94,5)-(94,7)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ └── name: :@a │ │ │ │ │ └── operator_loc: (94,4)-(94,5) = "^" │ │ │ │ ├── @ PinnedVariableNode (location: (94,9)-(94,12)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── variable: │ │ │ │ │ │ @ GlobalVariableReadNode (location: (94,10)-(94,12)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ └── name: :$b │ │ │ │ │ └── operator_loc: (94,9)-(94,10) = "^" │ │ │ │ └── @ PinnedVariableNode (location: (94,14)-(94,18)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── variable: │ │ │ │ │ @ ClassVariableReadNode (location: (94,15)-(94,18)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── name: :@@c │ │ │ │ └── operator_loc: (94,14)-(94,15) = "^" │ │ │ ├── rest: ∅ @@ -864,15 +980,17 @@ │ ├── case_keyword_loc: (93,0)-(93,4) = "case" │ └── end_keyword_loc: (95,0)-(95,3) = "end" ├── @ CaseMatchNode (location: (97,0)-(99,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (97,5)-(97,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (97,5)-(97,6) = ":" │ │ ├── value_loc: (97,6)-(97,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (98,0)-(98,12)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ XStringNode (location: (98,3)-(98,12)) │ │ │ ├── flags: ∅ @@ -887,22 +1005,28 @@ │ ├── case_keyword_loc: (97,0)-(97,4) = "case" │ └── end_keyword_loc: (99,0)-(99,3) = "end" ├── @ CaseMatchNode (location: (101,0)-(103,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (101,5)-(101,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (101,5)-(101,6) = ":" │ │ ├── value_loc: (101,6)-(101,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (102,0)-(102,16)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayPatternNode (location: (102,3)-(102,16)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: ∅ │ │ │ ├── requireds: (length: 3) │ │ │ │ ├── @ NilNode (location: (102,3)-(102,6)) + │ │ │ │ │ └── flags: static_literal │ │ │ │ ├── @ NilNode (location: (102,8)-(102,11)) + │ │ │ │ │ └── flags: static_literal │ │ │ │ └── @ NilNode (location: (102,13)-(102,16)) + │ │ │ │ └── flags: static_literal │ │ │ ├── rest: ∅ │ │ │ ├── posts: (length: 0) │ │ │ ├── opening_loc: ∅ @@ -914,31 +1038,37 @@ │ ├── case_keyword_loc: (101,0)-(101,4) = "case" │ └── end_keyword_loc: (103,0)-(103,3) = "end" ├── @ CaseMatchNode (location: (105,0)-(107,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ SymbolNode (location: (105,5)-(105,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (105,5)-(105,6) = ":" │ │ ├── value_loc: (105,6)-(105,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (106,0)-(106,11)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ HashPatternNode (location: (106,3)-(106,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: ∅ │ │ │ ├── elements: (length: 1) │ │ │ │ └── @ AssocNode (location: (106,5)-(106,9)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── key: │ │ │ │ │ @ SymbolNode (location: (106,5)-(106,9)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: (106,5)-(106,6) = "\"" │ │ │ │ │ ├── value_loc: (106,6)-(106,7) = "b" │ │ │ │ │ ├── closing_loc: (106,7)-(106,9) = "\":" │ │ │ │ │ └── unescaped: "b" │ │ │ │ ├── value: │ │ │ │ │ @ ImplicitNode (location: (106,6)-(106,7)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── value: │ │ │ │ │ @ LocalVariableTargetNode (location: (106,6)-(106,7)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── name: :b │ │ │ │ │ └── depth: 0 │ │ │ │ └── operator_loc: ∅ @@ -952,17 +1082,20 @@ │ ├── case_keyword_loc: (105,0)-(105,4) = "case" │ └── end_keyword_loc: (107,0)-(107,3) = "end" └── @ CaseMatchNode (location: (109,0)-(111,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (109,5)-(109,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (109,5)-(109,6) = ":" │ ├── value_loc: (109,6)-(109,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 1) │ └── @ InNode (location: (110,0)-(110,5)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ HashPatternNode (location: (110,3)-(110,5)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── elements: (length: 0) │ │ ├── rest: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_31.txt b/test/prism/snapshots/seattlerb/case_in_31.txt index fdf5ce2a29e6ee..f2bb9dac287ad9 100644 --- a/test/prism/snapshots/seattlerb/case_in_31.txt +++ b/test/prism/snapshots/seattlerb/case_in_31.txt @@ -1,33 +1,40 @@ @ ProgramNode (location: (1,0)-(4,3)) +├── flags: ∅ ├── locals: [:c] └── statements: @ StatementsNode (location: (1,0)-(4,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(4,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(3,4)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (2,3)-(2,11)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ SymbolNode (location: (2,4)-(2,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (2,4)-(2,5) = ":" │ │ │ ├── value_loc: (2,5)-(2,6) = "b" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "b" │ │ ├── rest: │ │ │ @ SplatNode (location: (2,8)-(2,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (2,8)-(2,9) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (2,9)-(2,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :c │ │ │ └── depth: 0 │ │ ├── posts: (length: 0) @@ -35,9 +42,10 @@ │ │ └── closing_loc: (2,10)-(2,11) = "]" │ ├── statements: │ │ @ StatementsNode (location: (3,2)-(3,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (3,2)-(3,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (3,2)-(3,3) = ":" │ │ ├── value_loc: (3,3)-(3,4) = "d" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_37.txt b/test/prism/snapshots/seattlerb/case_in_37.txt index 1a1d887b4fdf20..9083ac2d419661 100644 --- a/test/prism/snapshots/seattlerb/case_in_37.txt +++ b/test/prism/snapshots/seattlerb/case_in_37.txt @@ -1,38 +1,47 @@ @ ProgramNode (location: (1,0)-(4,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(4,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(4,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(3,4)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ HashPatternNode (location: (2,3)-(2,19)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── elements: (length: 1) │ │ │ └── @ AssocNode (location: (2,5)-(2,17)) + │ │ │ ├── flags: ∅ │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (2,5)-(2,7)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (2,5)-(2,6) = "b" │ │ │ │ ├── closing_loc: (2,6)-(2,7) = ":" │ │ │ │ └── unescaped: "b" │ │ │ ├── value: │ │ │ │ @ ArrayPatternNode (location: (2,8)-(2,17)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── constant: ∅ │ │ │ │ ├── requireds: (length: 1) │ │ │ │ │ └── @ ConstantReadNode (location: (2,9)-(2,13)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── name: :Hash │ │ │ │ ├── rest: │ │ │ │ │ @ SplatNode (location: (2,15)-(2,16)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── operator_loc: (2,15)-(2,16) = "*" │ │ │ │ │ └── expression: ∅ │ │ │ │ ├── posts: (length: 0) @@ -44,9 +53,10 @@ │ │ └── closing_loc: (2,18)-(2,19) = "}" │ ├── statements: │ │ @ StatementsNode (location: (3,2)-(3,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (3,2)-(3,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (3,2)-(3,3) = ":" │ │ ├── value_loc: (3,3)-(3,4) = "c" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_42.txt b/test/prism/snapshots/seattlerb/case_in_42.txt index f985d6bc8db09b..a498fa63b027bb 100644 --- a/test/prism/snapshots/seattlerb/case_in_42.txt +++ b/test/prism/snapshots/seattlerb/case_in_42.txt @@ -1,33 +1,40 @@ @ ProgramNode (location: (1,0)-(3,3)) +├── flags: ∅ ├── locals: [:_] └── statements: @ StatementsNode (location: (1,0)-(3,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(3,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(2,18)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (2,3)-(2,9)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ SymbolNode (location: (2,3)-(2,5)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (2,3)-(2,4) = ":" │ │ │ ├── value_loc: (2,4)-(2,5) = "b" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "b" │ │ ├── rest: │ │ │ @ SplatNode (location: (2,7)-(2,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (2,7)-(2,8) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (2,8)-(2,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :_ │ │ │ └── depth: 0 │ │ ├── posts: (length: 0) @@ -35,8 +42,10 @@ │ │ └── closing_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (2,15)-(2,18)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ NilNode (location: (2,15)-(2,18)) + │ │ └── flags: newline, static_literal │ ├── in_loc: (2,0)-(2,2) = "in" │ └── then_loc: (2,10)-(2,14) = "then" ├── consequent: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_42_2.txt b/test/prism/snapshots/seattlerb/case_in_42_2.txt index c399ba1bfa4adc..909874bc0c5985 100644 --- a/test/prism/snapshots/seattlerb/case_in_42_2.txt +++ b/test/prism/snapshots/seattlerb/case_in_42_2.txt @@ -1,29 +1,37 @@ @ ProgramNode (location: (1,0)-(3,3)) +├── flags: ∅ ├── locals: [:list] └── statements: @ StatementsNode (location: (1,0)-(3,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(3,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(2,20)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (2,3)-(2,11)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (2,3)-(2,4)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :A │ │ ├── requireds: (length: 0) │ │ ├── rest: │ │ │ @ SplatNode (location: (2,5)-(2,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (2,5)-(2,6) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (2,6)-(2,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :list │ │ │ └── depth: 0 │ │ ├── posts: (length: 0) @@ -31,8 +39,10 @@ │ │ └── closing_loc: (2,10)-(2,11) = ")" │ ├── statements: │ │ @ StatementsNode (location: (2,17)-(2,20)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ NilNode (location: (2,17)-(2,20)) + │ │ └── flags: newline, static_literal │ ├── in_loc: (2,0)-(2,2) = "in" │ └── then_loc: (2,12)-(2,16) = "then" ├── consequent: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_47.txt b/test/prism/snapshots/seattlerb/case_in_47.txt index 99baebce05a9b3..fdc31f3bba235d 100644 --- a/test/prism/snapshots/seattlerb/case_in_47.txt +++ b/test/prism/snapshots/seattlerb/case_in_47.txt @@ -1,35 +1,41 @@ @ ProgramNode (location: (1,0)-(4,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(4,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(4,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(3,4)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (2,3)-(2,14)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 0) │ │ ├── rest: │ │ │ @ SplatNode (location: (2,4)-(2,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (2,4)-(2,5) = "*" │ │ │ └── expression: ∅ │ │ ├── posts: (length: 2) │ │ │ ├── @ SymbolNode (location: (2,7)-(2,9)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (2,7)-(2,8) = ":" │ │ │ │ ├── value_loc: (2,8)-(2,9) = "b" │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: "b" │ │ │ └── @ SymbolNode (location: (2,11)-(2,13)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (2,11)-(2,12) = ":" │ │ │ ├── value_loc: (2,12)-(2,13) = "c" │ │ │ ├── closing_loc: ∅ @@ -38,9 +44,10 @@ │ │ └── closing_loc: (2,13)-(2,14) = "]" │ ├── statements: │ │ @ StatementsNode (location: (3,2)-(3,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (3,2)-(3,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (3,2)-(3,3) = ":" │ │ ├── value_loc: (3,3)-(3,4) = "d" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_67.txt b/test/prism/snapshots/seattlerb/case_in_67.txt index 4bab417d578b17..372b8fe221ae4e 100644 --- a/test/prism/snapshots/seattlerb/case_in_67.txt +++ b/test/prism/snapshots/seattlerb/case_in_67.txt @@ -1,31 +1,37 @@ @ ProgramNode (location: (1,0)-(3,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(3,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(2,15)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ RangeNode (location: (2,3)-(2,6)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (2,3)-(2,4)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: ∅ │ │ └── operator_loc: (2,4)-(2,6) = ".." │ ├── statements: │ │ @ StatementsNode (location: (2,12)-(2,15)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ NilNode (location: (2,12)-(2,15)) + │ │ └── flags: newline, static_literal │ ├── in_loc: (2,0)-(2,2) = "in" │ └── then_loc: (2,7)-(2,11) = "then" ├── consequent: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_86.txt b/test/prism/snapshots/seattlerb/case_in_86.txt index 082aa74ecac150..2cb5dd6867cbc7 100644 --- a/test/prism/snapshots/seattlerb/case_in_86.txt +++ b/test/prism/snapshots/seattlerb/case_in_86.txt @@ -1,21 +1,24 @@ @ ProgramNode (location: (1,0)-(3,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(3,3)) + ├── flags: newline ├── predicate: │ @ ArrayNode (location: (1,5)-(1,13)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 2) │ │ ├── @ SymbolNode (location: (1,6)-(1,8)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (1,6)-(1,7) = ":" │ │ │ ├── value_loc: (1,7)-(1,8) = "a" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a" │ │ └── @ SymbolNode (location: (1,10)-(1,12)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,10)-(1,11) = ":" │ │ ├── value_loc: (1,11)-(1,12) = "b" │ │ ├── closing_loc: ∅ @@ -24,17 +27,21 @@ │ └── closing_loc: (1,12)-(1,13) = "]" ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(2,25)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (2,3)-(2,16)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ ConstantPathNode (location: (2,3)-(2,13)) + │ │ │ ├── flags: ∅ │ │ │ ├── parent: ∅ │ │ │ ├── name: :NilClass │ │ │ ├── delimiter_loc: (2,3)-(2,5) = "::" │ │ │ └── name_loc: (2,5)-(2,13) = "NilClass" │ │ ├── rest: │ │ │ @ SplatNode (location: (2,15)-(2,16)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (2,15)-(2,16) = "*" │ │ │ └── expression: ∅ │ │ ├── posts: (length: 0) @@ -42,8 +49,10 @@ │ │ └── closing_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (2,22)-(2,25)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ NilNode (location: (2,22)-(2,25)) + │ │ └── flags: newline, static_literal │ ├── in_loc: (2,0)-(2,2) = "in" │ └── then_loc: (2,17)-(2,21) = "then" ├── consequent: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_86_2.txt b/test/prism/snapshots/seattlerb/case_in_86_2.txt index 346264f9071c8c..f02387c52d31bd 100644 --- a/test/prism/snapshots/seattlerb/case_in_86_2.txt +++ b/test/prism/snapshots/seattlerb/case_in_86_2.txt @@ -1,21 +1,24 @@ @ ProgramNode (location: (1,0)-(3,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(3,3)) + ├── flags: newline ├── predicate: │ @ ArrayNode (location: (1,5)-(1,13)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 2) │ │ ├── @ SymbolNode (location: (1,6)-(1,8)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (1,6)-(1,7) = ":" │ │ │ ├── value_loc: (1,7)-(1,8) = "a" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a" │ │ └── @ SymbolNode (location: (1,10)-(1,12)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,10)-(1,11) = ":" │ │ ├── value_loc: (1,11)-(1,12) = "b" │ │ ├── closing_loc: ∅ @@ -24,16 +27,20 @@ │ └── closing_loc: (1,12)-(1,13) = "]" ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(2,25)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (2,3)-(2,16)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 0) │ │ ├── rest: │ │ │ @ SplatNode (location: (2,3)-(2,4)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (2,3)-(2,4) = "*" │ │ │ └── expression: ∅ │ │ ├── posts: (length: 1) │ │ │ └── @ ConstantPathNode (location: (2,6)-(2,16)) + │ │ │ ├── flags: ∅ │ │ │ ├── parent: ∅ │ │ │ ├── name: :NilClass │ │ │ ├── delimiter_loc: (2,6)-(2,8) = "::" @@ -42,8 +49,10 @@ │ │ └── closing_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (2,22)-(2,25)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ NilNode (location: (2,22)-(2,25)) + │ │ └── flags: newline, static_literal │ ├── in_loc: (2,0)-(2,2) = "in" │ └── then_loc: (2,17)-(2,21) = "then" ├── consequent: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_array_pat_const.txt b/test/prism/snapshots/seattlerb/case_in_array_pat_const.txt index f361e8d4589940..64684df203587e 100644 --- a/test/prism/snapshots/seattlerb/case_in_array_pat_const.txt +++ b/test/prism/snapshots/seattlerb/case_in_array_pat_const.txt @@ -1,25 +1,32 @@ @ ProgramNode (location: (1,0)-(4,3)) +├── flags: ∅ ├── locals: [:c] └── statements: @ StatementsNode (location: (1,0)-(4,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(4,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(3,4)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (2,3)-(2,7)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (2,3)-(2,4)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :B │ │ ├── requireds: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (2,5)-(2,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :c │ │ │ └── depth: 0 │ │ ├── rest: ∅ @@ -28,9 +35,10 @@ │ │ └── closing_loc: (2,6)-(2,7) = "]" │ ├── statements: │ │ @ StatementsNode (location: (3,2)-(3,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (3,2)-(3,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (3,2)-(3,3) = ":" │ │ ├── value_loc: (3,3)-(3,4) = "d" │ │ ├── closing_loc: ∅ 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 d6fb80ef9087b8..6a14e0271a6841 100644 --- a/test/prism/snapshots/seattlerb/case_in_array_pat_const2.txt +++ b/test/prism/snapshots/seattlerb/case_in_array_pat_const2.txt @@ -1,30 +1,38 @@ @ ProgramNode (location: (1,0)-(4,3)) +├── flags: ∅ ├── locals: [:d] └── statements: @ StatementsNode (location: (1,0)-(4,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(4,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(3,4)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (2,3)-(2,10)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantPathNode (location: (2,3)-(2,7)) + │ │ │ ├── flags: ∅ │ │ │ ├── parent: │ │ │ │ @ ConstantReadNode (location: (2,3)-(2,4)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :B │ │ │ ├── name: :C │ │ │ ├── delimiter_loc: (2,4)-(2,6) = "::" │ │ │ └── name_loc: (2,6)-(2,7) = "C" │ │ ├── requireds: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (2,8)-(2,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :d │ │ │ └── depth: 0 │ │ ├── rest: ∅ @@ -33,9 +41,10 @@ │ │ └── closing_loc: (2,9)-(2,10) = "]" │ ├── statements: │ │ @ StatementsNode (location: (3,2)-(3,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (3,2)-(3,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (3,2)-(3,3) = ":" │ │ ├── value_loc: (3,3)-(3,4) = "e" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_array_pat_paren_assign.txt b/test/prism/snapshots/seattlerb/case_in_array_pat_paren_assign.txt index 8d185b250a7048..9d617078fda414 100644 --- a/test/prism/snapshots/seattlerb/case_in_array_pat_paren_assign.txt +++ b/test/prism/snapshots/seattlerb/case_in_array_pat_paren_assign.txt @@ -1,30 +1,39 @@ @ ProgramNode (location: (1,0)-(4,3)) +├── flags: ∅ ├── locals: [:d] └── statements: @ StatementsNode (location: (1,0)-(4,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(4,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(3,4)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ ArrayPatternNode (location: (2,3)-(2,12)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (2,3)-(2,4)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :B │ │ ├── requireds: (length: 1) │ │ │ └── @ CapturePatternNode (location: (2,5)-(2,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── value: │ │ │ │ @ ConstantReadNode (location: (2,5)-(2,6)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :C │ │ │ ├── target: │ │ │ │ @ LocalVariableTargetNode (location: (2,10)-(2,11)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :d │ │ │ │ └── depth: 0 │ │ │ └── operator_loc: (2,7)-(2,9) = "=>" @@ -34,9 +43,10 @@ │ │ └── closing_loc: (2,11)-(2,12) = ")" │ ├── statements: │ │ @ StatementsNode (location: (3,2)-(3,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (3,2)-(3,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (3,2)-(3,3) = ":" │ │ ├── value_loc: (3,3)-(3,4) = "d" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_const.txt b/test/prism/snapshots/seattlerb/case_in_const.txt index c4b838aa1d5e47..73f681c7dcd1ec 100644 --- a/test/prism/snapshots/seattlerb/case_in_const.txt +++ b/test/prism/snapshots/seattlerb/case_in_const.txt @@ -1,22 +1,29 @@ @ ProgramNode (location: (1,0)-(4,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(4,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(4,3)) + ├── flags: newline ├── predicate: │ @ ConstantReadNode (location: (1,5)-(1,10)) + │ ├── flags: ∅ │ └── name: :Array ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(3,4)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ ConstantReadNode (location: (2,3)-(2,8)) + │ │ ├── flags: ∅ │ │ └── name: :Class │ ├── statements: │ │ @ StatementsNode (location: (3,2)-(3,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (3,2)-(3,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (3,2)-(3,3) = ":" │ │ ├── value_loc: (3,3)-(3,4) = "b" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_else.txt b/test/prism/snapshots/seattlerb/case_in_else.txt index 5eae7fc1ea25bf..0ad3935ce8d688 100644 --- a/test/prism/snapshots/seattlerb/case_in_else.txt +++ b/test/prism/snapshots/seattlerb/case_in_else.txt @@ -1,22 +1,29 @@ @ ProgramNode (location: (1,0)-(6,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(6,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(6,3)) + ├── flags: newline ├── predicate: │ @ ConstantReadNode (location: (1,5)-(1,10)) + │ ├── flags: ∅ │ └── name: :Array ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(3,4)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ ConstantReadNode (location: (2,3)-(2,8)) + │ │ ├── flags: ∅ │ │ └── name: :Class │ ├── statements: │ │ @ StatementsNode (location: (3,2)-(3,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (3,2)-(3,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (3,2)-(3,3) = ":" │ │ ├── value_loc: (3,3)-(3,4) = "b" │ │ ├── closing_loc: ∅ @@ -25,12 +32,14 @@ │ └── then_loc: ∅ ├── consequent: │ @ ElseNode (location: (4,0)-(6,3)) + │ ├── flags: ∅ │ ├── else_keyword_loc: (4,0)-(4,4) = "else" │ ├── statements: │ │ @ StatementsNode (location: (5,2)-(5,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (5,2)-(5,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (5,2)-(5,3) = ":" │ │ ├── value_loc: (5,3)-(5,4) = "c" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_find.txt b/test/prism/snapshots/seattlerb/case_in_find.txt index f84c4c30d0d381..79d737afa7ef6a 100644 --- a/test/prism/snapshots/seattlerb/case_in_find.txt +++ b/test/prism/snapshots/seattlerb/case_in_find.txt @@ -1,40 +1,49 @@ @ ProgramNode (location: (1,0)-(3,3)) +├── flags: ∅ ├── locals: [:a, :b] └── statements: @ StatementsNode (location: (1,0)-(3,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(3,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 1) │ └── @ InNode (location: (2,2)-(2,15)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ FindPatternNode (location: (2,5)-(2,15)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── left: │ │ │ @ SplatNode (location: (2,5)-(2,7)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (2,5)-(2,6) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (2,6)-(2,7)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ ├── requireds: (length: 1) │ │ │ └── @ SymbolNode (location: (2,9)-(2,11)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (2,9)-(2,10) = ":" │ │ │ ├── value_loc: (2,10)-(2,11) = "+" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "+" │ │ ├── right: │ │ │ @ SplatNode (location: (2,13)-(2,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (2,13)-(2,14) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (2,14)-(2,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :b │ │ │ └── depth: 0 │ │ ├── opening_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_find_array.txt b/test/prism/snapshots/seattlerb/case_in_find_array.txt index a757f803460d03..b874867062d1cf 100644 --- a/test/prism/snapshots/seattlerb/case_in_find_array.txt +++ b/test/prism/snapshots/seattlerb/case_in_find_array.txt @@ -1,37 +1,45 @@ @ ProgramNode (location: (1,0)-(3,3)) +├── flags: ∅ ├── locals: [:c] └── statements: @ StatementsNode (location: (1,0)-(3,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(3,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(2,16)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ FindPatternNode (location: (2,3)-(2,16)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── left: │ │ │ @ SplatNode (location: (2,4)-(2,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (2,4)-(2,5) = "*" │ │ │ └── expression: ∅ │ │ ├── requireds: (length: 2) │ │ │ ├── @ SymbolNode (location: (2,7)-(2,9)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (2,7)-(2,8) = ":" │ │ │ │ ├── value_loc: (2,8)-(2,9) = "b" │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: "b" │ │ │ └── @ LocalVariableTargetNode (location: (2,11)-(2,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :c │ │ │ └── depth: 0 │ │ ├── right: │ │ │ @ SplatNode (location: (2,14)-(2,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (2,14)-(2,15) = "*" │ │ │ └── expression: ∅ │ │ ├── opening_loc: (2,3)-(2,4) = "[" diff --git a/test/prism/snapshots/seattlerb/case_in_hash_pat.txt b/test/prism/snapshots/seattlerb/case_in_hash_pat.txt index e813efa9ee52f2..3a106e973a8f57 100644 --- a/test/prism/snapshots/seattlerb/case_in_hash_pat.txt +++ b/test/prism/snapshots/seattlerb/case_in_hash_pat.txt @@ -1,26 +1,32 @@ @ ProgramNode (location: (1,0)-(4,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(4,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(4,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(3,4)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ HashPatternNode (location: (2,3)-(2,21)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── elements: (length: 2) │ │ │ ├── @ AssocNode (location: (2,5)-(2,11)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── key: │ │ │ │ │ @ SymbolNode (location: (2,5)-(2,7)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── value_loc: (2,5)-(2,6) = "b" │ │ │ │ │ ├── closing_loc: (2,6)-(2,7) = ":" @@ -34,9 +40,10 @@ │ │ │ │ │ └── unescaped: "c" │ │ │ │ └── operator_loc: ∅ │ │ │ └── @ AssocNode (location: (2,13)-(2,19)) + │ │ │ ├── flags: ∅ │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (2,13)-(2,15)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (2,13)-(2,14) = "d" │ │ │ │ ├── closing_loc: (2,14)-(2,15) = ":" @@ -54,9 +61,10 @@ │ │ └── closing_loc: (2,20)-(2,21) = "}" │ ├── statements: │ │ @ StatementsNode (location: (3,2)-(3,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (3,2)-(3,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (3,2)-(3,3) = ":" │ │ ├── value_loc: (3,3)-(3,4) = "f" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_hash_pat_assign.txt b/test/prism/snapshots/seattlerb/case_in_hash_pat_assign.txt index 790d9d63ffb2b3..8494b71025425c 100644 --- a/test/prism/snapshots/seattlerb/case_in_hash_pat_assign.txt +++ b/test/prism/snapshots/seattlerb/case_in_hash_pat_assign.txt @@ -1,45 +1,55 @@ @ ProgramNode (location: (1,0)-(4,3)) +├── flags: ∅ ├── locals: [:x, :f] └── statements: @ StatementsNode (location: (1,0)-(4,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(4,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(3,4)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ HashPatternNode (location: (2,3)-(2,34)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── elements: (length: 3) │ │ │ ├── @ AssocNode (location: (2,5)-(2,20)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── key: │ │ │ │ │ @ SymbolNode (location: (2,5)-(2,7)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── value_loc: (2,5)-(2,6) = "b" │ │ │ │ │ ├── closing_loc: (2,6)-(2,7) = ":" │ │ │ │ │ └── unescaped: "b" │ │ │ │ ├── value: │ │ │ │ │ @ CapturePatternNode (location: (2,8)-(2,20)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── value: │ │ │ │ │ │ @ ConstantReadNode (location: (2,8)-(2,15)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ └── name: :Integer │ │ │ │ │ ├── target: │ │ │ │ │ │ @ LocalVariableTargetNode (location: (2,19)-(2,20)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ ├── name: :x │ │ │ │ │ │ └── depth: 0 │ │ │ │ │ └── operator_loc: (2,16)-(2,18) = "=>" │ │ │ │ └── operator_loc: ∅ │ │ │ ├── @ AssocNode (location: (2,22)-(2,28)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── key: │ │ │ │ │ @ SymbolNode (location: (2,22)-(2,24)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── value_loc: (2,22)-(2,23) = "d" │ │ │ │ │ ├── closing_loc: (2,23)-(2,24) = ":" @@ -53,17 +63,20 @@ │ │ │ │ │ └── unescaped: "e" │ │ │ │ └── operator_loc: ∅ │ │ │ └── @ AssocNode (location: (2,30)-(2,32)) + │ │ │ ├── flags: ∅ │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (2,30)-(2,32)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (2,30)-(2,31) = "f" │ │ │ │ ├── closing_loc: (2,31)-(2,32) = ":" │ │ │ │ └── unescaped: "f" │ │ │ ├── value: │ │ │ │ @ ImplicitNode (location: (2,30)-(2,31)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── value: │ │ │ │ @ LocalVariableTargetNode (location: (2,30)-(2,31)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :f │ │ │ │ └── depth: 0 │ │ │ └── operator_loc: ∅ @@ -72,9 +85,10 @@ │ │ └── closing_loc: (2,33)-(2,34) = "}" │ ├── statements: │ │ @ StatementsNode (location: (3,2)-(3,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (3,2)-(3,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (3,2)-(3,3) = ":" │ │ ├── value_loc: (3,3)-(3,4) = "g" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_hash_pat_paren_assign.txt b/test/prism/snapshots/seattlerb/case_in_hash_pat_paren_assign.txt index 4c8cfd0e544974..c2995013dd586d 100644 --- a/test/prism/snapshots/seattlerb/case_in_hash_pat_paren_assign.txt +++ b/test/prism/snapshots/seattlerb/case_in_hash_pat_paren_assign.txt @@ -1,35 +1,42 @@ @ ProgramNode (location: (1,0)-(4,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(4,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(4,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(3,4)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ HashPatternNode (location: (2,3)-(2,11)) + │ │ ├── flags: ∅ │ │ ├── constant: │ │ │ @ ConstantReadNode (location: (2,3)-(2,4)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :B │ │ ├── elements: (length: 1) │ │ │ └── @ AssocNode (location: (2,5)-(2,10)) + │ │ │ ├── flags: static_literal │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (2,5)-(2,7)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (2,5)-(2,6) = "a" │ │ │ │ ├── closing_loc: (2,6)-(2,7) = ":" │ │ │ │ └── unescaped: "a" │ │ │ ├── value: │ │ │ │ @ IntegerNode (location: (2,8)-(2,10)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 42 │ │ │ └── operator_loc: ∅ │ │ ├── rest: ∅ @@ -37,9 +44,10 @@ │ │ └── closing_loc: (2,10)-(2,11) = ")" │ ├── statements: │ │ @ StatementsNode (location: (3,2)-(3,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (3,2)-(3,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (3,2)-(3,3) = ":" │ │ ├── value_loc: (3,3)-(3,4) = "d" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_hash_pat_paren_true.txt b/test/prism/snapshots/seattlerb/case_in_hash_pat_paren_true.txt index 8ff95a161f37da..07f1d543395587 100644 --- a/test/prism/snapshots/seattlerb/case_in_hash_pat_paren_true.txt +++ b/test/prism/snapshots/seattlerb/case_in_hash_pat_paren_true.txt @@ -1,41 +1,49 @@ @ ProgramNode (location: (1,0)-(4,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(4,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(4,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(3,4)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ HashPatternNode (location: (2,3)-(2,10)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── elements: (length: 1) │ │ │ └── @ AssocNode (location: (2,3)-(2,10)) + │ │ │ ├── flags: static_literal │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (2,3)-(2,5)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (2,3)-(2,4) = "b" │ │ │ │ ├── closing_loc: (2,4)-(2,5) = ":" │ │ │ │ └── unescaped: "b" │ │ │ ├── value: │ │ │ │ @ TrueNode (location: (2,6)-(2,10)) + │ │ │ │ └── flags: static_literal │ │ │ └── operator_loc: ∅ │ │ ├── rest: ∅ │ │ ├── opening_loc: ∅ │ │ └── closing_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (3,2)-(3,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (3,2)-(3,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (3,2)-(3,3) = ":" │ │ ├── value_loc: (3,3)-(3,4) = "c" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_hash_pat_rest.txt b/test/prism/snapshots/seattlerb/case_in_hash_pat_rest.txt index b93b889ec5fd91..42e6b3f2a5e50e 100644 --- a/test/prism/snapshots/seattlerb/case_in_hash_pat_rest.txt +++ b/test/prism/snapshots/seattlerb/case_in_hash_pat_rest.txt @@ -1,39 +1,48 @@ @ ProgramNode (location: (1,0)-(3,3)) +├── flags: ∅ ├── locals: [:c, :rest] └── statements: @ StatementsNode (location: (1,0)-(3,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(3,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(2,23)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ HashPatternNode (location: (2,3)-(2,15)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── elements: (length: 1) │ │ │ └── @ AssocNode (location: (2,3)-(2,7)) + │ │ │ ├── flags: ∅ │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (2,3)-(2,5)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (2,3)-(2,4) = "b" │ │ │ │ ├── closing_loc: (2,4)-(2,5) = ":" │ │ │ │ └── unescaped: "b" │ │ │ ├── value: │ │ │ │ @ LocalVariableTargetNode (location: (2,6)-(2,7)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :c │ │ │ │ └── depth: 0 │ │ │ └── operator_loc: ∅ │ │ ├── rest: │ │ │ @ AssocSplatNode (location: (2,9)-(2,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── value: │ │ │ │ @ LocalVariableTargetNode (location: (2,11)-(2,15)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :rest │ │ │ │ └── depth: 0 │ │ │ └── operator_loc: (2,9)-(2,11) = "**" @@ -41,9 +50,10 @@ │ │ └── closing_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (2,21)-(2,23)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (2,21)-(2,23)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (2,21)-(2,22) = ":" │ │ ├── value_loc: (2,22)-(2,23) = "d" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_hash_pat_rest_solo.txt b/test/prism/snapshots/seattlerb/case_in_hash_pat_rest_solo.txt index 956e93faa02b46..d69ff63b9d4136 100644 --- a/test/prism/snapshots/seattlerb/case_in_hash_pat_rest_solo.txt +++ b/test/prism/snapshots/seattlerb/case_in_hash_pat_rest_solo.txt @@ -1,26 +1,33 @@ @ ProgramNode (location: (1,0)-(3,3)) +├── flags: ∅ ├── locals: [:rest] └── statements: @ StatementsNode (location: (1,0)-(3,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(3,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(2,17)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ HashPatternNode (location: (2,3)-(2,9)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── elements: (length: 0) │ │ ├── rest: │ │ │ @ AssocSplatNode (location: (2,3)-(2,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── value: │ │ │ │ @ LocalVariableTargetNode (location: (2,5)-(2,9)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :rest │ │ │ │ └── depth: 0 │ │ │ └── operator_loc: (2,3)-(2,5) = "**" @@ -28,9 +35,10 @@ │ │ └── closing_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (2,15)-(2,17)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (2,15)-(2,17)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (2,15)-(2,16) = ":" │ │ ├── value_loc: (2,16)-(2,17) = "d" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_if_unless_post_mod.txt b/test/prism/snapshots/seattlerb/case_in_if_unless_post_mod.txt index a21d3e15dd4d4e..14ac02679072c8 100644 --- a/test/prism/snapshots/seattlerb/case_in_if_unless_post_mod.txt +++ b/test/prism/snapshots/seattlerb/case_in_if_unless_post_mod.txt @@ -1,36 +1,45 @@ @ ProgramNode (location: (1,0)-(6,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(6,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(6,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 2) │ ├── @ InNode (location: (2,0)-(3,4)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IfNode (location: (2,3)-(2,12)) + │ │ │ ├── flags: newline │ │ │ ├── if_keyword_loc: (2,5)-(2,7) = "if" │ │ │ ├── predicate: │ │ │ │ @ TrueNode (location: (2,8)-(2,12)) + │ │ │ │ └── flags: static_literal │ │ │ ├── then_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (2,3)-(2,4)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ ConstantReadNode (location: (2,3)-(2,4)) + │ │ │ │ ├── flags: newline │ │ │ │ └── name: :A │ │ │ ├── consequent: ∅ │ │ │ └── end_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (3,2)-(3,4)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ SymbolNode (location: (3,2)-(3,4)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (3,2)-(3,3) = ":" │ │ │ ├── value_loc: (3,3)-(3,4) = "C" │ │ │ ├── closing_loc: ∅ @@ -38,24 +47,30 @@ │ │ ├── in_loc: (2,0)-(2,2) = "in" │ │ └── then_loc: ∅ │ └── @ InNode (location: (4,0)-(5,4)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ UnlessNode (location: (4,3)-(4,17)) + │ │ ├── flags: newline │ │ ├── keyword_loc: (4,5)-(4,11) = "unless" │ │ ├── predicate: │ │ │ @ FalseNode (location: (4,12)-(4,17)) + │ │ │ └── flags: static_literal │ │ ├── then_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (4,3)-(4,4)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ ConstantReadNode (location: (4,3)-(4,4)) + │ │ │ ├── flags: newline │ │ │ └── name: :D │ │ ├── consequent: ∅ │ │ └── end_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (5,2)-(5,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (5,2)-(5,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (5,2)-(5,3) = ":" │ │ ├── value_loc: (5,3)-(5,4) = "E" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_multiple.txt b/test/prism/snapshots/seattlerb/case_in_multiple.txt index eba0084f969777..d0d790bdb54f96 100644 --- a/test/prism/snapshots/seattlerb/case_in_multiple.txt +++ b/test/prism/snapshots/seattlerb/case_in_multiple.txt @@ -1,31 +1,38 @@ @ ProgramNode (location: (1,0)-(6,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(6,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(6,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 2) │ ├── @ InNode (location: (2,0)-(3,4)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ConstantPathNode (location: (2,3)-(2,7)) + │ │ │ ├── flags: ∅ │ │ │ ├── parent: │ │ │ │ @ ConstantReadNode (location: (2,3)-(2,4)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :A │ │ │ ├── name: :B │ │ │ ├── delimiter_loc: (2,4)-(2,6) = "::" │ │ │ └── name_loc: (2,6)-(2,7) = "B" │ │ ├── statements: │ │ │ @ StatementsNode (location: (3,2)-(3,4)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ SymbolNode (location: (3,2)-(3,4)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (3,2)-(3,3) = ":" │ │ │ ├── value_loc: (3,3)-(3,4) = "C" │ │ │ ├── closing_loc: ∅ @@ -33,19 +40,23 @@ │ │ ├── in_loc: (2,0)-(2,2) = "in" │ │ └── then_loc: ∅ │ └── @ InNode (location: (4,0)-(5,4)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ ConstantPathNode (location: (4,3)-(4,7)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (4,3)-(4,4)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :D │ │ ├── name: :E │ │ ├── delimiter_loc: (4,4)-(4,6) = "::" │ │ └── name_loc: (4,6)-(4,7) = "E" │ ├── statements: │ │ @ StatementsNode (location: (5,2)-(5,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (5,2)-(5,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (5,2)-(5,3) = ":" │ │ ├── value_loc: (5,3)-(5,4) = "F" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/case_in_or.txt b/test/prism/snapshots/seattlerb/case_in_or.txt index 7ac66176082b45..9787f5c1ed43a6 100644 --- a/test/prism/snapshots/seattlerb/case_in_or.txt +++ b/test/prism/snapshots/seattlerb/case_in_or.txt @@ -1,32 +1,40 @@ @ ProgramNode (location: (1,0)-(4,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(4,3)) + ├── flags: ∅ └── body: (length: 1) └── @ CaseMatchNode (location: (1,0)-(4,3)) + ├── flags: newline ├── predicate: │ @ SymbolNode (location: (1,5)-(1,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (1,5)-(1,6) = ":" │ ├── value_loc: (1,6)-(1,7) = "a" │ ├── closing_loc: ∅ │ └── unescaped: "a" ├── conditions: (length: 1) │ └── @ InNode (location: (2,0)-(3,4)) + │ ├── flags: ∅ │ ├── pattern: │ │ @ AlternationPatternNode (location: (2,3)-(2,8)) + │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ ConstantReadNode (location: (2,3)-(2,4)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :B │ │ ├── right: │ │ │ @ ConstantReadNode (location: (2,7)-(2,8)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :C │ │ └── operator_loc: (2,5)-(2,6) = "|" │ ├── statements: │ │ @ StatementsNode (location: (3,2)-(3,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (3,2)-(3,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (3,2)-(3,3) = ":" │ │ ├── value_loc: (3,3)-(3,4) = "d" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/class_comments.txt b/test/prism/snapshots/seattlerb/class_comments.txt index 5ac05b6be686e3..75350722a1ac28 100644 --- a/test/prism/snapshots/seattlerb/class_comments.txt +++ b/test/prism/snapshots/seattlerb/class_comments.txt @@ -1,20 +1,26 @@ @ ProgramNode (location: (4,0)-(9,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (4,0)-(9,3)) + ├── flags: ∅ └── body: (length: 1) └── @ ClassNode (location: (4,0)-(9,3)) + ├── flags: newline ├── locals: [] ├── class_keyword_loc: (4,0)-(4,5) = "class" ├── constant_path: │ @ ConstantReadNode (location: (4,6)-(4,7)) + │ ├── flags: ∅ │ └── name: :X ├── inheritance_operator_loc: ∅ ├── superclass: ∅ ├── body: │ @ StatementsNode (location: (6,2)-(8,5)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ DefNode (location: (6,2)-(8,5)) + │ ├── flags: newline │ ├── name: :blah │ ├── name_loc: (6,6)-(6,10) = "blah" │ ├── receiver: ∅ diff --git a/test/prism/snapshots/seattlerb/cond_unary_minus.txt b/test/prism/snapshots/seattlerb/cond_unary_minus.txt index b6e12dfb15c80e..bf82ac4696748a 100644 --- a/test/prism/snapshots/seattlerb/cond_unary_minus.txt +++ b/test/prism/snapshots/seattlerb/cond_unary_minus.txt @@ -1,13 +1,16 @@ @ ProgramNode (location: (1,0)-(1,10)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,10)) + ├── flags: ∅ └── body: (length: 1) └── @ IfNode (location: (1,0)-(1,10)) + ├── flags: newline ├── if_keyword_loc: (1,0)-(1,2) = "if" ├── predicate: │ @ IntegerNode (location: (1,3)-(1,5)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: -1 ├── then_keyword_loc: ∅ ├── statements: ∅ 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 e09eed7d2ff7f2..25d77dac2d5df5 100644 --- a/test/prism/snapshots/seattlerb/const_2_op_asgn_or2.txt +++ b/test/prism/snapshots/seattlerb/const_2_op_asgn_or2.txt @@ -1,13 +1,18 @@ @ ProgramNode (location: (1,0)-(1,12)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,12)) + ├── flags: ∅ └── body: (length: 1) └── @ ConstantPathOrWriteNode (location: (1,0)-(1,12)) + ├── flags: newline ├── target: │ @ ConstantPathNode (location: (1,0)-(1,6)) + │ ├── flags: ∅ │ ├── parent: │ │ @ ConstantPathNode (location: (1,0)-(1,3)) + │ │ ├── flags: ∅ │ │ ├── parent: ∅ │ │ ├── name: :X │ │ ├── delimiter_loc: (1,0)-(1,2) = "::" @@ -18,5 +23,5 @@ ├── operator_loc: (1,7)-(1,10) = "||=" └── value: @ IntegerNode (location: (1,11)-(1,12)) - ├── flags: decimal + ├── flags: static_literal, decimal └── value: 1 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 398af888a89f37..dc098bcf23c4e5 100644 --- a/test/prism/snapshots/seattlerb/const_3_op_asgn_or.txt +++ b/test/prism/snapshots/seattlerb/const_3_op_asgn_or.txt @@ -1,11 +1,15 @@ @ ProgramNode (location: (1,0)-(1,9)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,9)) + ├── flags: ∅ └── body: (length: 1) └── @ ConstantPathOrWriteNode (location: (1,0)-(1,9)) + ├── flags: newline ├── target: │ @ ConstantPathNode (location: (1,0)-(1,3)) + │ ├── flags: ∅ │ ├── parent: ∅ │ ├── name: :X │ ├── delimiter_loc: (1,0)-(1,2) = "::" @@ -13,5 +17,5 @@ ├── operator_loc: (1,4)-(1,7) = "||=" └── value: @ IntegerNode (location: (1,8)-(1,9)) - ├── flags: decimal + ├── flags: static_literal, decimal └── value: 1 diff --git a/test/prism/snapshots/seattlerb/const_op_asgn_and1.txt b/test/prism/snapshots/seattlerb/const_op_asgn_and1.txt index f9792aebb364fa..63b8bc190b1447 100644 --- a/test/prism/snapshots/seattlerb/const_op_asgn_and1.txt +++ b/test/prism/snapshots/seattlerb/const_op_asgn_and1.txt @@ -1,11 +1,15 @@ @ ProgramNode (location: (1,0)-(1,8)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,8)) + ├── flags: ∅ └── body: (length: 1) └── @ ConstantPathOperatorWriteNode (location: (1,0)-(1,8)) + ├── flags: newline ├── target: │ @ ConstantPathNode (location: (1,0)-(1,3)) + │ ├── flags: ∅ │ ├── parent: ∅ │ ├── name: :X │ ├── delimiter_loc: (1,0)-(1,2) = "::" @@ -13,6 +17,6 @@ ├── binary_operator_loc: (1,4)-(1,6) = "&=" ├── value: │ @ IntegerNode (location: (1,7)-(1,8)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 └── 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 146455d327175d..c1ac2bcf526132 100644 --- a/test/prism/snapshots/seattlerb/const_op_asgn_and2.txt +++ b/test/prism/snapshots/seattlerb/const_op_asgn_and2.txt @@ -1,11 +1,15 @@ @ ProgramNode (location: (1,0)-(1,9)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,9)) + ├── flags: ∅ └── body: (length: 1) └── @ ConstantPathAndWriteNode (location: (1,0)-(1,9)) + ├── flags: newline ├── target: │ @ ConstantPathNode (location: (1,0)-(1,3)) + │ ├── flags: ∅ │ ├── parent: ∅ │ ├── name: :X │ ├── delimiter_loc: (1,0)-(1,2) = "::" @@ -13,5 +17,5 @@ ├── operator_loc: (1,4)-(1,7) = "&&=" └── value: @ IntegerNode (location: (1,8)-(1,9)) - ├── flags: decimal + ├── flags: static_literal, decimal └── value: 1 diff --git a/test/prism/snapshots/seattlerb/const_op_asgn_or.txt b/test/prism/snapshots/seattlerb/const_op_asgn_or.txt index 5e9dd39604371f..0a53593d123cbe 100644 --- a/test/prism/snapshots/seattlerb/const_op_asgn_or.txt +++ b/test/prism/snapshots/seattlerb/const_op_asgn_or.txt @@ -1,13 +1,18 @@ @ ProgramNode (location: (1,0)-(1,10)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,10)) + ├── flags: ∅ └── body: (length: 1) └── @ ConstantPathOrWriteNode (location: (1,0)-(1,10)) + ├── flags: newline ├── target: │ @ ConstantPathNode (location: (1,0)-(1,4)) + │ ├── flags: ∅ │ ├── parent: │ │ @ ConstantReadNode (location: (1,0)-(1,1)) + │ │ ├── flags: ∅ │ │ └── name: :X │ ├── name: :Y │ ├── delimiter_loc: (1,1)-(1,3) = "::" @@ -15,5 +20,5 @@ ├── operator_loc: (1,5)-(1,8) = "||=" └── value: @ IntegerNode (location: (1,9)-(1,10)) - ├── flags: decimal + ├── flags: static_literal, decimal └── value: 1 diff --git a/test/prism/snapshots/seattlerb/defined_eh_parens.txt b/test/prism/snapshots/seattlerb/defined_eh_parens.txt index 49b577fcd13431..7e18a24787572f 100644 --- a/test/prism/snapshots/seattlerb/defined_eh_parens.txt +++ b/test/prism/snapshots/seattlerb/defined_eh_parens.txt @@ -1,13 +1,16 @@ @ ProgramNode (location: (1,0)-(1,12)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,12)) + ├── flags: ∅ └── body: (length: 1) └── @ DefinedNode (location: (1,0)-(1,12)) + ├── flags: newline ├── lparen_loc: (1,8)-(1,9) = "(" ├── value: │ @ IntegerNode (location: (1,9)-(1,11)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 42 ├── rparen_loc: (1,11)-(1,12) = ")" └── keyword_loc: (1,0)-(1,8) = "defined?" diff --git a/test/prism/snapshots/seattlerb/defn_arg_asplat_arg.txt b/test/prism/snapshots/seattlerb/defn_arg_asplat_arg.txt index 3f2bdf44a440e6..312b0214d6f61b 100644 --- a/test/prism/snapshots/seattlerb/defn_arg_asplat_arg.txt +++ b/test/prism/snapshots/seattlerb/defn_arg_asplat_arg.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,29)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,29)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,29)) + ├── flags: newline ├── name: :call ├── name_loc: (1,4)-(1,8) = "call" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,9)-(1,24)) + │ ├── flags: ∅ │ ├── requireds: (length: 1) │ │ └── @ RequiredParameterNode (location: (1,9)-(1,15)) │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/defn_arg_forward_args.txt b/test/prism/snapshots/seattlerb/defn_arg_forward_args.txt index 4121770c5c2e1f..905978bc2f165c 100644 --- a/test/prism/snapshots/seattlerb/defn_arg_forward_args.txt +++ b/test/prism/snapshots/seattlerb/defn_arg_forward_args.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,29)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,29)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,29)) + ├── flags: newline ├── name: :a ├── name_loc: (1,4)-(1,5) = "a" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,6)-(1,12)) + │ ├── flags: ∅ │ ├── requireds: (length: 1) │ │ └── @ RequiredParameterNode (location: (1,6)-(1,7)) │ │ ├── flags: ∅ @@ -19,12 +23,14 @@ │ ├── keywords: (length: 0) │ ├── keyword_rest: │ │ @ ForwardingParameterNode (location: (1,9)-(1,12)) + │ │ └── flags: ∅ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (1,15)-(1,24)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,15)-(1,24)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :b @@ -35,9 +41,11 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 2) │ │ ├── @ LocalVariableReadNode (location: (1,17)-(1,18)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :x │ │ │ └── depth: 0 │ │ └── @ ForwardingArgumentsNode (location: (1,20)-(1,23)) + │ │ └── flags: ∅ │ ├── closing_loc: (1,23)-(1,24) = ")" │ └── block: ∅ ├── locals: [:x] diff --git a/test/prism/snapshots/seattlerb/defn_args_forward_args.txt b/test/prism/snapshots/seattlerb/defn_args_forward_args.txt index 178b6ccde736a3..ca03f79d57d5e9 100644 --- a/test/prism/snapshots/seattlerb/defn_args_forward_args.txt +++ b/test/prism/snapshots/seattlerb/defn_args_forward_args.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,41)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,41)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,41)) + ├── flags: newline ├── name: :a ├── name_loc: (1,4)-(1,5) = "a" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,6)-(1,18)) + │ ├── flags: ∅ │ ├── requireds: (length: 3) │ │ ├── @ RequiredParameterNode (location: (1,6)-(1,7)) │ │ │ ├── flags: ∅ @@ -25,12 +29,14 @@ │ ├── keywords: (length: 0) │ ├── keyword_rest: │ │ @ ForwardingParameterNode (location: (1,15)-(1,18)) + │ │ └── flags: ∅ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (1,21)-(1,36)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,21)-(1,36)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :b @@ -41,15 +47,17 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 3) │ │ ├── @ SymbolNode (location: (1,23)-(1,27)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (1,23)-(1,24) = ":" │ │ │ ├── value_loc: (1,24)-(1,27) = "get" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "get" │ │ ├── @ LocalVariableReadNode (location: (1,29)-(1,30)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :z │ │ │ └── depth: 0 │ │ └── @ ForwardingArgumentsNode (location: (1,32)-(1,35)) + │ │ └── flags: ∅ │ ├── closing_loc: (1,35)-(1,36) = ")" │ └── block: ∅ ├── locals: [:x, :y, :z] diff --git a/test/prism/snapshots/seattlerb/defn_comments.txt b/test/prism/snapshots/seattlerb/defn_comments.txt index 585aa65c9a6df2..8ea66b77603487 100644 --- a/test/prism/snapshots/seattlerb/defn_comments.txt +++ b/test/prism/snapshots/seattlerb/defn_comments.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (4,0)-(5,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (4,0)-(5,3)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (4,0)-(5,3)) + ├── flags: newline ├── name: :blah ├── name_loc: (4,4)-(4,8) = "blah" ├── receiver: ∅ diff --git a/test/prism/snapshots/seattlerb/defn_endless_command.txt b/test/prism/snapshots/seattlerb/defn_endless_command.txt index c3ea59282a7e90..e951ee2e45f528 100644 --- a/test/prism/snapshots/seattlerb/defn_endless_command.txt +++ b/test/prism/snapshots/seattlerb/defn_endless_command.txt @@ -1,18 +1,22 @@ @ ProgramNode (location: (1,0)-(1,33)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,33)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,33)) + ├── flags: newline ├── name: :some_method ├── name_loc: (1,4)-(1,15) = "some_method" ├── receiver: ∅ ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (1,18)-(1,33)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,18)-(1,33)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :other_method @@ -23,7 +27,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (1,31)-(1,33)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 42 │ ├── closing_loc: ∅ │ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/defn_endless_command_rescue.txt b/test/prism/snapshots/seattlerb/defn_endless_command_rescue.txt index dfd1d01ba828a1..7efd02cd062e27 100644 --- a/test/prism/snapshots/seattlerb/defn_endless_command_rescue.txt +++ b/test/prism/snapshots/seattlerb/defn_endless_command_rescue.txt @@ -1,17 +1,22 @@ @ ProgramNode (location: (1,0)-(1,43)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,43)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,43)) + ├── flags: newline ├── name: :some_method ├── name_loc: (1,4)-(1,15) = "some_method" ├── receiver: ∅ ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (1,18)-(1,43)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ RescueModifierNode (location: (1,18)-(1,43)) + │ ├── flags: newline │ ├── expression: │ │ @ CallNode (location: (1,18)-(1,33)) │ │ ├── flags: ignore_visibility @@ -25,14 +30,14 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (1,31)-(1,33)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 42 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ ├── keyword_loc: (1,34)-(1,40) = "rescue" │ └── rescue_expression: │ @ IntegerNode (location: (1,41)-(1,43)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 24 ├── locals: [] ├── def_keyword_loc: (1,0)-(1,3) = "def" diff --git a/test/prism/snapshots/seattlerb/defn_forward_args.txt b/test/prism/snapshots/seattlerb/defn_forward_args.txt index 71a984c81159a5..50fc32c0473c43 100644 --- a/test/prism/snapshots/seattlerb/defn_forward_args.txt +++ b/test/prism/snapshots/seattlerb/defn_forward_args.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,23)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,23)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,23)) + ├── flags: newline ├── name: :a ├── name_loc: (1,4)-(1,5) = "a" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,6)-(1,9)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ @@ -16,12 +20,14 @@ │ ├── keywords: (length: 0) │ ├── keyword_rest: │ │ @ ForwardingParameterNode (location: (1,6)-(1,9)) + │ │ └── flags: ∅ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (1,12)-(1,18)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,12)-(1,18)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :b @@ -32,6 +38,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ ForwardingArgumentsNode (location: (1,14)-(1,17)) + │ │ └── flags: ∅ │ ├── closing_loc: (1,17)-(1,18) = ")" │ └── block: ∅ ├── locals: [] diff --git a/test/prism/snapshots/seattlerb/defn_forward_args__no_parens.txt b/test/prism/snapshots/seattlerb/defn_forward_args__no_parens.txt index 4a4d69e4d039be..8926f2cdf18d4b 100644 --- a/test/prism/snapshots/seattlerb/defn_forward_args__no_parens.txt +++ b/test/prism/snapshots/seattlerb/defn_forward_args__no_parens.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(3,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,3)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(3,3)) + ├── flags: newline ├── name: :f ├── name_loc: (1,4)-(1,5) = "f" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,6)-(1,9)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ @@ -16,12 +20,14 @@ │ ├── keywords: (length: 0) │ ├── keyword_rest: │ │ @ ForwardingParameterNode (location: (1,6)-(1,9)) + │ │ └── flags: ∅ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (2,2)-(2,8)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (2,2)-(2,8)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :m @@ -32,6 +38,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ ForwardingArgumentsNode (location: (2,4)-(2,7)) + │ │ └── flags: ∅ │ ├── closing_loc: (2,7)-(2,8) = ")" │ └── block: ∅ ├── locals: [] diff --git a/test/prism/snapshots/seattlerb/defn_kwarg_env.txt b/test/prism/snapshots/seattlerb/defn_kwarg_env.txt index 2aadedd9646eda..0df82b5dc2be21 100644 --- a/test/prism/snapshots/seattlerb/defn_kwarg_env.txt +++ b/test/prism/snapshots/seattlerb/defn_kwarg_env.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,45)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,45)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,45)) + ├── flags: newline ├── name: :test ├── name_loc: (1,4)-(1,8) = "test" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,9)-(1,18)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ @@ -23,9 +27,10 @@ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (1,20)-(1,41)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,20)-(1,41)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :test_splat @@ -39,8 +44,10 @@ │ │ ├── flags: ∅ │ │ └── elements: (length: 1) │ │ └── @ AssocSplatNode (location: (1,31)-(1,40)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ LocalVariableReadNode (location: (1,33)-(1,40)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :testing │ │ │ └── depth: 0 │ │ └── operator_loc: (1,31)-(1,33) = "**" diff --git a/test/prism/snapshots/seattlerb/defn_kwarg_kwarg.txt b/test/prism/snapshots/seattlerb/defn_kwarg_kwarg.txt index 8a5022ff37d45c..ead1437a86fae8 100644 --- a/test/prism/snapshots/seattlerb/defn_kwarg_kwarg.txt +++ b/test/prism/snapshots/seattlerb/defn_kwarg_kwarg.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,24)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,24)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,24)) + ├── flags: newline ├── name: :f ├── name_loc: (1,4)-(1,5) = "f" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,6)-(1,19)) + │ ├── flags: ∅ │ ├── requireds: (length: 1) │ │ └── @ RequiredParameterNode (location: (1,6)-(1,7)) │ │ ├── flags: ∅ @@ -23,7 +27,7 @@ │ │ │ ├── name_loc: (1,9)-(1,11) = "b:" │ │ │ └── value: │ │ │ @ IntegerNode (location: (1,12)-(1,13)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ OptionalKeywordParameterNode (location: (1,15)-(1,19)) │ │ ├── flags: ∅ @@ -31,7 +35,7 @@ │ │ ├── name_loc: (1,15)-(1,17) = "c:" │ │ └── value: │ │ @ IntegerNode (location: (1,18)-(1,19)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── keyword_rest: ∅ │ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/defn_kwarg_kwsplat.txt b/test/prism/snapshots/seattlerb/defn_kwarg_kwsplat.txt index 4c980bc27f8055..2b228334604e2d 100644 --- a/test/prism/snapshots/seattlerb/defn_kwarg_kwsplat.txt +++ b/test/prism/snapshots/seattlerb/defn_kwarg_kwsplat.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,20)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,20)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,20)) + ├── flags: newline ├── name: :a ├── name_loc: (1,4)-(1,5) = "a" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,6)-(1,15)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ @@ -20,7 +24,7 @@ │ │ ├── name_loc: (1,6)-(1,8) = "b:" │ │ └── value: │ │ @ IntegerNode (location: (1,9)-(1,10)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── keyword_rest: │ │ @ KeywordRestParameterNode (location: (1,12)-(1,15)) diff --git a/test/prism/snapshots/seattlerb/defn_kwarg_kwsplat_anon.txt b/test/prism/snapshots/seattlerb/defn_kwarg_kwsplat_anon.txt index 40afacc2a7d1b9..1e0e1f132e7416 100644 --- a/test/prism/snapshots/seattlerb/defn_kwarg_kwsplat_anon.txt +++ b/test/prism/snapshots/seattlerb/defn_kwarg_kwsplat_anon.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,19)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,19)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,19)) + ├── flags: newline ├── name: :a ├── name_loc: (1,4)-(1,5) = "a" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,6)-(1,14)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ @@ -20,7 +24,7 @@ │ │ ├── name_loc: (1,6)-(1,8) = "b:" │ │ └── value: │ │ @ IntegerNode (location: (1,9)-(1,10)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── keyword_rest: │ │ @ KeywordRestParameterNode (location: (1,12)-(1,14)) diff --git a/test/prism/snapshots/seattlerb/defn_kwarg_lvar.txt b/test/prism/snapshots/seattlerb/defn_kwarg_lvar.txt index 0eae56924c852f..0835d3987db21c 100644 --- a/test/prism/snapshots/seattlerb/defn_kwarg_lvar.txt +++ b/test/prism/snapshots/seattlerb/defn_kwarg_lvar.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,26)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,26)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,26)) + ├── flags: newline ├── name: :fun ├── name_loc: (1,4)-(1,7) = "fun" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,8)-(1,16)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ @@ -20,7 +24,7 @@ │ │ ├── name_loc: (1,8)-(1,11) = "kw:" │ │ └── value: │ │ @ SymbolNode (location: (1,12)-(1,16)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,12)-(1,13) = ":" │ │ ├── value_loc: (1,13)-(1,16) = "val" │ │ ├── closing_loc: ∅ @@ -29,8 +33,10 @@ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (1,19)-(1,21)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ LocalVariableReadNode (location: (1,19)-(1,21)) + │ ├── flags: newline │ ├── name: :kw │ └── depth: 0 ├── locals: [:kw] diff --git a/test/prism/snapshots/seattlerb/defn_kwarg_no_parens.txt b/test/prism/snapshots/seattlerb/defn_kwarg_no_parens.txt index bc5747ad02dc07..3f6af5e0ee49a9 100644 --- a/test/prism/snapshots/seattlerb/defn_kwarg_no_parens.txt +++ b/test/prism/snapshots/seattlerb/defn_kwarg_no_parens.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(2,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(2,3)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(2,3)) + ├── flags: newline ├── name: :f ├── name_loc: (1,4)-(1,5) = "f" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,6)-(1,10)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ @@ -20,7 +24,7 @@ │ │ ├── name_loc: (1,6)-(1,8) = "a:" │ │ └── value: │ │ @ IntegerNode (location: (1,9)-(1,10)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── keyword_rest: ∅ │ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/defn_kwarg_val.txt b/test/prism/snapshots/seattlerb/defn_kwarg_val.txt index 82527f7875fa18..38cb995c093258 100644 --- a/test/prism/snapshots/seattlerb/defn_kwarg_val.txt +++ b/test/prism/snapshots/seattlerb/defn_kwarg_val.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,17)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,17)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,17)) + ├── flags: newline ├── name: :f ├── name_loc: (1,4)-(1,5) = "f" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,6)-(1,12)) + │ ├── flags: ∅ │ ├── requireds: (length: 1) │ │ └── @ RequiredParameterNode (location: (1,6)-(1,7)) │ │ ├── flags: ∅ @@ -23,7 +27,7 @@ │ │ ├── name_loc: (1,9)-(1,11) = "b:" │ │ └── value: │ │ @ IntegerNode (location: (1,11)-(1,12)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── keyword_rest: ∅ │ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/defn_no_kwargs.txt b/test/prism/snapshots/seattlerb/defn_no_kwargs.txt index 0ef0634a537d57..778b300a4be2e1 100644 --- a/test/prism/snapshots/seattlerb/defn_no_kwargs.txt +++ b/test/prism/snapshots/seattlerb/defn_no_kwargs.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,17)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,17)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,17)) + ├── flags: newline ├── name: :x ├── name_loc: (1,4)-(1,5) = "x" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,6)-(1,11)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ @@ -16,6 +20,7 @@ │ ├── keywords: (length: 0) │ ├── keyword_rest: │ │ @ NoKeywordsParameterNode (location: (1,6)-(1,11)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (1,6)-(1,8) = "**" │ │ └── keyword_loc: (1,8)-(1,11) = "nil" │ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/defn_oneliner.txt b/test/prism/snapshots/seattlerb/defn_oneliner.txt index e7004998092e7c..efa53f09d58f6b 100644 --- a/test/prism/snapshots/seattlerb/defn_oneliner.txt +++ b/test/prism/snapshots/seattlerb/defn_oneliner.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,27)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,27)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,27)) + ├── flags: newline ├── name: :exec ├── name_loc: (1,4)-(1,8) = "exec" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,9)-(1,12)) + │ ├── flags: ∅ │ ├── requireds: (length: 1) │ │ └── @ RequiredParameterNode (location: (1,9)-(1,12)) │ │ ├── flags: ∅ @@ -21,9 +25,10 @@ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (1,16)-(1,27)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,16)-(1,27)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :system @@ -34,6 +39,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ LocalVariableReadNode (location: (1,23)-(1,26)) + │ │ ├── flags: ∅ │ │ ├── name: :cmd │ │ └── depth: 0 │ ├── closing_loc: (1,26)-(1,27) = ")" diff --git a/test/prism/snapshots/seattlerb/defn_oneliner_eq2.txt b/test/prism/snapshots/seattlerb/defn_oneliner_eq2.txt index 2708351ede4256..8d9f6a2267c285 100644 --- a/test/prism/snapshots/seattlerb/defn_oneliner_eq2.txt +++ b/test/prism/snapshots/seattlerb/defn_oneliner_eq2.txt @@ -1,25 +1,32 @@ @ ProgramNode (location: (1,0)-(3,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,3)) + ├── flags: ∅ └── body: (length: 1) └── @ ClassNode (location: (1,0)-(3,3)) + ├── flags: newline ├── locals: [] ├── class_keyword_loc: (1,0)-(1,5) = "class" ├── constant_path: │ @ ConstantReadNode (location: (1,6)-(1,7)) + │ ├── flags: ∅ │ └── name: :X ├── inheritance_operator_loc: ∅ ├── superclass: ∅ ├── body: │ @ StatementsNode (location: (2,2)-(2,16)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ DefNode (location: (2,2)-(2,16)) + │ ├── flags: newline │ ├── name: :== │ ├── name_loc: (2,6)-(2,8) = "==" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (2,9)-(2,10)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (2,9)-(2,10)) │ │ │ ├── flags: ∅ @@ -32,9 +39,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (2,14)-(2,16)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (2,14)-(2,16)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 42 │ ├── locals: [:o] │ ├── def_keyword_loc: (2,2)-(2,5) = "def" diff --git a/test/prism/snapshots/seattlerb/defn_oneliner_noargs.txt b/test/prism/snapshots/seattlerb/defn_oneliner_noargs.txt index 54555b1a2385e4..a7bc11ae6b4662 100644 --- a/test/prism/snapshots/seattlerb/defn_oneliner_noargs.txt +++ b/test/prism/snapshots/seattlerb/defn_oneliner_noargs.txt @@ -1,18 +1,22 @@ @ ProgramNode (location: (1,0)-(1,17)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,17)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,17)) + ├── flags: newline ├── name: :exec ├── name_loc: (1,4)-(1,8) = "exec" ├── receiver: ∅ ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (1,11)-(1,17)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,11)-(1,17)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :system diff --git a/test/prism/snapshots/seattlerb/defn_oneliner_noargs_parentheses.txt b/test/prism/snapshots/seattlerb/defn_oneliner_noargs_parentheses.txt index e0fc4636f18996..174c49d1e1198c 100644 --- a/test/prism/snapshots/seattlerb/defn_oneliner_noargs_parentheses.txt +++ b/test/prism/snapshots/seattlerb/defn_oneliner_noargs_parentheses.txt @@ -1,18 +1,22 @@ @ ProgramNode (location: (1,0)-(1,19)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,19)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,19)) + ├── flags: newline ├── name: :exec ├── name_loc: (1,4)-(1,8) = "exec" ├── receiver: ∅ ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (1,13)-(1,19)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,13)-(1,19)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :system diff --git a/test/prism/snapshots/seattlerb/defn_oneliner_rescue.txt b/test/prism/snapshots/seattlerb/defn_oneliner_rescue.txt index b5b5dbe6ac6a07..5e9c7ccef77843 100644 --- a/test/prism/snapshots/seattlerb/defn_oneliner_rescue.txt +++ b/test/prism/snapshots/seattlerb/defn_oneliner_rescue.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(13,38)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(13,38)) + ├── flags: ∅ └── body: (length: 3) ├── @ DefNode (location: (1,0)-(5,3)) + │ ├── flags: newline │ ├── name: :exec │ ├── name_loc: (1,4)-(1,8) = "exec" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,9)-(1,12)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,12)) │ │ │ ├── flags: ∅ @@ -21,12 +25,14 @@ │ │ └── block: ∅ │ ├── body: │ │ @ BeginNode (location: (1,0)-(5,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (2,2)-(2,13)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (2,2)-(2,13)) - │ │ │ ├── flags: ignore_visibility + │ │ │ ├── flags: newline, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :system @@ -37,20 +43,24 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ LocalVariableReadNode (location: (2,9)-(2,12)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :cmd │ │ │ │ └── depth: 0 │ │ │ ├── closing_loc: (2,12)-(2,13) = ")" │ │ │ └── block: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (3,0)-(4,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (3,0)-(3,6) = "rescue" │ │ │ ├── exceptions: (length: 0) │ │ │ ├── operator_loc: ∅ │ │ │ ├── reference: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (4,2)-(4,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ NilNode (location: (4,2)-(4,5)) + │ │ │ │ └── flags: newline, static_literal │ │ │ └── consequent: ∅ │ │ ├── else_clause: ∅ │ │ ├── ensure_clause: ∅ @@ -63,11 +73,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (5,0)-(5,3) = "end" ├── @ DefNode (location: (8,0)-(10,3)) + │ ├── flags: newline │ ├── name: :exec │ ├── name_loc: (8,4)-(8,8) = "exec" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (8,9)-(8,12)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (8,9)-(8,12)) │ │ │ ├── flags: ∅ @@ -80,8 +92,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (9,2)-(9,24)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ RescueModifierNode (location: (9,2)-(9,24)) + │ │ ├── flags: newline │ │ ├── expression: │ │ │ @ CallNode (location: (9,2)-(9,13)) │ │ │ ├── flags: ignore_visibility @@ -95,6 +109,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ LocalVariableReadNode (location: (9,9)-(9,12)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :cmd │ │ │ │ └── depth: 0 │ │ │ ├── closing_loc: (9,12)-(9,13) = ")" @@ -102,6 +117,7 @@ │ │ ├── keyword_loc: (9,14)-(9,20) = "rescue" │ │ └── rescue_expression: │ │ @ NilNode (location: (9,21)-(9,24)) + │ │ └── flags: static_literal │ ├── locals: [:cmd] │ ├── def_keyword_loc: (8,0)-(8,3) = "def" │ ├── operator_loc: ∅ @@ -110,11 +126,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (10,0)-(10,3) = "end" └── @ DefNode (location: (13,0)-(13,38)) + ├── flags: newline ├── name: :exec ├── name_loc: (13,4)-(13,8) = "exec" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (13,9)-(13,12)) + │ ├── flags: ∅ │ ├── requireds: (length: 1) │ │ └── @ RequiredParameterNode (location: (13,9)-(13,12)) │ │ ├── flags: ∅ @@ -127,8 +145,10 @@ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (13,16)-(13,38)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ RescueModifierNode (location: (13,16)-(13,38)) + │ ├── flags: newline │ ├── expression: │ │ @ CallNode (location: (13,16)-(13,27)) │ │ ├── flags: ignore_visibility @@ -142,6 +162,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ LocalVariableReadNode (location: (13,23)-(13,26)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :cmd │ │ │ └── depth: 0 │ │ ├── closing_loc: (13,26)-(13,27) = ")" @@ -149,6 +170,7 @@ │ ├── keyword_loc: (13,28)-(13,34) = "rescue" │ └── rescue_expression: │ @ NilNode (location: (13,35)-(13,38)) + │ └── flags: static_literal ├── locals: [:cmd] ├── def_keyword_loc: (13,0)-(13,3) = "def" ├── operator_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/defn_opt_last_arg.txt b/test/prism/snapshots/seattlerb/defn_opt_last_arg.txt index 569bc23078376c..f7c37e3d457b35 100644 --- a/test/prism/snapshots/seattlerb/defn_opt_last_arg.txt +++ b/test/prism/snapshots/seattlerb/defn_opt_last_arg.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(2,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(2,3)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(2,3)) + ├── flags: newline ├── name: :m ├── name_loc: (1,4)-(1,5) = "m" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,6)-(1,17)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 1) │ │ └── @ OptionalParameterNode (location: (1,6)-(1,17)) @@ -18,6 +22,7 @@ │ │ ├── operator_loc: (1,10)-(1,11) = "=" │ │ └── value: │ │ @ FalseNode (location: (1,12)-(1,17)) + │ │ └── flags: static_literal │ ├── rest: ∅ │ ├── posts: (length: 0) │ ├── keywords: (length: 0) diff --git a/test/prism/snapshots/seattlerb/defn_opt_reg.txt b/test/prism/snapshots/seattlerb/defn_opt_reg.txt index f86168513ac619..9bdd14390504ee 100644 --- a/test/prism/snapshots/seattlerb/defn_opt_reg.txt +++ b/test/prism/snapshots/seattlerb/defn_opt_reg.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,19)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,19)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,19)) + ├── flags: newline ├── name: :f ├── name_loc: (1,4)-(1,5) = "f" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,6)-(1,14)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 1) │ │ └── @ OptionalParameterNode (location: (1,6)-(1,11)) @@ -18,6 +22,7 @@ │ │ ├── operator_loc: (1,7)-(1,8) = "=" │ │ └── value: │ │ @ NilNode (location: (1,8)-(1,11)) + │ │ └── flags: static_literal │ ├── rest: ∅ │ ├── posts: (length: 1) │ │ └── @ RequiredParameterNode (location: (1,13)-(1,14)) diff --git a/test/prism/snapshots/seattlerb/defn_opt_splat_arg.txt b/test/prism/snapshots/seattlerb/defn_opt_splat_arg.txt index 3019e9b73e371c..d90fddc6533ace 100644 --- a/test/prism/snapshots/seattlerb/defn_opt_splat_arg.txt +++ b/test/prism/snapshots/seattlerb/defn_opt_splat_arg.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,24)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,24)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,24)) + ├── flags: newline ├── name: :f ├── name_loc: (1,4)-(1,5) = "f" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,7)-(1,19)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 1) │ │ └── @ OptionalParameterNode (location: (1,7)-(1,12)) @@ -18,7 +22,7 @@ │ │ ├── operator_loc: (1,9)-(1,10) = "=" │ │ └── value: │ │ @ IntegerNode (location: (1,11)-(1,12)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── rest: │ │ @ RestParameterNode (location: (1,14)-(1,16)) diff --git a/test/prism/snapshots/seattlerb/defn_powarg.txt b/test/prism/snapshots/seattlerb/defn_powarg.txt index bce131ad183df0..8d4ff4283ac141 100644 --- a/test/prism/snapshots/seattlerb/defn_powarg.txt +++ b/test/prism/snapshots/seattlerb/defn_powarg.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,17)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,17)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,17)) + ├── flags: newline ├── name: :f ├── name_loc: (1,4)-(1,5) = "f" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,6)-(1,12)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ diff --git a/test/prism/snapshots/seattlerb/defn_reg_opt_reg.txt b/test/prism/snapshots/seattlerb/defn_reg_opt_reg.txt index d079e1b5f39d67..0cd1435e8f992b 100644 --- a/test/prism/snapshots/seattlerb/defn_reg_opt_reg.txt +++ b/test/prism/snapshots/seattlerb/defn_reg_opt_reg.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,23)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,23)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,23)) + ├── flags: newline ├── name: :f ├── name_loc: (1,4)-(1,5) = "f" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,6)-(1,18)) + │ ├── flags: ∅ │ ├── requireds: (length: 1) │ │ └── @ RequiredParameterNode (location: (1,6)-(1,7)) │ │ ├── flags: ∅ @@ -21,7 +25,7 @@ │ │ ├── operator_loc: (1,11)-(1,12) = "=" │ │ └── value: │ │ @ SymbolNode (location: (1,13)-(1,15)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,13)-(1,14) = ":" │ │ ├── value_loc: (1,14)-(1,15) = "c" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/defn_splat_arg.txt b/test/prism/snapshots/seattlerb/defn_splat_arg.txt index 109fac495abaa0..c137e263c4433c 100644 --- a/test/prism/snapshots/seattlerb/defn_splat_arg.txt +++ b/test/prism/snapshots/seattlerb/defn_splat_arg.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,15)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,15)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,15)) + ├── flags: newline ├── name: :f ├── name_loc: (1,4)-(1,5) = "f" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,6)-(1,10)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: diff --git a/test/prism/snapshots/seattlerb/defn_unary_not.txt b/test/prism/snapshots/seattlerb/defn_unary_not.txt index 231a3c0da9e99c..0722d3cdca18a2 100644 --- a/test/prism/snapshots/seattlerb/defn_unary_not.txt +++ b/test/prism/snapshots/seattlerb/defn_unary_not.txt @@ -1,17 +1,22 @@ @ ProgramNode (location: (1,0)-(1,17)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,17)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,17)) + ├── flags: newline ├── name: :! ├── name_loc: (1,4)-(1,6) = "!@" ├── receiver: ∅ ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (1,8)-(1,12)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ TrueNode (location: (1,8)-(1,12)) + │ └── flags: newline, static_literal ├── locals: [] ├── def_keyword_loc: (1,0)-(1,3) = "def" ├── operator_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/defns_reserved.txt b/test/prism/snapshots/seattlerb/defns_reserved.txt index 96860b49ce04c0..10361184ac86a7 100644 --- a/test/prism/snapshots/seattlerb/defns_reserved.txt +++ b/test/prism/snapshots/seattlerb/defns_reserved.txt @@ -1,13 +1,17 @@ @ ProgramNode (location: (1,0)-(1,20)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,20)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,20)) + ├── flags: newline ├── name: :return ├── name_loc: (1,9)-(1,15) = "return" ├── receiver: │ @ SelfNode (location: (1,4)-(1,8)) + │ └── flags: ∅ ├── parameters: ∅ ├── body: ∅ ├── locals: [] diff --git a/test/prism/snapshots/seattlerb/defs_as_arg_with_do_block_inside.txt b/test/prism/snapshots/seattlerb/defs_as_arg_with_do_block_inside.txt index 24bb14f8e1443b..f9479eb11bddca 100644 --- a/test/prism/snapshots/seattlerb/defs_as_arg_with_do_block_inside.txt +++ b/test/prism/snapshots/seattlerb/defs_as_arg_with_do_block_inside.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,30)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,30)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,30)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :p @@ -15,16 +17,19 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ DefNode (location: (1,2)-(1,30)) + │ ├── flags: ∅ │ ├── name: :b │ ├── name_loc: (1,11)-(1,12) = "b" │ ├── receiver: │ │ @ SelfNode (location: (1,6)-(1,10)) + │ │ └── flags: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (1,14)-(1,25)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (1,14)-(1,25)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ CallNode (location: (1,14)-(1,15)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -44,6 +49,7 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (1,18)-(1,25)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── parameters: ∅ │ │ ├── body: ∅ diff --git a/test/prism/snapshots/seattlerb/defs_comments.txt b/test/prism/snapshots/seattlerb/defs_comments.txt index a2976e7ee28219..7f94497978b32a 100644 --- a/test/prism/snapshots/seattlerb/defs_comments.txt +++ b/test/prism/snapshots/seattlerb/defs_comments.txt @@ -1,13 +1,17 @@ @ ProgramNode (location: (4,0)-(5,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (4,0)-(5,3)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (4,0)-(5,3)) + ├── flags: newline ├── name: :blah ├── name_loc: (4,9)-(4,13) = "blah" ├── receiver: │ @ SelfNode (location: (4,4)-(4,8)) + │ └── flags: ∅ ├── parameters: ∅ ├── body: ∅ ├── locals: [] diff --git a/test/prism/snapshots/seattlerb/defs_endless_command.txt b/test/prism/snapshots/seattlerb/defs_endless_command.txt index f3c4e7941788f9..b1b542296c8cf0 100644 --- a/test/prism/snapshots/seattlerb/defs_endless_command.txt +++ b/test/prism/snapshots/seattlerb/defs_endless_command.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(1,35)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,35)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,35)) + ├── flags: newline ├── name: :some_method ├── name_loc: (1,6)-(1,17) = "some_method" ├── receiver: @@ -20,9 +23,10 @@ ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (1,20)-(1,35)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,20)-(1,35)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :other_method @@ -33,7 +37,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (1,33)-(1,35)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 42 │ ├── closing_loc: ∅ │ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/defs_endless_command_rescue.txt b/test/prism/snapshots/seattlerb/defs_endless_command_rescue.txt index b0cd34a9c831f6..e8a7703de3d217 100644 --- a/test/prism/snapshots/seattlerb/defs_endless_command_rescue.txt +++ b/test/prism/snapshots/seattlerb/defs_endless_command_rescue.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(1,45)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,45)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,45)) + ├── flags: newline ├── name: :some_method ├── name_loc: (1,6)-(1,17) = "some_method" ├── receiver: @@ -20,8 +23,10 @@ ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (1,20)-(1,45)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ RescueModifierNode (location: (1,20)-(1,45)) + │ ├── flags: newline │ ├── expression: │ │ @ CallNode (location: (1,20)-(1,35)) │ │ ├── flags: ignore_visibility @@ -35,14 +40,14 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (1,33)-(1,35)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 42 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ ├── keyword_loc: (1,36)-(1,42) = "rescue" │ └── rescue_expression: │ @ IntegerNode (location: (1,43)-(1,45)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 24 ├── locals: [] ├── def_keyword_loc: (1,0)-(1,3) = "def" diff --git a/test/prism/snapshots/seattlerb/defs_kwarg.txt b/test/prism/snapshots/seattlerb/defs_kwarg.txt index 53235c9bb80f86..86a2d562e3aafa 100644 --- a/test/prism/snapshots/seattlerb/defs_kwarg.txt +++ b/test/prism/snapshots/seattlerb/defs_kwarg.txt @@ -1,15 +1,20 @@ @ ProgramNode (location: (1,0)-(2,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(2,3)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(2,3)) + ├── flags: newline ├── name: :a ├── name_loc: (1,9)-(1,10) = "a" ├── receiver: │ @ SelfNode (location: (1,4)-(1,8)) + │ └── flags: ∅ ├── parameters: │ @ ParametersNode (location: (1,11)-(1,15)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ @@ -21,7 +26,7 @@ │ │ ├── name_loc: (1,11)-(1,13) = "b:" │ │ └── value: │ │ @ IntegerNode (location: (1,14)-(1,15)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── keyword_rest: ∅ │ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/defs_oneliner.txt b/test/prism/snapshots/seattlerb/defs_oneliner.txt index d32975354d9207..4ddde4309d74b3 100644 --- a/test/prism/snapshots/seattlerb/defs_oneliner.txt +++ b/test/prism/snapshots/seattlerb/defs_oneliner.txt @@ -1,15 +1,20 @@ @ ProgramNode (location: (1,0)-(1,32)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,32)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,32)) + ├── flags: newline ├── name: :exec ├── name_loc: (1,9)-(1,13) = "exec" ├── receiver: │ @ SelfNode (location: (1,4)-(1,8)) + │ └── flags: ∅ ├── parameters: │ @ ParametersNode (location: (1,14)-(1,17)) + │ ├── flags: ∅ │ ├── requireds: (length: 1) │ │ └── @ RequiredParameterNode (location: (1,14)-(1,17)) │ │ ├── flags: ∅ @@ -22,9 +27,10 @@ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (1,21)-(1,32)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,21)-(1,32)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :system @@ -35,6 +41,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ LocalVariableReadNode (location: (1,28)-(1,31)) + │ │ ├── flags: ∅ │ │ ├── name: :cmd │ │ └── depth: 0 │ ├── closing_loc: (1,31)-(1,32) = ")" diff --git a/test/prism/snapshots/seattlerb/defs_oneliner_eq2.txt b/test/prism/snapshots/seattlerb/defs_oneliner_eq2.txt index fcc5c63cf06323..f51a58e37e6727 100644 --- a/test/prism/snapshots/seattlerb/defs_oneliner_eq2.txt +++ b/test/prism/snapshots/seattlerb/defs_oneliner_eq2.txt @@ -1,26 +1,34 @@ @ ProgramNode (location: (1,0)-(3,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,3)) + ├── flags: ∅ └── body: (length: 1) └── @ ClassNode (location: (1,0)-(3,3)) + ├── flags: newline ├── locals: [] ├── class_keyword_loc: (1,0)-(1,5) = "class" ├── constant_path: │ @ ConstantReadNode (location: (1,6)-(1,7)) + │ ├── flags: ∅ │ └── name: :X ├── inheritance_operator_loc: ∅ ├── superclass: ∅ ├── body: │ @ StatementsNode (location: (2,2)-(2,21)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ DefNode (location: (2,2)-(2,21)) + │ ├── flags: newline │ ├── name: :== │ ├── name_loc: (2,11)-(2,13) = "==" │ ├── receiver: │ │ @ SelfNode (location: (2,6)-(2,10)) + │ │ └── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (2,14)-(2,15)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (2,14)-(2,15)) │ │ │ ├── flags: ∅ @@ -33,9 +41,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (2,19)-(2,21)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (2,19)-(2,21)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 42 │ ├── locals: [:o] │ ├── def_keyword_loc: (2,2)-(2,5) = "def" diff --git a/test/prism/snapshots/seattlerb/defs_oneliner_rescue.txt b/test/prism/snapshots/seattlerb/defs_oneliner_rescue.txt index f7762107682b3a..13b91598944d74 100644 --- a/test/prism/snapshots/seattlerb/defs_oneliner_rescue.txt +++ b/test/prism/snapshots/seattlerb/defs_oneliner_rescue.txt @@ -1,15 +1,20 @@ @ ProgramNode (location: (1,0)-(13,43)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(13,43)) + ├── flags: ∅ └── body: (length: 3) ├── @ DefNode (location: (1,0)-(5,3)) + │ ├── flags: newline │ ├── name: :exec │ ├── name_loc: (1,9)-(1,13) = "exec" │ ├── receiver: │ │ @ SelfNode (location: (1,4)-(1,8)) + │ │ └── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,14)-(1,17)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (1,14)-(1,17)) │ │ │ ├── flags: ∅ @@ -22,12 +27,14 @@ │ │ └── block: ∅ │ ├── body: │ │ @ BeginNode (location: (1,0)-(5,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (2,2)-(2,13)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (2,2)-(2,13)) - │ │ │ ├── flags: ignore_visibility + │ │ │ ├── flags: newline, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :system @@ -38,20 +45,24 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ LocalVariableReadNode (location: (2,9)-(2,12)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :cmd │ │ │ │ └── depth: 0 │ │ │ ├── closing_loc: (2,12)-(2,13) = ")" │ │ │ └── block: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (3,0)-(4,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (3,0)-(3,6) = "rescue" │ │ │ ├── exceptions: (length: 0) │ │ │ ├── operator_loc: ∅ │ │ │ ├── reference: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (4,2)-(4,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ NilNode (location: (4,2)-(4,5)) + │ │ │ │ └── flags: newline, static_literal │ │ │ └── consequent: ∅ │ │ ├── else_clause: ∅ │ │ ├── ensure_clause: ∅ @@ -64,12 +75,15 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (5,0)-(5,3) = "end" ├── @ DefNode (location: (8,0)-(10,3)) + │ ├── flags: newline │ ├── name: :exec │ ├── name_loc: (8,9)-(8,13) = "exec" │ ├── receiver: │ │ @ SelfNode (location: (8,4)-(8,8)) + │ │ └── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (8,14)-(8,17)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (8,14)-(8,17)) │ │ │ ├── flags: ∅ @@ -82,8 +96,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (9,2)-(9,24)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ RescueModifierNode (location: (9,2)-(9,24)) + │ │ ├── flags: newline │ │ ├── expression: │ │ │ @ CallNode (location: (9,2)-(9,13)) │ │ │ ├── flags: ignore_visibility @@ -97,6 +113,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ LocalVariableReadNode (location: (9,9)-(9,12)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :cmd │ │ │ │ └── depth: 0 │ │ │ ├── closing_loc: (9,12)-(9,13) = ")" @@ -104,6 +121,7 @@ │ │ ├── keyword_loc: (9,14)-(9,20) = "rescue" │ │ └── rescue_expression: │ │ @ NilNode (location: (9,21)-(9,24)) + │ │ └── flags: static_literal │ ├── locals: [:cmd] │ ├── def_keyword_loc: (8,0)-(8,3) = "def" │ ├── operator_loc: (8,8)-(8,9) = "." @@ -112,12 +130,15 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (10,0)-(10,3) = "end" └── @ DefNode (location: (13,0)-(13,43)) + ├── flags: newline ├── name: :exec ├── name_loc: (13,9)-(13,13) = "exec" ├── receiver: │ @ SelfNode (location: (13,4)-(13,8)) + │ └── flags: ∅ ├── parameters: │ @ ParametersNode (location: (13,14)-(13,17)) + │ ├── flags: ∅ │ ├── requireds: (length: 1) │ │ └── @ RequiredParameterNode (location: (13,14)-(13,17)) │ │ ├── flags: ∅ @@ -130,8 +151,10 @@ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (13,21)-(13,43)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ RescueModifierNode (location: (13,21)-(13,43)) + │ ├── flags: newline │ ├── expression: │ │ @ CallNode (location: (13,21)-(13,32)) │ │ ├── flags: ignore_visibility @@ -145,6 +168,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ LocalVariableReadNode (location: (13,28)-(13,31)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :cmd │ │ │ └── depth: 0 │ │ ├── closing_loc: (13,31)-(13,32) = ")" @@ -152,6 +176,7 @@ │ ├── keyword_loc: (13,33)-(13,39) = "rescue" │ └── rescue_expression: │ @ NilNode (location: (13,40)-(13,43)) + │ └── flags: static_literal ├── locals: [:cmd] ├── def_keyword_loc: (13,0)-(13,3) = "def" ├── operator_loc: (13,8)-(13,9) = "." diff --git a/test/prism/snapshots/seattlerb/difficult0_.txt b/test/prism/snapshots/seattlerb/difficult0_.txt index 8ba30ccf85d247..233440b101920c 100644 --- a/test/prism/snapshots/seattlerb/difficult0_.txt +++ b/test/prism/snapshots/seattlerb/difficult0_.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(4,8)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(4,8)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(4,8)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :p @@ -35,17 +37,17 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ InterpolatedStringNode (location: (1,9)-(4,4)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: static_literal │ │ │ ├── opening_loc: (1,9)-(1,10) = "'" │ │ │ ├── parts: (length: 2) │ │ │ │ ├── @ StringNode (location: (1,10)-(2,0)) - │ │ │ │ │ ├── flags: frozen + │ │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── content_loc: (1,10)-(2,0) = "b\n" │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── unescaped: "b\n" │ │ │ │ └── @ StringNode (location: (4,0)-(4,3)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── content_loc: (4,0)-(4,3) = " c" │ │ │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/difficult1_line_numbers.txt b/test/prism/snapshots/seattlerb/difficult1_line_numbers.txt index da2306312c7fc2..daf2f413bf1bda 100644 --- a/test/prism/snapshots/seattlerb/difficult1_line_numbers.txt +++ b/test/prism/snapshots/seattlerb/difficult1_line_numbers.txt @@ -1,18 +1,23 @@ @ ProgramNode (location: (1,0)-(12,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(12,3)) + ├── flags: ∅ └── body: (length: 1) └── @ IfNode (location: (1,0)-(12,3)) + ├── flags: newline ├── if_keyword_loc: (1,0)-(1,2) = "if" ├── predicate: │ @ TrueNode (location: (1,3)-(1,7)) + │ └── flags: static_literal ├── then_keyword_loc: ∅ ├── statements: │ @ StatementsNode (location: (2,2)-(11,11)) + │ ├── flags: ∅ │ └── body: (length: 10) │ ├── @ CallNode (location: (2,2)-(2,5)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :p @@ -23,12 +28,12 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (2,4)-(2,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ ├── @ CallNode (location: (3,2)-(3,7)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ CallNode (location: (3,2)-(3,3)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -49,12 +54,12 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (3,6)-(3,7)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ ├── @ CallNode (location: (4,2)-(4,10)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ CallNode (location: (4,2)-(4,3)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -75,15 +80,15 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 2) │ │ │ ├── @ IntegerNode (location: (4,6)-(4,7)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 3 │ │ │ └── @ IntegerNode (location: (4,9)-(4,10)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 4 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ ├── @ CallNode (location: (5,2)-(5,7)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ CallNode (location: (5,2)-(5,3)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -104,12 +109,12 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (5,6)-(5,7)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 5 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ ├── @ CallNode (location: (6,2)-(6,10)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ CallNode (location: (6,2)-(6,3)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -130,15 +135,15 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 2) │ │ │ ├── @ IntegerNode (location: (6,6)-(6,7)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 6 │ │ │ └── @ IntegerNode (location: (6,9)-(6,10)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 7 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ ├── @ CallNode (location: (7,2)-(7,6)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :p @@ -149,12 +154,12 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (7,4)-(7,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── closing_loc: (7,5)-(7,6) = ")" │ │ └── block: ∅ │ ├── @ CallNode (location: (8,2)-(8,8)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ CallNode (location: (8,2)-(8,3)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -175,12 +180,12 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (8,6)-(8,7)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── closing_loc: (8,7)-(8,8) = ")" │ │ └── block: ∅ │ ├── @ CallNode (location: (9,2)-(9,11)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ CallNode (location: (9,2)-(9,3)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -201,15 +206,15 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 2) │ │ │ ├── @ IntegerNode (location: (9,6)-(9,7)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 3 │ │ │ └── @ IntegerNode (location: (9,9)-(9,10)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 4 │ │ ├── closing_loc: (9,10)-(9,11) = ")" │ │ └── block: ∅ │ ├── @ CallNode (location: (10,2)-(10,8)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ CallNode (location: (10,2)-(10,3)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -230,12 +235,12 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (10,6)-(10,7)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 5 │ │ ├── closing_loc: (10,7)-(10,8) = ")" │ │ └── block: ∅ │ └── @ CallNode (location: (11,2)-(11,11)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (11,2)-(11,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -256,10 +261,10 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 2) │ │ ├── @ IntegerNode (location: (11,6)-(11,7)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 6 │ │ └── @ IntegerNode (location: (11,9)-(11,10)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 7 │ ├── closing_loc: (11,10)-(11,11) = ")" │ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/difficult1_line_numbers2.txt b/test/prism/snapshots/seattlerb/difficult1_line_numbers2.txt index f586634c59b1f0..480e51c2591413 100644 --- a/test/prism/snapshots/seattlerb/difficult1_line_numbers2.txt +++ b/test/prism/snapshots/seattlerb/difficult1_line_numbers2.txt @@ -1,18 +1,23 @@ @ ProgramNode (location: (1,0)-(7,1)) +├── flags: ∅ ├── locals: [:b, :c] └── statements: @ StatementsNode (location: (1,0)-(7,1)) + ├── flags: ∅ └── body: (length: 2) ├── @ IfNode (location: (1,0)-(6,3)) + │ ├── flags: newline │ ├── if_keyword_loc: (1,0)-(1,2) = "if" │ ├── predicate: │ │ @ TrueNode (location: (1,3)-(1,7)) + │ │ └── flags: static_literal │ ├── then_keyword_loc: (1,8)-(1,12) = "then" │ ├── statements: │ │ @ StatementsNode (location: (2,2)-(5,6)) + │ │ ├── flags: ∅ │ │ └── body: (length: 4) │ │ ├── @ CallNode (location: (2,2)-(2,8)) - │ │ │ ├── flags: ignore_visibility + │ │ │ ├── flags: newline, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :p @@ -31,16 +36,17 @@ │ │ │ ├── closing_loc: (2,7)-(2,8) = ")" │ │ │ └── block: ∅ │ │ ├── @ LocalVariableWriteNode (location: (3,2)-(3,7)) + │ │ │ ├── flags: newline │ │ │ ├── name: :b │ │ │ ├── depth: 0 │ │ │ ├── name_loc: (3,2)-(3,3) = "b" │ │ │ ├── value: │ │ │ │ @ IntegerNode (location: (3,6)-(3,7)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── operator_loc: (3,4)-(3,5) = "=" │ │ ├── @ CallNode (location: (4,2)-(4,5)) - │ │ │ ├── flags: ignore_visibility + │ │ │ ├── flags: newline, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :p @@ -51,23 +57,25 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ LocalVariableReadNode (location: (4,4)-(4,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :b │ │ │ │ └── depth: 0 │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ └── @ LocalVariableWriteNode (location: (5,2)-(5,6)) + │ │ ├── flags: newline │ │ ├── name: :c │ │ ├── depth: 0 │ │ ├── name_loc: (5,2)-(5,3) = "c" │ │ ├── value: │ │ │ @ IntegerNode (location: (5,5)-(5,6)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── operator_loc: (5,4)-(5,5) = "=" │ ├── consequent: ∅ │ └── end_keyword_loc: (6,0)-(6,3) = "end" └── @ CallNode (location: (7,0)-(7,1)) - ├── flags: variable_call, ignore_visibility + ├── flags: newline, variable_call, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a diff --git a/test/prism/snapshots/seattlerb/difficult2_.txt b/test/prism/snapshots/seattlerb/difficult2_.txt index b53d4cad3f6fd8..2ffeda1526074e 100644 --- a/test/prism/snapshots/seattlerb/difficult2_.txt +++ b/test/prism/snapshots/seattlerb/difficult2_.txt @@ -1,20 +1,24 @@ @ ProgramNode (location: (1,0)-(2,6)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(2,6)) + ├── flags: ∅ └── body: (length: 2) ├── @ IfNode (location: (1,0)-(1,13)) + │ ├── flags: newline │ ├── if_keyword_loc: ∅ │ ├── predicate: │ │ @ IntegerNode (location: (1,0)-(1,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── then_keyword_loc: (1,2)-(1,3) = "?" │ ├── statements: │ │ @ StatementsNode (location: (1,4)-(1,9)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (1,4)-(1,9)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :b @@ -34,17 +38,19 @@ │ │ └── block: ∅ │ ├── consequent: │ │ @ ElseNode (location: (1,10)-(1,13)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (1,10)-(1,11) = ":" │ │ ├── statements: │ │ │ @ StatementsNode (location: (1,12)-(1,13)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ IntegerNode (location: (1,12)-(1,13)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: newline, static_literal, decimal │ │ │ └── value: 2 │ │ └── end_keyword_loc: ∅ │ └── end_keyword_loc: ∅ └── @ CallNode (location: (2,0)-(2,6)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :a @@ -58,16 +64,17 @@ │ ├── flags: symbol_keys │ └── elements: (length: 1) │ └── @ AssocNode (location: (2,2)-(2,6)) + │ ├── flags: static_literal │ ├── key: │ │ @ SymbolNode (location: (2,2)-(2,4)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (2,2)-(2,3) = "d" │ │ ├── closing_loc: (2,3)-(2,4) = ":" │ │ └── unescaped: "d" │ ├── value: │ │ @ IntegerNode (location: (2,5)-(2,6)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 3 │ └── operator_loc: ∅ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/difficult3_.txt b/test/prism/snapshots/seattlerb/difficult3_.txt index f074c49a9f9911..313cfa4cf265c2 100644 --- a/test/prism/snapshots/seattlerb/difficult3_.txt +++ b/test/prism/snapshots/seattlerb/difficult3_.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,18)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,18)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,18)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,22 +16,27 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,18)) + ├── flags: ∅ ├── locals: [:a, :b, :c] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,16)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,15)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 2) │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :a │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,10)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :b │ │ │ ├── rest: │ │ │ │ @ SplatNode (location: (1,12)-(1,14)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (1,12)-(1,13) = "*" │ │ │ │ └── expression: │ │ │ │ @ RequiredParameterNode (location: (1,13)-(1,14)) diff --git a/test/prism/snapshots/seattlerb/difficult3_2.txt b/test/prism/snapshots/seattlerb/difficult3_2.txt index af1a6491716f93..e1eed78c7963eb 100644 --- a/test/prism/snapshots/seattlerb/difficult3_2.txt +++ b/test/prism/snapshots/seattlerb/difficult3_2.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,13)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,13)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,13)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,13)) + ├── flags: ∅ ├── locals: [:a, :b] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,11)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,10)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: diff --git a/test/prism/snapshots/seattlerb/difficult3_3.txt b/test/prism/snapshots/seattlerb/difficult3_3.txt index e49bbcd55ab04d..5dcd71d651508a 100644 --- a/test/prism/snapshots/seattlerb/difficult3_3.txt +++ b/test/prism/snapshots/seattlerb/difficult3_3.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,17)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,17)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,17)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,11 +16,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,17)) + ├── flags: ∅ ├── locals: [:a, :b, :c] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,15)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,14)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: diff --git a/test/prism/snapshots/seattlerb/difficult3_4.txt b/test/prism/snapshots/seattlerb/difficult3_4.txt index 73afffb4cb7671..8d84f2d4c3d0c8 100644 --- a/test/prism/snapshots/seattlerb/difficult3_4.txt +++ b/test/prism/snapshots/seattlerb/difficult3_4.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,17)) +├── flags: ∅ ├── locals: [:a] └── statements: @ StatementsNode (location: (1,0)-(1,17)) + ├── flags: ∅ └── body: (length: 1) └── @ LocalVariableWriteNode (location: (1,0)-(1,17)) + ├── flags: newline ├── name: :a ├── depth: 0 ├── name_loc: (1,0)-(1,1) = "a" ├── value: │ @ IfNode (location: (1,2)-(1,17)) + │ ├── flags: newline │ ├── if_keyword_loc: ∅ │ ├── predicate: │ │ @ CallNode (location: (1,2)-(1,3)) @@ -24,15 +28,20 @@ │ ├── then_keyword_loc: (1,4)-(1,5) = "?" │ ├── statements: │ │ @ StatementsNode (location: (1,6)-(1,10)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ TrueNode (location: (1,6)-(1,10)) + │ │ └── flags: newline, static_literal │ ├── consequent: │ │ @ ElseNode (location: (1,10)-(1,17)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (1,10)-(1,11) = ":" │ │ ├── statements: │ │ │ @ StatementsNode (location: (1,12)-(1,17)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ FalseNode (location: (1,12)-(1,17)) + │ │ │ └── flags: newline, static_literal │ │ └── end_keyword_loc: ∅ │ └── end_keyword_loc: ∅ └── operator_loc: (1,1)-(1,2) = "=" diff --git a/test/prism/snapshots/seattlerb/difficult3_5.txt b/test/prism/snapshots/seattlerb/difficult3_5.txt index 793c3f1e11890c..b8bbbdc03ec5a1 100644 --- a/test/prism/snapshots/seattlerb/difficult3_5.txt +++ b/test/prism/snapshots/seattlerb/difficult3_5.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,19)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,19)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,19)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -15,21 +17,24 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ LambdaNode (location: (1,2)-(1,19)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── operator_loc: (1,2)-(1,4) = "->" │ ├── opening_loc: (1,7)-(1,8) = "{" │ ├── closing_loc: (1,18)-(1,19) = "}" │ ├── parameters: │ │ @ BlockParametersNode (location: (1,4)-(1,6)) + │ │ ├── flags: ∅ │ │ ├── parameters: ∅ │ │ ├── locals: (length: 0) │ │ ├── opening_loc: (1,4)-(1,5) = "(" │ │ └── closing_loc: (1,5)-(1,6) = ")" │ └── body: │ @ StatementsNode (location: (1,9)-(1,17)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,9)-(1,17)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :g @@ -39,6 +44,7 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (1,11)-(1,17)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: ∅ diff --git a/test/prism/snapshots/seattlerb/difficult3__10.txt b/test/prism/snapshots/seattlerb/difficult3__10.txt index 0131e44d445c16..a5ce0762f16700 100644 --- a/test/prism/snapshots/seattlerb/difficult3__10.txt +++ b/test/prism/snapshots/seattlerb/difficult3__10.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,18)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,18)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,18)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,19 +16,24 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,18)) + ├── flags: ∅ ├── locals: [:a, :b, :c] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,16)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,15)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 2) │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :a │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 0) │ │ │ ├── rest: │ │ │ │ @ SplatNode (location: (1,9)-(1,11)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (1,9)-(1,10) = "*" │ │ │ │ └── expression: │ │ │ │ @ RequiredParameterNode (location: (1,10)-(1,11)) diff --git a/test/prism/snapshots/seattlerb/difficult3__11.txt b/test/prism/snapshots/seattlerb/difficult3__11.txt index a658b091c231ca..c1017885d8ef16 100644 --- a/test/prism/snapshots/seattlerb/difficult3__11.txt +++ b/test/prism/snapshots/seattlerb/difficult3__11.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,14)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,14)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,14)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,19 +16,24 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,14)) + ├── flags: ∅ ├── locals: [:a] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,12)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,11)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 2) │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :a │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 0) │ │ │ ├── rest: │ │ │ │ @ SplatNode (location: (1,9)-(1,10)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (1,9)-(1,10) = "*" │ │ │ │ └── expression: ∅ │ │ │ ├── rights: (length: 0) diff --git a/test/prism/snapshots/seattlerb/difficult3__12.txt b/test/prism/snapshots/seattlerb/difficult3__12.txt index 5aa252fe6ac9c5..bbcfad48b4c68a 100644 --- a/test/prism/snapshots/seattlerb/difficult3__12.txt +++ b/test/prism/snapshots/seattlerb/difficult3__12.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,17)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,17)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,17)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,19 +16,24 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,17)) + ├── flags: ∅ ├── locals: [:a, :b] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,15)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,14)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 2) │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :a │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 0) │ │ │ ├── rest: │ │ │ │ @ SplatNode (location: (1,9)-(1,10)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (1,9)-(1,10) = "*" │ │ │ │ └── expression: ∅ │ │ │ ├── rights: (length: 1) diff --git a/test/prism/snapshots/seattlerb/difficult3__6.txt b/test/prism/snapshots/seattlerb/difficult3__6.txt index a42a625be73743..95d1ac39b58409 100644 --- a/test/prism/snapshots/seattlerb/difficult3__6.txt +++ b/test/prism/snapshots/seattlerb/difficult3__6.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,21)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,21)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,21)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,22 +16,27 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,21)) + ├── flags: ∅ ├── locals: [:a, :b, :c, :d] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,19)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,18)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 2) │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :a │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,18)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,10)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :b │ │ │ ├── rest: │ │ │ │ @ SplatNode (location: (1,12)-(1,14)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (1,12)-(1,13) = "*" │ │ │ │ └── expression: │ │ │ │ @ RequiredParameterNode (location: (1,13)-(1,14)) diff --git a/test/prism/snapshots/seattlerb/difficult3__7.txt b/test/prism/snapshots/seattlerb/difficult3__7.txt index b08025804ca3b1..4640cfdb534854 100644 --- a/test/prism/snapshots/seattlerb/difficult3__7.txt +++ b/test/prism/snapshots/seattlerb/difficult3__7.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,17)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,17)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,17)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,22 +16,27 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,17)) + ├── flags: ∅ ├── locals: [:a, :b] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,15)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,14)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 2) │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :a │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,10)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :b │ │ │ ├── rest: │ │ │ │ @ SplatNode (location: (1,12)-(1,13)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (1,12)-(1,13) = "*" │ │ │ │ └── expression: ∅ │ │ │ ├── rights: (length: 0) diff --git a/test/prism/snapshots/seattlerb/difficult3__8.txt b/test/prism/snapshots/seattlerb/difficult3__8.txt index b2b118faef0bde..e19cbfc279eb08 100644 --- a/test/prism/snapshots/seattlerb/difficult3__8.txt +++ b/test/prism/snapshots/seattlerb/difficult3__8.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,20)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,20)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,20)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,22 +16,27 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,20)) + ├── flags: ∅ ├── locals: [:a, :b, :c] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,18)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,17)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 2) │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :a │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,17)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (1,9)-(1,10)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :b │ │ │ ├── rest: │ │ │ │ @ SplatNode (location: (1,12)-(1,13)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (1,12)-(1,13) = "*" │ │ │ │ └── expression: ∅ │ │ │ ├── rights: (length: 1) diff --git a/test/prism/snapshots/seattlerb/difficult3__9.txt b/test/prism/snapshots/seattlerb/difficult3__9.txt index 85c10a4432b8a8..2259351b41f94e 100644 --- a/test/prism/snapshots/seattlerb/difficult3__9.txt +++ b/test/prism/snapshots/seattlerb/difficult3__9.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,15)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,15)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,15)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :f @@ -14,19 +16,24 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,2)-(1,15)) + ├── flags: ∅ ├── locals: [:a, :b] ├── parameters: │ @ BlockParametersNode (location: (1,4)-(1,13)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,5)-(1,12)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 2) │ │ │ ├── @ RequiredParameterNode (location: (1,5)-(1,6)) │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :a │ │ │ └── @ MultiTargetNode (location: (1,8)-(1,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 0) │ │ │ ├── rest: │ │ │ │ @ SplatNode (location: (1,9)-(1,11)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (1,9)-(1,10) = "*" │ │ │ │ └── expression: │ │ │ │ @ RequiredParameterNode (location: (1,10)-(1,11)) diff --git a/test/prism/snapshots/seattlerb/difficult4__leading_dots.txt b/test/prism/snapshots/seattlerb/difficult4__leading_dots.txt index 8307c806e6258d..4201ed00aadd84 100644 --- a/test/prism/snapshots/seattlerb/difficult4__leading_dots.txt +++ b/test/prism/snapshots/seattlerb/difficult4__leading_dots.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(2,2)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(2,2)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(2,2)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (1,0)-(1,1)) │ ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/seattlerb/difficult4__leading_dots2.txt b/test/prism/snapshots/seattlerb/difficult4__leading_dots2.txt index ee4370c0f03971..8dd122ddd444bc 100644 --- a/test/prism/snapshots/seattlerb/difficult4__leading_dots2.txt +++ b/test/prism/snapshots/seattlerb/difficult4__leading_dots2.txt @@ -1,16 +1,18 @@ @ ProgramNode (location: (1,0)-(2,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(2,3)) + ├── flags: ∅ └── body: (length: 2) ├── @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 1 └── @ RangeNode (location: (2,0)-(2,3)) - ├── flags: ∅ + ├── flags: newline, static_literal ├── left: ∅ ├── right: │ @ IntegerNode (location: (2,2)-(2,3)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 3 └── operator_loc: (2,0)-(2,2) = ".." diff --git a/test/prism/snapshots/seattlerb/difficult6_.txt b/test/prism/snapshots/seattlerb/difficult6_.txt index bf80034fe95e50..fc273bda46d91d 100644 --- a/test/prism/snapshots/seattlerb/difficult6_.txt +++ b/test/prism/snapshots/seattlerb/difficult6_.txt @@ -1,17 +1,22 @@ @ ProgramNode (location: (1,0)-(1,25)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,25)) + ├── flags: ∅ └── body: (length: 1) └── @ LambdaNode (location: (1,0)-(1,25)) + ├── flags: newline ├── locals: [:a, :b] ├── operator_loc: (1,0)-(1,2) = "->" ├── opening_loc: (1,13)-(1,14) = "{" ├── closing_loc: (1,24)-(1,25) = "}" ├── parameters: │ @ BlockParametersNode (location: (1,2)-(1,12)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,3)-(1,11)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (1,3)-(1,4)) │ │ │ ├── flags: ∅ @@ -24,6 +29,7 @@ │ │ │ ├── operator_loc: (1,7)-(1,8) = "=" │ │ │ └── value: │ │ │ @ NilNode (location: (1,8)-(1,11)) + │ │ │ └── flags: static_literal │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) │ │ ├── keywords: (length: 0) @@ -34,9 +40,10 @@ │ └── closing_loc: (1,11)-(1,12) = ")" └── body: @ StatementsNode (location: (1,15)-(1,23)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,15)-(1,23)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :p @@ -50,9 +57,11 @@ │ ├── flags: ∅ │ ├── elements: (length: 2) │ │ ├── @ LocalVariableReadNode (location: (1,18)-(1,19)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ └── @ LocalVariableReadNode (location: (1,21)-(1,22)) + │ │ ├── flags: ∅ │ │ ├── name: :b │ │ └── depth: 0 │ ├── opening_loc: (1,17)-(1,18) = "[" diff --git a/test/prism/snapshots/seattlerb/difficult6__7.txt b/test/prism/snapshots/seattlerb/difficult6__7.txt index 7fe70c7033279f..509da6914f4725 100644 --- a/test/prism/snapshots/seattlerb/difficult6__7.txt +++ b/test/prism/snapshots/seattlerb/difficult6__7.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,11)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,11)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,11)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (1,0)-(1,1)) │ ├── flags: variable_call, ignore_visibility @@ -25,24 +27,28 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ ParenthesesNode (location: (1,4)-(1,7)) + │ ├── flags: ∅ │ ├── body: │ │ @ StatementsNode (location: (1,5)-(1,6)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (1,5)-(1,6)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 1 │ ├── opening_loc: (1,4)-(1,5) = "(" │ └── closing_loc: (1,6)-(1,7) = ")" ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,8)-(1,11)) + ├── flags: ∅ ├── locals: [] ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (1,9)-(1,10)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,9)-(1,10)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :c diff --git a/test/prism/snapshots/seattlerb/difficult6__8.txt b/test/prism/snapshots/seattlerb/difficult6__8.txt index 7f915e283c73df..c6fece62cb418d 100644 --- a/test/prism/snapshots/seattlerb/difficult6__8.txt +++ b/test/prism/snapshots/seattlerb/difficult6__8.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,12)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,12)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,12)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (1,0)-(1,1)) │ ├── flags: variable_call, ignore_visibility @@ -25,24 +27,28 @@ │ ├── flags: ∅ │ └── arguments: (length: 1) │ └── @ ParenthesesNode (location: (1,5)-(1,8)) + │ ├── flags: ∅ │ ├── body: │ │ @ StatementsNode (location: (1,6)-(1,7)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (1,6)-(1,7)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 1 │ ├── opening_loc: (1,5)-(1,6) = "(" │ └── closing_loc: (1,7)-(1,8) = ")" ├── closing_loc: ∅ └── block: @ BlockNode (location: (1,9)-(1,12)) + ├── flags: ∅ ├── locals: [] ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (1,10)-(1,11)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,10)-(1,11)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :c diff --git a/test/prism/snapshots/seattlerb/difficult7_.txt b/test/prism/snapshots/seattlerb/difficult7_.txt index 40c778cf6cc2cd..0635798848490e 100644 --- a/test/prism/snapshots/seattlerb/difficult7_.txt +++ b/test/prism/snapshots/seattlerb/difficult7_.txt @@ -1,15 +1,19 @@ @ ProgramNode (location: (1,6)-(4,7)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,6)-(4,7)) + ├── flags: ∅ └── body: (length: 1) └── @ HashNode (location: (1,6)-(4,7)) + ├── flags: newline ├── opening_loc: (1,6)-(1,7) = "{" ├── elements: (length: 2) │ ├── @ AssocNode (location: (2,8)-(2,33)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ SymbolNode (location: (2,8)-(2,10)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (2,8)-(2,9) = "a" │ │ │ ├── closing_loc: (2,9)-(2,10) = ":" @@ -26,12 +30,15 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: │ │ │ @ BlockNode (location: (2,18)-(2,33)) + │ │ │ ├── flags: ∅ │ │ │ ├── locals: [] │ │ │ ├── parameters: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (2,20)-(2,31)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ IfNode (location: (2,20)-(2,31)) + │ │ │ │ ├── flags: newline │ │ │ │ ├── if_keyword_loc: ∅ │ │ │ │ ├── predicate: │ │ │ │ │ @ CallNode (location: (2,20)-(2,21)) @@ -47,9 +54,10 @@ │ │ │ │ ├── then_keyword_loc: (2,22)-(2,23) = "?" │ │ │ │ ├── statements: │ │ │ │ │ @ StatementsNode (location: (2,24)-(2,27)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ CallNode (location: (2,24)-(2,27)) - │ │ │ │ │ ├── flags: ignore_visibility + │ │ │ │ │ ├── flags: newline, ignore_visibility │ │ │ │ │ ├── receiver: ∅ │ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ │ ├── name: :c @@ -60,12 +68,14 @@ │ │ │ │ │ └── block: ∅ │ │ │ │ ├── consequent: │ │ │ │ │ @ ElseNode (location: (2,28)-(2,31)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── else_keyword_loc: (2,28)-(2,29) = ":" │ │ │ │ │ ├── statements: │ │ │ │ │ │ @ StatementsNode (location: (2,30)-(2,31)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ │ └── @ CallNode (location: (2,30)-(2,31)) - │ │ │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ │ │ ├── receiver: ∅ │ │ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ │ │ ├── name: :d @@ -80,14 +90,16 @@ │ │ │ └── closing_loc: (2,32)-(2,33) = "}" │ │ └── operator_loc: ∅ │ └── @ AssocNode (location: (3,8)-(3,14)) + │ ├── flags: static_literal │ ├── key: │ │ @ SymbolNode (location: (3,8)-(3,10)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (3,8)-(3,9) = "e" │ │ ├── closing_loc: (3,9)-(3,10) = ":" │ │ └── unescaped: "e" │ ├── value: │ │ @ NilNode (location: (3,11)-(3,14)) + │ │ └── flags: static_literal │ └── operator_loc: ∅ └── closing_loc: (4,6)-(4,7) = "}" diff --git a/test/prism/snapshots/seattlerb/do_bug.txt b/test/prism/snapshots/seattlerb/do_bug.txt index 5877b18d686d55..8d1a0b786ce944 100644 --- a/test/prism/snapshots/seattlerb/do_bug.txt +++ b/test/prism/snapshots/seattlerb/do_bug.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(4,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(4,3)) + ├── flags: ∅ └── body: (length: 2) ├── @ CallNode (location: (1,0)-(1,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :a @@ -15,12 +17,12 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (1,2)-(1,3)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── closing_loc: ∅ │ └── block: ∅ └── @ CallNode (location: (2,0)-(4,3)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (2,0)-(2,1)) │ ├── flags: variable_call, ignore_visibility @@ -40,11 +42,14 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (2,4)-(4,3)) + ├── flags: ∅ ├── locals: [:c] ├── parameters: │ @ BlockParametersNode (location: (2,7)-(2,10)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (2,8)-(2,9)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (2,8)-(2,9)) │ │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/do_lambda.txt b/test/prism/snapshots/seattlerb/do_lambda.txt index 4713fb3e4b7dcc..76383b0ada0a0b 100644 --- a/test/prism/snapshots/seattlerb/do_lambda.txt +++ b/test/prism/snapshots/seattlerb/do_lambda.txt @@ -1,15 +1,19 @@ @ ProgramNode (location: (1,0)-(1,11)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,11)) + ├── flags: ∅ └── body: (length: 1) └── @ LambdaNode (location: (1,0)-(1,11)) + ├── flags: newline ├── locals: [] ├── operator_loc: (1,0)-(1,2) = "->" ├── opening_loc: (1,5)-(1,7) = "do" ├── closing_loc: (1,8)-(1,11) = "end" ├── parameters: │ @ BlockParametersNode (location: (1,2)-(1,4)) + │ ├── flags: ∅ │ ├── parameters: ∅ │ ├── locals: (length: 0) │ ├── opening_loc: (1,2)-(1,3) = "(" diff --git a/test/prism/snapshots/seattlerb/dot2_nil__26.txt b/test/prism/snapshots/seattlerb/dot2_nil__26.txt index 104515ac3ade9d..fc5122055f51ec 100644 --- a/test/prism/snapshots/seattlerb/dot2_nil__26.txt +++ b/test/prism/snapshots/seattlerb/dot2_nil__26.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,3)) + ├── flags: ∅ └── body: (length: 1) └── @ RangeNode (location: (1,0)-(1,3)) - ├── flags: ∅ + ├── flags: newline ├── left: │ @ CallNode (location: (1,0)-(1,1)) │ ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/seattlerb/dot3_nil__26.txt b/test/prism/snapshots/seattlerb/dot3_nil__26.txt index ec7f57cd96f83a..c2277975db0ad9 100644 --- a/test/prism/snapshots/seattlerb/dot3_nil__26.txt +++ b/test/prism/snapshots/seattlerb/dot3_nil__26.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,4)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,4)) + ├── flags: ∅ └── body: (length: 1) └── @ RangeNode (location: (1,0)-(1,4)) - ├── flags: exclude_end + ├── flags: newline, exclude_end ├── left: │ @ CallNode (location: (1,0)-(1,1)) │ ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/seattlerb/dstr_evstr.txt b/test/prism/snapshots/seattlerb/dstr_evstr.txt index add8ad6f5c1248..143d8ff5840a7a 100644 --- a/test/prism/snapshots/seattlerb/dstr_evstr.txt +++ b/test/prism/snapshots/seattlerb/dstr_evstr.txt @@ -1,28 +1,34 @@ @ ProgramNode (location: (1,0)-(1,12)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,12)) + ├── flags: ∅ └── body: (length: 1) └── @ InterpolatedStringNode (location: (1,0)-(1,12)) - ├── flags: ∅ + ├── flags: newline ├── opening_loc: (1,0)-(1,1) = "\"" ├── parts: (length: 2) │ ├── @ EmbeddedStatementsNode (location: (1,1)-(1,7)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (1,1)-(1,3) = "\#{" │ │ ├── statements: │ │ │ @ StatementsNode (location: (1,3)-(1,6)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ StringNode (location: (1,3)-(1,6)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: (1,3)-(1,4) = "'" │ │ │ ├── content_loc: (1,4)-(1,5) = "a" │ │ │ ├── closing_loc: (1,5)-(1,6) = "'" │ │ │ └── unescaped: "a" │ │ └── closing_loc: (1,6)-(1,7) = "}" │ └── @ EmbeddedStatementsNode (location: (1,7)-(1,11)) + │ ├── flags: ∅ │ ├── opening_loc: (1,7)-(1,9) = "\#{" │ ├── statements: │ │ @ StatementsNode (location: (1,9)-(1,10)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (1,9)-(1,10)) │ │ ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/seattlerb/dstr_evstr_empty_end.txt b/test/prism/snapshots/seattlerb/dstr_evstr_empty_end.txt index 53f97e4b3634be..8e3ade63c97361 100644 --- a/test/prism/snapshots/seattlerb/dstr_evstr_empty_end.txt +++ b/test/prism/snapshots/seattlerb/dstr_evstr_empty_end.txt @@ -1,15 +1,20 @@ @ ProgramNode (location: (1,0)-(1,11)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,11)) + ├── flags: ∅ └── body: (length: 1) └── @ InterpolatedSymbolNode (location: (1,0)-(1,11)) + ├── flags: newline ├── opening_loc: (1,0)-(1,2) = ":\"" ├── parts: (length: 1) │ └── @ EmbeddedStatementsNode (location: (1,2)-(1,10)) + │ ├── flags: ∅ │ ├── opening_loc: (1,2)-(1,4) = "\#{" │ ├── statements: │ │ @ StatementsNode (location: (1,4)-(1,9)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (1,4)-(1,9)) │ │ ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/seattlerb/dstr_lex_state.txt b/test/prism/snapshots/seattlerb/dstr_lex_state.txt index c4c0ef0437d2fd..2bdeab0834ede0 100644 --- a/test/prism/snapshots/seattlerb/dstr_lex_state.txt +++ b/test/prism/snapshots/seattlerb/dstr_lex_state.txt @@ -1,16 +1,20 @@ @ ProgramNode (location: (1,0)-(1,8)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,8)) + ├── flags: ∅ └── body: (length: 1) └── @ InterpolatedStringNode (location: (1,0)-(1,8)) - ├── flags: ∅ + ├── flags: newline ├── opening_loc: (1,0)-(1,1) = "\"" ├── parts: (length: 1) │ └── @ EmbeddedStatementsNode (location: (1,1)-(1,7)) + │ ├── flags: ∅ │ ├── opening_loc: (1,1)-(1,3) = "\#{" │ ├── statements: │ │ @ StatementsNode (location: (1,3)-(1,6)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (1,3)-(1,6)) │ │ ├── flags: ignore_visibility @@ -24,7 +28,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ SymbolNode (location: (1,4)-(1,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (1,4)-(1,5) = ":" │ │ │ ├── value_loc: (1,5)-(1,6) = "a" │ │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/dstr_str.txt b/test/prism/snapshots/seattlerb/dstr_str.txt index 6fe07818809a59..7b3e0e36ad5006 100644 --- a/test/prism/snapshots/seattlerb/dstr_str.txt +++ b/test/prism/snapshots/seattlerb/dstr_str.txt @@ -1,26 +1,30 @@ @ ProgramNode (location: (1,0)-(1,10)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,10)) + ├── flags: ∅ └── body: (length: 1) └── @ InterpolatedStringNode (location: (1,0)-(1,10)) - ├── flags: mutable + ├── flags: newline, static_literal, mutable ├── opening_loc: (1,0)-(1,1) = "\"" ├── parts: (length: 2) │ ├── @ EmbeddedStatementsNode (location: (1,1)-(1,7)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (1,1)-(1,3) = "\#{" │ │ ├── statements: │ │ │ @ StatementsNode (location: (1,3)-(1,6)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ StringNode (location: (1,3)-(1,6)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: (1,3)-(1,4) = "'" │ │ │ ├── content_loc: (1,4)-(1,5) = "a" │ │ │ ├── closing_loc: (1,5)-(1,6) = "'" │ │ │ └── unescaped: "a" │ │ └── closing_loc: (1,6)-(1,7) = "}" │ └── @ StringNode (location: (1,7)-(1,9)) - │ ├── flags: frozen + │ ├── flags: static_literal, frozen │ ├── opening_loc: ∅ │ ├── content_loc: (1,7)-(1,9) = " b" │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/dsym_esc_to_sym.txt b/test/prism/snapshots/seattlerb/dsym_esc_to_sym.txt index 7b1b68131e0b29..4224e305e9f9ca 100644 --- a/test/prism/snapshots/seattlerb/dsym_esc_to_sym.txt +++ b/test/prism/snapshots/seattlerb/dsym_esc_to_sym.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,17)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,17)) + ├── flags: ∅ └── body: (length: 1) └── @ SymbolNode (location: (1,0)-(1,17)) - ├── flags: forced_utf8_encoding + ├── flags: newline, static_literal, forced_utf8_encoding ├── opening_loc: (1,0)-(1,2) = ":\"" ├── value_loc: (1,2)-(1,16) = "Variet\\303\\240" ├── closing_loc: (1,16)-(1,17) = "\"" diff --git a/test/prism/snapshots/seattlerb/dsym_to_sym.txt b/test/prism/snapshots/seattlerb/dsym_to_sym.txt index eb7e435c630fcd..7c5d2bef87497b 100644 --- a/test/prism/snapshots/seattlerb/dsym_to_sym.txt +++ b/test/prism/snapshots/seattlerb/dsym_to_sym.txt @@ -1,35 +1,39 @@ @ ProgramNode (location: (1,0)-(3,13)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,13)) + ├── flags: ∅ └── body: (length: 2) ├── @ AliasMethodNode (location: (1,0)-(1,17)) + │ ├── flags: newline │ ├── new_name: │ │ @ SymbolNode (location: (1,6)-(1,11)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,6)-(1,8) = ":\"" │ │ ├── value_loc: (1,8)-(1,10) = "<<" │ │ ├── closing_loc: (1,10)-(1,11) = "\"" │ │ └── unescaped: "<<" │ ├── old_name: │ │ @ SymbolNode (location: (1,12)-(1,17)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,12)-(1,14) = ":\"" │ │ ├── value_loc: (1,14)-(1,16) = ">>" │ │ ├── closing_loc: (1,16)-(1,17) = "\"" │ │ └── unescaped: ">>" │ └── keyword_loc: (1,0)-(1,5) = "alias" └── @ AliasMethodNode (location: (3,0)-(3,13)) + ├── flags: newline ├── new_name: │ @ SymbolNode (location: (3,6)-(3,9)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (3,6)-(3,7) = ":" │ ├── value_loc: (3,7)-(3,9) = "<<" │ ├── closing_loc: ∅ │ └── unescaped: "<<" ├── old_name: │ @ SymbolNode (location: (3,10)-(3,13)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (3,10)-(3,11) = ":" │ ├── value_loc: (3,11)-(3,13) = ">>" │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/eq_begin_line_numbers.txt b/test/prism/snapshots/seattlerb/eq_begin_line_numbers.txt index a5fc3951d69d2d..010ee71da042ef 100644 --- a/test/prism/snapshots/seattlerb/eq_begin_line_numbers.txt +++ b/test/prism/snapshots/seattlerb/eq_begin_line_numbers.txt @@ -1,11 +1,13 @@ @ ProgramNode (location: (1,0)-(6,1)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(6,1)) + ├── flags: ∅ └── body: (length: 2) ├── @ IntegerNode (location: (1,0)-(1,1)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 1 └── @ IntegerNode (location: (6,0)-(6,1)) - ├── flags: decimal + ├── flags: newline, static_literal, decimal └── value: 2 diff --git a/test/prism/snapshots/seattlerb/eq_begin_why_wont_people_use_their_spacebar.txt b/test/prism/snapshots/seattlerb/eq_begin_why_wont_people_use_their_spacebar.txt index 2103bde8cbfa76..5f6a3fe62ee9c4 100644 --- a/test/prism/snapshots/seattlerb/eq_begin_why_wont_people_use_their_spacebar.txt +++ b/test/prism/snapshots/seattlerb/eq_begin_why_wont_people_use_their_spacebar.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(3,8)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,8)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(3,8)) - ├── flags: attribute_write + ├── flags: newline, attribute_write ├── receiver: │ @ CallNode (location: (1,0)-(1,1)) │ ├── flags: variable_call, ignore_visibility @@ -35,12 +37,14 @@ │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ └── @ BeginNode (location: (1,5)-(3,8)) + │ ├── flags: ∅ │ ├── begin_keyword_loc: (1,5)-(1,10) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (2,7)-(2,9)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (2,7)-(2,9)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 42 │ ├── rescue_clause: ∅ │ ├── else_clause: ∅ diff --git a/test/prism/snapshots/seattlerb/evstr_evstr.txt b/test/prism/snapshots/seattlerb/evstr_evstr.txt index 9c801299f812e4..c8dba1c3e55443 100644 --- a/test/prism/snapshots/seattlerb/evstr_evstr.txt +++ b/test/prism/snapshots/seattlerb/evstr_evstr.txt @@ -1,16 +1,20 @@ @ ProgramNode (location: (1,0)-(1,10)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,10)) + ├── flags: ∅ └── body: (length: 1) └── @ InterpolatedStringNode (location: (1,0)-(1,10)) - ├── flags: ∅ + ├── flags: newline ├── opening_loc: (1,0)-(1,1) = "\"" ├── parts: (length: 2) │ ├── @ EmbeddedStatementsNode (location: (1,1)-(1,5)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (1,1)-(1,3) = "\#{" │ │ ├── statements: │ │ │ @ StatementsNode (location: (1,3)-(1,4)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (1,3)-(1,4)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -24,9 +28,11 @@ │ │ │ └── block: ∅ │ │ └── closing_loc: (1,4)-(1,5) = "}" │ └── @ EmbeddedStatementsNode (location: (1,5)-(1,9)) + │ ├── flags: ∅ │ ├── opening_loc: (1,5)-(1,7) = "\#{" │ ├── statements: │ │ @ StatementsNode (location: (1,7)-(1,8)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (1,7)-(1,8)) │ │ ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/seattlerb/evstr_str.txt b/test/prism/snapshots/seattlerb/evstr_str.txt index 54319e613c5e97..69a992b19a9dd1 100644 --- a/test/prism/snapshots/seattlerb/evstr_str.txt +++ b/test/prism/snapshots/seattlerb/evstr_str.txt @@ -1,16 +1,20 @@ @ ProgramNode (location: (1,0)-(1,8)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,8)) + ├── flags: ∅ └── body: (length: 1) └── @ InterpolatedStringNode (location: (1,0)-(1,8)) - ├── flags: ∅ + ├── flags: newline ├── opening_loc: (1,0)-(1,1) = "\"" ├── parts: (length: 2) │ ├── @ EmbeddedStatementsNode (location: (1,1)-(1,5)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (1,1)-(1,3) = "\#{" │ │ ├── statements: │ │ │ @ StatementsNode (location: (1,3)-(1,4)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (1,3)-(1,4)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -24,7 +28,7 @@ │ │ │ └── block: ∅ │ │ └── closing_loc: (1,4)-(1,5) = "}" │ └── @ StringNode (location: (1,5)-(1,7)) - │ ├── flags: frozen + │ ├── flags: static_literal, frozen │ ├── opening_loc: ∅ │ ├── content_loc: (1,5)-(1,7) = " b" │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/expr_not_bang.txt b/test/prism/snapshots/seattlerb/expr_not_bang.txt index 0a289ab7bed37f..868ef85648f759 100644 --- a/test/prism/snapshots/seattlerb/expr_not_bang.txt +++ b/test/prism/snapshots/seattlerb/expr_not_bang.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(1,5)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,5)) + ├── flags: ∅ └── body: (length: 1) └── @ CallNode (location: (1,0)-(1,5)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (1,2)-(1,5)) │ ├── flags: ignore_visibility diff --git a/test/prism/snapshots/seattlerb/f_kw.txt b/test/prism/snapshots/seattlerb/f_kw.txt index 4226137925ae02..e2b82371b87734 100644 --- a/test/prism/snapshots/seattlerb/f_kw.txt +++ b/test/prism/snapshots/seattlerb/f_kw.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,15)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,15)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,15)) + ├── flags: newline ├── name: :x ├── name_loc: (1,4)-(1,5) = "x" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,6)-(1,10)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ @@ -20,7 +24,7 @@ │ │ ├── name_loc: (1,6)-(1,8) = "k:" │ │ └── value: │ │ @ IntegerNode (location: (1,8)-(1,10)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 42 │ ├── keyword_rest: ∅ │ └── block: ∅ diff --git a/test/prism/snapshots/seattlerb/f_kw__required.txt b/test/prism/snapshots/seattlerb/f_kw__required.txt index f72f43e034e6dd..c44dbbf2fc81c0 100644 --- a/test/prism/snapshots/seattlerb/f_kw__required.txt +++ b/test/prism/snapshots/seattlerb/f_kw__required.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,13)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,13)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,13)) + ├── flags: newline ├── name: :x ├── name_loc: (1,4)-(1,5) = "x" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,6)-(1,8)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ diff --git a/test/prism/snapshots/seattlerb/flip2_env_lvar.txt b/test/prism/snapshots/seattlerb/flip2_env_lvar.txt index 5a71ab6cda38fb..8950bbf0876844 100644 --- a/test/prism/snapshots/seattlerb/flip2_env_lvar.txt +++ b/test/prism/snapshots/seattlerb/flip2_env_lvar.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(1,16)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,16)) + ├── flags: ∅ └── body: (length: 1) └── @ IfNode (location: (1,0)-(1,16)) + ├── flags: newline ├── if_keyword_loc: (1,0)-(1,2) = "if" ├── predicate: │ @ FlipFlopNode (location: (1,3)-(1,7)) diff --git a/test/prism/snapshots/seattlerb/float_with_if_modifier.txt b/test/prism/snapshots/seattlerb/float_with_if_modifier.txt index 9c1da70f242576..aab855944c0958 100644 --- a/test/prism/snapshots/seattlerb/float_with_if_modifier.txt +++ b/test/prism/snapshots/seattlerb/float_with_if_modifier.txt @@ -1,17 +1,23 @@ @ ProgramNode (location: (1,0)-(1,10)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,10)) + ├── flags: ∅ └── body: (length: 1) └── @ IfNode (location: (1,0)-(1,10)) + ├── flags: newline ├── if_keyword_loc: (1,3)-(1,5) = "if" ├── predicate: │ @ TrueNode (location: (1,6)-(1,10)) + │ └── flags: static_literal ├── then_keyword_loc: ∅ ├── statements: │ @ StatementsNode (location: (1,0)-(1,3)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ FloatNode (location: (1,0)-(1,3)) + │ ├── flags: newline, static_literal │ └── value: 1.0 ├── consequent: ∅ └── end_keyword_loc: ∅ diff --git a/test/prism/snapshots/seattlerb/heredoc__backslash_dos_format.txt b/test/prism/snapshots/seattlerb/heredoc__backslash_dos_format.txt index 353e4c6964ceec..7d745587b3f14f 100644 --- a/test/prism/snapshots/seattlerb/heredoc__backslash_dos_format.txt +++ b/test/prism/snapshots/seattlerb/heredoc__backslash_dos_format.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(1,12)) +├── flags: ∅ ├── locals: [:str] └── statements: @ StatementsNode (location: (1,0)-(1,12)) + ├── flags: ∅ └── body: (length: 1) └── @ LocalVariableWriteNode (location: (1,0)-(1,12)) + ├── flags: newline ├── name: :str ├── depth: 0 ├── name_loc: (1,0)-(1,3) = "str" diff --git a/test/prism/snapshots/seattlerb/heredoc_backslash_nl.txt b/test/prism/snapshots/seattlerb/heredoc_backslash_nl.txt index fc4c1784fef43e..4c34bf6ef60586 100644 --- a/test/prism/snapshots/seattlerb/heredoc_backslash_nl.txt +++ b/test/prism/snapshots/seattlerb/heredoc_backslash_nl.txt @@ -1,16 +1,18 @@ @ ProgramNode (location: (1,0)-(5,7)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(5,7)) + ├── flags: ∅ └── body: (length: 2) ├── @ StringNode (location: (1,0)-(3,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (1,0)-(1,1) = "\"" │ ├── content_loc: (1,1)-(3,0) = " why would someone do this? \\\n blah\n" │ ├── closing_loc: (3,0)-(3,1) = "\"" │ └── unescaped: " why would someone do this? blah\n" └── @ StringNode (location: (5,0)-(5,7)) - ├── flags: ∅ + ├── flags: newline ├── opening_loc: (5,0)-(5,7) = "<<-DESC" ├── content_loc: (6,0)-(8,0) = " why would someone do this? \\\n blah\n" ├── closing_loc: (8,0)-(9,0) = "DESC\n" diff --git a/test/prism/snapshots/seattlerb/heredoc_bad_hex_escape.txt b/test/prism/snapshots/seattlerb/heredoc_bad_hex_escape.txt index 2b1d776404bbcc..1c521108c04f04 100644 --- a/test/prism/snapshots/seattlerb/heredoc_bad_hex_escape.txt +++ b/test/prism/snapshots/seattlerb/heredoc_bad_hex_escape.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(1,9)) +├── flags: ∅ ├── locals: [:s] └── statements: @ StatementsNode (location: (1,0)-(1,9)) + ├── flags: ∅ └── body: (length: 1) └── @ LocalVariableWriteNode (location: (1,0)-(1,9)) + ├── flags: newline ├── name: :s ├── depth: 0 ├── name_loc: (1,0)-(1,1) = "s" diff --git a/test/prism/snapshots/seattlerb/heredoc_bad_oct_escape.txt b/test/prism/snapshots/seattlerb/heredoc_bad_oct_escape.txt index 7a01f8d6d1c801..9726a71822bda3 100644 --- a/test/prism/snapshots/seattlerb/heredoc_bad_oct_escape.txt +++ b/test/prism/snapshots/seattlerb/heredoc_bad_oct_escape.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(1,10)) +├── flags: ∅ ├── locals: [:s] └── statements: @ StatementsNode (location: (1,0)-(1,10)) + ├── flags: ∅ └── body: (length: 1) └── @ LocalVariableWriteNode (location: (1,0)-(1,10)) + ├── flags: newline ├── name: :s ├── depth: 0 ├── name_loc: (1,0)-(1,1) = "s" diff --git a/test/prism/snapshots/seattlerb/heredoc_comma_arg.txt b/test/prism/snapshots/seattlerb/heredoc_comma_arg.txt index 888ebc809a80a6..0a432984723adb 100644 --- a/test/prism/snapshots/seattlerb/heredoc_comma_arg.txt +++ b/test/prism/snapshots/seattlerb/heredoc_comma_arg.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(7,1)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(7,1)) + ├── flags: ∅ └── body: (length: 2) ├── @ ArrayNode (location: (1,0)-(2,3)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 1) │ │ └── @ StringNode (location: (1,1)-(2,1)) │ │ ├── flags: ∅ @@ -15,7 +17,7 @@ │ ├── opening_loc: (1,0)-(1,1) = "[" │ └── closing_loc: (2,2)-(2,3) = "]" └── @ ArrayNode (location: (4,0)-(7,1)) - ├── flags: ∅ + ├── flags: newline ├── elements: (length: 1) │ └── @ StringNode (location: (4,1)-(4,8)) │ ├── flags: ∅ diff --git a/test/prism/snapshots/seattlerb/heredoc_lineno.txt b/test/prism/snapshots/seattlerb/heredoc_lineno.txt index a51ce71afe83ca..7dde22912e5e8e 100644 --- a/test/prism/snapshots/seattlerb/heredoc_lineno.txt +++ b/test/prism/snapshots/seattlerb/heredoc_lineno.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(7,6)) +├── flags: ∅ ├── locals: [:c, :d] └── statements: @ StatementsNode (location: (1,0)-(7,6)) + ├── flags: ∅ └── body: (length: 2) ├── @ LocalVariableWriteNode (location: (1,0)-(1,11)) + │ ├── flags: newline │ ├── name: :c │ ├── depth: 0 │ ├── name_loc: (1,0)-(1,1) = "c" @@ -16,11 +19,12 @@ │ │ └── unescaped: "line2\nline3\nline4\n" │ └── operator_loc: (1,2)-(1,3) = "=" └── @ LocalVariableWriteNode (location: (7,0)-(7,6)) + ├── flags: newline ├── name: :d ├── depth: 0 ├── name_loc: (7,0)-(7,1) = "d" ├── value: │ @ IntegerNode (location: (7,4)-(7,6)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 42 └── operator_loc: (7,2)-(7,3) = "=" diff --git a/test/prism/snapshots/seattlerb/heredoc_nested.txt b/test/prism/snapshots/seattlerb/heredoc_nested.txt index a2322b9632eeba..4820ef6d4dbf6d 100644 --- a/test/prism/snapshots/seattlerb/heredoc_nested.txt +++ b/test/prism/snapshots/seattlerb/heredoc_nested.txt @@ -1,42 +1,46 @@ @ ProgramNode (location: (1,0)-(7,2)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(7,2)) + ├── flags: ∅ └── body: (length: 1) └── @ ArrayNode (location: (1,0)-(7,2)) - ├── flags: ∅ + ├── flags: newline, static_literal ├── elements: (length: 2) │ ├── @ InterpolatedStringNode (location: (1,1)-(1,4)) - │ │ ├── flags: mutable + │ │ ├── flags: static_literal, mutable │ │ ├── opening_loc: (1,1)-(1,4) = "<=" ├── @ SymbolNode (location: (77,0)-(77,3)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (77,0)-(77,1) = ":" │ ├── value_loc: (77,1)-(77,3) = ">>" │ ├── closing_loc: ∅ │ └── unescaped: ">>" ├── @ SymbolNode (location: (79,0)-(79,2)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (79,0)-(79,1) = ":" │ ├── value_loc: (79,1)-(79,2) = ">" │ ├── closing_loc: ∅ │ └── unescaped: ">" ├── @ SymbolNode (location: (81,0)-(81,4)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (81,0)-(81,1) = ":" │ ├── value_loc: (81,1)-(81,4) = "<=>" │ ├── closing_loc: ∅ │ └── unescaped: "<=>" ├── @ SymbolNode (location: (83,0)-(83,3)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (83,0)-(83,1) = ":" │ ├── value_loc: (83,1)-(83,3) = "<=" │ ├── closing_loc: ∅ │ └── unescaped: "<=" ├── @ SymbolNode (location: (85,0)-(85,3)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (85,0)-(85,1) = ":" │ ├── value_loc: (85,1)-(85,3) = "<<" │ ├── closing_loc: ∅ │ └── unescaped: "<<" ├── @ SymbolNode (location: (87,0)-(87,2)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (87,0)-(87,1) = ":" │ ├── value_loc: (87,1)-(87,2) = "<" │ ├── closing_loc: ∅ │ └── unescaped: "<" ├── @ SymbolNode (location: (89,0)-(89,9)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (89,0)-(89,1) = ":" │ ├── value_loc: (89,1)-(89,9) = "__LINE__" │ ├── closing_loc: ∅ │ └── unescaped: "__LINE__" ├── @ SymbolNode (location: (91,0)-(91,9)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (91,0)-(91,1) = ":" │ ├── value_loc: (91,1)-(91,9) = "__FILE__" │ ├── closing_loc: ∅ │ └── unescaped: "__FILE__" └── @ SymbolNode (location: (93,0)-(93,13)) - ├── flags: forced_us_ascii_encoding + ├── flags: newline, static_literal, forced_us_ascii_encoding ├── opening_loc: (93,0)-(93,1) = ":" ├── value_loc: (93,1)-(93,13) = "__ENCODING__" ├── closing_loc: ∅ diff --git a/test/prism/snapshots/ternary_operator.txt b/test/prism/snapshots/ternary_operator.txt index 0277ac88f0c150..76debe3fa7463f 100644 --- a/test/prism/snapshots/ternary_operator.txt +++ b/test/prism/snapshots/ternary_operator.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(15,12)) +├── flags: ∅ ├── locals: [:_a] └── statements: @ StatementsNode (location: (1,0)-(15,12)) + ├── flags: ∅ └── body: (length: 8) ├── @ IfNode (location: (1,0)-(1,9)) + │ ├── flags: newline │ ├── if_keyword_loc: ∅ │ ├── predicate: │ │ @ CallNode (location: (1,0)-(1,1)) @@ -19,9 +22,10 @@ │ ├── then_keyword_loc: (1,2)-(1,3) = "?" │ ├── statements: │ │ @ StatementsNode (location: (1,4)-(1,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (1,4)-(1,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :b @@ -32,12 +36,14 @@ │ │ └── block: ∅ │ ├── consequent: │ │ @ ElseNode (location: (1,6)-(1,9)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (1,6)-(1,7) = ":" │ │ ├── statements: │ │ │ @ StatementsNode (location: (1,8)-(1,9)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (1,8)-(1,9)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :c @@ -49,6 +55,7 @@ │ │ └── end_keyword_loc: ∅ │ └── end_keyword_loc: ∅ ├── @ IfNode (location: (3,0)-(3,27)) + │ ├── flags: newline │ ├── if_keyword_loc: ∅ │ ├── predicate: │ │ @ CallNode (location: (3,0)-(3,1)) @@ -64,8 +71,10 @@ │ ├── then_keyword_loc: (3,2)-(3,3) = "?" │ ├── statements: │ │ @ StatementsNode (location: (3,4)-(3,14)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ DefinedNode (location: (3,4)-(3,14)) + │ │ ├── flags: newline │ │ ├── lparen_loc: ∅ │ │ ├── value: │ │ │ @ CallNode (location: (3,13)-(3,14)) @@ -82,11 +91,14 @@ │ │ └── keyword_loc: (3,4)-(3,12) = "defined?" │ ├── consequent: │ │ @ ElseNode (location: (3,15)-(3,27)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (3,15)-(3,16) = ":" │ │ ├── statements: │ │ │ @ StatementsNode (location: (3,17)-(3,27)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ DefinedNode (location: (3,17)-(3,27)) + │ │ │ ├── flags: newline │ │ │ ├── lparen_loc: ∅ │ │ │ ├── value: │ │ │ │ @ CallNode (location: (3,26)-(3,27)) @@ -104,6 +116,7 @@ │ │ └── end_keyword_loc: ∅ │ └── end_keyword_loc: ∅ ├── @ IfNode (location: (5,0)-(5,15)) + │ ├── flags: newline │ ├── if_keyword_loc: ∅ │ ├── predicate: │ │ @ CallNode (location: (5,0)-(5,6)) @@ -119,18 +132,24 @@ │ ├── then_keyword_loc: (5,6)-(5,7) = "?" │ ├── statements: │ │ @ StatementsNode (location: (5,7)-(5,11)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ TrueNode (location: (5,7)-(5,11)) + │ │ └── flags: newline, static_literal │ ├── consequent: │ │ @ ElseNode (location: (5,11)-(5,15)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (5,11)-(5,12) = ":" │ │ ├── statements: │ │ │ @ StatementsNode (location: (5,12)-(5,15)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ NilNode (location: (5,12)-(5,15)) + │ │ │ └── flags: newline, static_literal │ │ └── end_keyword_loc: ∅ │ └── end_keyword_loc: ∅ ├── @ IfNode (location: (7,0)-(7,16)) + │ ├── flags: newline │ ├── if_keyword_loc: ∅ │ ├── predicate: │ │ @ CallNode (location: (7,0)-(7,6)) @@ -146,18 +165,24 @@ │ ├── then_keyword_loc: (7,6)-(7,7) = "?" │ ├── statements: │ │ @ StatementsNode (location: (7,7)-(7,12)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ FalseNode (location: (7,7)-(7,12)) + │ │ └── flags: newline, static_literal │ ├── consequent: │ │ @ ElseNode (location: (7,12)-(7,16)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (7,12)-(7,13) = ":" │ │ ├── statements: │ │ │ @ StatementsNode (location: (7,13)-(7,16)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ NilNode (location: (7,13)-(7,16)) + │ │ │ └── flags: newline, static_literal │ │ └── end_keyword_loc: ∅ │ └── end_keyword_loc: ∅ ├── @ IfNode (location: (9,0)-(9,14)) + │ ├── flags: newline │ ├── if_keyword_loc: ∅ │ ├── predicate: │ │ @ CallNode (location: (9,0)-(9,6)) @@ -173,18 +198,24 @@ │ ├── then_keyword_loc: (9,6)-(9,7) = "?" │ ├── statements: │ │ @ StatementsNode (location: (9,7)-(9,10)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ NilNode (location: (9,7)-(9,10)) + │ │ └── flags: newline, static_literal │ ├── consequent: │ │ @ ElseNode (location: (9,10)-(9,14)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (9,10)-(9,11) = ":" │ │ ├── statements: │ │ │ @ StatementsNode (location: (9,11)-(9,14)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ NilNode (location: (9,11)-(9,14)) + │ │ │ └── flags: newline, static_literal │ │ └── end_keyword_loc: ∅ │ └── end_keyword_loc: ∅ ├── @ IfNode (location: (11,0)-(11,10)) + │ ├── flags: newline │ ├── if_keyword_loc: ∅ │ ├── predicate: │ │ @ CallNode (location: (11,0)-(11,2)) @@ -200,18 +231,24 @@ │ ├── then_keyword_loc: (11,2)-(11,3) = "?" │ ├── statements: │ │ @ StatementsNode (location: (11,3)-(11,6)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ NilNode (location: (11,3)-(11,6)) + │ │ └── flags: newline, static_literal │ ├── consequent: │ │ @ ElseNode (location: (11,6)-(11,10)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (11,6)-(11,7) = ":" │ │ ├── statements: │ │ │ @ StatementsNode (location: (11,7)-(11,10)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ NilNode (location: (11,7)-(11,10)) + │ │ │ └── flags: newline, static_literal │ │ └── end_keyword_loc: ∅ │ └── end_keyword_loc: ∅ ├── @ IfNode (location: (13,0)-(13,14)) + │ ├── flags: newline │ ├── if_keyword_loc: ∅ │ ├── predicate: │ │ @ CallNode (location: (13,0)-(13,1)) @@ -227,9 +264,10 @@ │ ├── then_keyword_loc: (13,2)-(13,3) = "?" │ ├── statements: │ │ @ StatementsNode (location: (13,3)-(13,7)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (13,3)-(13,7)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :var1 @@ -240,12 +278,14 @@ │ │ └── block: ∅ │ ├── consequent: │ │ @ ElseNode (location: (13,8)-(13,14)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (13,8)-(13,9) = ":" │ │ ├── statements: │ │ │ @ StatementsNode (location: (13,10)-(13,14)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (13,10)-(13,14)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :var2 @@ -257,6 +297,7 @@ │ │ └── end_keyword_loc: ∅ │ └── end_keyword_loc: ∅ └── @ IfNode (location: (15,0)-(15,12)) + ├── flags: newline ├── if_keyword_loc: ∅ ├── predicate: │ @ CallNode (location: (15,0)-(15,4)) @@ -272,24 +313,28 @@ ├── then_keyword_loc: (15,4)-(15,5) = "?" ├── statements: │ @ StatementsNode (location: (15,5)-(15,10)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ LocalVariableWriteNode (location: (15,5)-(15,10)) + │ ├── flags: newline │ ├── name: :_a │ ├── depth: 0 │ ├── name_loc: (15,5)-(15,7) = "_a" │ ├── value: │ │ @ IntegerNode (location: (15,9)-(15,10)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ └── operator_loc: (15,8)-(15,9) = "=" ├── consequent: │ @ ElseNode (location: (15,10)-(15,12)) + │ ├── flags: ∅ │ ├── else_keyword_loc: (15,10)-(15,11) = ":" │ ├── statements: │ │ @ StatementsNode (location: (15,11)-(15,12)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (15,11)-(15,12)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 1 │ └── end_keyword_loc: ∅ └── end_keyword_loc: ∅ diff --git a/test/prism/snapshots/tilde_heredocs.txt b/test/prism/snapshots/tilde_heredocs.txt index f50f915a648dc3..dc6321ce1a5972 100644 --- a/test/prism/snapshots/tilde_heredocs.txt +++ b/test/prism/snapshots/tilde_heredocs.txt @@ -1,403 +1,419 @@ @ ProgramNode (location: (1,0)-(94,6)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(94,6)) + ├── flags: ∅ └── body: (length: 19) ├── @ InterpolatedStringNode (location: (1,0)-(1,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (1,0)-(1,6) = "<<~EOF" │ ├── parts: (length: 4) │ │ ├── @ StringNode (location: (2,0)-(3,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (2,0)-(3,0) = " a\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: " a\n" │ │ ├── @ EmbeddedStatementsNode (location: (3,0)-(3,4)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (3,0)-(3,2) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (3,2)-(3,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ IntegerNode (location: (3,2)-(3,3)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── closing_loc: (3,3)-(3,4) = "}" │ │ ├── @ StringNode (location: (3,4)-(4,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (3,4)-(4,0) = "\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "\n" │ │ └── @ StringNode (location: (4,0)-(5,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (4,0)-(5,0) = " a\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: " a\n" │ └── closing_loc: (5,0)-(6,0) = "EOF\n" ├── @ StringNode (location: (7,0)-(7,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (7,0)-(7,6) = "<<~EOF" │ ├── content_loc: (8,0)-(9,0) = " a\n" │ ├── closing_loc: (9,0)-(10,0) = "EOF\n" │ └── unescaped: "a\n" ├── @ InterpolatedStringNode (location: (11,0)-(11,6)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── opening_loc: (11,0)-(11,6) = "<<~EOF" │ ├── parts: (length: 3) │ │ ├── @ StringNode (location: (12,0)-(13,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (12,0)-(13,0) = "\ta\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "\ta\n" │ │ ├── @ StringNode (location: (13,0)-(14,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (13,0)-(14,0) = " b\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "b\n" │ │ └── @ StringNode (location: (14,0)-(15,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (14,0)-(15,0) = "\t\tc\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "\t\tc\n" │ └── closing_loc: (15,0)-(16,0) = "EOF\n" ├── @ InterpolatedStringNode (location: (17,0)-(17,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (17,0)-(17,6) = "<<~EOF" │ ├── parts: (length: 2) │ │ ├── @ EmbeddedStatementsNode (location: (18,2)-(18,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (18,2)-(18,4) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (18,4)-(18,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ IntegerNode (location: (18,4)-(18,5)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── closing_loc: (18,5)-(18,6) = "}" │ │ └── @ StringNode (location: (18,6)-(19,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (18,6)-(19,0) = " a\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: " a\n" │ └── closing_loc: (19,0)-(20,0) = "EOF\n" ├── @ InterpolatedStringNode (location: (21,0)-(21,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (21,0)-(21,6) = "<<~EOF" │ ├── parts: (length: 3) │ │ ├── @ StringNode (location: (22,0)-(22,4)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (22,0)-(22,4) = " a " │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a " │ │ ├── @ EmbeddedStatementsNode (location: (22,4)-(22,8)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (22,4)-(22,6) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (22,6)-(22,7)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ IntegerNode (location: (22,6)-(22,7)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── closing_loc: (22,7)-(22,8) = "}" │ │ └── @ StringNode (location: (22,8)-(23,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (22,8)-(23,0) = "\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "\n" │ └── closing_loc: (23,0)-(24,0) = "EOF\n" ├── @ InterpolatedStringNode (location: (25,0)-(25,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (25,0)-(25,6) = "<<~EOF" │ ├── parts: (length: 3) │ │ ├── @ StringNode (location: (26,0)-(27,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (26,0)-(27,0) = " a\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: " a\n" │ │ ├── @ EmbeddedStatementsNode (location: (27,1)-(27,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (27,1)-(27,3) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (27,3)-(27,4)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ IntegerNode (location: (27,3)-(27,4)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── closing_loc: (27,4)-(27,5) = "}" │ │ └── @ StringNode (location: (27,5)-(28,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (27,5)-(28,0) = "\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "\n" │ └── closing_loc: (28,0)-(29,0) = "EOF\n" ├── @ InterpolatedStringNode (location: (30,0)-(30,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (30,0)-(30,6) = "<<~EOF" │ ├── parts: (length: 3) │ │ ├── @ StringNode (location: (31,0)-(32,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (31,0)-(32,0) = " a\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a\n" │ │ ├── @ EmbeddedStatementsNode (location: (32,2)-(32,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (32,2)-(32,4) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (32,4)-(32,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ IntegerNode (location: (32,4)-(32,5)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── closing_loc: (32,5)-(32,6) = "}" │ │ └── @ StringNode (location: (32,6)-(33,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (32,6)-(33,0) = "\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "\n" │ └── closing_loc: (33,0)-(34,0) = "EOF\n" ├── @ InterpolatedStringNode (location: (35,0)-(35,6)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── opening_loc: (35,0)-(35,6) = "<<~EOF" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (36,0)-(37,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (36,0)-(37,0) = " a\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a\n" │ │ └── @ StringNode (location: (37,0)-(38,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (37,0)-(38,0) = " b\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "b\n" │ └── closing_loc: (38,0)-(39,0) = "EOF\n" ├── @ InterpolatedStringNode (location: (40,0)-(40,6)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── opening_loc: (40,0)-(40,6) = "<<~EOF" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (41,0)-(42,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (41,0)-(42,0) = " a\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a\n" │ │ └── @ StringNode (location: (42,0)-(43,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (42,0)-(43,0) = " b\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: " b\n" │ └── closing_loc: (43,0)-(44,0) = "EOF\n" ├── @ InterpolatedStringNode (location: (45,0)-(45,6)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── opening_loc: (45,0)-(45,6) = "<<~EOF" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (46,0)-(47,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (46,0)-(47,0) = "\t\t\ta\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "\ta\n" │ │ └── @ StringNode (location: (47,0)-(48,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (47,0)-(48,0) = "\t\tb\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "b\n" │ └── closing_loc: (48,0)-(49,0) = "EOF\n" ├── @ StringNode (location: (50,0)-(50,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (50,0)-(50,8) = "<<~'EOF'" │ ├── content_loc: (51,0)-(52,0) = " a \#{1}\n" │ ├── closing_loc: (52,0)-(53,0) = "EOF\n" │ └── unescaped: "a \#{1}\n" ├── @ InterpolatedStringNode (location: (54,0)-(54,6)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── opening_loc: (54,0)-(54,6) = "<<~EOF" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (55,0)-(56,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (55,0)-(56,0) = "\ta\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a\n" │ │ └── @ StringNode (location: (56,0)-(57,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (56,0)-(57,0) = "\t b\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: " b\n" │ └── closing_loc: (57,0)-(58,0) = "EOF\n" ├── @ InterpolatedStringNode (location: (59,0)-(59,6)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── opening_loc: (59,0)-(59,6) = "<<~EOF" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (60,0)-(61,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (60,0)-(61,0) = "\t a\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: " a\n" │ │ └── @ StringNode (location: (61,0)-(62,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (61,0)-(62,0) = "\tb\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "b\n" │ └── closing_loc: (62,0)-(63,0) = "EOF\n" ├── @ InterpolatedStringNode (location: (64,0)-(64,6)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── opening_loc: (64,0)-(64,6) = "<<~EOF" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (65,0)-(66,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (65,0)-(66,0) = " \ta\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a\n" │ │ └── @ StringNode (location: (66,0)-(67,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (66,0)-(67,0) = " b\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "b\n" │ └── closing_loc: (67,0)-(68,0) = "EOF\n" ├── @ InterpolatedStringNode (location: (69,0)-(69,6)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── opening_loc: (69,0)-(69,6) = "<<~EOF" │ ├── parts: (length: 3) │ │ ├── @ StringNode (location: (70,0)-(71,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (70,0)-(71,0) = " a\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a\n" │ │ ├── @ StringNode (location: (71,0)-(72,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (71,0)-(72,0) = "\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "\n" │ │ └── @ StringNode (location: (72,0)-(73,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (72,0)-(73,0) = " b\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "b\n" │ └── closing_loc: (73,0)-(74,0) = "EOF\n" ├── @ InterpolatedStringNode (location: (75,0)-(75,6)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── opening_loc: (75,0)-(75,6) = "<<~EOF" │ ├── parts: (length: 3) │ │ ├── @ StringNode (location: (76,0)-(77,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (76,0)-(77,0) = " a\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a\n" │ │ ├── @ StringNode (location: (77,0)-(78,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (77,0)-(78,0) = "\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "\n" │ │ └── @ StringNode (location: (78,0)-(79,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (78,0)-(79,0) = " b\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "b\n" │ └── closing_loc: (79,0)-(80,0) = "EOF\n" ├── @ InterpolatedStringNode (location: (81,0)-(81,6)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── opening_loc: (81,0)-(81,6) = "<<~EOF" │ ├── parts: (length: 5) │ │ ├── @ StringNode (location: (82,0)-(83,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (82,0)-(83,0) = " a\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a\n" │ │ ├── @ StringNode (location: (83,0)-(84,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (83,0)-(84,0) = "\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "\n" │ │ ├── @ StringNode (location: (84,0)-(85,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (84,0)-(85,0) = "\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "\n" │ │ ├── @ StringNode (location: (85,0)-(86,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (85,0)-(86,0) = "\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "\n" │ │ └── @ StringNode (location: (86,0)-(87,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (86,0)-(87,0) = " b\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "b\n" │ └── closing_loc: (87,0)-(88,0) = "EOF\n" ├── @ InterpolatedStringNode (location: (89,0)-(89,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (89,0)-(89,6) = "<<~EOF" │ ├── parts: (length: 3) │ │ ├── @ StringNode (location: (90,0)-(91,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (90,0)-(91,0) = "\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "\n" │ │ ├── @ EmbeddedStatementsNode (location: (91,2)-(91,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (91,2)-(91,4) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (91,4)-(91,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ IntegerNode (location: (91,4)-(91,5)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── closing_loc: (91,5)-(91,6) = "}" │ │ └── @ StringNode (location: (91,6)-(92,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (91,6)-(92,0) = "a\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a\n" │ └── closing_loc: (92,0)-(93,0) = " EOF\n" └── @ InterpolatedStringNode (location: (94,0)-(94,6)) - ├── flags: ∅ + ├── flags: newline ├── opening_loc: (94,0)-(94,6) = "<<~EOT" ├── parts: (length: 3) │ ├── @ EmbeddedStatementsNode (location: (95,2)-(95,6)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (95,2)-(95,4) = "\#{" │ │ ├── statements: │ │ │ @ StatementsNode (location: (95,4)-(95,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ IntegerNode (location: (95,4)-(95,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── closing_loc: (95,5)-(95,6) = "}" │ ├── @ StringNode (location: (95,6)-(96,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (95,6)-(96,0) = "\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "\n" │ └── @ StringNode (location: (96,0)-(97,0)) - │ ├── flags: frozen + │ ├── flags: static_literal, frozen │ ├── opening_loc: ∅ │ ├── content_loc: (96,0)-(97,0) = "\tb\n" │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/undef.txt b/test/prism/snapshots/undef.txt index e59ace92f5f715..7491fc4c953659 100644 --- a/test/prism/snapshots/undef.txt +++ b/test/prism/snapshots/undef.txt @@ -1,115 +1,129 @@ @ ProgramNode (location: (1,0)-(17,14)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(17,14)) + ├── flags: ∅ └── body: (length: 9) ├── @ UndefNode (location: (1,0)-(1,7)) + │ ├── flags: newline │ ├── names: (length: 1) │ │ └── @ SymbolNode (location: (1,6)-(1,7)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (1,6)-(1,7) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ └── keyword_loc: (1,0)-(1,5) = "undef" ├── @ UndefNode (location: (3,0)-(3,10)) + │ ├── flags: newline │ ├── names: (length: 2) │ │ ├── @ SymbolNode (location: (3,6)-(3,7)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (3,6)-(3,7) = "a" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a" │ │ └── @ SymbolNode (location: (3,9)-(3,10)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (3,9)-(3,10) = "b" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "b" │ └── keyword_loc: (3,0)-(3,5) = "undef" ├── @ UndefNode (location: (5,0)-(5,8)) + │ ├── flags: newline │ ├── names: (length: 1) │ │ └── @ SymbolNode (location: (5,6)-(5,8)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (5,6)-(5,8) = "if" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "if" │ └── keyword_loc: (5,0)-(5,5) = "undef" ├── @ UndefNode (location: (7,0)-(7,9)) + │ ├── flags: newline │ ├── names: (length: 1) │ │ └── @ SymbolNode (location: (7,6)-(7,9)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (7,6)-(7,9) = "<=>" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "<=>" │ └── keyword_loc: (7,0)-(7,5) = "undef" ├── @ UndefNode (location: (9,0)-(9,8)) + │ ├── flags: newline │ ├── names: (length: 1) │ │ └── @ SymbolNode (location: (9,6)-(9,8)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (9,6)-(9,7) = ":" │ │ ├── value_loc: (9,7)-(9,8) = "a" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "a" │ └── keyword_loc: (9,0)-(9,5) = "undef" ├── @ UndefNode (location: (11,0)-(11,16)) + │ ├── flags: newline │ ├── names: (length: 3) │ │ ├── @ SymbolNode (location: (11,6)-(11,8)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (11,6)-(11,7) = ":" │ │ │ ├── value_loc: (11,7)-(11,8) = "a" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a" │ │ ├── @ SymbolNode (location: (11,10)-(11,12)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (11,10)-(11,11) = ":" │ │ │ ├── value_loc: (11,11)-(11,12) = "b" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "b" │ │ └── @ SymbolNode (location: (11,14)-(11,16)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (11,14)-(11,15) = ":" │ │ ├── value_loc: (11,15)-(11,16) = "c" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "c" │ └── keyword_loc: (11,0)-(11,5) = "undef" ├── @ UndefNode (location: (13,0)-(13,12)) + │ ├── flags: newline │ ├── names: (length: 1) │ │ └── @ SymbolNode (location: (13,6)-(13,12)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (13,6)-(13,8) = ":'" │ │ ├── value_loc: (13,8)-(13,11) = "abc" │ │ ├── closing_loc: (13,11)-(13,12) = "'" │ │ └── unescaped: "abc" │ └── keyword_loc: (13,0)-(13,5) = "undef" ├── @ UndefNode (location: (15,0)-(15,16)) + │ ├── flags: newline │ ├── names: (length: 1) │ │ └── @ InterpolatedSymbolNode (location: (15,6)-(15,16)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (15,6)-(15,8) = ":\"" │ │ ├── parts: (length: 2) │ │ │ ├── @ StringNode (location: (15,8)-(15,11)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── content_loc: (15,8)-(15,11) = "abc" │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: "abc" │ │ │ └── @ EmbeddedStatementsNode (location: (15,11)-(15,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (15,11)-(15,13) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (15,13)-(15,14)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ IntegerNode (location: (15,13)-(15,14)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── closing_loc: (15,14)-(15,15) = "}" │ │ └── closing_loc: (15,15)-(15,16) = "\"" │ └── keyword_loc: (15,0)-(15,5) = "undef" └── @ UndefNode (location: (17,0)-(17,14)) + ├── flags: newline ├── names: (length: 1) │ └── @ SymbolNode (location: (17,6)-(17,14)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: ∅ │ ├── value_loc: (17,6)-(17,14) = "Constant" │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/unescaping.txt b/test/prism/snapshots/unescaping.txt index 456ef226d07f79..822fbe7d8f2936 100644 --- a/test/prism/snapshots/unescaping.txt +++ b/test/prism/snapshots/unescaping.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(7,7)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(7,7)) + ├── flags: ∅ └── body: (length: 4) ├── @ ArrayNode (location: (1,0)-(1,10)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 1) │ │ └── @ StringNode (location: (1,1)-(1,9)) │ │ ├── flags: ∅ @@ -15,19 +17,19 @@ │ ├── opening_loc: (1,0)-(1,1) = "[" │ └── closing_loc: (1,9)-(1,10) = "]" ├── @ RegularExpressionNode (location: (3,0)-(3,8)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (3,0)-(3,1) = "/" │ ├── content_loc: (3,1)-(3,7) = "\\c\#{1}" │ ├── closing_loc: (3,7)-(3,8) = "/" │ └── unescaped: "\\x03{1}" ├── @ StringNode (location: (5,0)-(5,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (5,0)-(5,1) = "\"" │ ├── content_loc: (5,1)-(5,7) = "\\c\#{1}" │ ├── closing_loc: (5,7)-(5,8) = "\"" │ └── unescaped: "\u0003{1}" └── @ StringNode (location: (7,0)-(7,7)) - ├── flags: ∅ + ├── flags: newline ├── opening_loc: (7,0)-(7,7) = "<<~HERE" ├── content_loc: (8,0)-(9,0) = " \\c\#{1}\n" ├── closing_loc: (9,0)-(10,0) = "HERE\n" diff --git a/test/prism/snapshots/unless.txt b/test/prism/snapshots/unless.txt index 6c4aaf66a5d7fc..130c0bb2b862bf 100644 --- a/test/prism/snapshots/unless.txt +++ b/test/prism/snapshots/unless.txt @@ -1,58 +1,71 @@ @ ProgramNode (location: (1,0)-(14,22)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(14,22)) + ├── flags: ∅ └── body: (length: 7) ├── @ UnlessNode (location: (1,0)-(1,19)) + │ ├── flags: newline │ ├── keyword_loc: (1,0)-(1,6) = "unless" │ ├── predicate: │ │ @ TrueNode (location: (1,7)-(1,11)) + │ │ └── flags: static_literal │ ├── then_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (1,13)-(1,14)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (1,13)-(1,14)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 1 │ ├── consequent: ∅ │ └── end_keyword_loc: (1,16)-(1,19) = "end" ├── @ UnlessNode (location: (3,0)-(4,12)) + │ ├── flags: newline │ ├── keyword_loc: (3,0)-(3,6) = "unless" │ ├── predicate: │ │ @ TrueNode (location: (3,7)-(3,11)) + │ │ └── flags: static_literal │ ├── then_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (4,0)-(4,1)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (4,0)-(4,1)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 1 │ ├── consequent: │ │ @ ElseNode (location: (4,2)-(4,12)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (4,2)-(4,6) = "else" │ │ ├── statements: │ │ │ @ StatementsNode (location: (4,7)-(4,8)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ IntegerNode (location: (4,7)-(4,8)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: newline, static_literal, decimal │ │ │ └── value: 2 │ │ └── end_keyword_loc: (4,9)-(4,12) = "end" │ └── end_keyword_loc: (4,9)-(4,12) = "end" ├── @ UnlessNode (location: (6,0)-(6,13)) + │ ├── flags: newline │ ├── keyword_loc: (6,2)-(6,8) = "unless" │ ├── predicate: │ │ @ TrueNode (location: (6,9)-(6,13)) + │ │ └── flags: static_literal │ ├── then_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (6,0)-(6,1)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (6,0)-(6,1)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 1 │ ├── consequent: ∅ │ └── end_keyword_loc: ∅ ├── @ CallNode (location: (8,0)-(8,25)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -62,20 +75,26 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (8,4)-(8,25)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (8,6)-(8,23)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ UnlessNode (location: (8,6)-(8,23)) + │ │ ├── flags: newline │ │ ├── keyword_loc: (8,12)-(8,18) = "unless" │ │ ├── predicate: │ │ │ @ TrueNode (location: (8,19)-(8,23)) + │ │ │ └── flags: static_literal │ │ ├── then_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (8,6)-(8,11)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ BreakNode (location: (8,6)-(8,11)) + │ │ │ ├── flags: newline │ │ │ ├── arguments: ∅ │ │ │ └── keyword_loc: (8,6)-(8,11) = "break" │ │ ├── consequent: ∅ @@ -83,7 +102,7 @@ │ ├── opening_loc: (8,4)-(8,5) = "{" │ └── closing_loc: (8,24)-(8,25) = "}" ├── @ CallNode (location: (10,0)-(10,24)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :tap @@ -93,20 +112,26 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (10,4)-(10,24)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (10,6)-(10,22)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ UnlessNode (location: (10,6)-(10,22)) + │ │ ├── flags: newline │ │ ├── keyword_loc: (10,11)-(10,17) = "unless" │ │ ├── predicate: │ │ │ @ TrueNode (location: (10,18)-(10,22)) + │ │ │ └── flags: static_literal │ │ ├── then_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (10,6)-(10,10)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ NextNode (location: (10,6)-(10,10)) + │ │ │ ├── flags: newline │ │ │ ├── arguments: ∅ │ │ │ └── keyword_loc: (10,6)-(10,10) = "next" │ │ ├── consequent: ∅ @@ -114,20 +139,24 @@ │ ├── opening_loc: (10,4)-(10,5) = "{" │ └── closing_loc: (10,23)-(10,24) = "}" ├── @ UnlessNode (location: (12,0)-(12,18)) + │ ├── flags: newline │ ├── keyword_loc: (12,7)-(12,13) = "unless" │ ├── predicate: │ │ @ TrueNode (location: (12,14)-(12,18)) + │ │ └── flags: static_literal │ ├── then_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (12,0)-(12,6)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ ReturnNode (location: (12,0)-(12,6)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── keyword_loc: (12,0)-(12,6) = "return" │ │ └── arguments: ∅ │ ├── consequent: ∅ │ └── end_keyword_loc: ∅ └── @ UnlessNode (location: (14,0)-(14,22)) + ├── flags: newline ├── keyword_loc: (14,11)-(14,17) = "unless" ├── predicate: │ @ CallNode (location: (14,18)-(14,22)) @@ -143,9 +172,10 @@ ├── then_keyword_loc: ∅ ├── statements: │ @ StatementsNode (location: (14,0)-(14,10)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (14,0)-(14,10)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -156,13 +186,13 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 2) │ │ ├── @ SymbolNode (location: (14,4)-(14,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (14,4)-(14,5) = ":" │ │ │ ├── value_loc: (14,5)-(14,6) = "a" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a" │ │ └── @ SymbolNode (location: (14,8)-(14,10)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (14,8)-(14,9) = ":" │ │ ├── value_loc: (14,9)-(14,10) = "b" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/unparser/corpus/literal/alias.txt b/test/prism/snapshots/unparser/corpus/literal/alias.txt index 18ddc86d4f4817..6ce892b54d0943 100644 --- a/test/prism/snapshots/unparser/corpus/literal/alias.txt +++ b/test/prism/snapshots/unparser/corpus/literal/alias.txt @@ -1,27 +1,33 @@ @ ProgramNode (location: (1,0)-(2,15)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(2,15)) + ├── flags: ∅ └── body: (length: 2) ├── @ AliasGlobalVariableNode (location: (1,0)-(1,15)) + │ ├── flags: newline │ ├── new_name: │ │ @ GlobalVariableReadNode (location: (1,6)-(1,10)) + │ │ ├── flags: ∅ │ │ └── name: :$foo │ ├── old_name: │ │ @ GlobalVariableReadNode (location: (1,11)-(1,15)) + │ │ ├── flags: ∅ │ │ └── name: :$bar │ └── keyword_loc: (1,0)-(1,5) = "alias" └── @ AliasMethodNode (location: (2,0)-(2,15)) + ├── flags: newline ├── new_name: │ @ SymbolNode (location: (2,6)-(2,10)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (2,6)-(2,7) = ":" │ ├── value_loc: (2,7)-(2,10) = "foo" │ ├── closing_loc: ∅ │ └── unescaped: "foo" ├── old_name: │ @ SymbolNode (location: (2,11)-(2,15)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (2,11)-(2,12) = ":" │ ├── value_loc: (2,12)-(2,15) = "bar" │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/unparser/corpus/literal/assignment.txt b/test/prism/snapshots/unparser/corpus/literal/assignment.txt index 7d3cc389c68d61..aa1d1037af90b0 100644 --- a/test/prism/snapshots/unparser/corpus/literal/assignment.txt +++ b/test/prism/snapshots/unparser/corpus/literal/assignment.txt @@ -1,21 +1,27 @@ @ ProgramNode (location: (1,0)-(51,17)) +├── flags: ∅ ├── locals: [:a, :b, :foo, :c, :x] └── statements: @ StatementsNode (location: (1,0)-(51,17)) + ├── flags: ∅ └── body: (length: 43) ├── @ GlobalVariableWriteNode (location: (1,0)-(1,6)) + │ ├── flags: newline │ ├── name: :$a │ ├── name_loc: (1,0)-(1,2) = "$a" │ ├── value: │ │ @ IntegerNode (location: (1,5)-(1,6)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (1,3)-(1,4) = "=" ├── @ MultiWriteNode (location: (2,0)-(2,17)) + │ ├── flags: newline │ ├── lefts: (length: 2) │ │ ├── @ GlobalVariableTargetNode (location: (2,1)-(2,3)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :$a │ │ └── @ GlobalVariableTargetNode (location: (2,5)-(2,7)) + │ │ ├── flags: ∅ │ │ └── name: :$b │ ├── rest: ∅ │ ├── rights: (length: 0) @@ -24,29 +30,34 @@ │ ├── operator_loc: (2,9)-(2,10) = "=" │ └── value: │ @ ArrayNode (location: (2,11)-(2,17)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 2) │ │ ├── @ IntegerNode (location: (2,12)-(2,13)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ IntegerNode (location: (2,15)-(2,16)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── opening_loc: (2,11)-(2,12) = "[" │ └── closing_loc: (2,16)-(2,17) = "]" ├── @ MultiWriteNode (location: (3,0)-(3,13)) + │ ├── flags: newline │ ├── lefts: (length: 2) │ │ ├── @ MultiTargetNode (location: (3,1)-(3,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 1) │ │ │ │ └── @ LocalVariableTargetNode (location: (3,2)-(3,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :a │ │ │ │ └── depth: 0 │ │ │ ├── rest: │ │ │ │ @ ImplicitRestNode (location: (3,3)-(3,4)) + │ │ │ │ └── flags: ∅ │ │ │ ├── rights: (length: 0) │ │ │ ├── lparen_loc: (3,1)-(3,2) = "(" │ │ │ └── rparen_loc: (3,4)-(3,5) = ")" │ │ └── @ LocalVariableTargetNode (location: (3,7)-(3,8)) + │ │ ├── flags: ∅ │ │ ├── name: :b │ │ └── depth: 0 │ ├── rest: ∅ @@ -56,15 +67,18 @@ │ ├── operator_loc: (3,10)-(3,11) = "=" │ └── value: │ @ IntegerNode (location: (3,12)-(3,13)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ MultiWriteNode (location: (4,0)-(4,9)) + │ ├── flags: newline │ ├── lefts: (length: 0) │ ├── rest: │ │ @ SplatNode (location: (4,1)-(4,3)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (4,1)-(4,2) = "*" │ │ └── expression: │ │ @ LocalVariableTargetNode (location: (4,2)-(4,3)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── rights: (length: 0) @@ -73,17 +87,20 @@ │ ├── operator_loc: (4,5)-(4,6) = "=" │ └── value: │ @ ArrayNode (location: (4,7)-(4,9)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 0) │ ├── opening_loc: (4,7)-(4,8) = "[" │ └── closing_loc: (4,8)-(4,9) = "]" ├── @ MultiWriteNode (location: (5,0)-(5,15)) + │ ├── flags: newline │ ├── lefts: (length: 0) │ ├── rest: │ │ @ SplatNode (location: (5,1)-(5,5)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (5,1)-(5,2) = "*" │ │ └── expression: │ │ @ LocalVariableTargetNode (location: (5,2)-(5,5)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ └── depth: 0 │ ├── rights: (length: 0) @@ -92,21 +109,24 @@ │ ├── operator_loc: (5,7)-(5,8) = "=" │ └── value: │ @ ArrayNode (location: (5,9)-(5,15)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 2) │ │ ├── @ IntegerNode (location: (5,10)-(5,11)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ IntegerNode (location: (5,13)-(5,14)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── opening_loc: (5,9)-(5,10) = "[" │ └── closing_loc: (5,14)-(5,15) = "]" ├── @ MultiWriteNode (location: (6,0)-(6,19)) + │ ├── flags: newline │ ├── lefts: (length: 2) │ │ ├── @ ClassVariableTargetNode (location: (6,1)-(6,4)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :@@a │ │ └── @ ClassVariableTargetNode (location: (6,6)-(6,9)) + │ │ ├── flags: ∅ │ │ └── name: :@@b │ ├── rest: ∅ │ ├── rights: (length: 0) @@ -115,21 +135,24 @@ │ ├── operator_loc: (6,11)-(6,12) = "=" │ └── value: │ @ ArrayNode (location: (6,13)-(6,19)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 2) │ │ ├── @ IntegerNode (location: (6,14)-(6,15)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ IntegerNode (location: (6,17)-(6,18)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── opening_loc: (6,13)-(6,14) = "[" │ └── closing_loc: (6,18)-(6,19) = "]" ├── @ MultiWriteNode (location: (7,0)-(7,17)) + │ ├── flags: newline │ ├── lefts: (length: 2) │ │ ├── @ InstanceVariableTargetNode (location: (7,1)-(7,3)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :@a │ │ └── @ InstanceVariableTargetNode (location: (7,5)-(7,7)) + │ │ ├── flags: ∅ │ │ └── name: :@b │ ├── rest: ∅ │ ├── rights: (length: 0) @@ -138,27 +161,32 @@ │ ├── operator_loc: (7,9)-(7,10) = "=" │ └── value: │ @ ArrayNode (location: (7,11)-(7,17)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 2) │ │ ├── @ IntegerNode (location: (7,12)-(7,13)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ IntegerNode (location: (7,15)-(7,16)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── opening_loc: (7,11)-(7,12) = "[" │ └── closing_loc: (7,16)-(7,17) = "]" ├── @ MultiWriteNode (location: (8,0)-(8,25)) + │ ├── flags: newline │ ├── lefts: (length: 2) │ │ ├── @ LocalVariableTargetNode (location: (8,1)-(8,2)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ └── @ MultiTargetNode (location: (8,4)-(8,10)) + │ │ ├── flags: ∅ │ │ ├── lefts: (length: 2) │ │ │ ├── @ LocalVariableTargetNode (location: (8,5)-(8,6)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :b │ │ │ │ └── depth: 0 │ │ │ └── @ LocalVariableTargetNode (location: (8,8)-(8,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :c │ │ │ └── depth: 0 │ │ ├── rest: ∅ @@ -175,28 +203,31 @@ │ ├── flags: ∅ │ ├── elements: (length: 2) │ │ ├── @ IntegerNode (location: (8,15)-(8,16)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ ArrayNode (location: (8,18)-(8,24)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── elements: (length: 2) │ │ │ ├── @ IntegerNode (location: (8,19)-(8,20)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ └── @ IntegerNode (location: (8,22)-(8,23)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 3 │ │ ├── opening_loc: (8,18)-(8,19) = "[" │ │ └── closing_loc: (8,23)-(8,24) = "]" │ ├── opening_loc: (8,14)-(8,15) = "[" │ └── closing_loc: (8,24)-(8,25) = "]" ├── @ MultiWriteNode (location: (9,0)-(9,15)) + │ ├── flags: newline │ ├── lefts: (length: 1) │ │ └── @ LocalVariableTargetNode (location: (9,1)-(9,2)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── rest: │ │ @ SplatNode (location: (9,4)-(9,5)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (9,4)-(9,5) = "*" │ │ └── expression: ∅ │ ├── rights: (length: 0) @@ -205,26 +236,30 @@ │ ├── operator_loc: (9,7)-(9,8) = "=" │ └── value: │ @ ArrayNode (location: (9,9)-(9,15)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 2) │ │ ├── @ IntegerNode (location: (9,10)-(9,11)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ IntegerNode (location: (9,13)-(9,14)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── opening_loc: (9,9)-(9,10) = "[" │ └── closing_loc: (9,14)-(9,15) = "]" ├── @ MultiWriteNode (location: (10,0)-(10,18)) + │ ├── flags: newline │ ├── lefts: (length: 1) │ │ └── @ LocalVariableTargetNode (location: (10,1)-(10,2)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── rest: │ │ @ SplatNode (location: (10,4)-(10,8)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (10,4)-(10,5) = "*" │ │ └── expression: │ │ @ LocalVariableTargetNode (location: (10,5)-(10,8)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ └── depth: 0 │ ├── rights: (length: 0) @@ -233,22 +268,25 @@ │ ├── operator_loc: (10,10)-(10,11) = "=" │ └── value: │ @ ArrayNode (location: (10,12)-(10,18)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 2) │ │ ├── @ IntegerNode (location: (10,13)-(10,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ IntegerNode (location: (10,16)-(10,17)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── opening_loc: (10,12)-(10,13) = "[" │ └── closing_loc: (10,17)-(10,18) = "]" ├── @ MultiWriteNode (location: (11,0)-(11,15)) + │ ├── flags: newline │ ├── lefts: (length: 2) │ │ ├── @ LocalVariableTargetNode (location: (11,1)-(11,2)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ └── @ LocalVariableTargetNode (location: (11,4)-(11,5)) + │ │ ├── flags: ∅ │ │ ├── name: :b │ │ └── depth: 0 │ ├── rest: ∅ @@ -258,22 +296,25 @@ │ ├── operator_loc: (11,7)-(11,8) = "=" │ └── value: │ @ ArrayNode (location: (11,9)-(11,15)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 2) │ │ ├── @ IntegerNode (location: (11,10)-(11,11)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ IntegerNode (location: (11,13)-(11,14)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── opening_loc: (11,9)-(11,10) = "[" │ └── closing_loc: (11,14)-(11,15) = "]" ├── @ MultiWriteNode (location: (12,0)-(12,12)) + │ ├── flags: newline │ ├── lefts: (length: 2) │ │ ├── @ LocalVariableTargetNode (location: (12,1)-(12,2)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ └── @ LocalVariableTargetNode (location: (12,4)-(12,5)) + │ │ ├── flags: ∅ │ │ ├── name: :b │ │ └── depth: 0 │ ├── rest: ∅ @@ -283,29 +324,36 @@ │ ├── operator_loc: (12,7)-(12,8) = "=" │ └── value: │ @ LocalVariableReadNode (location: (12,9)-(12,12)) + │ ├── flags: ∅ │ ├── name: :foo │ └── depth: 0 ├── @ MultiWriteNode (location: (13,0)-(13,10)) + │ ├── flags: newline │ ├── lefts: (length: 1) │ │ └── @ LocalVariableTargetNode (location: (13,1)-(13,2)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── rest: │ │ @ ImplicitRestNode (location: (13,2)-(13,3)) + │ │ └── flags: ∅ │ ├── rights: (length: 0) │ ├── lparen_loc: (13,0)-(13,1) = "(" │ ├── rparen_loc: (13,3)-(13,4) = ")" │ ├── operator_loc: (13,5)-(13,6) = "=" │ └── value: │ @ LocalVariableReadNode (location: (13,7)-(13,10)) + │ ├── flags: ∅ │ ├── name: :foo │ └── depth: 0 ├── @ MultiWriteNode (location: (14,0)-(14,23)) + │ ├── flags: newline │ ├── lefts: (length: 2) │ │ ├── @ CallTargetNode (location: (14,1)-(14,6)) │ │ │ ├── flags: ∅ │ │ │ ├── receiver: │ │ │ │ @ LocalVariableReadNode (location: (14,1)-(14,2)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :a │ │ │ │ └── depth: 0 │ │ │ ├── call_operator_loc: (14,2)-(14,3) = "." @@ -315,6 +363,7 @@ │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ LocalVariableReadNode (location: (14,8)-(14,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ ├── call_operator_loc: (14,9)-(14,10) = "." @@ -327,22 +376,24 @@ │ ├── operator_loc: (14,15)-(14,16) = "=" │ └── value: │ @ ArrayNode (location: (14,17)-(14,23)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 2) │ │ ├── @ IntegerNode (location: (14,18)-(14,19)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ IntegerNode (location: (14,21)-(14,22)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── opening_loc: (14,17)-(14,18) = "[" │ └── closing_loc: (14,22)-(14,23) = "]" ├── @ MultiWriteNode (location: (15,0)-(15,24)) + │ ├── flags: newline │ ├── lefts: (length: 2) │ │ ├── @ IndexTargetNode (location: (15,1)-(15,8)) │ │ │ ├── flags: attribute_write │ │ │ ├── receiver: │ │ │ │ @ LocalVariableReadNode (location: (15,1)-(15,2)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :a │ │ │ │ └── depth: 0 │ │ │ ├── opening_loc: (15,2)-(15,3) = "[" @@ -351,9 +402,11 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ SplatNode (location: (15,3)-(15,7)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (15,3)-(15,4) = "*" │ │ │ │ └── expression: │ │ │ │ @ LocalVariableReadNode (location: (15,4)-(15,7)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :foo │ │ │ │ └── depth: 0 │ │ │ ├── closing_loc: (15,7)-(15,8) = "]" @@ -362,6 +415,7 @@ │ │ ├── flags: attribute_write │ │ ├── receiver: │ │ │ @ LocalVariableReadNode (location: (15,10)-(15,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ ├── opening_loc: (15,11)-(15,12) = "[" @@ -370,7 +424,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (15,12)-(15,13)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── closing_loc: (15,13)-(15,14) = "]" │ │ └── block: ∅ @@ -381,22 +435,24 @@ │ ├── operator_loc: (15,16)-(15,17) = "=" │ └── value: │ @ ArrayNode (location: (15,18)-(15,24)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 2) │ │ ├── @ IntegerNode (location: (15,19)-(15,20)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ IntegerNode (location: (15,22)-(15,23)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── opening_loc: (15,18)-(15,19) = "[" │ └── closing_loc: (15,23)-(15,24) = "]" ├── @ MultiWriteNode (location: (16,0)-(16,21)) + │ ├── flags: newline │ ├── lefts: (length: 2) │ │ ├── @ IndexTargetNode (location: (16,1)-(16,5)) │ │ │ ├── flags: attribute_write │ │ │ ├── receiver: │ │ │ │ @ LocalVariableReadNode (location: (16,1)-(16,2)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :a │ │ │ │ └── depth: 0 │ │ │ ├── opening_loc: (16,2)-(16,3) = "[" @@ -405,7 +461,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ IntegerNode (location: (16,3)-(16,4)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 0 │ │ │ ├── closing_loc: (16,4)-(16,5) = "]" │ │ │ └── block: ∅ @@ -413,6 +469,7 @@ │ │ ├── flags: attribute_write │ │ ├── receiver: │ │ │ @ LocalVariableReadNode (location: (16,7)-(16,8)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ ├── opening_loc: (16,8)-(16,9) = "[" @@ -421,7 +478,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (16,9)-(16,10)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── closing_loc: (16,10)-(16,11) = "]" │ │ └── block: ∅ @@ -432,26 +489,29 @@ │ ├── operator_loc: (16,13)-(16,14) = "=" │ └── value: │ @ ArrayNode (location: (16,15)-(16,21)) - │ ├── flags: ∅ + │ ├── flags: static_literal │ ├── elements: (length: 2) │ │ ├── @ IntegerNode (location: (16,16)-(16,17)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ IntegerNode (location: (16,19)-(16,20)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── opening_loc: (16,15)-(16,16) = "[" │ └── closing_loc: (16,20)-(16,21) = "]" ├── @ MultiWriteNode (location: (17,0)-(17,12)) + │ ├── flags: newline │ ├── lefts: (length: 0) │ ├── rest: │ │ @ SplatNode (location: (17,1)-(17,7)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (17,1)-(17,2) = "*" │ │ └── expression: │ │ @ CallTargetNode (location: (17,2)-(17,7)) │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ LocalVariableReadNode (location: (17,2)-(17,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :c │ │ │ └── depth: 0 │ │ ├── call_operator_loc: (17,3)-(17,4) = "." @@ -463,11 +523,13 @@ │ ├── operator_loc: (17,9)-(17,10) = "=" │ └── value: │ @ IntegerNode (location: (17,11)-(17,12)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ ConstantPathWriteNode (location: (18,0)-(18,13)) + │ ├── flags: newline │ ├── target: │ │ @ ConstantPathNode (location: (18,0)-(18,5)) + │ │ ├── flags: ∅ │ │ ├── parent: ∅ │ │ ├── name: :Foo │ │ ├── delimiter_loc: (18,0)-(18,2) = "::" @@ -475,41 +537,49 @@ │ ├── operator_loc: (18,6)-(18,7) = "=" │ └── value: │ @ ConstantPathNode (location: (18,8)-(18,13)) + │ ├── flags: ∅ │ ├── parent: ∅ │ ├── name: :Bar │ ├── delimiter_loc: (18,8)-(18,10) = "::" │ └── name_loc: (18,10)-(18,13) = "Bar" ├── @ ClassVariableWriteNode (location: (19,0)-(19,7)) + │ ├── flags: newline │ ├── name: :@@a │ ├── name_loc: (19,0)-(19,3) = "@@a" │ ├── value: │ │ @ IntegerNode (location: (19,6)-(19,7)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (19,4)-(19,5) = "=" ├── @ InstanceVariableWriteNode (location: (20,0)-(20,6)) + │ ├── flags: newline │ ├── name: :@a │ ├── name_loc: (20,0)-(20,2) = "@a" │ ├── value: │ │ @ IntegerNode (location: (20,5)-(20,6)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (20,3)-(20,4) = "=" ├── @ ConstantWriteNode (location: (21,0)-(21,9)) + │ ├── flags: newline │ ├── name: :CONST │ ├── name_loc: (21,0)-(21,5) = "CONST" │ ├── value: │ │ @ IntegerNode (location: (21,8)-(21,9)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (21,6)-(21,7) = "=" ├── @ ConstantPathWriteNode (location: (22,0)-(22,23)) + │ ├── flags: newline │ ├── target: │ │ @ ConstantPathNode (location: (22,0)-(22,19)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ ConstantPathNode (location: (22,0)-(22,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── parent: │ │ │ │ @ ConstantReadNode (location: (22,0)-(22,4)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :Name │ │ │ ├── name: :Spaced │ │ │ ├── delimiter_loc: (22,4)-(22,6) = "::" @@ -520,23 +590,29 @@ │ ├── operator_loc: (22,20)-(22,21) = "=" │ └── value: │ @ IntegerNode (location: (22,22)-(22,23)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── @ LocalVariableWriteNode (location: (23,0)-(23,16)) + │ ├── flags: newline │ ├── name: :a │ ├── depth: 0 │ ├── name_loc: (23,0)-(23,1) = "a" │ ├── value: │ │ @ ParenthesesNode (location: (23,4)-(23,16)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (23,5)-(23,15)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ MultiWriteNode (location: (23,5)-(23,15)) + │ │ │ ├── flags: newline │ │ │ ├── lefts: (length: 2) │ │ │ │ ├── @ LocalVariableTargetNode (location: (23,6)-(23,7)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── name: :b │ │ │ │ │ └── depth: 0 │ │ │ │ └── @ LocalVariableTargetNode (location: (23,9)-(23,10)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :c │ │ │ │ └── depth: 0 │ │ │ ├── rest: ∅ @@ -546,21 +622,23 @@ │ │ │ ├── operator_loc: (23,12)-(23,13) = "=" │ │ │ └── value: │ │ │ @ IntegerNode (location: (23,14)-(23,15)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── opening_loc: (23,4)-(23,5) = "(" │ │ └── closing_loc: (23,15)-(23,16) = ")" │ └── operator_loc: (23,2)-(23,3) = "=" ├── @ LocalVariableWriteNode (location: (24,0)-(24,5)) + │ ├── flags: newline │ ├── name: :a │ ├── depth: 0 │ ├── name_loc: (24,0)-(24,1) = "a" │ ├── value: │ │ @ IntegerNode (location: (24,4)-(24,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (24,2)-(24,3) = "=" ├── @ LocalVariableWriteNode (location: (25,0)-(25,11)) + │ ├── flags: newline │ ├── name: :foo │ ├── depth: 0 │ ├── name_loc: (25,0)-(25,3) = "foo" @@ -577,9 +655,10 @@ │ │ └── block: ∅ │ └── operator_loc: (25,4)-(25,5) = "=" ├── @ CallNode (location: (26,0)-(26,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (26,0)-(26,3)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ └── depth: 0 │ ├── call_operator_loc: (26,3)-(26,4) = "." @@ -590,9 +669,10 @@ │ ├── closing_loc: (26,8)-(26,9) = ")" │ └── block: ∅ ├── @ CallNode (location: (27,0)-(27,13)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (27,0)-(27,3)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ └── depth: 0 │ ├── call_operator_loc: (27,3)-(27,4) = "." @@ -604,17 +684,18 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 2) │ │ ├── @ IntegerNode (location: (27,8)-(27,9)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ IntegerNode (location: (27,11)-(27,12)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: (27,12)-(27,13) = ")" │ └── block: ∅ ├── @ CallNode (location: (28,0)-(28,11)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (28,0)-(28,3)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ └── depth: 0 │ ├── call_operator_loc: (28,3)-(28,4) = "." @@ -626,12 +707,14 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ TrueNode (location: (28,7)-(28,11)) + │ │ └── flags: static_literal │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (29,0)-(29,19)) - │ ├── flags: attribute_write + │ ├── flags: newline, attribute_write │ ├── receiver: │ │ @ LocalVariableReadNode (location: (29,0)-(29,3)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ └── depth: 0 │ ├── call_operator_loc: ∅ @@ -643,6 +726,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 2) │ │ ├── @ SplatNode (location: (29,4)-(29,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (29,4)-(29,5) = "*" │ │ │ └── expression: │ │ │ @ CallNode (location: (29,5)-(29,10)) @@ -668,9 +752,10 @@ │ ├── closing_loc: (29,10)-(29,11) = "]" │ └── block: ∅ ├── @ CallNode (location: (30,0)-(30,17)) - │ ├── flags: attribute_write + │ ├── flags: newline, attribute_write │ ├── receiver: │ │ @ LocalVariableReadNode (location: (30,0)-(30,3)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ └── depth: 0 │ ├── call_operator_loc: ∅ @@ -682,14 +767,14 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 2) │ │ ├── @ RangeNode (location: (30,4)-(30,8)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: static_literal │ │ │ ├── left: │ │ │ │ @ IntegerNode (location: (30,4)-(30,5)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── right: │ │ │ │ @ IntegerNode (location: (30,7)-(30,8)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ └── operator_loc: (30,5)-(30,7) = ".." │ │ └── @ CallNode (location: (30,12)-(30,17)) @@ -705,9 +790,10 @@ │ ├── closing_loc: (30,8)-(30,9) = "]" │ └── block: ∅ ├── @ CallNode (location: (31,0)-(31,9)) - │ ├── flags: attribute_write + │ ├── flags: newline, attribute_write │ ├── receiver: │ │ @ LocalVariableReadNode (location: (31,0)-(31,3)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ └── depth: 0 │ ├── call_operator_loc: ∅ @@ -719,14 +805,15 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (31,8)-(31,9)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── closing_loc: (31,4)-(31,5) = "]" │ └── block: ∅ ├── @ CallNode (location: (32,0)-(32,17)) - │ ├── flags: attribute_write + │ ├── flags: newline, attribute_write │ ├── receiver: │ │ @ LocalVariableReadNode (location: (32,0)-(32,3)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ └── depth: 0 │ ├── call_operator_loc: ∅ @@ -738,9 +825,11 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 3) │ │ ├── @ LocalVariableReadNode (location: (32,4)-(32,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ ├── @ LocalVariableReadNode (location: (32,7)-(32,8)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :b │ │ │ └── depth: 0 │ │ └── @ CallNode (location: (32,12)-(32,17)) @@ -756,9 +845,10 @@ │ ├── closing_loc: (32,8)-(32,9) = "]" │ └── block: ∅ ├── @ CallNode (location: (33,0)-(33,18)) - │ ├── flags: attribute_write + │ ├── flags: newline, attribute_write │ ├── receiver: │ │ @ LocalVariableReadNode (location: (33,0)-(33,3)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ └── depth: 0 │ ├── call_operator_loc: ∅ @@ -792,6 +882,7 @@ │ ├── closing_loc: (33,9)-(33,10) = "]" │ └── block: ∅ ├── @ LocalVariableWriteNode (location: (34,0)-(34,7)) + │ ├── flags: newline │ ├── name: :x │ ├── depth: 0 │ ├── name_loc: (34,0)-(34,1) = "x" @@ -804,9 +895,10 @@ │ │ └── unescaped: "" │ └── operator_loc: (34,2)-(34,3) = "=" ├── @ CallNode (location: (35,0)-(35,7)) - │ ├── flags: attribute_write + │ ├── flags: newline, attribute_write │ ├── receiver: │ │ @ LocalVariableReadNode (location: (35,0)-(35,1)) + │ │ ├── flags: ∅ │ │ ├── name: :x │ │ └── depth: 0 │ ├── call_operator_loc: (35,1)-(35,2) = "." @@ -826,9 +918,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (36,0)-(36,12)) - │ ├── flags: attribute_write + │ ├── flags: newline, attribute_write │ ├── receiver: │ │ @ LocalVariableReadNode (location: (36,0)-(36,1)) + │ │ ├── flags: ∅ │ │ ├── name: :x │ │ └── depth: 0 │ ├── call_operator_loc: ∅ @@ -858,9 +951,10 @@ │ ├── closing_loc: (36,5)-(36,6) = "]" │ └── block: ∅ ├── @ IndexOrWriteNode (location: (37,0)-(37,14)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (37,0)-(37,1)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── call_operator_loc: ∅ @@ -890,6 +984,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ InstanceVariableOrWriteNode (location: (38,0)-(38,10)) + │ ├── flags: newline │ ├── name: :@a │ ├── name_loc: (38,0)-(38,2) = "@a" │ ├── operator_loc: (38,3)-(38,6) = "||=" @@ -901,6 +996,7 @@ │ ├── closing_loc: (38,9)-(38,10) = ")" │ └── unescaped: "" ├── @ LocalVariableWriteNode (location: (39,0)-(39,14)) + │ ├── flags: newline │ ├── name: :x │ ├── depth: 0 │ ├── name_loc: (39,0)-(39,1) = "x" @@ -910,17 +1006,18 @@ │ │ ├── opening_loc: (39,4)-(39,14) = "<<-HEREDOC" │ │ ├── parts: (length: 3) │ │ │ ├── @ StringNode (location: (40,0)-(40,2)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── content_loc: (40,0)-(40,2) = " " │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: " " │ │ │ ├── @ EmbeddedStatementsNode (location: (40,2)-(40,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── opening_loc: (40,2)-(40,4) = "\#{" │ │ │ │ ├── statements: ∅ │ │ │ │ └── closing_loc: (40,4)-(40,5) = "}" │ │ │ └── @ StringNode (location: (40,5)-(41,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (40,5)-(41,0) = "\n" │ │ │ ├── closing_loc: ∅ @@ -928,9 +1025,10 @@ │ │ └── closing_loc: (41,0)-(42,0) = "HEREDOC\n" │ └── operator_loc: (39,2)-(39,3) = "=" ├── @ CallNode (location: (42,0)-(42,14)) - │ ├── flags: attribute_write + │ ├── flags: newline, attribute_write │ ├── receiver: │ │ @ LocalVariableReadNode (location: (42,0)-(42,1)) + │ │ ├── flags: ∅ │ │ ├── name: :x │ │ └── depth: 0 │ ├── call_operator_loc: (42,1)-(42,2) = "." @@ -946,17 +1044,18 @@ │ │ ├── opening_loc: (42,4)-(42,14) = "<<-HEREDOC" │ │ ├── parts: (length: 3) │ │ │ ├── @ StringNode (location: (43,0)-(43,2)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── content_loc: (43,0)-(43,2) = " " │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: " " │ │ │ ├── @ EmbeddedStatementsNode (location: (43,2)-(43,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── opening_loc: (43,2)-(43,4) = "\#{" │ │ │ │ ├── statements: ∅ │ │ │ │ └── closing_loc: (43,4)-(43,5) = "}" │ │ │ └── @ StringNode (location: (43,5)-(44,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (43,5)-(44,0) = "\n" │ │ │ ├── closing_loc: ∅ @@ -965,9 +1064,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (45,0)-(45,16)) - │ ├── flags: attribute_write + │ ├── flags: newline, attribute_write │ ├── receiver: │ │ @ LocalVariableReadNode (location: (45,0)-(45,1)) + │ │ ├── flags: ∅ │ │ ├── name: :x │ │ └── depth: 0 │ ├── call_operator_loc: ∅ @@ -983,17 +1083,18 @@ │ │ ├── opening_loc: (45,6)-(45,16) = "<<-HEREDOC" │ │ ├── parts: (length: 3) │ │ │ ├── @ StringNode (location: (46,0)-(46,2)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── content_loc: (46,0)-(46,2) = " " │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: " " │ │ │ ├── @ EmbeddedStatementsNode (location: (46,2)-(46,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── opening_loc: (46,2)-(46,4) = "\#{" │ │ │ │ ├── statements: ∅ │ │ │ │ └── closing_loc: (46,4)-(46,5) = "}" │ │ │ └── @ StringNode (location: (46,5)-(47,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (46,5)-(47,0) = "\n" │ │ │ ├── closing_loc: ∅ @@ -1002,9 +1103,10 @@ │ ├── closing_loc: (45,2)-(45,3) = "]" │ └── block: ∅ ├── @ IndexOrWriteNode (location: (48,0)-(48,21)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (48,0)-(48,1)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── call_operator_loc: ∅ @@ -1018,17 +1120,18 @@ │ │ ├── opening_loc: (48,2)-(48,12) = "<<-HEREDOC" │ │ ├── parts: (length: 3) │ │ │ ├── @ StringNode (location: (49,0)-(49,2)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── content_loc: (49,0)-(49,2) = " " │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: " " │ │ │ ├── @ EmbeddedStatementsNode (location: (49,2)-(49,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── opening_loc: (49,2)-(49,4) = "\#{" │ │ │ │ ├── statements: ∅ │ │ │ │ └── closing_loc: (49,4)-(49,5) = "}" │ │ │ └── @ StringNode (location: (49,5)-(50,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (49,5)-(50,0) = "\n" │ │ │ ├── closing_loc: ∅ @@ -1049,6 +1152,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ └── @ InstanceVariableOrWriteNode (location: (51,0)-(51,17)) + ├── flags: newline ├── name: :@a ├── name_loc: (51,0)-(51,2) = "@a" ├── operator_loc: (51,3)-(51,6) = "||=" @@ -1058,17 +1162,18 @@ ├── opening_loc: (51,7)-(51,17) = "<<-HEREDOC" ├── parts: (length: 3) │ ├── @ StringNode (location: (52,0)-(52,2)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (52,0)-(52,2) = " " │ │ ├── closing_loc: ∅ │ │ └── unescaped: " " │ ├── @ EmbeddedStatementsNode (location: (52,2)-(52,5)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (52,2)-(52,4) = "\#{" │ │ ├── statements: ∅ │ │ └── closing_loc: (52,4)-(52,5) = "}" │ └── @ StringNode (location: (52,5)-(53,0)) - │ ├── flags: frozen + │ ├── flags: static_literal, frozen │ ├── opening_loc: ∅ │ ├── content_loc: (52,5)-(53,0) = "\n" │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/unparser/corpus/literal/block.txt b/test/prism/snapshots/unparser/corpus/literal/block.txt index b4c86d0b04be45..63cd76d683c10b 100644 --- a/test/prism/snapshots/unparser/corpus/literal/block.txt +++ b/test/prism/snapshots/unparser/corpus/literal/block.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(96,1)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(96,1)) + ├── flags: ∅ └── body: (length: 30) ├── @ CallNode (location: (1,0)-(2,1)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -14,13 +16,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (1,4)-(2,1)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: ∅ │ ├── opening_loc: (1,4)-(1,5) = "{" │ └── closing_loc: (2,0)-(2,1) = "}" ├── @ CallNode (location: (3,0)-(4,1)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -30,11 +33,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (3,4)-(4,1)) + │ ├── flags: ∅ │ ├── locals: [:a] │ ├── parameters: │ │ @ BlockParametersNode (location: (3,6)-(3,9)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (3,7)-(3,8)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (3,7)-(3,8)) │ │ │ │ ├── flags: ∅ @@ -52,7 +58,7 @@ │ ├── opening_loc: (3,4)-(3,5) = "{" │ └── closing_loc: (4,0)-(4,1) = "}" ├── @ CallNode (location: (5,0)-(6,1)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -62,11 +68,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (5,4)-(6,1)) + │ ├── flags: ∅ │ ├── locals: [:a] │ ├── parameters: │ │ @ BlockParametersNode (location: (5,6)-(5,10)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (5,7)-(5,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (5,7)-(5,8)) │ │ │ │ ├── flags: ∅ @@ -74,6 +83,7 @@ │ │ │ ├── optionals: (length: 0) │ │ │ ├── rest: │ │ │ │ @ ImplicitRestNode (location: (5,8)-(5,9)) + │ │ │ │ └── flags: ∅ │ │ │ ├── posts: (length: 0) │ │ │ ├── keywords: (length: 0) │ │ │ ├── keyword_rest: ∅ @@ -85,7 +95,7 @@ │ ├── opening_loc: (5,4)-(5,5) = "{" │ └── closing_loc: (6,0)-(6,1) = "}" ├── @ CallNode (location: (7,0)-(8,1)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -95,11 +105,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (7,4)-(8,1)) + │ ├── flags: ∅ │ ├── locals: [:a, :x] │ ├── parameters: │ │ @ BlockParametersNode (location: (7,6)-(7,13)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (7,7)-(7,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (7,7)-(7,8)) │ │ │ │ ├── flags: ∅ @@ -107,6 +120,7 @@ │ │ │ ├── optionals: (length: 0) │ │ │ ├── rest: │ │ │ │ @ ImplicitRestNode (location: (7,8)-(7,9)) + │ │ │ │ └── flags: ∅ │ │ │ ├── posts: (length: 0) │ │ │ ├── keywords: (length: 0) │ │ │ ├── keyword_rest: ∅ @@ -121,7 +135,7 @@ │ ├── opening_loc: (7,4)-(7,5) = "{" │ └── closing_loc: (8,0)-(8,1) = "}" ├── @ CallNode (location: (9,0)-(10,1)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -131,11 +145,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (9,4)-(10,1)) + │ ├── flags: ∅ │ ├── locals: [:a, :b] │ ├── parameters: │ │ @ BlockParametersNode (location: (9,6)-(9,12)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (9,7)-(9,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 2) │ │ │ │ ├── @ RequiredParameterNode (location: (9,7)-(9,8)) │ │ │ │ │ ├── flags: ∅ @@ -156,7 +173,7 @@ │ ├── opening_loc: (9,4)-(9,5) = "{" │ └── closing_loc: (10,0)-(10,1) = "}" ├── @ CallNode (location: (11,0)-(13,1)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -167,21 +184,24 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (11,4)-(11,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── closing_loc: (11,5)-(11,6) = ")" │ └── block: │ @ BlockNode (location: (11,7)-(13,1)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (12,2)-(12,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ NilNode (location: (12,2)-(12,5)) + │ │ └── flags: newline, static_literal │ ├── opening_loc: (11,7)-(11,8) = "{" │ └── closing_loc: (13,0)-(13,1) = "}" ├── @ CallNode (location: (14,0)-(16,1)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -191,11 +211,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (14,4)-(16,1)) + │ ├── flags: ∅ │ ├── locals: [:a, :b] │ ├── parameters: │ │ @ BlockParametersNode (location: (14,6)-(14,13)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (14,7)-(14,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (14,7)-(14,8)) │ │ │ │ ├── flags: ∅ @@ -216,12 +239,14 @@ │ │ └── closing_loc: (14,12)-(14,13) = "|" │ ├── body: │ │ @ StatementsNode (location: (15,2)-(15,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ NilNode (location: (15,2)-(15,5)) + │ │ └── flags: newline, static_literal │ ├── opening_loc: (14,4)-(14,5) = "{" │ └── closing_loc: (16,0)-(16,1) = "}" ├── @ CallNode (location: (17,0)-(19,1)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -231,11 +256,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (17,4)-(19,1)) + │ ├── flags: ∅ │ ├── locals: [:a] │ ├── parameters: │ │ @ BlockParametersNode (location: (17,6)-(17,12)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (17,7)-(17,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (17,7)-(17,8)) │ │ │ │ ├── flags: ∅ @@ -256,12 +284,14 @@ │ │ └── closing_loc: (17,11)-(17,12) = "|" │ ├── body: │ │ @ StatementsNode (location: (18,2)-(18,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ NilNode (location: (18,2)-(18,5)) + │ │ └── flags: newline, static_literal │ ├── opening_loc: (17,4)-(17,5) = "{" │ └── closing_loc: (19,0)-(19,1) = "}" ├── @ CallNode (location: (20,0)-(22,1)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -271,13 +301,15 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (20,4)-(22,1)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (21,2)-(21,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (21,2)-(21,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -289,7 +321,7 @@ │ ├── opening_loc: (20,4)-(20,5) = "{" │ └── closing_loc: (22,0)-(22,1) = "}" ├── @ CallNode (location: (23,0)-(25,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (23,0)-(23,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -309,13 +341,17 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (23,8)-(25,1)) + │ ├── flags: ∅ │ ├── locals: [:a, :b, :c] │ ├── parameters: │ │ @ BlockParametersNode (location: (23,10)-(23,21)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (23,11)-(23,20)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 2) │ │ │ │ ├── @ MultiTargetNode (location: (23,11)-(23,17)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── lefts: (length: 2) │ │ │ │ │ │ ├── @ RequiredParameterNode (location: (23,12)-(23,13)) │ │ │ │ │ │ │ ├── flags: ∅ @@ -341,9 +377,10 @@ │ │ └── closing_loc: (23,20)-(23,21) = "|" │ ├── body: │ │ @ StatementsNode (location: (24,2)-(24,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (24,2)-(24,3)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :d @@ -355,7 +392,7 @@ │ ├── opening_loc: (23,8)-(23,9) = "{" │ └── closing_loc: (25,0)-(25,1) = "}" ├── @ CallNode (location: (26,0)-(27,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (26,0)-(26,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -375,11 +412,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (26,8)-(27,1)) + │ ├── flags: ∅ │ ├── locals: [:a, :b] │ ├── parameters: │ │ @ BlockParametersNode (location: (26,10)-(26,17)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (26,11)-(26,13)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 0) │ │ │ ├── optionals: (length: 0) │ │ │ ├── rest: @@ -402,7 +442,7 @@ │ ├── opening_loc: (26,8)-(26,9) = "{" │ └── closing_loc: (27,0)-(27,1) = "}" ├── @ CallNode (location: (28,0)-(29,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (28,0)-(28,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -422,11 +462,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (28,8)-(29,1)) + │ ├── flags: ∅ │ ├── locals: [:a, :b] │ ├── parameters: │ │ @ BlockParametersNode (location: (28,10)-(28,16)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (28,11)-(28,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (28,11)-(28,12)) │ │ │ │ ├── flags: ∅ @@ -447,7 +490,7 @@ │ ├── opening_loc: (28,8)-(28,9) = "{" │ └── closing_loc: (29,0)-(29,1) = "}" ├── @ CallNode (location: (30,0)-(31,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (30,0)-(30,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -467,9 +510,11 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (30,8)-(31,1)) + │ ├── flags: ∅ │ ├── locals: [:a, :b] │ ├── parameters: │ │ @ BlockParametersNode (location: (30,10)-(30,18)) + │ │ ├── flags: ∅ │ │ ├── parameters: ∅ │ │ ├── locals: (length: 2) │ │ │ ├── @ BlockLocalVariableNode (location: (30,13)-(30,14)) @@ -484,7 +529,7 @@ │ ├── opening_loc: (30,8)-(30,9) = "{" │ └── closing_loc: (31,0)-(31,1) = "}" ├── @ CallNode (location: (32,0)-(34,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (32,0)-(32,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -504,11 +549,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (32,8)-(34,1)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: │ │ @ BlockParametersNode (location: (32,10)-(32,13)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (32,11)-(32,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 0) │ │ │ ├── optionals: (length: 0) │ │ │ ├── rest: @@ -526,9 +574,10 @@ │ │ └── closing_loc: (32,12)-(32,13) = "|" │ ├── body: │ │ @ StatementsNode (location: (33,2)-(33,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (33,2)-(33,3)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :d @@ -540,7 +589,7 @@ │ ├── opening_loc: (32,8)-(32,9) = "{" │ └── closing_loc: (34,0)-(34,1) = "}" ├── @ CallNode (location: (35,0)-(37,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (35,0)-(35,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -560,16 +609,21 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (35,8)-(37,1)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: │ │ @ BlockParametersNode (location: (35,10)-(35,15)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (35,11)-(35,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ MultiTargetNode (location: (35,11)-(35,14)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── lefts: (length: 0) │ │ │ │ ├── rest: │ │ │ │ │ @ SplatNode (location: (35,12)-(35,13)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── operator_loc: (35,12)-(35,13) = "*" │ │ │ │ │ └── expression: ∅ │ │ │ │ ├── rights: (length: 0) @@ -586,9 +640,10 @@ │ │ └── closing_loc: (35,14)-(35,15) = "|" │ ├── body: │ │ @ StatementsNode (location: (36,2)-(36,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (36,2)-(36,3)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :d @@ -600,7 +655,7 @@ │ ├── opening_loc: (35,8)-(35,9) = "{" │ └── closing_loc: (37,0)-(37,1) = "}" ├── @ CallNode (location: (38,0)-(40,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (38,0)-(38,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -620,18 +675,24 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (38,8)-(40,1)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: │ │ @ BlockParametersNode (location: (38,10)-(38,17)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (38,11)-(38,16)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ MultiTargetNode (location: (38,11)-(38,16)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── lefts: (length: 1) │ │ │ │ │ └── @ MultiTargetNode (location: (38,12)-(38,15)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── lefts: (length: 0) │ │ │ │ │ ├── rest: │ │ │ │ │ │ @ SplatNode (location: (38,13)-(38,14)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ ├── operator_loc: (38,13)-(38,14) = "*" │ │ │ │ │ │ └── expression: ∅ │ │ │ │ │ ├── rights: (length: 0) @@ -652,9 +713,10 @@ │ │ └── closing_loc: (38,16)-(38,17) = "|" │ ├── body: │ │ @ StatementsNode (location: (39,2)-(39,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (39,2)-(39,3)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :d @@ -666,7 +728,7 @@ │ ├── opening_loc: (38,8)-(38,9) = "{" │ └── closing_loc: (40,0)-(40,1) = "}" ├── @ CallNode (location: (41,0)-(43,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (41,0)-(41,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -686,21 +748,27 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (41,8)-(43,1)) + │ ├── flags: ∅ │ ├── locals: [:a] │ ├── parameters: │ │ @ BlockParametersNode (location: (41,10)-(41,20)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (41,11)-(41,19)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ MultiTargetNode (location: (41,11)-(41,19)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── lefts: (length: 2) │ │ │ │ │ ├── @ RequiredParameterNode (location: (41,12)-(41,13)) │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ └── name: :a │ │ │ │ │ └── @ MultiTargetNode (location: (41,15)-(41,18)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── lefts: (length: 0) │ │ │ │ │ ├── rest: │ │ │ │ │ │ @ SplatNode (location: (41,16)-(41,17)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ ├── operator_loc: (41,16)-(41,17) = "*" │ │ │ │ │ │ └── expression: ∅ │ │ │ │ │ ├── rights: (length: 0) @@ -721,9 +789,10 @@ │ │ └── closing_loc: (41,19)-(41,20) = "|" │ ├── body: │ │ @ StatementsNode (location: (42,2)-(42,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (42,2)-(42,3)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :d @@ -735,7 +804,7 @@ │ ├── opening_loc: (41,8)-(41,9) = "{" │ └── closing_loc: (43,0)-(43,1) = "}" ├── @ CallNode (location: (44,0)-(46,1)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (44,0)-(44,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -755,13 +824,17 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (44,8)-(46,1)) + │ ├── flags: ∅ │ ├── locals: [:a, :b] │ ├── parameters: │ │ @ BlockParametersNode (location: (44,10)-(44,18)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (44,11)-(44,17)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ MultiTargetNode (location: (44,11)-(44,17)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── lefts: (length: 2) │ │ │ │ │ ├── @ RequiredParameterNode (location: (44,12)-(44,13)) │ │ │ │ │ │ ├── flags: ∅ @@ -784,9 +857,10 @@ │ │ └── closing_loc: (44,17)-(44,18) = "|" │ ├── body: │ │ @ StatementsNode (location: (45,2)-(45,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (45,2)-(45,3)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :d @@ -798,7 +872,7 @@ │ ├── opening_loc: (44,8)-(44,9) = "{" │ └── closing_loc: (46,0)-(46,1) = "}" ├── @ CallNode (location: (47,0)-(48,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (47,0)-(48,1)) │ │ ├── flags: ∅ @@ -821,6 +895,7 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (47,8)-(48,1)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── parameters: ∅ │ │ ├── body: ∅ @@ -834,7 +909,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (49,0)-(51,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :m @@ -844,21 +919,26 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (49,2)-(51,3)) + │ ├── flags: ∅ │ ├── locals: [:e] │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (49,2)-(51,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (50,0)-(50,21)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (50,0)-(50,6) = "rescue" │ │ │ ├── exceptions: (length: 1) │ │ │ │ └── @ ConstantReadNode (location: (50,7)-(50,16)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :Exception │ │ │ ├── operator_loc: (50,17)-(50,19) = "=>" │ │ │ ├── reference: │ │ │ │ @ LocalVariableTargetNode (location: (50,20)-(50,21)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :e │ │ │ │ └── depth: 0 │ │ │ ├── statements: ∅ @@ -869,7 +949,7 @@ │ ├── opening_loc: (49,2)-(49,4) = "do" │ └── closing_loc: (51,0)-(51,3) = "end" ├── @ CallNode (location: (52,0)-(56,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :m @@ -879,16 +959,19 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (52,2)-(56,3)) + │ ├── flags: ∅ │ ├── locals: [:bar] │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (52,2)-(56,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (53,2)-(53,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (53,2)-(53,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :foo @@ -899,19 +982,24 @@ │ │ │ └── block: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (54,0)-(55,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (54,0)-(54,6) = "rescue" │ │ │ ├── exceptions: (length: 1) │ │ │ │ └── @ ConstantReadNode (location: (54,7)-(54,16)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :Exception │ │ │ ├── operator_loc: (54,17)-(54,19) = "=>" │ │ │ ├── reference: │ │ │ │ @ LocalVariableTargetNode (location: (54,20)-(54,23)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :bar │ │ │ │ └── depth: 0 │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (55,2)-(55,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ LocalVariableReadNode (location: (55,2)-(55,5)) + │ │ │ │ ├── flags: newline │ │ │ │ ├── name: :bar │ │ │ │ └── depth: 0 │ │ │ └── consequent: ∅ @@ -921,7 +1009,7 @@ │ ├── opening_loc: (52,2)-(52,4) = "do" │ └── closing_loc: (56,0)-(56,3) = "end" ├── @ CallNode (location: (57,0)-(61,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :m @@ -931,16 +1019,19 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (57,2)-(61,3)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (57,2)-(61,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (58,2)-(58,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (58,2)-(58,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :bar @@ -951,11 +1042,14 @@ │ │ │ └── block: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (59,0)-(60,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (59,0)-(59,6) = "rescue" │ │ │ ├── exceptions: (length: 2) │ │ │ │ ├── @ ConstantReadNode (location: (59,7)-(59,16)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── name: :SomeError │ │ │ │ └── @ SplatNode (location: (59,18)-(59,22)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (59,18)-(59,19) = "*" │ │ │ │ └── expression: │ │ │ │ @ CallNode (location: (59,19)-(59,22)) @@ -972,9 +1066,10 @@ │ │ │ ├── reference: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (60,2)-(60,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (60,2)-(60,5)) - │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ ├── receiver: ∅ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :baz @@ -990,7 +1085,7 @@ │ ├── opening_loc: (57,2)-(57,4) = "do" │ └── closing_loc: (61,0)-(61,3) = "end" ├── @ CallNode (location: (62,0)-(66,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :m @@ -1000,16 +1095,19 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (62,2)-(66,3)) + │ ├── flags: ∅ │ ├── locals: [:exception] │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (62,2)-(66,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (63,2)-(63,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (63,2)-(63,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :bar @@ -1020,11 +1118,14 @@ │ │ │ └── block: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (64,0)-(65,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (64,0)-(64,6) = "rescue" │ │ │ ├── exceptions: (length: 2) │ │ │ │ ├── @ ConstantReadNode (location: (64,7)-(64,16)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── name: :SomeError │ │ │ │ └── @ SplatNode (location: (64,18)-(64,22)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (64,18)-(64,19) = "*" │ │ │ │ └── expression: │ │ │ │ @ CallNode (location: (64,19)-(64,22)) @@ -1040,13 +1141,15 @@ │ │ │ ├── operator_loc: (64,23)-(64,25) = "=>" │ │ │ ├── reference: │ │ │ │ @ LocalVariableTargetNode (location: (64,26)-(64,35)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :exception │ │ │ │ └── depth: 0 │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (65,2)-(65,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (65,2)-(65,5)) - │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ ├── receiver: ∅ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :baz @@ -1062,7 +1165,7 @@ │ ├── opening_loc: (62,2)-(62,4) = "do" │ └── closing_loc: (66,0)-(66,3) = "end" ├── @ CallNode (location: (67,0)-(71,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :m @@ -1072,16 +1175,19 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (67,2)-(71,3)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (67,2)-(71,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (68,2)-(68,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (68,2)-(68,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :bar @@ -1092,9 +1198,11 @@ │ │ │ └── block: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (69,0)-(70,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (69,0)-(69,6) = "rescue" │ │ │ ├── exceptions: (length: 1) │ │ │ │ └── @ SplatNode (location: (69,7)-(69,11)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (69,7)-(69,8) = "*" │ │ │ │ └── expression: │ │ │ │ @ CallNode (location: (69,8)-(69,11)) @@ -1111,9 +1219,10 @@ │ │ │ ├── reference: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (70,2)-(70,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (70,2)-(70,5)) - │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ ├── receiver: ∅ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :baz @@ -1129,7 +1238,7 @@ │ ├── opening_loc: (67,2)-(67,4) = "do" │ └── closing_loc: (71,0)-(71,3) = "end" ├── @ CallNode (location: (72,0)-(75,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :m @@ -1139,16 +1248,19 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (72,2)-(75,3)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (72,2)-(75,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (73,2)-(73,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (73,2)-(73,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :bar @@ -1159,9 +1271,11 @@ │ │ │ └── block: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (74,0)-(74,16)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (74,0)-(74,6) = "rescue" │ │ │ ├── exceptions: (length: 1) │ │ │ │ └── @ ConstantReadNode (location: (74,7)-(74,16)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :LoadError │ │ │ ├── operator_loc: ∅ │ │ │ ├── reference: ∅ @@ -1173,7 +1287,7 @@ │ ├── opening_loc: (72,2)-(72,4) = "do" │ └── closing_loc: (75,0)-(75,3) = "end" ├── @ CallNode (location: (76,0)-(81,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :m @@ -1183,16 +1297,19 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (76,2)-(81,3)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (76,2)-(81,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (77,2)-(77,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (77,2)-(77,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :bar @@ -1203,6 +1320,7 @@ │ │ │ └── block: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (78,0)-(78,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (78,0)-(78,6) = "rescue" │ │ │ ├── exceptions: (length: 0) │ │ │ ├── operator_loc: ∅ @@ -1211,12 +1329,14 @@ │ │ │ └── consequent: ∅ │ │ ├── else_clause: │ │ │ @ ElseNode (location: (79,0)-(81,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── else_keyword_loc: (79,0)-(79,4) = "else" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (80,2)-(80,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (80,2)-(80,5)) - │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ ├── receiver: ∅ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :baz @@ -1231,7 +1351,7 @@ │ ├── opening_loc: (76,2)-(76,4) = "do" │ └── closing_loc: (81,0)-(81,3) = "end" ├── @ CallNode (location: (82,0)-(86,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :m @@ -1241,16 +1361,19 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (82,2)-(86,3)) + │ ├── flags: ∅ │ ├── locals: [:exception] │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (82,2)-(86,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (83,2)-(83,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (83,2)-(83,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :bar @@ -1261,9 +1384,11 @@ │ │ │ └── block: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (84,0)-(85,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (84,0)-(84,6) = "rescue" │ │ │ ├── exceptions: (length: 1) │ │ │ │ └── @ SplatNode (location: (84,7)-(84,11)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (84,7)-(84,8) = "*" │ │ │ │ └── expression: │ │ │ │ @ CallNode (location: (84,8)-(84,11)) @@ -1279,13 +1404,15 @@ │ │ │ ├── operator_loc: (84,12)-(84,14) = "=>" │ │ │ ├── reference: │ │ │ │ @ LocalVariableTargetNode (location: (84,15)-(84,24)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :exception │ │ │ │ └── depth: 0 │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (85,2)-(85,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (85,2)-(85,5)) - │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ ├── receiver: ∅ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :baz @@ -1301,7 +1428,7 @@ │ ├── opening_loc: (82,2)-(82,4) = "do" │ └── closing_loc: (86,0)-(86,3) = "end" ├── @ CallNode (location: (87,0)-(89,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :m @@ -1311,16 +1438,19 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (87,2)-(89,3)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (87,2)-(89,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: ∅ │ │ ├── rescue_clause: ∅ │ │ ├── else_clause: ∅ │ │ ├── ensure_clause: │ │ │ @ EnsureNode (location: (88,0)-(89,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── ensure_keyword_loc: (88,0)-(88,6) = "ensure" │ │ │ ├── statements: ∅ │ │ │ └── end_keyword_loc: (89,0)-(89,3) = "end" @@ -1328,7 +1458,7 @@ │ ├── opening_loc: (87,2)-(87,4) = "do" │ └── closing_loc: (89,0)-(89,3) = "end" ├── @ CallNode (location: (90,0)-(93,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :m @@ -1338,14 +1468,17 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (90,2)-(93,3)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (90,2)-(93,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (91,0)-(91,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (91,0)-(91,6) = "rescue" │ │ │ ├── exceptions: (length: 0) │ │ │ ├── operator_loc: ∅ @@ -1355,6 +1488,7 @@ │ │ ├── else_clause: ∅ │ │ ├── ensure_clause: │ │ │ @ EnsureNode (location: (92,0)-(93,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── ensure_keyword_loc: (92,0)-(92,6) = "ensure" │ │ │ ├── statements: ∅ │ │ │ └── end_keyword_loc: (93,0)-(93,3) = "end" @@ -1362,7 +1496,7 @@ │ ├── opening_loc: (90,2)-(90,4) = "do" │ └── closing_loc: (93,0)-(93,3) = "end" └── @ CallNode (location: (94,0)-(96,1)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :bar @@ -1372,17 +1506,21 @@ ├── closing_loc: ∅ └── block: @ BlockNode (location: (94,4)-(96,1)) + ├── flags: ∅ ├── locals: [:_1, :_2] ├── parameters: │ @ NumberedParametersNode (location: (94,4)-(96,1)) + │ ├── flags: ∅ │ └── maximum: 2 ├── body: │ @ StatementsNode (location: (95,2)-(95,9)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (95,2)-(95,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (95,2)-(95,4)) + │ │ ├── flags: ∅ │ │ ├── name: :_1 │ │ └── depth: 0 │ ├── call_operator_loc: ∅ @@ -1394,6 +1532,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ LocalVariableReadNode (location: (95,7)-(95,9)) + │ │ ├── flags: ∅ │ │ ├── name: :_2 │ │ └── depth: 0 │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/unparser/corpus/literal/case.txt b/test/prism/snapshots/unparser/corpus/literal/case.txt index 509caa55c8b1ba..f7b279dffcfa6a 100644 --- a/test/prism/snapshots/unparser/corpus/literal/case.txt +++ b/test/prism/snapshots/unparser/corpus/literal/case.txt @@ -1,12 +1,16 @@ @ ProgramNode (location: (1,0)-(37,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(37,3)) + ├── flags: ∅ └── body: (length: 8) ├── @ CaseNode (location: (1,0)-(6,3)) + │ ├── flags: newline │ ├── predicate: ∅ │ ├── conditions: (length: 2) │ │ ├── @ WhenNode (location: (2,0)-(3,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (2,0)-(2,4) = "when" │ │ │ ├── conditions: (length: 1) │ │ │ │ └── @ CallNode (location: (2,5)-(2,8)) @@ -22,9 +26,10 @@ │ │ │ ├── then_keyword_loc: ∅ │ │ │ └── statements: │ │ │ @ StatementsNode (location: (3,2)-(3,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (3,2)-(3,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :baz @@ -34,6 +39,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ └── @ WhenNode (location: (4,0)-(5,5)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (4,0)-(4,4) = "when" │ │ ├── conditions: (length: 1) │ │ │ └── @ CallNode (location: (4,5)-(4,8)) @@ -49,9 +55,10 @@ │ │ ├── then_keyword_loc: ∅ │ │ └── statements: │ │ @ StatementsNode (location: (5,2)-(5,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (5,2)-(5,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -64,6 +71,7 @@ │ ├── case_keyword_loc: (1,0)-(1,4) = "case" │ └── end_keyword_loc: (6,0)-(6,3) = "end" ├── @ CaseNode (location: (7,0)-(11,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (7,5)-(7,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -77,6 +85,7 @@ │ │ └── block: ∅ │ ├── conditions: (length: 2) │ │ ├── @ WhenNode (location: (8,0)-(8,8)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (8,0)-(8,4) = "when" │ │ │ ├── conditions: (length: 1) │ │ │ │ └── @ CallNode (location: (8,5)-(8,8)) @@ -92,6 +101,7 @@ │ │ │ ├── then_keyword_loc: ∅ │ │ │ └── statements: ∅ │ │ └── @ WhenNode (location: (9,0)-(10,5)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (9,0)-(9,4) = "when" │ │ ├── conditions: (length: 1) │ │ │ └── @ CallNode (location: (9,5)-(9,8)) @@ -107,9 +117,10 @@ │ │ ├── then_keyword_loc: ∅ │ │ └── statements: │ │ @ StatementsNode (location: (10,2)-(10,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (10,2)-(10,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -122,6 +133,7 @@ │ ├── case_keyword_loc: (7,0)-(7,4) = "case" │ └── end_keyword_loc: (11,0)-(11,3) = "end" ├── @ CaseNode (location: (12,0)-(17,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (12,5)-(12,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -135,6 +147,7 @@ │ │ └── block: ∅ │ ├── conditions: (length: 2) │ │ ├── @ WhenNode (location: (13,0)-(14,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (13,0)-(13,4) = "when" │ │ │ ├── conditions: (length: 1) │ │ │ │ └── @ CallNode (location: (13,5)-(13,8)) @@ -150,9 +163,10 @@ │ │ │ ├── then_keyword_loc: ∅ │ │ │ └── statements: │ │ │ @ StatementsNode (location: (14,2)-(14,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (14,2)-(14,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :baz @@ -162,6 +176,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ └── @ WhenNode (location: (15,0)-(16,5)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (15,0)-(15,4) = "when" │ │ ├── conditions: (length: 1) │ │ │ └── @ CallNode (location: (15,5)-(15,8)) @@ -177,9 +192,10 @@ │ │ ├── then_keyword_loc: ∅ │ │ └── statements: │ │ @ StatementsNode (location: (16,2)-(16,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (16,2)-(16,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -192,6 +208,7 @@ │ ├── case_keyword_loc: (12,0)-(12,4) = "case" │ └── end_keyword_loc: (17,0)-(17,3) = "end" ├── @ CaseNode (location: (18,0)-(21,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (18,5)-(18,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -205,6 +222,7 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ WhenNode (location: (19,0)-(20,8)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (19,0)-(19,4) = "when" │ │ ├── conditions: (length: 2) │ │ │ ├── @ CallNode (location: (19,5)-(19,8)) @@ -230,9 +248,10 @@ │ │ ├── then_keyword_loc: ∅ │ │ └── statements: │ │ @ StatementsNode (location: (20,2)-(20,8)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (20,2)-(20,8)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (20,2)-(20,3) = ":" │ │ ├── value_loc: (20,3)-(20,8) = "other" │ │ ├── closing_loc: ∅ @@ -241,6 +260,7 @@ │ ├── case_keyword_loc: (18,0)-(18,4) = "case" │ └── end_keyword_loc: (21,0)-(21,3) = "end" ├── @ CaseNode (location: (22,0)-(25,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (22,5)-(22,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -254,9 +274,11 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ WhenNode (location: (23,0)-(24,8)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (23,0)-(23,4) = "when" │ │ ├── conditions: (length: 1) │ │ │ └── @ SplatNode (location: (23,5)-(23,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (23,5)-(23,6) = "*" │ │ │ └── expression: │ │ │ @ CallNode (location: (23,6)-(23,9)) @@ -272,9 +294,10 @@ │ │ ├── then_keyword_loc: ∅ │ │ └── statements: │ │ @ StatementsNode (location: (24,2)-(24,8)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (24,2)-(24,8)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (24,2)-(24,3) = ":" │ │ ├── value_loc: (24,3)-(24,8) = "value" │ │ ├── closing_loc: ∅ @@ -283,6 +306,7 @@ │ ├── case_keyword_loc: (22,0)-(22,4) = "case" │ └── end_keyword_loc: (25,0)-(25,3) = "end" ├── @ CaseNode (location: (26,0)-(31,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (26,5)-(26,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -296,6 +320,7 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ WhenNode (location: (27,0)-(28,5)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (27,0)-(27,4) = "when" │ │ ├── conditions: (length: 1) │ │ │ └── @ CallNode (location: (27,5)-(27,8)) @@ -311,9 +336,10 @@ │ │ ├── then_keyword_loc: ∅ │ │ └── statements: │ │ @ StatementsNode (location: (28,2)-(28,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (28,2)-(28,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :baz @@ -324,12 +350,14 @@ │ │ └── block: ∅ │ ├── consequent: │ │ @ ElseNode (location: (29,0)-(31,3)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (29,0)-(29,4) = "else" │ │ ├── statements: │ │ │ @ StatementsNode (location: (30,2)-(30,6)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ SymbolNode (location: (30,2)-(30,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (30,2)-(30,3) = ":" │ │ │ ├── value_loc: (30,3)-(30,6) = "foo" │ │ │ ├── closing_loc: ∅ @@ -338,6 +366,7 @@ │ ├── case_keyword_loc: (26,0)-(26,4) = "case" │ └── end_keyword_loc: (31,0)-(31,3) = "end" ├── @ CaseNode (location: (32,0)-(34,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (32,5)-(32,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -351,9 +380,11 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ WhenNode (location: (33,0)-(33,15)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (33,0)-(33,4) = "when" │ │ ├── conditions: (length: 1) │ │ │ └── @ SplatNode (location: (33,5)-(33,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (33,5)-(33,6) = "*" │ │ │ └── expression: │ │ │ @ CallNode (location: (33,6)-(33,15)) @@ -395,6 +426,7 @@ │ ├── case_keyword_loc: (32,0)-(32,4) = "case" │ └── end_keyword_loc: (34,0)-(34,3) = "end" └── @ CaseNode (location: (35,0)-(37,3)) + ├── flags: newline ├── predicate: │ @ CallNode (location: (35,5)-(35,8)) │ ├── flags: variable_call, ignore_visibility @@ -408,9 +440,11 @@ │ └── block: ∅ ├── conditions: (length: 1) │ └── @ WhenNode (location: (36,0)-(36,15)) + │ ├── flags: ∅ │ ├── keyword_loc: (36,0)-(36,4) = "when" │ ├── conditions: (length: 1) │ │ └── @ SplatNode (location: (36,5)-(36,15)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (36,5)-(36,6) = "*" │ │ └── expression: │ │ @ CallNode (location: (36,6)-(36,15)) @@ -435,7 +469,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (36,14)-(36,15)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ diff --git a/test/prism/snapshots/unparser/corpus/literal/class.txt b/test/prism/snapshots/unparser/corpus/literal/class.txt index 53068883987b43..add60ce30560a2 100644 --- a/test/prism/snapshots/unparser/corpus/literal/class.txt +++ b/test/prism/snapshots/unparser/corpus/literal/class.txt @@ -1,13 +1,17 @@ @ ProgramNode (location: (1,0)-(35,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(35,3)) + ├── flags: ∅ └── body: (length: 10) ├── @ ClassNode (location: (1,0)-(2,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (1,0)-(1,5) = "class" │ ├── constant_path: │ │ @ ConstantReadNode (location: (1,6)-(1,7)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── inheritance_operator_loc: ∅ │ ├── superclass: ∅ @@ -15,6 +19,7 @@ │ ├── end_keyword_loc: (2,0)-(2,3) = "end" │ └── name: :A ├── @ SingletonClassNode (location: (4,0)-(5,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (4,0)-(4,5) = "class" │ ├── operator_loc: (4,6)-(4,8) = "<<" @@ -32,6 +37,7 @@ │ ├── body: ∅ │ └── end_keyword_loc: (5,0)-(5,3) = "end" ├── @ SingletonClassNode (location: (7,0)-(9,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (7,0)-(7,5) = "class" │ ├── operator_loc: (7,6)-(7,8) = "<<" @@ -48,9 +54,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (8,2)-(8,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (8,2)-(8,3)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :b @@ -61,12 +68,15 @@ │ │ └── block: ∅ │ └── end_keyword_loc: (9,0)-(9,3) = "end" ├── @ ClassNode (location: (11,0)-(12,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (11,0)-(11,5) = "class" │ ├── constant_path: │ │ @ ConstantPathNode (location: (11,6)-(11,10)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (11,6)-(11,7)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :A │ │ ├── name: :B │ │ ├── delimiter_loc: (11,7)-(11,9) = "::" @@ -77,14 +87,18 @@ │ ├── end_keyword_loc: (12,0)-(12,3) = "end" │ └── name: :B ├── @ ClassNode (location: (14,0)-(15,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (14,0)-(14,5) = "class" │ ├── constant_path: │ │ @ ConstantPathNode (location: (14,6)-(14,13)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ ConstantPathNode (location: (14,6)-(14,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── parent: │ │ │ │ @ ConstantReadNode (location: (14,6)-(14,7)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :A │ │ │ ├── name: :B │ │ │ ├── delimiter_loc: (14,7)-(14,9) = "::" @@ -98,29 +112,36 @@ │ ├── end_keyword_loc: (15,0)-(15,3) = "end" │ └── name: :C ├── @ ClassNode (location: (17,0)-(18,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (17,0)-(17,5) = "class" │ ├── constant_path: │ │ @ ConstantReadNode (location: (17,6)-(17,7)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── inheritance_operator_loc: (17,8)-(17,9) = "<" │ ├── superclass: │ │ @ ConstantReadNode (location: (17,10)-(17,11)) + │ │ ├── flags: ∅ │ │ └── name: :B │ ├── body: ∅ │ ├── end_keyword_loc: (18,0)-(18,3) = "end" │ └── name: :A ├── @ ClassNode (location: (20,0)-(21,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (20,0)-(20,5) = "class" │ ├── constant_path: │ │ @ ConstantReadNode (location: (20,6)-(20,7)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── inheritance_operator_loc: (20,8)-(20,9) = "<" │ ├── superclass: │ │ @ ConstantPathNode (location: (20,10)-(20,14)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (20,10)-(20,11)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :B │ │ ├── name: :C │ │ ├── delimiter_loc: (20,11)-(20,13) = "::" @@ -129,12 +150,15 @@ │ ├── end_keyword_loc: (21,0)-(21,3) = "end" │ └── name: :A ├── @ ClassNode (location: (23,0)-(24,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (23,0)-(23,5) = "class" │ ├── constant_path: │ │ @ ConstantPathNode (location: (23,6)-(23,10)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (23,6)-(23,7)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :A │ │ ├── name: :B │ │ ├── delimiter_loc: (23,7)-(23,9) = "::" @@ -142,8 +166,10 @@ │ ├── inheritance_operator_loc: (23,11)-(23,12) = "<" │ ├── superclass: │ │ @ ConstantPathNode (location: (23,13)-(23,17)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (23,13)-(23,14)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :C │ │ ├── name: :D │ │ ├── delimiter_loc: (23,14)-(23,16) = "::" @@ -152,18 +178,21 @@ │ ├── end_keyword_loc: (24,0)-(24,3) = "end" │ └── name: :B ├── @ ClassNode (location: (26,0)-(32,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── class_keyword_loc: (26,0)-(26,5) = "class" │ ├── constant_path: │ │ @ ConstantReadNode (location: (26,6)-(26,7)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── inheritance_operator_loc: ∅ │ ├── superclass: ∅ │ ├── body: │ │ @ StatementsNode (location: (27,2)-(31,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 2) │ │ ├── @ CallNode (location: (27,2)-(27,16)) - │ │ │ ├── flags: ignore_visibility + │ │ │ ├── flags: newline, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :include @@ -177,6 +206,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ ├── receiver: │ │ │ │ │ @ ConstantReadNode (location: (27,10)-(27,11)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── name: :B │ │ │ │ ├── call_operator_loc: (27,11)-(27,12) = "." │ │ │ │ ├── name: :new @@ -188,15 +218,17 @@ │ │ │ ├── closing_loc: (27,15)-(27,16) = ")" │ │ │ └── block: ∅ │ │ └── @ DefNode (location: (29,2)-(31,5)) + │ │ ├── flags: newline │ │ ├── name: :foo │ │ ├── name_loc: (29,6)-(29,9) = "foo" │ │ ├── receiver: ∅ │ │ ├── parameters: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (30,4)-(30,8)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ SymbolNode (location: (30,4)-(30,8)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (30,4)-(30,5) = ":" │ │ │ ├── value_loc: (30,5)-(30,8) = "bar" │ │ │ ├── closing_loc: ∅ @@ -211,10 +243,12 @@ │ ├── end_keyword_loc: (32,0)-(32,3) = "end" │ └── name: :A └── @ ClassNode (location: (34,0)-(35,3)) + ├── flags: newline ├── locals: [] ├── class_keyword_loc: (34,0)-(34,5) = "class" ├── constant_path: │ @ ConstantPathNode (location: (34,6)-(34,9)) + │ ├── flags: ∅ │ ├── parent: ∅ │ ├── name: :A │ ├── delimiter_loc: (34,6)-(34,8) = "::" diff --git a/test/prism/snapshots/unparser/corpus/literal/def.txt b/test/prism/snapshots/unparser/corpus/literal/def.txt index f3ef6c388e4f0a..405c4f3fb84ddd 100644 --- a/test/prism/snapshots/unparser/corpus/literal/def.txt +++ b/test/prism/snapshots/unparser/corpus/literal/def.txt @@ -1,21 +1,26 @@ @ ProgramNode (location: (1,0)-(134,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(134,3)) + ├── flags: ∅ └── body: (length: 30) ├── @ DefNode (location: (1,0)-(9,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (1,4)-(1,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (1,0)-(9,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (2,2)-(2,3)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (2,2)-(2,3)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :a @@ -26,15 +31,17 @@ │ │ │ └── block: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (3,0)-(4,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (3,0)-(3,6) = "rescue" │ │ │ ├── exceptions: (length: 0) │ │ │ ├── operator_loc: ∅ │ │ │ ├── reference: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (4,2)-(4,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (4,2)-(4,3)) - │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ ├── receiver: ∅ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :b @@ -46,12 +53,14 @@ │ │ │ └── consequent: ∅ │ │ ├── else_clause: │ │ │ @ ElseNode (location: (5,0)-(7,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── else_keyword_loc: (5,0)-(5,4) = "else" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (6,2)-(6,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (6,2)-(6,3)) - │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ ├── receiver: ∅ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :c @@ -63,12 +72,14 @@ │ │ │ └── end_keyword_loc: (7,0)-(7,6) = "ensure" │ │ ├── ensure_clause: │ │ │ @ EnsureNode (location: (7,0)-(9,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── ensure_keyword_loc: (7,0)-(7,6) = "ensure" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (8,2)-(8,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (8,2)-(8,3)) - │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ ├── receiver: ∅ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :d @@ -87,17 +98,21 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (9,0)-(9,3) = "end" ├── @ DefNode (location: (11,0)-(19,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (11,4)-(11,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (11,0)-(19,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (12,2)-(12,12)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ RescueModifierNode (location: (12,2)-(12,12)) + │ │ │ ├── flags: newline │ │ │ ├── expression: │ │ │ │ @ CallNode (location: (12,2)-(12,3)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -123,15 +138,17 @@ │ │ │ └── block: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (13,0)-(14,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (13,0)-(13,6) = "rescue" │ │ │ ├── exceptions: (length: 0) │ │ │ ├── operator_loc: ∅ │ │ │ ├── reference: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (14,2)-(14,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (14,2)-(14,3)) - │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ ├── receiver: ∅ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :b @@ -143,12 +160,14 @@ │ │ │ └── consequent: ∅ │ │ ├── else_clause: │ │ │ @ ElseNode (location: (15,0)-(17,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── else_keyword_loc: (15,0)-(15,4) = "else" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (16,2)-(16,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (16,2)-(16,3)) - │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ ├── receiver: ∅ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :c @@ -160,12 +179,14 @@ │ │ │ └── end_keyword_loc: (17,0)-(17,6) = "ensure" │ │ ├── ensure_clause: │ │ │ @ EnsureNode (location: (17,0)-(19,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── ensure_keyword_loc: (17,0)-(17,6) = "ensure" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (18,2)-(18,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (18,2)-(18,3)) - │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ ├── receiver: ∅ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :d @@ -184,11 +205,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (19,0)-(19,3) = "end" ├── @ DefNode (location: (21,0)-(22,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (21,4)-(21,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (21,8)-(21,18)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -213,6 +236,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (22,0)-(22,3) = "end" ├── @ DefNode (location: (24,0)-(25,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (24,4)-(24,7) = "foo" │ ├── receiver: ∅ @@ -226,15 +250,17 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (25,0)-(25,3) = "end" ├── @ DefNode (location: (27,0)-(29,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (27,4)-(27,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (28,2)-(28,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (28,2)-(28,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -251,18 +277,21 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (29,0)-(29,3) = "end" ├── @ DefNode (location: (31,0)-(37,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (31,4)-(31,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (31,0)-(37,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (32,2)-(32,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (32,2)-(32,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :foo @@ -273,15 +302,17 @@ │ │ │ └── block: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (33,0)-(34,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (33,0)-(33,6) = "rescue" │ │ │ ├── exceptions: (length: 0) │ │ │ ├── operator_loc: ∅ │ │ │ ├── reference: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (34,2)-(34,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (34,2)-(34,5)) - │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ ├── receiver: ∅ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :bar @@ -294,12 +325,14 @@ │ │ ├── else_clause: ∅ │ │ ├── ensure_clause: │ │ │ @ EnsureNode (location: (35,0)-(37,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── ensure_keyword_loc: (35,0)-(35,6) = "ensure" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (36,2)-(36,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (36,2)-(36,5)) - │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ ├── receiver: ∅ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :baz @@ -318,18 +351,21 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (37,0)-(37,3) = "end" ├── @ DefNode (location: (39,0)-(43,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (39,4)-(39,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (39,0)-(43,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (40,2)-(40,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (40,2)-(40,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :bar @@ -342,12 +378,14 @@ │ │ ├── else_clause: ∅ │ │ ├── ensure_clause: │ │ │ @ EnsureNode (location: (41,0)-(43,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── ensure_keyword_loc: (41,0)-(41,6) = "ensure" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (42,2)-(42,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (42,2)-(42,5)) - │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ ├── receiver: ∅ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :baz @@ -366,18 +404,21 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (43,0)-(43,3) = "end" ├── @ DefNode (location: (45,0)-(49,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (45,4)-(45,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (45,0)-(49,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (46,2)-(46,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (46,2)-(46,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :bar @@ -388,15 +429,17 @@ │ │ │ └── block: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (47,0)-(48,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (47,0)-(47,6) = "rescue" │ │ │ ├── exceptions: (length: 0) │ │ │ ├── operator_loc: ∅ │ │ │ ├── reference: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (48,2)-(48,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (48,2)-(48,5)) - │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ ├── receiver: ∅ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :baz @@ -417,11 +460,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (49,0)-(49,3) = "end" ├── @ DefNode (location: (51,0)-(53,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (51,4)-(51,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (51,8)-(51,11)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (51,8)-(51,11)) │ │ │ ├── flags: ∅ @@ -434,8 +479,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (52,2)-(52,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (52,2)-(52,5)) + │ │ ├── flags: newline │ │ ├── name: :bar │ │ └── depth: 0 │ ├── locals: [:bar] @@ -446,11 +493,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (53,0)-(53,3) = "end" ├── @ DefNode (location: (55,0)-(57,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (55,4)-(55,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (55,8)-(55,16)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 2) │ │ │ ├── @ RequiredParameterNode (location: (55,8)-(55,11)) │ │ │ │ ├── flags: ∅ @@ -466,8 +515,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (56,2)-(56,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (56,2)-(56,5)) + │ │ ├── flags: newline │ │ ├── name: :bar │ │ └── depth: 0 │ ├── locals: [:bar, :baz] @@ -478,11 +529,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (57,0)-(57,3) = "end" ├── @ DefNode (location: (59,0)-(61,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (59,4)-(59,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (59,8)-(59,16)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 1) │ │ │ └── @ OptionalParameterNode (location: (59,8)-(59,16)) @@ -492,6 +545,7 @@ │ │ │ ├── operator_loc: (59,12)-(59,13) = "=" │ │ │ └── value: │ │ │ @ ParenthesesNode (location: (59,14)-(59,16)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: ∅ │ │ │ ├── opening_loc: (59,14)-(59,15) = "(" │ │ │ └── closing_loc: (59,15)-(59,16) = ")" @@ -502,8 +556,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (60,2)-(60,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (60,2)-(60,5)) + │ │ ├── flags: newline │ │ ├── name: :bar │ │ └── depth: 0 │ ├── locals: [:bar] @@ -514,11 +570,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (61,0)-(61,3) = "end" ├── @ DefNode (location: (63,0)-(64,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (63,4)-(63,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (63,8)-(63,24)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 1) │ │ │ └── @ OptionalParameterNode (location: (63,8)-(63,24)) @@ -528,11 +586,13 @@ │ │ │ ├── operator_loc: (63,12)-(63,13) = "=" │ │ │ └── value: │ │ │ @ ParenthesesNode (location: (63,14)-(63,24)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (63,15)-(63,23)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 2) │ │ │ │ ├── @ CallNode (location: (63,15)-(63,18)) - │ │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ │ ├── receiver: ∅ │ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ │ ├── name: :baz @@ -542,6 +602,7 @@ │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── block: ∅ │ │ │ │ └── @ NilNode (location: (63,20)-(63,23)) + │ │ │ │ └── flags: newline, static_literal │ │ │ ├── opening_loc: (63,14)-(63,15) = "(" │ │ │ └── closing_loc: (63,23)-(63,24) = ")" │ │ ├── rest: ∅ @@ -558,11 +619,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (64,0)-(64,3) = "end" ├── @ DefNode (location: (66,0)-(68,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (66,4)-(66,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (66,8)-(66,18)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 1) │ │ │ └── @ OptionalParameterNode (location: (66,8)-(66,18)) @@ -572,6 +635,7 @@ │ │ │ ├── operator_loc: (66,12)-(66,13) = "=" │ │ │ └── value: │ │ │ @ TrueNode (location: (66,14)-(66,18)) + │ │ │ └── flags: static_literal │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) │ │ ├── keywords: (length: 0) @@ -579,8 +643,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (67,2)-(67,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (67,2)-(67,5)) + │ │ ├── flags: newline │ │ ├── name: :bar │ │ └── depth: 0 │ ├── locals: [:bar] @@ -591,11 +657,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (68,0)-(68,3) = "end" ├── @ DefNode (location: (70,0)-(72,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (70,4)-(70,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (70,8)-(70,23)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (70,8)-(70,11)) │ │ │ ├── flags: ∅ @@ -608,6 +676,7 @@ │ │ │ ├── operator_loc: (70,17)-(70,18) = "=" │ │ │ └── value: │ │ │ @ TrueNode (location: (70,19)-(70,23)) + │ │ │ └── flags: static_literal │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) │ │ ├── keywords: (length: 0) @@ -615,8 +684,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (71,2)-(71,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (71,2)-(71,5)) + │ │ ├── flags: newline │ │ ├── name: :bar │ │ └── depth: 0 │ ├── locals: [:bar, :baz] @@ -627,11 +698,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (72,0)-(72,3) = "end" ├── @ DefNode (location: (74,0)-(75,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (74,4)-(74,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (74,8)-(74,14)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -643,7 +716,7 @@ │ │ │ ├── name_loc: (74,8)-(74,12) = "bar:" │ │ │ └── value: │ │ │ @ IntegerNode (location: (74,13)-(74,14)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── keyword_rest: ∅ │ │ └── block: ∅ @@ -656,11 +729,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (75,0)-(75,3) = "end" ├── @ DefNode (location: (77,0)-(78,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (77,4)-(77,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (77,8)-(77,16)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -692,11 +767,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (78,0)-(78,3) = "end" ├── @ DefNode (location: (80,0)-(81,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (80,4)-(80,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (80,8)-(80,18)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -728,11 +805,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (81,0)-(81,3) = "end" ├── @ DefNode (location: (83,0)-(85,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (83,4)-(83,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (83,8)-(83,9)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: @@ -747,9 +826,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (84,2)-(84,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (84,2)-(84,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -766,11 +846,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (85,0)-(85,3) = "end" ├── @ DefNode (location: (87,0)-(89,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (87,4)-(87,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (87,8)-(87,12)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: @@ -785,8 +867,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (88,2)-(88,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (88,2)-(88,5)) + │ │ ├── flags: newline │ │ ├── name: :bar │ │ └── depth: 0 │ ├── locals: [:bar] @@ -797,11 +881,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (89,0)-(89,3) = "end" ├── @ DefNode (location: (91,0)-(93,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (91,4)-(91,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (91,8)-(91,17)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (91,8)-(91,11)) │ │ │ ├── flags: ∅ @@ -819,8 +905,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (92,2)-(92,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (92,2)-(92,5)) + │ │ ├── flags: newline │ │ ├── name: :bar │ │ └── depth: 0 │ ├── locals: [:bar, :baz] @@ -831,11 +919,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (93,0)-(93,3) = "end" ├── @ DefNode (location: (95,0)-(97,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (95,4)-(95,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (95,8)-(95,24)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 1) │ │ │ └── @ OptionalParameterNode (location: (95,8)-(95,18)) @@ -845,6 +935,7 @@ │ │ │ ├── operator_loc: (95,12)-(95,13) = "=" │ │ │ └── value: │ │ │ @ TrueNode (location: (95,14)-(95,18)) + │ │ │ └── flags: static_literal │ │ ├── rest: │ │ │ @ RestParameterNode (location: (95,20)-(95,24)) │ │ │ ├── flags: ∅ @@ -857,9 +948,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (96,2)-(96,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (96,2)-(96,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -876,11 +968,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (97,0)-(97,3) = "end" ├── @ DefNode (location: (99,0)-(101,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (99,4)-(99,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (99,8)-(99,32)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 1) │ │ │ └── @ OptionalParameterNode (location: (99,8)-(99,18)) @@ -890,6 +984,7 @@ │ │ │ ├── operator_loc: (99,12)-(99,13) = "=" │ │ │ └── value: │ │ │ @ TrueNode (location: (99,14)-(99,18)) + │ │ │ └── flags: static_literal │ │ ├── rest: │ │ │ @ RestParameterNode (location: (99,20)-(99,24)) │ │ │ ├── flags: ∅ @@ -907,9 +1002,10 @@ │ │ └── operator_loc: (99,26)-(99,27) = "&" │ ├── body: │ │ @ StatementsNode (location: (100,2)-(100,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (100,2)-(100,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -926,11 +1022,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (101,0)-(101,3) = "end" ├── @ DefNode (location: (103,0)-(105,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (103,4)-(103,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (103,8)-(103,29)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (103,8)-(103,11)) │ │ │ ├── flags: ∅ @@ -943,6 +1041,7 @@ │ │ │ ├── operator_loc: (103,17)-(103,18) = "=" │ │ │ └── value: │ │ │ @ TrueNode (location: (103,19)-(103,23)) + │ │ │ └── flags: static_literal │ │ ├── rest: │ │ │ @ RestParameterNode (location: (103,25)-(103,29)) │ │ │ ├── flags: ∅ @@ -955,8 +1054,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (104,2)-(104,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (104,2)-(104,5)) + │ │ ├── flags: newline │ │ ├── name: :bar │ │ └── depth: 0 │ ├── locals: [:bar, :baz, :bor] @@ -967,11 +1068,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (105,0)-(105,3) = "end" ├── @ DefNode (location: (107,0)-(109,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (107,4)-(107,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (107,8)-(107,14)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -986,9 +1089,10 @@ │ │ └── operator_loc: (107,8)-(107,9) = "&" │ ├── body: │ │ @ StatementsNode (location: (108,2)-(108,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (108,2)-(108,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -1005,11 +1109,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (109,0)-(109,3) = "end" ├── @ DefNode (location: (111,0)-(113,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (111,4)-(111,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (111,8)-(111,19)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (111,8)-(111,11)) │ │ │ ├── flags: ∅ @@ -1027,8 +1133,10 @@ │ │ └── operator_loc: (111,13)-(111,14) = "&" │ ├── body: │ │ @ StatementsNode (location: (112,2)-(112,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (112,2)-(112,5)) + │ │ ├── flags: newline │ │ ├── name: :bar │ │ └── depth: 0 │ ├── locals: [:bar, :block] @@ -1039,15 +1147,17 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (113,0)-(113,3) = "end" ├── @ DefNode (location: (115,0)-(118,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (115,4)-(115,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (116,2)-(117,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 2) │ │ ├── @ CallNode (location: (116,2)-(116,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :bar @@ -1057,7 +1167,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ └── @ CallNode (location: (117,2)-(117,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :baz @@ -1074,15 +1184,19 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (118,0)-(118,3) = "end" ├── @ DefNode (location: (120,0)-(121,3)) + │ ├── flags: newline │ ├── name: :f │ ├── name_loc: (120,4)-(120,5) = "f" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (120,6)-(120,11)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ MultiTargetNode (location: (120,6)-(120,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── lefts: (length: 1) │ │ │ │ └── @ MultiTargetNode (location: (120,7)-(120,10)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── lefts: (length: 1) │ │ │ │ │ └── @ RequiredParameterNode (location: (120,8)-(120,9)) │ │ │ │ │ ├── flags: ∅ @@ -1110,11 +1224,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (121,0)-(121,3) = "end" ├── @ DefNode (location: (123,0)-(124,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (123,4)-(123,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (123,8)-(123,26)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -1146,29 +1262,32 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (124,0)-(124,3) = "end" ├── @ DefNode (location: (126,0)-(130,3)) + │ ├── flags: newline │ ├── name: :f │ ├── name_loc: (126,4)-(126,5) = "f" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (127,2)-(127,12)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ InterpolatedStringNode (location: (127,2)-(127,12)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── opening_loc: (127,2)-(127,12) = "<<-HEREDOC" │ │ ├── parts: (length: 3) │ │ │ ├── @ StringNode (location: (128,0)-(128,4)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── content_loc: (128,0)-(128,4) = " " │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: " " │ │ │ ├── @ EmbeddedStatementsNode (location: (128,4)-(128,7)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── opening_loc: (128,4)-(128,6) = "\#{" │ │ │ │ ├── statements: ∅ │ │ │ │ └── closing_loc: (128,6)-(128,7) = "}" │ │ │ └── @ StringNode (location: (128,7)-(129,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (128,7)-(129,0) = "\n" │ │ │ ├── closing_loc: ∅ @@ -1182,15 +1301,17 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (130,0)-(130,3) = "end" └── @ DefNode (location: (132,0)-(134,3)) + ├── flags: newline ├── name: :f ├── name_loc: (132,4)-(132,5) = "f" ├── receiver: ∅ ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (133,2)-(133,5)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ StringNode (location: (133,2)-(133,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (133,2)-(133,4) = "%(" │ ├── content_loc: (133,4)-(133,4) = "" │ ├── closing_loc: (133,4)-(133,5) = ")" diff --git a/test/prism/snapshots/unparser/corpus/literal/defined.txt b/test/prism/snapshots/unparser/corpus/literal/defined.txt index 89145ddcda8d30..d1b5a15685e0d3 100644 --- a/test/prism/snapshots/unparser/corpus/literal/defined.txt +++ b/test/prism/snapshots/unparser/corpus/literal/defined.txt @@ -1,35 +1,47 @@ @ ProgramNode (location: (1,0)-(3,27)) +├── flags: ∅ ├── locals: [:a, :b] └── statements: @ StatementsNode (location: (1,0)-(3,27)) + ├── flags: ∅ └── body: (length: 3) ├── @ DefinedNode (location: (1,0)-(1,14)) + │ ├── flags: newline │ ├── lparen_loc: (1,8)-(1,9) = "(" │ ├── value: │ │ @ InstanceVariableReadNode (location: (1,9)-(1,13)) + │ │ ├── flags: ∅ │ │ └── name: :@foo │ ├── rparen_loc: (1,13)-(1,14) = ")" │ └── keyword_loc: (1,0)-(1,8) = "defined?" ├── @ DefinedNode (location: (2,0)-(2,13)) + │ ├── flags: newline │ ├── lparen_loc: (2,8)-(2,9) = "(" │ ├── value: │ │ @ ConstantReadNode (location: (2,9)-(2,12)) + │ │ ├── flags: ∅ │ │ └── name: :Foo │ ├── rparen_loc: (2,12)-(2,13) = ")" │ └── keyword_loc: (2,0)-(2,8) = "defined?" └── @ DefinedNode (location: (3,0)-(3,27)) + ├── flags: newline ├── lparen_loc: (3,8)-(3,9) = "(" ├── value: │ @ ParenthesesNode (location: (3,9)-(3,26)) + │ ├── flags: ∅ │ ├── body: │ │ @ StatementsNode (location: (3,10)-(3,25)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ MultiWriteNode (location: (3,10)-(3,25)) + │ │ ├── flags: newline │ │ ├── lefts: (length: 2) │ │ │ ├── @ LocalVariableTargetNode (location: (3,11)-(3,12)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :a │ │ │ │ └── depth: 0 │ │ │ └── @ LocalVariableTargetNode (location: (3,14)-(3,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :b │ │ │ └── depth: 0 │ │ ├── rest: ∅ @@ -39,13 +51,13 @@ │ │ ├── operator_loc: (3,17)-(3,18) = "=" │ │ └── value: │ │ @ ArrayNode (location: (3,19)-(3,25)) - │ │ ├── flags: ∅ + │ │ ├── flags: static_literal │ │ ├── elements: (length: 2) │ │ │ ├── @ IntegerNode (location: (3,20)-(3,21)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── @ IntegerNode (location: (3,23)-(3,24)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── opening_loc: (3,19)-(3,20) = "[" │ │ └── closing_loc: (3,24)-(3,25) = "]" diff --git a/test/prism/snapshots/unparser/corpus/literal/defs.txt b/test/prism/snapshots/unparser/corpus/literal/defs.txt index 7858877172332d..33c24f3b24ef74 100644 --- a/test/prism/snapshots/unparser/corpus/literal/defs.txt +++ b/test/prism/snapshots/unparser/corpus/literal/defs.txt @@ -1,13 +1,17 @@ @ ProgramNode (location: (1,0)-(40,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(40,3)) + ├── flags: ∅ └── body: (length: 10) ├── @ DefNode (location: (1,0)-(2,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (1,9)-(1,12) = "foo" │ ├── receiver: │ │ @ SelfNode (location: (1,4)-(1,8)) + │ │ └── flags: ∅ │ ├── parameters: ∅ │ ├── body: ∅ │ ├── locals: [] @@ -18,16 +22,19 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (2,0)-(2,3) = "end" ├── @ DefNode (location: (4,0)-(6,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (4,9)-(4,12) = "foo" │ ├── receiver: │ │ @ SelfNode (location: (4,4)-(4,8)) + │ │ └── flags: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (5,2)-(5,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (5,2)-(5,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -44,16 +51,19 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (6,0)-(6,3) = "end" ├── @ DefNode (location: (8,0)-(11,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (8,9)-(8,12) = "foo" │ ├── receiver: │ │ @ SelfNode (location: (8,4)-(8,8)) + │ │ └── flags: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (9,2)-(10,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 2) │ │ ├── @ CallNode (location: (9,2)-(9,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :bar @@ -63,7 +73,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ └── @ CallNode (location: (10,2)-(10,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :baz @@ -80,17 +90,20 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (11,0)-(11,3) = "end" ├── @ DefNode (location: (13,0)-(15,3)) + │ ├── flags: newline │ ├── name: :bar │ ├── name_loc: (13,8)-(13,11) = "bar" │ ├── receiver: │ │ @ ConstantReadNode (location: (13,4)-(13,7)) + │ │ ├── flags: ∅ │ │ └── name: :Foo │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (14,2)-(14,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (14,2)-(14,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -107,10 +120,12 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (15,0)-(15,3) = "end" ├── @ DefNode (location: (17,0)-(20,3)) + │ ├── flags: newline │ ├── name: :bar │ ├── name_loc: (18,3)-(18,6) = "bar" │ ├── receiver: │ │ @ ParenthesesNode (location: (17,4)-(18,2)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ CallNode (location: (17,5)-(18,1)) │ │ │ ├── flags: ignore_visibility @@ -123,11 +138,14 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: │ │ │ @ BlockNode (location: (17,9)-(18,1)) + │ │ │ ├── flags: ∅ │ │ │ ├── locals: [:bar] │ │ │ ├── parameters: │ │ │ │ @ BlockParametersNode (location: (17,11)-(17,16)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── parameters: │ │ │ │ │ @ ParametersNode (location: (17,12)-(17,15)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── requireds: (length: 1) │ │ │ │ │ │ └── @ RequiredParameterNode (location: (17,12)-(17,15)) │ │ │ │ │ │ ├── flags: ∅ @@ -149,9 +167,10 @@ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (19,2)-(19,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (19,2)-(19,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -168,10 +187,12 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (20,0)-(20,3) = "end" ├── @ DefNode (location: (22,0)-(24,3)) + │ ├── flags: newline │ ├── name: :bar │ ├── name_loc: (22,13)-(22,16) = "bar" │ ├── receiver: │ │ @ ParenthesesNode (location: (22,4)-(22,12)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ CallNode (location: (22,5)-(22,11)) │ │ │ ├── flags: ignore_visibility @@ -185,7 +206,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ IntegerNode (location: (22,9)-(22,10)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── closing_loc: (22,10)-(22,11) = ")" │ │ │ └── block: ∅ @@ -194,9 +215,10 @@ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (23,2)-(23,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (23,2)-(23,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -213,17 +235,21 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (24,0)-(24,3) = "end" ├── @ DefNode (location: (26,0)-(28,3)) + │ ├── flags: newline │ ├── name: :bar │ ├── name_loc: (26,19)-(26,22) = "bar" │ ├── receiver: │ │ @ ParenthesesNode (location: (26,4)-(26,18)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ CallNode (location: (26,5)-(26,17)) │ │ │ ├── flags: ∅ │ │ │ ├── receiver: │ │ │ │ @ ConstantPathNode (location: (26,5)-(26,13)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── parent: │ │ │ │ │ @ ConstantReadNode (location: (26,5)-(26,8)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── name: :Foo │ │ │ │ ├── name: :Bar │ │ │ │ ├── delimiter_loc: (26,8)-(26,10) = "::" @@ -240,9 +266,10 @@ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (27,2)-(27,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (27,2)-(27,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :baz @@ -259,14 +286,18 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (28,0)-(28,3) = "end" ├── @ DefNode (location: (30,0)-(32,3)) + │ ├── flags: newline │ ├── name: :bar │ ├── name_loc: (30,15)-(30,18) = "bar" │ ├── receiver: │ │ @ ParenthesesNode (location: (30,4)-(30,14)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ ConstantPathNode (location: (30,5)-(30,13)) + │ │ │ ├── flags: ∅ │ │ │ ├── parent: │ │ │ │ @ ConstantReadNode (location: (30,5)-(30,8)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :Foo │ │ │ ├── name: :Bar │ │ │ ├── delimiter_loc: (30,8)-(30,10) = "::" @@ -276,9 +307,10 @@ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (31,2)-(31,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (31,2)-(31,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :baz @@ -295,17 +327,20 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (32,0)-(32,3) = "end" ├── @ DefNode (location: (34,0)-(36,3)) + │ ├── flags: newline │ ├── name: :bar │ ├── name_loc: (34,8)-(34,11) = "bar" │ ├── receiver: │ │ @ ConstantReadNode (location: (34,4)-(34,7)) + │ │ ├── flags: ∅ │ │ └── name: :Foo │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (35,2)-(35,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (35,2)-(35,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :baz @@ -322,6 +357,7 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (36,0)-(36,3) = "end" └── @ DefNode (location: (38,0)-(40,3)) + ├── flags: newline ├── name: :bar ├── name_loc: (38,8)-(38,11) = "bar" ├── receiver: @@ -338,9 +374,10 @@ ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (39,2)-(39,5)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (39,2)-(39,5)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :baz diff --git a/test/prism/snapshots/unparser/corpus/literal/dstr.txt b/test/prism/snapshots/unparser/corpus/literal/dstr.txt index 8893e8b75d2aef..3092360608f91c 100644 --- a/test/prism/snapshots/unparser/corpus/literal/dstr.txt +++ b/test/prism/snapshots/unparser/corpus/literal/dstr.txt @@ -1,26 +1,32 @@ @ ProgramNode (location: (1,0)-(37,1)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(37,1)) + ├── flags: ∅ └── body: (length: 11) ├── @ IfNode (location: (1,0)-(3,3)) + │ ├── flags: newline │ ├── if_keyword_loc: (1,0)-(1,2) = "if" │ ├── predicate: │ │ @ TrueNode (location: (1,3)-(1,7)) + │ │ └── flags: static_literal │ ├── then_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (2,2)-(2,8)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ InterpolatedStringNode (location: (2,2)-(2,8)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── opening_loc: (2,2)-(2,3) = "\"" │ │ ├── parts: (length: 2) │ │ │ ├── @ EmbeddedStatementsNode (location: (2,3)-(2,6)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── opening_loc: (2,3)-(2,5) = "\#{" │ │ │ │ ├── statements: ∅ │ │ │ │ └── closing_loc: (2,5)-(2,6) = "}" │ │ │ └── @ StringNode (location: (2,6)-(2,7)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (2,6)-(2,7) = "a" │ │ │ ├── closing_loc: ∅ @@ -29,36 +35,40 @@ │ ├── consequent: ∅ │ └── end_keyword_loc: (3,0)-(3,3) = "end" ├── @ IfNode (location: (4,0)-(11,3)) + │ ├── flags: newline │ ├── if_keyword_loc: (4,0)-(4,2) = "if" │ ├── predicate: │ │ @ TrueNode (location: (4,3)-(4,7)) + │ │ └── flags: static_literal │ ├── then_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (5,2)-(10,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 2) │ │ ├── @ InterpolatedStringNode (location: (5,2)-(5,12)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── opening_loc: (5,2)-(5,12) = "<<-HEREDOC" │ │ │ ├── parts: (length: 3) │ │ │ │ ├── @ StringNode (location: (6,0)-(7,0)) - │ │ │ │ │ ├── flags: frozen + │ │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── content_loc: (6,0)-(7,0) = "a\n" │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── unescaped: "a\n" │ │ │ │ ├── @ EmbeddedStatementsNode (location: (7,0)-(7,3)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── opening_loc: (7,0)-(7,2) = "\#{" │ │ │ │ │ ├── statements: ∅ │ │ │ │ │ └── closing_loc: (7,2)-(7,3) = "}" │ │ │ │ └── @ StringNode (location: (7,3)-(9,0)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── content_loc: (7,3)-(9,0) = "a\nb\n" │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: "a\nb\n" │ │ │ └── closing_loc: (9,0)-(10,0) = " HEREDOC\n" │ │ └── @ CallNode (location: (10,2)-(10,3)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :x @@ -70,58 +80,63 @@ │ ├── consequent: ∅ │ └── end_keyword_loc: (11,0)-(11,3) = "end" ├── @ InterpolatedStringNode (location: (12,0)-(12,10)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (12,0)-(12,10) = "<<-HEREDOC" │ ├── parts: (length: 7) │ │ ├── @ StringNode (location: (13,0)-(14,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (13,0)-(14,0) = "\\\#{}\\\#{}\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "\#{}\#{}\n" │ │ ├── @ EmbeddedStatementsNode (location: (14,0)-(14,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (14,0)-(14,2) = "\#{" │ │ │ ├── statements: ∅ │ │ │ └── closing_loc: (14,2)-(14,3) = "}" │ │ ├── @ StringNode (location: (14,3)-(15,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (14,3)-(15,0) = "\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "\n" │ │ ├── @ EmbeddedStatementsNode (location: (15,0)-(15,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (15,0)-(15,2) = "\#{" │ │ │ ├── statements: ∅ │ │ │ └── closing_loc: (15,2)-(15,3) = "}" │ │ ├── @ StringNode (location: (15,3)-(16,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (15,3)-(16,0) = "\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "\n" │ │ ├── @ EmbeddedStatementsNode (location: (16,0)-(16,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (16,0)-(16,2) = "\#{" │ │ │ ├── statements: ∅ │ │ │ └── closing_loc: (16,2)-(16,3) = "}" │ │ └── @ StringNode (location: (16,3)-(17,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (16,3)-(17,0) = "\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "\n" │ └── closing_loc: (17,0)-(18,0) = "HEREDOC\n" ├── @ RescueModifierNode (location: (18,0)-(18,21)) + │ ├── flags: newline │ ├── expression: │ │ @ InterpolatedStringNode (location: (18,0)-(18,10)) │ │ ├── flags: ∅ │ │ ├── opening_loc: (18,0)-(18,10) = "<<-HEREDOC" │ │ ├── parts: (length: 2) │ │ │ ├── @ EmbeddedStatementsNode (location: (19,0)-(19,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── opening_loc: (19,0)-(19,2) = "\#{" │ │ │ │ ├── statements: ∅ │ │ │ │ └── closing_loc: (19,2)-(19,3) = "}" │ │ │ └── @ StringNode (location: (19,3)-(21,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (19,3)-(21,0) = "\na\n" │ │ │ ├── closing_loc: ∅ @@ -130,80 +145,92 @@ │ ├── keyword_loc: (18,11)-(18,17) = "rescue" │ └── rescue_expression: │ @ NilNode (location: (18,18)-(18,21)) + │ └── flags: static_literal ├── @ InterpolatedStringNode (location: (22,0)-(22,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (22,0)-(22,1) = "\"" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (22,1)-(22,2)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (22,1)-(22,2) = "a" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a" │ │ └── @ EmbeddedVariableNode (location: (22,2)-(22,5)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (22,2)-(22,3) = "#" │ │ └── variable: │ │ @ NumberedReferenceReadNode (location: (22,3)-(22,5)) + │ │ ├── flags: ∅ │ │ └── number: 1 │ └── closing_loc: (22,5)-(22,6) = "\"" ├── @ InterpolatedStringNode (location: (23,0)-(23,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (23,0)-(23,1) = "\"" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (23,1)-(23,2)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (23,1)-(23,2) = "a" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a" │ │ └── @ EmbeddedVariableNode (location: (23,2)-(23,5)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (23,2)-(23,3) = "#" │ │ └── variable: │ │ @ GlobalVariableReadNode (location: (23,3)-(23,5)) + │ │ ├── flags: ∅ │ │ └── name: :$a │ └── closing_loc: (23,5)-(23,6) = "\"" ├── @ InterpolatedStringNode (location: (24,0)-(24,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (24,0)-(24,1) = "\"" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (24,1)-(24,2)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (24,1)-(24,2) = "a" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a" │ │ └── @ EmbeddedVariableNode (location: (24,2)-(24,5)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (24,2)-(24,3) = "#" │ │ └── variable: │ │ @ InstanceVariableReadNode (location: (24,3)-(24,5)) + │ │ ├── flags: ∅ │ │ └── name: :@a │ └── closing_loc: (24,5)-(24,6) = "\"" ├── @ InterpolatedStringNode (location: (25,0)-(25,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (25,0)-(25,1) = "\"" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (25,1)-(25,2)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (25,1)-(25,2) = "a" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "a" │ │ └── @ EmbeddedVariableNode (location: (25,2)-(25,6)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (25,2)-(25,3) = "#" │ │ └── variable: │ │ @ ClassVariableReadNode (location: (25,3)-(25,6)) + │ │ ├── flags: ∅ │ │ └── name: :@@a │ └── closing_loc: (25,6)-(25,7) = "\"" ├── @ IfNode (location: (26,0)-(30,3)) + │ ├── flags: newline │ ├── if_keyword_loc: (26,0)-(26,2) = "if" │ ├── predicate: │ │ @ TrueNode (location: (26,3)-(26,7)) + │ │ └── flags: static_literal │ ├── then_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (27,2)-(27,19)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ ReturnNode (location: (27,2)-(27,19)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── keyword_loc: (27,2)-(27,8) = "return" │ │ └── arguments: │ │ @ ArgumentsNode (location: (27,9)-(27,19)) @@ -214,22 +241,24 @@ │ │ ├── opening_loc: (27,9)-(27,19) = "<<-HEREDOC" │ │ ├── parts: (length: 3) │ │ │ ├── @ StringNode (location: (28,0)-(28,4)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── content_loc: (28,0)-(28,4) = " " │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: " " │ │ │ ├── @ EmbeddedStatementsNode (location: (28,4)-(28,9)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── opening_loc: (28,4)-(28,6) = "\#{" │ │ │ │ ├── statements: │ │ │ │ │ @ StatementsNode (location: (28,6)-(28,8)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ IntegerNode (location: (28,6)-(28,8)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 42 │ │ │ │ └── closing_loc: (28,8)-(28,9) = "}" │ │ │ └── @ StringNode (location: (28,9)-(29,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (28,9)-(29,0) = "\n" │ │ │ ├── closing_loc: ∅ @@ -238,7 +267,7 @@ │ ├── consequent: ∅ │ └── end_keyword_loc: (30,0)-(30,3) = "end" ├── @ CallNode (location: (31,0)-(31,15)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -253,15 +282,17 @@ │ │ ├── opening_loc: (31,4)-(31,14) = "<<-HEREDOC" │ │ ├── parts: (length: 3) │ │ │ ├── @ StringNode (location: (32,0)-(32,2)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── content_loc: (32,0)-(32,2) = " " │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: " " │ │ │ ├── @ EmbeddedStatementsNode (location: (32,2)-(32,8)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── opening_loc: (32,2)-(32,4) = "\#{" │ │ │ │ ├── statements: │ │ │ │ │ @ StatementsNode (location: (32,4)-(32,7)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ CallNode (location: (32,4)-(32,7)) │ │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -275,7 +306,7 @@ │ │ │ │ │ └── block: ∅ │ │ │ │ └── closing_loc: (32,7)-(32,8) = "}" │ │ │ └── @ StringNode (location: (32,8)-(33,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (32,8)-(33,0) = "\n" │ │ │ ├── closing_loc: ∅ @@ -284,7 +315,7 @@ │ ├── closing_loc: (31,14)-(31,15) = ")" │ └── block: ∅ └── @ CallNode (location: (34,0)-(37,1)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :foo @@ -299,15 +330,17 @@ │ ├── opening_loc: (34,4)-(34,14) = "<<-HEREDOC" │ ├── parts: (length: 3) │ │ ├── @ StringNode (location: (35,0)-(35,2)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (35,0)-(35,2) = " " │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: " " │ │ ├── @ EmbeddedStatementsNode (location: (35,2)-(35,8)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (35,2)-(35,4) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (35,4)-(35,7)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (35,4)-(35,7)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -321,7 +354,7 @@ │ │ │ │ └── block: ∅ │ │ │ └── closing_loc: (35,7)-(35,8) = "}" │ │ └── @ StringNode (location: (35,8)-(36,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (35,8)-(36,0) = "\n" │ │ ├── closing_loc: ∅ @@ -330,11 +363,14 @@ ├── closing_loc: (34,14)-(34,15) = ")" └── block: @ BlockNode (location: (34,16)-(37,1)) + ├── flags: ∅ ├── locals: [:x] ├── parameters: │ @ BlockParametersNode (location: (34,18)-(34,21)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (34,19)-(34,20)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (34,19)-(34,20)) │ │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/unparser/corpus/literal/empty.txt b/test/prism/snapshots/unparser/corpus/literal/empty.txt index 3a21ce5559c982..5756285aaf2bd2 100644 --- a/test/prism/snapshots/unparser/corpus/literal/empty.txt +++ b/test/prism/snapshots/unparser/corpus/literal/empty.txt @@ -1,5 +1,7 @@ @ ProgramNode (location: (1,0)-(1,0)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,0)) + ├── flags: ∅ └── body: (length: 0) diff --git a/test/prism/snapshots/unparser/corpus/literal/empty_begin.txt b/test/prism/snapshots/unparser/corpus/literal/empty_begin.txt index 838b4bf6f040c6..25dc0b26a216a3 100644 --- a/test/prism/snapshots/unparser/corpus/literal/empty_begin.txt +++ b/test/prism/snapshots/unparser/corpus/literal/empty_begin.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(1,2)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,2)) + ├── flags: ∅ └── body: (length: 1) └── @ ParenthesesNode (location: (1,0)-(1,2)) + ├── flags: newline ├── body: ∅ ├── opening_loc: (1,0)-(1,1) = "(" └── closing_loc: (1,1)-(1,2) = ")" diff --git a/test/prism/snapshots/unparser/corpus/literal/flipflop.txt b/test/prism/snapshots/unparser/corpus/literal/flipflop.txt index 2d9f669e6f14dc..4c21f6c305df8b 100644 --- a/test/prism/snapshots/unparser/corpus/literal/flipflop.txt +++ b/test/prism/snapshots/unparser/corpus/literal/flipflop.txt @@ -1,24 +1,31 @@ @ ProgramNode (location: (1,0)-(10,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(10,3)) + ├── flags: ∅ └── body: (length: 4) ├── @ IfNode (location: (1,0)-(3,3)) + │ ├── flags: newline │ ├── if_keyword_loc: (1,0)-(1,2) = "if" │ ├── predicate: │ │ @ ParenthesesNode (location: (1,3)-(1,23)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (1,4)-(1,22)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ FlipFlopNode (location: (1,4)-(1,22)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── left: │ │ │ │ @ ParenthesesNode (location: (1,4)-(1,12)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── body: │ │ │ │ │ @ StatementsNode (location: (1,5)-(1,11)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ CallNode (location: (1,5)-(1,11)) - │ │ │ │ │ ├── flags: ∅ + │ │ │ │ │ ├── flags: newline │ │ │ │ │ ├── receiver: │ │ │ │ │ │ @ CallNode (location: (1,5)-(1,6)) │ │ │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -39,7 +46,7 @@ │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ └── arguments: (length: 1) │ │ │ │ │ │ └── @ IntegerNode (location: (1,10)-(1,11)) - │ │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ │ └── value: 4 │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── block: ∅ @@ -47,11 +54,13 @@ │ │ │ │ └── closing_loc: (1,11)-(1,12) = ")" │ │ │ ├── right: │ │ │ │ @ ParenthesesNode (location: (1,14)-(1,22)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── body: │ │ │ │ │ @ StatementsNode (location: (1,15)-(1,21)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ CallNode (location: (1,15)-(1,21)) - │ │ │ │ │ ├── flags: ∅ + │ │ │ │ │ ├── flags: newline │ │ │ │ │ ├── receiver: │ │ │ │ │ │ @ CallNode (location: (1,15)-(1,16)) │ │ │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -72,7 +81,7 @@ │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ └── arguments: (length: 1) │ │ │ │ │ │ └── @ IntegerNode (location: (1,20)-(1,21)) - │ │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ │ └── value: 4 │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── block: ∅ @@ -84,9 +93,10 @@ │ ├── then_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (2,2)-(2,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (2,2)-(2,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :foo @@ -98,21 +108,26 @@ │ ├── consequent: ∅ │ └── end_keyword_loc: (3,0)-(3,3) = "end" ├── @ IfNode (location: (4,0)-(6,3)) + │ ├── flags: newline │ ├── if_keyword_loc: (4,0)-(4,2) = "if" │ ├── predicate: │ │ @ ParenthesesNode (location: (4,3)-(4,24)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (4,4)-(4,23)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ FlipFlopNode (location: (4,4)-(4,23)) - │ │ │ ├── flags: exclude_end + │ │ │ ├── flags: newline, exclude_end │ │ │ ├── left: │ │ │ │ @ ParenthesesNode (location: (4,4)-(4,12)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── body: │ │ │ │ │ @ StatementsNode (location: (4,5)-(4,11)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ CallNode (location: (4,5)-(4,11)) - │ │ │ │ │ ├── flags: ∅ + │ │ │ │ │ ├── flags: newline │ │ │ │ │ ├── receiver: │ │ │ │ │ │ @ CallNode (location: (4,5)-(4,6)) │ │ │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -133,7 +148,7 @@ │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ └── arguments: (length: 1) │ │ │ │ │ │ └── @ IntegerNode (location: (4,10)-(4,11)) - │ │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ │ └── value: 4 │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── block: ∅ @@ -141,11 +156,13 @@ │ │ │ │ └── closing_loc: (4,11)-(4,12) = ")" │ │ │ ├── right: │ │ │ │ @ ParenthesesNode (location: (4,15)-(4,23)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── body: │ │ │ │ │ @ StatementsNode (location: (4,16)-(4,22)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ CallNode (location: (4,16)-(4,22)) - │ │ │ │ │ ├── flags: ∅ + │ │ │ │ │ ├── flags: newline │ │ │ │ │ ├── receiver: │ │ │ │ │ │ @ CallNode (location: (4,16)-(4,17)) │ │ │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -166,7 +183,7 @@ │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ └── arguments: (length: 1) │ │ │ │ │ │ └── @ IntegerNode (location: (4,21)-(4,22)) - │ │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ │ └── value: 4 │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── block: ∅ @@ -178,9 +195,10 @@ │ ├── then_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (5,2)-(5,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (5,2)-(5,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :foo @@ -192,6 +210,7 @@ │ ├── consequent: ∅ │ └── end_keyword_loc: (6,0)-(6,3) = "end" ├── @ IfNode (location: (7,0)-(8,3)) + │ ├── flags: newline │ ├── if_keyword_loc: (7,0)-(7,2) = "if" │ ├── predicate: │ │ @ FlipFlopNode (location: (7,3)-(7,8)) @@ -214,6 +233,7 @@ │ ├── consequent: ∅ │ └── end_keyword_loc: (8,0)-(8,3) = "end" └── @ IfNode (location: (9,0)-(10,3)) + ├── flags: newline ├── if_keyword_loc: (9,0)-(9,2) = "if" ├── predicate: │ @ FlipFlopNode (location: (9,3)-(9,8)) diff --git a/test/prism/snapshots/unparser/corpus/literal/for.txt b/test/prism/snapshots/unparser/corpus/literal/for.txt index 660c6b73f3fd93..a1722a8a099cf9 100644 --- a/test/prism/snapshots/unparser/corpus/literal/for.txt +++ b/test/prism/snapshots/unparser/corpus/literal/for.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(12,3)) +├── flags: ∅ ├── locals: [:a, :b] └── statements: @ StatementsNode (location: (1,0)-(12,3)) + ├── flags: ∅ └── body: (length: 4) ├── @ CallNode (location: (1,0)-(3,4)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :bar @@ -15,8 +17,10 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ ForNode (location: (1,4)-(3,3)) + │ │ ├── flags: ∅ │ │ ├── index: │ │ │ @ LocalVariableTargetNode (location: (1,8)-(1,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ ├── collection: @@ -32,9 +36,10 @@ │ │ │ └── block: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (2,2)-(2,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (2,2)-(2,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :baz @@ -50,8 +55,10 @@ │ ├── closing_loc: (3,3)-(3,4) = ")" │ └── block: ∅ ├── @ ForNode (location: (4,0)-(6,3)) + │ ├── flags: newline │ ├── index: │ │ @ LocalVariableTargetNode (location: (4,4)-(4,5)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── collection: @@ -67,9 +74,10 @@ │ │ └── block: ∅ │ ├── statements: │ │ @ StatementsNode (location: (5,2)-(5,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (5,2)-(5,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :baz @@ -83,17 +91,22 @@ │ ├── do_keyword_loc: (4,13)-(4,15) = "do" │ └── end_keyword_loc: (6,0)-(6,3) = "end" ├── @ ForNode (location: (7,0)-(9,3)) + │ ├── flags: newline │ ├── index: │ │ @ MultiTargetNode (location: (7,4)-(7,11)) + │ │ ├── flags: ∅ │ │ ├── lefts: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (7,5)-(7,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ ├── rest: │ │ │ @ SplatNode (location: (7,8)-(7,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (7,8)-(7,9) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableTargetNode (location: (7,9)-(7,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :b │ │ │ └── depth: 0 │ │ ├── rights: (length: 0) @@ -112,9 +125,10 @@ │ │ └── block: ∅ │ ├── statements: │ │ @ StatementsNode (location: (8,2)-(8,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (8,2)-(8,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :baz @@ -128,13 +142,17 @@ │ ├── do_keyword_loc: (7,19)-(7,21) = "do" │ └── end_keyword_loc: (9,0)-(9,3) = "end" └── @ ForNode (location: (10,0)-(12,3)) + ├── flags: newline ├── index: │ @ MultiTargetNode (location: (10,4)-(10,10)) + │ ├── flags: ∅ │ ├── lefts: (length: 2) │ │ ├── @ LocalVariableTargetNode (location: (10,5)-(10,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ └── @ LocalVariableTargetNode (location: (10,8)-(10,9)) + │ │ ├── flags: ∅ │ │ ├── name: :b │ │ └── depth: 0 │ ├── rest: ∅ @@ -154,9 +172,10 @@ │ └── block: ∅ ├── statements: │ @ StatementsNode (location: (11,2)-(11,5)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (11,2)-(11,5)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :baz diff --git a/test/prism/snapshots/unparser/corpus/literal/hookexe.txt b/test/prism/snapshots/unparser/corpus/literal/hookexe.txt index dabedbc5885895..bbcb8fee1b0da7 100644 --- a/test/prism/snapshots/unparser/corpus/literal/hookexe.txt +++ b/test/prism/snapshots/unparser/corpus/literal/hookexe.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(7,1)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(7,1)) + ├── flags: ∅ └── body: (length: 3) ├── @ PreExecutionNode (location: (1,0)-(3,1)) + │ ├── flags: newline │ ├── statements: │ │ @ StatementsNode (location: (2,2)-(2,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (2,2)-(2,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :foo @@ -21,7 +25,7 @@ │ ├── opening_loc: (1,6)-(1,7) = "{" │ └── closing_loc: (3,0)-(3,1) = "}" ├── @ CallNode (location: (4,0)-(4,3)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :bar @@ -31,11 +35,13 @@ │ ├── closing_loc: ∅ │ └── block: ∅ └── @ PostExecutionNode (location: (5,0)-(7,1)) + ├── flags: newline ├── statements: │ @ StatementsNode (location: (6,2)-(6,5)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (6,2)-(6,5)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :baz diff --git a/test/prism/snapshots/unparser/corpus/literal/if.txt b/test/prism/snapshots/unparser/corpus/literal/if.txt index 00eeba179c6782..f4a18c0c1200e6 100644 --- a/test/prism/snapshots/unparser/corpus/literal/if.txt +++ b/test/prism/snapshots/unparser/corpus/literal/if.txt @@ -1,13 +1,16 @@ @ ProgramNode (location: (1,0)-(36,3)) +├── flags: ∅ ├── locals: [:foo, :pair] └── statements: @ StatementsNode (location: (1,0)-(36,3)) + ├── flags: ∅ └── body: (length: 10) ├── @ IfNode (location: (1,0)-(3,3)) + │ ├── flags: newline │ ├── if_keyword_loc: (1,0)-(1,2) = "if" │ ├── predicate: │ │ @ MatchLastLineNode (location: (1,3)-(1,8)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,3)-(1,4) = "/" │ │ ├── content_loc: (1,4)-(1,7) = "foo" │ │ ├── closing_loc: (1,7)-(1,8) = "/" @@ -15,9 +18,10 @@ │ ├── then_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (2,2)-(2,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (2,2)-(2,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -29,73 +33,85 @@ │ ├── consequent: ∅ │ └── end_keyword_loc: (3,0)-(3,3) = "end" ├── @ IfNode (location: (4,0)-(6,3)) + │ ├── flags: newline │ ├── if_keyword_loc: (4,0)-(4,2) = "if" │ ├── predicate: │ │ @ IntegerNode (location: (4,3)-(4,4)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 3 │ ├── then_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (5,2)-(5,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (5,2)-(5,3)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 9 │ ├── consequent: ∅ │ └── end_keyword_loc: (6,0)-(6,3) = "end" ├── @ IfNode (location: (7,0)-(11,3)) + │ ├── flags: newline │ ├── if_keyword_loc: (7,0)-(7,2) = "if" │ ├── predicate: │ │ @ IntegerNode (location: (7,3)-(7,4)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 4 │ ├── then_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (8,2)-(8,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (8,2)-(8,3)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 5 │ ├── consequent: │ │ @ ElseNode (location: (9,0)-(11,3)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (9,0)-(9,4) = "else" │ │ ├── statements: │ │ │ @ StatementsNode (location: (10,2)-(10,3)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ IntegerNode (location: (10,2)-(10,3)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: newline, static_literal, decimal │ │ │ └── value: 6 │ │ └── end_keyword_loc: (11,0)-(11,3) = "end" │ └── end_keyword_loc: (11,0)-(11,3) = "end" ├── @ UnlessNode (location: (12,0)-(14,3)) + │ ├── flags: newline │ ├── keyword_loc: (12,0)-(12,6) = "unless" │ ├── predicate: │ │ @ IntegerNode (location: (12,7)-(12,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 3 │ ├── then_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (13,2)-(13,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ NilNode (location: (13,2)-(13,5)) + │ │ └── flags: newline, static_literal │ ├── consequent: ∅ │ └── end_keyword_loc: (14,0)-(14,3) = "end" ├── @ UnlessNode (location: (15,0)-(17,3)) + │ ├── flags: newline │ ├── keyword_loc: (15,0)-(15,6) = "unless" │ ├── predicate: │ │ @ IntegerNode (location: (15,7)-(15,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 3 │ ├── then_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (16,2)-(16,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (16,2)-(16,3)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 9 │ ├── consequent: ∅ │ └── end_keyword_loc: (17,0)-(17,3) = "end" ├── @ IfNode (location: (18,0)-(19,3)) + │ ├── flags: newline │ ├── if_keyword_loc: (18,0)-(18,2) = "if" │ ├── predicate: │ │ @ CallNode (location: (18,3)-(18,6)) @@ -113,25 +129,32 @@ │ ├── consequent: ∅ │ └── end_keyword_loc: (19,0)-(19,3) = "end" ├── @ ModuleNode (location: (21,0)-(23,3)) + │ ├── flags: newline │ ├── locals: [:foo] │ ├── module_keyword_loc: (21,0)-(21,6) = "module" │ ├── constant_path: │ │ @ ConstantReadNode (location: (21,7)-(21,8)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── body: │ │ @ StatementsNode (location: (22,2)-(22,18)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IfNode (location: (22,2)-(22,18)) + │ │ ├── flags: newline │ │ ├── if_keyword_loc: (22,12)-(22,14) = "if" │ │ ├── predicate: │ │ │ @ LocalVariableReadNode (location: (22,15)-(22,18)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :foo │ │ │ └── depth: 0 │ │ ├── then_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (22,2)-(22,11)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ LocalVariableWriteNode (location: (22,2)-(22,11)) + │ │ │ ├── flags: newline │ │ │ ├── name: :foo │ │ │ ├── depth: 0 │ │ │ ├── name_loc: (22,2)-(22,5) = "foo" @@ -152,25 +175,32 @@ │ ├── end_keyword_loc: (23,0)-(23,3) = "end" │ └── name: :A ├── @ ModuleNode (location: (25,0)-(27,3)) + │ ├── flags: newline │ ├── locals: [:foo] │ ├── module_keyword_loc: (25,0)-(25,6) = "module" │ ├── constant_path: │ │ @ ConstantReadNode (location: (25,7)-(25,8)) + │ │ ├── flags: ∅ │ │ └── name: :B │ ├── body: │ │ @ StatementsNode (location: (26,2)-(26,22)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ UnlessNode (location: (26,2)-(26,22)) + │ │ ├── flags: newline │ │ ├── keyword_loc: (26,12)-(26,18) = "unless" │ │ ├── predicate: │ │ │ @ LocalVariableReadNode (location: (26,19)-(26,22)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :foo │ │ │ └── depth: 0 │ │ ├── then_keyword_loc: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (26,2)-(26,11)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ LocalVariableWriteNode (location: (26,2)-(26,11)) + │ │ │ ├── flags: newline │ │ │ ├── name: :foo │ │ │ ├── depth: 0 │ │ │ ├── name_loc: (26,2)-(26,5) = "foo" @@ -191,6 +221,7 @@ │ ├── end_keyword_loc: (27,0)-(27,3) = "end" │ └── name: :B ├── @ UnlessNode (location: (28,0)-(30,3)) + │ ├── flags: newline │ ├── keyword_loc: (28,0)-(28,6) = "unless" │ ├── predicate: │ │ @ CallNode (location: (28,7)-(28,10)) @@ -206,8 +237,10 @@ │ ├── then_keyword_loc: ∅ │ ├── statements: │ │ @ StatementsNode (location: (29,2)-(29,11)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableWriteNode (location: (29,2)-(29,11)) + │ │ ├── flags: newline │ │ ├── name: :foo │ │ ├── depth: 0 │ │ ├── name_loc: (29,2)-(29,5) = "foo" @@ -226,6 +259,7 @@ │ ├── consequent: ∅ │ └── end_keyword_loc: (30,0)-(30,3) = "end" └── @ IfNode (location: (31,0)-(36,3)) + ├── flags: newline ├── if_keyword_loc: (31,0)-(31,2) = "if" ├── predicate: │ @ CallNode (location: (31,3)-(33,1)) @@ -239,11 +273,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (31,7)-(33,1)) + │ ├── flags: ∅ │ ├── locals: [:pair] │ ├── parameters: │ │ @ BlockParametersNode (location: (31,9)-(31,15)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (31,10)-(31,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (31,10)-(31,14)) │ │ │ │ ├── flags: ∅ @@ -259,8 +296,10 @@ │ │ └── closing_loc: (31,14)-(31,15) = "|" │ ├── body: │ │ @ StatementsNode (location: (32,2)-(32,6)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (32,2)-(32,6)) + │ │ ├── flags: newline │ │ ├── name: :pair │ │ └── depth: 0 │ ├── opening_loc: (31,7)-(31,8) = "{" @@ -268,20 +307,23 @@ ├── then_keyword_loc: ∅ ├── statements: │ @ StatementsNode (location: (34,2)-(35,5)) + │ ├── flags: ∅ │ └── body: (length: 2) │ ├── @ LocalVariableWriteNode (location: (34,2)-(34,13)) + │ │ ├── flags: newline │ │ ├── name: :pair │ │ ├── depth: 0 │ │ ├── name_loc: (34,2)-(34,6) = "pair" │ │ ├── value: │ │ │ @ SymbolNode (location: (34,9)-(34,13)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (34,9)-(34,10) = ":" │ │ │ ├── value_loc: (34,10)-(34,13) = "foo" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "foo" │ │ └── operator_loc: (34,7)-(34,8) = "=" │ └── @ LocalVariableReadNode (location: (35,2)-(35,5)) + │ ├── flags: newline │ ├── name: :foo │ └── depth: 0 ├── consequent: ∅ diff --git a/test/prism/snapshots/unparser/corpus/literal/kwbegin.txt b/test/prism/snapshots/unparser/corpus/literal/kwbegin.txt index 48e53af00e4e0f..068e20ad989c17 100644 --- a/test/prism/snapshots/unparser/corpus/literal/kwbegin.txt +++ b/test/prism/snapshots/unparser/corpus/literal/kwbegin.txt @@ -1,13 +1,17 @@ @ ProgramNode (location: (1,0)-(80,3)) +├── flags: ∅ ├── locals: [:foo, :bar, :exception] └── statements: @ StatementsNode (location: (1,0)-(80,3)) + ├── flags: ∅ └── body: (length: 14) ├── @ BeginNode (location: (1,0)-(3,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (1,0)-(1,5) = "begin" │ ├── statements: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (2,0)-(2,6)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (2,0)-(2,6) = "rescue" │ │ ├── exceptions: (length: 0) │ │ ├── operator_loc: ∅ @@ -18,23 +22,27 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (3,0)-(3,3) = "end" ├── @ BeginNode (location: (5,0)-(7,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (5,0)-(5,5) = "begin" │ ├── statements: ∅ │ ├── rescue_clause: ∅ │ ├── else_clause: ∅ │ ├── ensure_clause: │ │ @ EnsureNode (location: (6,0)-(7,3)) + │ │ ├── flags: ∅ │ │ ├── ensure_keyword_loc: (6,0)-(6,6) = "ensure" │ │ ├── statements: ∅ │ │ └── end_keyword_loc: (7,0)-(7,3) = "end" │ └── end_keyword_loc: (7,0)-(7,3) = "end" ├── @ BeginNode (location: (9,0)-(11,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (9,0)-(9,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (10,2)-(10,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (10,2)-(10,3)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -48,12 +56,14 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (11,0)-(11,3) = "end" ├── @ BeginNode (location: (13,0)-(17,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (13,0)-(13,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (14,2)-(14,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (14,2)-(14,3)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -64,15 +74,17 @@ │ │ └── block: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (15,0)-(16,3)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (15,0)-(15,6) = "rescue" │ │ ├── exceptions: (length: 0) │ │ ├── operator_loc: ∅ │ │ ├── reference: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (16,2)-(16,3)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (16,2)-(16,3)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -86,12 +98,14 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (17,0)-(17,3) = "end" ├── @ BeginNode (location: (19,0)-(24,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (19,0)-(19,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (20,2)-(21,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 2) │ │ ├── @ CallNode (location: (20,2)-(20,3)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :a @@ -101,7 +115,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ └── @ CallNode (location: (21,2)-(21,3)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :b @@ -112,15 +126,17 @@ │ │ └── block: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (22,0)-(23,3)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (22,0)-(22,6) = "rescue" │ │ ├── exceptions: (length: 0) │ │ ├── operator_loc: ∅ │ │ ├── reference: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (23,2)-(23,3)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (23,2)-(23,3)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -134,13 +150,16 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (24,0)-(24,3) = "end" ├── @ BeginNode (location: (26,0)-(28,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (26,0)-(26,5) = "begin" │ ├── statements: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (27,0)-(27,8)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (27,0)-(27,6) = "rescue" │ │ ├── exceptions: (length: 1) │ │ │ └── @ ConstantReadNode (location: (27,7)-(27,8)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :A │ │ ├── operator_loc: ∅ │ │ ├── reference: ∅ @@ -150,17 +169,21 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (28,0)-(28,3) = "end" ├── @ BeginNode (location: (30,0)-(32,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (30,0)-(30,5) = "begin" │ ├── statements: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (31,0)-(31,15)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (31,0)-(31,6) = "rescue" │ │ ├── exceptions: (length: 1) │ │ │ └── @ ConstantReadNode (location: (31,7)-(31,8)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :A │ │ ├── operator_loc: (31,9)-(31,11) = "=>" │ │ ├── reference: │ │ │ @ LocalVariableTargetNode (location: (31,12)-(31,15)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :foo │ │ │ └── depth: 0 │ │ ├── statements: ∅ @@ -169,12 +192,14 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (32,0)-(32,3) = "end" ├── @ BeginNode (location: (34,0)-(42,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (34,0)-(34,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (35,2)-(35,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (35,2)-(35,3)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :a @@ -185,17 +210,20 @@ │ │ └── block: ∅ │ ├── rescue_clause: │ │ @ RescueNode (location: (36,0)-(39,3)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (36,0)-(36,6) = "rescue" │ │ ├── exceptions: (length: 1) │ │ │ └── @ ConstantReadNode (location: (36,7)-(36,8)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :A │ │ ├── operator_loc: ∅ │ │ ├── reference: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (37,2)-(37,3)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (37,2)-(37,3)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :b @@ -206,17 +234,20 @@ │ │ │ └── block: ∅ │ │ └── consequent: │ │ @ RescueNode (location: (38,0)-(39,3)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (38,0)-(38,6) = "rescue" │ │ ├── exceptions: (length: 1) │ │ │ └── @ ConstantReadNode (location: (38,7)-(38,8)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :B │ │ ├── operator_loc: ∅ │ │ ├── reference: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (39,2)-(39,3)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (39,2)-(39,3)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :c @@ -229,12 +260,14 @@ │ ├── else_clause: ∅ │ ├── ensure_clause: │ │ @ EnsureNode (location: (40,0)-(42,3)) + │ │ ├── flags: ∅ │ │ ├── ensure_keyword_loc: (40,0)-(40,6) = "ensure" │ │ ├── statements: │ │ │ @ StatementsNode (location: (41,2)-(41,3)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (41,2)-(41,3)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :d @@ -246,20 +279,25 @@ │ │ └── end_keyword_loc: (42,0)-(42,3) = "end" │ └── end_keyword_loc: (42,0)-(42,3) = "end" ├── @ BeginNode (location: (44,0)-(53,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (44,0)-(44,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (45,2)-(49,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ BeginNode (location: (45,2)-(49,5)) + │ │ ├── flags: newline │ │ ├── begin_keyword_loc: (45,2)-(45,7) = "begin" │ │ ├── statements: │ │ │ @ StatementsNode (location: (46,4)-(47,7)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 2) │ │ │ ├── @ LocalVariableReadNode (location: (46,4)-(46,7)) + │ │ │ │ ├── flags: newline │ │ │ │ ├── name: :foo │ │ │ │ └── depth: 0 │ │ │ └── @ CallNode (location: (47,4)-(47,7)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :bar @@ -270,6 +308,7 @@ │ │ │ └── block: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (48,2)-(48,8)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (48,2)-(48,8) = "rescue" │ │ │ ├── exceptions: (length: 0) │ │ │ ├── operator_loc: ∅ @@ -281,15 +320,17 @@ │ │ └── end_keyword_loc: (49,2)-(49,5) = "end" │ ├── rescue_clause: │ │ @ RescueNode (location: (50,0)-(52,5)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (50,0)-(50,6) = "rescue" │ │ ├── exceptions: (length: 0) │ │ ├── operator_loc: ∅ │ │ ├── reference: ∅ │ │ ├── statements: │ │ │ @ StatementsNode (location: (51,2)-(52,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 2) │ │ │ ├── @ CallNode (location: (51,2)-(51,5)) - │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ ├── receiver: ∅ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :baz @@ -299,7 +340,7 @@ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── block: ∅ │ │ │ └── @ CallNode (location: (52,2)-(52,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :bar @@ -313,11 +354,14 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (53,0)-(53,3) = "end" ├── @ BeginNode (location: (55,0)-(58,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (55,0)-(55,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (56,2)-(56,35)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ RescueModifierNode (location: (56,2)-(56,35)) + │ │ ├── flags: newline │ │ ├── expression: │ │ │ @ CallNode (location: (56,2)-(56,18)) │ │ │ ├── flags: ignore_visibility @@ -331,12 +375,14 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ ConstantReadNode (location: (56,8)-(56,17)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :Exception │ │ │ ├── closing_loc: (56,17)-(56,18) = ")" │ │ │ └── block: ∅ │ │ ├── keyword_loc: (56,19)-(56,25) = "rescue" │ │ └── rescue_expression: │ │ @ LocalVariableWriteNode (location: (56,26)-(56,35)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ ├── depth: 0 │ │ ├── name_loc: (56,26)-(56,29) = "foo" @@ -354,9 +400,11 @@ │ │ └── operator_loc: (56,30)-(56,31) = "=" │ ├── rescue_clause: │ │ @ RescueNode (location: (57,0)-(57,16)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (57,0)-(57,6) = "rescue" │ │ ├── exceptions: (length: 1) │ │ │ └── @ ConstantReadNode (location: (57,7)-(57,16)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Exception │ │ ├── operator_loc: ∅ │ │ ├── reference: ∅ @@ -366,26 +414,33 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (58,0)-(58,3) = "end" ├── @ BeginNode (location: (60,0)-(64,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (60,0)-(60,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (61,2)-(61,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (61,2)-(61,5)) + │ │ ├── flags: newline │ │ ├── name: :foo │ │ └── depth: 0 │ ├── rescue_clause: │ │ @ RescueNode (location: (62,0)-(63,5)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (62,0)-(62,6) = "rescue" │ │ ├── exceptions: (length: 0) │ │ ├── operator_loc: (62,7)-(62,9) = "=>" │ │ ├── reference: │ │ │ @ LocalVariableTargetNode (location: (62,10)-(62,13)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── statements: │ │ │ @ StatementsNode (location: (63,2)-(63,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ LocalVariableReadNode (location: (63,2)-(63,5)) + │ │ │ ├── flags: newline │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ └── consequent: ∅ @@ -393,30 +448,39 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (64,0)-(64,3) = "end" ├── @ BeginNode (location: (66,0)-(70,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (66,0)-(66,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (67,2)-(67,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (67,2)-(67,5)) + │ │ ├── flags: newline │ │ ├── name: :foo │ │ └── depth: 0 │ ├── rescue_clause: │ │ @ RescueNode (location: (68,0)-(69,5)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (68,0)-(68,6) = "rescue" │ │ ├── exceptions: (length: 2) │ │ │ ├── @ ConstantReadNode (location: (68,7)-(68,16)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :Exception │ │ │ └── @ ConstantReadNode (location: (68,18)-(68,23)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :Other │ │ ├── operator_loc: (68,24)-(68,26) = "=>" │ │ ├── reference: │ │ │ @ LocalVariableTargetNode (location: (68,27)-(68,30)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── statements: │ │ │ @ StatementsNode (location: (69,2)-(69,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ LocalVariableReadNode (location: (69,2)-(69,5)) + │ │ │ ├── flags: newline │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ └── consequent: ∅ @@ -424,35 +488,44 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (70,0)-(70,3) = "end" ├── @ BeginNode (location: (72,0)-(76,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (72,0)-(72,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (73,2)-(73,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (73,2)-(73,5)) + │ │ ├── flags: newline │ │ ├── name: :bar │ │ └── depth: 0 │ ├── rescue_clause: │ │ @ RescueNode (location: (74,0)-(75,5)) + │ │ ├── flags: ∅ │ │ ├── keyword_loc: (74,0)-(74,6) = "rescue" │ │ ├── exceptions: (length: 2) │ │ │ ├── @ ConstantReadNode (location: (74,7)-(74,16)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :SomeError │ │ │ └── @ SplatNode (location: (74,18)-(74,22)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (74,18)-(74,19) = "*" │ │ │ └── expression: │ │ │ @ LocalVariableReadNode (location: (74,19)-(74,22)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :bar │ │ │ └── depth: 0 │ │ ├── operator_loc: (74,23)-(74,25) = "=>" │ │ ├── reference: │ │ │ @ LocalVariableTargetNode (location: (74,26)-(74,35)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :exception │ │ │ └── depth: 0 │ │ ├── statements: │ │ │ @ StatementsNode (location: (75,2)-(75,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (75,2)-(75,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :baz @@ -466,20 +539,25 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (76,0)-(76,3) = "end" └── @ SingletonClassNode (location: (78,0)-(80,3)) + ├── flags: newline ├── locals: [] ├── class_keyword_loc: (78,0)-(78,5) = "class" ├── operator_loc: (78,6)-(78,8) = "<<" ├── expression: │ @ SelfNode (location: (78,9)-(78,13)) + │ └── flags: ∅ ├── body: │ @ StatementsNode (location: (79,2)-(79,23)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ RescueModifierNode (location: (79,2)-(79,23)) + │ ├── flags: newline │ ├── expression: │ │ @ UndefNode (location: (79,2)-(79,12)) + │ │ ├── flags: ∅ │ │ ├── names: (length: 1) │ │ │ └── @ SymbolNode (location: (79,8)-(79,12)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (79,8)-(79,9) = ":" │ │ │ ├── value_loc: (79,9)-(79,12) = "bar" │ │ │ ├── closing_loc: ∅ @@ -488,4 +566,5 @@ │ ├── keyword_loc: (79,13)-(79,19) = "rescue" │ └── rescue_expression: │ @ NilNode (location: (79,20)-(79,23)) + │ └── flags: static_literal └── end_keyword_loc: (80,0)-(80,3) = "end" diff --git a/test/prism/snapshots/unparser/corpus/literal/lambda.txt b/test/prism/snapshots/unparser/corpus/literal/lambda.txt index 3594787bcab0c0..863678f17b4a8f 100644 --- a/test/prism/snapshots/unparser/corpus/literal/lambda.txt +++ b/test/prism/snapshots/unparser/corpus/literal/lambda.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(13,1)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(13,1)) + ├── flags: ∅ └── body: (length: 6) ├── @ CallNode (location: (1,0)-(2,1)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :lambda @@ -14,13 +16,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (1,7)-(2,1)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: ∅ │ ├── opening_loc: (1,7)-(1,8) = "{" │ └── closing_loc: (2,0)-(2,1) = "}" ├── @ CallNode (location: (3,0)-(5,1)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :lambda @@ -30,11 +33,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (3,7)-(5,1)) + │ ├── flags: ∅ │ ├── locals: [:a, :b] │ ├── parameters: │ │ @ BlockParametersNode (location: (3,9)-(3,15)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (3,10)-(3,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 2) │ │ │ │ ├── @ RequiredParameterNode (location: (3,10)-(3,11)) │ │ │ │ │ ├── flags: ∅ @@ -53,33 +59,40 @@ │ │ └── closing_loc: (3,14)-(3,15) = "|" │ ├── body: │ │ @ StatementsNode (location: (4,2)-(4,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (4,2)-(4,3)) + │ │ ├── flags: newline │ │ ├── name: :a │ │ └── depth: 0 │ ├── opening_loc: (3,7)-(3,8) = "{" │ └── closing_loc: (5,0)-(5,1) = "}" ├── @ LambdaNode (location: (6,0)-(7,1)) + │ ├── flags: newline │ ├── locals: [] │ ├── operator_loc: (6,0)-(6,2) = "->" │ ├── opening_loc: (6,5)-(6,6) = "{" │ ├── closing_loc: (7,0)-(7,1) = "}" │ ├── parameters: │ │ @ BlockParametersNode (location: (6,2)-(6,4)) + │ │ ├── flags: ∅ │ │ ├── parameters: ∅ │ │ ├── locals: (length: 0) │ │ ├── opening_loc: (6,2)-(6,3) = "(" │ │ └── closing_loc: (6,3)-(6,4) = ")" │ └── body: ∅ ├── @ LambdaNode (location: (8,0)-(9,1)) + │ ├── flags: newline │ ├── locals: [:a] │ ├── operator_loc: (8,0)-(8,2) = "->" │ ├── opening_loc: (8,6)-(8,7) = "{" │ ├── closing_loc: (9,0)-(9,1) = "}" │ ├── parameters: │ │ @ BlockParametersNode (location: (8,2)-(8,5)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (8,3)-(8,4)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (8,3)-(8,4)) │ │ │ │ ├── flags: ∅ @@ -95,14 +108,17 @@ │ │ └── closing_loc: (8,4)-(8,5) = ")" │ └── body: ∅ ├── @ LambdaNode (location: (10,0)-(11,1)) + │ ├── flags: newline │ ├── locals: [:a, :b] │ ├── operator_loc: (10,0)-(10,2) = "->" │ ├── opening_loc: (10,9)-(10,10) = "{" │ ├── closing_loc: (11,0)-(11,1) = "}" │ ├── parameters: │ │ @ BlockParametersNode (location: (10,2)-(10,8)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (10,3)-(10,7)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 2) │ │ │ │ ├── @ RequiredParameterNode (location: (10,3)-(10,4)) │ │ │ │ │ ├── flags: ∅ @@ -121,14 +137,17 @@ │ │ └── closing_loc: (10,7)-(10,8) = ")" │ └── body: ∅ └── @ LambdaNode (location: (12,0)-(13,1)) + ├── flags: newline ├── locals: [:a, :b, :c] ├── operator_loc: (12,0)-(12,2) = "->" ├── opening_loc: (12,12)-(12,13) = "{" ├── closing_loc: (13,0)-(13,1) = "}" ├── parameters: │ @ BlockParametersNode (location: (12,2)-(12,11)) + │ ├── flags: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (12,3)-(12,7)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 2) │ │ │ ├── @ RequiredParameterNode (location: (12,3)-(12,4)) │ │ │ │ ├── flags: ∅ diff --git a/test/prism/snapshots/unparser/corpus/literal/literal.txt b/test/prism/snapshots/unparser/corpus/literal/literal.txt index ddb10456bf0a7a..430a8a06793857 100644 --- a/test/prism/snapshots/unparser/corpus/literal/literal.txt +++ b/test/prism/snapshots/unparser/corpus/literal/literal.txt @@ -1,15 +1,19 @@ @ ProgramNode (location: (1,0)-(91,2)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(91,2)) + ├── flags: ∅ └── body: (length: 78) ├── @ HashNode (location: (1,0)-(1,38)) + │ ├── flags: newline │ ├── opening_loc: (1,0)-(1,1) = "{" │ ├── elements: (length: 2) │ │ ├── @ AssocNode (location: (1,2)-(1,21)) + │ │ │ ├── flags: ∅ │ │ │ ├── key: │ │ │ │ @ StringNode (location: (1,2)-(1,7)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: (1,2)-(1,3) = "\"" │ │ │ │ ├── content_loc: (1,3)-(1,6) = "foo" │ │ │ │ ├── closing_loc: (1,6)-(1,7) = "\"" @@ -20,17 +24,18 @@ │ │ │ │ ├── opening_loc: (1,11)-(1,21) = "<<-HEREDOC" │ │ │ │ ├── parts: (length: 3) │ │ │ │ │ ├── @ StringNode (location: (2,0)-(2,2)) - │ │ │ │ │ │ ├── flags: frozen + │ │ │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ │ ├── content_loc: (2,0)-(2,2) = " " │ │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ │ └── unescaped: " " │ │ │ │ │ ├── @ EmbeddedStatementsNode (location: (2,2)-(2,5)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ ├── opening_loc: (2,2)-(2,4) = "\#{" │ │ │ │ │ │ ├── statements: ∅ │ │ │ │ │ │ └── closing_loc: (2,4)-(2,5) = "}" │ │ │ │ │ └── @ StringNode (location: (2,5)-(3,0)) - │ │ │ │ │ ├── flags: frozen + │ │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── content_loc: (2,5)-(3,0) = "\n" │ │ │ │ │ ├── closing_loc: ∅ @@ -38,16 +43,17 @@ │ │ │ │ └── closing_loc: (3,0)-(4,0) = "HEREDOC\n" │ │ │ └── operator_loc: (1,8)-(1,10) = "=>" │ │ └── @ AssocNode (location: (1,23)-(1,36)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ StringNode (location: (1,23)-(1,28)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: (1,23)-(1,24) = "\"" │ │ │ ├── content_loc: (1,24)-(1,27) = "bar" │ │ │ ├── closing_loc: (1,27)-(1,28) = "\"" │ │ │ └── unescaped: "bar" │ │ ├── value: │ │ │ @ SymbolNode (location: (1,32)-(1,36)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (1,32)-(1,33) = ":" │ │ │ ├── value_loc: (1,33)-(1,36) = "baz" │ │ │ ├── closing_loc: ∅ @@ -55,12 +61,14 @@ │ │ └── operator_loc: (1,29)-(1,31) = "=>" │ └── closing_loc: (1,37)-(1,38) = "}" ├── @ HashNode (location: (4,0)-(4,31)) + │ ├── flags: newline │ ├── opening_loc: (4,0)-(4,1) = "{" │ ├── elements: (length: 2) │ │ ├── @ AssocNode (location: (4,2)-(4,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── key: │ │ │ │ @ StringNode (location: (4,2)-(4,7)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: (4,2)-(4,3) = "\"" │ │ │ │ ├── content_loc: (4,3)-(4,6) = "foo" │ │ │ │ ├── closing_loc: (4,6)-(4,7) = "\"" @@ -74,16 +82,17 @@ │ │ │ │ └── unescaped: "" │ │ │ └── operator_loc: (4,8)-(4,10) = "=>" │ │ └── @ AssocNode (location: (4,16)-(4,29)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ StringNode (location: (4,16)-(4,21)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: (4,16)-(4,17) = "\"" │ │ │ ├── content_loc: (4,17)-(4,20) = "bar" │ │ │ ├── closing_loc: (4,20)-(4,21) = "\"" │ │ │ └── unescaped: "bar" │ │ ├── value: │ │ │ @ SymbolNode (location: (4,25)-(4,29)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (4,25)-(4,26) = ":" │ │ │ ├── value_loc: (4,26)-(4,29) = "baz" │ │ │ ├── closing_loc: ∅ @@ -91,7 +100,7 @@ │ │ └── operator_loc: (4,22)-(4,24) = "=>" │ └── closing_loc: (4,30)-(4,31) = "}" ├── @ ArrayNode (location: (5,0)-(5,12)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 2) │ │ ├── @ StringNode (location: (5,1)-(5,6)) │ │ │ ├── flags: ∅ @@ -108,7 +117,7 @@ │ ├── opening_loc: (5,0)-(5,1) = "[" │ └── closing_loc: (5,11)-(5,12) = "]" ├── @ CallNode (location: (6,0)-(6,15)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (6,0)-(6,13)) │ │ ├── flags: ignore_visibility @@ -126,17 +135,18 @@ │ │ │ ├── opening_loc: (6,2)-(6,12) = "<<-HEREDOC" │ │ │ ├── parts: (length: 3) │ │ │ │ ├── @ StringNode (location: (7,0)-(7,2)) - │ │ │ │ │ ├── flags: frozen + │ │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── content_loc: (7,0)-(7,2) = " " │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ └── unescaped: " " │ │ │ │ ├── @ EmbeddedStatementsNode (location: (7,2)-(7,5)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── opening_loc: (7,2)-(7,4) = "\#{" │ │ │ │ │ ├── statements: ∅ │ │ │ │ │ └── closing_loc: (7,4)-(7,5) = "}" │ │ │ │ └── @ StringNode (location: (7,5)-(8,0)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── content_loc: (7,5)-(8,0) = "\n" │ │ │ │ ├── closing_loc: ∅ @@ -152,7 +162,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (9,0)-(9,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (9,0)-(9,6)) │ │ ├── flags: ignore_visibility @@ -181,12 +191,14 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ HashNode (location: (10,0)-(10,30)) + │ ├── flags: newline │ ├── opening_loc: (10,0)-(10,1) = "{" │ ├── elements: (length: 2) │ │ ├── @ AssocNode (location: (10,2)-(10,21)) + │ │ │ ├── flags: ∅ │ │ │ ├── key: │ │ │ │ @ StringNode (location: (10,2)-(10,7)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: (10,2)-(10,3) = "\"" │ │ │ │ ├── content_loc: (10,3)-(10,6) = "foo" │ │ │ │ ├── closing_loc: (10,6)-(10,7) = "\"" @@ -197,17 +209,18 @@ │ │ │ │ ├── opening_loc: (10,11)-(10,21) = "<<-HEREDOC" │ │ │ │ ├── parts: (length: 3) │ │ │ │ │ ├── @ StringNode (location: (11,0)-(11,2)) - │ │ │ │ │ │ ├── flags: frozen + │ │ │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ │ ├── content_loc: (11,0)-(11,2) = " " │ │ │ │ │ │ ├── closing_loc: ∅ │ │ │ │ │ │ └── unescaped: " " │ │ │ │ │ ├── @ EmbeddedStatementsNode (location: (11,2)-(11,5)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ ├── opening_loc: (11,2)-(11,4) = "\#{" │ │ │ │ │ │ ├── statements: ∅ │ │ │ │ │ │ └── closing_loc: (11,4)-(11,5) = "}" │ │ │ │ │ └── @ StringNode (location: (11,5)-(12,0)) - │ │ │ │ │ ├── flags: frozen + │ │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── content_loc: (11,5)-(12,0) = "\n" │ │ │ │ │ ├── closing_loc: ∅ @@ -215,6 +228,7 @@ │ │ │ │ └── closing_loc: (12,0)-(13,0) = "HEREDOC\n" │ │ │ └── operator_loc: (10,8)-(10,10) = "=>" │ │ └── @ AssocSplatNode (location: (10,23)-(10,28)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ CallNode (location: (10,25)-(10,28)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -229,12 +243,14 @@ │ │ └── operator_loc: (10,23)-(10,25) = "**" │ └── closing_loc: (10,29)-(10,30) = "}" ├── @ HashNode (location: (13,0)-(13,23)) + │ ├── flags: newline │ ├── opening_loc: (13,0)-(13,1) = "{" │ ├── elements: (length: 2) │ │ ├── @ AssocNode (location: (13,2)-(13,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── key: │ │ │ │ @ StringNode (location: (13,2)-(13,7)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: (13,2)-(13,3) = "\"" │ │ │ │ ├── content_loc: (13,3)-(13,6) = "foo" │ │ │ │ ├── closing_loc: (13,6)-(13,7) = "\"" @@ -248,6 +264,7 @@ │ │ │ │ └── unescaped: "" │ │ │ └── operator_loc: (13,8)-(13,10) = "=>" │ │ └── @ AssocSplatNode (location: (13,16)-(13,21)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ CallNode (location: (13,18)-(13,21)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -262,45 +279,51 @@ │ │ └── operator_loc: (13,16)-(13,18) = "**" │ └── closing_loc: (13,22)-(13,23) = "}" ├── @ InterpolatedStringNode (location: (14,0)-(14,14)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (14,0)-(14,1) = "\"" │ ├── parts: (length: 5) │ │ ├── @ EmbeddedVariableNode (location: (14,1)-(14,4)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (14,1)-(14,2) = "#" │ │ │ └── variable: │ │ │ @ InstanceVariableReadNode (location: (14,2)-(14,4)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :@a │ │ ├── @ StringNode (location: (14,4)-(14,5)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (14,4)-(14,5) = " " │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: " " │ │ ├── @ EmbeddedVariableNode (location: (14,5)-(14,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (14,5)-(14,6) = "#" │ │ │ └── variable: │ │ │ @ ClassVariableReadNode (location: (14,6)-(14,9)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :@@a │ │ ├── @ StringNode (location: (14,9)-(14,10)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (14,9)-(14,10) = " " │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: " " │ │ └── @ EmbeddedVariableNode (location: (14,10)-(14,13)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (14,10)-(14,11) = "#" │ │ └── variable: │ │ @ GlobalVariableReadNode (location: (14,11)-(14,13)) + │ │ ├── flags: ∅ │ │ └── name: :$a │ └── closing_loc: (14,13)-(14,14) = "\"" ├── @ IntegerNode (location: (15,0)-(15,1)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 0 ├── @ CallNode (location: (16,0)-(16,3)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ IntegerNode (location: (16,1)-(16,3)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── call_operator_loc: ∅ │ ├── name: :+@ @@ -310,83 +333,93 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ IntegerNode (location: (17,0)-(17,1)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 1 ├── @ IntegerNode (location: (18,0)-(18,1)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 1 ├── @ RationalNode (location: (19,0)-(19,2)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ ├── numerator: 1 │ └── denominator: 1 ├── @ RationalNode (location: (20,0)-(20,4)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ ├── numerator: 3 │ └── denominator: 2 ├── @ RationalNode (location: (21,0)-(21,4)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ ├── numerator: 13 │ └── denominator: 10 ├── @ ImaginaryNode (location: (22,0)-(22,2)) + │ ├── flags: newline, static_literal │ └── numeric: │ @ IntegerNode (location: (22,0)-(22,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 5 ├── @ ImaginaryNode (location: (23,0)-(23,3)) + │ ├── flags: newline, static_literal │ └── numeric: │ @ IntegerNode (location: (23,0)-(23,2)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: -5 ├── @ ImaginaryNode (location: (24,0)-(24,4)) + │ ├── flags: newline, static_literal │ └── numeric: │ @ FloatNode (location: (24,0)-(24,3)) + │ ├── flags: static_literal │ └── value: 0.6 ├── @ ImaginaryNode (location: (25,0)-(25,5)) + │ ├── flags: newline, static_literal │ └── numeric: │ @ FloatNode (location: (25,0)-(25,4)) + │ ├── flags: static_literal │ └── value: -0.6 ├── @ ImaginaryNode (location: (26,0)-(26,32)) + │ ├── flags: newline, static_literal │ └── numeric: │ @ IntegerNode (location: (26,0)-(26,31)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1000000000000000000000000000000 ├── @ ImaginaryNode (location: (27,0)-(27,3)) + │ ├── flags: newline, static_literal │ └── numeric: │ @ RationalNode (location: (27,0)-(27,2)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ ├── numerator: 1 │ └── denominator: 1 ├── @ InterpolatedStringNode (location: (28,0)-(28,11)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── opening_loc: ∅ │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (28,0)-(28,5)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: (28,0)-(28,1) = "\"" │ │ │ ├── content_loc: (28,1)-(28,4) = "foo" │ │ │ ├── closing_loc: (28,4)-(28,5) = "\"" │ │ │ └── unescaped: "foo" │ │ └── @ StringNode (location: (28,6)-(28,11)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: (28,6)-(28,7) = "\"" │ │ ├── content_loc: (28,7)-(28,10) = "bar" │ │ ├── closing_loc: (28,10)-(28,11) = "\"" │ │ └── unescaped: "bar" │ └── closing_loc: ∅ ├── @ InterpolatedStringNode (location: (29,0)-(29,15)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (29,0)-(29,1) = "\"" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (29,1)-(29,8)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (29,1)-(29,8) = "foobar " │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "foobar " │ │ └── @ EmbeddedStatementsNode (location: (29,8)-(29,14)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (29,8)-(29,10) = "\#{" │ │ ├── statements: │ │ │ @ StatementsNode (location: (29,10)-(29,13)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (29,10)-(29,13)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -401,239 +434,256 @@ │ │ └── closing_loc: (29,13)-(29,14) = "}" │ └── closing_loc: (29,14)-(29,15) = "\"" ├── @ InterpolatedStringNode (location: (30,0)-(30,12)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (30,0)-(30,1) = "\"" │ ├── parts: (length: 3) │ │ ├── @ StringNode (location: (30,1)-(30,4)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (30,1)-(30,4) = "foo" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "foo" │ │ ├── @ EmbeddedStatementsNode (location: (30,4)-(30,8)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (30,4)-(30,6) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (30,6)-(30,7)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ IntegerNode (location: (30,6)-(30,7)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── closing_loc: (30,7)-(30,8) = "}" │ │ └── @ StringNode (location: (30,8)-(30,11)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (30,8)-(30,11) = "bar" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "bar" │ └── closing_loc: (30,11)-(30,12) = "\"" ├── @ InterpolatedStringNode (location: (31,0)-(31,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (31,0)-(31,1) = "\"" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (31,1)-(31,5)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (31,1)-(31,5) = "\\\\\\\\" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "\\\\" │ │ └── @ EmbeddedStatementsNode (location: (31,5)-(31,8)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (31,5)-(31,7) = "\#{" │ │ ├── statements: ∅ │ │ └── closing_loc: (31,7)-(31,8) = "}" │ └── closing_loc: (31,8)-(31,9) = "\"" ├── @ InterpolatedStringNode (location: (32,0)-(32,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (32,0)-(32,1) = "\"" │ ├── parts: (length: 2) │ │ ├── @ EmbeddedStatementsNode (location: (32,1)-(32,4)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (32,1)-(32,3) = "\#{" │ │ │ ├── statements: ∅ │ │ │ └── closing_loc: (32,3)-(32,4) = "}" │ │ └── @ StringNode (location: (32,4)-(32,8)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (32,4)-(32,8) = "\\\#{}" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "\#{}" │ └── closing_loc: (32,8)-(32,9) = "\"" ├── @ InterpolatedStringNode (location: (33,0)-(33,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (33,0)-(33,1) = "\"" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (33,1)-(33,5)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (33,1)-(33,5) = "\\\#{}" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "\#{}" │ │ └── @ EmbeddedStatementsNode (location: (33,5)-(33,8)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (33,5)-(33,7) = "\#{" │ │ ├── statements: ∅ │ │ └── closing_loc: (33,7)-(33,8) = "}" │ └── closing_loc: (33,8)-(33,9) = "\"" ├── @ StringNode (location: (34,0)-(34,15)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (34,0)-(34,1) = "\"" │ ├── content_loc: (34,1)-(34,14) = "foo\\\\\\\#{@bar}" │ ├── closing_loc: (34,14)-(34,15) = "\"" │ └── unescaped: "foo\\\#{@bar}" ├── @ StringNode (location: (35,0)-(35,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (35,0)-(35,1) = "\"" │ ├── content_loc: (35,1)-(35,3) = "\\\"" │ ├── closing_loc: (35,3)-(35,4) = "\"" │ └── unescaped: "\"" ├── @ StringNode (location: (36,0)-(36,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (36,0)-(36,1) = "\"" │ ├── content_loc: (36,1)-(36,8) = "foo bar" │ ├── closing_loc: (36,8)-(36,9) = "\"" │ └── unescaped: "foo bar" ├── @ StringNode (location: (37,0)-(37,10)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (37,0)-(37,1) = "\"" │ ├── content_loc: (37,1)-(37,9) = "foo\\nbar" │ ├── closing_loc: (37,9)-(37,10) = "\"" │ └── unescaped: "foo\nbar" ├── @ XStringNode (location: (38,0)-(38,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (38,0)-(38,1) = "`" │ ├── content_loc: (38,1)-(38,4) = "foo" │ ├── closing_loc: (38,4)-(38,5) = "`" │ └── unescaped: "foo" ├── @ InterpolatedXStringNode (location: (39,0)-(39,12)) + │ ├── flags: newline │ ├── opening_loc: (39,0)-(39,1) = "`" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (39,1)-(39,4)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (39,1)-(39,4) = "foo" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "foo" │ │ └── @ EmbeddedStatementsNode (location: (39,4)-(39,11)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (39,4)-(39,6) = "\#{" │ │ ├── statements: │ │ │ @ StatementsNode (location: (39,6)-(39,10)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ InstanceVariableReadNode (location: (39,6)-(39,10)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :@bar │ │ └── closing_loc: (39,10)-(39,11) = "}" │ └── closing_loc: (39,11)-(39,12) = "`" ├── @ XStringNode (location: (40,0)-(40,3)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (40,0)-(40,1) = "`" │ ├── content_loc: (40,1)-(40,2) = ")" │ ├── closing_loc: (40,2)-(40,3) = "`" │ └── unescaped: ")" ├── @ XStringNode (location: (41,0)-(41,4)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (41,0)-(41,1) = "`" │ ├── content_loc: (41,1)-(41,3) = "\\`" │ ├── closing_loc: (41,3)-(41,4) = "`" │ └── unescaped: "`" ├── @ XStringNode (location: (42,0)-(42,3)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (42,0)-(42,1) = "`" │ ├── content_loc: (42,1)-(42,2) = "\"" │ ├── closing_loc: (42,2)-(42,3) = "`" │ └── unescaped: "\"" ├── @ SymbolNode (location: (43,0)-(43,4)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (43,0)-(43,1) = ":" │ ├── value_loc: (43,1)-(43,4) = "foo" │ ├── closing_loc: ∅ │ └── unescaped: "foo" ├── @ SymbolNode (location: (44,0)-(44,6)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (44,0)-(44,2) = ":\"" │ ├── value_loc: (44,2)-(44,5) = "A B" │ ├── closing_loc: (44,5)-(44,6) = "\"" │ └── unescaped: "A B" ├── @ SymbolNode (location: (45,0)-(45,4)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (45,0)-(45,1) = ":" │ ├── value_loc: (45,1)-(45,4) = "foo" │ ├── closing_loc: ∅ │ └── unescaped: "foo" ├── @ SymbolNode (location: (46,0)-(46,6)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (46,0)-(46,2) = ":\"" │ ├── value_loc: (46,2)-(46,5) = "A B" │ ├── closing_loc: (46,5)-(46,6) = "\"" │ └── unescaped: "A B" ├── @ SymbolNode (location: (47,0)-(47,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (47,0)-(47,2) = ":\"" │ ├── value_loc: (47,2)-(47,6) = "A\\\"B" │ ├── closing_loc: (47,6)-(47,7) = "\"" │ └── unescaped: "A\"B" ├── @ SymbolNode (location: (48,0)-(48,3)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── opening_loc: (48,0)-(48,2) = ":\"" │ ├── value_loc: (1,0)-(1,0) = "" │ ├── closing_loc: (48,2)-(48,3) = "\"" │ └── unescaped: "" ├── @ RegularExpressionNode (location: (49,0)-(49,5)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (49,0)-(49,1) = "/" │ ├── content_loc: (49,1)-(49,4) = "foo" │ ├── closing_loc: (49,4)-(49,5) = "/" │ └── unescaped: "foo" ├── @ RegularExpressionNode (location: (50,0)-(50,28)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (50,0)-(50,1) = "/" │ ├── content_loc: (50,1)-(50,27) = "[^-+',.\\/:@[:alnum:]\\[\\]]+" │ ├── closing_loc: (50,27)-(50,28) = "/" │ └── unescaped: "[^-+',./:@[:alnum:]\\[\\]]+" ├── @ InterpolatedRegularExpressionNode (location: (51,0)-(51,12)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (51,0)-(51,1) = "/" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (51,1)-(51,4)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (51,1)-(51,4) = "foo" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "foo" │ │ └── @ EmbeddedStatementsNode (location: (51,4)-(51,11)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (51,4)-(51,6) = "\#{" │ │ ├── statements: │ │ │ @ StatementsNode (location: (51,6)-(51,10)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ InstanceVariableReadNode (location: (51,6)-(51,10)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :@bar │ │ └── closing_loc: (51,10)-(51,11) = "}" │ └── closing_loc: (51,11)-(51,12) = "/" ├── @ InterpolatedRegularExpressionNode (location: (52,0)-(52,15)) - │ ├── flags: ignore_case, extended, multi_line + │ ├── flags: newline, ignore_case, extended, multi_line │ ├── opening_loc: (52,0)-(52,1) = "/" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (52,1)-(52,4)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (52,1)-(52,4) = "foo" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "foo" │ │ └── @ EmbeddedStatementsNode (location: (52,4)-(52,11)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (52,4)-(52,6) = "\#{" │ │ ├── statements: │ │ │ @ StatementsNode (location: (52,6)-(52,10)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ InstanceVariableReadNode (location: (52,6)-(52,10)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :@bar │ │ └── closing_loc: (52,10)-(52,11) = "}" │ └── closing_loc: (52,11)-(52,15) = "/imx" ├── @ InterpolatedRegularExpressionNode (location: (53,0)-(53,13)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── opening_loc: (53,0)-(53,1) = "/" │ ├── parts: (length: 1) │ │ └── @ EmbeddedStatementsNode (location: (53,1)-(53,12)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (53,1)-(53,3) = "\#{" │ │ ├── statements: │ │ │ @ StatementsNode (location: (53,3)-(53,11)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ StringNode (location: (53,3)-(53,11)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: (53,3)-(53,4) = "\"" │ │ │ ├── content_loc: (53,4)-(53,10) = "\\u0000" │ │ │ ├── closing_loc: (53,10)-(53,11) = "\"" @@ -641,42 +691,45 @@ │ │ └── closing_loc: (53,11)-(53,12) = "}" │ └── closing_loc: (53,12)-(53,13) = "/" ├── @ RegularExpressionNode (location: (54,0)-(54,4)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (54,0)-(54,1) = "/" │ ├── content_loc: (54,1)-(54,3) = "\\n" │ ├── closing_loc: (54,3)-(54,4) = "/" │ └── unescaped: "\\n" ├── @ RegularExpressionNode (location: (55,0)-(55,4)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (55,0)-(55,1) = "/" │ ├── content_loc: (55,1)-(55,3) = "\\n" │ ├── closing_loc: (55,3)-(55,4) = "/" │ └── unescaped: "\\n" ├── @ RegularExpressionNode (location: (56,0)-(56,5)) - │ ├── flags: extended, forced_us_ascii_encoding + │ ├── flags: newline, static_literal, extended, forced_us_ascii_encoding │ ├── opening_loc: (56,0)-(56,1) = "/" │ ├── content_loc: (56,1)-(56,3) = "\\n" │ ├── closing_loc: (56,3)-(56,5) = "/x" │ └── unescaped: "\\n" ├── @ RegularExpressionNode (location: (57,0)-(57,7)) - │ ├── flags: extended, forced_us_ascii_encoding + │ ├── flags: newline, static_literal, extended, forced_us_ascii_encoding │ ├── opening_loc: (57,0)-(57,1) = "/" │ ├── content_loc: (57,1)-(57,5) = "\\/\\/" │ ├── closing_loc: (57,5)-(57,7) = "/x" │ └── unescaped: "//" ├── @ InterpolatedSymbolNode (location: (58,0)-(58,15)) + │ ├── flags: newline │ ├── opening_loc: (58,0)-(58,2) = ":\"" │ ├── parts: (length: 3) │ │ ├── @ StringNode (location: (58,2)-(58,5)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (58,2)-(58,5) = "foo" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "foo" │ │ ├── @ EmbeddedStatementsNode (location: (58,5)-(58,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (58,5)-(58,7) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (58,7)-(58,10)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (58,7)-(58,10)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -690,22 +743,25 @@ │ │ │ │ └── block: ∅ │ │ │ └── closing_loc: (58,10)-(58,11) = "}" │ │ └── @ StringNode (location: (58,11)-(58,14)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (58,11)-(58,14) = "baz" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "baz" │ └── closing_loc: (58,14)-(58,15) = "\"" ├── @ InterpolatedSymbolNode (location: (59,0)-(59,11)) + │ ├── flags: newline, static_literal │ ├── opening_loc: (59,0)-(59,2) = ":\"" │ ├── parts: (length: 1) │ │ └── @ EmbeddedStatementsNode (location: (59,2)-(59,10)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (59,2)-(59,4) = "\#{" │ │ ├── statements: │ │ │ @ StatementsNode (location: (59,4)-(59,9)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ StringNode (location: (59,4)-(59,9)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: (59,4)-(59,5) = "\"" │ │ │ ├── content_loc: (59,5)-(59,8) = "foo" │ │ │ ├── closing_loc: (59,8)-(59,9) = "\"" @@ -713,16 +769,19 @@ │ │ └── closing_loc: (59,9)-(59,10) = "}" │ └── closing_loc: (59,10)-(59,11) = "\"" ├── @ RangeNode (location: (60,0)-(60,14)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── left: │ │ @ ParenthesesNode (location: (60,0)-(60,11)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (60,1)-(60,10)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (60,1)-(60,10)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── receiver: │ │ │ │ @ FloatNode (location: (60,1)-(60,4)) + │ │ │ │ ├── flags: static_literal │ │ │ │ └── value: 0.0 │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :/ @@ -733,6 +792,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ FloatNode (location: (60,7)-(60,10)) + │ │ │ │ ├── flags: static_literal │ │ │ │ └── value: 0.0 │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ @@ -740,24 +800,27 @@ │ │ └── closing_loc: (60,10)-(60,11) = ")" │ ├── right: │ │ @ IntegerNode (location: (60,13)-(60,14)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (60,11)-(60,13) = ".." ├── @ RangeNode (location: (61,0)-(61,14)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── left: │ │ @ IntegerNode (location: (61,0)-(61,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── right: │ │ @ ParenthesesNode (location: (61,3)-(61,14)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (61,4)-(61,13)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (61,4)-(61,13)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── receiver: │ │ │ │ @ FloatNode (location: (61,4)-(61,7)) + │ │ │ │ ├── flags: static_literal │ │ │ │ └── value: 0.0 │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :/ @@ -768,6 +831,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ FloatNode (location: (61,10)-(61,13)) + │ │ │ │ ├── flags: static_literal │ │ │ │ └── value: 0.0 │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ @@ -775,16 +839,19 @@ │ │ └── closing_loc: (61,13)-(61,14) = ")" │ └── operator_loc: (61,1)-(61,3) = ".." ├── @ RangeNode (location: (62,0)-(62,16)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── left: │ │ @ ParenthesesNode (location: (62,0)-(62,11)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (62,1)-(62,10)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (62,1)-(62,10)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── receiver: │ │ │ │ @ FloatNode (location: (62,1)-(62,4)) + │ │ │ │ ├── flags: static_literal │ │ │ │ └── value: 0.0 │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :/ @@ -795,6 +862,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ FloatNode (location: (62,7)-(62,10)) + │ │ │ │ ├── flags: static_literal │ │ │ │ └── value: 0.0 │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ @@ -802,31 +870,34 @@ │ │ └── closing_loc: (62,10)-(62,11) = ")" │ ├── right: │ │ @ IntegerNode (location: (62,13)-(62,16)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 100 │ └── operator_loc: (62,11)-(62,13) = ".." ├── @ FloatNode (location: (63,0)-(63,4)) + │ ├── flags: newline, static_literal │ └── value: -0.1 ├── @ FloatNode (location: (64,0)-(64,3)) + │ ├── flags: newline, static_literal │ └── value: 0.1 ├── @ ArrayNode (location: (65,0)-(65,6)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── elements: (length: 2) │ │ ├── @ IntegerNode (location: (65,1)-(65,2)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ IntegerNode (location: (65,4)-(65,5)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── opening_loc: (65,0)-(65,1) = "[" │ └── closing_loc: (65,5)-(65,6) = "]" ├── @ ArrayNode (location: (66,0)-(66,11)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── elements: (length: 3) │ │ ├── @ IntegerNode (location: (66,1)-(66,2)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── @ ParenthesesNode (location: (66,4)-(66,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: ∅ │ │ │ ├── opening_loc: (66,4)-(66,5) = "(" │ │ │ └── closing_loc: (66,5)-(66,6) = ")" @@ -843,137 +914,160 @@ │ ├── opening_loc: (66,0)-(66,1) = "[" │ └── closing_loc: (66,10)-(66,11) = "]" ├── @ ArrayNode (location: (67,0)-(67,3)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── elements: (length: 1) │ │ └── @ IntegerNode (location: (67,1)-(67,2)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── opening_loc: (67,0)-(67,1) = "[" │ └── closing_loc: (67,2)-(67,3) = "]" ├── @ ArrayNode (location: (68,0)-(68,2)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── elements: (length: 0) │ ├── opening_loc: (68,0)-(68,1) = "[" │ └── closing_loc: (68,1)-(68,2) = "]" ├── @ ArrayNode (location: (69,0)-(69,10)) - │ ├── flags: contains_splat + │ ├── flags: newline, contains_splat │ ├── elements: (length: 2) │ │ ├── @ IntegerNode (location: (69,1)-(69,2)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ SplatNode (location: (69,4)-(69,9)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (69,4)-(69,5) = "*" │ │ └── expression: │ │ @ InstanceVariableReadNode (location: (69,5)-(69,9)) + │ │ ├── flags: ∅ │ │ └── name: :@foo │ ├── opening_loc: (69,0)-(69,1) = "[" │ └── closing_loc: (69,9)-(69,10) = "]" ├── @ ArrayNode (location: (70,0)-(70,10)) - │ ├── flags: contains_splat + │ ├── flags: newline, contains_splat │ ├── elements: (length: 2) │ │ ├── @ SplatNode (location: (70,1)-(70,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (70,1)-(70,2) = "*" │ │ │ └── expression: │ │ │ @ InstanceVariableReadNode (location: (70,2)-(70,6)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :@foo │ │ └── @ IntegerNode (location: (70,8)-(70,9)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── opening_loc: (70,0)-(70,1) = "[" │ └── closing_loc: (70,9)-(70,10) = "]" ├── @ ArrayNode (location: (71,0)-(71,14)) - │ ├── flags: contains_splat + │ ├── flags: newline, contains_splat │ ├── elements: (length: 2) │ │ ├── @ SplatNode (location: (71,1)-(71,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (71,1)-(71,2) = "*" │ │ │ └── expression: │ │ │ @ InstanceVariableReadNode (location: (71,2)-(71,6)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :@foo │ │ └── @ SplatNode (location: (71,8)-(71,13)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (71,8)-(71,9) = "*" │ │ └── expression: │ │ @ InstanceVariableReadNode (location: (71,9)-(71,13)) + │ │ ├── flags: ∅ │ │ └── name: :@baz │ ├── opening_loc: (71,0)-(71,1) = "[" │ └── closing_loc: (71,13)-(71,14) = "]" ├── @ HashNode (location: (72,0)-(72,2)) + │ ├── flags: newline, static_literal │ ├── opening_loc: (72,0)-(72,1) = "{" │ ├── elements: (length: 0) │ └── closing_loc: (72,1)-(72,2) = "}" ├── @ HashNode (location: (73,0)-(73,12)) + │ ├── flags: newline │ ├── opening_loc: (73,0)-(73,1) = "{" │ ├── elements: (length: 1) │ │ └── @ AssocNode (location: (73,2)-(73,10)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ ParenthesesNode (location: (73,2)-(73,4)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: ∅ │ │ │ ├── opening_loc: (73,2)-(73,3) = "(" │ │ │ └── closing_loc: (73,3)-(73,4) = ")" │ │ ├── value: │ │ │ @ ParenthesesNode (location: (73,8)-(73,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: ∅ │ │ │ ├── opening_loc: (73,8)-(73,9) = "(" │ │ │ └── closing_loc: (73,9)-(73,10) = ")" │ │ └── operator_loc: (73,5)-(73,7) = "=>" │ └── closing_loc: (73,11)-(73,12) = "}" ├── @ HashNode (location: (74,0)-(74,10)) + │ ├── flags: newline, static_literal │ ├── opening_loc: (74,0)-(74,1) = "{" │ ├── elements: (length: 1) │ │ └── @ AssocNode (location: (74,2)-(74,8)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ IntegerNode (location: (74,2)-(74,3)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── value: │ │ │ @ IntegerNode (location: (74,7)-(74,8)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── operator_loc: (74,4)-(74,6) = "=>" │ └── closing_loc: (74,9)-(74,10) = "}" ├── @ HashNode (location: (75,0)-(75,18)) + │ ├── flags: newline, static_literal │ ├── opening_loc: (75,0)-(75,1) = "{" │ ├── elements: (length: 2) │ │ ├── @ AssocNode (location: (75,2)-(75,8)) + │ │ │ ├── flags: static_literal │ │ │ ├── key: │ │ │ │ @ IntegerNode (location: (75,2)-(75,3)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── value: │ │ │ │ @ IntegerNode (location: (75,7)-(75,8)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ └── operator_loc: (75,4)-(75,6) = "=>" │ │ └── @ AssocNode (location: (75,10)-(75,16)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ IntegerNode (location: (75,10)-(75,11)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 3 │ │ ├── value: │ │ │ @ IntegerNode (location: (75,15)-(75,16)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 4 │ │ └── operator_loc: (75,12)-(75,14) = "=>" │ └── closing_loc: (75,17)-(75,18) = "}" ├── @ HashNode (location: (76,0)-(76,27)) + │ ├── flags: newline │ ├── opening_loc: (76,0)-(76,1) = "{" │ ├── elements: (length: 2) │ │ ├── @ AssocNode (location: (76,2)-(76,19)) + │ │ │ ├── flags: ∅ │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (76,2)-(76,4)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (76,2)-(76,3) = "a" │ │ │ │ ├── closing_loc: (76,3)-(76,4) = ":" │ │ │ │ └── unescaped: "a" │ │ │ ├── value: │ │ │ │ @ ParenthesesNode (location: (76,5)-(76,19)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── body: │ │ │ │ │ @ StatementsNode (location: (76,6)-(76,18)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ RescueModifierNode (location: (76,6)-(76,18)) + │ │ │ │ │ ├── flags: newline │ │ │ │ │ ├── expression: │ │ │ │ │ │ @ IntegerNode (location: (76,6)-(76,7)) - │ │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ │ └── value: 1 │ │ │ │ │ ├── keyword_loc: (76,8)-(76,14) = "rescue" │ │ │ │ │ └── rescue_expression: @@ -991,63 +1085,69 @@ │ │ │ │ └── closing_loc: (76,18)-(76,19) = ")" │ │ │ └── operator_loc: ∅ │ │ └── @ AssocNode (location: (76,21)-(76,25)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (76,21)-(76,23)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (76,21)-(76,22) = "b" │ │ │ ├── closing_loc: (76,22)-(76,23) = ":" │ │ │ └── unescaped: "b" │ │ ├── value: │ │ │ @ IntegerNode (location: (76,24)-(76,25)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── operator_loc: ∅ │ └── closing_loc: (76,26)-(76,27) = "}" ├── @ HashNode (location: (77,0)-(77,14)) + │ ├── flags: newline, static_literal │ ├── opening_loc: (77,0)-(77,1) = "{" │ ├── elements: (length: 2) │ │ ├── @ AssocNode (location: (77,2)-(77,6)) + │ │ │ ├── flags: static_literal │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (77,2)-(77,4)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (77,2)-(77,3) = "a" │ │ │ │ ├── closing_loc: (77,3)-(77,4) = ":" │ │ │ │ └── unescaped: "a" │ │ │ ├── value: │ │ │ │ @ IntegerNode (location: (77,5)-(77,6)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── operator_loc: ∅ │ │ └── @ AssocNode (location: (77,8)-(77,12)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (77,8)-(77,10)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (77,8)-(77,9) = "b" │ │ │ ├── closing_loc: (77,9)-(77,10) = ":" │ │ │ └── unescaped: "b" │ │ ├── value: │ │ │ @ IntegerNode (location: (77,11)-(77,12)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── operator_loc: ∅ │ └── closing_loc: (77,13)-(77,14) = "}" ├── @ HashNode (location: (78,0)-(78,9)) + │ ├── flags: newline, static_literal │ ├── opening_loc: (78,0)-(78,1) = "{" │ ├── elements: (length: 1) │ │ └── @ AssocNode (location: (78,2)-(78,7)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (78,2)-(78,4)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (78,2)-(78,3) = "a" │ │ │ ├── closing_loc: (78,3)-(78,4) = ":" │ │ │ └── unescaped: "a" │ │ ├── value: │ │ │ @ SymbolNode (location: (78,5)-(78,7)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (78,5)-(78,6) = ":" │ │ │ ├── value_loc: (78,6)-(78,7) = "a" │ │ │ ├── closing_loc: ∅ @@ -1055,66 +1155,72 @@ │ │ └── operator_loc: ∅ │ └── closing_loc: (78,8)-(78,9) = "}" ├── @ HashNode (location: (79,0)-(79,15)) + │ ├── flags: newline, static_literal │ ├── opening_loc: (79,0)-(79,1) = "{" │ ├── elements: (length: 1) │ │ └── @ AssocNode (location: (79,2)-(79,13)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (79,2)-(79,8)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (79,2)-(79,4) = ":\"" │ │ │ ├── value_loc: (79,4)-(79,7) = "a b" │ │ │ ├── closing_loc: (79,7)-(79,8) = "\"" │ │ │ └── unescaped: "a b" │ │ ├── value: │ │ │ @ IntegerNode (location: (79,12)-(79,13)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── operator_loc: (79,9)-(79,11) = "=>" │ └── closing_loc: (79,14)-(79,15) = "}" ├── @ HashNode (location: (80,0)-(80,12)) + │ ├── flags: newline, static_literal │ ├── opening_loc: (80,0)-(80,1) = "{" │ ├── elements: (length: 1) │ │ └── @ AssocNode (location: (80,2)-(80,10)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (80,2)-(80,5)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (80,2)-(80,3) = ":" │ │ │ ├── value_loc: (80,3)-(80,5) = "-@" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "-@" │ │ ├── value: │ │ │ @ IntegerNode (location: (80,9)-(80,10)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── operator_loc: (80,6)-(80,8) = "=>" │ └── closing_loc: (80,11)-(80,12) = "}" ├── @ InterpolatedStringNode (location: (81,0)-(82,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (81,0)-(81,1) = "\"" │ ├── parts: (length: 4) │ │ ├── @ EmbeddedStatementsNode (location: (81,1)-(81,4)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (81,1)-(81,3) = "\#{" │ │ │ ├── statements: ∅ │ │ │ └── closing_loc: (81,3)-(81,4) = "}" │ │ ├── @ StringNode (location: (81,4)-(82,0)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (81,4)-(82,0) = "\n" │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "\n" │ │ ├── @ EmbeddedStatementsNode (location: (82,0)-(82,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (82,0)-(82,2) = "\#{" │ │ │ ├── statements: ∅ │ │ │ └── closing_loc: (82,2)-(82,3) = "}" │ │ └── @ StringNode (location: (82,3)-(82,6)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (82,3)-(82,6) = "\\na" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "\na" │ └── closing_loc: (82,6)-(82,7) = "\"" ├── @ CallNode (location: (83,0)-(86,1)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -1124,31 +1230,35 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (83,4)-(86,1)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (84,2)-(85,7)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ InterpolatedStringNode (location: (84,2)-(85,7)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── opening_loc: (84,2)-(84,3) = "\"" │ │ ├── parts: (length: 4) │ │ │ ├── @ EmbeddedStatementsNode (location: (84,3)-(84,6)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── opening_loc: (84,3)-(84,5) = "\#{" │ │ │ │ ├── statements: ∅ │ │ │ │ └── closing_loc: (84,5)-(84,6) = "}" │ │ │ ├── @ StringNode (location: (84,6)-(85,0)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── content_loc: (84,6)-(85,0) = "\n" │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: "\n" │ │ │ ├── @ EmbeddedStatementsNode (location: (85,0)-(85,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── opening_loc: (85,0)-(85,2) = "\#{" │ │ │ │ ├── statements: ∅ │ │ │ │ └── closing_loc: (85,2)-(85,3) = "}" │ │ │ └── @ StringNode (location: (85,3)-(85,6)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (85,3)-(85,6) = "\\na" │ │ │ ├── closing_loc: ∅ @@ -1157,24 +1267,27 @@ │ ├── opening_loc: (83,4)-(83,5) = "{" │ └── closing_loc: (86,0)-(86,1) = "}" ├── @ SymbolNode (location: (87,0)-(88,2)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (87,0)-(87,2) = ":\"" │ ├── value_loc: (87,2)-(88,1) = "a\\\\\nb" │ ├── closing_loc: (88,1)-(88,2) = "\"" │ └── unescaped: "a\\\nb" └── @ InterpolatedXStringNode (location: (89,0)-(91,2)) + ├── flags: newline ├── opening_loc: (89,0)-(89,1) = "`" ├── parts: (length: 3) │ ├── @ StringNode (location: (89,1)-(90,0)) - │ │ ├── flags: frozen + │ │ ├── flags: static_literal, frozen │ │ ├── opening_loc: ∅ │ │ ├── content_loc: (89,1)-(90,0) = " x\n" │ │ ├── closing_loc: ∅ │ │ └── unescaped: " x\n" │ ├── @ EmbeddedStatementsNode (location: (90,0)-(90,6)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (90,0)-(90,2) = "\#{" │ │ ├── statements: │ │ │ @ StatementsNode (location: (90,2)-(90,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (90,2)-(90,5)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -1188,7 +1301,7 @@ │ │ │ └── block: ∅ │ │ └── closing_loc: (90,5)-(90,6) = "}" │ └── @ StringNode (location: (90,6)-(91,1)) - │ ├── flags: frozen + │ ├── flags: static_literal, frozen │ ├── opening_loc: ∅ │ ├── content_loc: (90,6)-(91,1) = "\n#" │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/unparser/corpus/literal/module.txt b/test/prism/snapshots/unparser/corpus/literal/module.txt index 6428aeea82e24a..9ad15d2dcd18fa 100644 --- a/test/prism/snapshots/unparser/corpus/literal/module.txt +++ b/test/prism/snapshots/unparser/corpus/literal/module.txt @@ -1,24 +1,31 @@ @ ProgramNode (location: (1,0)-(16,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(16,3)) + ├── flags: ∅ └── body: (length: 4) ├── @ ModuleNode (location: (1,0)-(2,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── module_keyword_loc: (1,0)-(1,6) = "module" │ ├── constant_path: │ │ @ ConstantReadNode (location: (1,7)-(1,8)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── body: ∅ │ ├── end_keyword_loc: (2,0)-(2,3) = "end" │ └── name: :A ├── @ ModuleNode (location: (4,0)-(5,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── module_keyword_loc: (4,0)-(4,6) = "module" │ ├── constant_path: │ │ @ ConstantPathNode (location: (4,7)-(4,11)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ ConstantReadNode (location: (4,7)-(4,8)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :A │ │ ├── name: :B │ │ ├── delimiter_loc: (4,8)-(4,10) = "::" @@ -27,14 +34,18 @@ │ ├── end_keyword_loc: (5,0)-(5,3) = "end" │ └── name: :B ├── @ ModuleNode (location: (7,0)-(8,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── module_keyword_loc: (7,0)-(7,6) = "module" │ ├── constant_path: │ │ @ ConstantPathNode (location: (7,7)-(7,14)) + │ │ ├── flags: ∅ │ │ ├── parent: │ │ │ @ ConstantPathNode (location: (7,7)-(7,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── parent: │ │ │ │ @ ConstantReadNode (location: (7,7)-(7,8)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :A │ │ │ ├── name: :B │ │ │ ├── delimiter_loc: (7,8)-(7,10) = "::" @@ -46,16 +57,19 @@ │ ├── end_keyword_loc: (8,0)-(8,3) = "end" │ └── name: :C └── @ ModuleNode (location: (10,0)-(16,3)) + ├── flags: newline ├── locals: [] ├── module_keyword_loc: (10,0)-(10,6) = "module" ├── constant_path: │ @ ConstantReadNode (location: (10,7)-(10,8)) + │ ├── flags: ∅ │ └── name: :A ├── body: │ @ StatementsNode (location: (11,2)-(15,5)) + │ ├── flags: ∅ │ └── body: (length: 2) │ ├── @ CallNode (location: (11,2)-(11,16)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :include @@ -69,6 +83,7 @@ │ │ │ ├── flags: ∅ │ │ │ ├── receiver: │ │ │ │ @ ConstantReadNode (location: (11,10)-(11,11)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :B │ │ │ ├── call_operator_loc: (11,11)-(11,12) = "." │ │ │ ├── name: :new @@ -80,15 +95,17 @@ │ │ ├── closing_loc: (11,15)-(11,16) = ")" │ │ └── block: ∅ │ └── @ DefNode (location: (13,2)-(15,5)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (13,6)-(13,9) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (14,4)-(14,8)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ SymbolNode (location: (14,4)-(14,8)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (14,4)-(14,5) = ":" │ │ ├── value_loc: (14,5)-(14,8) = "bar" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/unparser/corpus/literal/opasgn.txt b/test/prism/snapshots/unparser/corpus/literal/opasgn.txt index 0761b47348b692..3df6248d4badb7 100644 --- a/test/prism/snapshots/unparser/corpus/literal/opasgn.txt +++ b/test/prism/snapshots/unparser/corpus/literal/opasgn.txt @@ -1,59 +1,67 @@ @ ProgramNode (location: (1,0)-(24,10)) +├── flags: ∅ ├── locals: [:a, :h] └── statements: @ StatementsNode (location: (1,0)-(24,10)) + ├── flags: ∅ └── body: (length: 24) ├── @ LocalVariableOperatorWriteNode (location: (1,0)-(1,6)) + │ ├── flags: newline │ ├── name_loc: (1,0)-(1,1) = "a" │ ├── binary_operator_loc: (1,2)-(1,4) = "+=" │ ├── value: │ │ @ IntegerNode (location: (1,5)-(1,6)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── name: :a │ ├── binary_operator: :+ │ └── depth: 0 ├── @ LocalVariableOperatorWriteNode (location: (2,0)-(2,6)) + │ ├── flags: newline │ ├── name_loc: (2,0)-(2,1) = "a" │ ├── binary_operator_loc: (2,2)-(2,4) = "-=" │ ├── value: │ │ @ IntegerNode (location: (2,5)-(2,6)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── name: :a │ ├── binary_operator: :- │ └── depth: 0 ├── @ LocalVariableOperatorWriteNode (location: (3,0)-(3,7)) + │ ├── flags: newline │ ├── name_loc: (3,0)-(3,1) = "a" │ ├── binary_operator_loc: (3,2)-(3,5) = "**=" │ ├── value: │ │ @ IntegerNode (location: (3,6)-(3,7)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── name: :a │ ├── binary_operator: :** │ └── depth: 0 ├── @ LocalVariableOperatorWriteNode (location: (4,0)-(4,6)) + │ ├── flags: newline │ ├── name_loc: (4,0)-(4,1) = "a" │ ├── binary_operator_loc: (4,2)-(4,4) = "*=" │ ├── value: │ │ @ IntegerNode (location: (4,5)-(4,6)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── name: :a │ ├── binary_operator: :* │ └── depth: 0 ├── @ LocalVariableOperatorWriteNode (location: (5,0)-(5,6)) + │ ├── flags: newline │ ├── name_loc: (5,0)-(5,1) = "a" │ ├── binary_operator_loc: (5,2)-(5,4) = "/=" │ ├── value: │ │ @ IntegerNode (location: (5,5)-(5,6)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── name: :a │ ├── binary_operator: :/ │ └── depth: 0 ├── @ LocalVariableAndWriteNode (location: (6,0)-(6,7)) + │ ├── flags: newline │ ├── name_loc: (6,0)-(6,1) = "a" │ ├── operator_loc: (6,2)-(6,5) = "&&=" │ ├── value: @@ -70,27 +78,31 @@ │ ├── name: :a │ └── depth: 0 ├── @ LocalVariableOrWriteNode (location: (7,0)-(7,7)) + │ ├── flags: newline │ ├── name_loc: (7,0)-(7,1) = "a" │ ├── operator_loc: (7,2)-(7,5) = "||=" │ ├── value: │ │ @ IntegerNode (location: (7,6)-(7,7)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── name: :a │ └── depth: 0 ├── @ CallNode (location: (8,0)-(8,13)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ParenthesesNode (location: (8,0)-(8,9)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (8,1)-(8,8)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ LocalVariableOrWriteNode (location: (8,1)-(8,8)) + │ │ │ ├── flags: newline │ │ │ ├── name_loc: (8,1)-(8,2) = "a" │ │ │ ├── operator_loc: (8,3)-(8,6) = "||=" │ │ │ ├── value: │ │ │ │ @ IntegerNode (location: (8,7)-(8,8)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ ├── name: :a │ │ │ └── depth: 0 @@ -104,17 +116,21 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (9,0)-(9,17)) - │ ├── flags: attribute_write + │ ├── flags: newline, attribute_write │ ├── receiver: │ │ @ ParenthesesNode (location: (9,0)-(9,10)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (9,1)-(9,9)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ LocalVariableOrWriteNode (location: (9,1)-(9,9)) + │ │ │ ├── flags: newline │ │ │ ├── name_loc: (9,1)-(9,2) = "h" │ │ │ ├── operator_loc: (9,3)-(9,6) = "||=" │ │ │ ├── value: │ │ │ │ @ HashNode (location: (9,7)-(9,9)) + │ │ │ │ ├── flags: static_literal │ │ │ │ ├── opening_loc: (9,7)-(9,8) = "{" │ │ │ │ ├── elements: (length: 0) │ │ │ │ └── closing_loc: (9,8)-(9,9) = "}" @@ -153,9 +169,10 @@ │ ├── closing_loc: (9,12)-(9,13) = "]" │ └── block: ∅ ├── @ CallOperatorWriteNode (location: (10,0)-(10,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (10,0)-(10,1)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── call_operator_loc: (10,1)-(10,2) = "." @@ -166,12 +183,13 @@ │ ├── binary_operator_loc: (10,4)-(10,6) = "+=" │ └── value: │ @ IntegerNode (location: (10,7)-(10,8)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── @ CallOperatorWriteNode (location: (11,0)-(11,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (11,0)-(11,1)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── call_operator_loc: (11,1)-(11,2) = "." @@ -182,12 +200,13 @@ │ ├── binary_operator_loc: (11,4)-(11,6) = "-=" │ └── value: │ @ IntegerNode (location: (11,7)-(11,8)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── @ CallOperatorWriteNode (location: (12,0)-(12,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (12,0)-(12,1)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── call_operator_loc: (12,1)-(12,2) = "." @@ -198,12 +217,13 @@ │ ├── binary_operator_loc: (12,4)-(12,7) = "**=" │ └── value: │ @ IntegerNode (location: (12,8)-(12,9)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── @ CallOperatorWriteNode (location: (13,0)-(13,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (13,0)-(13,1)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── call_operator_loc: (13,1)-(13,2) = "." @@ -214,12 +234,13 @@ │ ├── binary_operator_loc: (13,4)-(13,6) = "*=" │ └── value: │ @ IntegerNode (location: (13,7)-(13,8)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── @ CallOperatorWriteNode (location: (14,0)-(14,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (14,0)-(14,1)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── call_operator_loc: (14,1)-(14,2) = "." @@ -230,12 +251,13 @@ │ ├── binary_operator_loc: (14,4)-(14,6) = "/=" │ └── value: │ @ IntegerNode (location: (14,7)-(14,8)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── @ CallAndWriteNode (location: (15,0)-(15,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (15,0)-(15,1)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── call_operator_loc: (15,1)-(15,2) = "." @@ -255,9 +277,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallOrWriteNode (location: (16,0)-(16,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (16,0)-(16,1)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── call_operator_loc: (16,1)-(16,2) = "." @@ -267,12 +290,13 @@ │ ├── operator_loc: (16,4)-(16,7) = "||=" │ └── value: │ @ IntegerNode (location: (16,8)-(16,9)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── @ IndexOperatorWriteNode (location: (17,0)-(17,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (17,0)-(17,1)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── call_operator_loc: ∅ @@ -297,12 +321,13 @@ │ ├── binary_operator_loc: (17,5)-(17,7) = "+=" │ └── value: │ @ IntegerNode (location: (17,8)-(17,9)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── @ IndexOperatorWriteNode (location: (18,0)-(18,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (18,0)-(18,1)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── call_operator_loc: ∅ @@ -327,12 +352,13 @@ │ ├── binary_operator_loc: (18,5)-(18,7) = "-=" │ └── value: │ @ IntegerNode (location: (18,8)-(18,9)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── @ IndexOperatorWriteNode (location: (19,0)-(19,10)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (19,0)-(19,1)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── call_operator_loc: ∅ @@ -357,12 +383,13 @@ │ ├── binary_operator_loc: (19,5)-(19,8) = "**=" │ └── value: │ @ IntegerNode (location: (19,9)-(19,10)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── @ IndexOperatorWriteNode (location: (20,0)-(20,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (20,0)-(20,1)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── call_operator_loc: ∅ @@ -387,12 +414,13 @@ │ ├── binary_operator_loc: (20,5)-(20,7) = "*=" │ └── value: │ @ IntegerNode (location: (20,8)-(20,9)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── @ IndexOperatorWriteNode (location: (21,0)-(21,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (21,0)-(21,1)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── call_operator_loc: ∅ @@ -417,12 +445,13 @@ │ ├── binary_operator_loc: (21,5)-(21,7) = "/=" │ └── value: │ @ IntegerNode (location: (21,8)-(21,9)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── @ IndexAndWriteNode (location: (22,0)-(22,10)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (22,0)-(22,1)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── call_operator_loc: ∅ @@ -456,9 +485,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ IndexOrWriteNode (location: (23,0)-(23,10)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (23,0)-(23,1)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── call_operator_loc: ∅ @@ -482,10 +512,10 @@ │ ├── operator_loc: (23,5)-(23,8) = "||=" │ └── value: │ @ IntegerNode (location: (23,9)-(23,10)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 └── @ CallOperatorWriteNode (location: (24,0)-(24,10)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (24,0)-(24,3)) │ ├── flags: variable_call, ignore_visibility @@ -505,5 +535,5 @@ ├── binary_operator_loc: (24,6)-(24,8) = "+=" └── value: @ IntegerNode (location: (24,9)-(24,10)) - ├── flags: decimal + ├── flags: static_literal, decimal └── value: 1 diff --git a/test/prism/snapshots/unparser/corpus/literal/pattern.txt b/test/prism/snapshots/unparser/corpus/literal/pattern.txt index 5a0b4bb7336399..7a542b26cf971d 100644 --- a/test/prism/snapshots/unparser/corpus/literal/pattern.txt +++ b/test/prism/snapshots/unparser/corpus/literal/pattern.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(41,8)) +├── flags: ∅ ├── locals: [:a, :x, :y] └── statements: @ StatementsNode (location: (1,0)-(41,8)) + ├── flags: ∅ └── body: (length: 4) ├── @ CaseMatchNode (location: (1,0)-(33,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (1,5)-(1,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -17,58 +20,69 @@ │ │ └── block: ∅ │ ├── conditions: (length: 15) │ │ ├── @ InNode (location: (2,0)-(3,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── pattern: │ │ │ │ @ ArrayPatternNode (location: (2,3)-(2,17)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── constant: │ │ │ │ │ @ ConstantReadNode (location: (2,3)-(2,4)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── name: :A │ │ │ │ ├── requireds: (length: 2) │ │ │ │ │ ├── @ IntegerNode (location: (2,5)-(2,6)) - │ │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ │ └── value: 1 │ │ │ │ │ └── @ IntegerNode (location: (2,8)-(2,9)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 2 │ │ │ │ ├── rest: │ │ │ │ │ @ SplatNode (location: (2,11)-(2,13)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── operator_loc: (2,11)-(2,12) = "*" │ │ │ │ │ └── expression: │ │ │ │ │ @ LocalVariableTargetNode (location: (2,12)-(2,13)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── name: :a │ │ │ │ │ └── depth: 0 │ │ │ │ ├── posts: (length: 1) │ │ │ │ │ └── @ IntegerNode (location: (2,15)-(2,16)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 3 │ │ │ │ ├── opening_loc: (2,4)-(2,5) = "[" │ │ │ │ └── closing_loc: (2,16)-(2,17) = "]" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (3,2)-(3,6)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ TrueNode (location: (3,2)-(3,6)) + │ │ │ │ └── flags: newline, static_literal │ │ │ ├── in_loc: (2,0)-(2,2) = "in" │ │ │ └── then_loc: (2,18)-(2,22) = "then" │ │ ├── @ InNode (location: (4,0)-(5,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── pattern: │ │ │ │ @ ArrayPatternNode (location: (4,3)-(4,11)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── constant: ∅ │ │ │ │ ├── requireds: (length: 2) │ │ │ │ │ ├── @ IntegerNode (location: (4,4)-(4,5)) - │ │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ │ └── value: 1 │ │ │ │ │ └── @ IntegerNode (location: (4,7)-(4,8)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 2 │ │ │ │ ├── rest: │ │ │ │ │ @ ImplicitRestNode (location: (4,8)-(4,9)) + │ │ │ │ │ └── flags: ∅ │ │ │ │ ├── posts: (length: 0) │ │ │ │ ├── opening_loc: (4,3)-(4,4) = "[" │ │ │ │ └── closing_loc: (4,10)-(4,11) = "]" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (5,2)-(5,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (5,2)-(5,3)) - │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ ├── receiver: ∅ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :y @@ -80,24 +94,30 @@ │ │ │ ├── in_loc: (4,0)-(4,2) = "in" │ │ │ └── then_loc: (4,12)-(4,16) = "then" │ │ ├── @ InNode (location: (6,0)-(7,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── pattern: │ │ │ │ @ HashPatternNode (location: (6,3)-(6,8)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── constant: │ │ │ │ │ @ ConstantReadNode (location: (6,3)-(6,4)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── name: :A │ │ │ │ ├── elements: (length: 1) │ │ │ │ │ └── @ AssocNode (location: (6,5)-(6,7)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── key: │ │ │ │ │ │ @ SymbolNode (location: (6,5)-(6,7)) - │ │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ │ ├── value_loc: (6,5)-(6,6) = "x" │ │ │ │ │ │ ├── closing_loc: (6,6)-(6,7) = ":" │ │ │ │ │ │ └── unescaped: "x" │ │ │ │ │ ├── value: │ │ │ │ │ │ @ ImplicitNode (location: (6,5)-(6,6)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ └── value: │ │ │ │ │ │ @ LocalVariableTargetNode (location: (6,5)-(6,6)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ ├── name: :x │ │ │ │ │ │ └── depth: 0 │ │ │ │ │ └── operator_loc: ∅ @@ -106,19 +126,25 @@ │ │ │ │ └── closing_loc: (6,7)-(6,8) = ")" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (7,2)-(7,6)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ TrueNode (location: (7,2)-(7,6)) + │ │ │ │ └── flags: newline, static_literal │ │ │ ├── in_loc: (6,0)-(6,2) = "in" │ │ │ └── then_loc: (6,9)-(6,13) = "then" │ │ ├── @ InNode (location: (8,0)-(9,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── pattern: │ │ │ │ @ HashPatternNode (location: (8,3)-(8,8)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── constant: ∅ │ │ │ │ ├── elements: (length: 0) │ │ │ │ ├── rest: │ │ │ │ │ @ AssocSplatNode (location: (8,4)-(8,7)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── value: │ │ │ │ │ │ @ LocalVariableTargetNode (location: (8,6)-(8,7)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ ├── name: :a │ │ │ │ │ │ └── depth: 0 │ │ │ │ │ └── operator_loc: (8,4)-(8,6) = "**" @@ -126,21 +152,28 @@ │ │ │ │ └── closing_loc: (8,7)-(8,8) = "}" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (9,2)-(9,6)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ TrueNode (location: (9,2)-(9,6)) + │ │ │ │ └── flags: newline, static_literal │ │ │ ├── in_loc: (8,0)-(8,2) = "in" │ │ │ └── then_loc: (8,9)-(8,13) = "then" │ │ ├── @ InNode (location: (10,0)-(11,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── pattern: │ │ │ │ @ IfNode (location: (10,3)-(10,13)) + │ │ │ │ ├── flags: newline │ │ │ │ ├── if_keyword_loc: (10,6)-(10,8) = "if" │ │ │ │ ├── predicate: │ │ │ │ │ @ TrueNode (location: (10,9)-(10,13)) + │ │ │ │ │ └── flags: static_literal │ │ │ │ ├── then_keyword_loc: ∅ │ │ │ │ ├── statements: │ │ │ │ │ @ StatementsNode (location: (10,3)-(10,5)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ HashPatternNode (location: (10,3)-(10,5)) + │ │ │ │ │ ├── flags: newline │ │ │ │ │ ├── constant: ∅ │ │ │ │ │ ├── elements: (length: 0) │ │ │ │ │ ├── rest: ∅ @@ -150,23 +183,30 @@ │ │ │ │ └── end_keyword_loc: ∅ │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (11,2)-(11,6)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ TrueNode (location: (11,2)-(11,6)) + │ │ │ │ └── flags: newline, static_literal │ │ │ ├── in_loc: (10,0)-(10,2) = "in" │ │ │ └── then_loc: (10,14)-(10,18) = "then" │ │ ├── @ InNode (location: (12,0)-(13,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── pattern: │ │ │ │ @ ArrayPatternNode (location: (12,3)-(12,12)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── constant: ∅ │ │ │ │ ├── requireds: (length: 2) │ │ │ │ │ ├── @ LocalVariableTargetNode (location: (12,4)-(12,5)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ ├── name: :x │ │ │ │ │ │ └── depth: 0 │ │ │ │ │ └── @ LocalVariableTargetNode (location: (12,7)-(12,8)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── name: :y │ │ │ │ │ └── depth: 0 │ │ │ │ ├── rest: │ │ │ │ │ @ SplatNode (location: (12,10)-(12,11)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── operator_loc: (12,10)-(12,11) = "*" │ │ │ │ │ └── expression: ∅ │ │ │ │ ├── posts: (length: 0) @@ -174,39 +214,45 @@ │ │ │ │ └── closing_loc: (12,11)-(12,12) = "]" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (13,2)-(13,6)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ TrueNode (location: (13,2)-(13,6)) + │ │ │ │ └── flags: newline, static_literal │ │ │ ├── in_loc: (12,0)-(12,2) = "in" │ │ │ └── then_loc: (12,13)-(12,17) = "then" │ │ ├── @ InNode (location: (14,0)-(15,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── pattern: │ │ │ │ @ HashPatternNode (location: (14,3)-(14,16)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── constant: ∅ │ │ │ │ ├── elements: (length: 2) │ │ │ │ │ ├── @ AssocNode (location: (14,4)-(14,8)) + │ │ │ │ │ │ ├── flags: static_literal │ │ │ │ │ │ ├── key: │ │ │ │ │ │ │ @ SymbolNode (location: (14,4)-(14,6)) - │ │ │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ │ │ ├── value_loc: (14,4)-(14,5) = "a" │ │ │ │ │ │ │ ├── closing_loc: (14,5)-(14,6) = ":" │ │ │ │ │ │ │ └── unescaped: "a" │ │ │ │ │ │ ├── value: │ │ │ │ │ │ │ @ IntegerNode (location: (14,7)-(14,8)) - │ │ │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ │ │ └── value: 1 │ │ │ │ │ │ └── operator_loc: ∅ │ │ │ │ │ └── @ AssocNode (location: (14,10)-(14,15)) + │ │ │ │ │ ├── flags: static_literal │ │ │ │ │ ├── key: │ │ │ │ │ │ @ SymbolNode (location: (14,10)-(14,13)) - │ │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ │ ├── value_loc: (14,10)-(14,12) = "aa" │ │ │ │ │ │ ├── closing_loc: (14,12)-(14,13) = ":" │ │ │ │ │ │ └── unescaped: "aa" │ │ │ │ │ ├── value: │ │ │ │ │ │ @ IntegerNode (location: (14,14)-(14,15)) - │ │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ │ └── value: 2 │ │ │ │ │ └── operator_loc: ∅ │ │ │ │ ├── rest: ∅ @@ -214,13 +260,17 @@ │ │ │ │ └── closing_loc: (14,15)-(14,16) = "}" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (15,2)-(15,6)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ TrueNode (location: (15,2)-(15,6)) + │ │ │ │ └── flags: newline, static_literal │ │ │ ├── in_loc: (14,0)-(14,2) = "in" │ │ │ └── then_loc: (14,17)-(14,21) = "then" │ │ ├── @ InNode (location: (16,0)-(17,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── pattern: │ │ │ │ @ HashPatternNode (location: (16,3)-(16,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── constant: ∅ │ │ │ │ ├── elements: (length: 0) │ │ │ │ ├── rest: ∅ @@ -228,43 +278,53 @@ │ │ │ │ └── closing_loc: (16,4)-(16,5) = "}" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (17,2)-(17,6)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ TrueNode (location: (17,2)-(17,6)) + │ │ │ │ └── flags: newline, static_literal │ │ │ ├── in_loc: (16,0)-(16,2) = "in" │ │ │ └── then_loc: (16,6)-(16,10) = "then" │ │ ├── @ InNode (location: (18,0)-(19,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── pattern: │ │ │ │ @ HashPatternNode (location: (18,3)-(18,10)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── constant: ∅ │ │ │ │ ├── elements: (length: 0) │ │ │ │ ├── rest: │ │ │ │ │ @ NoKeywordsParameterNode (location: (18,4)-(18,9)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── operator_loc: (18,4)-(18,6) = "**" │ │ │ │ │ └── keyword_loc: (18,6)-(18,9) = "nil" │ │ │ │ ├── opening_loc: (18,3)-(18,4) = "{" │ │ │ │ └── closing_loc: (18,9)-(18,10) = "}" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (19,2)-(19,6)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ TrueNode (location: (19,2)-(19,6)) + │ │ │ │ └── flags: newline, static_literal │ │ │ ├── in_loc: (18,0)-(18,2) = "in" │ │ │ └── then_loc: (18,11)-(18,15) = "then" │ │ ├── @ InNode (location: (20,0)-(21,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── pattern: │ │ │ │ @ HashPatternNode (location: (20,3)-(20,11)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── constant: ∅ │ │ │ │ ├── elements: (length: 1) │ │ │ │ │ └── @ AssocNode (location: (20,4)-(20,10)) + │ │ │ │ │ ├── flags: static_literal │ │ │ │ │ ├── key: │ │ │ │ │ │ @ SymbolNode (location: (20,4)-(20,8)) - │ │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ │ ├── opening_loc: (20,4)-(20,5) = "\"" │ │ │ │ │ │ ├── value_loc: (20,5)-(20,6) = "a" │ │ │ │ │ │ ├── closing_loc: (20,6)-(20,8) = "\":" │ │ │ │ │ │ └── unescaped: "a" │ │ │ │ │ ├── value: │ │ │ │ │ │ @ IntegerNode (location: (20,9)-(20,10)) - │ │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ │ └── value: 1 │ │ │ │ │ └── operator_loc: ∅ │ │ │ │ ├── rest: ∅ @@ -272,90 +332,114 @@ │ │ │ │ └── closing_loc: (20,10)-(20,11) = "}" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (21,2)-(21,6)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ TrueNode (location: (21,2)-(21,6)) + │ │ │ │ └── flags: newline, static_literal │ │ │ ├── in_loc: (20,0)-(20,2) = "in" │ │ │ └── then_loc: (20,12)-(20,16) = "then" │ │ ├── @ InNode (location: (22,0)-(23,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── pattern: │ │ │ │ @ AlternationPatternNode (location: (22,3)-(22,8)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── left: │ │ │ │ │ @ IntegerNode (location: (22,3)-(22,4)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ ├── right: │ │ │ │ │ @ IntegerNode (location: (22,7)-(22,8)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 2 │ │ │ │ └── operator_loc: (22,5)-(22,6) = "|" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (23,2)-(23,6)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ TrueNode (location: (23,2)-(23,6)) + │ │ │ │ └── flags: newline, static_literal │ │ │ ├── in_loc: (22,0)-(22,2) = "in" │ │ │ └── then_loc: (22,9)-(22,13) = "then" │ │ ├── @ InNode (location: (24,0)-(25,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── pattern: │ │ │ │ @ CapturePatternNode (location: (24,3)-(24,9)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── value: │ │ │ │ │ @ IntegerNode (location: (24,3)-(24,4)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ ├── target: │ │ │ │ │ @ LocalVariableTargetNode (location: (24,8)-(24,9)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── name: :a │ │ │ │ │ └── depth: 0 │ │ │ │ └── operator_loc: (24,5)-(24,7) = "=>" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (25,2)-(25,6)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ TrueNode (location: (25,2)-(25,6)) + │ │ │ │ └── flags: newline, static_literal │ │ │ ├── in_loc: (24,0)-(24,2) = "in" │ │ │ └── then_loc: (24,10)-(24,14) = "then" │ │ ├── @ InNode (location: (26,0)-(27,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── pattern: │ │ │ │ @ PinnedVariableNode (location: (26,3)-(26,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── variable: │ │ │ │ │ @ LocalVariableReadNode (location: (26,4)-(26,5)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── name: :x │ │ │ │ │ └── depth: 0 │ │ │ │ └── operator_loc: (26,3)-(26,4) = "^" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (27,2)-(27,6)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ TrueNode (location: (27,2)-(27,6)) + │ │ │ │ └── flags: newline, static_literal │ │ │ ├── in_loc: (26,0)-(26,2) = "in" │ │ │ └── then_loc: (26,6)-(26,10) = "then" │ │ ├── @ InNode (location: (28,0)-(28,4)) + │ │ │ ├── flags: ∅ │ │ │ ├── pattern: │ │ │ │ @ IntegerNode (location: (28,3)-(28,4)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── statements: ∅ │ │ │ ├── in_loc: (28,0)-(28,2) = "in" │ │ │ └── then_loc: ∅ │ │ └── @ InNode (location: (29,0)-(30,6)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ IntegerNode (location: (29,3)-(29,4)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── statements: │ │ │ @ StatementsNode (location: (30,2)-(30,6)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ TrueNode (location: (30,2)-(30,6)) + │ │ │ └── flags: newline, static_literal │ │ ├── in_loc: (29,0)-(29,2) = "in" │ │ └── then_loc: (29,5)-(29,9) = "then" │ ├── consequent: │ │ @ ElseNode (location: (31,0)-(33,3)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (31,0)-(31,4) = "else" │ │ ├── statements: │ │ │ @ StatementsNode (location: (32,2)-(32,6)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ TrueNode (location: (32,2)-(32,6)) + │ │ │ └── flags: newline, static_literal │ │ └── end_keyword_loc: (33,0)-(33,3) = "end" │ ├── case_keyword_loc: (1,0)-(1,4) = "case" │ └── end_keyword_loc: (33,0)-(33,3) = "end" ├── @ CaseMatchNode (location: (34,0)-(36,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (34,5)-(34,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -369,28 +453,33 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (35,0)-(35,17)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ArrayPatternNode (location: (35,3)-(35,17)) + │ │ │ ├── flags: ∅ │ │ │ ├── constant: │ │ │ │ @ ConstantReadNode (location: (35,3)-(35,4)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── name: :A │ │ │ ├── requireds: (length: 2) │ │ │ │ ├── @ IntegerNode (location: (35,5)-(35,6)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ └── @ IntegerNode (location: (35,8)-(35,9)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ ├── rest: │ │ │ │ @ SplatNode (location: (35,11)-(35,13)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── operator_loc: (35,11)-(35,12) = "*" │ │ │ │ └── expression: │ │ │ │ @ LocalVariableTargetNode (location: (35,12)-(35,13)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :a │ │ │ │ └── depth: 0 │ │ │ ├── posts: (length: 1) │ │ │ │ └── @ IntegerNode (location: (35,15)-(35,16)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 3 │ │ │ ├── opening_loc: (35,4)-(35,5) = "[" │ │ │ └── closing_loc: (35,16)-(35,17) = "]" @@ -401,6 +490,7 @@ │ ├── case_keyword_loc: (34,0)-(34,4) = "case" │ └── end_keyword_loc: (36,0)-(36,3) = "end" ├── @ CaseMatchNode (location: (37,0)-(40,3)) + │ ├── flags: newline │ ├── predicate: │ │ @ CallNode (location: (37,5)-(37,8)) │ │ ├── flags: variable_call, ignore_visibility @@ -414,29 +504,35 @@ │ │ └── block: ∅ │ ├── conditions: (length: 1) │ │ └── @ InNode (location: (38,0)-(38,4)) + │ │ ├── flags: ∅ │ │ ├── pattern: │ │ │ @ ConstantReadNode (location: (38,3)-(38,4)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :A │ │ ├── statements: ∅ │ │ ├── in_loc: (38,0)-(38,2) = "in" │ │ └── then_loc: ∅ │ ├── consequent: │ │ @ ElseNode (location: (39,0)-(40,3)) + │ │ ├── flags: ∅ │ │ ├── else_keyword_loc: (39,0)-(39,4) = "else" │ │ ├── statements: ∅ │ │ └── end_keyword_loc: (40,0)-(40,3) = "end" │ ├── case_keyword_loc: (37,0)-(37,4) = "case" │ └── end_keyword_loc: (40,0)-(40,3) = "end" └── @ MatchPredicateNode (location: (41,0)-(41,8)) + ├── flags: newline ├── value: │ @ IntegerNode (location: (41,0)-(41,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── pattern: │ @ ArrayPatternNode (location: (41,5)-(41,8)) + │ ├── flags: ∅ │ ├── constant: ∅ │ ├── requireds: (length: 1) │ │ └── @ LocalVariableTargetNode (location: (41,6)-(41,7)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── rest: ∅ diff --git a/test/prism/snapshots/unparser/corpus/literal/pragma.txt b/test/prism/snapshots/unparser/corpus/literal/pragma.txt index 08e386b872bc54..2a82f0c860eb5a 100644 --- a/test/prism/snapshots/unparser/corpus/literal/pragma.txt +++ b/test/prism/snapshots/unparser/corpus/literal/pragma.txt @@ -1,15 +1,19 @@ @ ProgramNode (location: (1,0)-(4,7)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(4,7)) + ├── flags: ∅ └── body: (length: 4) ├── @ SourceEncodingNode (location: (1,0)-(1,12)) + │ └── flags: newline, static_literal ├── @ SourceFileNode (location: (2,0)-(2,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ └── filepath: "unparser/corpus/literal/pragma.txt" ├── @ SourceLineNode (location: (3,0)-(3,8)) + │ └── flags: newline, static_literal └── @ CallNode (location: (4,0)-(4,7)) - ├── flags: variable_call, ignore_visibility + ├── flags: newline, variable_call, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :__dir__ diff --git a/test/prism/snapshots/unparser/corpus/literal/range.txt b/test/prism/snapshots/unparser/corpus/literal/range.txt index ab015d04fce0e2..d6bbe34b3430a0 100644 --- a/test/prism/snapshots/unparser/corpus/literal/range.txt +++ b/test/prism/snapshots/unparser/corpus/literal/range.txt @@ -1,55 +1,61 @@ @ ProgramNode (location: (1,0)-(4,5)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(4,5)) + ├── flags: ∅ └── body: (length: 4) ├── @ ParenthesesNode (location: (1,0)-(1,5)) + │ ├── flags: newline │ ├── body: │ │ @ StatementsNode (location: (1,1)-(1,4)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ RangeNode (location: (1,1)-(1,4)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline, static_literal │ │ ├── left: │ │ │ @ IntegerNode (location: (1,1)-(1,2)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: ∅ │ │ └── operator_loc: (1,2)-(1,4) = ".." │ ├── opening_loc: (1,0)-(1,1) = "(" │ └── closing_loc: (1,4)-(1,5) = ")" ├── @ RangeNode (location: (2,0)-(2,4)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── left: │ │ @ IntegerNode (location: (2,0)-(2,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── right: │ │ @ IntegerNode (location: (2,3)-(2,4)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ └── operator_loc: (2,1)-(2,3) = ".." ├── @ ParenthesesNode (location: (3,0)-(3,6)) + │ ├── flags: newline │ ├── body: │ │ @ StatementsNode (location: (3,1)-(3,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ RangeNode (location: (3,1)-(3,5)) - │ │ ├── flags: exclude_end + │ │ ├── flags: newline, static_literal, exclude_end │ │ ├── left: │ │ │ @ IntegerNode (location: (3,1)-(3,2)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── right: ∅ │ │ └── operator_loc: (3,2)-(3,5) = "..." │ ├── opening_loc: (3,0)-(3,1) = "(" │ └── closing_loc: (3,5)-(3,6) = ")" └── @ RangeNode (location: (4,0)-(4,5)) - ├── flags: exclude_end + ├── flags: newline, static_literal, exclude_end ├── left: │ @ IntegerNode (location: (4,0)-(4,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── right: │ @ IntegerNode (location: (4,4)-(4,5)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 └── operator_loc: (4,1)-(4,4) = "..." diff --git a/test/prism/snapshots/unparser/corpus/literal/rescue.txt b/test/prism/snapshots/unparser/corpus/literal/rescue.txt index d3c9d62166e638..78801c1d90a204 100644 --- a/test/prism/snapshots/unparser/corpus/literal/rescue.txt +++ b/test/prism/snapshots/unparser/corpus/literal/rescue.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(3,27)) +├── flags: ∅ ├── locals: [:x] └── statements: @ StatementsNode (location: (1,0)-(3,27)) + ├── flags: ∅ └── body: (length: 3) ├── @ RescueModifierNode (location: (1,0)-(1,14)) + │ ├── flags: newline │ ├── expression: │ │ @ CallNode (location: (1,0)-(1,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -28,6 +31,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ RescueModifierNode (location: (2,0)-(2,21)) + │ ├── flags: newline │ ├── expression: │ │ @ CallNode (location: (2,0)-(2,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -59,15 +63,19 @@ │ ├── closing_loc: ∅ │ └── block: ∅ └── @ LocalVariableWriteNode (location: (3,0)-(3,27)) + ├── flags: newline ├── name: :x ├── depth: 0 ├── name_loc: (3,0)-(3,1) = "x" ├── value: │ @ ParenthesesNode (location: (3,4)-(3,27)) + │ ├── flags: ∅ │ ├── body: │ │ @ StatementsNode (location: (3,5)-(3,26)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ RescueModifierNode (location: (3,5)-(3,26)) + │ │ ├── flags: newline │ │ ├── expression: │ │ │ @ CallNode (location: (3,5)-(3,8)) │ │ │ ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/unparser/corpus/literal/send.txt b/test/prism/snapshots/unparser/corpus/literal/send.txt index 3fd7f719a1d7f8..56d137402946d8 100644 --- a/test/prism/snapshots/unparser/corpus/literal/send.txt +++ b/test/prism/snapshots/unparser/corpus/literal/send.txt @@ -1,31 +1,42 @@ @ ProgramNode (location: (1,0)-(84,7)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(84,7)) + ├── flags: ∅ └── body: (length: 62) ├── @ ModuleNode (location: (1,0)-(3,3)) + │ ├── flags: newline │ ├── locals: [:foo, :a, :_] │ ├── module_keyword_loc: (1,0)-(1,6) = "module" │ ├── constant_path: │ │ @ ConstantReadNode (location: (1,7)-(1,8)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── body: │ │ @ StatementsNode (location: (2,2)-(2,22)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableOrWriteNode (location: (2,2)-(2,22)) + │ │ ├── flags: newline │ │ ├── name_loc: (2,2)-(2,5) = "foo" │ │ ├── operator_loc: (2,6)-(2,9) = "||=" │ │ ├── value: │ │ │ @ ParenthesesNode (location: (2,10)-(2,22)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (2,11)-(2,21)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ MultiWriteNode (location: (2,11)-(2,21)) + │ │ │ │ ├── flags: newline │ │ │ │ ├── lefts: (length: 2) │ │ │ │ │ ├── @ LocalVariableTargetNode (location: (2,12)-(2,13)) + │ │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ │ ├── name: :a │ │ │ │ │ │ └── depth: 0 │ │ │ │ │ └── @ LocalVariableTargetNode (location: (2,15)-(2,16)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ ├── name: :_ │ │ │ │ │ └── depth: 0 │ │ │ │ ├── rest: ∅ @@ -51,27 +62,32 @@ │ ├── end_keyword_loc: (3,0)-(3,3) = "end" │ └── name: :A ├── @ ModuleNode (location: (5,0)-(8,3)) + │ ├── flags: newline │ ├── locals: [:local] │ ├── module_keyword_loc: (5,0)-(5,6) = "module" │ ├── constant_path: │ │ @ ConstantReadNode (location: (5,7)-(5,8)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── body: │ │ @ StatementsNode (location: (6,2)-(7,11)) + │ │ ├── flags: ∅ │ │ └── body: (length: 2) │ │ ├── @ LocalVariableWriteNode (location: (6,2)-(6,11)) + │ │ │ ├── flags: newline │ │ │ ├── name: :local │ │ │ ├── depth: 0 │ │ │ ├── name_loc: (6,2)-(6,7) = "local" │ │ │ ├── value: │ │ │ │ @ IntegerNode (location: (6,10)-(6,11)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── operator_loc: (6,8)-(6,9) = "=" │ │ └── @ CallNode (location: (7,2)-(7,11)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ LocalVariableReadNode (location: (7,2)-(7,7)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :local │ │ │ └── depth: 0 │ │ ├── call_operator_loc: (7,7)-(7,8) = "." @@ -84,13 +100,15 @@ │ ├── end_keyword_loc: (8,0)-(8,3) = "end" │ └── name: :A ├── @ CallNode (location: (9,0)-(10,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ClassNode (location: (9,0)-(10,3)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── class_keyword_loc: (9,0)-(9,5) = "class" │ │ ├── constant_path: │ │ │ @ ConstantReadNode (location: (9,6)-(9,7)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :A │ │ ├── inheritance_operator_loc: ∅ │ │ ├── superclass: ∅ @@ -105,13 +123,15 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (11,0)-(12,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ModuleNode (location: (11,0)-(12,3)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── module_keyword_loc: (11,0)-(11,6) = "module" │ │ ├── constant_path: │ │ │ @ ConstantReadNode (location: (11,7)-(11,8)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :A │ │ ├── body: ∅ │ │ ├── end_keyword_loc: (12,0)-(12,3) = "end" @@ -124,13 +144,15 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (13,0)-(15,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ BeginNode (location: (13,0)-(15,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: (13,0)-(13,5) = "begin" │ │ ├── statements: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (14,0)-(14,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (14,0)-(14,6) = "rescue" │ │ │ ├── exceptions: (length: 0) │ │ │ ├── operator_loc: ∅ @@ -148,15 +170,19 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (16,0)-(19,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CaseNode (location: (16,0)-(19,3)) + │ │ ├── flags: ∅ │ │ ├── predicate: │ │ │ @ ParenthesesNode (location: (16,5)-(17,10)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (16,6)-(17,9)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 2) │ │ │ │ ├── @ DefNode (location: (16,6)-(17,3)) + │ │ │ │ │ ├── flags: newline │ │ │ │ │ ├── name: :foo │ │ │ │ │ ├── name_loc: (16,10)-(16,13) = "foo" │ │ │ │ │ ├── receiver: ∅ @@ -170,7 +196,7 @@ │ │ │ │ │ ├── equal_loc: ∅ │ │ │ │ │ └── end_keyword_loc: (17,0)-(17,3) = "end" │ │ │ │ └── @ SymbolNode (location: (17,5)-(17,9)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (17,5)-(17,6) = ":" │ │ │ │ ├── value_loc: (17,6)-(17,9) = "bar" │ │ │ │ ├── closing_loc: ∅ @@ -179,6 +205,7 @@ │ │ │ └── closing_loc: (17,9)-(17,10) = ")" │ │ ├── conditions: (length: 1) │ │ │ └── @ WhenNode (location: (18,0)-(18,8)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (18,0)-(18,4) = "when" │ │ │ ├── conditions: (length: 1) │ │ │ │ └── @ CallNode (location: (18,5)-(18,8)) @@ -204,9 +231,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (20,0)-(22,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CaseNode (location: (20,0)-(22,3)) + │ │ ├── flags: ∅ │ │ ├── predicate: │ │ │ @ CallNode (location: (20,5)-(20,8)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -220,6 +248,7 @@ │ │ │ └── block: ∅ │ │ ├── conditions: (length: 1) │ │ │ └── @ WhenNode (location: (21,0)-(21,8)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (21,0)-(21,4) = "when" │ │ │ ├── conditions: (length: 1) │ │ │ │ └── @ CallNode (location: (21,5)-(21,8)) @@ -245,14 +274,16 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (23,0)-(24,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ SingletonClassNode (location: (23,0)-(24,3)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── class_keyword_loc: (23,0)-(23,5) = "class" │ │ ├── operator_loc: (23,6)-(23,8) = "<<" │ │ ├── expression: │ │ │ @ SelfNode (location: (23,9)-(23,13)) + │ │ │ └── flags: ∅ │ │ ├── body: ∅ │ │ └── end_keyword_loc: (24,0)-(24,3) = "end" │ ├── call_operator_loc: (24,3)-(24,4) = "." @@ -263,13 +294,15 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (25,0)-(26,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ DefNode (location: (25,0)-(26,3)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ ├── name_loc: (25,9)-(25,12) = "foo" │ │ ├── receiver: │ │ │ @ SelfNode (location: (25,4)-(25,8)) + │ │ │ └── flags: ∅ │ │ ├── parameters: ∅ │ │ ├── body: ∅ │ │ ├── locals: [] @@ -287,9 +320,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (27,0)-(28,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ DefNode (location: (27,0)-(28,3)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ ├── name_loc: (27,4)-(27,7) = "foo" │ │ ├── receiver: ∅ @@ -310,7 +344,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (29,0)-(30,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ UntilNode (location: (29,0)-(30,3)) │ │ ├── flags: ∅ @@ -336,7 +370,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (31,0)-(32,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ WhileNode (location: (31,0)-(32,3)) │ │ ├── flags: ∅ @@ -362,7 +396,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (33,0)-(34,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (33,0)-(34,1)) │ │ ├── flags: ignore_visibility @@ -375,6 +409,7 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (33,5)-(34,1)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── parameters: ∅ │ │ ├── body: ∅ @@ -388,9 +423,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (35,0)-(36,7)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ IfNode (location: (35,0)-(36,3)) + │ │ ├── flags: newline │ │ ├── if_keyword_loc: (35,0)-(35,2) = "if" │ │ ├── predicate: │ │ │ @ CallNode (location: (35,3)-(35,6)) @@ -415,17 +451,19 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (37,0)-(37,19)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ParenthesesNode (location: (37,0)-(37,15)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (37,1)-(37,14)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (37,1)-(37,14)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── receiver: │ │ │ │ @ RegularExpressionNode (location: (37,1)-(37,6)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (37,1)-(37,2) = "/" │ │ │ │ ├── content_loc: (37,2)-(37,5) = "bar" │ │ │ │ ├── closing_loc: (37,5)-(37,6) = "/" @@ -439,7 +477,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ SymbolNode (location: (37,10)-(37,14)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (37,10)-(37,11) = ":" │ │ │ │ ├── value_loc: (37,11)-(37,14) = "foo" │ │ │ │ ├── closing_loc: ∅ @@ -456,21 +494,23 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (38,0)-(38,10)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ParenthesesNode (location: (38,0)-(38,6)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (38,1)-(38,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ RangeNode (location: (38,1)-(38,5)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline, static_literal │ │ │ ├── left: │ │ │ │ @ IntegerNode (location: (38,1)-(38,2)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── right: │ │ │ │ @ IntegerNode (location: (38,4)-(38,5)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 2 │ │ │ └── operator_loc: (38,2)-(38,4) = ".." │ │ ├── opening_loc: (38,0)-(38,1) = "(" @@ -483,14 +523,16 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (39,0)-(39,18)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ParenthesesNode (location: (39,0)-(39,14)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (39,1)-(39,13)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (39,1)-(39,13)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── receiver: │ │ │ │ @ CallNode (location: (39,1)-(39,4)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -511,7 +553,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ RegularExpressionNode (location: (39,8)-(39,13)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: (39,8)-(39,9) = "/" │ │ │ │ ├── content_loc: (39,9)-(39,12) = "bar" │ │ │ │ ├── closing_loc: (39,12)-(39,13) = "/" @@ -528,10 +570,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (40,0)-(40,13)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ RegularExpressionNode (location: (40,0)-(40,5)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (40,0)-(40,1) = "/" │ │ ├── content_loc: (40,1)-(40,4) = "bar" │ │ ├── closing_loc: (40,4)-(40,5) = "/" @@ -545,7 +587,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ SymbolNode (location: (40,9)-(40,13)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (40,9)-(40,10) = ":" │ │ ├── value_loc: (40,10)-(40,13) = "foo" │ │ ├── closing_loc: ∅ @@ -553,10 +595,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (41,0)-(41,12)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ RegularExpressionNode (location: (41,0)-(41,5)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (41,0)-(41,1) = "/" │ │ ├── content_loc: (41,1)-(41,4) = "bar" │ │ ├── closing_loc: (41,4)-(41,5) = "/" @@ -582,17 +624,17 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ RangeNode (location: (42,0)-(42,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── left: │ │ @ IntegerNode (location: (42,0)-(42,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── right: │ │ @ CallNode (location: (42,3)-(42,8)) │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ IntegerNode (location: (42,3)-(42,4)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ ├── call_operator_loc: (42,4)-(42,5) = "." │ │ ├── name: :max @@ -603,9 +645,10 @@ │ │ └── block: ∅ │ └── operator_loc: (42,1)-(42,3) = ".." ├── @ CallNode (location: (43,0)-(43,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ConstantReadNode (location: (43,0)-(43,1)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── call_operator_loc: (43,1)-(43,2) = "." │ ├── name: :foo @@ -615,7 +658,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (44,0)-(44,5)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :FOO @@ -625,7 +668,7 @@ │ ├── closing_loc: (44,4)-(44,5) = ")" │ └── block: ∅ ├── @ CallNode (location: (45,0)-(45,4)) - │ ├── flags: safe_navigation + │ ├── flags: newline, safe_navigation │ ├── receiver: │ │ @ CallNode (location: (45,0)-(45,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -645,7 +688,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (46,0)-(46,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (46,0)-(46,1)) │ │ ├── flags: variable_call, ignore_visibility @@ -665,7 +708,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (47,0)-(47,3)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -675,7 +718,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (48,0)-(48,18)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (48,0)-(48,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -696,11 +739,13 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ ParenthesesNode (location: (48,7)-(48,18)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (48,8)-(48,17)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (48,8)-(48,17)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── receiver: │ │ │ │ @ CallNode (location: (48,8)-(48,11)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -737,7 +782,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (49,0)-(49,12)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (49,0)-(49,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -758,7 +803,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ RegularExpressionNode (location: (49,7)-(49,12)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (49,7)-(49,8) = "/" │ │ ├── content_loc: (49,8)-(49,11) = "bar" │ │ ├── closing_loc: (49,11)-(49,12) = "/" @@ -766,7 +811,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (50,0)-(50,17)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -776,12 +821,16 @@ │ ├── closing_loc: (50,17)-(50,18) = ")" │ └── block: │ @ BlockArgumentNode (location: (50,4)-(50,17)) + │ ├── flags: ∅ │ ├── expression: │ │ @ ParenthesesNode (location: (50,5)-(50,17)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (50,6)-(50,16)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ OrNode (location: (50,6)-(50,16)) + │ │ │ ├── flags: newline │ │ │ ├── left: │ │ │ │ @ CallNode (location: (50,6)-(50,9)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -809,7 +858,7 @@ │ │ └── closing_loc: (50,16)-(50,17) = ")" │ └── operator_loc: (50,4)-(50,5) = "&" ├── @ CallNode (location: (51,0)-(51,10)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -819,6 +868,7 @@ │ ├── closing_loc: (51,10)-(51,11) = ")" │ └── block: │ @ BlockArgumentNode (location: (51,4)-(51,10)) + │ ├── flags: ∅ │ ├── expression: │ │ @ CallNode (location: (51,5)-(51,10)) │ │ ├── flags: variable_call, ignore_visibility @@ -832,7 +882,7 @@ │ │ └── block: ∅ │ └── operator_loc: (51,4)-(51,5) = "&" ├── @ CallNode (location: (52,0)-(52,17)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -843,6 +893,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ SplatNode (location: (52,4)-(52,9)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (52,4)-(52,5) = "*" │ │ └── expression: │ │ @ CallNode (location: (52,5)-(52,9)) @@ -858,6 +909,7 @@ │ ├── closing_loc: (52,17)-(52,18) = ")" │ └── block: │ @ BlockArgumentNode (location: (52,11)-(52,17)) + │ ├── flags: ∅ │ ├── expression: │ │ @ CallNode (location: (52,12)-(52,17)) │ │ ├── flags: variable_call, ignore_visibility @@ -871,7 +923,7 @@ │ │ └── block: ∅ │ └── operator_loc: (52,11)-(52,12) = "&" ├── @ CallNode (location: (53,0)-(53,15)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -882,6 +934,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ SplatNode (location: (53,4)-(53,14)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (53,4)-(53,5) = "*" │ │ └── expression: │ │ @ CallNode (location: (53,5)-(53,14)) @@ -897,7 +950,7 @@ │ ├── closing_loc: (53,14)-(53,15) = ")" │ └── block: ∅ ├── @ CallNode (location: (54,0)-(54,9)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -908,15 +961,15 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 2) │ │ ├── @ IntegerNode (location: (54,4)-(54,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ IntegerNode (location: (54,7)-(54,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: (54,8)-(54,9) = ")" │ └── block: ∅ ├── @ CallNode (location: (55,0)-(55,8)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -939,7 +992,7 @@ │ ├── closing_loc: (55,7)-(55,8) = ")" │ └── block: ∅ ├── @ CallNode (location: (56,0)-(56,15)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -960,6 +1013,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ └── @ SplatNode (location: (56,9)-(56,14)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (56,9)-(56,10) = "*" │ │ └── expression: │ │ @ CallNode (location: (56,10)-(56,14)) @@ -975,7 +1029,7 @@ │ ├── closing_loc: (56,14)-(56,15) = ")" │ └── block: ∅ ├── @ CallNode (location: (57,0)-(57,17)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -1007,7 +1061,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ RegularExpressionNode (location: (57,11)-(57,16)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (57,11)-(57,12) = "/" │ │ │ ├── content_loc: (57,12)-(57,15) = "bar" │ │ │ ├── closing_loc: (57,15)-(57,16) = "/" @@ -1017,7 +1071,7 @@ │ ├── closing_loc: (57,16)-(57,17) = ")" │ └── block: ∅ ├── @ CallNode (location: (58,0)-(58,13)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (58,0)-(58,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1037,6 +1091,7 @@ │ ├── closing_loc: (58,12)-(58,13) = ")" │ └── block: │ @ BlockArgumentNode (location: (58,8)-(58,12)) + │ ├── flags: ∅ │ ├── expression: │ │ @ CallNode (location: (58,9)-(58,12)) │ │ ├── flags: variable_call, ignore_visibility @@ -1050,7 +1105,7 @@ │ │ └── block: ∅ │ └── operator_loc: (58,8)-(58,9) = "&" ├── @ CallNode (location: (59,0)-(59,26)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (59,0)-(59,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1071,6 +1126,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 3) │ │ ├── @ SplatNode (location: (59,8)-(59,13)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (59,8)-(59,9) = "*" │ │ │ └── expression: │ │ │ @ CallNode (location: (59,9)-(59,13)) @@ -1094,6 +1150,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ └── @ SplatNode (location: (59,20)-(59,25)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (59,20)-(59,21) = "*" │ │ └── expression: │ │ @ CallNode (location: (59,21)-(59,25)) @@ -1109,7 +1166,7 @@ │ ├── closing_loc: (59,25)-(59,26) = ")" │ └── block: ∅ ├── @ CallNode (location: (60,0)-(60,14)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (60,0)-(60,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1130,6 +1187,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ SplatNode (location: (60,8)-(60,13)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (60,8)-(60,9) = "*" │ │ └── expression: │ │ @ CallNode (location: (60,9)-(60,13)) @@ -1145,7 +1203,7 @@ │ ├── closing_loc: (60,13)-(60,14) = ")" │ └── block: ∅ ├── @ CallNode (location: (61,0)-(61,19)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (61,0)-(61,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1166,6 +1224,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 2) │ │ ├── @ SplatNode (location: (61,8)-(61,13)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (61,8)-(61,9) = "*" │ │ │ └── expression: │ │ │ @ CallNode (location: (61,9)-(61,13)) @@ -1191,7 +1250,7 @@ │ ├── closing_loc: (61,18)-(61,19) = ")" │ └── block: ∅ ├── @ CallNode (location: (62,0)-(62,19)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (62,0)-(62,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1212,7 +1271,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ SymbolNode (location: (62,8)-(62,12)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (62,8)-(62,9) = ":" │ │ ├── value_loc: (62,9)-(62,12) = "baz" │ │ ├── closing_loc: ∅ @@ -1220,6 +1279,7 @@ │ ├── closing_loc: (62,18)-(62,19) = ")" │ └── block: │ @ BlockArgumentNode (location: (62,14)-(62,18)) + │ ├── flags: ∅ │ ├── expression: │ │ @ CallNode (location: (62,15)-(62,18)) │ │ ├── flags: variable_call, ignore_visibility @@ -1233,7 +1293,7 @@ │ │ └── block: ∅ │ └── operator_loc: (62,14)-(62,15) = "&" ├── @ CallNode (location: (63,0)-(63,17)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (63,0)-(63,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1257,9 +1317,10 @@ │ │ ├── flags: symbol_keys │ │ └── elements: (length: 1) │ │ └── @ AssocNode (location: (63,8)-(63,16)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ SymbolNode (location: (63,8)-(63,12)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (63,8)-(63,11) = "baz" │ │ │ ├── closing_loc: (63,11)-(63,12) = ":" @@ -1279,7 +1340,7 @@ │ ├── closing_loc: (63,16)-(63,17) = ")" │ └── block: ∅ ├── @ CallNode (location: (64,0)-(64,26)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (64,0)-(64,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1313,9 +1374,10 @@ │ │ ├── flags: ∅ │ │ └── elements: (length: 1) │ │ └── @ AssocNode (location: (64,13)-(64,25)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ StringNode (location: (64,13)-(64,18)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: (64,13)-(64,14) = "\"" │ │ │ ├── content_loc: (64,14)-(64,17) = "baz" │ │ │ ├── closing_loc: (64,17)-(64,18) = "\"" @@ -1335,7 +1397,7 @@ │ ├── closing_loc: (64,25)-(64,26) = ")" │ └── block: ∅ ├── @ CallNode (location: (65,0)-(65,19)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (65,0)-(65,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1366,6 +1428,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ └── @ SplatNode (location: (65,13)-(65,18)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (65,13)-(65,14) = "*" │ │ └── expression: │ │ @ CallNode (location: (65,14)-(65,18)) @@ -1381,7 +1444,7 @@ │ ├── closing_loc: (65,18)-(65,19) = ")" │ └── block: ∅ ├── @ CallNode (location: (66,0)-(66,27)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (66,0)-(66,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1412,6 +1475,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ └── @ SplatNode (location: (66,13)-(66,18)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (66,13)-(66,14) = "*" │ │ └── expression: │ │ @ CallNode (location: (66,14)-(66,18)) @@ -1427,6 +1491,7 @@ │ ├── closing_loc: (66,26)-(66,27) = ")" │ └── block: │ @ BlockArgumentNode (location: (66,20)-(66,26)) + │ ├── flags: ∅ │ ├── expression: │ │ @ CallNode (location: (66,21)-(66,26)) │ │ ├── flags: variable_call, ignore_visibility @@ -1440,7 +1505,7 @@ │ │ └── block: ∅ │ └── operator_loc: (66,20)-(66,21) = "&" ├── @ CallNode (location: (67,0)-(67,16)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (67,0)-(67,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1471,13 +1536,14 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ └── @ HashNode (location: (67,13)-(67,15)) + │ │ ├── flags: static_literal │ │ ├── opening_loc: (67,13)-(67,14) = "{" │ │ ├── elements: (length: 0) │ │ └── closing_loc: (67,14)-(67,15) = "}" │ ├── closing_loc: (67,15)-(67,16) = ")" │ └── block: ∅ ├── @ CallNode (location: (68,0)-(68,26)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (68,0)-(68,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1498,12 +1564,14 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 2) │ │ ├── @ HashNode (location: (68,8)-(68,20)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (68,8)-(68,9) = "{" │ │ │ ├── elements: (length: 1) │ │ │ │ └── @ AssocNode (location: (68,10)-(68,18)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── key: │ │ │ │ │ @ SymbolNode (location: (68,10)-(68,14)) - │ │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ │ ├── opening_loc: ∅ │ │ │ │ │ ├── value_loc: (68,10)-(68,13) = "foo" │ │ │ │ │ ├── closing_loc: (68,13)-(68,14) = ":" @@ -1534,7 +1602,7 @@ │ ├── closing_loc: (68,25)-(68,26) = ")" │ └── block: ∅ ├── @ CallNode (location: (69,0)-(69,12)) - │ ├── flags: attribute_write + │ ├── flags: newline, attribute_write │ ├── receiver: │ │ @ CallNode (location: (69,0)-(69,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1555,7 +1623,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ SymbolNode (location: (69,8)-(69,12)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (69,8)-(69,9) = ":" │ │ ├── value_loc: (69,9)-(69,12) = "baz" │ │ ├── closing_loc: ∅ @@ -1563,7 +1631,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (70,0)-(70,9)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -1577,9 +1645,10 @@ │ │ ├── flags: symbol_keys │ │ └── elements: (length: 1) │ │ └── @ AssocNode (location: (70,4)-(70,8)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ SymbolNode (location: (70,4)-(70,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (70,4)-(70,5) = "a" │ │ │ ├── closing_loc: (70,5)-(70,6) = ":" @@ -1599,7 +1668,7 @@ │ ├── closing_loc: (70,8)-(70,9) = ")" │ └── block: ∅ ├── @ CallNode (location: (71,0)-(71,11)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (71,0)-(71,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1623,9 +1692,10 @@ │ │ ├── flags: symbol_keys │ │ └── elements: (length: 1) │ │ └── @ AssocNode (location: (71,6)-(71,10)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ SymbolNode (location: (71,6)-(71,8)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (71,6)-(71,7) = "a" │ │ │ ├── closing_loc: (71,7)-(71,8) = ":" @@ -1645,7 +1715,7 @@ │ ├── closing_loc: (71,10)-(71,11) = ")" │ └── block: ∅ ├── @ CallNode (location: (72,0)-(72,10)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (72,0)-(72,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1669,6 +1739,7 @@ │ │ ├── flags: ∅ │ │ └── elements: (length: 1) │ │ └── @ AssocSplatNode (location: (72,6)-(72,9)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ CallNode (location: (72,8)-(72,9)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -1684,7 +1755,7 @@ │ ├── closing_loc: (72,9)-(72,10) = ")" │ └── block: ∅ ├── @ CallNode (location: (73,0)-(73,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (73,0)-(73,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1705,6 +1776,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ SplatNode (location: (73,4)-(73,8)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (73,4)-(73,5) = "*" │ │ └── expression: │ │ @ CallNode (location: (73,5)-(73,8)) @@ -1720,7 +1792,7 @@ │ ├── closing_loc: (73,8)-(73,9) = "]" │ └── block: ∅ ├── @ CallNode (location: (74,0)-(74,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (74,0)-(74,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1741,15 +1813,15 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 2) │ │ ├── @ IntegerNode (location: (74,4)-(74,5)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ └── @ IntegerNode (location: (74,7)-(74,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── closing_loc: (74,8)-(74,9) = "]" │ └── block: ∅ ├── @ CallNode (location: (75,0)-(75,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (75,0)-(75,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -1769,9 +1841,10 @@ │ ├── closing_loc: (75,4)-(75,5) = "]" │ └── block: ∅ ├── @ CallNode (location: (76,0)-(76,8)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: │ │ @ SelfNode (location: (76,0)-(76,4)) + │ │ └── flags: ∅ │ ├── call_operator_loc: (76,4)-(76,5) = "." │ ├── name: :foo │ ├── message_loc: (76,5)-(76,8) = "foo" @@ -1780,9 +1853,10 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (77,0)-(77,13)) - │ ├── flags: attribute_write, ignore_visibility + │ ├── flags: newline, attribute_write, ignore_visibility │ ├── receiver: │ │ @ SelfNode (location: (77,0)-(77,4)) + │ │ └── flags: ∅ │ ├── call_operator_loc: (77,4)-(77,5) = "." │ ├── name: :foo= │ ├── message_loc: (77,5)-(77,8) = "foo" @@ -1792,7 +1866,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ SymbolNode (location: (77,9)-(77,13)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (77,9)-(77,10) = ":" │ │ ├── value_loc: (77,10)-(77,13) = "bar" │ │ ├── closing_loc: ∅ @@ -1800,14 +1874,16 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (78,0)-(78,17)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ParenthesesNode (location: (78,0)-(78,7)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (78,1)-(78,6)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (78,1)-(78,6)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── receiver: │ │ │ │ @ CallNode (location: (78,1)-(78,2)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -1850,11 +1926,13 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ ParenthesesNode (location: (78,10)-(78,17)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (78,11)-(78,16)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (78,11)-(78,16)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── receiver: │ │ │ │ @ CallNode (location: (78,11)-(78,12)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -1891,14 +1969,16 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (79,0)-(79,19)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ParenthesesNode (location: (79,0)-(79,7)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (79,1)-(79,6)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (79,1)-(79,6)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── receiver: │ │ │ │ @ CallNode (location: (79,1)-(79,2)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -1986,14 +2066,16 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (80,0)-(80,17)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ParenthesesNode (location: (80,0)-(80,7)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (80,1)-(80,6)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (80,1)-(80,6)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── receiver: │ │ │ │ @ CallNode (location: (80,1)-(80,2)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -2057,6 +2139,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ SplatNode (location: (80,14)-(80,16)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (80,14)-(80,15) = "*" │ │ │ └── expression: │ │ │ @ CallNode (location: (80,15)-(80,16)) @@ -2074,7 +2157,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (81,0)-(81,8)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :x @@ -2088,6 +2171,7 @@ │ │ ├── flags: ∅ │ │ └── elements: (length: 1) │ │ └── @ AssocSplatNode (location: (81,2)-(81,7)) + │ │ ├── flags: ∅ │ │ ├── value: │ │ │ @ CallNode (location: (81,4)-(81,7)) │ │ │ ├── flags: variable_call, ignore_visibility @@ -2103,7 +2187,7 @@ │ ├── closing_loc: (81,7)-(81,8) = ")" │ └── block: ∅ ├── @ CallNode (location: (82,0)-(82,6)) - │ ├── flags: safe_navigation + │ ├── flags: newline, safe_navigation │ ├── receiver: │ │ @ CallNode (location: (82,0)-(82,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2123,7 +2207,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (83,0)-(83,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (83,0)-(83,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -2156,7 +2240,7 @@ │ ├── closing_loc: (83,7)-(83,8) = ")" │ └── block: ∅ └── @ CallNode (location: (84,0)-(84,7)) - ├── flags: safe_navigation + ├── flags: newline, safe_navigation ├── receiver: │ @ CallNode (location: (84,0)-(84,1)) │ ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/unparser/corpus/literal/since/27.txt b/test/prism/snapshots/unparser/corpus/literal/since/27.txt index 60edc18604f026..e4cda312f836bf 100644 --- a/test/prism/snapshots/unparser/corpus/literal/since/27.txt +++ b/test/prism/snapshots/unparser/corpus/literal/since/27.txt @@ -1,23 +1,29 @@ @ ProgramNode (location: (1,0)-(4,5)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(4,5)) + ├── flags: ∅ └── body: (length: 2) ├── @ LambdaNode (location: (1,0)-(3,1)) + │ ├── flags: newline │ ├── locals: [:_1, :_2] │ ├── operator_loc: (1,0)-(1,2) = "->" │ ├── opening_loc: (1,3)-(1,4) = "{" │ ├── closing_loc: (3,0)-(3,1) = "}" │ ├── parameters: │ │ @ NumberedParametersNode (location: (1,0)-(3,1)) + │ │ ├── flags: ∅ │ │ └── maximum: 2 │ └── body: │ @ StatementsNode (location: (2,2)-(2,9)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (2,2)-(2,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (2,2)-(2,4)) + │ │ ├── flags: ∅ │ │ ├── name: :_1 │ │ └── depth: 0 │ ├── call_operator_loc: ∅ @@ -29,20 +35,23 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ LocalVariableReadNode (location: (2,7)-(2,9)) + │ │ ├── flags: ∅ │ │ ├── name: :_2 │ │ └── depth: 0 │ ├── closing_loc: ∅ │ └── block: ∅ └── @ ParenthesesNode (location: (4,0)-(4,5)) + ├── flags: newline ├── body: │ @ StatementsNode (location: (4,1)-(4,4)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ RangeNode (location: (4,1)-(4,4)) - │ ├── flags: ∅ + │ ├── flags: newline, static_literal │ ├── left: ∅ │ ├── right: │ │ @ IntegerNode (location: (4,3)-(4,4)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ └── operator_loc: (4,1)-(4,3) = ".." ├── opening_loc: (4,0)-(4,1) = "(" diff --git a/test/prism/snapshots/unparser/corpus/literal/since/30.txt b/test/prism/snapshots/unparser/corpus/literal/since/30.txt index 300dd869f5a59a..0102b2c97b4f5e 100644 --- a/test/prism/snapshots/unparser/corpus/literal/since/30.txt +++ b/test/prism/snapshots/unparser/corpus/literal/since/30.txt @@ -1,18 +1,23 @@ @ ProgramNode (location: (1,0)-(4,17)) +├── flags: ∅ ├── locals: [:a, :foo] └── statements: @ StatementsNode (location: (1,0)-(4,17)) + ├── flags: ∅ └── body: (length: 4) ├── @ MatchRequiredNode (location: (1,0)-(1,8)) + │ ├── flags: newline │ ├── value: │ │ @ IntegerNode (location: (1,0)-(1,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── pattern: │ │ @ ArrayPatternNode (location: (1,5)-(1,8)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ LocalVariableTargetNode (location: (1,6)-(1,7)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ ├── rest: ∅ @@ -21,16 +26,19 @@ │ │ └── closing_loc: (1,7)-(1,8) = "]" │ └── operator_loc: (1,2)-(1,4) = "=>" ├── @ MatchRequiredNode (location: (2,0)-(2,8)) + │ ├── flags: newline │ ├── value: │ │ @ IntegerNode (location: (2,0)-(2,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── pattern: │ │ @ ArrayPatternNode (location: (2,5)-(2,8)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── requireds: (length: 0) │ │ ├── rest: │ │ │ @ SplatNode (location: (2,6)-(2,7)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (2,6)-(2,7) = "*" │ │ │ └── expression: ∅ │ │ ├── posts: (length: 0) @@ -38,49 +46,59 @@ │ │ └── closing_loc: (2,7)-(2,8) = "]" │ └── operator_loc: (2,2)-(2,4) = "=>" ├── @ MatchPredicateNode (location: (3,0)-(3,15)) + │ ├── flags: newline │ ├── value: │ │ @ IntegerNode (location: (3,0)-(3,1)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── pattern: │ │ @ FindPatternNode (location: (3,5)-(3,15)) + │ │ ├── flags: ∅ │ │ ├── constant: ∅ │ │ ├── left: │ │ │ @ SplatNode (location: (3,6)-(3,7)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (3,6)-(3,7) = "*" │ │ │ └── expression: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ IntegerNode (location: (3,9)-(3,11)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 42 │ │ ├── right: │ │ │ @ SplatNode (location: (3,13)-(3,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (3,13)-(3,14) = "*" │ │ │ └── expression: ∅ │ │ ├── opening_loc: (3,5)-(3,6) = "[" │ │ └── closing_loc: (3,14)-(3,15) = "]" │ └── operator_loc: (3,2)-(3,4) = "in" └── @ MatchPredicateNode (location: (4,0)-(4,17)) + ├── flags: newline ├── value: │ @ IntegerNode (location: (4,0)-(4,1)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 1 ├── pattern: │ @ FindPatternNode (location: (4,5)-(4,17)) + │ ├── flags: ∅ │ ├── constant: ∅ │ ├── left: │ │ @ SplatNode (location: (4,6)-(4,7)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (4,6)-(4,7) = "*" │ │ └── expression: ∅ │ ├── requireds: (length: 1) │ │ └── @ LocalVariableTargetNode (location: (4,9)-(4,10)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── right: │ │ @ SplatNode (location: (4,12)-(4,16)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (4,12)-(4,13) = "*" │ │ └── expression: │ │ @ LocalVariableTargetNode (location: (4,13)-(4,16)) + │ │ ├── flags: ∅ │ │ ├── name: :foo │ │ └── depth: 0 │ ├── opening_loc: (4,5)-(4,6) = "[" diff --git a/test/prism/snapshots/unparser/corpus/literal/since/31.txt b/test/prism/snapshots/unparser/corpus/literal/since/31.txt index 142a56ae8321ef..81bcd9662bd136 100644 --- a/test/prism/snapshots/unparser/corpus/literal/since/31.txt +++ b/test/prism/snapshots/unparser/corpus/literal/since/31.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(7,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(7,3)) + ├── flags: ∅ └── body: (length: 2) ├── @ DefNode (location: (1,0)-(3,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (1,4)-(1,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,8)-(1,9)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -23,9 +27,10 @@ │ │ └── operator_loc: (1,8)-(1,9) = "&" │ ├── body: │ │ @ StatementsNode (location: (2,2)-(2,7)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (2,2)-(2,7)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -35,6 +40,7 @@ │ │ ├── closing_loc: (2,7)-(2,8) = ")" │ │ └── block: │ │ @ BlockArgumentNode (location: (2,6)-(2,7)) + │ │ ├── flags: ∅ │ │ ├── expression: ∅ │ │ └── operator_loc: (2,6)-(2,7) = "&" │ ├── locals: [] @@ -45,11 +51,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (3,0)-(3,3) = "end" └── @ DefNode (location: (5,0)-(7,3)) + ├── flags: newline ├── name: :foo ├── name_loc: (5,4)-(5,7) = "foo" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (5,8)-(5,12)) + │ ├── flags: ∅ │ ├── requireds: (length: 1) │ │ └── @ RequiredParameterNode (location: (5,8)-(5,9)) │ │ ├── flags: ∅ @@ -67,9 +75,10 @@ │ └── operator_loc: (5,11)-(5,12) = "&" ├── body: │ @ StatementsNode (location: (6,2)-(6,7)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (6,2)-(6,7)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :bar @@ -79,6 +88,7 @@ │ ├── closing_loc: (6,7)-(6,8) = ")" │ └── block: │ @ BlockArgumentNode (location: (6,6)-(6,7)) + │ ├── flags: ∅ │ ├── expression: ∅ │ └── operator_loc: (6,6)-(6,7) = "&" ├── locals: [:a] diff --git a/test/prism/snapshots/unparser/corpus/literal/since/32.txt b/test/prism/snapshots/unparser/corpus/literal/since/32.txt index 2b28be2fa80d4f..efd01196785f96 100644 --- a/test/prism/snapshots/unparser/corpus/literal/since/32.txt +++ b/test/prism/snapshots/unparser/corpus/literal/since/32.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(11,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(11,3)) + ├── flags: ∅ └── body: (length: 3) ├── @ DefNode (location: (1,0)-(3,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (1,4)-(1,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,8)-(1,20)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (1,8)-(1,16)) │ │ │ ├── flags: ∅ @@ -26,9 +30,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (2,2)-(2,19)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (2,2)-(2,19)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -39,12 +44,14 @@ │ │ │ ├── flags: contains_keywords, contains_keyword_splat │ │ │ └── arguments: (length: 2) │ │ │ ├── @ LocalVariableReadNode (location: (2,6)-(2,14)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :argument │ │ │ │ └── depth: 0 │ │ │ └── @ KeywordHashNode (location: (2,16)-(2,18)) │ │ │ ├── flags: ∅ │ │ │ └── elements: (length: 1) │ │ │ └── @ AssocSplatNode (location: (2,16)-(2,18)) + │ │ │ ├── flags: ∅ │ │ │ ├── value: ∅ │ │ │ └── operator_loc: (2,16)-(2,18) = "**" │ │ ├── closing_loc: (2,18)-(2,19) = ")" @@ -57,11 +64,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (3,0)-(3,3) = "end" ├── @ DefNode (location: (5,0)-(7,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (5,4)-(5,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (5,8)-(5,19)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (5,8)-(5,16)) │ │ │ ├── flags: ∅ @@ -79,9 +88,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (6,2)-(6,18)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (6,2)-(6,18)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -92,9 +102,11 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 2) │ │ │ ├── @ LocalVariableReadNode (location: (6,6)-(6,14)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :argument │ │ │ │ └── depth: 0 │ │ │ └── @ SplatNode (location: (6,16)-(6,17)) + │ │ │ ├── flags: ∅ │ │ │ ├── operator_loc: (6,16)-(6,17) = "*" │ │ │ └── expression: ∅ │ │ ├── closing_loc: (6,17)-(6,18) = ")" @@ -107,11 +119,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (7,0)-(7,3) = "end" └── @ DefNode (location: (9,0)-(11,3)) + ├── flags: newline ├── name: :foo ├── name_loc: (9,4)-(9,7) = "foo" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (9,8)-(9,10)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ @@ -126,24 +140,28 @@ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (10,2)-(10,20)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ HashNode (location: (10,2)-(10,20)) + │ ├── flags: newline │ ├── opening_loc: (10,2)-(10,3) = "{" │ ├── elements: (length: 2) │ │ ├── @ AssocNode (location: (10,4)-(10,14)) + │ │ │ ├── flags: static_literal │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (10,4)-(10,12)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (10,4)-(10,11) = "default" │ │ │ │ ├── closing_loc: (10,11)-(10,12) = ":" │ │ │ │ └── unescaped: "default" │ │ │ ├── value: │ │ │ │ @ IntegerNode (location: (10,13)-(10,14)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── operator_loc: ∅ │ │ └── @ AssocSplatNode (location: (10,16)-(10,18)) + │ │ ├── flags: ∅ │ │ ├── value: ∅ │ │ └── operator_loc: (10,16)-(10,18) = "**" │ └── closing_loc: (10,19)-(10,20) = "}" diff --git a/test/prism/snapshots/unparser/corpus/literal/singletons.txt b/test/prism/snapshots/unparser/corpus/literal/singletons.txt index 45c06f7b07d1bb..23069207c93422 100644 --- a/test/prism/snapshots/unparser/corpus/literal/singletons.txt +++ b/test/prism/snapshots/unparser/corpus/literal/singletons.txt @@ -1,9 +1,15 @@ @ ProgramNode (location: (1,0)-(4,4)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(4,4)) + ├── flags: ∅ └── body: (length: 4) ├── @ FalseNode (location: (1,0)-(1,5)) + │ └── flags: newline, static_literal ├── @ NilNode (location: (2,0)-(2,3)) + │ └── flags: newline, static_literal ├── @ SelfNode (location: (3,0)-(3,4)) + │ └── flags: newline └── @ TrueNode (location: (4,0)-(4,4)) + └── flags: newline, static_literal diff --git a/test/prism/snapshots/unparser/corpus/literal/super.txt b/test/prism/snapshots/unparser/corpus/literal/super.txt index d5a78899197907..a6311116ca5c23 100644 --- a/test/prism/snapshots/unparser/corpus/literal/super.txt +++ b/test/prism/snapshots/unparser/corpus/literal/super.txt @@ -1,17 +1,22 @@ @ ProgramNode (location: (1,0)-(21,1)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(21,1)) + ├── flags: ∅ └── body: (length: 11) ├── @ ForwardingSuperNode (location: (1,0)-(1,5)) + │ ├── flags: newline │ └── block: ∅ ├── @ SuperNode (location: (2,0)-(2,7)) + │ ├── flags: newline │ ├── keyword_loc: (2,0)-(2,5) = "super" │ ├── lparen_loc: (2,5)-(2,6) = "(" │ ├── arguments: ∅ │ ├── rparen_loc: (2,6)-(2,7) = ")" │ └── block: ∅ ├── @ SuperNode (location: (3,0)-(3,8)) + │ ├── flags: newline │ ├── keyword_loc: (3,0)-(3,5) = "super" │ ├── lparen_loc: (3,5)-(3,6) = "(" │ ├── arguments: @@ -31,6 +36,7 @@ │ ├── rparen_loc: (3,7)-(3,8) = ")" │ └── block: ∅ ├── @ SuperNode (location: (4,0)-(4,11)) + │ ├── flags: newline │ ├── keyword_loc: (4,0)-(4,5) = "super" │ ├── lparen_loc: (4,5)-(4,6) = "(" │ ├── arguments: @@ -60,12 +66,14 @@ │ ├── rparen_loc: (4,10)-(4,11) = ")" │ └── block: ∅ ├── @ SuperNode (location: (5,0)-(5,13)) + │ ├── flags: newline │ ├── keyword_loc: (5,0)-(5,5) = "super" │ ├── lparen_loc: (5,5)-(5,6) = "(" │ ├── arguments: ∅ │ ├── rparen_loc: (5,12)-(5,13) = ")" │ └── block: │ @ BlockArgumentNode (location: (5,6)-(5,12)) + │ ├── flags: ∅ │ ├── expression: │ │ @ CallNode (location: (5,7)-(5,12)) │ │ ├── flags: variable_call, ignore_visibility @@ -79,6 +87,7 @@ │ │ └── block: ∅ │ └── operator_loc: (5,6)-(5,7) = "&" ├── @ SuperNode (location: (6,0)-(6,16)) + │ ├── flags: newline │ ├── keyword_loc: (6,0)-(6,5) = "super" │ ├── lparen_loc: (6,5)-(6,6) = "(" │ ├── arguments: @@ -98,6 +107,7 @@ │ ├── rparen_loc: (6,15)-(6,16) = ")" │ └── block: │ @ BlockArgumentNode (location: (6,9)-(6,15)) + │ ├── flags: ∅ │ ├── expression: │ │ @ CallNode (location: (6,10)-(6,15)) │ │ ├── flags: variable_call, ignore_visibility @@ -111,6 +121,7 @@ │ │ └── block: ∅ │ └── operator_loc: (6,9)-(6,10) = "&" ├── @ SuperNode (location: (7,0)-(9,2)) + │ ├── flags: newline │ ├── keyword_loc: (7,0)-(7,5) = "super" │ ├── lparen_loc: (7,5)-(7,6) = "(" │ ├── arguments: @@ -128,13 +139,15 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (7,8)-(9,1)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── parameters: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (8,2)-(8,5)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (8,2)-(8,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :foo @@ -148,15 +161,18 @@ │ ├── rparen_loc: (9,1)-(9,2) = ")" │ └── block: ∅ ├── @ ForwardingSuperNode (location: (10,0)-(12,1)) + │ ├── flags: newline │ └── block: │ @ BlockNode (location: (10,6)-(12,1)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (11,2)-(11,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (11,2)-(11,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :foo @@ -168,6 +184,7 @@ │ ├── opening_loc: (10,6)-(10,7) = "{" │ └── closing_loc: (12,0)-(12,1) = "}" ├── @ SuperNode (location: (13,0)-(15,1)) + │ ├── flags: newline │ ├── keyword_loc: (13,0)-(13,5) = "super" │ ├── lparen_loc: (13,5)-(13,6) = "(" │ ├── arguments: @@ -187,13 +204,15 @@ │ ├── rparen_loc: (13,7)-(13,8) = ")" │ └── block: │ @ BlockNode (location: (13,9)-(15,1)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (14,2)-(14,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (14,2)-(14,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :foo @@ -205,19 +224,22 @@ │ ├── opening_loc: (13,9)-(13,10) = "{" │ └── closing_loc: (15,0)-(15,1) = "}" ├── @ SuperNode (location: (16,0)-(18,1)) + │ ├── flags: newline │ ├── keyword_loc: (16,0)-(16,5) = "super" │ ├── lparen_loc: (16,5)-(16,6) = "(" │ ├── arguments: ∅ │ ├── rparen_loc: (16,6)-(16,7) = ")" │ └── block: │ @ BlockNode (location: (16,8)-(18,1)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (17,2)-(17,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (17,2)-(17,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :foo @@ -229,6 +251,7 @@ │ ├── opening_loc: (16,8)-(16,9) = "{" │ └── closing_loc: (18,0)-(18,1) = "}" └── @ SuperNode (location: (19,0)-(21,1)) + ├── flags: newline ├── keyword_loc: (19,0)-(19,5) = "super" ├── lparen_loc: (19,5)-(19,6) = "(" ├── arguments: @@ -258,13 +281,15 @@ ├── rparen_loc: (19,10)-(19,11) = ")" └── block: @ BlockNode (location: (19,12)-(21,1)) + ├── flags: ∅ ├── locals: [] ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (20,2)-(20,5)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (20,2)-(20,5)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo diff --git a/test/prism/snapshots/unparser/corpus/literal/unary.txt b/test/prism/snapshots/unparser/corpus/literal/unary.txt index 5e9563d811d5b7..be19b0457c9df0 100644 --- a/test/prism/snapshots/unparser/corpus/literal/unary.txt +++ b/test/prism/snapshots/unparser/corpus/literal/unary.txt @@ -1,13 +1,15 @@ @ ProgramNode (location: (1,0)-(8,9)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(8,9)) + ├── flags: ∅ └── body: (length: 8) ├── @ CallNode (location: (1,0)-(1,2)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ IntegerNode (location: (1,1)-(1,2)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── call_operator_loc: ∅ │ ├── name: :! @@ -17,17 +19,19 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (2,0)-(2,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ParenthesesNode (location: (2,1)-(2,5)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (2,2)-(2,4)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (2,2)-(2,4)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── receiver: │ │ │ │ @ IntegerNode (location: (2,3)-(2,4)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ └── value: 1 │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :! @@ -46,20 +50,25 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (3,0)-(3,16)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ ParenthesesNode (location: (3,1)-(3,16)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (3,2)-(3,15)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (3,2)-(3,15)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── receiver: │ │ │ │ @ ParenthesesNode (location: (3,3)-(3,15)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── body: │ │ │ │ │ @ StatementsNode (location: (3,4)-(3,14)) + │ │ │ │ │ ├── flags: ∅ │ │ │ │ │ └── body: (length: 1) │ │ │ │ │ └── @ OrNode (location: (3,4)-(3,14)) + │ │ │ │ │ ├── flags: newline │ │ │ │ │ ├── left: │ │ │ │ │ │ @ CallNode (location: (3,4)-(3,7)) │ │ │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -102,20 +111,22 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (4,0)-(4,9)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (4,1)-(4,9)) │ │ ├── flags: ∅ │ │ ├── receiver: │ │ │ @ ParenthesesNode (location: (4,1)-(4,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── body: │ │ │ │ @ StatementsNode (location: (4,2)-(4,4)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (4,2)-(4,4)) - │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── flags: newline │ │ │ │ ├── receiver: │ │ │ │ │ @ IntegerNode (location: (4,3)-(4,4)) - │ │ │ │ │ ├── flags: decimal + │ │ │ │ │ ├── flags: static_literal, decimal │ │ │ │ │ └── value: 1 │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :! @@ -141,7 +152,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (5,0)-(5,2)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (5,1)-(5,2)) │ │ ├── flags: variable_call, ignore_visibility @@ -161,7 +172,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (6,0)-(6,2)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (6,1)-(6,2)) │ │ ├── flags: variable_call, ignore_visibility @@ -181,7 +192,7 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ CallNode (location: (7,0)-(7,2)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ CallNode (location: (7,1)-(7,2)) │ │ ├── flags: variable_call, ignore_visibility @@ -201,17 +212,19 @@ │ ├── closing_loc: ∅ │ └── block: ∅ └── @ CallNode (location: (8,0)-(8,9)) - ├── flags: ∅ + ├── flags: newline ├── receiver: │ @ CallNode (location: (8,1)-(8,9)) │ ├── flags: ∅ │ ├── receiver: │ │ @ ParenthesesNode (location: (8,1)-(8,5)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (8,2)-(8,4)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (8,2)-(8,4)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── receiver: │ │ │ │ @ CallNode (location: (8,3)-(8,4)) │ │ │ │ ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/unparser/corpus/literal/undef.txt b/test/prism/snapshots/unparser/corpus/literal/undef.txt index 282cbe8f8725a7..b7ccf68e27e78b 100644 --- a/test/prism/snapshots/unparser/corpus/literal/undef.txt +++ b/test/prism/snapshots/unparser/corpus/literal/undef.txt @@ -1,27 +1,31 @@ @ ProgramNode (location: (1,0)-(2,16)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(2,16)) + ├── flags: ∅ └── body: (length: 2) ├── @ UndefNode (location: (1,0)-(1,10)) + │ ├── flags: newline │ ├── names: (length: 1) │ │ └── @ SymbolNode (location: (1,6)-(1,10)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (1,6)-(1,7) = ":" │ │ ├── value_loc: (1,7)-(1,10) = "foo" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "foo" │ └── keyword_loc: (1,0)-(1,5) = "undef" └── @ UndefNode (location: (2,0)-(2,16)) + ├── flags: newline ├── names: (length: 2) │ ├── @ SymbolNode (location: (2,6)-(2,10)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (2,6)-(2,7) = ":" │ │ ├── value_loc: (2,7)-(2,10) = "foo" │ │ ├── closing_loc: ∅ │ │ └── unescaped: "foo" │ └── @ SymbolNode (location: (2,12)-(2,16)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: static_literal, forced_us_ascii_encoding │ ├── opening_loc: (2,12)-(2,13) = ":" │ ├── value_loc: (2,13)-(2,16) = "bar" │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/unparser/corpus/literal/variables.txt b/test/prism/snapshots/unparser/corpus/literal/variables.txt index 9ae8ad12071399..3861af1a4629c1 100644 --- a/test/prism/snapshots/unparser/corpus/literal/variables.txt +++ b/test/prism/snapshots/unparser/corpus/literal/variables.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(10,17)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(10,17)) + ├── flags: ∅ └── body: (length: 10) ├── @ CallNode (location: (1,0)-(1,1)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :a @@ -14,32 +16,43 @@ │ ├── closing_loc: ∅ │ └── block: ∅ ├── @ InstanceVariableReadNode (location: (2,0)-(2,2)) + │ ├── flags: newline │ └── name: :@a ├── @ ClassVariableReadNode (location: (3,0)-(3,3)) + │ ├── flags: newline │ └── name: :@@a ├── @ GlobalVariableReadNode (location: (4,0)-(4,2)) + │ ├── flags: newline │ └── name: :$a ├── @ NumberedReferenceReadNode (location: (5,0)-(5,2)) + │ ├── flags: newline │ └── number: 1 ├── @ BackReferenceReadNode (location: (6,0)-(6,2)) + │ ├── flags: newline │ └── name: :$` ├── @ ConstantReadNode (location: (7,0)-(7,5)) + │ ├── flags: newline │ └── name: :CONST ├── @ ConstantPathNode (location: (8,0)-(8,13)) + │ ├── flags: newline │ ├── parent: │ │ @ ConstantReadNode (location: (8,0)-(8,6)) + │ │ ├── flags: ∅ │ │ └── name: :SCOPED │ ├── name: :CONST │ ├── delimiter_loc: (8,6)-(8,8) = "::" │ └── name_loc: (8,8)-(8,13) = "CONST" ├── @ ConstantPathNode (location: (9,0)-(9,10)) + │ ├── flags: newline │ ├── parent: ∅ │ ├── name: :TOPLEVEL │ ├── delimiter_loc: (9,0)-(9,2) = "::" │ └── name_loc: (9,2)-(9,10) = "TOPLEVEL" └── @ ConstantPathNode (location: (10,0)-(10,17)) + ├── flags: newline ├── parent: │ @ ConstantPathNode (location: (10,0)-(10,10)) + │ ├── flags: ∅ │ ├── parent: ∅ │ ├── name: :TOPLEVEL │ ├── delimiter_loc: (10,0)-(10,2) = "::" diff --git a/test/prism/snapshots/unparser/corpus/literal/while.txt b/test/prism/snapshots/unparser/corpus/literal/while.txt index 0f752f3392ddb3..7d9a24ec624313 100644 --- a/test/prism/snapshots/unparser/corpus/literal/while.txt +++ b/test/prism/snapshots/unparser/corpus/literal/while.txt @@ -1,19 +1,24 @@ @ ProgramNode (location: (1,0)-(73,3)) +├── flags: ∅ ├── locals: [:x] └── statements: @ StatementsNode (location: (1,0)-(73,3)) + ├── flags: ∅ └── body: (length: 17) ├── @ ModuleNode (location: (1,0)-(7,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── module_keyword_loc: (1,0)-(1,6) = "module" │ ├── constant_path: │ │ @ ConstantReadNode (location: (1,7)-(1,8)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── body: │ │ @ StatementsNode (location: (2,2)-(6,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (2,2)-(6,3)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :foo @@ -23,11 +28,14 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (2,6)-(6,3)) + │ │ ├── flags: ∅ │ │ ├── locals: [:bar, :foo] │ │ ├── parameters: │ │ │ @ BlockParametersNode (location: (2,8)-(2,13)) + │ │ │ ├── flags: ∅ │ │ │ ├── parameters: │ │ │ │ @ ParametersNode (location: (2,9)-(2,12)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── requireds: (length: 1) │ │ │ │ │ └── @ RequiredParameterNode (location: (2,9)-(2,12)) │ │ │ │ │ ├── flags: ∅ @@ -43,9 +51,10 @@ │ │ │ └── closing_loc: (2,12)-(2,13) = "|" │ │ ├── body: │ │ │ @ StatementsNode (location: (3,4)-(5,7)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ WhileNode (location: (3,4)-(5,7)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── keyword_loc: (3,4)-(3,9) = "while" │ │ │ ├── closing_loc: (5,4)-(5,7) = "end" │ │ │ ├── predicate: @@ -61,13 +70,16 @@ │ │ │ │ └── block: ∅ │ │ │ └── statements: │ │ │ @ StatementsNode (location: (4,6)-(4,15)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ LocalVariableWriteNode (location: (4,6)-(4,15)) + │ │ │ ├── flags: newline │ │ │ ├── name: :foo │ │ │ ├── depth: 0 │ │ │ ├── name_loc: (4,6)-(4,9) = "foo" │ │ │ ├── value: │ │ │ │ @ LocalVariableReadNode (location: (4,12)-(4,15)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :bar │ │ │ │ └── depth: 0 │ │ │ └── operator_loc: (4,10)-(4,11) = "=" @@ -76,15 +88,17 @@ │ ├── end_keyword_loc: (7,0)-(7,3) = "end" │ └── name: :A ├── @ DefNode (location: (9,0)-(11,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (9,4)-(9,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (10,2)-(10,28)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ WhileNode (location: (10,2)-(10,28)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── keyword_loc: (10,12)-(10,17) = "while" │ │ ├── closing_loc: ∅ │ │ ├── predicate: @@ -92,6 +106,7 @@ │ │ │ ├── flags: ∅ │ │ │ ├── receiver: │ │ │ │ @ LocalVariableReadNode (location: (10,18)-(10,21)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :foo │ │ │ │ └── depth: 0 │ │ │ ├── call_operator_loc: ∅ @@ -116,8 +131,10 @@ │ │ │ └── block: ∅ │ │ └── statements: │ │ @ StatementsNode (location: (10,2)-(10,11)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableWriteNode (location: (10,2)-(10,11)) + │ │ ├── flags: newline │ │ ├── name: :foo │ │ ├── depth: 0 │ │ ├── name_loc: (10,2)-(10,5) = "foo" @@ -141,26 +158,32 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (11,0)-(11,3) = "end" ├── @ ModuleNode (location: (13,0)-(15,3)) + │ ├── flags: newline │ ├── locals: [:foo] │ ├── module_keyword_loc: (13,0)-(13,6) = "module" │ ├── constant_path: │ │ @ ConstantReadNode (location: (13,7)-(13,8)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── body: │ │ @ StatementsNode (location: (14,2)-(14,21)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ WhileNode (location: (14,2)-(14,21)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── keyword_loc: (14,12)-(14,17) = "while" │ │ ├── closing_loc: ∅ │ │ ├── predicate: │ │ │ @ LocalVariableReadNode (location: (14,18)-(14,21)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :foo │ │ │ └── depth: 0 │ │ └── statements: │ │ @ StatementsNode (location: (14,2)-(14,11)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableWriteNode (location: (14,2)-(14,11)) + │ │ ├── flags: newline │ │ ├── name: :foo │ │ ├── depth: 0 │ │ ├── name_loc: (14,2)-(14,5) = "foo" @@ -179,26 +202,32 @@ │ ├── end_keyword_loc: (15,0)-(15,3) = "end" │ └── name: :A ├── @ ModuleNode (location: (17,0)-(19,3)) + │ ├── flags: newline │ ├── locals: [:foo] │ ├── module_keyword_loc: (17,0)-(17,6) = "module" │ ├── constant_path: │ │ @ ConstantReadNode (location: (17,7)-(17,8)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── body: │ │ @ StatementsNode (location: (18,2)-(18,21)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ UntilNode (location: (18,2)-(18,21)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── keyword_loc: (18,12)-(18,17) = "until" │ │ ├── closing_loc: ∅ │ │ ├── predicate: │ │ │ @ LocalVariableReadNode (location: (18,18)-(18,21)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :foo │ │ │ └── depth: 0 │ │ └── statements: │ │ @ StatementsNode (location: (18,2)-(18,11)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableWriteNode (location: (18,2)-(18,11)) + │ │ ├── flags: newline │ │ ├── name: :foo │ │ ├── depth: 0 │ │ ├── name_loc: (18,2)-(18,5) = "foo" @@ -217,16 +246,19 @@ │ ├── end_keyword_loc: (19,0)-(19,3) = "end" │ └── name: :A ├── @ ModuleNode (location: (21,0)-(25,3)) + │ ├── flags: newline │ ├── locals: [:foo] │ ├── module_keyword_loc: (21,0)-(21,6) = "module" │ ├── constant_path: │ │ @ ConstantReadNode (location: (21,7)-(21,8)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── body: │ │ @ StatementsNode (location: (22,2)-(24,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ WhileNode (location: (22,2)-(24,5)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── keyword_loc: (22,2)-(22,7) = "while" │ │ ├── closing_loc: (24,2)-(24,5) = "end" │ │ ├── predicate: @@ -242,8 +274,10 @@ │ │ │ └── block: ∅ │ │ └── statements: │ │ @ StatementsNode (location: (23,4)-(23,13)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableWriteNode (location: (23,4)-(23,13)) + │ │ ├── flags: newline │ │ ├── name: :foo │ │ ├── depth: 0 │ │ ├── name_loc: (23,4)-(23,7) = "foo" @@ -262,16 +296,19 @@ │ ├── end_keyword_loc: (25,0)-(25,3) = "end" │ └── name: :A ├── @ ModuleNode (location: (27,0)-(33,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── module_keyword_loc: (27,0)-(27,6) = "module" │ ├── constant_path: │ │ @ ConstantReadNode (location: (27,7)-(27,8)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── body: │ │ @ StatementsNode (location: (28,2)-(32,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (28,2)-(32,3)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :each @@ -281,11 +318,14 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (28,7)-(32,3)) + │ │ ├── flags: ∅ │ │ ├── locals: [:baz, :foo] │ │ ├── parameters: │ │ │ @ BlockParametersNode (location: (28,9)-(28,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── parameters: │ │ │ │ @ ParametersNode (location: (28,10)-(28,13)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── requireds: (length: 1) │ │ │ │ │ └── @ RequiredParameterNode (location: (28,10)-(28,13)) │ │ │ │ │ ├── flags: ∅ @@ -301,9 +341,10 @@ │ │ │ └── closing_loc: (28,13)-(28,14) = "|" │ │ ├── body: │ │ │ @ StatementsNode (location: (29,4)-(31,7)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ WhileNode (location: (29,4)-(31,7)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── keyword_loc: (29,4)-(29,9) = "while" │ │ │ ├── closing_loc: (31,4)-(31,7) = "end" │ │ │ ├── predicate: @@ -319,8 +360,10 @@ │ │ │ │ └── block: ∅ │ │ │ └── statements: │ │ │ @ StatementsNode (location: (30,6)-(30,15)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ LocalVariableWriteNode (location: (30,6)-(30,15)) + │ │ │ ├── flags: newline │ │ │ ├── name: :foo │ │ │ ├── depth: 0 │ │ │ ├── name_loc: (30,6)-(30,9) = "foo" @@ -341,16 +384,19 @@ │ ├── end_keyword_loc: (33,0)-(33,3) = "end" │ └── name: :A ├── @ ModuleNode (location: (35,0)-(41,3)) + │ ├── flags: newline │ ├── locals: [] │ ├── module_keyword_loc: (35,0)-(35,6) = "module" │ ├── constant_path: │ │ @ ConstantReadNode (location: (35,7)-(35,8)) + │ │ ├── flags: ∅ │ │ └── name: :A │ ├── body: │ │ @ StatementsNode (location: (36,2)-(40,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (36,2)-(40,3)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :each @@ -360,11 +406,14 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (36,7)-(40,3)) + │ │ ├── flags: ∅ │ │ ├── locals: [:foo] │ │ ├── parameters: │ │ │ @ BlockParametersNode (location: (36,9)-(36,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── parameters: │ │ │ │ @ ParametersNode (location: (36,10)-(36,13)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── requireds: (length: 1) │ │ │ │ │ └── @ RequiredParameterNode (location: (36,10)-(36,13)) │ │ │ │ │ ├── flags: ∅ @@ -380,19 +429,23 @@ │ │ │ └── closing_loc: (36,13)-(36,14) = "|" │ │ ├── body: │ │ │ @ StatementsNode (location: (37,4)-(39,7)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ WhileNode (location: (37,4)-(39,7)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── keyword_loc: (37,4)-(37,9) = "while" │ │ │ ├── closing_loc: (39,4)-(39,7) = "end" │ │ │ ├── predicate: │ │ │ │ @ LocalVariableReadNode (location: (37,10)-(37,13)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :foo │ │ │ │ └── depth: 0 │ │ │ └── statements: │ │ │ @ StatementsNode (location: (38,6)-(38,15)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ LocalVariableWriteNode (location: (38,6)-(38,15)) + │ │ │ ├── flags: newline │ │ │ ├── name: :foo │ │ │ ├── depth: 0 │ │ │ ├── name_loc: (38,6)-(38,9) = "foo" @@ -413,16 +466,19 @@ │ ├── end_keyword_loc: (41,0)-(41,3) = "end" │ └── name: :A ├── @ LocalVariableWriteNode (location: (42,0)-(44,14)) + │ ├── flags: newline │ ├── name: :x │ ├── depth: 0 │ ├── name_loc: (42,0)-(42,1) = "x" │ ├── value: │ │ @ ParenthesesNode (location: (42,4)-(44,14)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (42,5)-(44,13)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ WhileNode (location: (42,5)-(44,13)) - │ │ │ ├── flags: begin_modifier + │ │ │ ├── flags: newline, begin_modifier │ │ │ ├── keyword_loc: (44,4)-(44,9) = "while" │ │ │ ├── closing_loc: ∅ │ │ │ ├── predicate: @@ -438,14 +494,17 @@ │ │ │ │ └── block: ∅ │ │ │ └── statements: │ │ │ @ StatementsNode (location: (42,5)-(44,3)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ BeginNode (location: (42,5)-(44,3)) + │ │ │ ├── flags: newline │ │ │ ├── begin_keyword_loc: (42,5)-(42,10) = "begin" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (43,2)-(43,5)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ CallNode (location: (43,2)-(43,5)) - │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ │ ├── receiver: ∅ │ │ │ │ ├── call_operator_loc: ∅ │ │ │ │ ├── name: :foo @@ -462,7 +521,7 @@ │ │ └── closing_loc: (44,13)-(44,14) = ")" │ └── operator_loc: (42,2)-(42,3) = "=" ├── @ WhileNode (location: (45,0)-(47,13)) - │ ├── flags: begin_modifier + │ ├── flags: newline, begin_modifier │ ├── keyword_loc: (47,4)-(47,9) = "while" │ ├── closing_loc: ∅ │ ├── predicate: @@ -478,14 +537,17 @@ │ │ └── block: ∅ │ └── statements: │ @ StatementsNode (location: (45,0)-(47,3)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ BeginNode (location: (45,0)-(47,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (45,0)-(45,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (46,2)-(46,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (46,2)-(46,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :foo @@ -499,7 +561,7 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (47,0)-(47,3) = "end" ├── @ UntilNode (location: (48,0)-(51,13)) - │ ├── flags: begin_modifier + │ ├── flags: newline, begin_modifier │ ├── keyword_loc: (51,4)-(51,9) = "until" │ ├── closing_loc: ∅ │ ├── predicate: @@ -515,14 +577,17 @@ │ │ └── block: ∅ │ └── statements: │ @ StatementsNode (location: (48,0)-(51,3)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ BeginNode (location: (48,0)-(51,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (48,0)-(48,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (49,2)-(50,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 2) │ │ ├── @ CallNode (location: (49,2)-(49,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :foo @@ -532,7 +597,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ └── @ CallNode (location: (50,2)-(50,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -546,7 +611,7 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (51,0)-(51,3) = "end" ├── @ WhileNode (location: (52,0)-(55,13)) - │ ├── flags: begin_modifier + │ ├── flags: newline, begin_modifier │ ├── keyword_loc: (55,4)-(55,9) = "while" │ ├── closing_loc: ∅ │ ├── predicate: @@ -562,14 +627,17 @@ │ │ └── block: ∅ │ └── statements: │ @ StatementsNode (location: (52,0)-(55,3)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ BeginNode (location: (52,0)-(55,3)) + │ ├── flags: newline │ ├── begin_keyword_loc: (52,0)-(52,5) = "begin" │ ├── statements: │ │ @ StatementsNode (location: (53,2)-(54,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 2) │ │ ├── @ CallNode (location: (53,2)-(53,5)) - │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── flags: newline, variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :foo @@ -579,7 +647,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: ∅ │ │ └── @ CallNode (location: (54,2)-(54,5)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -593,35 +661,40 @@ │ ├── ensure_clause: ∅ │ └── end_keyword_loc: (55,0)-(55,3) = "end" ├── @ WhileNode (location: (56,0)-(57,3)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── keyword_loc: (56,0)-(56,5) = "while" │ ├── closing_loc: (57,0)-(57,3) = "end" │ ├── predicate: │ │ @ FalseNode (location: (56,6)-(56,11)) + │ │ └── flags: static_literal │ └── statements: ∅ ├── @ WhileNode (location: (58,0)-(60,3)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── keyword_loc: (58,0)-(58,5) = "while" │ ├── closing_loc: (60,0)-(60,3) = "end" │ ├── predicate: │ │ @ FalseNode (location: (58,6)-(58,11)) + │ │ └── flags: static_literal │ └── statements: │ @ StatementsNode (location: (59,2)-(59,3)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ IntegerNode (location: (59,2)-(59,3)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 3 ├── @ WhileNode (location: (61,0)-(64,3)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── keyword_loc: (61,0)-(61,5) = "while" │ ├── closing_loc: (64,0)-(64,3) = "end" │ ├── predicate: │ │ @ ParenthesesNode (location: (61,6)-(62,2)) + │ │ ├── flags: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (61,7)-(62,1)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (61,7)-(62,1)) - │ │ │ ├── flags: ignore_visibility + │ │ │ ├── flags: newline, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :foo @@ -631,6 +704,7 @@ │ │ │ ├── closing_loc: ∅ │ │ │ └── block: │ │ │ @ BlockNode (location: (61,11)-(62,1)) + │ │ │ ├── flags: ∅ │ │ │ ├── locals: [] │ │ │ ├── parameters: ∅ │ │ │ ├── body: ∅ @@ -640,43 +714,49 @@ │ │ └── closing_loc: (62,1)-(62,2) = ")" │ └── statements: │ @ StatementsNode (location: (63,2)-(63,7)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ SymbolNode (location: (63,2)-(63,7)) - │ ├── flags: forced_us_ascii_encoding + │ ├── flags: newline, static_literal, forced_us_ascii_encoding │ ├── opening_loc: (63,2)-(63,3) = ":" │ ├── value_loc: (63,3)-(63,7) = "body" │ ├── closing_loc: ∅ │ └── unescaped: "body" ├── @ UntilNode (location: (65,0)-(66,3)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── keyword_loc: (65,0)-(65,5) = "until" │ ├── closing_loc: (66,0)-(66,3) = "end" │ ├── predicate: │ │ @ FalseNode (location: (65,6)-(65,11)) + │ │ └── flags: static_literal │ └── statements: ∅ ├── @ UntilNode (location: (67,0)-(69,3)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── keyword_loc: (67,0)-(67,5) = "until" │ ├── closing_loc: (69,0)-(69,3) = "end" │ ├── predicate: │ │ @ FalseNode (location: (67,6)-(67,11)) + │ │ └── flags: static_literal │ └── statements: │ @ StatementsNode (location: (68,2)-(68,3)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ IntegerNode (location: (68,2)-(68,3)) - │ ├── flags: decimal + │ ├── flags: newline, static_literal, decimal │ └── value: 3 └── @ UntilNode (location: (70,0)-(73,3)) - ├── flags: ∅ + ├── flags: newline ├── keyword_loc: (70,0)-(70,5) = "until" ├── closing_loc: (73,0)-(73,3) = "end" ├── predicate: │ @ ParenthesesNode (location: (70,6)-(71,2)) + │ ├── flags: ∅ │ ├── body: │ │ @ StatementsNode (location: (70,7)-(71,1)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (70,7)-(71,1)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :foo @@ -686,6 +766,7 @@ │ │ ├── closing_loc: ∅ │ │ └── block: │ │ @ BlockNode (location: (70,11)-(71,1)) + │ │ ├── flags: ∅ │ │ ├── locals: [] │ │ ├── parameters: ∅ │ │ ├── body: ∅ @@ -695,9 +776,10 @@ │ └── closing_loc: (71,1)-(71,2) = ")" └── statements: @ StatementsNode (location: (72,2)-(72,7)) + ├── flags: ∅ └── body: (length: 1) └── @ SymbolNode (location: (72,2)-(72,7)) - ├── flags: forced_us_ascii_encoding + ├── flags: newline, static_literal, forced_us_ascii_encoding ├── opening_loc: (72,2)-(72,3) = ":" ├── value_loc: (72,3)-(72,7) = "body" ├── closing_loc: ∅ diff --git a/test/prism/snapshots/unparser/corpus/semantic/and.txt b/test/prism/snapshots/unparser/corpus/semantic/and.txt index bc9d674e44ec5e..2a6fdcf2bc1a78 100644 --- a/test/prism/snapshots/unparser/corpus/semantic/and.txt +++ b/test/prism/snapshots/unparser/corpus/semantic/and.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(8,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(8,3)) + ├── flags: ∅ └── body: (length: 4) ├── @ OrNode (location: (1,0)-(1,14)) + │ ├── flags: newline │ ├── left: │ │ @ RangeNode (location: (1,0)-(1,5)) │ │ ├── flags: exclude_end @@ -58,6 +61,7 @@ │ │ └── operator_loc: (1,10)-(1,13) = "..." │ └── operator_loc: (1,6)-(1,8) = "or" ├── @ AndNode (location: (2,0)-(2,15)) + │ ├── flags: newline │ ├── left: │ │ @ RangeNode (location: (2,0)-(2,5)) │ │ ├── flags: exclude_end @@ -112,9 +116,11 @@ │ │ └── operator_loc: (2,11)-(2,14) = "..." │ └── operator_loc: (2,6)-(2,9) = "and" ├── @ IfNode (location: (4,0)-(5,3)) + │ ├── flags: newline │ ├── if_keyword_loc: (4,0)-(4,2) = "if" │ ├── predicate: │ │ @ OrNode (location: (4,3)-(4,17)) + │ │ ├── flags: ∅ │ │ ├── left: │ │ │ @ FlipFlopNode (location: (4,3)-(4,8)) │ │ │ ├── flags: exclude_end @@ -173,9 +179,11 @@ │ ├── consequent: ∅ │ └── end_keyword_loc: (5,0)-(5,3) = "end" └── @ IfNode (location: (7,0)-(8,3)) + ├── flags: newline ├── if_keyword_loc: (7,0)-(7,2) = "if" ├── predicate: │ @ AndNode (location: (7,3)-(7,18)) + │ ├── flags: ∅ │ ├── left: │ │ @ FlipFlopNode (location: (7,3)-(7,8)) │ │ ├── flags: exclude_end diff --git a/test/prism/snapshots/unparser/corpus/semantic/block.txt b/test/prism/snapshots/unparser/corpus/semantic/block.txt index b9aa6b21844309..c3b27b194eeb8e 100644 --- a/test/prism/snapshots/unparser/corpus/semantic/block.txt +++ b/test/prism/snapshots/unparser/corpus/semantic/block.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(26,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(26,3)) + ├── flags: ∅ └── body: (length: 6) ├── @ CallNode (location: (1,0)-(2,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -14,13 +16,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (1,4)-(2,3)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: ∅ │ ├── opening_loc: (1,4)-(1,6) = "do" │ └── closing_loc: (2,0)-(2,3) = "end" ├── @ CallNode (location: (4,0)-(6,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -30,14 +33,17 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (4,4)-(6,3)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ BeginNode (location: (4,4)-(6,3)) + │ │ ├── flags: ∅ │ │ ├── begin_keyword_loc: ∅ │ │ ├── statements: ∅ │ │ ├── rescue_clause: │ │ │ @ RescueNode (location: (5,0)-(5,6)) + │ │ │ ├── flags: ∅ │ │ │ ├── keyword_loc: (5,0)-(5,6) = "rescue" │ │ │ ├── exceptions: (length: 0) │ │ │ ├── operator_loc: ∅ @@ -50,7 +56,7 @@ │ ├── opening_loc: (4,4)-(4,6) = "do" │ └── closing_loc: (6,0)-(6,3) = "end" ├── @ CallNode (location: (8,0)-(11,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -60,22 +66,28 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (8,4)-(11,3)) + │ ├── flags: ∅ │ ├── locals: [] │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (9,2)-(10,5)) + │ │ ├── flags: ∅ │ │ └── body: (length: 2) │ │ ├── @ RescueModifierNode (location: (9,2)-(9,16)) + │ │ │ ├── flags: newline │ │ │ ├── expression: │ │ │ │ @ NilNode (location: (9,2)-(9,5)) + │ │ │ │ └── flags: static_literal │ │ │ ├── keyword_loc: (9,6)-(9,12) = "rescue" │ │ │ └── rescue_expression: │ │ │ @ NilNode (location: (9,13)-(9,16)) + │ │ │ └── flags: static_literal │ │ └── @ NilNode (location: (10,2)-(10,5)) + │ │ └── flags: newline, static_literal │ ├── opening_loc: (8,4)-(8,6) = "do" │ └── closing_loc: (11,0)-(11,3) = "end" ├── @ CallNode (location: (13,0)-(14,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -85,11 +97,14 @@ │ ├── closing_loc: ∅ │ └── block: │ @ BlockNode (location: (13,4)-(14,3)) + │ ├── flags: ∅ │ ├── locals: [:a] │ ├── parameters: │ │ @ BlockParametersNode (location: (13,7)-(13,10)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (13,8)-(13,9)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (13,8)-(13,9)) │ │ │ │ ├── flags: ∅ @@ -107,7 +122,7 @@ │ ├── opening_loc: (13,4)-(13,6) = "do" │ └── closing_loc: (14,0)-(14,3) = "end" ├── @ CallNode (location: (16,0)-(20,3)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :foo @@ -126,11 +141,14 @@ │ ├── closing_loc: (16,10)-(16,11) = ")" │ └── block: │ @ BlockNode (location: (16,12)-(20,3)) + │ ├── flags: ∅ │ ├── locals: [:a] │ ├── parameters: │ │ @ BlockParametersNode (location: (16,15)-(16,18)) + │ │ ├── flags: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (16,16)-(16,17)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 1) │ │ │ │ └── @ RequiredParameterNode (location: (16,16)-(16,17)) │ │ │ │ ├── flags: ∅ @@ -146,14 +164,16 @@ │ │ └── closing_loc: (16,17)-(16,18) = "|" │ ├── body: │ │ @ StatementsNode (location: (19,2)-(19,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ LocalVariableReadNode (location: (19,2)-(19,3)) + │ │ ├── flags: newline │ │ ├── name: :a │ │ └── depth: 0 │ ├── opening_loc: (16,12)-(16,14) = "do" │ └── closing_loc: (20,0)-(20,3) = "end" └── @ CallNode (location: (22,0)-(26,3)) - ├── flags: ignore_visibility + ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :foo @@ -172,13 +192,15 @@ ├── closing_loc: (22,10)-(22,11) = ")" └── block: @ BlockNode (location: (22,12)-(26,3)) + ├── flags: ∅ ├── locals: [] ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (25,2)-(25,3)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (25,2)-(25,3)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :a diff --git a/test/prism/snapshots/unparser/corpus/semantic/def.txt b/test/prism/snapshots/unparser/corpus/semantic/def.txt index b44983c2f37df5..f05ce02bca7cf3 100644 --- a/test/prism/snapshots/unparser/corpus/semantic/def.txt +++ b/test/prism/snapshots/unparser/corpus/semantic/def.txt @@ -1,22 +1,28 @@ @ ProgramNode (location: (1,0)-(7,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(7,3)) + ├── flags: ∅ └── body: (length: 2) ├── @ DefNode (location: (1,0)-(3,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (1,4)-(1,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (2,2)-(2,9)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ ParenthesesNode (location: (2,2)-(2,9)) + │ │ ├── flags: newline │ │ ├── body: │ │ │ @ StatementsNode (location: (2,3)-(2,8)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (2,3)-(2,8)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── receiver: │ │ │ │ @ CallNode (location: (2,3)-(2,4)) │ │ │ │ ├── flags: variable_call, ignore_visibility @@ -58,14 +64,17 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (3,0)-(3,3) = "end" └── @ DefNode (location: (5,0)-(7,3)) + ├── flags: newline ├── name: :foo ├── name_loc: (5,4)-(5,7) = "foo" ├── receiver: ∅ ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (6,2)-(6,20)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ RescueModifierNode (location: (6,2)-(6,20)) + │ ├── flags: newline │ ├── expression: │ │ @ CallNode (location: (6,2)-(6,3)) │ │ ├── flags: variable_call, ignore_visibility @@ -80,6 +89,7 @@ │ ├── keyword_loc: (6,4)-(6,10) = "rescue" │ └── rescue_expression: │ @ ConstantReadNode (location: (6,11)-(6,20)) + │ ├── flags: ∅ │ └── name: :Exception ├── locals: [] ├── def_keyword_loc: (5,0)-(5,3) = "def" diff --git a/test/prism/snapshots/unparser/corpus/semantic/dstr.txt b/test/prism/snapshots/unparser/corpus/semantic/dstr.txt index 499bf59d1abc81..e1b647c744bce4 100644 --- a/test/prism/snapshots/unparser/corpus/semantic/dstr.txt +++ b/test/prism/snapshots/unparser/corpus/semantic/dstr.txt @@ -1,248 +1,262 @@ @ ProgramNode (location: (1,0)-(127,11)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(127,11)) + ├── flags: ∅ └── body: (length: 33) ├── @ StringNode (location: (1,0)-(1,5)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (1,0)-(1,5) = "<= ├── name_loc: (11,4)-(11,6) = ">=" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (11,7)-(11,12)) + │ ├── flags: ∅ │ ├── requireds: (length: 1) │ │ └── @ RequiredParameterNode (location: (11,7)-(11,12)) │ │ ├── flags: ∅ @@ -201,9 +220,10 @@ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (11,16)-(11,28)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (11,16)-(11,28)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :do_something diff --git a/test/prism/snapshots/whitequark/endless_method.txt b/test/prism/snapshots/whitequark/endless_method.txt index 17d3873b66381f..5b48a95ae58544 100644 --- a/test/prism/snapshots/whitequark/endless_method.txt +++ b/test/prism/snapshots/whitequark/endless_method.txt @@ -1,18 +1,22 @@ @ ProgramNode (location: (1,0)-(7,22)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(7,22)) + ├── flags: ∅ └── body: (length: 4) ├── @ DefNode (location: (1,0)-(1,14)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (1,4)-(1,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (1,12)-(1,14)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (1,12)-(1,14)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 42 │ ├── locals: [] │ ├── def_keyword_loc: (1,0)-(1,3) = "def" @@ -22,11 +26,13 @@ │ ├── equal_loc: (1,10)-(1,11) = "=" │ └── end_keyword_loc: ∅ ├── @ DefNode (location: (3,0)-(3,18)) + │ ├── flags: newline │ ├── name: :inc │ ├── name_loc: (3,4)-(3,7) = "inc" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (3,8)-(3,9)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (3,8)-(3,9)) │ │ │ ├── flags: ∅ @@ -39,11 +45,13 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (3,13)-(3,18)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (3,13)-(3,18)) - │ │ ├── flags: ∅ + │ │ ├── flags: newline │ │ ├── receiver: │ │ │ @ LocalVariableReadNode (location: (3,13)-(3,14)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :x │ │ │ └── depth: 0 │ │ ├── call_operator_loc: ∅ @@ -55,7 +63,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ IntegerNode (location: (3,17)-(3,18)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── closing_loc: ∅ │ │ └── block: ∅ @@ -67,6 +75,7 @@ │ ├── equal_loc: (3,11)-(3,12) = "=" │ └── end_keyword_loc: ∅ ├── @ DefNode (location: (5,0)-(5,18)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (5,8)-(5,11) = "foo" │ ├── receiver: @@ -83,9 +92,10 @@ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (5,16)-(5,18)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (5,16)-(5,18)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 42 │ ├── locals: [] │ ├── def_keyword_loc: (5,0)-(5,3) = "def" @@ -95,6 +105,7 @@ │ ├── equal_loc: (5,14)-(5,15) = "=" │ └── end_keyword_loc: ∅ └── @ DefNode (location: (7,0)-(7,22)) + ├── flags: newline ├── name: :inc ├── name_loc: (7,8)-(7,11) = "inc" ├── receiver: @@ -110,6 +121,7 @@ │ └── block: ∅ ├── parameters: │ @ ParametersNode (location: (7,12)-(7,13)) + │ ├── flags: ∅ │ ├── requireds: (length: 1) │ │ └── @ RequiredParameterNode (location: (7,12)-(7,13)) │ │ ├── flags: ∅ @@ -122,11 +134,13 @@ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (7,17)-(7,22)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (7,17)-(7,22)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── receiver: │ │ @ LocalVariableReadNode (location: (7,17)-(7,18)) + │ │ ├── flags: ∅ │ │ ├── name: :x │ │ └── depth: 0 │ ├── call_operator_loc: ∅ @@ -138,7 +152,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IntegerNode (location: (7,21)-(7,22)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── closing_loc: ∅ │ └── block: ∅ diff --git a/test/prism/snapshots/whitequark/endless_method_command_syntax.txt b/test/prism/snapshots/whitequark/endless_method_command_syntax.txt index 4ec57ccd35f77e..f2fa941fee27a6 100644 --- a/test/prism/snapshots/whitequark/endless_method_command_syntax.txt +++ b/test/prism/snapshots/whitequark/endless_method_command_syntax.txt @@ -1,18 +1,22 @@ @ ProgramNode (location: (1,0)-(15,62)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(15,62)) + ├── flags: ∅ └── body: (length: 8) ├── @ DefNode (location: (1,0)-(1,22)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (1,4)-(1,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (1,10)-(1,22)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (1,10)-(1,22)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :puts @@ -38,15 +42,17 @@ │ ├── equal_loc: (1,8)-(1,9) = "=" │ └── end_keyword_loc: ∅ ├── @ DefNode (location: (3,0)-(3,24)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (3,4)-(3,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (3,12)-(3,24)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (3,12)-(3,24)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :puts @@ -72,11 +78,13 @@ │ ├── equal_loc: (3,10)-(3,11) = "=" │ └── end_keyword_loc: ∅ ├── @ DefNode (location: (5,0)-(5,19)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (5,4)-(5,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (5,8)-(5,9)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (5,8)-(5,9)) │ │ │ ├── flags: ∅ @@ -89,9 +97,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (5,13)-(5,19)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (5,13)-(5,19)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :puts @@ -102,6 +111,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ LocalVariableReadNode (location: (5,18)-(5,19)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :x │ │ │ └── depth: 0 │ │ ├── closing_loc: ∅ @@ -114,6 +124,7 @@ │ ├── equal_loc: (5,11)-(5,12) = "=" │ └── end_keyword_loc: ∅ ├── @ DefNode (location: (7,0)-(7,26)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (7,8)-(7,11) = "foo" │ ├── receiver: @@ -130,9 +141,10 @@ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (7,14)-(7,26)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (7,14)-(7,26)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :puts @@ -158,6 +170,7 @@ │ ├── equal_loc: (7,12)-(7,13) = "=" │ └── end_keyword_loc: ∅ ├── @ DefNode (location: (9,0)-(9,28)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (9,8)-(9,11) = "foo" │ ├── receiver: @@ -174,9 +187,10 @@ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (9,16)-(9,28)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (9,16)-(9,28)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :puts @@ -202,6 +216,7 @@ │ ├── equal_loc: (9,14)-(9,15) = "=" │ └── end_keyword_loc: ∅ ├── @ DefNode (location: (11,0)-(11,23)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (11,8)-(11,11) = "foo" │ ├── receiver: @@ -217,6 +232,7 @@ │ │ └── block: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (11,12)-(11,13)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (11,12)-(11,13)) │ │ │ ├── flags: ∅ @@ -229,9 +245,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (11,17)-(11,23)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (11,17)-(11,23)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :puts @@ -242,6 +259,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ LocalVariableReadNode (location: (11,22)-(11,23)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :x │ │ │ └── depth: 0 │ │ ├── closing_loc: ∅ @@ -254,11 +272,13 @@ │ ├── equal_loc: (11,15)-(11,16) = "=" │ └── end_keyword_loc: ∅ ├── @ DefNode (location: (13,0)-(13,60)) + │ ├── flags: newline │ ├── name: :rescued │ ├── name_loc: (13,4)-(13,11) = "rescued" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (13,12)-(13,13)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (13,12)-(13,13)) │ │ │ ├── flags: ∅ @@ -271,8 +291,10 @@ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (13,17)-(13,60)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ RescueModifierNode (location: (13,17)-(13,60)) + │ │ ├── flags: newline │ │ ├── expression: │ │ │ @ CallNode (location: (13,17)-(13,37)) │ │ │ ├── flags: ignore_visibility @@ -300,17 +322,20 @@ │ │ ├── opening_loc: (13,45)-(13,46) = "\"" │ │ ├── parts: (length: 2) │ │ │ ├── @ StringNode (location: (13,46)-(13,55)) - │ │ │ │ ├── flags: frozen + │ │ │ │ ├── flags: static_literal, frozen │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── content_loc: (13,46)-(13,55) = "instance " │ │ │ │ ├── closing_loc: ∅ │ │ │ │ └── unescaped: "instance " │ │ │ └── @ EmbeddedStatementsNode (location: (13,55)-(13,59)) + │ │ │ ├── flags: ∅ │ │ │ ├── opening_loc: (13,55)-(13,57) = "\#{" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (13,57)-(13,58)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ LocalVariableReadNode (location: (13,57)-(13,58)) + │ │ │ │ ├── flags: ∅ │ │ │ │ ├── name: :x │ │ │ │ └── depth: 0 │ │ │ └── closing_loc: (13,58)-(13,59) = "}" @@ -323,12 +348,15 @@ │ ├── equal_loc: (13,15)-(13,16) = "=" │ └── end_keyword_loc: ∅ └── @ DefNode (location: (15,0)-(15,62)) + ├── flags: newline ├── name: :rescued ├── name_loc: (15,9)-(15,16) = "rescued" ├── receiver: │ @ SelfNode (location: (15,4)-(15,8)) + │ └── flags: ∅ ├── parameters: │ @ ParametersNode (location: (15,17)-(15,18)) + │ ├── flags: ∅ │ ├── requireds: (length: 1) │ │ └── @ RequiredParameterNode (location: (15,17)-(15,18)) │ │ ├── flags: ∅ @@ -341,8 +369,10 @@ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (15,22)-(15,62)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ RescueModifierNode (location: (15,22)-(15,62)) + │ ├── flags: newline │ ├── expression: │ │ @ CallNode (location: (15,22)-(15,42)) │ │ ├── flags: ignore_visibility @@ -370,17 +400,20 @@ │ ├── opening_loc: (15,50)-(15,51) = "\"" │ ├── parts: (length: 2) │ │ ├── @ StringNode (location: (15,51)-(15,57)) - │ │ │ ├── flags: frozen + │ │ │ ├── flags: static_literal, frozen │ │ │ ├── opening_loc: ∅ │ │ │ ├── content_loc: (15,51)-(15,57) = "class " │ │ │ ├── closing_loc: ∅ │ │ │ └── unescaped: "class " │ │ └── @ EmbeddedStatementsNode (location: (15,57)-(15,61)) + │ │ ├── flags: ∅ │ │ ├── opening_loc: (15,57)-(15,59) = "\#{" │ │ ├── statements: │ │ │ @ StatementsNode (location: (15,59)-(15,60)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ LocalVariableReadNode (location: (15,59)-(15,60)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :x │ │ │ └── depth: 0 │ │ └── closing_loc: (15,60)-(15,61) = "}" diff --git a/test/prism/snapshots/whitequark/endless_method_forwarded_args_legacy.txt b/test/prism/snapshots/whitequark/endless_method_forwarded_args_legacy.txt index 64a3ffa25205be..67264d3b1c40d0 100644 --- a/test/prism/snapshots/whitequark/endless_method_forwarded_args_legacy.txt +++ b/test/prism/snapshots/whitequark/endless_method_forwarded_args_legacy.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,23)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,23)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,23)) + ├── flags: newline ├── name: :foo ├── name_loc: (1,4)-(1,7) = "foo" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,8)-(1,11)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ @@ -16,12 +20,14 @@ │ ├── keywords: (length: 0) │ ├── keyword_rest: │ │ @ ForwardingParameterNode (location: (1,8)-(1,11)) + │ │ └── flags: ∅ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (1,15)-(1,23)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,15)-(1,23)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :bar @@ -32,6 +38,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ ForwardingArgumentsNode (location: (1,19)-(1,22)) + │ │ └── flags: ∅ │ ├── closing_loc: (1,22)-(1,23) = ")" │ └── block: ∅ ├── locals: [] diff --git a/test/prism/snapshots/whitequark/endless_method_with_rescue_mod.txt b/test/prism/snapshots/whitequark/endless_method_with_rescue_mod.txt index 2284b243549535..54562212860be9 100644 --- a/test/prism/snapshots/whitequark/endless_method_with_rescue_mod.txt +++ b/test/prism/snapshots/whitequark/endless_method_with_rescue_mod.txt @@ -1,25 +1,30 @@ @ ProgramNode (location: (1,0)-(3,25)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,25)) + ├── flags: ∅ └── body: (length: 2) ├── @ DefNode (location: (1,0)-(1,20)) + │ ├── flags: newline │ ├── name: :m │ ├── name_loc: (1,4)-(1,5) = "m" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (1,10)-(1,20)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ RescueModifierNode (location: (1,10)-(1,20)) + │ │ ├── flags: newline │ │ ├── expression: │ │ │ @ IntegerNode (location: (1,10)-(1,11)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── keyword_loc: (1,12)-(1,18) = "rescue" │ │ └── rescue_expression: │ │ @ IntegerNode (location: (1,19)-(1,20)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ ├── locals: [] │ ├── def_keyword_loc: (1,0)-(1,3) = "def" @@ -29,23 +34,27 @@ │ ├── equal_loc: (1,8)-(1,9) = "=" │ └── end_keyword_loc: ∅ └── @ DefNode (location: (3,0)-(3,25)) + ├── flags: newline ├── name: :m ├── name_loc: (3,9)-(3,10) = "m" ├── receiver: │ @ SelfNode (location: (3,4)-(3,8)) + │ └── flags: ∅ ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (3,15)-(3,25)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ RescueModifierNode (location: (3,15)-(3,25)) + │ ├── flags: newline │ ├── expression: │ │ @ IntegerNode (location: (3,15)-(3,16)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 1 │ ├── keyword_loc: (3,17)-(3,23) = "rescue" │ └── rescue_expression: │ @ IntegerNode (location: (3,24)-(3,25)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 2 ├── locals: [] ├── def_keyword_loc: (3,0)-(3,3) = "def" diff --git a/test/prism/snapshots/whitequark/endless_method_without_args.txt b/test/prism/snapshots/whitequark/endless_method_without_args.txt index a7a9c93ef34dd4..2e97f101c77f7c 100644 --- a/test/prism/snapshots/whitequark/endless_method_without_args.txt +++ b/test/prism/snapshots/whitequark/endless_method_without_args.txt @@ -1,18 +1,22 @@ @ ProgramNode (location: (1,0)-(7,28)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(7,28)) + ├── flags: ∅ └── body: (length: 4) ├── @ DefNode (location: (1,0)-(1,12)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (1,4)-(1,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (1,10)-(1,12)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (1,10)-(1,12)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 42 │ ├── locals: [] │ ├── def_keyword_loc: (1,0)-(1,3) = "def" @@ -22,21 +26,25 @@ │ ├── equal_loc: (1,8)-(1,9) = "=" │ └── end_keyword_loc: ∅ ├── @ DefNode (location: (3,0)-(3,23)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (3,4)-(3,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (3,10)-(3,23)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ RescueModifierNode (location: (3,10)-(3,23)) + │ │ ├── flags: newline │ │ ├── expression: │ │ │ @ IntegerNode (location: (3,10)-(3,12)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 42 │ │ ├── keyword_loc: (3,13)-(3,19) = "rescue" │ │ └── rescue_expression: │ │ @ NilNode (location: (3,20)-(3,23)) + │ │ └── flags: static_literal │ ├── locals: [] │ ├── def_keyword_loc: (3,0)-(3,3) = "def" │ ├── operator_loc: ∅ @@ -45,16 +53,19 @@ │ ├── equal_loc: (3,8)-(3,9) = "=" │ └── end_keyword_loc: ∅ ├── @ DefNode (location: (5,0)-(5,17)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (5,9)-(5,12) = "foo" │ ├── receiver: │ │ @ SelfNode (location: (5,4)-(5,8)) + │ │ └── flags: ∅ │ ├── parameters: ∅ │ ├── body: │ │ @ StatementsNode (location: (5,15)-(5,17)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ IntegerNode (location: (5,15)-(5,17)) - │ │ ├── flags: decimal + │ │ ├── flags: newline, static_literal, decimal │ │ └── value: 42 │ ├── locals: [] │ ├── def_keyword_loc: (5,0)-(5,3) = "def" @@ -64,22 +75,27 @@ │ ├── equal_loc: (5,13)-(5,14) = "=" │ └── end_keyword_loc: ∅ └── @ DefNode (location: (7,0)-(7,28)) + ├── flags: newline ├── name: :foo ├── name_loc: (7,9)-(7,12) = "foo" ├── receiver: │ @ SelfNode (location: (7,4)-(7,8)) + │ └── flags: ∅ ├── parameters: ∅ ├── body: │ @ StatementsNode (location: (7,15)-(7,28)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ RescueModifierNode (location: (7,15)-(7,28)) + │ ├── flags: newline │ ├── expression: │ │ @ IntegerNode (location: (7,15)-(7,17)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 42 │ ├── keyword_loc: (7,18)-(7,24) = "rescue" │ └── rescue_expression: │ @ NilNode (location: (7,25)-(7,28)) + │ └── flags: static_literal ├── locals: [] ├── def_keyword_loc: (7,0)-(7,3) = "def" ├── operator_loc: (7,8)-(7,9) = "." diff --git a/test/prism/snapshots/whitequark/ensure.txt b/test/prism/snapshots/whitequark/ensure.txt index a48d2370e8deba..63d4a3b05dc8b4 100644 --- a/test/prism/snapshots/whitequark/ensure.txt +++ b/test/prism/snapshots/whitequark/ensure.txt @@ -1,15 +1,19 @@ @ ProgramNode (location: (1,0)-(1,29)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,29)) + ├── flags: ∅ └── body: (length: 1) └── @ BeginNode (location: (1,0)-(1,29)) + ├── flags: newline ├── begin_keyword_loc: (1,0)-(1,5) = "begin" ├── statements: │ @ StatementsNode (location: (1,7)-(1,11)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,7)-(1,11)) - │ ├── flags: variable_call, ignore_visibility + │ ├── flags: newline, variable_call, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :meth @@ -22,12 +26,14 @@ ├── else_clause: ∅ ├── ensure_clause: │ @ EnsureNode (location: (1,13)-(1,29)) + │ ├── flags: ∅ │ ├── ensure_keyword_loc: (1,13)-(1,19) = "ensure" │ ├── statements: │ │ @ StatementsNode (location: (1,21)-(1,24)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (1,21)-(1,24)) - │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── flags: newline, variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar diff --git a/test/prism/snapshots/whitequark/ensure_empty.txt b/test/prism/snapshots/whitequark/ensure_empty.txt index 0bab5d80c363ef..c7369b7b1f59c4 100644 --- a/test/prism/snapshots/whitequark/ensure_empty.txt +++ b/test/prism/snapshots/whitequark/ensure_empty.txt @@ -1,15 +1,19 @@ @ ProgramNode (location: (1,0)-(1,16)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,16)) + ├── flags: ∅ └── body: (length: 1) └── @ BeginNode (location: (1,0)-(1,16)) + ├── flags: newline ├── begin_keyword_loc: (1,0)-(1,5) = "begin" ├── statements: ∅ ├── rescue_clause: ∅ ├── else_clause: ∅ ├── ensure_clause: │ @ EnsureNode (location: (1,6)-(1,16)) + │ ├── flags: ∅ │ ├── ensure_keyword_loc: (1,6)-(1,12) = "ensure" │ ├── statements: ∅ │ └── end_keyword_loc: (1,13)-(1,16) = "end" diff --git a/test/prism/snapshots/whitequark/false.txt b/test/prism/snapshots/whitequark/false.txt index 00562f703a98c6..4686f6aa08a145 100644 --- a/test/prism/snapshots/whitequark/false.txt +++ b/test/prism/snapshots/whitequark/false.txt @@ -1,6 +1,9 @@ @ ProgramNode (location: (1,0)-(1,5)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,5)) + ├── flags: ∅ └── body: (length: 1) └── @ FalseNode (location: (1,0)-(1,5)) + └── flags: newline, static_literal diff --git a/test/prism/snapshots/whitequark/float.txt b/test/prism/snapshots/whitequark/float.txt index 5e6a597db7deba..7839f899625893 100644 --- a/test/prism/snapshots/whitequark/float.txt +++ b/test/prism/snapshots/whitequark/float.txt @@ -1,9 +1,13 @@ @ ProgramNode (location: (1,0)-(3,4)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,4)) + ├── flags: ∅ └── body: (length: 2) ├── @ FloatNode (location: (1,0)-(1,5)) + │ ├── flags: newline, static_literal │ └── value: -1.33 └── @ FloatNode (location: (3,0)-(3,4)) + ├── flags: newline, static_literal └── value: 1.33 diff --git a/test/prism/snapshots/whitequark/for.txt b/test/prism/snapshots/whitequark/for.txt index fec8bdfd64f32b..8dca927c905146 100644 --- a/test/prism/snapshots/whitequark/for.txt +++ b/test/prism/snapshots/whitequark/for.txt @@ -1,11 +1,15 @@ @ ProgramNode (location: (1,0)-(3,22)) +├── flags: ∅ ├── locals: [:a] └── statements: @ StatementsNode (location: (1,0)-(3,22)) + ├── flags: ∅ └── body: (length: 2) ├── @ ForNode (location: (1,0)-(1,24)) + │ ├── flags: newline │ ├── index: │ │ @ LocalVariableTargetNode (location: (1,4)-(1,5)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── collection: @@ -21,9 +25,10 @@ │ │ └── block: ∅ │ ├── statements: │ │ @ StatementsNode (location: (1,16)-(1,19)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (1,16)-(1,19)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :p @@ -34,6 +39,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ LocalVariableReadNode (location: (1,18)-(1,19)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ ├── closing_loc: ∅ @@ -43,8 +49,10 @@ │ ├── do_keyword_loc: (1,13)-(1,15) = "do" │ └── end_keyword_loc: (1,21)-(1,24) = "end" └── @ ForNode (location: (3,0)-(3,22)) + ├── flags: newline ├── index: │ @ LocalVariableTargetNode (location: (3,4)-(3,5)) + │ ├── flags: ∅ │ ├── name: :a │ └── depth: 0 ├── collection: @@ -60,9 +68,10 @@ │ └── block: ∅ ├── statements: │ @ StatementsNode (location: (3,14)-(3,17)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (3,14)-(3,17)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :p @@ -73,6 +82,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ LocalVariableReadNode (location: (3,16)-(3,17)) + │ │ ├── flags: ∅ │ │ ├── name: :a │ │ └── depth: 0 │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/whitequark/for_mlhs.txt b/test/prism/snapshots/whitequark/for_mlhs.txt index 42d8fa225858d0..0f70dd8a7dad73 100644 --- a/test/prism/snapshots/whitequark/for_mlhs.txt +++ b/test/prism/snapshots/whitequark/for_mlhs.txt @@ -1,16 +1,22 @@ @ ProgramNode (location: (1,0)-(1,28)) +├── flags: ∅ ├── locals: [:a, :b] └── statements: @ StatementsNode (location: (1,0)-(1,28)) + ├── flags: ∅ └── body: (length: 1) └── @ ForNode (location: (1,0)-(1,28)) + ├── flags: newline ├── index: │ @ MultiTargetNode (location: (1,4)-(1,8)) + │ ├── flags: ∅ │ ├── lefts: (length: 2) │ │ ├── @ LocalVariableTargetNode (location: (1,4)-(1,5)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ └── @ LocalVariableTargetNode (location: (1,7)-(1,8)) + │ │ ├── flags: ∅ │ │ ├── name: :b │ │ └── depth: 0 │ ├── rest: ∅ @@ -30,9 +36,10 @@ │ └── block: ∅ ├── statements: │ @ StatementsNode (location: (1,17)-(1,23)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,17)-(1,23)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :p @@ -43,9 +50,11 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 2) │ │ ├── @ LocalVariableReadNode (location: (1,19)-(1,20)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :a │ │ │ └── depth: 0 │ │ └── @ LocalVariableReadNode (location: (1,22)-(1,23)) + │ │ ├── flags: ∅ │ │ ├── name: :b │ │ └── depth: 0 │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/whitequark/forward_arg.txt b/test/prism/snapshots/whitequark/forward_arg.txt index 17504c64e02c39..b8c22d08f2db34 100644 --- a/test/prism/snapshots/whitequark/forward_arg.txt +++ b/test/prism/snapshots/whitequark/forward_arg.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,27)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,27)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,27)) + ├── flags: newline ├── name: :foo ├── name_loc: (1,4)-(1,7) = "foo" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,8)-(1,11)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ @@ -16,12 +20,14 @@ │ ├── keywords: (length: 0) │ ├── keyword_rest: │ │ @ ForwardingParameterNode (location: (1,8)-(1,11)) + │ │ └── flags: ∅ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (1,14)-(1,22)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,14)-(1,22)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :bar @@ -32,6 +38,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ ForwardingArgumentsNode (location: (1,18)-(1,21)) + │ │ └── flags: ∅ │ ├── closing_loc: (1,21)-(1,22) = ")" │ └── block: ∅ ├── locals: [] diff --git a/test/prism/snapshots/whitequark/forward_arg_with_open_args.txt b/test/prism/snapshots/whitequark/forward_arg_with_open_args.txt index 7e58260b582202..20c8486276b9d4 100644 --- a/test/prism/snapshots/whitequark/forward_arg_with_open_args.txt +++ b/test/prism/snapshots/whitequark/forward_arg_with_open_args.txt @@ -1,18 +1,24 @@ @ ProgramNode (location: (1,0)-(27,28)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(27,28)) + ├── flags: ∅ └── body: (length: 10) ├── @ ParenthesesNode (location: (1,0)-(3,4)) + │ ├── flags: newline │ ├── body: │ │ @ StatementsNode (location: (1,1)-(3,3)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ DefNode (location: (1,1)-(3,3)) + │ │ ├── flags: newline │ │ ├── name: :foo │ │ ├── name_loc: (1,5)-(1,8) = "foo" │ │ ├── receiver: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (1,9)-(1,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 0) │ │ │ ├── optionals: (length: 0) │ │ │ ├── rest: ∅ @@ -20,12 +26,14 @@ │ │ │ ├── keywords: (length: 0) │ │ │ ├── keyword_rest: │ │ │ │ @ ForwardingParameterNode (location: (1,9)-(1,12)) + │ │ │ │ └── flags: ∅ │ │ │ └── block: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (2,2)-(2,10)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (2,2)-(2,10)) - │ │ │ ├── flags: ignore_visibility + │ │ │ ├── flags: newline, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :bar @@ -36,6 +44,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ ForwardingArgumentsNode (location: (2,6)-(2,9)) + │ │ │ │ └── flags: ∅ │ │ │ ├── closing_loc: (2,9)-(2,10) = ")" │ │ │ └── block: ∅ │ │ ├── locals: [] @@ -48,15 +57,19 @@ │ ├── opening_loc: (1,0)-(1,1) = "(" │ └── closing_loc: (3,3)-(3,4) = ")" ├── @ ParenthesesNode (location: (5,0)-(5,28)) + │ ├── flags: newline │ ├── body: │ │ @ StatementsNode (location: (5,1)-(5,27)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ DefNode (location: (5,1)-(5,27)) + │ │ ├── flags: newline │ │ ├── name: :foo │ │ ├── name_loc: (5,5)-(5,8) = "foo" │ │ ├── receiver: ∅ │ │ ├── parameters: │ │ │ @ ParametersNode (location: (5,9)-(5,12)) + │ │ │ ├── flags: ∅ │ │ │ ├── requireds: (length: 0) │ │ │ ├── optionals: (length: 0) │ │ │ ├── rest: ∅ @@ -64,12 +77,14 @@ │ │ │ ├── keywords: (length: 0) │ │ │ ├── keyword_rest: │ │ │ │ @ ForwardingParameterNode (location: (5,9)-(5,12)) + │ │ │ │ └── flags: ∅ │ │ │ └── block: ∅ │ │ ├── body: │ │ │ @ StatementsNode (location: (5,14)-(5,22)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ CallNode (location: (5,14)-(5,22)) - │ │ │ ├── flags: ignore_visibility + │ │ │ ├── flags: newline, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :bar @@ -80,6 +95,7 @@ │ │ │ │ ├── flags: ∅ │ │ │ │ └── arguments: (length: 1) │ │ │ │ └── @ ForwardingArgumentsNode (location: (5,18)-(5,21)) + │ │ │ │ └── flags: ∅ │ │ │ ├── closing_loc: (5,21)-(5,22) = ")" │ │ │ └── block: ∅ │ │ ├── locals: [] @@ -92,11 +108,13 @@ │ ├── opening_loc: (5,0)-(5,1) = "(" │ └── closing_loc: (5,27)-(5,28) = ")" ├── @ DefNode (location: (7,0)-(8,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (7,4)-(7,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (7,8)-(7,11)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -104,6 +122,7 @@ │ │ ├── keywords: (length: 0) │ │ ├── keyword_rest: │ │ │ @ ForwardingParameterNode (location: (7,8)-(7,11)) + │ │ │ └── flags: ∅ │ │ └── block: ∅ │ ├── body: ∅ │ ├── locals: [] @@ -114,11 +133,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (8,0)-(8,3) = "end" ├── @ DefNode (location: (10,0)-(10,26)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (10,4)-(10,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (10,8)-(10,11)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -126,12 +147,14 @@ │ │ ├── keywords: (length: 0) │ │ ├── keyword_rest: │ │ │ @ ForwardingParameterNode (location: (10,8)-(10,11)) + │ │ │ └── flags: ∅ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (10,13)-(10,21)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (10,13)-(10,21)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -142,6 +165,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ ForwardingArgumentsNode (location: (10,17)-(10,20)) + │ │ │ └── flags: ∅ │ │ ├── closing_loc: (10,20)-(10,21) = ")" │ │ └── block: ∅ │ ├── locals: [] @@ -152,11 +176,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (10,23)-(10,26) = "end" ├── @ DefNode (location: (12,0)-(14,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (12,4)-(12,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (12,8)-(12,14)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (12,8)-(12,9)) │ │ │ ├── flags: ∅ @@ -167,12 +193,14 @@ │ │ ├── keywords: (length: 0) │ │ ├── keyword_rest: │ │ │ @ ForwardingParameterNode (location: (12,11)-(12,14)) + │ │ │ └── flags: ∅ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (13,2)-(13,10)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (13,2)-(13,10)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -183,6 +211,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ ForwardingArgumentsNode (location: (13,6)-(13,9)) + │ │ │ └── flags: ∅ │ │ ├── closing_loc: (13,9)-(13,10) = ")" │ │ └── block: ∅ │ ├── locals: [:a] @@ -193,11 +222,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (14,0)-(14,3) = "end" ├── @ DefNode (location: (16,0)-(16,29)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (16,4)-(16,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (16,8)-(16,14)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (16,8)-(16,9)) │ │ │ ├── flags: ∅ @@ -208,12 +239,14 @@ │ │ ├── keywords: (length: 0) │ │ ├── keyword_rest: │ │ │ @ ForwardingParameterNode (location: (16,11)-(16,14)) + │ │ │ └── flags: ∅ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (16,16)-(16,24)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (16,16)-(16,24)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -224,6 +257,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ ForwardingArgumentsNode (location: (16,20)-(16,23)) + │ │ │ └── flags: ∅ │ │ ├── closing_loc: (16,23)-(16,24) = ")" │ │ └── block: ∅ │ ├── locals: [:a] @@ -234,11 +268,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (16,26)-(16,29) = "end" ├── @ DefNode (location: (18,0)-(19,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (18,4)-(18,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (18,8)-(18,21)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) │ │ │ └── @ RequiredParameterNode (location: (18,8)-(18,9)) │ │ │ ├── flags: ∅ @@ -251,13 +287,14 @@ │ │ │ ├── operator_loc: (18,13)-(18,14) = "=" │ │ │ └── value: │ │ │ @ IntegerNode (location: (18,15)-(18,16)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) │ │ ├── keywords: (length: 0) │ │ ├── keyword_rest: │ │ │ @ ForwardingParameterNode (location: (18,18)-(18,21)) + │ │ │ └── flags: ∅ │ │ └── block: ∅ │ ├── body: ∅ │ ├── locals: [:a, :b] @@ -268,11 +305,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (19,0)-(19,3) = "end" ├── @ DefNode (location: (21,0)-(23,3)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (21,4)-(21,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (21,8)-(21,18)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 1) │ │ │ └── @ OptionalParameterNode (location: (21,8)-(21,13)) @@ -282,19 +321,21 @@ │ │ │ ├── operator_loc: (21,10)-(21,11) = "=" │ │ │ └── value: │ │ │ @ IntegerNode (location: (21,12)-(21,13)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) │ │ ├── keywords: (length: 0) │ │ ├── keyword_rest: │ │ │ @ ForwardingParameterNode (location: (21,15)-(21,18)) + │ │ │ └── flags: ∅ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (22,2)-(22,10)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (22,2)-(22,10)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -305,6 +346,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ ForwardingArgumentsNode (location: (22,6)-(22,9)) + │ │ │ └── flags: ∅ │ │ ├── closing_loc: (22,9)-(22,10) = ")" │ │ └── block: ∅ │ ├── locals: [:b] @@ -315,11 +357,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (23,0)-(23,3) = "end" ├── @ DefNode (location: (25,0)-(25,33)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (25,4)-(25,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (25,8)-(25,18)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 1) │ │ │ └── @ OptionalParameterNode (location: (25,8)-(25,13)) @@ -329,19 +373,21 @@ │ │ │ ├── operator_loc: (25,10)-(25,11) = "=" │ │ │ └── value: │ │ │ @ IntegerNode (location: (25,12)-(25,13)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) │ │ ├── keywords: (length: 0) │ │ ├── keyword_rest: │ │ │ @ ForwardingParameterNode (location: (25,15)-(25,18)) + │ │ │ └── flags: ∅ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (25,20)-(25,28)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (25,20)-(25,28)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -352,6 +398,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ ForwardingArgumentsNode (location: (25,24)-(25,27)) + │ │ │ └── flags: ∅ │ │ ├── closing_loc: (25,27)-(25,28) = ")" │ │ └── block: ∅ │ ├── locals: [:b] @@ -362,11 +409,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (25,30)-(25,33) = "end" └── @ DefNode (location: (27,0)-(27,28)) + ├── flags: newline ├── name: :foo ├── name_loc: (27,4)-(27,7) = "foo" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (27,8)-(27,14)) + │ ├── flags: ∅ │ ├── requireds: (length: 1) │ │ └── @ RequiredParameterNode (location: (27,8)-(27,9)) │ │ ├── flags: ∅ @@ -377,12 +426,14 @@ │ ├── keywords: (length: 0) │ ├── keyword_rest: │ │ @ ForwardingParameterNode (location: (27,11)-(27,14)) + │ │ └── flags: ∅ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (27,16)-(27,24)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (27,16)-(27,24)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :bar @@ -393,6 +444,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ ForwardingArgumentsNode (location: (27,20)-(27,23)) + │ │ └── flags: ∅ │ ├── closing_loc: (27,23)-(27,24) = ")" │ └── block: ∅ ├── locals: [:a] diff --git a/test/prism/snapshots/whitequark/forward_args_legacy.txt b/test/prism/snapshots/whitequark/forward_args_legacy.txt index f46967ba5009c2..6fd9c536e35153 100644 --- a/test/prism/snapshots/whitequark/forward_args_legacy.txt +++ b/test/prism/snapshots/whitequark/forward_args_legacy.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(5,29)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(5,29)) + ├── flags: ∅ └── body: (length: 3) ├── @ DefNode (location: (1,0)-(1,27)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (1,4)-(1,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (1,8)-(1,11)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -16,12 +20,14 @@ │ │ ├── keywords: (length: 0) │ │ ├── keyword_rest: │ │ │ @ ForwardingParameterNode (location: (1,8)-(1,11)) + │ │ │ └── flags: ∅ │ │ └── block: ∅ │ ├── body: │ │ @ StatementsNode (location: (1,14)-(1,22)) + │ │ ├── flags: ∅ │ │ └── body: (length: 1) │ │ └── @ CallNode (location: (1,14)-(1,22)) - │ │ ├── flags: ignore_visibility + │ │ ├── flags: newline, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bar @@ -32,6 +38,7 @@ │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) │ │ │ └── @ ForwardingArgumentsNode (location: (1,18)-(1,21)) + │ │ │ └── flags: ∅ │ │ ├── closing_loc: (1,21)-(1,22) = ")" │ │ └── block: ∅ │ ├── locals: [] @@ -42,11 +49,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (1,24)-(1,27) = "end" ├── @ DefNode (location: (3,0)-(3,17)) + │ ├── flags: newline │ ├── name: :foo │ ├── name_loc: (3,4)-(3,7) = "foo" │ ├── receiver: ∅ │ ├── parameters: │ │ @ ParametersNode (location: (3,8)-(3,11)) + │ │ ├── flags: ∅ │ │ ├── requireds: (length: 0) │ │ ├── optionals: (length: 0) │ │ ├── rest: ∅ @@ -54,6 +63,7 @@ │ │ ├── keywords: (length: 0) │ │ ├── keyword_rest: │ │ │ @ ForwardingParameterNode (location: (3,8)-(3,11)) + │ │ │ └── flags: ∅ │ │ └── block: ∅ │ ├── body: ∅ │ ├── locals: [] @@ -64,11 +74,13 @@ │ ├── equal_loc: ∅ │ └── end_keyword_loc: (3,14)-(3,17) = "end" └── @ DefNode (location: (5,0)-(5,29)) + ├── flags: newline ├── name: :foo ├── name_loc: (5,4)-(5,7) = "foo" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (5,8)-(5,11)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ @@ -76,11 +88,14 @@ │ ├── keywords: (length: 0) │ ├── keyword_rest: │ │ @ ForwardingParameterNode (location: (5,8)-(5,11)) + │ │ └── flags: ∅ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (5,14)-(5,24)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ SuperNode (location: (5,14)-(5,24)) + │ ├── flags: newline │ ├── keyword_loc: (5,14)-(5,19) = "super" │ ├── lparen_loc: (5,19)-(5,20) = "(" │ ├── arguments: @@ -88,6 +103,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ ForwardingArgumentsNode (location: (5,20)-(5,23)) + │ │ └── flags: ∅ │ ├── rparen_loc: (5,23)-(5,24) = ")" │ └── block: ∅ ├── locals: [] diff --git a/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt b/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt index acaf9c052d1902..d80ace6fbaf9bd 100644 --- a/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt +++ b/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,45)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,45)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,45)) + ├── flags: newline ├── name: :foo ├── name_loc: (1,4)-(1,7) = "foo" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,8)-(1,20)) + │ ├── flags: ∅ │ ├── requireds: (length: 1) │ │ └── @ RequiredParameterNode (location: (1,8)-(1,16)) │ │ ├── flags: ∅ @@ -26,9 +30,10 @@ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (1,23)-(1,40)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,23)-(1,40)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :bar @@ -39,12 +44,14 @@ │ │ ├── flags: contains_keywords, contains_keyword_splat │ │ └── arguments: (length: 2) │ │ ├── @ LocalVariableReadNode (location: (1,27)-(1,35)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :argument │ │ │ └── depth: 0 │ │ └── @ KeywordHashNode (location: (1,37)-(1,39)) │ │ ├── flags: ∅ │ │ └── elements: (length: 1) │ │ └── @ AssocSplatNode (location: (1,37)-(1,39)) + │ │ ├── flags: ∅ │ │ ├── value: ∅ │ │ └── operator_loc: (1,37)-(1,39) = "**" │ ├── closing_loc: (1,39)-(1,40) = ")" diff --git a/test/prism/snapshots/whitequark/forwarded_argument_with_restarg.txt b/test/prism/snapshots/whitequark/forwarded_argument_with_restarg.txt index 367fad7fec8fa8..3607930b72820d 100644 --- a/test/prism/snapshots/whitequark/forwarded_argument_with_restarg.txt +++ b/test/prism/snapshots/whitequark/forwarded_argument_with_restarg.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,43)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,43)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,43)) + ├── flags: newline ├── name: :foo ├── name_loc: (1,4)-(1,7) = "foo" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,8)-(1,19)) + │ ├── flags: ∅ │ ├── requireds: (length: 1) │ │ └── @ RequiredParameterNode (location: (1,8)-(1,16)) │ │ ├── flags: ∅ @@ -26,9 +30,10 @@ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (1,22)-(1,38)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,22)-(1,38)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :bar @@ -39,9 +44,11 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 2) │ │ ├── @ LocalVariableReadNode (location: (1,26)-(1,34)) + │ │ │ ├── flags: ∅ │ │ │ ├── name: :argument │ │ │ └── depth: 0 │ │ └── @ SplatNode (location: (1,36)-(1,37)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (1,36)-(1,37) = "*" │ │ └── expression: ∅ │ ├── closing_loc: (1,37)-(1,38) = ")" diff --git a/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt b/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt index b4235fb20aebe3..50cc9ca38b857c 100644 --- a/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt +++ b/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,25)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,25)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,25)) + ├── flags: newline ├── name: :foo ├── name_loc: (1,4)-(1,7) = "foo" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,8)-(1,10)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ @@ -23,9 +27,10 @@ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (1,13)-(1,20)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,13)-(1,20)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :bar @@ -39,6 +44,7 @@ │ │ ├── flags: ∅ │ │ └── elements: (length: 1) │ │ └── @ AssocSplatNode (location: (1,17)-(1,19)) + │ │ ├── flags: ∅ │ │ ├── value: ∅ │ │ └── operator_loc: (1,17)-(1,19) = "**" │ ├── closing_loc: (1,19)-(1,20) = ")" 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 33779abcc1698c..32ace5cb3d464e 100644 --- a/test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt +++ b/test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,41)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,41)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,41)) + ├── flags: newline ├── name: :foo ├── name_loc: (1,4)-(1,7) = "foo" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,8)-(1,10)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: ∅ @@ -23,9 +27,10 @@ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (1,13)-(1,36)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,13)-(1,36)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :bar @@ -39,18 +44,21 @@ │ │ ├── flags: ∅ │ │ └── elements: (length: 2) │ │ ├── @ AssocSplatNode (location: (1,17)-(1,19)) + │ │ │ ├── flags: ∅ │ │ │ ├── value: ∅ │ │ │ └── operator_loc: (1,17)-(1,19) = "**" │ │ └── @ AssocNode (location: (1,21)-(1,35)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (1,21)-(1,30)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (1,21)-(1,29) = "from_foo" │ │ │ ├── closing_loc: (1,29)-(1,30) = ":" │ │ │ └── unescaped: "from_foo" │ │ ├── value: │ │ │ @ TrueNode (location: (1,31)-(1,35)) + │ │ │ └── flags: static_literal │ │ └── operator_loc: ∅ │ ├── closing_loc: (1,35)-(1,36) = ")" │ └── block: ∅ diff --git a/test/prism/snapshots/whitequark/forwarded_restarg.txt b/test/prism/snapshots/whitequark/forwarded_restarg.txt index 17ff8894afac53..c0bdd05a36e119 100644 --- a/test/prism/snapshots/whitequark/forwarded_restarg.txt +++ b/test/prism/snapshots/whitequark/forwarded_restarg.txt @@ -1,14 +1,18 @@ @ ProgramNode (location: (1,0)-(1,23)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,23)) + ├── flags: ∅ └── body: (length: 1) └── @ DefNode (location: (1,0)-(1,23)) + ├── flags: newline ├── name: :foo ├── name_loc: (1,4)-(1,7) = "foo" ├── receiver: ∅ ├── parameters: │ @ ParametersNode (location: (1,8)-(1,9)) + │ ├── flags: ∅ │ ├── requireds: (length: 0) │ ├── optionals: (length: 0) │ ├── rest: @@ -23,9 +27,10 @@ │ └── block: ∅ ├── body: │ @ StatementsNode (location: (1,12)-(1,18)) + │ ├── flags: ∅ │ └── body: (length: 1) │ └── @ CallNode (location: (1,12)-(1,18)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :bar @@ -36,6 +41,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ SplatNode (location: (1,16)-(1,17)) + │ │ ├── flags: ∅ │ │ ├── operator_loc: (1,16)-(1,17) = "*" │ │ └── expression: ∅ │ ├── closing_loc: (1,17)-(1,18) = ")" diff --git a/test/prism/snapshots/whitequark/gvar.txt b/test/prism/snapshots/whitequark/gvar.txt index f4401c4389b7d8..5191f28e241745 100644 --- a/test/prism/snapshots/whitequark/gvar.txt +++ b/test/prism/snapshots/whitequark/gvar.txt @@ -1,7 +1,10 @@ @ ProgramNode (location: (1,0)-(1,4)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,4)) + ├── flags: ∅ └── body: (length: 1) └── @ GlobalVariableReadNode (location: (1,0)-(1,4)) + ├── flags: newline └── name: :$foo diff --git a/test/prism/snapshots/whitequark/gvasgn.txt b/test/prism/snapshots/whitequark/gvasgn.txt index fc044054d8878a..e5bfe44d18ac3d 100644 --- a/test/prism/snapshots/whitequark/gvasgn.txt +++ b/test/prism/snapshots/whitequark/gvasgn.txt @@ -1,13 +1,16 @@ @ ProgramNode (location: (1,0)-(1,9)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,9)) + ├── flags: ∅ └── body: (length: 1) └── @ GlobalVariableWriteNode (location: (1,0)-(1,9)) + ├── flags: newline ├── name: :$var ├── name_loc: (1,0)-(1,4) = "$var" ├── value: │ @ IntegerNode (location: (1,7)-(1,9)) - │ ├── flags: decimal + │ ├── flags: static_literal, decimal │ └── value: 10 └── operator_loc: (1,5)-(1,6) = "=" diff --git a/test/prism/snapshots/whitequark/hash_empty.txt b/test/prism/snapshots/whitequark/hash_empty.txt index 38a2c15a9aded4..3f6fcdd171fd32 100644 --- a/test/prism/snapshots/whitequark/hash_empty.txt +++ b/test/prism/snapshots/whitequark/hash_empty.txt @@ -1,9 +1,12 @@ @ ProgramNode (location: (1,0)-(1,3)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,3)) + ├── flags: ∅ └── body: (length: 1) └── @ HashNode (location: (1,0)-(1,3)) + ├── flags: newline, static_literal ├── opening_loc: (1,0)-(1,1) = "{" ├── elements: (length: 0) └── closing_loc: (1,2)-(1,3) = "}" diff --git a/test/prism/snapshots/whitequark/hash_hashrocket.txt b/test/prism/snapshots/whitequark/hash_hashrocket.txt index e661a7b0480e8d..fcf3fe3055ba6f 100644 --- a/test/prism/snapshots/whitequark/hash_hashrocket.txt +++ b/test/prism/snapshots/whitequark/hash_hashrocket.txt @@ -1,39 +1,46 @@ @ ProgramNode (location: (1,0)-(3,25)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(3,25)) + ├── flags: ∅ └── body: (length: 2) ├── @ HashNode (location: (1,0)-(1,10)) + │ ├── flags: newline, static_literal │ ├── opening_loc: (1,0)-(1,1) = "{" │ ├── elements: (length: 1) │ │ └── @ AssocNode (location: (1,2)-(1,8)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ IntegerNode (location: (1,2)-(1,3)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── value: │ │ │ @ IntegerNode (location: (1,7)-(1,8)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── operator_loc: (1,4)-(1,6) = "=>" │ └── closing_loc: (1,9)-(1,10) = "}" └── @ HashNode (location: (3,0)-(3,25)) + ├── flags: newline ├── opening_loc: (3,0)-(3,1) = "{" ├── elements: (length: 2) │ ├── @ AssocNode (location: (3,2)-(3,8)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ IntegerNode (location: (3,2)-(3,3)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 1 │ │ ├── value: │ │ │ @ IntegerNode (location: (3,7)-(3,8)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── operator_loc: (3,4)-(3,6) = "=>" │ └── @ AssocNode (location: (3,10)-(3,23)) + │ ├── flags: ∅ │ ├── key: │ │ @ SymbolNode (location: (3,10)-(3,14)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (3,10)-(3,11) = ":" │ │ ├── value_loc: (3,11)-(3,14) = "foo" │ │ ├── closing_loc: ∅ diff --git a/test/prism/snapshots/whitequark/hash_kwsplat.txt b/test/prism/snapshots/whitequark/hash_kwsplat.txt index a5d12174cc40d2..acccc48b3d67ff 100644 --- a/test/prism/snapshots/whitequark/hash_kwsplat.txt +++ b/test/prism/snapshots/whitequark/hash_kwsplat.txt @@ -1,25 +1,30 @@ @ ProgramNode (location: (1,0)-(1,17)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,17)) + ├── flags: ∅ └── body: (length: 1) └── @ HashNode (location: (1,0)-(1,17)) + ├── flags: newline ├── opening_loc: (1,0)-(1,1) = "{" ├── elements: (length: 2) │ ├── @ AssocNode (location: (1,2)-(1,8)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (1,2)-(1,6)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (1,2)-(1,5) = "foo" │ │ │ ├── closing_loc: (1,5)-(1,6) = ":" │ │ │ └── unescaped: "foo" │ │ ├── value: │ │ │ @ IntegerNode (location: (1,7)-(1,8)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── operator_loc: ∅ │ └── @ AssocSplatNode (location: (1,10)-(1,15)) + │ ├── flags: ∅ │ ├── value: │ │ @ CallNode (location: (1,12)-(1,15)) │ │ ├── flags: variable_call, ignore_visibility diff --git a/test/prism/snapshots/whitequark/hash_label.txt b/test/prism/snapshots/whitequark/hash_label.txt index fdf7a21ed0ca5e..b321e2185920a1 100644 --- a/test/prism/snapshots/whitequark/hash_label.txt +++ b/test/prism/snapshots/whitequark/hash_label.txt @@ -1,22 +1,26 @@ @ ProgramNode (location: (1,0)-(1,10)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(1,10)) + ├── flags: ∅ └── body: (length: 1) └── @ HashNode (location: (1,0)-(1,10)) + ├── flags: newline, static_literal ├── opening_loc: (1,0)-(1,1) = "{" ├── elements: (length: 1) │ └── @ AssocNode (location: (1,2)-(1,8)) + │ ├── flags: static_literal │ ├── key: │ │ @ SymbolNode (location: (1,2)-(1,6)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (1,2)-(1,5) = "foo" │ │ ├── closing_loc: (1,5)-(1,6) = ":" │ │ └── unescaped: "foo" │ ├── value: │ │ @ IntegerNode (location: (1,7)-(1,8)) - │ │ ├── flags: decimal + │ │ ├── flags: static_literal, decimal │ │ └── value: 2 │ └── operator_loc: ∅ └── closing_loc: (1,9)-(1,10) = "}" diff --git a/test/prism/snapshots/whitequark/hash_label_end.txt b/test/prism/snapshots/whitequark/hash_label_end.txt index 88b70d38e2cf05..7714021dbb44ba 100644 --- a/test/prism/snapshots/whitequark/hash_label_end.txt +++ b/test/prism/snapshots/whitequark/hash_label_end.txt @@ -1,10 +1,12 @@ @ ProgramNode (location: (1,0)-(5,22)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(5,22)) + ├── flags: ∅ └── body: (length: 3) ├── @ CallNode (location: (1,0)-(1,12)) - │ ├── flags: ignore_visibility + │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :f @@ -15,6 +17,7 @@ │ │ ├── flags: ∅ │ │ └── arguments: (length: 1) │ │ └── @ IfNode (location: (1,2)-(1,11)) + │ │ ├── flags: newline │ │ ├── if_keyword_loc: ∅ │ │ ├── predicate: │ │ │ @ CallNode (location: (1,2)-(1,3)) @@ -30,69 +33,78 @@ │ │ ├── then_keyword_loc: (1,4)-(1,5) = "?" │ │ ├── statements: │ │ │ @ StatementsNode (location: (1,6)-(1,9)) + │ │ │ ├── flags: ∅ │ │ │ └── body: (length: 1) │ │ │ └── @ StringNode (location: (1,6)-(1,9)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: newline │ │ │ ├── opening_loc: (1,6)-(1,7) = "\"" │ │ │ ├── content_loc: (1,7)-(1,8) = "a" │ │ │ ├── closing_loc: (1,8)-(1,9) = "\"" │ │ │ └── unescaped: "a" │ │ ├── consequent: │ │ │ @ ElseNode (location: (1,9)-(1,11)) + │ │ │ ├── flags: ∅ │ │ │ ├── else_keyword_loc: (1,9)-(1,10) = ":" │ │ │ ├── statements: │ │ │ │ @ StatementsNode (location: (1,10)-(1,11)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── body: (length: 1) │ │ │ │ └── @ IntegerNode (location: (1,10)-(1,11)) - │ │ │ │ ├── flags: decimal + │ │ │ │ ├── flags: newline, static_literal, decimal │ │ │ │ └── value: 1 │ │ │ └── end_keyword_loc: ∅ │ │ └── end_keyword_loc: ∅ │ ├── closing_loc: (1,11)-(1,12) = ")" │ └── block: ∅ ├── @ HashNode (location: (3,0)-(3,12)) + │ ├── flags: newline, static_literal │ ├── opening_loc: (3,0)-(3,1) = "{" │ ├── elements: (length: 1) │ │ └── @ AssocNode (location: (3,2)-(3,10)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (3,2)-(3,8)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (3,2)-(3,3) = "'" │ │ │ ├── value_loc: (3,3)-(3,6) = "foo" │ │ │ ├── closing_loc: (3,6)-(3,8) = "':" │ │ │ └── unescaped: "foo" │ │ ├── value: │ │ │ @ IntegerNode (location: (3,9)-(3,10)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── operator_loc: ∅ │ └── closing_loc: (3,11)-(3,12) = "}" └── @ HashNode (location: (5,0)-(5,22)) + ├── flags: newline ├── opening_loc: (5,0)-(5,1) = "{" ├── elements: (length: 2) │ ├── @ AssocNode (location: (5,2)-(5,10)) + │ │ ├── flags: static_literal │ │ ├── key: │ │ │ @ SymbolNode (location: (5,2)-(5,8)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: (5,2)-(5,3) = "'" │ │ │ ├── value_loc: (5,3)-(5,6) = "foo" │ │ │ ├── closing_loc: (5,6)-(5,8) = "':" │ │ │ └── unescaped: "foo" │ │ ├── value: │ │ │ @ IntegerNode (location: (5,9)-(5,10)) - │ │ │ ├── flags: decimal + │ │ │ ├── flags: static_literal, decimal │ │ │ └── value: 2 │ │ └── operator_loc: ∅ │ └── @ AssocNode (location: (5,12)-(5,21)) + │ ├── flags: ∅ │ ├── key: │ │ @ SymbolNode (location: (5,12)-(5,18)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: (5,12)-(5,13) = "'" │ │ ├── value_loc: (5,13)-(5,16) = "bar" │ │ ├── closing_loc: (5,16)-(5,18) = "':" │ │ └── unescaped: "bar" │ ├── value: │ │ @ HashNode (location: (5,19)-(5,21)) + │ │ ├── flags: static_literal │ │ ├── opening_loc: (5,19)-(5,20) = "{" │ │ ├── elements: (length: 0) │ │ └── closing_loc: (5,20)-(5,21) = "}" diff --git a/test/prism/snapshots/whitequark/hash_pair_value_omission.txt b/test/prism/snapshots/whitequark/hash_pair_value_omission.txt index 455ba48407a898..d87b1ef0f9ffe9 100644 --- a/test/prism/snapshots/whitequark/hash_pair_value_omission.txt +++ b/test/prism/snapshots/whitequark/hash_pair_value_omission.txt @@ -1,39 +1,48 @@ @ ProgramNode (location: (1,0)-(5,7)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(5,7)) + ├── flags: ∅ └── body: (length: 3) ├── @ HashNode (location: (1,0)-(1,6)) + │ ├── flags: newline │ ├── opening_loc: (1,0)-(1,1) = "{" │ ├── elements: (length: 1) │ │ └── @ AssocNode (location: (1,1)-(1,5)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ SymbolNode (location: (1,1)-(1,5)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (1,1)-(1,4) = "BAR" │ │ │ ├── closing_loc: (1,4)-(1,5) = ":" │ │ │ └── unescaped: "BAR" │ │ ├── value: │ │ │ @ ImplicitNode (location: (1,1)-(1,5)) + │ │ │ ├── flags: ∅ │ │ │ └── value: │ │ │ @ ConstantReadNode (location: (1,1)-(1,5)) + │ │ │ ├── flags: ∅ │ │ │ └── name: :BAR │ │ └── operator_loc: ∅ │ └── closing_loc: (1,5)-(1,6) = "}" ├── @ HashNode (location: (3,0)-(3,8)) + │ ├── flags: newline │ ├── opening_loc: (3,0)-(3,1) = "{" │ ├── elements: (length: 2) │ │ ├── @ AssocNode (location: (3,1)-(3,3)) + │ │ │ ├── flags: ∅ │ │ │ ├── key: │ │ │ │ @ SymbolNode (location: (3,1)-(3,3)) - │ │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ │ ├── opening_loc: ∅ │ │ │ │ ├── value_loc: (3,1)-(3,2) = "a" │ │ │ │ ├── closing_loc: (3,2)-(3,3) = ":" │ │ │ │ └── unescaped: "a" │ │ │ ├── value: │ │ │ │ @ ImplicitNode (location: (3,1)-(3,3)) + │ │ │ │ ├── flags: ∅ │ │ │ │ └── value: │ │ │ │ @ CallNode (location: (3,1)-(3,3)) │ │ │ │ ├── flags: ignore_visibility @@ -47,15 +56,17 @@ │ │ │ │ └── block: ∅ │ │ │ └── operator_loc: ∅ │ │ └── @ AssocNode (location: (3,5)-(3,7)) + │ │ ├── flags: ∅ │ │ ├── key: │ │ │ @ SymbolNode (location: (3,5)-(3,7)) - │ │ │ ├── flags: forced_us_ascii_encoding + │ │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ │ ├── opening_loc: ∅ │ │ │ ├── value_loc: (3,5)-(3,6) = "b" │ │ │ ├── closing_loc: (3,6)-(3,7) = ":" │ │ │ └── unescaped: "b" │ │ ├── value: │ │ │ @ ImplicitNode (location: (3,5)-(3,7)) + │ │ │ ├── flags: ∅ │ │ │ └── value: │ │ │ @ CallNode (location: (3,5)-(3,7)) │ │ │ ├── flags: ignore_visibility @@ -70,18 +81,21 @@ │ │ └── operator_loc: ∅ │ └── closing_loc: (3,7)-(3,8) = "}" └── @ HashNode (location: (5,0)-(5,7)) + ├── flags: newline ├── opening_loc: (5,0)-(5,1) = "{" ├── elements: (length: 1) │ └── @ AssocNode (location: (5,1)-(5,6)) + │ ├── flags: ∅ │ ├── key: │ │ @ SymbolNode (location: (5,1)-(5,6)) - │ │ ├── flags: forced_us_ascii_encoding + │ │ ├── flags: static_literal, forced_us_ascii_encoding │ │ ├── opening_loc: ∅ │ │ ├── value_loc: (5,1)-(5,5) = "puts" │ │ ├── closing_loc: (5,5)-(5,6) = ":" │ │ └── unescaped: "puts" │ ├── value: │ │ @ ImplicitNode (location: (5,1)-(5,6)) + │ │ ├── flags: ∅ │ │ └── value: │ │ @ CallNode (location: (5,1)-(5,6)) │ │ ├── flags: ignore_visibility diff --git a/test/prism/snapshots/whitequark/heredoc.txt b/test/prism/snapshots/whitequark/heredoc.txt index 86543097ee1193..76b3e94a922b13 100644 --- a/test/prism/snapshots/whitequark/heredoc.txt +++ b/test/prism/snapshots/whitequark/heredoc.txt @@ -1,22 +1,24 @@ @ ProgramNode (location: (1,0)-(11,8)) +├── flags: ∅ ├── locals: [] └── statements: @ StatementsNode (location: (1,0)-(11,8)) + ├── flags: ∅ └── body: (length: 3) ├── @ StringNode (location: (1,0)-(1,8)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (1,0)-(1,8) = "<<'HERE'" │ ├── content_loc: (2,0)-(4,0) = "foo\nbar\n" │ ├── closing_loc: (4,0)-(5,0) = "HERE\n" │ └── unescaped: "foo\nbar\n" ├── @ StringNode (location: (6,0)-(6,6)) - │ ├── flags: ∅ + │ ├── flags: newline │ ├── opening_loc: (6,0)-(6,6) = "<' + end + RUBY + start_terminal(6, 30, ['ruby', "-I#{@pwd}/lib", '-rreline', '-e', code], startup_message: 'Multiline REPL.') + write "x\n" + close + assert_screen(<<~EOC) + Multiline REPL. + >x + > + EOC + end + def test_thread_safe start_terminal(6, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.') write("[Thread.new{Reline.readline'>'},Thread.new{Reline.readmultiline('>'){true}}].map(&:join).size\n") diff --git a/test/ruby/test_allocation.rb b/test/ruby/test_allocation.rb index ee489d94a79b85..fa12bf481b47d5 100644 --- a/test/ruby/test_allocation.rb +++ b/test/ruby/test_allocation.rb @@ -273,16 +273,11 @@ def self.keyword(a: nil#{block}); end check_allocations(0, 1, "keyword(**hash1, **empty_hash#{block})") check_allocations(1, 0, "keyword(*empty_array, *empty_array, **empty_hash#{block})") - check_allocations(0, 0, "keyword(*empty_array#{block})") - check_allocations(0, 1, "keyword(**hash1, **empty_hash#{block})") - check_allocations(1, 0, "keyword(*empty_array, *empty_array, **empty_hash#{block})") - - check_allocations(1, 0, "keyword(*r2k_empty_array#{block})") + check_allocations(0, 0, "keyword(*r2k_empty_array#{block})") check_allocations(1, 1, "keyword(*r2k_array#{block})") - # Currently allocates 1 array unnecessarily due to splatarray true - check_allocations(1, 1, "keyword(*empty_array, a: 2, **empty_hash#{block})") - check_allocations(1, 1, "keyword(*empty_array, **hash1, **empty_hash#{block})") + check_allocations(0, 1, "keyword(*empty_array, a: 2, **empty_hash#{block})") + check_allocations(0, 1, "keyword(*empty_array, **hash1, **empty_hash#{block})") RUBY end @@ -314,8 +309,8 @@ def self.keyword_splat(**kw#{block}); end check_allocations(1, 1, "keyword_splat(*r2k_array#{block})") # Currently allocates 1 array unnecessarily due to splatarray true - check_allocations(1, 1, "keyword_splat(*empty_array, a: 2, **empty_hash#{block})") - check_allocations(1, 1, "keyword_splat(*empty_array, **hash1, **empty_hash#{block})") + check_allocations(0, 1, "keyword_splat(*empty_array, a: 2, **empty_hash#{block})") + check_allocations(0, 1, "keyword_splat(*empty_array, **hash1, **empty_hash#{block})") RUBY end @@ -346,9 +341,8 @@ def self.keyword_and_keyword_splat(a: 1, **kw#{block}); end check_allocations(1, 1, "keyword_and_keyword_splat(*r2k_empty_array#{block})") check_allocations(1, 1, "keyword_and_keyword_splat(*r2k_array#{block})") - # Currently allocates 1 array unnecessarily due to splatarray true - check_allocations(1, 1, "keyword_and_keyword_splat(*empty_array, a: 2, **empty_hash#{block})") - check_allocations(1, 1, "keyword_and_keyword_splat(*empty_array, **hash1, **empty_hash#{block})") + check_allocations(0, 1, "keyword_and_keyword_splat(*empty_array, a: 2, **empty_hash#{block})") + check_allocations(0, 1, "keyword_and_keyword_splat(*empty_array, **hash1, **empty_hash#{block})") RUBY end @@ -385,15 +379,16 @@ def self.required_and_keyword(b, a: nil#{block}); end check_allocations(1, 1, "required_and_keyword(*array1, *empty_array, a: 2, **empty_hash#{block})") check_allocations(1, 1, "required_and_keyword(*array1, *empty_array, **hash1, **empty_hash#{block})") - check_allocations(1, 0, "required_and_keyword(*r2k_empty_array1#{block})") + check_allocations(0, 0, "required_and_keyword(*r2k_empty_array1#{block})") check_allocations(1, 1, "required_and_keyword(*r2k_array1#{block})") # Currently allocates 1 array unnecessarily due to splatarray true check_allocations(1, 1, "required_and_keyword(1, *empty_array, a: 2, **empty_hash#{block})") check_allocations(1, 1, "required_and_keyword(1, *empty_array, **hash1, **empty_hash#{block})") - check_allocations(1, 1, "required_and_keyword(*array1, **empty_hash, a: 2#{block})") - check_allocations(1, 1, "required_and_keyword(*array1, **hash1, **empty_hash#{block})") - check_allocations(1, 0, "required_and_keyword(*array1, **nil#{block})") + + check_allocations(0, 1, "required_and_keyword(*array1, **empty_hash, a: 2#{block})") + check_allocations(0, 1, "required_and_keyword(*array1, **hash1, **empty_hash#{block})") + check_allocations(0, 0, "required_and_keyword(*array1, **nil#{block})") RUBY end @@ -482,9 +477,9 @@ def self.required_and_keyword_splat(b, **kw#{block}); end # Currently allocates 1 array unnecessarily due to splatarray true check_allocations(1, 1, "required_and_keyword_splat(1, *empty_array, a: 2, **empty_hash#{block})") check_allocations(1, 1, "required_and_keyword_splat(1, *empty_array, **hash1, **empty_hash#{block})") - check_allocations(1, 1, "required_and_keyword_splat(*array1, **empty_hash, a: 2#{block})") - check_allocations(1, 1, "required_and_keyword_splat(*array1, **hash1, **empty_hash#{block})") - check_allocations(1, 1, "required_and_keyword_splat(*array1, **nil#{block})") + check_allocations(0, 1, "required_and_keyword_splat(*array1, **empty_hash, a: 2#{block})") + check_allocations(0, 1, "required_and_keyword_splat(*array1, **hash1, **empty_hash#{block})") + check_allocations(0, 1, "required_and_keyword_splat(*array1, **nil#{block})") RUBY end @@ -569,9 +564,9 @@ def self.anon_splat_and_anon_keyword_splat(*, **#{block}); end check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, *empty_array, a: 2, **empty_hash#{block})") check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, *empty_array, **hash1, **empty_hash#{block})") - check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash, a: 2#{block})") - check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **hash1, **empty_hash#{block})") - check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, **nil#{block})") + check_allocations(0, 1, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash, a: 2#{block})") + check_allocations(0, 1, "anon_splat_and_anon_keyword_splat(*array1, **hash1, **empty_hash#{block})") + check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, **nil#{block})") check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_empty_array#{block})") check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_array#{block})") @@ -615,9 +610,9 @@ def self.anon_splat_and_anon_keyword_splat(*, **#{block}); t(*, **) end; def sel check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, *empty_array, a: 2, **empty_hash#{block})") check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, *empty_array, **hash1, **empty_hash#{block})") - check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash, a: 2#{block})") - check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **hash1, **empty_hash#{block})") - check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, **nil#{block})") + check_allocations(0, 1, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash, a: 2#{block})") + check_allocations(0, 1, "anon_splat_and_anon_keyword_splat(*array1, **hash1, **empty_hash#{block})") + check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, **nil#{block})") check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_empty_array#{block})") check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_array#{block})") @@ -661,9 +656,9 @@ def self.argument_forwarding(...); end check_allocations(1, 1, "argument_forwarding(1, *empty_array, a: 2, **empty_hash#{block})") check_allocations(1, 1, "argument_forwarding(1, *empty_array, **hash1, **empty_hash#{block})") - check_allocations(1, 1, "argument_forwarding(*array1, **empty_hash, a: 2#{block})") - check_allocations(1, 1, "argument_forwarding(*array1, **hash1, **empty_hash#{block})") - check_allocations(1, 0, "argument_forwarding(*array1, **nil#{block})") + check_allocations(0, 1, "argument_forwarding(*array1, **empty_hash, a: 2#{block})") + check_allocations(0, 1, "argument_forwarding(*array1, **hash1, **empty_hash#{block})") + check_allocations(0, 0, "argument_forwarding(*array1, **nil#{block})") check_allocations(0, 0, "argument_forwarding(*r2k_empty_array#{block})") check_allocations(0, 0, "argument_forwarding(*r2k_array#{block})") @@ -707,9 +702,9 @@ def self.argument_forwarding(...); t(...) end; def self.t(...) end check_allocations(1, 1, "argument_forwarding(1, *empty_array, a: 2, **empty_hash#{block})") check_allocations(1, 1, "argument_forwarding(1, *empty_array, **hash1, **empty_hash#{block})") - check_allocations(1, 1, "argument_forwarding(*array1, **empty_hash, a: 2#{block})") - check_allocations(1, 1, "argument_forwarding(*array1, **hash1, **empty_hash#{block})") - check_allocations(1, 0, "argument_forwarding(*array1, **nil#{block})") + check_allocations(0, 1, "argument_forwarding(*array1, **empty_hash, a: 2#{block})") + check_allocations(0, 1, "argument_forwarding(*array1, **hash1, **empty_hash#{block})") + check_allocations(0, 0, "argument_forwarding(*array1, **nil#{block})") check_allocations(0, 0, "argument_forwarding(*r2k_empty_array#{block})") check_allocations(0, 0, "argument_forwarding(*r2k_array#{block})") @@ -729,7 +724,7 @@ def self.r2k(*a#{block}); end check_allocations(1, 1, "r2k(1, **empty_hash, a: 2#{block})") check_allocations(1, 0, "r2k(1, **nil#{block})") - check_allocations(1, 1, "r2k(1, **empty_hash#{block})") + check_allocations(1, 0, "r2k(1, **empty_hash#{block})") check_allocations(1, 1, "r2k(1, **hash1#{block})") check_allocations(1, 1, "r2k(1, *empty_array, **hash1#{block})") check_allocations(1, 1, "r2k(1, **hash1, **empty_hash#{block})") @@ -737,17 +732,17 @@ def self.r2k(*a#{block}); end check_allocations(1, 0, "r2k(1, *empty_array#{block})") check_allocations(1, 1, "r2k(1, **hash1, **empty_hash#{block})") - check_allocations(1, 1, "r2k(1, *empty_array, *empty_array, **empty_hash#{block})") + check_allocations(1, 0, "r2k(1, *empty_array, *empty_array, **empty_hash#{block})") check_allocations(1, 1, "r2k(*array1, a: 2#{block})") check_allocations(1, 0, "r2k(*array1, **nill#{block})") - check_allocations(1, 1, "r2k(*array1, **empty_hash#{block})") + check_allocations(1, 0, "r2k(*array1, **empty_hash#{block})") check_allocations(1, 1, "r2k(*array1, **hash1#{block})") check_allocations(1, 1, "r2k(*array1, *empty_array, **hash1#{block})") check_allocations(1, 0, "r2k(*array1, *empty_array#{block})") - check_allocations(1, 1, "r2k(*array1, *empty_array, **empty_hash#{block})") + check_allocations(1, 0, "r2k(*array1, *empty_array, **empty_hash#{block})") check_allocations(1, 1, "r2k(*array1, *empty_array, a: 2, **empty_hash#{block})") check_allocations(1, 1, "r2k(*array1, *empty_array, **hash1, **empty_hash#{block})") diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb index a8e039e82bf048..66251b9fb0029b 100644 --- a/test/ruby/test_array.rb +++ b/test/ruby/test_array.rb @@ -3493,6 +3493,17 @@ def assert_complex_equal(e, v, msg=nil) assert_typed_equal(e, v, Complex, msg) end + def test_shrink_shared_array + assert_normal_exit(<<~'RUBY', '[Feature #20589]') + array = [] + # Make sure the array is allocated + 10.times { |i| array << i } + # Simulate a C extension using OBJ_FREEZE + Object.instance_method(:freeze).bind_call(array) + array.dup + RUBY + end + def test_sum assert_int_equal(0, [].sum) assert_int_equal(3, [3].sum) diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb index d1528e65cbae4b..705107ddede0b9 100644 --- a/test/ruby/test_ast.rb +++ b/test/ruby/test_ast.rb @@ -792,6 +792,8 @@ def test_keep_script_lines_for_of end def test_keep_script_lines_for_of_with_existing_SCRIPT_LINES__that_has__FILE__as_a_key + omit if compiling_with_prism? + # This test confirms that the bug that previously occurred because of # `AbstractSyntaxTree.of`s unnecessary dependence on SCRIPT_LINES__ does not reproduce. # The bug occurred only if SCRIPT_LINES__ included __FILE__ as a key. @@ -859,6 +861,8 @@ def test_encoding_with_keep_script_lines end def test_e_option + omit if compiling_with_prism? + assert_in_out_err(["-e", "def foo; end; pp RubyVM::AbstractSyntaxTree.of(method(:foo)).type"], "", [":SCOPE"], []) end diff --git a/test/ruby/test_backtrace.rb b/test/ruby/test_backtrace.rb index fca7b620302680..1f96c0e4f30346 100644 --- a/test/ruby/test_backtrace.rb +++ b/test/ruby/test_backtrace.rb @@ -223,15 +223,15 @@ def self.foo @res = caller_locations(2, 1).inspect end @line = __LINE__ + 1 - [1].map.map { [1].map.map { foo } } - assert_equal("[\"#{__FILE__}:#{@line}:in 'Array#map'\"]", @res) + [1].map!.map { [1].map!.map { foo } } + assert_equal("[\"#{__FILE__}:#{@line}:in 'Array#map!'\"]", @res) end def test_caller_location_path_cfunc_iseq_no_pc def self.foo @res = caller_locations(2, 1)[0].path end - [1].map.map { [1].map.map { foo } } + [1].map!.map { [1].map!.map { foo } } assert_equal(__FILE__, @res) end diff --git a/test/ruby/test_enum.rb b/test/ruby/test_enum.rb index 7503e062726d8e..237bdc8a4d0155 100644 --- a/test/ruby/test_enum.rb +++ b/test/ruby/test_enum.rb @@ -1348,4 +1348,12 @@ def each klass.new.grep(/(b.)/) { svars << $1 } assert_equal(["ba", "ba"], svars) end + + def test_all_fast + data = { "key" => { "key2" => 1 } } + kk = vv = nil + data.all? { |(k, v)| kk, vv = k, v } + assert_equal(kk, "key") + assert_equal(vv, { "key2" => 1 }) + end end diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb index 491746fe83bdf0..f47f1e282a4781 100644 --- a/test/ruby/test_gc.rb +++ b/test/ruby/test_gc.rb @@ -52,6 +52,81 @@ def test_enable_disable GC.enable end + def test_gc_config_full_mark_by_default + omit "unsupoported platform/GC" unless defined?(GC.config) + + config = GC.config + assert_not_empty(config) + assert_true(config[:rgengc_allow_full_mark]) + end + + def test_gc_config_invalid_args + omit "unsupoported platform/GC" unless defined?(GC.config) + + assert_raise(ArgumentError) { GC.config(0) } + end + + def test_gc_config_setting_returns_updated_config_hash + omit "unsupoported platform/GC" unless defined?(GC.config) + + old_value = GC.config[:rgengc_allow_full_mark] + assert_true(old_value) + + new_value = GC.config(rgengc_allow_full_mark: false)[:rgengc_allow_full_mark] + assert_false(new_value) + ensure + GC.config(rgengc_allow_full_mark: true) + GC.start + end + + def test_gc_config_setting_returns_nil_for_missing_keys + omit "unsupoported platform/GC" unless defined?(GC.config) + + missing_value = GC.config(no_such_key: true)[:no_such_key] + assert_nil(missing_value) + ensure + GC.config(full_mark: true) + GC.start + end + + def test_gc_config_disable_major + omit "unsupoported platform/GC" unless defined?(GC.config) + + GC.enable + GC.start + + GC.config(rgengc_allow_full_mark: false) + major_count = GC.stat[:major_gc_count] + minor_count = GC.stat[:minor_gc_count] + + arr = [] + (GC.stat_heap[0][:heap_eden_slots] * 2).times do + arr << Object.new + Object.new + end + + assert_equal(major_count, GC.stat[:major_gc_count]) + assert_operator(minor_count, :<=, GC.stat[:minor_gc_count]) + assert_nil(GC.start) + ensure + GC.config(rgengc_allow_full_mark: true) + GC.start + end + + def test_gc_config_disable_major_gc_start_always_works + omit "unsupoported platform/GC" unless defined?(GC.config) + + GC.config(full_mark: false) + + major_count = GC.stat[:major_gc_count] + GC.start + + assert_operator(major_count, :<, GC.stat[:major_gc_count]) + ensure + GC.config(full_mark: true) + GC.start + end + def test_start_full_mark return unless use_rgengc? omit 'stress' if GC.stress @@ -159,7 +234,7 @@ def test_stat_heap GC.enable if reenable_gc end - assert_equal GC::INTERNAL_CONSTANTS[:RVALUE_SIZE] * (2**i), stat_heap[:slot_size] + assert_equal (GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] + GC::INTERNAL_CONSTANTS[:RVALUE_OVERHEAD]) * (2**i), stat_heap[:slot_size] assert_operator stat_heap[:heap_allocatable_pages], :<=, stat[:heap_allocatable_pages] assert_operator stat_heap[:heap_eden_pages], :<=, stat[:heap_eden_pages] assert_operator stat_heap[:heap_eden_slots], :>=, 0 @@ -186,13 +261,12 @@ def test_stat_heap_all omit "flaky with RJIT, which allocates objects itself" if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? stat_heap_all = {} stat_heap = {} - - 2.times do - GC.stat_heap(0, stat_heap) - GC.stat_heap(nil, stat_heap_all) - end + # Initialize to prevent GC in future calls + GC.stat_heap(0, stat_heap) + GC.stat_heap(nil, stat_heap_all) GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT].times do |i| + GC.stat_heap(nil, stat_heap_all) GC.stat_heap(i, stat_heap) # Remove keys that can vary between invocations @@ -681,7 +755,7 @@ def test_thrashing_for_young_objects def test_gc_internals assert_not_nil GC::INTERNAL_CONSTANTS[:HEAP_PAGE_OBJ_LIMIT] - assert_not_nil GC::INTERNAL_CONSTANTS[:RVALUE_SIZE] + assert_not_nil GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] end def test_sweep_in_finalizer diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb index 3349a1c4936848..0f33fde78a5a98 100644 --- a/test/ruby/test_integer.rb +++ b/test/ruby/test_integer.rb @@ -282,31 +282,31 @@ def test_chr def test_upto a = [] - 1.upto(3) {|x| a << x } + assert_equal(1, 1.upto(3) {|x| a << x }) assert_equal([1, 2, 3], a) a = [] - 1.upto(0) {|x| a << x } + assert_equal(1, 1.upto(0) {|x| a << x }) assert_equal([], a) y = 2**30 - 1 a = [] - y.upto(y+2) {|x| a << x } + assert_equal(y, y.upto(y+2) {|x| a << x }) assert_equal([y, y+1, y+2], a) end def test_downto a = [] - -1.downto(-3) {|x| a << x } + assert_equal(-1, -1.downto(-3) {|x| a << x }) assert_equal([-1, -2, -3], a) a = [] - 1.downto(2) {|x| a << x } + assert_equal(1, 1.downto(2) {|x| a << x }) assert_equal([], a) y = -(2**30) a = [] - y.downto(y-2) {|x| a << x } + assert_equal(y, y.downto(y-2) {|x| a << x }) assert_equal([y, y-1, y-2], a) end diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb index 7ef184d63964ab..e2f82dde5e7d24 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -1869,6 +1869,8 @@ def test_daemon_default end def test_daemon_noclose + pend "macOS 15 beta is not working with this test" if /darwin/ =~ RUBY_PLATFORM && /15/ =~ `sw_vers -productVersion` + data = IO.popen("-", "r+") do |f| break f.read if f Process.daemon(false, true) diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb index 828117f5167f29..04e24b2ded891f 100644 --- a/test/ruby/test_regexp.rb +++ b/test/ruby/test_regexp.rb @@ -559,16 +559,26 @@ def test_match_byteoffset_begin_end assert_raise(IndexError) { m.byteoffset(2) } assert_raise(IndexError) { m.begin(2) } assert_raise(IndexError) { m.end(2) } + assert_raise(IndexError) { m.bytebegin(2) } + assert_raise(IndexError) { m.byteend(2) } m = /(?q..)?/.match("foobarbaz") assert_equal([nil, nil], m.byteoffset("x")) assert_equal(nil, m.begin("x")) assert_equal(nil, m.end("x")) + assert_equal(nil, m.bytebegin("x")) + assert_equal(nil, m.byteend("x")) m = /\A\u3042(.)(.)?(.)\z/.match("\u3042\u3043\u3044") assert_equal([3, 6], m.byteoffset(1)) + assert_equal(3, m.bytebegin(1)) + assert_equal(6, m.byteend(1)) assert_equal([nil, nil], m.byteoffset(2)) + assert_equal(nil, m.bytebegin(2)) + assert_equal(nil, m.byteend(2)) assert_equal([6, 9], m.byteoffset(3)) + assert_equal(6, m.bytebegin(3)) + assert_equal(9, m.byteend(3)) end def test_match_to_s diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb index 0c271727014a7a..3215f25f9209d9 100644 --- a/test/ruby/test_rubyoptions.rb +++ b/test/ruby/test_rubyoptions.rb @@ -857,6 +857,8 @@ module SEGVTest end def assert_segv(args, message=nil, list: SEGVTest::ExpectedStderrList, **opt, &block) + pend "macOS 15 beta is not working with this assertion" if /darwin/ =~ RUBY_PLATFORM && /15/ =~ `sw_vers -productVersion` + # We want YJIT to be enabled in the subprocess if it's enabled for us # so that the Ruby description matches. env = Hash === args.first ? args.shift : {} @@ -900,6 +902,8 @@ def test_segv_setproctitle end def assert_crash_report(path, cmd = nil, &block) + pend "macOS 15 beta is not working with this assertion" if /darwin/ =~ RUBY_PLATFORM && /15/ =~ `sw_vers -productVersion` + Dir.mktmpdir("ruby_crash_report") do |dir| list = SEGVTest::ExpectedStderrList if cmd diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index 62e8cf123bfc95..0a3faa0e4fb20b 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -680,10 +680,8 @@ def trace_by_set_trace_func # [:c_return, 1, "xyzzy", TracePoint, :trace, TracePoint, nil, nil], [:line, 4, 'xyzzy', self.class, method, self, :outer, :nothing], - [:c_call, 4, 'xyzzy', Integer, :times, 1, nil, nil], [:line, 4, 'xyzzy', self.class, method, self, nil, :nothing], [:line, 5, 'xyzzy', self.class, method, self, :inner, :nothing], - [:c_return, 4, "xyzzy", Integer, :times, 1, nil, nil], [:line, 7, 'xyzzy', self.class, method, self, :outer, :nothing], [:c_call, 7, "xyzzy", Class, :inherited, Object, nil, nil], [:c_return, 7, "xyzzy", Class, :inherited, Object, nil, nil], @@ -1069,10 +1067,12 @@ def test_tracepoint_block # pp events # expected_events = [[:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil], - [:c_call, :map, Array, Array, nil], + [:call, :map, Array, Array, nil], [:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil], [:b_return, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 3], - [:c_return, :map, Array, Array, [3]], + [:c_call, :<<, Array, Array, nil], + [:c_return, :<<, Array, Array, [3]], + [:return, :map, Array, Array, [3]], [:call, :method_for_test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil], [:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil], [:b_return, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 4], @@ -1373,9 +1373,11 @@ def test_isolated_raise_in_trace def test_a_call events = [] + log = [] TracePoint.new(:a_call){|tp| next if !target_thread? events << tp.event + log << "| event:#{ tp.event } method_id:#{ tp.method_id } #{ tp.path }:#{ tp.lineno }" }.enable{ [1].map{ 3 @@ -1386,18 +1388,21 @@ def test_a_call } assert_equal([ :b_call, - :c_call, + :call, :b_call, + :c_call, :call, :b_call, - ], events) + ], events, "TracePoint log:\n#{ log.join("\n") }\n") end def test_a_return events = [] + log = [] TracePoint.new(:a_return){|tp| next if !target_thread? events << tp.event + log << "| event:#{ tp.event } method_id:#{ tp.method_id } #{ tp.path }:#{ tp.lineno }" }.enable{ [1].map{ 3 @@ -1409,10 +1414,11 @@ def test_a_return assert_equal([ :b_return, :c_return, + :return, :b_return, :return, :b_return - ], events) + ], events, "TracePoint log:\n#{ log.join("\n") }\n") end def test_const_missing diff --git a/test/ruby/test_transcode.rb b/test/ruby/test_transcode.rb index ceef19e7ea5951..144f4d83a1ba92 100644 --- a/test/ruby/test_transcode.rb +++ b/test/ruby/test_transcode.rb @@ -661,6 +661,25 @@ def test_IBM863 check_both_ways("\u00A0", "\xFF", 'IBM863') # non-breaking space end + def test_IBM864 + check_both_ways("\u00B0", "\x80", 'IBM864') # ° + check_both_ways("\u2518", "\x8F", 'IBM864') # ┘ + check_both_ways("\u03B2", "\x90", 'IBM864') # β + check_both_ways("\uFE73", "\x9F", 'IBM864') # ﹳ + check_both_ways("\u00A0", "\xA0", 'IBM864') # non-breaking space + check_both_ways("\uFEA5", "\xAF", 'IBM864') # ﺥ + check_both_ways("\u0660", "\xB0", 'IBM864') # ٠ + check_both_ways("\u061F", "\xBF", 'IBM864') # ؟ + check_both_ways("\u00A2", "\xC0", 'IBM864') # ¢ + check_both_ways("\uFEA9", "\xCF", 'IBM864') # ﺩ + check_both_ways("\uFEAB", "\xD0", 'IBM864') # ﺫ + check_both_ways("\uFEC9", "\xDF", 'IBM864') # ﻉ + check_both_ways("\u0640", "\xE0", 'IBM864') # ـ + check_both_ways("\uFEE1", "\xEF", 'IBM864') # ﻡ + check_both_ways("\uFE7D", "\xF0", 'IBM864') # ﹽ + check_both_ways("\u25A0", "\xFE", 'IBM864') # ■ + end + def test_IBM865 check_both_ways("\u00C7", "\x80", 'IBM865') # Ç check_both_ways("\u00C5", "\x8F", 'IBM865') # Å diff --git a/test/ruby/test_vm_dump.rb b/test/ruby/test_vm_dump.rb index f0dc04ba2a9173..0f5b3d16809fd4 100644 --- a/test/ruby/test_vm_dump.rb +++ b/test/ruby/test_vm_dump.rb @@ -5,6 +5,8 @@ class TestVMDump < Test::Unit::TestCase def assert_darwin_vm_dump_works(args) + pend "macOS 15 beta is not working with this assertion" if /darwin/ =~ RUBY_PLATFORM && /15/ =~ `sw_vers -productVersion` + assert_in_out_err(args, "", [], /^\[IMPORTANT\]/, timeout: 60) end diff --git a/test/rubygems/test_gem_commands_exec_command.rb b/test/rubygems/test_gem_commands_exec_command.rb index e52fe247a2c354..8d9c810d59ceaa 100644 --- a/test/rubygems/test_gem_commands_exec_command.rb +++ b/test/rubygems/test_gem_commands_exec_command.rb @@ -182,7 +182,7 @@ def test_gem_with_platform_dependencies fetcher.download "a", 2 do |s| s.executables = %w[a] s.files = %w[bin/a lib/a.rb] - s.add_runtime_dependency "with_platform" + s.add_dependency "with_platform" write_file File.join(*%W[gems #{s.original_name} bin a]) do |f| f << 'require "with_platform"' << "\n" @@ -222,7 +222,7 @@ def test_gem_with_platform_and_platform_dependencies fetcher.download "a", 2 do |s| s.executables = %w[a] s.files = %w[bin/a lib/a.rb] - s.add_runtime_dependency "with_platform" + s.add_dependency "with_platform" s.platform = Gem::Platform.local.to_s write_file File.join(*%W[gems #{s.original_name} bin a]) do |f| @@ -234,7 +234,7 @@ def test_gem_with_platform_and_platform_dependencies fetcher.download "a", 2 do |s| s.executables = %w[a] s.files = %w[bin/a lib/a.rb extconf.rb] - s.add_runtime_dependency "with_platform" + s.add_dependency "with_platform" write_file File.join(*%W[gems #{s.original_name} bin a]) do |f| f << 'require "with_platform"' << "\n" @@ -261,7 +261,7 @@ def test_gem_with_platform_and_platform_dependencies fetcher.download "with_platform", 2 do |s| s.files = %w[lib/with_platform.rb] - s.add_runtime_dependency "sometimes_used" + s.add_dependency "sometimes_used" end fetcher.download "sometimes_used", 2 do |s| @@ -677,7 +677,7 @@ def test_uses_newest_version_of_dependency fetcher.gem "a", 1 do |s| s.executables = %w[] s.files = %w[lib/a.rb] - s.add_runtime_dependency "b" + s.add_dependency "b" end fetcher.gem "b", 1 do |s| @@ -711,7 +711,7 @@ def test_gem_exec_gem_uninstall fetcher.download "a", 2 do |s| s.executables = %w[a] s.files = %w[bin/a lib/a.rb] - s.add_runtime_dependency "b" + s.add_dependency "b" write_file File.join(*%W[gems #{s.original_name} bin a]) do |f| f << "Gem.ui.say #{s.original_name.dump}" diff --git a/test/rubygems/test_gem_commands_lock_command.rb b/test/rubygems/test_gem_commands_lock_command.rb index 6afe4f35c5d221..02a7103f89f89b 100644 --- a/test/rubygems/test_gem_commands_lock_command.rb +++ b/test/rubygems/test_gem_commands_lock_command.rb @@ -9,11 +9,11 @@ def setup @a1 = quick_gem "a", "1" @b1 = quick_gem "b", "1" do |s| - s.add_runtime_dependency "a" + s.add_dependency "a" end @d1 = quick_gem "d", "1" do |s| - s.add_runtime_dependency "z" + s.add_dependency "z" end @cmd = Gem::Commands::LockCommand.new diff --git a/test/rubygems/test_gem_config_file.rb b/test/rubygems/test_gem_config_file.rb index a055f248be7526..b28dcf503e91a5 100644 --- a/test/rubygems/test_gem_config_file.rb +++ b/test/rubygems/test_gem_config_file.rb @@ -575,6 +575,73 @@ def test_handle_comment assert_equal("bar", actual[:foo]) end + def test_s3_source + yaml = <<~YAML + --- + :sources: + - s3://bucket1/ + - s3://bucket2/ + - s3://bucket3/path_to_gems_dir/ + - s3://bucket4/ + - https://rubygems.org/ + :s3_source: + :bucket1: + :provider: env + :bucket2: + :provider: instance_profile + :region: us-west-2 + :bucket3: + :id: AOUEAOEU123123AOEUAO + :secret: aodnuhtdao/saeuhto+19283oaehu/asoeu+123h + :region: us-east-2 + :bucket4: + :id: AOUEAOEU123123AOEUAO + :secret: aodnuhtdao/saeuhto+19283oaehu/asoeu+123h + :security_token: AQoDYXdzEJr + :region: us-west-1 + YAML + + File.open @temp_conf, "w" do |fp| + fp.puts yaml + end + util_config_file + + assert_equal(["s3://bucket1/", "s3://bucket2/", "s3://bucket3/path_to_gems_dir/", "s3://bucket4/", "https://rubygems.org/"], @cfg.sources) + expected_config = { + bucket1: { provider: "env" }, + bucket2: { provider: "instance_profile", region: "us-west-2" }, + bucket3: { id: "AOUEAOEU123123AOEUAO", secret: "aodnuhtdao/saeuhto+19283oaehu/asoeu+123h", region: "us-east-2" }, + bucket4: { id: "AOUEAOEU123123AOEUAO", secret: "aodnuhtdao/saeuhto+19283oaehu/asoeu+123h", security_token: "AQoDYXdzEJr", region: "us-west-1" }, + } + assert_equal(expected_config, @cfg[:s3_source]) + assert_equal(expected_config[:bucket1], @cfg[:s3_source][:bucket1]) + assert_equal(expected_config[:bucket2], @cfg[:s3_source][:bucket2]) + assert_equal(expected_config[:bucket3], @cfg[:s3_source][:bucket3]) + assert_equal(expected_config[:bucket4], @cfg[:s3_source][:bucket4]) + end + + def test_s3_source_with_config_without_lookahead + yaml = <<~YAML + :sources: + - s3://bucket1/ + s3_source: + bucket1: + provider: env + YAML + + File.open @temp_conf, "w" do |fp| + fp.puts yaml + end + util_config_file + + assert_equal(["s3://bucket1/"], @cfg.sources) + expected_config = { + "bucket1" => { "provider" => "env" }, + } + assert_equal(expected_config, @cfg[:s3_source]) + assert_equal(expected_config[:bucket1], @cfg[:s3_source][:bucket1]) + end + def util_config_file(args = @cfg_args) @cfg = Gem::ConfigFile.new args end 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 abd1e0ae333f3f..40532cf46c91a9 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.97" +version = "0.9.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d30bcad206b51f2f66121190ca678dce1fdf3a2eae0ac5d838d1818b19bdf5" +checksum = "8914b2e6af10bd50dd7aaac8c5146872d3924d6012929b4ff504e988f6badd24" dependencies = [ "rb-sys-build", ] [[package]] name = "rb-sys-build" -version = "0.9.97" +version = "0.9.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cbd92f281615f3c2dcb9dcb0f0576624752afbf9a7f99173b37c4b55b62dd8a" +checksum = "12af68c9757d419b82d65a12b5db538990dfe9416049fea3f0ba4b9a8ca108cd" 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 ad3e7f9b76ece1..f28abd74a00f58 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.97" +rb-sys = "0.9.98" 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 1d174f569e9cda..f3969f36216bae 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.97" +version = "0.9.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d30bcad206b51f2f66121190ca678dce1fdf3a2eae0ac5d838d1818b19bdf5" +checksum = "8914b2e6af10bd50dd7aaac8c5146872d3924d6012929b4ff504e988f6badd24" dependencies = [ "rb-sys-build", ] [[package]] name = "rb-sys-build" -version = "0.9.97" +version = "0.9.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cbd92f281615f3c2dcb9dcb0f0576624752afbf9a7f99173b37c4b55b62dd8a" +checksum = "12af68c9757d419b82d65a12b5db538990dfe9416049fea3f0ba4b9a8ca108cd" 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 60cf49ce03164a..ac8fd45bc6c4f2 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.97" +rb-sys = "0.9.98" diff --git a/test/rubygems/test_gem_platform.rb b/test/rubygems/test_gem_platform.rb index 7c21c734ff2787..070c8007bc0056 100644 --- a/test/rubygems/test_gem_platform.rb +++ b/test/rubygems/test_gem_platform.rb @@ -368,18 +368,27 @@ def test_equals3_cpu_arm arm = Gem::Platform.new "arm-linux" armv5 = Gem::Platform.new "armv5-linux" armv7 = Gem::Platform.new "armv7-linux" + arm64 = Gem::Platform.new "arm64-linux" util_set_arch "armv5-linux" assert((arm === Gem::Platform.local), "arm === armv5") assert((armv5 === Gem::Platform.local), "armv5 === armv5") refute((armv7 === Gem::Platform.local), "armv7 === armv5") + refute((arm64 === Gem::Platform.local), "arm64 === armv5") refute((Gem::Platform.local === arm), "armv5 === arm") util_set_arch "armv7-linux" assert((arm === Gem::Platform.local), "arm === armv7") refute((armv5 === Gem::Platform.local), "armv5 === armv7") assert((armv7 === Gem::Platform.local), "armv7 === armv7") + refute((arm64 === Gem::Platform.local), "arm64 === armv7") refute((Gem::Platform.local === arm), "armv7 === arm") + + util_set_arch "arm64-linux" + refute((arm === Gem::Platform.local), "arm === arm64") + refute((armv5 === Gem::Platform.local), "armv5 === arm64") + refute((armv7 === Gem::Platform.local), "armv7 === arm64") + assert((arm64 === Gem::Platform.local), "arm64 === arm64") end def test_equals3_universal_mingw diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb index e71b2f5ff6c731..ca858cfda5d33d 100644 --- a/test/rubygems/test_gem_remote_fetcher.rb +++ b/test/rubygems/test_gem_remote_fetcher.rb @@ -2,105 +2,14 @@ require_relative "helper" -require "webrick" -require "webrick/https" if Gem::HAVE_OPENSSL - -unless Gem::HAVE_OPENSSL - warn "Skipping Gem::RemoteFetcher tests. openssl not found." -end - require "rubygems/remote_fetcher" require "rubygems/package" -# = Testing Proxy Settings -# -# These tests check the proper proxy server settings by running two -# web servers. The web server at http://localhost:#{SERVER_PORT} -# represents the normal gem server and returns a gemspec with a rake -# version of 0.4.11. The web server at http://localhost:#{PROXY_PORT} -# represents the proxy server and returns a different dataset where -# rake has version 0.4.2. This allows us to detect which server is -# returning the data. -# -# Note that the proxy server is not a *real* proxy server. But our -# software doesn't really care, as long as we hit the proxy URL when a -# proxy is configured. - class TestGemRemoteFetcher < Gem::TestCase include Gem::DefaultUserInteraction - SERVER_DATA = <<-EOY ---- !ruby/object:Gem::Cache -gems: - rake-0.4.11: !ruby/object:Gem::Specification - rubygems_version: "0.7" - specification_version: 1 - name: rake - version: !ruby/object:Gem::Version - version: 0.4.11 - date: 2004-11-12 - summary: Ruby based make-like utility. - require_paths: - - lib - author: Jim Weirich - email: jim@weirichhouse.org - homepage: http://rake.rubyforge.org - description: Rake is a Make-like program implemented in Ruby. Tasks and dependencies are specified in standard Ruby syntax. - autorequire: - bindir: bin - has_rdoc: true - required_ruby_version: !ruby/object:Gem::Version::Requirement - requirements: - - - - ">" - - !ruby/object:Gem::Version - version: 0.0.0 - version: - platform: ruby - files: - - README - test_files: [] - library_stubs: - rdoc_options: - extra_rdoc_files: - executables: - - rake - extensions: [] - requirements: [] - dependencies: [] - EOY - - PROXY_DATA = SERVER_DATA.gsub(/0.4.11/, "0.4.2") - - # Generated via: - # x = OpenSSL::PKey::DH.new(2048) # wait a while... - # x.to_s => pem - TEST_KEY_DH2048 = OpenSSL::PKey::DH.new <<-_END_OF_PEM_ ------BEGIN DH PARAMETERS----- -MIIBCAKCAQEA3Ze2EHSfYkZLUn557torAmjBgPsqzbodaRaGZtgK1gEU+9nNJaFV -G1JKhmGUiEDyIW7idsBpe4sX/Wqjnp48Lr8IeI/SlEzLdoGpf05iRYXC8Cm9o8aM -cfmVgoSEAo9YLBpzoji2jHkO7Q5IPt4zxbTdlmmGFLc/GO9q7LGHhC+rcMcNTGsM -49AnILNn49pq4Y72jSwdmvq4psHZwwFBbPwLdw6bLUDDCN90jfqvYt18muwUxDiN -NP0fuvVAIB158VnQ0liHSwcl6+9vE1mL0Jo/qEXQxl0+UdKDjaGfTsn6HIrwTnmJ -PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg== ------END DH PARAMETERS----- - _END_OF_PEM_ - def setup - @proxies = %w[https_proxy http_proxy HTTP_PROXY http_proxy_user HTTP_PROXY_USER http_proxy_pass HTTP_PROXY_PASS no_proxy NO_PROXY] - @old_proxies = @proxies.map {|k| ENV[k] } - @proxies.each {|k| ENV[k] = nil } - super - start_servers - self.enable_yaml = true - self.enable_zip = false - - base_server_uri = "http://localhost:#{normal_server_port}" - @proxy_uri = "http://localhost:#{proxy_server_port}" - - @server_uri = base_server_uri + "/yaml" - @server_z_uri = base_server_uri + "/yaml.Z" @cache_dir = File.join @gemhome, "cache" @@ -116,14 +25,6 @@ def setup @fetcher = Gem::RemoteFetcher.fetcher end - def teardown - @fetcher.close_all - stop_servers - super - Gem.configuration[:http_proxy] = nil - @proxies.each_with_index {|k, i| ENV[k] = @old_proxies[i] } - end - def test_self_fetcher fetcher = Gem::RemoteFetcher.fetcher refute_nil fetcher @@ -140,6 +41,8 @@ def test_self_fetcher_with_proxy refute_nil fetcher assert_kind_of Gem::RemoteFetcher, fetcher assert_equal proxy_uri, fetcher.instance_variable_get(:@proxy).to_s + ensure + Gem.configuration[:http_proxy] = nil end def test_fetch_path_bad_uri @@ -153,14 +56,6 @@ def test_fetch_path_bad_uri assert_equal "uri scheme is invalid: nil", e.message end - def test_no_proxy - use_ui @stub_ui do - assert_data_from_server @fetcher.fetch_path(@server_uri) - response = @fetcher.fetch_path(@server_uri, nil, true) - assert_equal SERVER_DATA.size, response["content-length"].to_i - end - end - def test_cache_update_path uri = Gem::URI "http://example/file" path = File.join @tempdir, "file" @@ -616,41 +511,6 @@ def fetcher.fetch_http(uri, mtime, head = nil) assert_nil fetcher.fetch_path(Gem::URI.parse(@gem_repo), Time.at(0)) end - def test_implicit_no_proxy - use_ui @stub_ui do - ENV["http_proxy"] = "http://fakeurl:12345" - fetcher = Gem::RemoteFetcher.new :no_proxy - @fetcher = fetcher - assert_data_from_server fetcher.fetch_path(@server_uri) - end - end - - def test_implicit_proxy - use_ui @stub_ui do - ENV["http_proxy"] = @proxy_uri - fetcher = Gem::RemoteFetcher.new nil - @fetcher = fetcher - assert_data_from_proxy fetcher.fetch_path(@server_uri) - end - end - - def test_implicit_upper_case_proxy - use_ui @stub_ui do - ENV["HTTP_PROXY"] = @proxy_uri - fetcher = Gem::RemoteFetcher.new nil - @fetcher = fetcher - assert_data_from_proxy fetcher.fetch_path(@server_uri) - end - end - - def test_implicit_proxy_no_env - use_ui @stub_ui do - fetcher = Gem::RemoteFetcher.new nil - @fetcher = fetcher - assert_data_from_server fetcher.fetch_path(@server_uri) - end - end - def test_fetch_http fetcher = Gem::RemoteFetcher.new nil @fetcher = fetcher @@ -712,247 +572,6 @@ def fetcher.request(uri, request_class, last_modified = nil) assert_equal "redirecting but no redirect location was given (#{url})", e.message end - def test_fetch_http_with_additional_headers - ENV["http_proxy"] = @proxy_uri - ENV["no_proxy"] = Gem::URI.parse(@server_uri).host - fetcher = Gem::RemoteFetcher.new nil, nil, { "X-Captain" => "murphy" } - @fetcher = fetcher - assert_equal "murphy", fetcher.fetch_path(@server_uri) - end - - def assert_fetch_s3(url, signature, token=nil, region="us-east-1", instance_profile_json=nil) - fetcher = Gem::RemoteFetcher.new nil - @fetcher = fetcher - $fetched_uri = nil - $instance_profile = instance_profile_json - - def fetcher.request(uri, request_class, last_modified = nil) - $fetched_uri = uri - res = Gem::Net::HTTPOK.new nil, 200, nil - def res.body - "success" - end - res - end - - def fetcher.s3_uri_signer(uri) - require "json" - s3_uri_signer = Gem::S3URISigner.new(uri) - def s3_uri_signer.ec2_metadata_credentials_json - JSON.parse($instance_profile) - end - # Running sign operation to make sure uri.query is not mutated - s3_uri_signer.sign - raise "URI query is not empty: #{uri.query}" unless uri.query.nil? - s3_uri_signer - end - - data = fetcher.fetch_s3 Gem::URI.parse(url) - - assert_equal "https://my-bucket.s3.#{region}.amazonaws.com/gems/specs.4.8.gz?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=testuser%2F20190624%2F#{region}%2Fs3%2Faws4_request&X-Amz-Date=20190624T050641Z&X-Amz-Expires=86400#{token ? "&X-Amz-Security-Token=" + token : ""}&X-Amz-SignedHeaders=host&X-Amz-Signature=#{signature}", $fetched_uri.to_s - assert_equal "success", data - ensure - $fetched_uri = nil - end - - def test_fetch_s3_config_creds - Gem.configuration[:s3_source] = { - "my-bucket" => { id: "testuser", secret: "testpass" }, - } - url = "s3://my-bucket/gems/specs.4.8.gz" - Time.stub :now, Time.at(1_561_353_581) do - assert_fetch_s3 url, "20f974027db2f3cd6193565327a7c73457a138efb1a63ea248d185ce6827d41b" - end - ensure - Gem.configuration[:s3_source] = nil - end - - def test_fetch_s3_config_creds_with_region - Gem.configuration[:s3_source] = { - "my-bucket" => { id: "testuser", secret: "testpass", region: "us-west-2" }, - } - url = "s3://my-bucket/gems/specs.4.8.gz" - Time.stub :now, Time.at(1_561_353_581) do - assert_fetch_s3 url, "4afc3010757f1fd143e769f1d1dabd406476a4fc7c120e9884fd02acbb8f26c9", nil, "us-west-2" - end - ensure - Gem.configuration[:s3_source] = nil - end - - def test_fetch_s3_config_creds_with_token - Gem.configuration[:s3_source] = { - "my-bucket" => { id: "testuser", secret: "testpass", security_token: "testtoken" }, - } - url = "s3://my-bucket/gems/specs.4.8.gz" - Time.stub :now, Time.at(1_561_353_581) do - assert_fetch_s3 url, "935160a427ef97e7630f799232b8f208c4a4e49aad07d0540572a2ad5fe9f93c", "testtoken" - end - ensure - Gem.configuration[:s3_source] = nil - end - - def test_fetch_s3_env_creds - ENV["AWS_ACCESS_KEY_ID"] = "testuser" - ENV["AWS_SECRET_ACCESS_KEY"] = "testpass" - ENV["AWS_SESSION_TOKEN"] = nil - Gem.configuration[:s3_source] = { - "my-bucket" => { provider: "env" }, - } - url = "s3://my-bucket/gems/specs.4.8.gz" - Time.stub :now, Time.at(1_561_353_581) do - assert_fetch_s3 url, "20f974027db2f3cd6193565327a7c73457a138efb1a63ea248d185ce6827d41b" - end - ensure - ENV.each_key {|key| ENV.delete(key) if key.start_with?("AWS") } - Gem.configuration[:s3_source] = nil - end - - def test_fetch_s3_env_creds_with_region - ENV["AWS_ACCESS_KEY_ID"] = "testuser" - ENV["AWS_SECRET_ACCESS_KEY"] = "testpass" - ENV["AWS_SESSION_TOKEN"] = nil - Gem.configuration[:s3_source] = { - "my-bucket" => { provider: "env", region: "us-west-2" }, - } - url = "s3://my-bucket/gems/specs.4.8.gz" - Time.stub :now, Time.at(1_561_353_581) do - assert_fetch_s3 url, "4afc3010757f1fd143e769f1d1dabd406476a4fc7c120e9884fd02acbb8f26c9", nil, "us-west-2" - end - ensure - ENV.each_key {|key| ENV.delete(key) if key.start_with?("AWS") } - Gem.configuration[:s3_source] = nil - end - - def test_fetch_s3_env_creds_with_token - ENV["AWS_ACCESS_KEY_ID"] = "testuser" - ENV["AWS_SECRET_ACCESS_KEY"] = "testpass" - ENV["AWS_SESSION_TOKEN"] = "testtoken" - Gem.configuration[:s3_source] = { - "my-bucket" => { provider: "env" }, - } - url = "s3://my-bucket/gems/specs.4.8.gz" - Time.stub :now, Time.at(1_561_353_581) do - assert_fetch_s3 url, "935160a427ef97e7630f799232b8f208c4a4e49aad07d0540572a2ad5fe9f93c", "testtoken" - end - ensure - ENV.each_key {|key| ENV.delete(key) if key.start_with?("AWS") } - Gem.configuration[:s3_source] = nil - end - - def test_fetch_s3_url_creds - url = "s3://testuser:testpass@my-bucket/gems/specs.4.8.gz" - Time.stub :now, Time.at(1_561_353_581) do - assert_fetch_s3 url, "20f974027db2f3cd6193565327a7c73457a138efb1a63ea248d185ce6827d41b" - end - end - - def test_fetch_s3_instance_profile_creds - Gem.configuration[:s3_source] = { - "my-bucket" => { provider: "instance_profile" }, - } - - url = "s3://my-bucket/gems/specs.4.8.gz" - Time.stub :now, Time.at(1_561_353_581) do - assert_fetch_s3 url, "20f974027db2f3cd6193565327a7c73457a138efb1a63ea248d185ce6827d41b", nil, "us-east-1", - '{"AccessKeyId": "testuser", "SecretAccessKey": "testpass"}' - end - ensure - Gem.configuration[:s3_source] = nil - end - - def test_fetch_s3_instance_profile_creds_with_region - Gem.configuration[:s3_source] = { - "my-bucket" => { provider: "instance_profile", region: "us-west-2" }, - } - - url = "s3://my-bucket/gems/specs.4.8.gz" - Time.stub :now, Time.at(1_561_353_581) do - assert_fetch_s3 url, "4afc3010757f1fd143e769f1d1dabd406476a4fc7c120e9884fd02acbb8f26c9", nil, "us-west-2", - '{"AccessKeyId": "testuser", "SecretAccessKey": "testpass"}' - end - ensure - Gem.configuration[:s3_source] = nil - end - - def test_fetch_s3_instance_profile_creds_with_token - Gem.configuration[:s3_source] = { - "my-bucket" => { provider: "instance_profile" }, - } - - url = "s3://my-bucket/gems/specs.4.8.gz" - Time.stub :now, Time.at(1_561_353_581) do - assert_fetch_s3 url, "935160a427ef97e7630f799232b8f208c4a4e49aad07d0540572a2ad5fe9f93c", "testtoken", "us-east-1", - '{"AccessKeyId": "testuser", "SecretAccessKey": "testpass", "Token": "testtoken"}' - end - ensure - Gem.configuration[:s3_source] = nil - end - - def refute_fetch_s3(url, expected_message) - fetcher = Gem::RemoteFetcher.new nil - @fetcher = fetcher - - e = assert_raise Gem::RemoteFetcher::FetchError do - fetcher.fetch_s3 Gem::URI.parse(url) - end - - assert_match expected_message, e.message - end - - def test_fetch_s3_no_source_key - url = "s3://my-bucket/gems/specs.4.8.gz" - refute_fetch_s3 url, "no s3_source key exists in .gemrc" - end - - def test_fetch_s3_no_host - Gem.configuration[:s3_source] = { - "my-bucket" => { id: "testuser", secret: "testpass" }, - } - - url = "s3://other-bucket/gems/specs.4.8.gz" - refute_fetch_s3 url, "no key for host other-bucket in s3_source in .gemrc" - ensure - Gem.configuration[:s3_source] = nil - end - - def test_fetch_s3_no_id - Gem.configuration[:s3_source] = { "my-bucket" => { secret: "testpass" } } - - url = "s3://my-bucket/gems/specs.4.8.gz" - refute_fetch_s3 url, "s3_source for my-bucket missing id or secret" - ensure - Gem.configuration[:s3_source] = nil - end - - def test_fetch_s3_no_secret - Gem.configuration[:s3_source] = { "my-bucket" => { id: "testuser" } } - - url = "s3://my-bucket/gems/specs.4.8.gz" - refute_fetch_s3 url, "s3_source for my-bucket missing id or secret" - ensure - Gem.configuration[:s3_source] = nil - end - - def test_observe_no_proxy_env_single_host - use_ui @stub_ui do - ENV["http_proxy"] = @proxy_uri - ENV["no_proxy"] = Gem::URI.parse(@server_uri).host - fetcher = Gem::RemoteFetcher.new nil - @fetcher = fetcher - assert_data_from_server fetcher.fetch_path(@server_uri) - end - end - - def test_observe_no_proxy_env_list - use_ui @stub_ui do - ENV["http_proxy"] = @proxy_uri - ENV["no_proxy"] = "fakeurl.com, #{Gem::URI.parse(@server_uri).host}" - fetcher = Gem::RemoteFetcher.new nil - @fetcher = fetcher - assert_data_from_server fetcher.fetch_path(@server_uri) - end - end - def test_request_block fetcher = Gem::RemoteFetcher.new nil @fetcher = fetcher @@ -967,112 +586,12 @@ def test_request_block def test_yaml_error_on_size use_ui @stub_ui do - self.enable_yaml = false fetcher = Gem::RemoteFetcher.new nil @fetcher = fetcher assert_error { fetcher.size } end end - def test_ssl_connection - ssl_server = start_ssl_server - temp_ca_cert = File.join(__dir__, "ca_cert.pem") - with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher| - fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml") - end - end - - def test_ssl_client_cert_auth_connection - ssl_server = start_ssl_server( - { SSLVerifyClient: OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT } - ) - - temp_ca_cert = File.join(__dir__, "ca_cert.pem") - temp_client_cert = File.join(__dir__, "client.pem") - - with_configured_fetcher( - ":ssl_ca_cert: #{temp_ca_cert}\n" \ - ":ssl_client_cert: #{temp_client_cert}\n" - ) do |fetcher| - fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml") - end - end - - def test_do_not_allow_invalid_client_cert_auth_connection - ssl_server = start_ssl_server( - { SSLVerifyClient: OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT } - ) - - temp_ca_cert = File.join(__dir__, "ca_cert.pem") - temp_client_cert = File.join(__dir__, "invalid_client.pem") - - with_configured_fetcher( - ":ssl_ca_cert: #{temp_ca_cert}\n" \ - ":ssl_client_cert: #{temp_client_cert}\n" - ) do |fetcher| - assert_raise Gem::RemoteFetcher::FetchError do - fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml") - end - end - end - - def test_do_not_allow_insecure_ssl_connection_by_default - ssl_server = start_ssl_server - with_configured_fetcher do |fetcher| - assert_raise Gem::RemoteFetcher::FetchError do - fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml") - end - end - end - - def test_ssl_connection_allow_verify_none - ssl_server = start_ssl_server - with_configured_fetcher(":ssl_verify_mode: 0") do |fetcher| - fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml") - end - end - - def test_do_not_follow_insecure_redirect - ssl_server = start_ssl_server - temp_ca_cert = File.join(__dir__, "ca_cert.pem") - expected_error_message = - "redirecting to non-https resource: #{@server_uri} (https://localhost:#{ssl_server.config[:Port]}/insecure_redirect?to=#{@server_uri})" - - with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher| - err = assert_raise Gem::RemoteFetcher::FetchError do - fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/insecure_redirect?to=#{@server_uri}") - end - - assert_equal(err.message, expected_error_message) - end - end - - def test_nil_ca_cert - ssl_server = start_ssl_server - temp_ca_cert = nil - - with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher| - assert_raise Gem::RemoteFetcher::FetchError do - fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}") - end - end - end - - def with_configured_fetcher(config_str = nil, &block) - if config_str - temp_conf = File.join @tempdir, ".gemrc" - File.open temp_conf, "w" do |fp| - fp.puts config_str - end - Gem.configuration = Gem::ConfigFile.new %W[--config-file #{temp_conf}] - end - fetcher = Gem::RemoteFetcher.new - yield fetcher - ensure - fetcher.close_all - Gem.configuration = nil - end - def assert_error(exception_class=Exception) got_exception = false @@ -1084,152 +603,4 @@ def assert_error(exception_class=Exception) assert got_exception, "Expected exception conforming to #{exception_class}" end - - def assert_data_from_server(data) - assert_match(/0\.4\.11/, data, "Data is not from server") - end - - def assert_data_from_proxy(data) - assert_match(/0\.4\.2/, data, "Data is not from proxy") - end - - class NilLog < WEBrick::Log - def log(level, data) # Do nothing - end - end - - private - - attr_reader :normal_server, :proxy_server - attr_accessor :enable_zip, :enable_yaml - - def start_servers - @normal_server ||= start_server(SERVER_DATA) - @proxy_server ||= start_server(PROXY_DATA) - @enable_yaml = true - @enable_zip = false - @ssl_server = nil - @ssl_server_thread = nil - end - - def stop_servers - if @normal_server - @normal_server.kill.join - @normal_server = nil - end - if @proxy_server - @proxy_server.kill.join - @proxy_server = nil - end - if @ssl_server - @ssl_server.stop - @ssl_server = nil - end - if @ssl_server_thread - @ssl_server_thread.kill.join - @ssl_server_thread = nil - end - WEBrick::Utils::TimeoutHandler.terminate - end - - def normal_server_port - @normal_server[:server].config[:Port] - end - - def proxy_server_port - @proxy_server[:server].config[:Port] - end - - def start_ssl_server(config = {}) - pend "starting this test server fails randomly on jruby" if Gem.java_platform? - - null_logger = NilLog.new - server = WEBrick::HTTPServer.new({ - Port: 0, - Logger: null_logger, - AccessLog: [], - SSLEnable: true, - SSLCACertificateFile: File.join(__dir__, "ca_cert.pem"), - SSLCertificate: cert("ssl_cert.pem"), - SSLPrivateKey: key("ssl_key.pem"), - SSLVerifyClient: nil, - SSLCertName: nil, - }.merge(config)) - server.mount_proc("/yaml") do |_req, res| - res.body = "--- true\n" - end - server.mount_proc("/insecure_redirect") do |req, res| - res.set_redirect(WEBrick::HTTPStatus::MovedPermanently, req.query["to"]) - end - server.ssl_context.tmp_dh_callback = proc { TEST_KEY_DH2048 } - t = Thread.new do - server.start - rescue StandardError => ex - puts "ERROR during server thread: #{ex.message}" - raise - ensure - server.shutdown - end - while server.status != :Running - sleep 0.1 - unless t.alive? - t.join - raise - end - end - @ssl_server = server - @ssl_server_thread = t - server - end - - def start_server(data) - null_logger = NilLog.new - s = WEBrick::HTTPServer.new( - Port: 0, - DocumentRoot: nil, - Logger: null_logger, - AccessLog: null_logger - ) - s.mount_proc("/kill") {|_req, _res| s.shutdown } - s.mount_proc("/yaml") do |req, res| - if req["X-Captain"] - res.body = req["X-Captain"] - elsif @enable_yaml - res.body = data - res["Content-Type"] = "text/plain" - res["content-length"] = data.size - else - res.status = "404" - res.body = "

NOT FOUND

" - res["Content-Type"] = "text/html" - end - end - s.mount_proc("/yaml.Z") do |_req, res| - if @enable_zip - res.body = Zlib::Deflate.deflate(data) - res["Content-Type"] = "text/plain" - else - res.status = "404" - res.body = "

NOT FOUND

" - res["Content-Type"] = "text/html" - end - end - th = Thread.new do - s.start - rescue StandardError => ex - abort "ERROR during server thread: #{ex.message}" - ensure - s.shutdown - end - th[:server] = s - th - end - - def cert(filename) - OpenSSL::X509::Certificate.new(File.read(File.join(__dir__, filename))) - end - - def key(filename) - OpenSSL::PKey::RSA.new(File.read(File.join(__dir__, filename))) - end -end if Gem::HAVE_OPENSSL +end diff --git a/test/rubygems/test_gem_remote_fetcher_local_server.rb b/test/rubygems/test_gem_remote_fetcher_local_server.rb new file mode 100644 index 00000000000000..3d3e8b503b1a4b --- /dev/null +++ b/test/rubygems/test_gem_remote_fetcher_local_server.rb @@ -0,0 +1,220 @@ +# frozen_string_literal: true + +require_relative "helper" + +require "socket" + +require "rubygems/remote_fetcher" +require "rubygems/package" + +# = Testing Proxy Settings +# +# These tests check the proper proxy server settings by running two +# web servers. The web server at http://localhost:#{SERVER_PORT} +# represents the normal gem server and returns a gemspec with a rake +# version of 0.4.11. The web server at http://localhost:#{PROXY_PORT} +# represents the proxy server and returns a different dataset where +# rake has version 0.4.2. This allows us to detect which server is +# returning the data. +# +# Note that the proxy server is not a *real* proxy server. But our +# software doesn't really care, as long as we hit the proxy URL when a +# proxy is configured. + +class TestGemRemoteFetcherLocalServer < Gem::TestCase + include Gem::DefaultUserInteraction + + SERVER_DATA = <<-EOY +--- !ruby/object:Gem::Cache +gems: + rake-0.4.11: !ruby/object:Gem::Specification + rubygems_version: "0.7" + specification_version: 1 + name: rake + version: !ruby/object:Gem::Version + version: 0.4.11 + date: 2004-11-12 + summary: Ruby based make-like utility. + require_paths: + - lib + author: Jim Weirich + email: jim@weirichhouse.org + homepage: http://rake.rubyforge.org + description: Rake is a Make-like program implemented in Ruby. Tasks and dependencies are specified in standard Ruby syntax. + autorequire: + bindir: bin + has_rdoc: true + required_ruby_version: !ruby/object:Gem::Version::Requirement + requirements: + - + - ">" + - !ruby/object:Gem::Version + version: 0.0.0 + version: + platform: ruby + files: + - README + test_files: [] + library_stubs: + rdoc_options: + extra_rdoc_files: + executables: + - rake + extensions: [] + requirements: [] + dependencies: [] + EOY + + PROXY_DATA = SERVER_DATA.gsub(/0.4.11/, "0.4.2") + + def setup + @proxies = %w[https_proxy http_proxy HTTP_PROXY http_proxy_user HTTP_PROXY_USER http_proxy_pass HTTP_PROXY_PASS no_proxy NO_PROXY] + @old_proxies = @proxies.map {|k| ENV[k] } + @proxies.each {|k| ENV[k] = nil } + + super + + @normal_server ||= start_server(SERVER_DATA) + @proxy_server ||= start_server(PROXY_DATA) + + @server_uri = "http://localhost:#{@normal_server[:server].addr[1]}" + "/yaml" + @proxy_uri = "http://localhost:#{@proxy_server[:server].addr[1]}" + + Gem::RemoteFetcher.fetcher = nil + @stub_ui = Gem::MockGemUi.new + @fetcher = Gem::RemoteFetcher.fetcher + end + + def teardown + @fetcher&.close_all + + if @normal_server + @normal_server.kill.join + @normal_server[:server].close + @normal_server = nil + end + if @proxy_server + @proxy_server.kill.join + @proxy_server[:server].close + @proxy_server = nil + end + + super + Gem.configuration[:http_proxy] = nil + @proxies.each_with_index {|k, i| ENV[k] = @old_proxies[i] } + end + + def test_no_proxy + use_ui @stub_ui do + assert_data_from_server @fetcher.fetch_path(@server_uri) + response = @fetcher.fetch_path(@server_uri, nil, true) + assert_equal SERVER_DATA.size, response["content-length"].to_i + end + end + + def test_implicit_no_proxy + use_ui @stub_ui do + ENV["http_proxy"] = "http://fakeurl:12345" + fetcher = Gem::RemoteFetcher.new :no_proxy + @fetcher = fetcher + assert_data_from_server fetcher.fetch_path(@server_uri) + end + end + + def test_implicit_proxy + use_ui @stub_ui do + ENV["http_proxy"] = @proxy_uri + fetcher = Gem::RemoteFetcher.new nil + @fetcher = fetcher + assert_data_from_proxy fetcher.fetch_path(@server_uri) + end + end + + def test_implicit_upper_case_proxy + use_ui @stub_ui do + ENV["HTTP_PROXY"] = @proxy_uri + fetcher = Gem::RemoteFetcher.new nil + @fetcher = fetcher + assert_data_from_proxy fetcher.fetch_path(@server_uri) + end + end + + def test_implicit_proxy_no_env + use_ui @stub_ui do + fetcher = Gem::RemoteFetcher.new nil + @fetcher = fetcher + assert_data_from_server fetcher.fetch_path(@server_uri) + end + end + + def test_fetch_http_with_additional_headers + ENV["http_proxy"] = @proxy_uri + ENV["no_proxy"] = Gem::URI.parse(@server_uri).host + fetcher = Gem::RemoteFetcher.new nil, nil, { "X-Captain" => "murphy" } + @fetcher = fetcher + assert_equal "murphy", fetcher.fetch_path(@server_uri) + end + + def test_observe_no_proxy_env_single_host + use_ui @stub_ui do + ENV["http_proxy"] = @proxy_uri + ENV["no_proxy"] = Gem::URI.parse(@server_uri).host + fetcher = Gem::RemoteFetcher.new nil + @fetcher = fetcher + assert_data_from_server fetcher.fetch_path(@server_uri) + end + end + + def test_observe_no_proxy_env_list + use_ui @stub_ui do + ENV["http_proxy"] = @proxy_uri + ENV["no_proxy"] = "fakeurl.com, #{Gem::URI.parse(@server_uri).host}" + fetcher = Gem::RemoteFetcher.new nil + @fetcher = fetcher + assert_data_from_server fetcher.fetch_path(@server_uri) + end + end + + private + + attr_reader :normal_server, :proxy_server + + def assert_data_from_server(data) + assert_match(/0\.4\.11/, data, "Data is not from server") + end + + def assert_data_from_proxy(data) + assert_match(/0\.4\.2/, data, "Data is not from proxy") + end + + def start_server(data) + server = TCPServer.new("localhost", 0) + thread = Thread.new do + loop do + client = server.accept + handle_request(client, data) + end + end + thread[:server] = server + thread + end + + def handle_request(client, data) + request_line = client.gets + headers = {} + while (line = client.gets) && line != "\r\n" + key, value = line.split(": ", 2) + headers[key] = value.strip + end + + if request_line.start_with?("GET /yaml") + response = headers["X-Captain"] ? headers["X-Captain"] : data + client.print "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: #{response.size}\r\n\r\n#{response}" + elsif request_line.start_with?("HEAD /yaml") || request_line.start_with?("GET http://") && request_line.include?("/yaml") + client.print "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: #{data.size}\r\n\r\n#{data}" + else + client.print "HTTP/1.1 404 Not Found\r\nContent-Type: text/html\r\n\r\n

NOT FOUND

" + end + client.close + end +end diff --git a/test/rubygems/test_gem_remote_fetcher_local_ssl_server.rb b/test/rubygems/test_gem_remote_fetcher_local_ssl_server.rb new file mode 100644 index 00000000000000..7ccc1e7883d3e5 --- /dev/null +++ b/test/rubygems/test_gem_remote_fetcher_local_ssl_server.rb @@ -0,0 +1,195 @@ +# frozen_string_literal: true + +require_relative "helper" +require "socket" +require "openssl" + +unless Gem::HAVE_OPENSSL + warn "Skipping Gem::RemoteFetcher tests. openssl not found." +end + +require "rubygems/remote_fetcher" +require "rubygems/package" + +class TestGemRemoteFetcherLocalSSLServer < Gem::TestCase + include Gem::DefaultUserInteraction + + # Generated via: + # x = OpenSSL::PKey::DH.new(2048) # wait a while... + # x.to_s => pem + TEST_KEY_DH2048 = OpenSSL::PKey::DH.new <<-_END_OF_PEM_ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEA3Ze2EHSfYkZLUn557torAmjBgPsqzbodaRaGZtgK1gEU+9nNJaFV +G1JKhmGUiEDyIW7idsBpe4sX/Wqjnp48Lr8IeI/SlEzLdoGpf05iRYXC8Cm9o8aM +cfmVgoSEAo9YLBpzoji2jHkO7Q5IPt4zxbTdlmmGFLc/GO9q7LGHhC+rcMcNTGsM +49AnILNn49pq4Y72jSwdmvq4psHZwwFBbPwLdw6bLUDDCN90jfqvYt18muwUxDiN +NP0fuvVAIB158VnQ0liHSwcl6+9vE1mL0Jo/qEXQxl0+UdKDjaGfTsn6HIrwTnmJ +PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg== +-----END DH PARAMETERS----- + _END_OF_PEM_ + + def setup + super + @ssl_server_thread = nil + @ssl_server = nil + end + + def teardown + if @ssl_server_thread + @ssl_server_thread.kill.join + @ssl_server_thread = nil + end + if @ssl_server + @ssl_server.close + @ssl_server = nil + end + super + end + + def test_ssl_connection + ssl_server = start_ssl_server + temp_ca_cert = File.join(__dir__, "ca_cert.pem") + with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher| + fetcher.fetch_path("https://localhost:#{ssl_server.addr[1]}/yaml") + end + end + + def test_ssl_client_cert_auth_connection + ssl_server = start_ssl_server( + { verify_mode: OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT } + ) + + temp_ca_cert = File.join(__dir__, "ca_cert.pem") + temp_client_cert = File.join(__dir__, "client.pem") + + with_configured_fetcher( + ":ssl_ca_cert: #{temp_ca_cert}\n" \ + ":ssl_client_cert: #{temp_client_cert}\n" + ) do |fetcher| + fetcher.fetch_path("https://localhost:#{ssl_server.addr[1]}/yaml") + end + end + + def test_do_not_allow_invalid_client_cert_auth_connection + ssl_server = start_ssl_server( + { verify_mode: OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT } + ) + + temp_ca_cert = File.join(__dir__, "ca_cert.pem") + temp_client_cert = File.join(__dir__, "invalid_client.pem") + + with_configured_fetcher( + ":ssl_ca_cert: #{temp_ca_cert}\n" \ + ":ssl_client_cert: #{temp_client_cert}\n" + ) do |fetcher| + assert_raise Gem::RemoteFetcher::FetchError do + fetcher.fetch_path("https://localhost:#{ssl_server.addr[1]}/yaml") + end + end + end + + def test_do_not_allow_insecure_ssl_connection_by_default + ssl_server = start_ssl_server + with_configured_fetcher do |fetcher| + assert_raise Gem::RemoteFetcher::FetchError do + fetcher.fetch_path("https://localhost:#{ssl_server.addr[1]}/yaml") + end + end + end + + def test_ssl_connection_allow_verify_none + ssl_server = start_ssl_server + with_configured_fetcher(":ssl_verify_mode: 0") do |fetcher| + fetcher.fetch_path("https://localhost:#{ssl_server.addr[1]}/yaml") + end + end + + def test_do_not_follow_insecure_redirect + @server_uri = "http://example.com" + ssl_server = start_ssl_server + temp_ca_cert = File.join(__dir__, "ca_cert.pem") + expected_error_message = + "redirecting to non-https resource: #{@server_uri} (https://localhost:#{ssl_server.addr[1]}/insecure_redirect?to=#{@server_uri})" + + with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher| + err = assert_raise Gem::RemoteFetcher::FetchError do + fetcher.fetch_path("https://localhost:#{ssl_server.addr[1]}/insecure_redirect?to=#{@server_uri}") + end + + assert_equal(err.message, expected_error_message) + end + end + + def test_nil_ca_cert + ssl_server = start_ssl_server + temp_ca_cert = nil + + with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher| + assert_raise Gem::RemoteFetcher::FetchError do + fetcher.fetch_path("https://localhost:#{ssl_server.addr[1]}") + end + end + end + + private + + def with_configured_fetcher(config_str = nil, &block) + if config_str + temp_conf = File.join @tempdir, ".gemrc" + File.open temp_conf, "w" do |fp| + fp.puts config_str + end + Gem.configuration = Gem::ConfigFile.new %W[--config-file #{temp_conf}] + end + fetcher = Gem::RemoteFetcher.new + yield fetcher + sleep 0.5 unless RUBY_PLATFORM.match?(/mswin|mingw/) + ensure + fetcher.close_all + Gem.configuration = nil + end + + def start_ssl_server(config = {}) + server = TCPServer.new(0) + ctx = OpenSSL::SSL::SSLContext.new + ctx.cert = cert("ssl_cert.pem") + ctx.key = key("ssl_key.pem") + ctx.ca_file = File.join(__dir__, "ca_cert.pem") + ctx.tmp_dh_callback = proc { TEST_KEY_DH2048 } + ctx.verify_mode = config[:verify_mode] if config[:verify_mode] + @ssl_server = OpenSSL::SSL::SSLServer.new(server, ctx) + @ssl_server_thread = Thread.new do + loop do + ssl_client = @ssl_server.accept + Thread.new(ssl_client) do |client| + handle_request(client) + ensure + client.close + end + rescue OpenSSL::SSL::SSLError => e + # Ignore SSL errors because we're testing them implicitly + end + end + @ssl_server + end + + def handle_request(client) + request = client.gets + if request.start_with?("GET /yaml") + client.print "HTTP/1.1 200 OK\r\nContent-Type: text/yaml\r\n\r\n--- true\n" + elsif request.start_with?("GET /insecure_redirect") + location = request.match(/to=([^ ]+)/)[1] + client.print "HTTP/1.1 301 Moved Permanently\r\nLocation: #{location}\r\n\r\n" + else + client.print "HTTP/1.1 404 Not Found\r\n\r\n" + end + end + + def cert(filename) + OpenSSL::X509::Certificate.new(File.read(File.join(__dir__, filename))) + end + + def key(filename) + OpenSSL::PKey::RSA.new(File.read(File.join(__dir__, filename))) + end +end if Gem::HAVE_OPENSSL diff --git a/test/rubygems/test_gem_remote_fetcher_s3.rb b/test/rubygems/test_gem_remote_fetcher_s3.rb new file mode 100644 index 00000000000000..fe7eb7ec01bb4d --- /dev/null +++ b/test/rubygems/test_gem_remote_fetcher_s3.rb @@ -0,0 +1,233 @@ +# frozen_string_literal: true + +require_relative "helper" + +require "rubygems/remote_fetcher" +require "rubygems/package" + +class TestGemRemoteFetcherS3 < Gem::TestCase + include Gem::DefaultUserInteraction + + def setup + super + + @a1, @a1_gem = util_gem "a", "1" do |s| + s.executables << "a_bin" + end + + @a1.loaded_from = File.join(@gemhome, "specifications", @a1.full_name) + end + + def assert_fetch_s3(url, signature, token=nil, region="us-east-1", instance_profile_json=nil) + fetcher = Gem::RemoteFetcher.new nil + @fetcher = fetcher + $fetched_uri = nil + $instance_profile = instance_profile_json + + def fetcher.request(uri, request_class, last_modified = nil) + $fetched_uri = uri + res = Gem::Net::HTTPOK.new nil, 200, nil + def res.body + "success" + end + res + end + + def fetcher.s3_uri_signer(uri) + require "json" + s3_uri_signer = Gem::S3URISigner.new(uri) + def s3_uri_signer.ec2_metadata_credentials_json + JSON.parse($instance_profile) + end + # Running sign operation to make sure uri.query is not mutated + s3_uri_signer.sign + raise "URI query is not empty: #{uri.query}" unless uri.query.nil? + s3_uri_signer + end + + data = fetcher.fetch_s3 Gem::URI.parse(url) + + assert_equal "https://my-bucket.s3.#{region}.amazonaws.com/gems/specs.4.8.gz?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=testuser%2F20190624%2F#{region}%2Fs3%2Faws4_request&X-Amz-Date=20190624T050641Z&X-Amz-Expires=86400#{token ? "&X-Amz-Security-Token=" + token : ""}&X-Amz-SignedHeaders=host&X-Amz-Signature=#{signature}", $fetched_uri.to_s + assert_equal "success", data + ensure + $fetched_uri = nil + end + + def test_fetch_s3_config_creds + Gem.configuration[:s3_source] = { + "my-bucket" => { id: "testuser", secret: "testpass" }, + } + url = "s3://my-bucket/gems/specs.4.8.gz" + Time.stub :now, Time.at(1_561_353_581) do + assert_fetch_s3 url, "20f974027db2f3cd6193565327a7c73457a138efb1a63ea248d185ce6827d41b" + end + ensure + Gem.configuration[:s3_source] = nil + end + + def test_fetch_s3_config_creds_with_region + Gem.configuration[:s3_source] = { + "my-bucket" => { id: "testuser", secret: "testpass", region: "us-west-2" }, + } + url = "s3://my-bucket/gems/specs.4.8.gz" + Time.stub :now, Time.at(1_561_353_581) do + assert_fetch_s3 url, "4afc3010757f1fd143e769f1d1dabd406476a4fc7c120e9884fd02acbb8f26c9", nil, "us-west-2" + end + ensure + Gem.configuration[:s3_source] = nil + end + + def test_fetch_s3_config_creds_with_token + Gem.configuration[:s3_source] = { + "my-bucket" => { id: "testuser", secret: "testpass", security_token: "testtoken" }, + } + url = "s3://my-bucket/gems/specs.4.8.gz" + Time.stub :now, Time.at(1_561_353_581) do + assert_fetch_s3 url, "935160a427ef97e7630f799232b8f208c4a4e49aad07d0540572a2ad5fe9f93c", "testtoken" + end + ensure + Gem.configuration[:s3_source] = nil + end + + def test_fetch_s3_env_creds + ENV["AWS_ACCESS_KEY_ID"] = "testuser" + ENV["AWS_SECRET_ACCESS_KEY"] = "testpass" + ENV["AWS_SESSION_TOKEN"] = nil + Gem.configuration[:s3_source] = { + "my-bucket" => { provider: "env" }, + } + url = "s3://my-bucket/gems/specs.4.8.gz" + Time.stub :now, Time.at(1_561_353_581) do + assert_fetch_s3 url, "20f974027db2f3cd6193565327a7c73457a138efb1a63ea248d185ce6827d41b" + end + ensure + ENV.each_key {|key| ENV.delete(key) if key.start_with?("AWS") } + Gem.configuration[:s3_source] = nil + end + + def test_fetch_s3_env_creds_with_region + ENV["AWS_ACCESS_KEY_ID"] = "testuser" + ENV["AWS_SECRET_ACCESS_KEY"] = "testpass" + ENV["AWS_SESSION_TOKEN"] = nil + Gem.configuration[:s3_source] = { + "my-bucket" => { provider: "env", region: "us-west-2" }, + } + url = "s3://my-bucket/gems/specs.4.8.gz" + Time.stub :now, Time.at(1_561_353_581) do + assert_fetch_s3 url, "4afc3010757f1fd143e769f1d1dabd406476a4fc7c120e9884fd02acbb8f26c9", nil, "us-west-2" + end + ensure + ENV.each_key {|key| ENV.delete(key) if key.start_with?("AWS") } + Gem.configuration[:s3_source] = nil + end + + def test_fetch_s3_env_creds_with_token + ENV["AWS_ACCESS_KEY_ID"] = "testuser" + ENV["AWS_SECRET_ACCESS_KEY"] = "testpass" + ENV["AWS_SESSION_TOKEN"] = "testtoken" + Gem.configuration[:s3_source] = { + "my-bucket" => { provider: "env" }, + } + url = "s3://my-bucket/gems/specs.4.8.gz" + Time.stub :now, Time.at(1_561_353_581) do + assert_fetch_s3 url, "935160a427ef97e7630f799232b8f208c4a4e49aad07d0540572a2ad5fe9f93c", "testtoken" + end + ensure + ENV.each_key {|key| ENV.delete(key) if key.start_with?("AWS") } + Gem.configuration[:s3_source] = nil + end + + def test_fetch_s3_url_creds + url = "s3://testuser:testpass@my-bucket/gems/specs.4.8.gz" + Time.stub :now, Time.at(1_561_353_581) do + assert_fetch_s3 url, "20f974027db2f3cd6193565327a7c73457a138efb1a63ea248d185ce6827d41b" + end + end + + def test_fetch_s3_instance_profile_creds + Gem.configuration[:s3_source] = { + "my-bucket" => { provider: "instance_profile" }, + } + + url = "s3://my-bucket/gems/specs.4.8.gz" + Time.stub :now, Time.at(1_561_353_581) do + assert_fetch_s3 url, "20f974027db2f3cd6193565327a7c73457a138efb1a63ea248d185ce6827d41b", nil, "us-east-1", + '{"AccessKeyId": "testuser", "SecretAccessKey": "testpass"}' + end + ensure + Gem.configuration[:s3_source] = nil + end + + def test_fetch_s3_instance_profile_creds_with_region + Gem.configuration[:s3_source] = { + "my-bucket" => { provider: "instance_profile", region: "us-west-2" }, + } + + url = "s3://my-bucket/gems/specs.4.8.gz" + Time.stub :now, Time.at(1_561_353_581) do + assert_fetch_s3 url, "4afc3010757f1fd143e769f1d1dabd406476a4fc7c120e9884fd02acbb8f26c9", nil, "us-west-2", + '{"AccessKeyId": "testuser", "SecretAccessKey": "testpass"}' + end + ensure + Gem.configuration[:s3_source] = nil + end + + def test_fetch_s3_instance_profile_creds_with_token + Gem.configuration[:s3_source] = { + "my-bucket" => { provider: "instance_profile" }, + } + + url = "s3://my-bucket/gems/specs.4.8.gz" + Time.stub :now, Time.at(1_561_353_581) do + assert_fetch_s3 url, "935160a427ef97e7630f799232b8f208c4a4e49aad07d0540572a2ad5fe9f93c", "testtoken", "us-east-1", + '{"AccessKeyId": "testuser", "SecretAccessKey": "testpass", "Token": "testtoken"}' + end + ensure + Gem.configuration[:s3_source] = nil + end + + def refute_fetch_s3(url, expected_message) + fetcher = Gem::RemoteFetcher.new nil + @fetcher = fetcher + + e = assert_raise Gem::RemoteFetcher::FetchError do + fetcher.fetch_s3 Gem::URI.parse(url) + end + + assert_match expected_message, e.message + end + + def test_fetch_s3_no_source_key + url = "s3://my-bucket/gems/specs.4.8.gz" + refute_fetch_s3 url, "no s3_source key exists in .gemrc" + end + + def test_fetch_s3_no_host + Gem.configuration[:s3_source] = { + "my-bucket" => { id: "testuser", secret: "testpass" }, + } + + url = "s3://other-bucket/gems/specs.4.8.gz" + refute_fetch_s3 url, "no key for host other-bucket in s3_source in .gemrc" + ensure + Gem.configuration[:s3_source] = nil + end + + def test_fetch_s3_no_id + Gem.configuration[:s3_source] = { "my-bucket" => { secret: "testpass" } } + + url = "s3://my-bucket/gems/specs.4.8.gz" + refute_fetch_s3 url, "s3_source for my-bucket missing id or secret" + ensure + Gem.configuration[:s3_source] = nil + end + + def test_fetch_s3_no_secret + Gem.configuration[:s3_source] = { "my-bucket" => { id: "testuser" } } + + url = "s3://my-bucket/gems/specs.4.8.gz" + refute_fetch_s3 url, "s3_source for my-bucket missing id or secret" + ensure + Gem.configuration[:s3_source] = nil + end +end diff --git a/test/rubygems/test_gem_request_set_gem_dependency_api.rb b/test/rubygems/test_gem_request_set_gem_dependency_api.rb index af32005a167505..4b5eaa38eda8ef 100644 --- a/test/rubygems/test_gem_request_set_gem_dependency_api.rb +++ b/test/rubygems/test_gem_request_set_gem_dependency_api.rb @@ -44,7 +44,7 @@ def with_engine_version(name, version) def test_gempspec_with_multiple_runtime_deps save_gemspec "foo", "1.0" do |s| - s.add_runtime_dependency "bar", ">= 1.6.0", "< 1.6.4" + s.add_dependency "bar", ">= 1.6.0", "< 1.6.4" end @gda.gemspec assert_equal %w[foo bar].sort, @set.dependencies.map(&:name).sort diff --git a/test/rubygems/test_gem_resolver.rb b/test/rubygems/test_gem_resolver.rb index b7dadda7085cba..4990d5d2dd1339 100644 --- a/test/rubygems/test_gem_resolver.rb +++ b/test/rubygems/test_gem_resolver.rb @@ -571,7 +571,7 @@ def test_raises_and_reports_a_toplevel_request_properly def test_raises_and_reports_an_implicit_request_properly a1 = util_spec "a", "1" do |s| - s.add_runtime_dependency "b", "= 2" + s.add_dependency "b", "= 2" end ad = make_dep "a", "= 1" diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb index 34ebb377c60ab8..0a67dad25d8f51 100644 --- a/test/rubygems/test_gem_specification.rb +++ b/test/rubygems/test_gem_specification.rb @@ -1833,7 +1833,7 @@ def test_files_non_array_pathological end def test_for_cache - @a2.add_runtime_dependency "b", "1" + @a2.add_dependency "b", "1" @a2.dependencies.first.instance_variable_set :@type, nil @a2.required_rubygems_version = Gem::Requirement.new "> 0" @a2.test_files = %w[test/test_b.rb] @@ -2265,7 +2265,7 @@ def test_runtime_predicate_false end def test_to_ruby - @a2.add_runtime_dependency "b", "1" + @a2.add_dependency "b", "1" @a2.dependencies.first.instance_variable_set :@type, nil @a2.required_rubygems_version = Gem::Requirement.new "> 0" @a2.require_paths << "other" @@ -2337,7 +2337,7 @@ def test_to_ruby_with_rsa_key end def test_to_ruby_for_cache - @a2.add_runtime_dependency "b", "1" + @a2.add_dependency "b", "1" @a2.dependencies.first.instance_variable_set :@type, nil @a2.required_rubygems_version = Gem::Requirement.new "> 0" @a2.installed_by_version = Gem.rubygems_version @@ -2760,7 +2760,7 @@ def test_validate_prerelease_dependencies_with_prerelease_version Dir.chdir @tempdir do @a1.version = "1.0.0.beta.1" - @a1.add_runtime_dependency "b", "~> 1.2.0.beta.1" + @a1.add_dependency "b", "~> 1.2.0.beta.1" use_ui @ui do @a1.validate @@ -2774,7 +2774,7 @@ def test_validate_self_referencing_dependencies util_setup_validate Dir.chdir @tempdir do - @a1.add_runtime_dependency @a1.name, "1" + @a1.add_dependency @a1.name, "1" use_ui @ui do @a1.validate @@ -2807,7 +2807,7 @@ def test_validate_rake_extension_have_rake_dependency_no_warning Dir.chdir @tempdir do @a1.extensions = ["Rakefile"] - @a1.add_runtime_dependency "rake" + @a1.add_dependency "rake" File.write File.join(@tempdir, "Rakefile"), "" use_ui @ui do @@ -2978,19 +2978,27 @@ def test_validate_executables end def test_validate_empty_require_paths - if Gem.win_platform? - pend "test_validate_empty_require_paths skipped on MS Windows (symlink)" - else - util_setup_validate + util_setup_validate - @a1.require_paths = [] - e = assert_raise Gem::InvalidSpecificationException do - @a1.validate - end + @a1.require_paths = [] + e = assert_raise Gem::InvalidSpecificationException do + @a1.validate + end - assert_equal "specification must have at least one require_path", - e.message + assert_equal "specification must have at least one require_path", + e.message + end + + def test_validate_require_paths_with_invalid_types + util_setup_validate + + @a1.require_paths = [1, 2] + e = assert_raise Gem::InvalidSpecificationException do + @a1.validate end + + assert_equal "require_paths must be an Array of String", + e.message end def test_validate_files @@ -3077,7 +3085,7 @@ def test_unresolved_specs_with_versions def test_duplicate_runtime_dependency expected = "WARNING: duplicated b dependency [\"~> 3.0\", \"~> 3.0\"]\n" out, err = capture_output do - @a1.add_runtime_dependency "b", "~> 3.0", "~> 3.0" + @a1.add_dependency "b", "~> 3.0", "~> 3.0" end assert_empty out assert_equal(expected, err) diff --git a/test/rubygems/test_gem_uninstaller.rb b/test/rubygems/test_gem_uninstaller.rb index 794aaf1aec1640..92ea01a3bca2dd 100644 --- a/test/rubygems/test_gem_uninstaller.rb +++ b/test/rubygems/test_gem_uninstaller.rb @@ -403,7 +403,7 @@ def test_uninstall_nonexistent def test_uninstall_not_ok quick_gem "z" do |s| - s.add_runtime_dependency @spec.name + s.add_dependency @spec.name end uninstaller = Gem::Uninstaller.new @spec.name diff --git a/test/test_tempfile.rb b/test/test_tempfile.rb index d4ae7d4b3f6b55..ccebbf3c1a5b0b 100644 --- a/test/test_tempfile.rb +++ b/test/test_tempfile.rb @@ -19,7 +19,7 @@ def teardown end end - def test_leackchecker + def test_leakchecker assert_instance_of(Tempfile, Tempfile.allocate) end diff --git a/thread.c b/thread.c index a4145b48830ce8..6bec7010a83364 100644 --- a/thread.c +++ b/thread.c @@ -3775,12 +3775,13 @@ static VALUE rb_thread_variable_get(VALUE thread, VALUE key) { VALUE locals; + VALUE symbol = rb_to_symbol(key); if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) { return Qnil; } locals = rb_thread_local_storage(thread); - return rb_hash_aref(locals, rb_to_symbol(key)); + return rb_hash_aref(locals, symbol); } /* @@ -3931,13 +3932,14 @@ static VALUE rb_thread_variable_p(VALUE thread, VALUE key) { VALUE locals; + VALUE symbol = rb_to_symbol(key); if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) { return Qfalse; } locals = rb_thread_local_storage(thread); - return RBOOL(rb_hash_lookup(locals, rb_to_symbol(key)) != Qnil); + return RBOOL(rb_hash_lookup(locals, symbol) != Qnil); } /* diff --git a/thread_pthread.c b/thread_pthread.c index 8a70598a79368e..eca1b5a9a6fab9 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -594,7 +594,7 @@ thread_sched_setup_running_threads(struct rb_thread_sched *sched, rb_ractor_t *c if (add_th) { rb_ractor_object_graph_safety_withdraw(cr, OGS_FLAG_NOT_RUNNING); - if (UNLIKELY(vm->ractor.sched.barrier_waiting)) { + while (UNLIKELY(vm->ractor.sched.barrier_waiting)) { RUBY_DEBUG_LOG("barrier-wait"); ractor_sched_barrier_join_signal_locked(vm); @@ -607,6 +607,7 @@ thread_sched_setup_running_threads(struct rb_thread_sched *sched, rb_ractor_t *c ccan_list_add(&vm->ractor.sched.running_threads, &add_th->sched.node.running_threads); vm->ractor.sched.running_cnt++; sched->is_running = true; + VM_ASSERT(!vm->ractor.sched.barrier_waiting); } if (add_timeslice_th) { @@ -1330,7 +1331,7 @@ rb_ractor_sched_sleep(rb_execution_context_t *ec, rb_ractor_t *cr, rb_unblock_fu { // ractor lock of cr is acquired // r is sleeping statuss - rb_thread_t *th = rb_ec_thread_ptr(ec); + rb_thread_t * volatile th = rb_ec_thread_ptr(ec); struct rb_thread_sched *sched = TH_SCHED(th); cr->sync.wait.waiting_thread = th; // TODO: multi-thread @@ -2866,6 +2867,17 @@ static void timer_thread_wakeup_thread(rb_thread_t *th); #include "thread_pthread_mn.c" +static rb_thread_t * +thread_sched_waiting_thread(struct rb_thread_sched_waiting *w) +{ + if (w) { + return (rb_thread_t *)((size_t)w - offsetof(rb_thread_t, sched.waiting_reason)); + } + else { + return NULL; + } +} + static int timer_thread_set_timeout(rb_vm_t *vm) { @@ -2898,7 +2910,9 @@ timer_thread_set_timeout(rb_vm_t *vm) if (vm->ractor.sched.timeslice_wait_inf) { rb_native_mutex_lock(&timer_th.waiting_lock); { - rb_thread_t *th = ccan_list_top(&timer_th.waiting, rb_thread_t, sched.waiting_reason.node); + struct rb_thread_sched_waiting *w = ccan_list_top(&timer_th.waiting, struct rb_thread_sched_waiting, node); + rb_thread_t *th = thread_sched_waiting_thread(w); + if (th && (th->sched.waiting_reason.flags & thread_sched_waiting_timeout)) { rb_hrtime_t now = rb_hrtime_now(); rb_hrtime_t hrrel = rb_hrtime_sub(th->sched.waiting_reason.data.timeout, now); @@ -2948,22 +2962,22 @@ timer_thread_check_exceed(rb_hrtime_t abs, rb_hrtime_t now) static rb_thread_t * timer_thread_deq_wakeup(rb_vm_t *vm, rb_hrtime_t now) { - rb_thread_t *th = ccan_list_top(&timer_th.waiting, rb_thread_t, sched.waiting_reason.node); + struct rb_thread_sched_waiting *w = ccan_list_top(&timer_th.waiting, struct rb_thread_sched_waiting, node); - if (th != NULL && - (th->sched.waiting_reason.flags & thread_sched_waiting_timeout) && - timer_thread_check_exceed(th->sched.waiting_reason.data.timeout, now)) { + if (w != NULL && + (w->flags & thread_sched_waiting_timeout) && + timer_thread_check_exceed(w->data.timeout, now)) { - RUBY_DEBUG_LOG("wakeup th:%u", rb_th_serial(th)); + RUBY_DEBUG_LOG("wakeup th:%u", rb_th_serial(thread_sched_waiting_thread(w))); // delete from waiting list - ccan_list_del_init(&th->sched.waiting_reason.node); + ccan_list_del_init(&w->node); // setup result - th->sched.waiting_reason.flags = thread_sched_waiting_none; - th->sched.waiting_reason.data.result = 0; + w->flags = thread_sched_waiting_none; + w->data.result = 0; - return th; + return thread_sched_waiting_thread(w); } return NULL; diff --git a/thread_pthread.h b/thread_pthread.h index 84e1e349f65a65..5c0fa66a919761 100644 --- a/thread_pthread.h +++ b/thread_pthread.h @@ -17,6 +17,31 @@ #define RB_NATIVETHREAD_LOCK_INIT PTHREAD_MUTEX_INITIALIZER #define RB_NATIVETHREAD_COND_INIT PTHREAD_COND_INITIALIZER +// this data should be protected by timer_th.waiting_lock +struct rb_thread_sched_waiting { + enum thread_sched_waiting_flag { + thread_sched_waiting_none = 0x00, + thread_sched_waiting_timeout = 0x01, + thread_sched_waiting_io_read = 0x02, + thread_sched_waiting_io_write = 0x08, + thread_sched_waiting_io_force = 0x40, // ignore readable + } flags; + + struct { + // should be compat with hrtime.h +#ifdef MY_RUBY_BUILD_MAY_TIME_TRAVEL + int128_t timeout; +#else + uint64_t timeout; +#endif + int fd; // -1 for timeout only + int result; + } data; + + // connected to timer_th.waiting + struct ccan_list_node node; +}; + // per-Thead scheduler helper data struct rb_thread_sched_item { struct { @@ -38,30 +63,7 @@ struct rb_thread_sched_item { struct ccan_list_node zombie_threads; } node; - // this data should be protected by timer_th.waiting_lock - struct { - enum thread_sched_waiting_flag { - thread_sched_waiting_none = 0x00, - thread_sched_waiting_timeout = 0x01, - thread_sched_waiting_io_read = 0x02, - thread_sched_waiting_io_write = 0x08, - thread_sched_waiting_io_force = 0x40, // ignore readable - } flags; - - struct { - // should be compat with hrtime.h -#ifdef MY_RUBY_BUILD_MAY_TIME_TRAVEL - int128_t timeout; -#else - uint64_t timeout; -#endif - int fd; // -1 for timeout only - int result; - } data; - - // connected to timer_th.waiting - struct ccan_list_node node; - } waiting_reason; + struct rb_thread_sched_waiting waiting_reason; bool finished; bool malloc_stack; diff --git a/thread_pthread_mn.c b/thread_pthread_mn.c index b605d6a7511373..6b0631ad5ae4c0 100644 --- a/thread_pthread_mn.c +++ b/thread_pthread_mn.c @@ -546,15 +546,18 @@ static void verify_waiting_list(void) { #if VM_CHECK_MODE > 0 - rb_thread_t *wth, *prev_wth = NULL; - ccan_list_for_each(&timer_th.waiting, wth, sched.waiting_reason.node) { + struct rb_thread_sched_waiting *w, *prev_w = NULL; + + // waiting list's timeout order should be [1, 2, 3, ..., 0, 0, 0] + + ccan_list_for_each(&timer_th.waiting, w, node) { // fprintf(stderr, "verify_waiting_list th:%u abs:%lu\n", rb_th_serial(wth), (unsigned long)wth->sched.waiting_reason.data.timeout); - if (prev_wth) { - rb_hrtime_t timeout = wth->sched.waiting_reason.data.timeout; - rb_hrtime_t prev_timeout = prev_wth->sched.waiting_reason.data.timeout; + if (prev_w) { + rb_hrtime_t timeout = w->data.timeout; + rb_hrtime_t prev_timeout = w->data.timeout; VM_ASSERT(timeout == 0 || prev_timeout <= timeout); } - prev_wth = wth; + prev_w = w; } #endif } @@ -632,16 +635,17 @@ kqueue_unregister_waiting(int fd, enum thread_sched_waiting_flag flags) static bool kqueue_already_registered(int fd) { - rb_thread_t *wth, *found_wth = NULL; - ccan_list_for_each(&timer_th.waiting, wth, sched.waiting_reason.node) { + struct rb_thread_sched_waiting *w, *found_w = NULL; + + ccan_list_for_each(&timer_th.waiting, w, node) { // Similar to EEXIST in epoll_ctl, but more strict because it checks fd rather than flags // for simplicity - if (wth->sched.waiting_reason.flags && wth->sched.waiting_reason.data.fd == fd) { - found_wth = wth; + if (w->flags && w->data.fd == fd) { + found_w = w; break; } } - return found_wth != NULL; + return found_w != NULL; } #endif // HAVE_SYS_EVENT_H @@ -786,20 +790,20 @@ timer_thread_register_waiting(rb_thread_t *th, int fd, enum thread_sched_waiting VM_ASSERT(flags & thread_sched_waiting_timeout); // insert th to sorted list (TODO: O(n)) - rb_thread_t *wth, *prev_wth = NULL; + struct rb_thread_sched_waiting *w, *prev_w = NULL; - ccan_list_for_each(&timer_th.waiting, wth, sched.waiting_reason.node) { - if ((wth->sched.waiting_reason.flags & thread_sched_waiting_timeout) && - wth->sched.waiting_reason.data.timeout < abs) { - prev_wth = wth; + ccan_list_for_each(&timer_th.waiting, w, node) { + if ((w->flags & thread_sched_waiting_timeout) && + w->data.timeout < abs) { + prev_w = w; } else { break; } } - if (prev_wth) { - ccan_list_add_after(&timer_th.waiting, &prev_wth->sched.waiting_reason.node, &th->sched.waiting_reason.node); + if (prev_w) { + ccan_list_add_after(&timer_th.waiting, &prev_w->node, &th->sched.waiting_reason.node); } else { ccan_list_add(&timer_th.waiting, &th->sched.waiting_reason.node); diff --git a/tool/lib/launchable.rb b/tool/lib/launchable.rb new file mode 100644 index 00000000000000..38f4fe92b3e192 --- /dev/null +++ b/tool/lib/launchable.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true +require 'json' +require 'uri' + +module Launchable + ## + # JsonStreamWriter writes a JSON file using a stream. + # By utilizing a stream, we can minimize memory usage, especially for large files. + class JsonStreamWriter + def initialize(path) + @file = File.open(path, "w") + @file.write("{") + @indent_level = 0 + @is_first_key_val = true + @is_first_obj = true + write_new_line + end + + def write_object obj + if @is_first_obj + @is_first_obj = false + else + write_comma + write_new_line + end + @indent_level += 1 + @file.write(to_json_str(obj)) + @indent_level -= 1 + @is_first_key_val = true + # Occasionally, invalid JSON will be created as shown below, especially when `--repeat-count` is specified. + # { + # "testPath": "file=test%2Ftest_timeout.rb&class=TestTimeout&testcase=test_allows_zero_seconds", + # "status": "TEST_PASSED", + # "duration": 2.7e-05, + # "createdAt": "2024-02-09 12:21:07 +0000", + # "stderr": null, + # "stdout": null + # }: null <- here + # }, + # To prevent this, IO#flush is called here. + @file.flush + end + + def write_array(key) + @indent_level += 1 + @file.write(to_json_str(key)) + write_colon + @file.write(" ", "[") + write_new_line + end + + def close + return if @file.closed? + close_array + @indent_level -= 1 + write_new_line + @file.write("}", "\n") + @file.flush + @file.close + end + + private + def to_json_str(obj) + json = JSON.pretty_generate(obj) + json.gsub(/^/, ' ' * (2 * @indent_level)) + end + + def write_indent + @file.write(" " * 2 * @indent_level) + end + + def write_new_line + @file.write("\n") + end + + def write_comma + @file.write(',') + end + + def write_colon + @file.write(":") + end + + def close_array + write_new_line + write_indent + @file.write("]") + @indent_level -= 1 + end + end +end diff --git a/tool/lib/test/unit.rb b/tool/lib/test/unit.rb index 5e5b34f5b615ad..be619e14453155 100644 --- a/tool/lib/test/unit.rb +++ b/tool/lib/test/unit.rb @@ -1450,9 +1450,8 @@ def record(suite, method, assertions, time, error, source_location = nil) def setup_options(opts, options) super opts.on_tail '--launchable-test-reports=PATH', String, 'Report test results in Launchable JSON format' do |path| - require 'json' - require 'uri' - options[:launchable_test_reports] = writer = JsonStreamWriter.new(path) + require_relative '../launchable' + options[:launchable_test_reports] = writer = Launchable::JsonStreamWriter.new(path) writer.write_array('testCases') main_pid = Process.pid at_exit { @@ -1469,92 +1468,6 @@ def encode_test_path_component component component.to_s.gsub('%', '%25').gsub('=', '%3D').gsub('#', '%23').gsub('&', '%26') end end - - ## - # JsonStreamWriter writes a JSON file using a stream. - # By utilizing a stream, we can minimize memory usage, especially for large files. - class JsonStreamWriter - def initialize(path) - @file = File.open(path, "w") - @file.write("{") - @indent_level = 0 - @is_first_key_val = true - @is_first_obj = true - write_new_line - end - - def write_object obj - if @is_first_obj - @is_first_obj = false - else - write_comma - write_new_line - end - @indent_level += 1 - @file.write(to_json_str(obj)) - @indent_level -= 1 - @is_first_key_val = true - # Occasionally, invalid JSON will be created as shown below, especially when `--repeat-count` is specified. - # { - # "testPath": "file=test%2Ftest_timeout.rb&class=TestTimeout&testcase=test_allows_zero_seconds", - # "status": "TEST_PASSED", - # "duration": 2.7e-05, - # "createdAt": "2024-02-09 12:21:07 +0000", - # "stderr": null, - # "stdout": null - # }: null <- here - # }, - # To prevent this, IO#flush is called here. - @file.flush - end - - def write_array(key) - @indent_level += 1 - @file.write(to_json_str(key)) - write_colon - @file.write(" ", "[") - write_new_line - end - - def close - return if @file.closed? - close_array - @indent_level -= 1 - write_new_line - @file.write("}", "\n") - @file.flush - @file.close - end - - private - def to_json_str(obj) - json = JSON.pretty_generate(obj) - json.gsub(/^/, ' ' * (2 * @indent_level)) - end - - def write_indent - @file.write(" " * 2 * @indent_level) - end - - def write_new_line - @file.write("\n") - end - - def write_comma - @file.write(',') - end - - def write_colon - @file.write(":") - end - - def close_array - write_new_line - write_indent - @file.write("]") - @indent_level -= 1 - end - end end class Runner # :nodoc: all diff --git a/tool/lib/webrick.rb b/tool/lib/webrick.rb deleted file mode 100644 index b854b68db4a98a..00000000000000 --- a/tool/lib/webrick.rb +++ /dev/null @@ -1,232 +0,0 @@ -# frozen_string_literal: false -## -# = WEB server toolkit. -# -# WEBrick is an HTTP server toolkit that can be configured as an HTTPS server, -# a proxy server, and a virtual-host server. WEBrick features complete -# logging of both server operations and HTTP access. WEBrick supports both -# basic and digest authentication in addition to algorithms not in RFC 2617. -# -# A WEBrick server can be composed of multiple WEBrick servers or servlets to -# provide differing behavior on a per-host or per-path basis. WEBrick -# includes servlets for handling CGI scripts, ERB pages, Ruby blocks and -# directory listings. -# -# WEBrick also includes tools for daemonizing a process and starting a process -# at a higher privilege level and dropping permissions. -# -# == Security -# -# *Warning:* WEBrick is not recommended for production. It only implements -# basic security checks. -# -# == Starting an HTTP server -# -# To create a new WEBrick::HTTPServer that will listen to connections on port -# 8000 and serve documents from the current user's public_html folder: -# -# require 'webrick' -# -# root = File.expand_path '~/public_html' -# server = WEBrick::HTTPServer.new :Port => 8000, :DocumentRoot => root -# -# To run the server you will need to provide a suitable shutdown hook as -# starting the server blocks the current thread: -# -# trap 'INT' do server.shutdown end -# -# server.start -# -# == Custom Behavior -# -# The easiest way to have a server perform custom operations is through -# WEBrick::HTTPServer#mount_proc. The block given will be called with a -# WEBrick::HTTPRequest with request info and a WEBrick::HTTPResponse which -# must be filled in appropriately: -# -# server.mount_proc '/' do |req, res| -# res.body = 'Hello, world!' -# end -# -# Remember that +server.mount_proc+ must precede +server.start+. -# -# == Servlets -# -# Advanced custom behavior can be obtained through mounting a subclass of -# WEBrick::HTTPServlet::AbstractServlet. Servlets provide more modularity -# when writing an HTTP server than mount_proc allows. Here is a simple -# servlet: -# -# class Simple < WEBrick::HTTPServlet::AbstractServlet -# def do_GET request, response -# status, content_type, body = do_stuff_with request -# -# response.status = 200 -# response['Content-Type'] = 'text/plain' -# response.body = 'Hello, World!' -# end -# end -# -# To initialize the servlet you mount it on the server: -# -# server.mount '/simple', Simple -# -# See WEBrick::HTTPServlet::AbstractServlet for more details. -# -# == Virtual Hosts -# -# A server can act as a virtual host for multiple host names. After creating -# the listening host, additional hosts that do not listen can be created and -# attached as virtual hosts: -# -# server = WEBrick::HTTPServer.new # ... -# -# vhost = WEBrick::HTTPServer.new :ServerName => 'vhost.example', -# :DoNotListen => true, # ... -# vhost.mount '/', ... -# -# server.virtual_host vhost -# -# If no +:DocumentRoot+ is provided and no servlets or procs are mounted on the -# main server it will return 404 for all URLs. -# -# == HTTPS -# -# To create an HTTPS server you only need to enable SSL and provide an SSL -# certificate name: -# -# require 'webrick' -# require 'webrick/https' -# -# cert_name = [ -# %w[CN localhost], -# ] -# -# server = WEBrick::HTTPServer.new(:Port => 8000, -# :SSLEnable => true, -# :SSLCertName => cert_name) -# -# This will start the server with a self-generated self-signed certificate. -# The certificate will be changed every time the server is restarted. -# -# To create a server with a pre-determined key and certificate you can provide -# them: -# -# require 'webrick' -# require 'webrick/https' -# require 'openssl' -# -# cert = OpenSSL::X509::Certificate.new File.read '/path/to/cert.pem' -# pkey = OpenSSL::PKey::RSA.new File.read '/path/to/pkey.pem' -# -# server = WEBrick::HTTPServer.new(:Port => 8000, -# :SSLEnable => true, -# :SSLCertificate => cert, -# :SSLPrivateKey => pkey) -# -# == Proxy Server -# -# WEBrick can act as a proxy server: -# -# require 'webrick' -# require 'webrick/httpproxy' -# -# proxy = WEBrick::HTTPProxyServer.new :Port => 8000 -# -# trap 'INT' do proxy.shutdown end -# -# See WEBrick::HTTPProxy for further details including modifying proxied -# responses. -# -# == Basic and Digest authentication -# -# WEBrick provides both Basic and Digest authentication for regular and proxy -# servers. See WEBrick::HTTPAuth, WEBrick::HTTPAuth::BasicAuth and -# WEBrick::HTTPAuth::DigestAuth. -# -# == WEBrick as a daemonized Web Server -# -# WEBrick can be run as a daemonized server for small loads. -# -# === Daemonizing -# -# To start a WEBrick server as a daemon simple run WEBrick::Daemon.start -# before starting the server. -# -# === Dropping Permissions -# -# WEBrick can be started as one user to gain permission to bind to port 80 or -# 443 for serving HTTP or HTTPS traffic then can drop these permissions for -# regular operation. To listen on all interfaces for HTTP traffic: -# -# sockets = WEBrick::Utils.create_listeners nil, 80 -# -# Then drop privileges: -# -# WEBrick::Utils.su 'www' -# -# Then create a server that does not listen by default: -# -# server = WEBrick::HTTPServer.new :DoNotListen => true, # ... -# -# Then overwrite the listening sockets with the port 80 sockets: -# -# server.listeners.replace sockets -# -# === Logging -# -# WEBrick can separately log server operations and end-user access. For -# server operations: -# -# log_file = File.open '/var/log/webrick.log', 'a+' -# log = WEBrick::Log.new log_file -# -# For user access logging: -# -# access_log = [ -# [log_file, WEBrick::AccessLog::COMBINED_LOG_FORMAT], -# ] -# -# server = WEBrick::HTTPServer.new :Logger => log, :AccessLog => access_log -# -# See WEBrick::AccessLog for further log formats. -# -# === Log Rotation -# -# To rotate logs in WEBrick on a HUP signal (like syslogd can send), open the -# log file in 'a+' mode (as above) and trap 'HUP' to reopen the log file: -# -# trap 'HUP' do log_file.reopen '/path/to/webrick.log', 'a+' -# -# == Copyright -# -# Author: IPR -- Internet Programming with Ruby -- writers -# -# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU YUUZOU -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -#-- -# $IPR: webrick.rb,v 1.12 2002/10/01 17:16:31 gotoyuzo Exp $ - -module WEBrick -end - -require 'webrick/compat.rb' - -require 'webrick/version.rb' -require 'webrick/config.rb' -require 'webrick/log.rb' -require 'webrick/server.rb' -require_relative 'webrick/utils.rb' -require 'webrick/accesslog' - -require 'webrick/htmlutils.rb' -require 'webrick/httputils.rb' -require 'webrick/cookie.rb' -require 'webrick/httpversion.rb' -require 'webrick/httpstatus.rb' -require 'webrick/httprequest.rb' -require 'webrick/httpresponse.rb' -require 'webrick/httpserver.rb' -require 'webrick/httpservlet.rb' -require 'webrick/httpauth.rb' diff --git a/tool/lib/webrick/.document b/tool/lib/webrick/.document deleted file mode 100644 index c62f89083b2e6f..00000000000000 --- a/tool/lib/webrick/.document +++ /dev/null @@ -1,6 +0,0 @@ -# Add files to this as they become documented - -*.rb - -httpauth -httpservlet diff --git a/tool/lib/webrick/accesslog.rb b/tool/lib/webrick/accesslog.rb deleted file mode 100644 index e4849637f35898..00000000000000 --- a/tool/lib/webrick/accesslog.rb +++ /dev/null @@ -1,157 +0,0 @@ -# frozen_string_literal: false -#-- -# accesslog.rb -- Access log handling utilities -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2002 keita yamaguchi -# Copyright (c) 2002 Internet Programming with Ruby writers -# -# $IPR: accesslog.rb,v 1.1 2002/10/01 17:16:32 gotoyuzo Exp $ - -module WEBrick - - ## - # AccessLog provides logging to various files in various formats. - # - # Multiple logs may be written to at the same time: - # - # access_log = [ - # [$stderr, WEBrick::AccessLog::COMMON_LOG_FORMAT], - # [$stderr, WEBrick::AccessLog::REFERER_LOG_FORMAT], - # ] - # - # server = WEBrick::HTTPServer.new :AccessLog => access_log - # - # Custom log formats may be defined. WEBrick::AccessLog provides a subset - # of the formatting from Apache's mod_log_config - # http://httpd.apache.org/docs/mod/mod_log_config.html#formats. See - # AccessLog::setup_params for a list of supported options - - module AccessLog - - ## - # Raised if a parameter such as %e, %i, %o or %n is used without fetching - # a specific field. - - class AccessLogError < StandardError; end - - ## - # The Common Log Format's time format - - CLF_TIME_FORMAT = "[%d/%b/%Y:%H:%M:%S %Z]" - - ## - # Common Log Format - - COMMON_LOG_FORMAT = "%h %l %u %t \"%r\" %s %b" - - ## - # Short alias for Common Log Format - - CLF = COMMON_LOG_FORMAT - - ## - # Referer Log Format - - REFERER_LOG_FORMAT = "%{Referer}i -> %U" - - ## - # User-Agent Log Format - - AGENT_LOG_FORMAT = "%{User-Agent}i" - - ## - # Combined Log Format - - COMBINED_LOG_FORMAT = "#{CLF} \"%{Referer}i\" \"%{User-agent}i\"" - - module_function - - # This format specification is a subset of mod_log_config of Apache: - # - # %a:: Remote IP address - # %b:: Total response size - # %e{variable}:: Given variable in ENV - # %f:: Response filename - # %h:: Remote host name - # %{header}i:: Given request header - # %l:: Remote logname, always "-" - # %m:: Request method - # %{attr}n:: Given request attribute from req.attributes - # %{header}o:: Given response header - # %p:: Server's request port - # %{format}p:: The canonical port of the server serving the request or the - # actual port or the client's actual port. Valid formats are - # canonical, local or remote. - # %q:: Request query string - # %r:: First line of the request - # %s:: Request status - # %t:: Time the request was received - # %T:: Time taken to process the request - # %u:: Remote user from auth - # %U:: Unparsed URI - # %%:: Literal % - - def setup_params(config, req, res) - params = Hash.new("") - params["a"] = req.peeraddr[3] - params["b"] = res.sent_size - params["e"] = ENV - params["f"] = res.filename || "" - params["h"] = req.peeraddr[2] - params["i"] = req - params["l"] = "-" - params["m"] = req.request_method - params["n"] = req.attributes - params["o"] = res - params["p"] = req.port - params["q"] = req.query_string - params["r"] = req.request_line.sub(/\x0d?\x0a\z/o, '') - params["s"] = res.status # won't support "%>s" - params["t"] = req.request_time - params["T"] = Time.now - req.request_time - params["u"] = req.user || "-" - params["U"] = req.unparsed_uri - params["v"] = config[:ServerName] - params - end - - ## - # Formats +params+ according to +format_string+ which is described in - # setup_params. - - def format(format_string, params) - format_string.gsub(/\%(?:\{(.*?)\})?>?([a-zA-Z%])/){ - param, spec = $1, $2 - case spec[0] - when ?e, ?i, ?n, ?o - raise AccessLogError, - "parameter is required for \"#{spec}\"" unless param - (param = params[spec][param]) ? escape(param) : "-" - when ?t - params[spec].strftime(param || CLF_TIME_FORMAT) - when ?p - case param - when 'remote' - escape(params["i"].peeraddr[1].to_s) - else - escape(params["p"].to_s) - end - when ?% - "%" - else - escape(params[spec].to_s) - end - } - end - - ## - # Escapes control characters in +data+ - - def escape(data) - data = data.gsub(/[[:cntrl:]\\]+/) {$&.dump[1...-1]} - data.untaint if RUBY_VERSION < '2.7' - data - end - end -end diff --git a/tool/lib/webrick/cgi.rb b/tool/lib/webrick/cgi.rb deleted file mode 100644 index bb0ae2fc844214..00000000000000 --- a/tool/lib/webrick/cgi.rb +++ /dev/null @@ -1,313 +0,0 @@ -# frozen_string_literal: false -# -# cgi.rb -- Yet another CGI library -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2003 Internet Programming with Ruby writers. All rights -# reserved. -# -# $Id$ - -require_relative "httprequest" -require_relative "httpresponse" -require_relative "config" -require "stringio" - -module WEBrick - - # A CGI library using WEBrick requests and responses. - # - # Example: - # - # class MyCGI < WEBrick::CGI - # def do_GET req, res - # res.body = 'it worked!' - # res.status = 200 - # end - # end - # - # MyCGI.new.start - - class CGI - - # The CGI error exception class - - CGIError = Class.new(StandardError) - - ## - # The CGI configuration. This is based on WEBrick::Config::HTTP - - attr_reader :config - - ## - # The CGI logger - - attr_reader :logger - - ## - # Creates a new CGI interface. - # - # The first argument in +args+ is a configuration hash which would update - # WEBrick::Config::HTTP. - # - # Any remaining arguments are stored in the @options instance - # variable for use by a subclass. - - def initialize(*args) - if defined?(MOD_RUBY) - unless ENV.has_key?("GATEWAY_INTERFACE") - Apache.request.setup_cgi_env - end - end - if %r{HTTP/(\d+\.\d+)} =~ ENV["SERVER_PROTOCOL"] - httpv = $1 - end - @config = WEBrick::Config::HTTP.dup.update( - :ServerSoftware => ENV["SERVER_SOFTWARE"] || "null", - :HTTPVersion => HTTPVersion.new(httpv || "1.0"), - :RunOnCGI => true, # to detect if it runs on CGI. - :NPH => false # set true to run as NPH script. - ) - if config = args.shift - @config.update(config) - end - @config[:Logger] ||= WEBrick::BasicLog.new($stderr) - @logger = @config[:Logger] - @options = args - end - - ## - # Reads +key+ from the configuration - - def [](key) - @config[key] - end - - ## - # Starts the CGI process with the given environment +env+ and standard - # input and output +stdin+ and +stdout+. - - def start(env=ENV, stdin=$stdin, stdout=$stdout) - sock = WEBrick::CGI::Socket.new(@config, env, stdin, stdout) - req = HTTPRequest.new(@config) - res = HTTPResponse.new(@config) - unless @config[:NPH] or defined?(MOD_RUBY) - def res.setup_header - unless @header["status"] - phrase = HTTPStatus::reason_phrase(@status) - @header["status"] = "#{@status} #{phrase}" - end - super - end - def res.status_line - "" - end - end - - begin - req.parse(sock) - req.script_name = (env["SCRIPT_NAME"] || File.expand_path($0)).dup - req.path_info = (env["PATH_INFO"] || "").dup - req.query_string = env["QUERY_STRING"] - req.user = env["REMOTE_USER"] - res.request_method = req.request_method - res.request_uri = req.request_uri - res.request_http_version = req.http_version - res.keep_alive = req.keep_alive? - self.service(req, res) - rescue HTTPStatus::Error => ex - res.set_error(ex) - rescue HTTPStatus::Status => ex - res.status = ex.code - rescue Exception => ex - @logger.error(ex) - res.set_error(ex, true) - ensure - req.fixup - if defined?(MOD_RUBY) - res.setup_header - Apache.request.status_line = "#{res.status} #{res.reason_phrase}" - Apache.request.status = res.status - table = Apache.request.headers_out - res.header.each{|key, val| - case key - when /^content-encoding$/i - Apache::request.content_encoding = val - when /^content-type$/i - Apache::request.content_type = val - else - table[key] = val.to_s - end - } - res.cookies.each{|cookie| - table.add("Set-Cookie", cookie.to_s) - } - Apache.request.send_http_header - res.send_body(sock) - else - res.send_response(sock) - end - end - end - - ## - # Services the request +req+ which will fill in the response +res+. See - # WEBrick::HTTPServlet::AbstractServlet#service for details. - - def service(req, res) - method_name = "do_" + req.request_method.gsub(/-/, "_") - if respond_to?(method_name) - __send__(method_name, req, res) - else - raise HTTPStatus::MethodNotAllowed, - "unsupported method `#{req.request_method}'." - end - end - - ## - # Provides HTTP socket emulation from the CGI environment - - class Socket # :nodoc: - include Enumerable - - private - - def initialize(config, env, stdin, stdout) - @config = config - @env = env - @header_part = StringIO.new - @body_part = stdin - @out_port = stdout - @out_port.binmode - - @server_addr = @env["SERVER_ADDR"] || "0.0.0.0" - @server_name = @env["SERVER_NAME"] - @server_port = @env["SERVER_PORT"] - @remote_addr = @env["REMOTE_ADDR"] - @remote_host = @env["REMOTE_HOST"] || @remote_addr - @remote_port = @env["REMOTE_PORT"] || 0 - - begin - @header_part << request_line << CRLF - setup_header - @header_part << CRLF - @header_part.rewind - rescue Exception - raise CGIError, "invalid CGI environment" - end - end - - def request_line - meth = @env["REQUEST_METHOD"] || "GET" - unless url = @env["REQUEST_URI"] - url = (@env["SCRIPT_NAME"] || File.expand_path($0)).dup - url << @env["PATH_INFO"].to_s - url = WEBrick::HTTPUtils.escape_path(url) - if query_string = @env["QUERY_STRING"] - unless query_string.empty? - url << "?" << query_string - end - end - end - # we cannot get real HTTP version of client ;) - httpv = @config[:HTTPVersion] - return "#{meth} #{url} HTTP/#{httpv}" - end - - def setup_header - @env.each{|key, value| - case key - when "CONTENT_TYPE", "CONTENT_LENGTH" - add_header(key.gsub(/_/, "-"), value) - when /^HTTP_(.*)/ - add_header($1.gsub(/_/, "-"), value) - end - } - end - - def add_header(hdrname, value) - unless value.empty? - @header_part << hdrname << ": " << value << CRLF - end - end - - def input - @header_part.eof? ? @body_part : @header_part - end - - public - - def peeraddr - [nil, @remote_port, @remote_host, @remote_addr] - end - - def addr - [nil, @server_port, @server_name, @server_addr] - end - - def gets(eol=LF, size=nil) - input.gets(eol, size) - end - - def read(size=nil) - input.read(size) - end - - def each - input.each{|line| yield(line) } - end - - def eof? - input.eof? - end - - def <<(data) - @out_port << data - end - - def write(data) - @out_port.write(data) - end - - def cert - return nil unless defined?(OpenSSL) - if pem = @env["SSL_SERVER_CERT"] - OpenSSL::X509::Certificate.new(pem) unless pem.empty? - end - end - - def peer_cert - return nil unless defined?(OpenSSL) - if pem = @env["SSL_CLIENT_CERT"] - OpenSSL::X509::Certificate.new(pem) unless pem.empty? - end - end - - def peer_cert_chain - return nil unless defined?(OpenSSL) - if @env["SSL_CLIENT_CERT_CHAIN_0"] - keys = @env.keys - certs = keys.sort.collect{|k| - if /^SSL_CLIENT_CERT_CHAIN_\d+$/ =~ k - if pem = @env[k] - OpenSSL::X509::Certificate.new(pem) unless pem.empty? - end - end - } - certs.compact - end - end - - def cipher - return nil unless defined?(OpenSSL) - if cipher = @env["SSL_CIPHER"] - ret = [ cipher ] - ret << @env["SSL_PROTOCOL"] - ret << @env["SSL_CIPHER_USEKEYSIZE"] - ret << @env["SSL_CIPHER_ALGKEYSIZE"] - ret - end - end - end - end -end diff --git a/tool/lib/webrick/compat.rb b/tool/lib/webrick/compat.rb deleted file mode 100644 index c497a1933c92af..00000000000000 --- a/tool/lib/webrick/compat.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: false -# -# compat.rb -- cross platform compatibility -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2002 GOTOU Yuuzou -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: compat.rb,v 1.6 2002/10/01 17:16:32 gotoyuzo Exp $ - -## -# System call error module used by webrick for cross platform compatibility. -# -# EPROTO:: protocol error -# ECONNRESET:: remote host reset the connection request -# ECONNABORTED:: Client sent TCP reset (RST) before server has accepted the -# connection requested by client. -# -module Errno - ## - # Protocol error. - - class EPROTO < SystemCallError; end - - ## - # Remote host reset the connection request. - - class ECONNRESET < SystemCallError; end - - ## - # Client sent TCP reset (RST) before server has accepted the connection - # requested by client. - - class ECONNABORTED < SystemCallError; end -end diff --git a/tool/lib/webrick/config.rb b/tool/lib/webrick/config.rb deleted file mode 100644 index 9f2ab44f49d80c..00000000000000 --- a/tool/lib/webrick/config.rb +++ /dev/null @@ -1,158 +0,0 @@ -# frozen_string_literal: false -# -# config.rb -- Default configurations. -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2003 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: config.rb,v 1.52 2003/07/22 19:20:42 gotoyuzo Exp $ - -require_relative 'version' -require_relative 'httpversion' -require_relative 'httputils' -require_relative 'utils' -require_relative 'log' - -module WEBrick - module Config - LIBDIR = File::dirname(__FILE__) # :nodoc: - - # for GenericServer - General = Hash.new { |hash, key| - case key - when :ServerName - hash[key] = Utils.getservername - else - nil - end - }.update( - :BindAddress => nil, # "0.0.0.0" or "::" or nil - :Port => nil, # users MUST specify this!! - :MaxClients => 100, # maximum number of the concurrent connections - :ServerType => nil, # default: WEBrick::SimpleServer - :Logger => nil, # default: WEBrick::Log.new - :ServerSoftware => "WEBrick/#{WEBrick::VERSION} " + - "(Ruby/#{RUBY_VERSION}/#{RUBY_RELEASE_DATE})", - :TempDir => ENV['TMPDIR']||ENV['TMP']||ENV['TEMP']||'/tmp', - :DoNotListen => false, - :StartCallback => nil, - :StopCallback => nil, - :AcceptCallback => nil, - :DoNotReverseLookup => true, - :ShutdownSocketWithoutClose => false, - ) - - # for HTTPServer, HTTPRequest, HTTPResponse ... - HTTP = General.dup.update( - :Port => 80, - :RequestTimeout => 30, - :HTTPVersion => HTTPVersion.new("1.1"), - :AccessLog => nil, - :MimeTypes => HTTPUtils::DefaultMimeTypes, - :DirectoryIndex => ["index.html","index.htm","index.cgi","index.rhtml"], - :DocumentRoot => nil, - :DocumentRootOptions => { :FancyIndexing => true }, - :RequestCallback => nil, - :ServerAlias => nil, - :InputBufferSize => 65536, # input buffer size in reading request body - :OutputBufferSize => 65536, # output buffer size in sending File or IO - - # for HTTPProxyServer - :ProxyAuthProc => nil, - :ProxyContentHandler => nil, - :ProxyVia => true, - :ProxyTimeout => true, - :ProxyURI => nil, - - :CGIInterpreter => nil, - :CGIPathEnv => nil, - - # workaround: if Request-URIs contain 8bit chars, - # they should be escaped before calling of URI::parse(). - :Escape8bitURI => false - ) - - ## - # Default configuration for WEBrick::HTTPServlet::FileHandler - # - # :AcceptableLanguages:: - # Array of languages allowed for accept-language. There is no default - # :DirectoryCallback:: - # Allows preprocessing of directory requests. There is no default - # callback. - # :FancyIndexing:: - # If true, show an index for directories. The default is true. - # :FileCallback:: - # Allows preprocessing of file requests. There is no default callback. - # :HandlerCallback:: - # Allows preprocessing of requests. There is no default callback. - # :HandlerTable:: - # Maps file suffixes to file handlers. DefaultFileHandler is used by - # default but any servlet can be used. - # :NondisclosureName:: - # Do not show files matching this array of globs. .ht* and *~ are - # excluded by default. - # :UserDir:: - # Directory inside ~user to serve content from for /~user requests. - # Only works if mounted on /. Disabled by default. - - FileHandler = { - :NondisclosureName => [".ht*", "*~"], - :FancyIndexing => false, - :HandlerTable => {}, - :HandlerCallback => nil, - :DirectoryCallback => nil, - :FileCallback => nil, - :UserDir => nil, # e.g. "public_html" - :AcceptableLanguages => [] # ["en", "ja", ... ] - } - - ## - # Default configuration for WEBrick::HTTPAuth::BasicAuth - # - # :AutoReloadUserDB:: Reload the user database provided by :UserDB - # automatically? - - BasicAuth = { - :AutoReloadUserDB => true, - } - - ## - # Default configuration for WEBrick::HTTPAuth::DigestAuth. - # - # :Algorithm:: MD5, MD5-sess (default), SHA1, SHA1-sess - # :Domain:: An Array of URIs that define the protected space - # :Qop:: 'auth' for authentication, 'auth-int' for integrity protection or - # both - # :UseOpaque:: Should the server send opaque values to the client? This - # helps prevent replay attacks. - # :CheckNc:: Should the server check the nonce count? This helps the - # server detect replay attacks. - # :UseAuthenticationInfoHeader:: Should the server send an - # AuthenticationInfo header? - # :AutoReloadUserDB:: Reload the user database provided by :UserDB - # automatically? - # :NonceExpirePeriod:: How long should we store used nonces? Default is - # 30 minutes. - # :NonceExpireDelta:: How long is a nonce valid? Default is 1 minute - # :InternetExplorerHack:: Hack which allows Internet Explorer to work. - # :OperaHack:: Hack which allows Opera to work. - - DigestAuth = { - :Algorithm => 'MD5-sess', # or 'MD5' - :Domain => nil, # an array includes domain names. - :Qop => [ 'auth' ], # 'auth' or 'auth-int' or both. - :UseOpaque => true, - :UseNextNonce => false, - :CheckNc => false, - :UseAuthenticationInfoHeader => true, - :AutoReloadUserDB => true, - :NonceExpirePeriod => 30*60, - :NonceExpireDelta => 60, - :InternetExplorerHack => true, - :OperaHack => true, - } - end -end diff --git a/tool/lib/webrick/cookie.rb b/tool/lib/webrick/cookie.rb deleted file mode 100644 index 5fd3bfb22862ee..00000000000000 --- a/tool/lib/webrick/cookie.rb +++ /dev/null @@ -1,172 +0,0 @@ -# frozen_string_literal: false -# -# cookie.rb -- Cookie class -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: cookie.rb,v 1.16 2002/09/21 12:23:35 gotoyuzo Exp $ - -require 'time' -require_relative 'httputils' - -module WEBrick - - ## - # Processes HTTP cookies - - class Cookie - - ## - # The cookie name - - attr_reader :name - - ## - # The cookie value - - attr_accessor :value - - ## - # The cookie version - - attr_accessor :version - - ## - # The cookie domain - attr_accessor :domain - - ## - # The cookie path - - attr_accessor :path - - ## - # Is this a secure cookie? - - attr_accessor :secure - - ## - # The cookie comment - - attr_accessor :comment - - ## - # The maximum age of the cookie - - attr_accessor :max_age - - #attr_accessor :comment_url, :discard, :port - - ## - # Creates a new cookie with the given +name+ and +value+ - - def initialize(name, value) - @name = name - @value = value - @version = 0 # Netscape Cookie - - @domain = @path = @secure = @comment = @max_age = - @expires = @comment_url = @discard = @port = nil - end - - ## - # Sets the cookie expiration to the time +t+. The expiration time may be - # a false value to disable expiration or a Time or HTTP format time string - # to set the expiration date. - - def expires=(t) - @expires = t && (t.is_a?(Time) ? t.httpdate : t.to_s) - end - - ## - # Retrieves the expiration time as a Time - - def expires - @expires && Time.parse(@expires) - end - - ## - # The cookie string suitable for use in an HTTP header - - def to_s - ret = "" - ret << @name << "=" << @value - ret << "; " << "Version=" << @version.to_s if @version > 0 - ret << "; " << "Domain=" << @domain if @domain - ret << "; " << "Expires=" << @expires if @expires - ret << "; " << "Max-Age=" << @max_age.to_s if @max_age - ret << "; " << "Comment=" << @comment if @comment - ret << "; " << "Path=" << @path if @path - ret << "; " << "Secure" if @secure - ret - end - - ## - # Parses a Cookie field sent from the user-agent. Returns an array of - # cookies. - - def self.parse(str) - if str - ret = [] - cookie = nil - ver = 0 - str.split(/;\s+/).each{|x| - key, val = x.split(/=/,2) - val = val ? HTTPUtils::dequote(val) : "" - case key - when "$Version"; ver = val.to_i - when "$Path"; cookie.path = val - when "$Domain"; cookie.domain = val - when "$Port"; cookie.port = val - else - ret << cookie if cookie - cookie = self.new(key, val) - cookie.version = ver - end - } - ret << cookie if cookie - ret - end - end - - ## - # Parses the cookie in +str+ - - def self.parse_set_cookie(str) - cookie_elem = str.split(/;/) - first_elem = cookie_elem.shift - first_elem.strip! - key, value = first_elem.split(/=/, 2) - cookie = new(key, HTTPUtils.dequote(value)) - cookie_elem.each{|pair| - pair.strip! - key, value = pair.split(/=/, 2) - if value - value = HTTPUtils.dequote(value.strip) - end - case key.downcase - when "domain" then cookie.domain = value - when "path" then cookie.path = value - when "expires" then cookie.expires = value - when "max-age" then cookie.max_age = Integer(value) - when "comment" then cookie.comment = value - when "version" then cookie.version = Integer(value) - when "secure" then cookie.secure = true - end - } - return cookie - end - - ## - # Parses the cookies in +str+ - - def self.parse_set_cookies(str) - return str.split(/,(?=[^;,]*=)|,$/).collect{|c| - parse_set_cookie(c) - } - end - end -end diff --git a/tool/lib/webrick/htmlutils.rb b/tool/lib/webrick/htmlutils.rb deleted file mode 100644 index ed9f4ac0d3e97c..00000000000000 --- a/tool/lib/webrick/htmlutils.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: false -#-- -# htmlutils.rb -- HTMLUtils Module -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: htmlutils.rb,v 1.7 2002/09/21 12:23:35 gotoyuzo Exp $ - -module WEBrick - module HTMLUtils - - ## - # Escapes &, ", > and < in +string+ - - def escape(string) - return "" unless string - str = string.b - str.gsub!(/&/n, '&') - str.gsub!(/\"/n, '"') - str.gsub!(/>/n, '>') - str.gsub!(/ 'DigestAuth example realm' } - # - # htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file' - # htpasswd.auth_type = WEBrick::HTTPAuth::DigestAuth - # htpasswd.set_passwd config[:Realm], 'username', 'password' - # htpasswd.flush - # - # The +:Realm+ is used to provide different access to different groups - # across several resources on a server. Typically you'll need only one - # realm for a server. - # - # This database can be used to create an authenticator: - # - # config[:UserDB] = htpasswd - # - # digest_auth = WEBrick::HTTPAuth::DigestAuth.new config - # - # To authenticate a request call #authenticate with a request and response - # object in a servlet: - # - # def do_GET req, res - # @authenticator.authenticate req, res - # end - # - # For digest authentication the authenticator must not be created every - # request, it must be passed in as an option via WEBrick::HTTPServer#mount. - - module HTTPAuth - module_function - - def _basic_auth(req, res, realm, req_field, res_field, err_type, - block) # :nodoc: - user = pass = nil - if /^Basic\s+(.*)/o =~ req[req_field] - userpass = $1 - user, pass = userpass.unpack("m*")[0].split(":", 2) - end - if block.call(user, pass) - req.user = user - return - end - res[res_field] = "Basic realm=\"#{realm}\"" - raise err_type - end - - ## - # Simple wrapper for providing basic authentication for a request. When - # called with a request +req+, response +res+, authentication +realm+ and - # +block+ the block will be called with a +username+ and +password+. If - # the block returns true the request is allowed to continue, otherwise an - # HTTPStatus::Unauthorized error is raised. - - def basic_auth(req, res, realm, &block) # :yield: username, password - _basic_auth(req, res, realm, "Authorization", "WWW-Authenticate", - HTTPStatus::Unauthorized, block) - end - - ## - # Simple wrapper for providing basic authentication for a proxied request. - # When called with a request +req+, response +res+, authentication +realm+ - # and +block+ the block will be called with a +username+ and +password+. - # If the block returns true the request is allowed to continue, otherwise - # an HTTPStatus::ProxyAuthenticationRequired error is raised. - - def proxy_basic_auth(req, res, realm, &block) # :yield: username, password - _basic_auth(req, res, realm, "Proxy-Authorization", "Proxy-Authenticate", - HTTPStatus::ProxyAuthenticationRequired, block) - end - end -end diff --git a/tool/lib/webrick/httpauth/authenticator.rb b/tool/lib/webrick/httpauth/authenticator.rb deleted file mode 100644 index 8f0eaa3acaef2f..00000000000000 --- a/tool/lib/webrick/httpauth/authenticator.rb +++ /dev/null @@ -1,117 +0,0 @@ -# frozen_string_literal: false -#-- -# httpauth/authenticator.rb -- Authenticator mix-in module. -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2003 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: authenticator.rb,v 1.3 2003/02/20 07:15:47 gotoyuzo Exp $ - -module WEBrick - module HTTPAuth - - ## - # Module providing generic support for both Digest and Basic - # authentication schemes. - - module Authenticator - - RequestField = "Authorization" # :nodoc: - ResponseField = "WWW-Authenticate" # :nodoc: - ResponseInfoField = "Authentication-Info" # :nodoc: - AuthException = HTTPStatus::Unauthorized # :nodoc: - - ## - # Method of authentication, must be overridden by the including class - - AuthScheme = nil - - ## - # The realm this authenticator covers - - attr_reader :realm - - ## - # The user database for this authenticator - - attr_reader :userdb - - ## - # The logger for this authenticator - - attr_reader :logger - - private - - # :stopdoc: - - ## - # Initializes the authenticator from +config+ - - def check_init(config) - [:UserDB, :Realm].each{|sym| - unless config[sym] - raise ArgumentError, "Argument #{sym.inspect} missing." - end - } - @realm = config[:Realm] - @userdb = config[:UserDB] - @logger = config[:Logger] || Log::new($stderr) - @reload_db = config[:AutoReloadUserDB] - @request_field = self::class::RequestField - @response_field = self::class::ResponseField - @resp_info_field = self::class::ResponseInfoField - @auth_exception = self::class::AuthException - @auth_scheme = self::class::AuthScheme - end - - ## - # Ensures +req+ has credentials that can be authenticated. - - def check_scheme(req) - unless credentials = req[@request_field] - error("no credentials in the request.") - return nil - end - unless match = /^#{@auth_scheme}\s+/i.match(credentials) - error("invalid scheme in %s.", credentials) - info("%s: %s", @request_field, credentials) if $DEBUG - return nil - end - return match.post_match - end - - def log(meth, fmt, *args) - msg = format("%s %s: ", @auth_scheme, @realm) - msg << fmt % args - @logger.__send__(meth, msg) - end - - def error(fmt, *args) - if @logger.error? - log(:error, fmt, *args) - end - end - - def info(fmt, *args) - if @logger.info? - log(:info, fmt, *args) - end - end - - # :startdoc: - end - - ## - # Module providing generic support for both Digest and Basic - # authentication schemes for proxies. - - module ProxyAuthenticator - RequestField = "Proxy-Authorization" # :nodoc: - ResponseField = "Proxy-Authenticate" # :nodoc: - InfoField = "Proxy-Authentication-Info" # :nodoc: - AuthException = HTTPStatus::ProxyAuthenticationRequired # :nodoc: - end - end -end diff --git a/tool/lib/webrick/httpauth/basicauth.rb b/tool/lib/webrick/httpauth/basicauth.rb deleted file mode 100644 index 7d0a9cfc8fd43d..00000000000000 --- a/tool/lib/webrick/httpauth/basicauth.rb +++ /dev/null @@ -1,116 +0,0 @@ -# frozen_string_literal: false -# -# httpauth/basicauth.rb -- HTTP basic access authentication -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2003 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: basicauth.rb,v 1.5 2003/02/20 07:15:47 gotoyuzo Exp $ - -require_relative '../config' -require_relative '../httpstatus' -require_relative 'authenticator' - -module WEBrick - module HTTPAuth - - ## - # Basic Authentication for WEBrick - # - # Use this class to add basic authentication to a WEBrick servlet. - # - # Here is an example of how to set up a BasicAuth: - # - # config = { :Realm => 'BasicAuth example realm' } - # - # htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file', password_hash: :bcrypt - # htpasswd.set_passwd config[:Realm], 'username', 'password' - # htpasswd.flush - # - # config[:UserDB] = htpasswd - # - # basic_auth = WEBrick::HTTPAuth::BasicAuth.new config - - class BasicAuth - include Authenticator - - AuthScheme = "Basic" # :nodoc: - - ## - # Used by UserDB to create a basic password entry - - def self.make_passwd(realm, user, pass) - pass ||= "" - pass.crypt(Utils::random_string(2)) - end - - attr_reader :realm, :userdb, :logger - - ## - # Creates a new BasicAuth instance. - # - # See WEBrick::Config::BasicAuth for default configuration entries - # - # You must supply the following configuration entries: - # - # :Realm:: The name of the realm being protected. - # :UserDB:: A database of usernames and passwords. - # A WEBrick::HTTPAuth::Htpasswd instance should be used. - - def initialize(config, default=Config::BasicAuth) - check_init(config) - @config = default.dup.update(config) - end - - ## - # Authenticates a +req+ and returns a 401 Unauthorized using +res+ if - # the authentication was not correct. - - def authenticate(req, res) - unless basic_credentials = check_scheme(req) - challenge(req, res) - end - userid, password = basic_credentials.unpack("m*")[0].split(":", 2) - password ||= "" - if userid.empty? - error("user id was not given.") - challenge(req, res) - end - unless encpass = @userdb.get_passwd(@realm, userid, @reload_db) - error("%s: the user is not allowed.", userid) - challenge(req, res) - end - - case encpass - when /\A\$2[aby]\$/ - password_matches = BCrypt::Password.new(encpass.sub(/\A\$2[aby]\$/, '$2a$')) == password - else - password_matches = password.crypt(encpass) == encpass - end - - unless password_matches - error("%s: password unmatch.", userid) - challenge(req, res) - end - info("%s: authentication succeeded.", userid) - req.user = userid - end - - ## - # Returns a challenge response which asks for authentication information - - def challenge(req, res) - res[@response_field] = "#{@auth_scheme} realm=\"#{@realm}\"" - raise @auth_exception - end - end - - ## - # Basic authentication for proxy servers. See BasicAuth for details. - - class ProxyBasicAuth < BasicAuth - include ProxyAuthenticator - end - end -end diff --git a/tool/lib/webrick/httpauth/digestauth.rb b/tool/lib/webrick/httpauth/digestauth.rb deleted file mode 100644 index 3cf12899d2f2ae..00000000000000 --- a/tool/lib/webrick/httpauth/digestauth.rb +++ /dev/null @@ -1,395 +0,0 @@ -# frozen_string_literal: false -# -# httpauth/digestauth.rb -- HTTP digest access authentication -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2003 Internet Programming with Ruby writers. -# Copyright (c) 2003 H.M. -# -# The original implementation is provided by H.M. -# URL: http://rwiki.jin.gr.jp/cgi-bin/rw-cgi.rb?cmd=view;name= -# %C7%A7%BE%DA%B5%A1%C7%BD%A4%F2%B2%FE%C2%A4%A4%B7%A4%C6%A4%DF%A4%EB -# -# $IPR: digestauth.rb,v 1.5 2003/02/20 07:15:47 gotoyuzo Exp $ - -require_relative '../config' -require_relative '../httpstatus' -require_relative 'authenticator' -require 'digest/md5' -require 'digest/sha1' - -module WEBrick - module HTTPAuth - - ## - # RFC 2617 Digest Access Authentication for WEBrick - # - # Use this class to add digest authentication to a WEBrick servlet. - # - # Here is an example of how to set up DigestAuth: - # - # config = { :Realm => 'DigestAuth example realm' } - # - # htdigest = WEBrick::HTTPAuth::Htdigest.new 'my_password_file' - # htdigest.set_passwd config[:Realm], 'username', 'password' - # htdigest.flush - # - # config[:UserDB] = htdigest - # - # digest_auth = WEBrick::HTTPAuth::DigestAuth.new config - # - # When using this as with a servlet be sure not to create a new DigestAuth - # object in the servlet's #initialize. By default WEBrick creates a new - # servlet instance for every request and the DigestAuth object must be - # used across requests. - - class DigestAuth - include Authenticator - - AuthScheme = "Digest" # :nodoc: - - ## - # Struct containing the opaque portion of the digest authentication - - OpaqueInfo = Struct.new(:time, :nonce, :nc) # :nodoc: - - ## - # Digest authentication algorithm - - attr_reader :algorithm - - ## - # Quality of protection. RFC 2617 defines "auth" and "auth-int" - - attr_reader :qop - - ## - # Used by UserDB to create a digest password entry - - def self.make_passwd(realm, user, pass) - pass ||= "" - Digest::MD5::hexdigest([user, realm, pass].join(":")) - end - - ## - # Creates a new DigestAuth instance. Be sure to use the same DigestAuth - # instance for multiple requests as it saves state between requests in - # order to perform authentication. - # - # See WEBrick::Config::DigestAuth for default configuration entries - # - # You must supply the following configuration entries: - # - # :Realm:: The name of the realm being protected. - # :UserDB:: A database of usernames and passwords. - # A WEBrick::HTTPAuth::Htdigest instance should be used. - - def initialize(config, default=Config::DigestAuth) - check_init(config) - @config = default.dup.update(config) - @algorithm = @config[:Algorithm] - @domain = @config[:Domain] - @qop = @config[:Qop] - @use_opaque = @config[:UseOpaque] - @use_next_nonce = @config[:UseNextNonce] - @check_nc = @config[:CheckNc] - @use_auth_info_header = @config[:UseAuthenticationInfoHeader] - @nonce_expire_period = @config[:NonceExpirePeriod] - @nonce_expire_delta = @config[:NonceExpireDelta] - @internet_explorer_hack = @config[:InternetExplorerHack] - - case @algorithm - when 'MD5','MD5-sess' - @h = Digest::MD5 - when 'SHA1','SHA1-sess' # it is a bonus feature :-) - @h = Digest::SHA1 - else - msg = format('Algorithm "%s" is not supported.', @algorithm) - raise ArgumentError.new(msg) - end - - @instance_key = hexdigest(self.__id__, Time.now.to_i, Process.pid) - @opaques = {} - @last_nonce_expire = Time.now - @mutex = Thread::Mutex.new - end - - ## - # Authenticates a +req+ and returns a 401 Unauthorized using +res+ if - # the authentication was not correct. - - def authenticate(req, res) - unless result = @mutex.synchronize{ _authenticate(req, res) } - challenge(req, res) - end - if result == :nonce_is_stale - challenge(req, res, true) - end - return true - end - - ## - # Returns a challenge response which asks for authentication information - - def challenge(req, res, stale=false) - nonce = generate_next_nonce(req) - if @use_opaque - opaque = generate_opaque(req) - @opaques[opaque].nonce = nonce - end - - param = Hash.new - param["realm"] = HTTPUtils::quote(@realm) - param["domain"] = HTTPUtils::quote(@domain.to_a.join(" ")) if @domain - param["nonce"] = HTTPUtils::quote(nonce) - param["opaque"] = HTTPUtils::quote(opaque) if opaque - param["stale"] = stale.to_s - param["algorithm"] = @algorithm - param["qop"] = HTTPUtils::quote(@qop.to_a.join(",")) if @qop - - res[@response_field] = - "#{@auth_scheme} " + param.map{|k,v| "#{k}=#{v}" }.join(", ") - info("%s: %s", @response_field, res[@response_field]) if $DEBUG - raise @auth_exception - end - - private - - # :stopdoc: - - MustParams = ['username','realm','nonce','uri','response'] - MustParamsAuth = ['cnonce','nc'] - - def _authenticate(req, res) - unless digest_credentials = check_scheme(req) - return false - end - - auth_req = split_param_value(digest_credentials) - if auth_req['qop'] == "auth" || auth_req['qop'] == "auth-int" - req_params = MustParams + MustParamsAuth - else - req_params = MustParams - end - req_params.each{|key| - unless auth_req.has_key?(key) - error('%s: parameter missing. "%s"', auth_req['username'], key) - raise HTTPStatus::BadRequest - end - } - - if !check_uri(req, auth_req) - raise HTTPStatus::BadRequest - end - - if auth_req['realm'] != @realm - error('%s: realm unmatch. "%s" for "%s"', - auth_req['username'], auth_req['realm'], @realm) - return false - end - - auth_req['algorithm'] ||= 'MD5' - if auth_req['algorithm'].upcase != @algorithm.upcase - error('%s: algorithm unmatch. "%s" for "%s"', - auth_req['username'], auth_req['algorithm'], @algorithm) - return false - end - - if (@qop.nil? && auth_req.has_key?('qop')) || - (@qop && (! @qop.member?(auth_req['qop']))) - error('%s: the qop is not allowed. "%s"', - auth_req['username'], auth_req['qop']) - return false - end - - password = @userdb.get_passwd(@realm, auth_req['username'], @reload_db) - unless password - error('%s: the user is not allowed.', auth_req['username']) - return false - end - - nonce_is_invalid = false - if @use_opaque - info("@opaque = %s", @opaque.inspect) if $DEBUG - if !(opaque = auth_req['opaque']) - error('%s: opaque is not given.', auth_req['username']) - nonce_is_invalid = true - elsif !(opaque_struct = @opaques[opaque]) - error('%s: invalid opaque is given.', auth_req['username']) - nonce_is_invalid = true - elsif !check_opaque(opaque_struct, req, auth_req) - @opaques.delete(auth_req['opaque']) - nonce_is_invalid = true - end - elsif !check_nonce(req, auth_req) - nonce_is_invalid = true - end - - if /-sess$/i =~ auth_req['algorithm'] - ha1 = hexdigest(password, auth_req['nonce'], auth_req['cnonce']) - else - ha1 = password - end - - if auth_req['qop'] == "auth" || auth_req['qop'] == nil - ha2 = hexdigest(req.request_method, auth_req['uri']) - ha2_res = hexdigest("", auth_req['uri']) - elsif auth_req['qop'] == "auth-int" - body_digest = @h.new - req.body { |chunk| body_digest.update(chunk) } - body_digest = body_digest.hexdigest - ha2 = hexdigest(req.request_method, auth_req['uri'], body_digest) - ha2_res = hexdigest("", auth_req['uri'], body_digest) - end - - if auth_req['qop'] == "auth" || auth_req['qop'] == "auth-int" - param2 = ['nonce', 'nc', 'cnonce', 'qop'].map{|key| - auth_req[key] - }.join(':') - digest = hexdigest(ha1, param2, ha2) - digest_res = hexdigest(ha1, param2, ha2_res) - else - digest = hexdigest(ha1, auth_req['nonce'], ha2) - digest_res = hexdigest(ha1, auth_req['nonce'], ha2_res) - end - - if digest != auth_req['response'] - error("%s: digest unmatch.", auth_req['username']) - return false - elsif nonce_is_invalid - error('%s: digest is valid, but nonce is not valid.', - auth_req['username']) - return :nonce_is_stale - elsif @use_auth_info_header - auth_info = { - 'nextnonce' => generate_next_nonce(req), - 'rspauth' => digest_res - } - if @use_opaque - opaque_struct.time = req.request_time - opaque_struct.nonce = auth_info['nextnonce'] - opaque_struct.nc = "%08x" % (auth_req['nc'].hex + 1) - end - if auth_req['qop'] == "auth" || auth_req['qop'] == "auth-int" - ['qop','cnonce','nc'].each{|key| - auth_info[key] = auth_req[key] - } - end - res[@resp_info_field] = auth_info.keys.map{|key| - if key == 'nc' - key + '=' + auth_info[key] - else - key + "=" + HTTPUtils::quote(auth_info[key]) - end - }.join(', ') - end - info('%s: authentication succeeded.', auth_req['username']) - req.user = auth_req['username'] - return true - end - - def split_param_value(string) - ret = {} - string.scan(/\G\s*([\w\-.*%!]+)=\s*(?:\"((?>\\.|[^\"])*)\"|([^,\"]*))\s*,?/) do - ret[$1] = $3 || $2.gsub(/\\(.)/, "\\1") - end - ret - end - - def generate_next_nonce(req) - now = "%012d" % req.request_time.to_i - pk = hexdigest(now, @instance_key)[0,32] - nonce = [now + ":" + pk].pack("m0") # it has 60 length of chars. - nonce - end - - def check_nonce(req, auth_req) - username = auth_req['username'] - nonce = auth_req['nonce'] - - pub_time, pk = nonce.unpack("m*")[0].split(":", 2) - if (!pub_time || !pk) - error("%s: empty nonce is given", username) - return false - elsif (hexdigest(pub_time, @instance_key)[0,32] != pk) - error("%s: invalid private-key: %s for %s", - username, hexdigest(pub_time, @instance_key)[0,32], pk) - return false - end - - diff_time = req.request_time.to_i - pub_time.to_i - if (diff_time < 0) - error("%s: difference of time-stamp is negative.", username) - return false - elsif diff_time > @nonce_expire_period - error("%s: nonce is expired.", username) - return false - end - - return true - end - - def generate_opaque(req) - @mutex.synchronize{ - now = req.request_time - if now - @last_nonce_expire > @nonce_expire_delta - @opaques.delete_if{|key,val| - (now - val.time) > @nonce_expire_period - } - @last_nonce_expire = now - end - begin - opaque = Utils::random_string(16) - end while @opaques[opaque] - @opaques[opaque] = OpaqueInfo.new(now, nil, '00000001') - opaque - } - end - - def check_opaque(opaque_struct, req, auth_req) - if (@use_next_nonce && auth_req['nonce'] != opaque_struct.nonce) - error('%s: nonce unmatched. "%s" for "%s"', - auth_req['username'], auth_req['nonce'], opaque_struct.nonce) - return false - elsif !check_nonce(req, auth_req) - return false - end - if (@check_nc && auth_req['nc'] != opaque_struct.nc) - error('%s: nc unmatched."%s" for "%s"', - auth_req['username'], auth_req['nc'], opaque_struct.nc) - return false - end - true - end - - def check_uri(req, auth_req) - uri = auth_req['uri'] - if uri != req.request_uri.to_s && uri != req.unparsed_uri && - (@internet_explorer_hack && uri != req.path) - error('%s: uri unmatch. "%s" for "%s"', auth_req['username'], - auth_req['uri'], req.request_uri.to_s) - return false - end - true - end - - def hexdigest(*args) - @h.hexdigest(args.join(":")) - end - - # :startdoc: - end - - ## - # Digest authentication for proxy servers. See DigestAuth for details. - - class ProxyDigestAuth < DigestAuth - include ProxyAuthenticator - - private - def check_uri(req, auth_req) # :nodoc: - return true - end - end - end -end diff --git a/tool/lib/webrick/httpauth/htdigest.rb b/tool/lib/webrick/httpauth/htdigest.rb deleted file mode 100644 index 93b18e2c750bd9..00000000000000 --- a/tool/lib/webrick/httpauth/htdigest.rb +++ /dev/null @@ -1,132 +0,0 @@ -# frozen_string_literal: false -# -# httpauth/htdigest.rb -- Apache compatible htdigest file -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2003 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: htdigest.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $ - -require_relative 'userdb' -require_relative 'digestauth' -require 'tempfile' - -module WEBrick - module HTTPAuth - - ## - # Htdigest accesses apache-compatible digest password files. Passwords are - # matched to a realm where they are valid. For security, the path for a - # digest password database should be stored outside of the paths available - # to the HTTP server. - # - # Htdigest is intended for use with WEBrick::HTTPAuth::DigestAuth and - # stores passwords using cryptographic hashes. - # - # htpasswd = WEBrick::HTTPAuth::Htdigest.new 'my_password_file' - # htpasswd.set_passwd 'my realm', 'username', 'password' - # htpasswd.flush - - class Htdigest - include UserDB - - ## - # Open a digest password database at +path+ - - def initialize(path) - @path = path - @mtime = Time.at(0) - @digest = Hash.new - @mutex = Thread::Mutex::new - @auth_type = DigestAuth - File.open(@path,"a").close unless File.exist?(@path) - reload - end - - ## - # Reloads passwords from the database - - def reload - mtime = File::mtime(@path) - if mtime > @mtime - @digest.clear - File.open(@path){|io| - while line = io.gets - line.chomp! - user, realm, pass = line.split(/:/, 3) - unless @digest[realm] - @digest[realm] = Hash.new - end - @digest[realm][user] = pass - end - } - @mtime = mtime - end - end - - ## - # Flush the password database. If +output+ is given the database will - # be written there instead of to the original path. - - def flush(output=nil) - output ||= @path - tmp = Tempfile.create("htpasswd", File::dirname(output)) - renamed = false - begin - each{|item| tmp.puts(item.join(":")) } - tmp.close - File::rename(tmp.path, output) - renamed = true - ensure - tmp.close - File.unlink(tmp.path) if !renamed - end - end - - ## - # Retrieves a password from the database for +user+ in +realm+. If - # +reload_db+ is true the database will be reloaded first. - - def get_passwd(realm, user, reload_db) - reload() if reload_db - if hash = @digest[realm] - hash[user] - end - end - - ## - # Sets a password in the database for +user+ in +realm+ to +pass+. - - def set_passwd(realm, user, pass) - @mutex.synchronize{ - unless @digest[realm] - @digest[realm] = Hash.new - end - @digest[realm][user] = make_passwd(realm, user, pass) - } - end - - ## - # Removes a password from the database for +user+ in +realm+. - - def delete_passwd(realm, user) - if hash = @digest[realm] - hash.delete(user) - end - end - - ## - # Iterate passwords in the database. - - def each # :yields: [user, realm, password_hash] - @digest.keys.sort.each{|realm| - hash = @digest[realm] - hash.keys.sort.each{|user| - yield([user, realm, hash[user]]) - } - } - end - end - end -end diff --git a/tool/lib/webrick/httpauth/htgroup.rb b/tool/lib/webrick/httpauth/htgroup.rb deleted file mode 100644 index e06c441b18f356..00000000000000 --- a/tool/lib/webrick/httpauth/htgroup.rb +++ /dev/null @@ -1,97 +0,0 @@ -# frozen_string_literal: false -# -# httpauth/htgroup.rb -- Apache compatible htgroup file -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2003 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: htgroup.rb,v 1.1 2003/02/16 22:22:56 gotoyuzo Exp $ - -require 'tempfile' - -module WEBrick - module HTTPAuth - - ## - # Htgroup accesses apache-compatible group files. Htgroup can be used to - # provide group-based authentication for users. Currently Htgroup is not - # directly integrated with any authenticators in WEBrick. For security, - # the path for a digest password database should be stored outside of the - # paths available to the HTTP server. - # - # Example: - # - # htgroup = WEBrick::HTTPAuth::Htgroup.new 'my_group_file' - # htgroup.add 'superheroes', %w[spiderman batman] - # - # htgroup.members('superheroes').include? 'magneto' # => false - - class Htgroup - - ## - # Open a group database at +path+ - - def initialize(path) - @path = path - @mtime = Time.at(0) - @group = Hash.new - File.open(@path,"a").close unless File.exist?(@path) - reload - end - - ## - # Reload groups from the database - - def reload - if (mtime = File::mtime(@path)) > @mtime - @group.clear - File.open(@path){|io| - while line = io.gets - line.chomp! - group, members = line.split(/:\s*/) - @group[group] = members.split(/\s+/) - end - } - @mtime = mtime - end - end - - ## - # Flush the group database. If +output+ is given the database will be - # written there instead of to the original path. - - def flush(output=nil) - output ||= @path - tmp = Tempfile.create("htgroup", File::dirname(output)) - begin - @group.keys.sort.each{|group| - tmp.puts(format("%s: %s", group, self.members(group).join(" "))) - } - ensure - tmp.close - if $! - File.unlink(tmp.path) - else - return File.rename(tmp.path, output) - end - end - end - - ## - # Retrieve the list of members from +group+ - - def members(group) - reload - @group[group] || [] - end - - ## - # Add an Array of +members+ to +group+ - - def add(group, members) - @group[group] = members(group) | members - end - end - end -end diff --git a/tool/lib/webrick/httpauth/htpasswd.rb b/tool/lib/webrick/httpauth/htpasswd.rb deleted file mode 100644 index abca30532e0429..00000000000000 --- a/tool/lib/webrick/httpauth/htpasswd.rb +++ /dev/null @@ -1,158 +0,0 @@ -# frozen_string_literal: false -# -# httpauth/htpasswd -- Apache compatible htpasswd file -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2003 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: htpasswd.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $ - -require_relative 'userdb' -require_relative 'basicauth' -require 'tempfile' - -module WEBrick - module HTTPAuth - - ## - # Htpasswd accesses apache-compatible password files. Passwords are - # matched to a realm where they are valid. For security, the path for a - # password database should be stored outside of the paths available to the - # HTTP server. - # - # Htpasswd is intended for use with WEBrick::HTTPAuth::BasicAuth. - # - # To create an Htpasswd database with a single user: - # - # htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file' - # htpasswd.set_passwd 'my realm', 'username', 'password' - # htpasswd.flush - - class Htpasswd - include UserDB - - ## - # Open a password database at +path+ - - def initialize(path, password_hash: nil) - @path = path - @mtime = Time.at(0) - @passwd = Hash.new - @auth_type = BasicAuth - @password_hash = password_hash - - case @password_hash - when nil - # begin - # require "string/crypt" - # rescue LoadError - # warn("Unable to load string/crypt, proceeding with deprecated use of String#crypt, consider using password_hash: :bcrypt") - # end - @password_hash = :crypt - when :crypt - # require "string/crypt" - when :bcrypt - require "bcrypt" - else - raise ArgumentError, "only :crypt and :bcrypt are supported for password_hash keyword argument" - end - - File.open(@path,"a").close unless File.exist?(@path) - reload - end - - ## - # Reload passwords from the database - - def reload - mtime = File::mtime(@path) - if mtime > @mtime - @passwd.clear - File.open(@path){|io| - while line = io.gets - line.chomp! - case line - when %r!\A[^:]+:[a-zA-Z0-9./]{13}\z! - if @password_hash == :bcrypt - raise StandardError, ".htpasswd file contains crypt password, only bcrypt passwords supported" - end - user, pass = line.split(":") - when %r!\A[^:]+:\$2[aby]\$\d{2}\$.{53}\z! - if @password_hash == :crypt - raise StandardError, ".htpasswd file contains bcrypt password, only crypt passwords supported" - end - user, pass = line.split(":") - when /:\$/, /:{SHA}/ - raise NotImplementedError, - 'MD5, SHA1 .htpasswd file not supported' - else - raise StandardError, 'bad .htpasswd file' - end - @passwd[user] = pass - end - } - @mtime = mtime - end - end - - ## - # Flush the password database. If +output+ is given the database will - # be written there instead of to the original path. - - def flush(output=nil) - output ||= @path - tmp = Tempfile.create("htpasswd", File::dirname(output)) - renamed = false - begin - each{|item| tmp.puts(item.join(":")) } - tmp.close - File::rename(tmp.path, output) - renamed = true - ensure - tmp.close - File.unlink(tmp.path) if !renamed - end - end - - ## - # Retrieves a password from the database for +user+ in +realm+. If - # +reload_db+ is true the database will be reloaded first. - - def get_passwd(realm, user, reload_db) - reload() if reload_db - @passwd[user] - end - - ## - # Sets a password in the database for +user+ in +realm+ to +pass+. - - def set_passwd(realm, user, pass) - if @password_hash == :bcrypt - # Cost of 5 to match Apache default, and because the - # bcrypt default of 10 will introduce significant delays - # for every request. - @passwd[user] = BCrypt::Password.create(pass, :cost=>5) - else - @passwd[user] = make_passwd(realm, user, pass) - end - end - - ## - # Removes a password from the database for +user+ in +realm+. - - def delete_passwd(realm, user) - @passwd.delete(user) - end - - ## - # Iterate passwords in the database. - - def each # :yields: [user, password] - @passwd.keys.sort.each{|user| - yield([user, @passwd[user]]) - } - end - end - end -end diff --git a/tool/lib/webrick/httpauth/userdb.rb b/tool/lib/webrick/httpauth/userdb.rb deleted file mode 100644 index 7a17715cdf3951..00000000000000 --- a/tool/lib/webrick/httpauth/userdb.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: false -#-- -# httpauth/userdb.rb -- UserDB mix-in module. -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2003 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: userdb.rb,v 1.2 2003/02/20 07:15:48 gotoyuzo Exp $ - -module WEBrick - module HTTPAuth - - ## - # User database mixin for HTTPAuth. This mixin dispatches user record - # access to the underlying auth_type for this database. - - module UserDB - - ## - # The authentication type. - # - # WEBrick::HTTPAuth::BasicAuth or WEBrick::HTTPAuth::DigestAuth are - # built-in. - - attr_accessor :auth_type - - ## - # Creates an obscured password in +realm+ with +user+ and +password+ - # using the auth_type of this database. - - def make_passwd(realm, user, pass) - @auth_type::make_passwd(realm, user, pass) - end - - ## - # Sets a password in +realm+ with +user+ and +password+ for the - # auth_type of this database. - - def set_passwd(realm, user, pass) - self[user] = pass - end - - ## - # Retrieves a password in +realm+ for +user+ for the auth_type of this - # database. +reload_db+ is a dummy value. - - def get_passwd(realm, user, reload_db=false) - make_passwd(realm, user, self[user]) - end - end - end -end diff --git a/tool/lib/webrick/httpproxy.rb b/tool/lib/webrick/httpproxy.rb deleted file mode 100644 index 7607c3df88e3e6..00000000000000 --- a/tool/lib/webrick/httpproxy.rb +++ /dev/null @@ -1,354 +0,0 @@ -# frozen_string_literal: false -# -# httpproxy.rb -- HTTPProxy Class -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2002 GOTO Kentaro -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: httpproxy.rb,v 1.18 2003/03/08 18:58:10 gotoyuzo Exp $ -# $kNotwork: straw.rb,v 1.3 2002/02/12 15:13:07 gotoken Exp $ - -require_relative "httpserver" -require "net/http" - -module WEBrick - - NullReader = Object.new # :nodoc: - class << NullReader # :nodoc: - def read(*args) - nil - end - alias gets read - end - - FakeProxyURI = Object.new # :nodoc: - class << FakeProxyURI # :nodoc: - def method_missing(meth, *args) - if %w(scheme host port path query userinfo).member?(meth.to_s) - return nil - end - super - end - end - - # :startdoc: - - ## - # An HTTP Proxy server which proxies GET, HEAD and POST requests. - # - # To create a simple proxy server: - # - # require 'webrick' - # require 'webrick/httpproxy' - # - # proxy = WEBrick::HTTPProxyServer.new Port: 8000 - # - # trap 'INT' do proxy.shutdown end - # trap 'TERM' do proxy.shutdown end - # - # proxy.start - # - # See ::new for proxy-specific configuration items. - # - # == Modifying proxied responses - # - # To modify content the proxy server returns use the +:ProxyContentHandler+ - # option: - # - # handler = proc do |req, res| - # if res['content-type'] == 'text/plain' then - # res.body << "\nThis content was proxied!\n" - # end - # end - # - # proxy = - # WEBrick::HTTPProxyServer.new Port: 8000, ProxyContentHandler: handler - - class HTTPProxyServer < HTTPServer - - ## - # Proxy server configurations. The proxy server handles the following - # configuration items in addition to those supported by HTTPServer: - # - # :ProxyAuthProc:: Called with a request and response to authorize a - # request - # :ProxyVia:: Appended to the via header - # :ProxyURI:: The proxy server's URI - # :ProxyContentHandler:: Called with a request and response and allows - # modification of the response - # :ProxyTimeout:: Sets the proxy timeouts to 30 seconds for open and 60 - # seconds for read operations - - def initialize(config={}, default=Config::HTTP) - super(config, default) - c = @config - @via = "#{c[:HTTPVersion]} #{c[:ServerName]}:#{c[:Port]}" - end - - # :stopdoc: - def service(req, res) - if req.request_method == "CONNECT" - do_CONNECT(req, res) - elsif req.unparsed_uri =~ %r!^http://! - proxy_service(req, res) - else - super(req, res) - end - end - - def proxy_auth(req, res) - if proc = @config[:ProxyAuthProc] - proc.call(req, res) - end - req.header.delete("proxy-authorization") - end - - def proxy_uri(req, res) - # should return upstream proxy server's URI - return @config[:ProxyURI] - end - - def proxy_service(req, res) - # Proxy Authentication - proxy_auth(req, res) - - begin - public_send("do_#{req.request_method}", req, res) - rescue NoMethodError - raise HTTPStatus::MethodNotAllowed, - "unsupported method `#{req.request_method}'." - rescue => err - logger.debug("#{err.class}: #{err.message}") - raise HTTPStatus::ServiceUnavailable, err.message - end - - # Process contents - if handler = @config[:ProxyContentHandler] - handler.call(req, res) - end - end - - def do_CONNECT(req, res) - # Proxy Authentication - proxy_auth(req, res) - - ua = Thread.current[:WEBrickSocket] # User-Agent - raise HTTPStatus::InternalServerError, - "[BUG] cannot get socket" unless ua - - host, port = req.unparsed_uri.split(":", 2) - # Proxy authentication for upstream proxy server - if proxy = proxy_uri(req, res) - proxy_request_line = "CONNECT #{host}:#{port} HTTP/1.0" - if proxy.userinfo - credentials = "Basic " + [proxy.userinfo].pack("m0") - end - host, port = proxy.host, proxy.port - end - - begin - @logger.debug("CONNECT: upstream proxy is `#{host}:#{port}'.") - os = TCPSocket.new(host, port) # origin server - - if proxy - @logger.debug("CONNECT: sending a Request-Line") - os << proxy_request_line << CRLF - @logger.debug("CONNECT: > #{proxy_request_line}") - if credentials - @logger.debug("CONNECT: sending credentials") - os << "Proxy-Authorization: " << credentials << CRLF - end - os << CRLF - proxy_status_line = os.gets(LF) - @logger.debug("CONNECT: read Status-Line from the upstream server") - @logger.debug("CONNECT: < #{proxy_status_line}") - if %r{^HTTP/\d+\.\d+\s+200\s*} =~ proxy_status_line - while line = os.gets(LF) - break if /\A(#{CRLF}|#{LF})\z/om =~ line - end - else - raise HTTPStatus::BadGateway - end - end - @logger.debug("CONNECT #{host}:#{port}: succeeded") - res.status = HTTPStatus::RC_OK - rescue => ex - @logger.debug("CONNECT #{host}:#{port}: failed `#{ex.message}'") - res.set_error(ex) - raise HTTPStatus::EOFError - ensure - if handler = @config[:ProxyContentHandler] - handler.call(req, res) - end - res.send_response(ua) - access_log(@config, req, res) - - # Should clear request-line not to send the response twice. - # see: HTTPServer#run - req.parse(NullReader) rescue nil - end - - begin - while fds = IO::select([ua, os]) - if fds[0].member?(ua) - buf = ua.readpartial(1024); - @logger.debug("CONNECT: #{buf.bytesize} byte from User-Agent") - os.write(buf) - elsif fds[0].member?(os) - buf = os.readpartial(1024); - @logger.debug("CONNECT: #{buf.bytesize} byte from #{host}:#{port}") - ua.write(buf) - end - end - rescue - os.close - @logger.debug("CONNECT #{host}:#{port}: closed") - end - - raise HTTPStatus::EOFError - end - - def do_GET(req, res) - perform_proxy_request(req, res, Net::HTTP::Get) - end - - def do_HEAD(req, res) - perform_proxy_request(req, res, Net::HTTP::Head) - end - - def do_POST(req, res) - perform_proxy_request(req, res, Net::HTTP::Post, req.body_reader) - end - - def do_OPTIONS(req, res) - res['allow'] = "GET,HEAD,POST,OPTIONS,CONNECT" - end - - private - - # Some header fields should not be transferred. - HopByHop = %w( connection keep-alive proxy-authenticate upgrade - proxy-authorization te trailers transfer-encoding ) - ShouldNotTransfer = %w( set-cookie proxy-connection ) - def split_field(f) f ? f.split(/,\s+/).collect{|i| i.downcase } : [] end - - def choose_header(src, dst) - connections = split_field(src['connection']) - src.each{|key, value| - key = key.downcase - if HopByHop.member?(key) || # RFC2616: 13.5.1 - connections.member?(key) || # RFC2616: 14.10 - ShouldNotTransfer.member?(key) # pragmatics - @logger.debug("choose_header: `#{key}: #{value}'") - next - end - dst[key] = value - } - end - - # Net::HTTP is stupid about the multiple header fields. - # Here is workaround: - def set_cookie(src, dst) - if str = src['set-cookie'] - cookies = [] - str.split(/,\s*/).each{|token| - if /^[^=]+;/o =~ token - cookies[-1] << ", " << token - elsif /=/o =~ token - cookies << token - else - cookies[-1] << ", " << token - end - } - dst.cookies.replace(cookies) - end - end - - def set_via(h) - if @config[:ProxyVia] - if h['via'] - h['via'] << ", " << @via - else - h['via'] = @via - end - end - end - - def setup_proxy_header(req, res) - # Choose header fields to transfer - header = Hash.new - choose_header(req, header) - set_via(header) - return header - end - - def setup_upstream_proxy_authentication(req, res, header) - if upstream = proxy_uri(req, res) - if upstream.userinfo - header['proxy-authorization'] = - "Basic " + [upstream.userinfo].pack("m0") - end - return upstream - end - return FakeProxyURI - end - - def create_net_http(uri, upstream) - Net::HTTP.new(uri.host, uri.port, upstream.host, upstream.port) - end - - def perform_proxy_request(req, res, req_class, body_stream = nil) - uri = req.request_uri - path = uri.path.dup - path << "?" << uri.query if uri.query - header = setup_proxy_header(req, res) - upstream = setup_upstream_proxy_authentication(req, res, header) - - body_tmp = [] - http = create_net_http(uri, upstream) - req_fib = Fiber.new do - http.start do - if @config[:ProxyTimeout] - ################################## these issues are - http.open_timeout = 30 # secs # necessary (maybe because - http.read_timeout = 60 # secs # Ruby's bug, but why?) - ################################## - end - if body_stream && req['transfer-encoding'] =~ /\bchunked\b/i - header['Transfer-Encoding'] = 'chunked' - end - http_req = req_class.new(path, header) - http_req.body_stream = body_stream if body_stream - http.request(http_req) do |response| - # Persistent connection requirements are mysterious for me. - # So I will close the connection in every response. - res['proxy-connection'] = "close" - res['connection'] = "close" - - # stream Net::HTTP::HTTPResponse to WEBrick::HTTPResponse - res.status = response.code.to_i - res.chunked = response.chunked? - choose_header(response, res) - set_cookie(response, res) - set_via(res) - response.read_body do |buf| - body_tmp << buf - Fiber.yield # wait for res.body Proc#call - end - end # http.request - end - end - req_fib.resume # read HTTP response headers and first chunk of the body - res.body = ->(socket) do - while buf = body_tmp.shift - socket.write(buf) - buf.clear - req_fib.resume # continue response.read_body - end - end - end - # :stopdoc: - end -end diff --git a/tool/lib/webrick/httprequest.rb b/tool/lib/webrick/httprequest.rb deleted file mode 100644 index 258ee37a38efbe..00000000000000 --- a/tool/lib/webrick/httprequest.rb +++ /dev/null @@ -1,636 +0,0 @@ -# frozen_string_literal: false -# -# httprequest.rb -- HTTPRequest Class -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: httprequest.rb,v 1.64 2003/07/13 17:18:22 gotoyuzo Exp $ - -require 'fiber' -require 'uri' -require_relative 'httpversion' -require_relative 'httpstatus' -require_relative 'httputils' -require_relative 'cookie' - -module WEBrick - - ## - # An HTTP request. This is consumed by service and do_* methods in - # WEBrick servlets - - class HTTPRequest - - BODY_CONTAINABLE_METHODS = [ "POST", "PUT" ] # :nodoc: - - # :section: Request line - - ## - # The complete request line such as: - # - # GET / HTTP/1.1 - - attr_reader :request_line - - ## - # The request method, GET, POST, PUT, etc. - - attr_reader :request_method - - ## - # The unparsed URI of the request - - attr_reader :unparsed_uri - - ## - # The HTTP version of the request - - attr_reader :http_version - - # :section: Request-URI - - ## - # The parsed URI of the request - - attr_reader :request_uri - - ## - # The request path - - attr_reader :path - - ## - # The script name (CGI variable) - - attr_accessor :script_name - - ## - # The path info (CGI variable) - - attr_accessor :path_info - - ## - # The query from the URI of the request - - attr_accessor :query_string - - # :section: Header and entity body - - ## - # The raw header of the request - - attr_reader :raw_header - - ## - # The parsed header of the request - - attr_reader :header - - ## - # The parsed request cookies - - attr_reader :cookies - - ## - # The Accept header value - - attr_reader :accept - - ## - # The Accept-Charset header value - - attr_reader :accept_charset - - ## - # The Accept-Encoding header value - - attr_reader :accept_encoding - - ## - # The Accept-Language header value - - attr_reader :accept_language - - # :section: - - ## - # The remote user (CGI variable) - - attr_accessor :user - - ## - # The socket address of the server - - attr_reader :addr - - ## - # The socket address of the client - - attr_reader :peeraddr - - ## - # Hash of request attributes - - attr_reader :attributes - - ## - # Is this a keep-alive connection? - - attr_reader :keep_alive - - ## - # The local time this request was received - - attr_reader :request_time - - ## - # Creates a new HTTP request. WEBrick::Config::HTTP is the default - # configuration. - - def initialize(config) - @config = config - @buffer_size = @config[:InputBufferSize] - @logger = config[:Logger] - - @request_line = @request_method = - @unparsed_uri = @http_version = nil - - @request_uri = @host = @port = @path = nil - @script_name = @path_info = nil - @query_string = nil - @query = nil - @form_data = nil - - @raw_header = Array.new - @header = nil - @cookies = [] - @accept = [] - @accept_charset = [] - @accept_encoding = [] - @accept_language = [] - @body = "" - - @addr = @peeraddr = nil - @attributes = {} - @user = nil - @keep_alive = false - @request_time = nil - - @remaining_size = nil - @socket = nil - - @forwarded_proto = @forwarded_host = @forwarded_port = - @forwarded_server = @forwarded_for = nil - end - - ## - # Parses a request from +socket+. This is called internally by - # WEBrick::HTTPServer. - - def parse(socket=nil) - @socket = socket - begin - @peeraddr = socket.respond_to?(:peeraddr) ? socket.peeraddr : [] - @addr = socket.respond_to?(:addr) ? socket.addr : [] - rescue Errno::ENOTCONN - raise HTTPStatus::EOFError - end - - read_request_line(socket) - if @http_version.major > 0 - read_header(socket) - @header['cookie'].each{|cookie| - @cookies += Cookie::parse(cookie) - } - @accept = HTTPUtils.parse_qvalues(self['accept']) - @accept_charset = HTTPUtils.parse_qvalues(self['accept-charset']) - @accept_encoding = HTTPUtils.parse_qvalues(self['accept-encoding']) - @accept_language = HTTPUtils.parse_qvalues(self['accept-language']) - end - return if @request_method == "CONNECT" - return if @unparsed_uri == "*" - - begin - setup_forwarded_info - @request_uri = parse_uri(@unparsed_uri) - @path = HTTPUtils::unescape(@request_uri.path) - @path = HTTPUtils::normalize_path(@path) - @host = @request_uri.host - @port = @request_uri.port - @query_string = @request_uri.query - @script_name = "" - @path_info = @path.dup - rescue - raise HTTPStatus::BadRequest, "bad URI `#{@unparsed_uri}'." - end - - if /\Aclose\z/io =~ self["connection"] - @keep_alive = false - elsif /\Akeep-alive\z/io =~ self["connection"] - @keep_alive = true - elsif @http_version < "1.1" - @keep_alive = false - else - @keep_alive = true - end - end - - ## - # Generate HTTP/1.1 100 continue response if the client expects it, - # otherwise does nothing. - - def continue # :nodoc: - if self['expect'] == '100-continue' && @config[:HTTPVersion] >= "1.1" - @socket << "HTTP/#{@config[:HTTPVersion]} 100 continue#{CRLF}#{CRLF}" - @header.delete('expect') - end - end - - ## - # Returns the request body. - - def body(&block) # :yields: body_chunk - block ||= Proc.new{|chunk| @body << chunk } - read_body(@socket, block) - @body.empty? ? nil : @body - end - - ## - # Prepares the HTTPRequest object for use as the - # source for IO.copy_stream - - def body_reader - @body_tmp = [] - @body_rd = Fiber.new do - body do |buf| - @body_tmp << buf - Fiber.yield - end - end - @body_rd.resume # grab the first chunk and yield - self - end - - # for IO.copy_stream. - def readpartial(size, buf = ''.b) # :nodoc - res = @body_tmp.shift or raise EOFError, 'end of file reached' - if res.length > size - @body_tmp.unshift(res[size..-1]) - res = res[0..size - 1] - end - buf.replace(res) - res.clear - # get more chunks - check alive? because we can take a partial chunk - @body_rd.resume if @body_rd.alive? - buf - end - - ## - # Request query as a Hash - - def query - unless @query - parse_query() - end - @query - end - - ## - # The content-length header - - def content_length - return Integer(self['content-length']) - end - - ## - # The content-type header - - def content_type - return self['content-type'] - end - - ## - # Retrieves +header_name+ - - def [](header_name) - if @header - value = @header[header_name.downcase] - value.empty? ? nil : value.join(", ") - end - end - - ## - # Iterates over the request headers - - def each - if @header - @header.each{|k, v| - value = @header[k] - yield(k, value.empty? ? nil : value.join(", ")) - } - end - end - - ## - # The host this request is for - - def host - return @forwarded_host || @host - end - - ## - # The port this request is for - - def port - return @forwarded_port || @port - end - - ## - # The server name this request is for - - def server_name - return @forwarded_server || @config[:ServerName] - end - - ## - # The client's IP address - - def remote_ip - return self["client-ip"] || @forwarded_for || @peeraddr[3] - end - - ## - # Is this an SSL request? - - def ssl? - return @request_uri.scheme == "https" - end - - ## - # Should the connection this request was made on be kept alive? - - def keep_alive? - @keep_alive - end - - def to_s # :nodoc: - ret = @request_line.dup - @raw_header.each{|line| ret << line } - ret << CRLF - ret << body if body - ret - end - - ## - # Consumes any remaining body and updates keep-alive status - - def fixup() # :nodoc: - begin - body{|chunk| } # read remaining body - rescue HTTPStatus::Error => ex - @logger.error("HTTPRequest#fixup: #{ex.class} occurred.") - @keep_alive = false - rescue => ex - @logger.error(ex) - @keep_alive = false - end - end - - # This method provides the metavariables defined by the revision 3 - # of "The WWW Common Gateway Interface Version 1.1" - # To browse the current document of CGI Version 1.1, see below: - # https://www.rfc-editor.org/rfc/rfc3875 - - def meta_vars - meta = Hash.new - - cl = self["Content-Length"] - ct = self["Content-Type"] - meta["CONTENT_LENGTH"] = cl if cl.to_i > 0 - meta["CONTENT_TYPE"] = ct.dup if ct - meta["GATEWAY_INTERFACE"] = "CGI/1.1" - meta["PATH_INFO"] = @path_info ? @path_info.dup : "" - #meta["PATH_TRANSLATED"] = nil # no plan to be provided - meta["QUERY_STRING"] = @query_string ? @query_string.dup : "" - meta["REMOTE_ADDR"] = @peeraddr[3] - meta["REMOTE_HOST"] = @peeraddr[2] - #meta["REMOTE_IDENT"] = nil # no plan to be provided - meta["REMOTE_USER"] = @user - meta["REQUEST_METHOD"] = @request_method.dup - meta["REQUEST_URI"] = @request_uri.to_s - meta["SCRIPT_NAME"] = @script_name.dup - meta["SERVER_NAME"] = @host - meta["SERVER_PORT"] = @port.to_s - meta["SERVER_PROTOCOL"] = "HTTP/" + @config[:HTTPVersion].to_s - meta["SERVER_SOFTWARE"] = @config[:ServerSoftware].dup - - self.each{|key, val| - next if /^content-type$/i =~ key - next if /^content-length$/i =~ key - name = "HTTP_" + key - name.gsub!(/-/o, "_") - name.upcase! - meta[name] = val - } - - meta - end - - private - - # :stopdoc: - - MAX_URI_LENGTH = 2083 # :nodoc: - - # same as Mongrel, Thin and Puma - MAX_HEADER_LENGTH = (112 * 1024) # :nodoc: - - def read_request_line(socket) - @request_line = read_line(socket, MAX_URI_LENGTH) if socket - raise HTTPStatus::EOFError unless @request_line - - @request_bytes = @request_line.bytesize - if @request_bytes >= MAX_URI_LENGTH and @request_line[-1, 1] != LF - raise HTTPStatus::RequestURITooLarge - end - - @request_time = Time.now - if /^(\S+)\s+(\S++)(?:\s+HTTP\/(\d+\.\d+))?\r?\n/mo =~ @request_line - @request_method = $1 - @unparsed_uri = $2 - @http_version = HTTPVersion.new($3 ? $3 : "0.9") - else - rl = @request_line.sub(/\x0d?\x0a\z/o, '') - raise HTTPStatus::BadRequest, "bad Request-Line `#{rl}'." - end - end - - def read_header(socket) - if socket - while line = read_line(socket) - break if /\A(#{CRLF}|#{LF})\z/om =~ line - if (@request_bytes += line.bytesize) > MAX_HEADER_LENGTH - raise HTTPStatus::RequestEntityTooLarge, 'headers too large' - end - @raw_header << line - end - end - @header = HTTPUtils::parse_header(@raw_header.join) - end - - def parse_uri(str, scheme="http") - if @config[:Escape8bitURI] - str = HTTPUtils::escape8bit(str) - end - str.sub!(%r{\A/+}o, '/') - uri = URI::parse(str) - return uri if uri.absolute? - if @forwarded_host - host, port = @forwarded_host, @forwarded_port - elsif self["host"] - pattern = /\A(#{URI::REGEXP::PATTERN::HOST})(?::(\d+))?\z/n - host, port = *self['host'].scan(pattern)[0] - elsif @addr.size > 0 - host, port = @addr[2], @addr[1] - else - host, port = @config[:ServerName], @config[:Port] - end - uri.scheme = @forwarded_proto || scheme - uri.host = host - uri.port = port ? port.to_i : nil - return URI::parse(uri.to_s) - end - - def read_body(socket, block) - return unless socket - if tc = self['transfer-encoding'] - case tc - when /\Achunked\z/io then read_chunked(socket, block) - else raise HTTPStatus::NotImplemented, "Transfer-Encoding: #{tc}." - end - elsif self['content-length'] || @remaining_size - @remaining_size ||= self['content-length'].to_i - while @remaining_size > 0 - sz = [@buffer_size, @remaining_size].min - break unless buf = read_data(socket, sz) - @remaining_size -= buf.bytesize - block.call(buf) - end - if @remaining_size > 0 && @socket.eof? - raise HTTPStatus::BadRequest, "invalid body size." - end - elsif BODY_CONTAINABLE_METHODS.member?(@request_method) && !@socket.eof - raise HTTPStatus::LengthRequired - end - return @body - end - - def read_chunk_size(socket) - line = read_line(socket) - if /^([0-9a-fA-F]+)(?:;(\S+))?/ =~ line - chunk_size = $1.hex - chunk_ext = $2 - [ chunk_size, chunk_ext ] - else - raise HTTPStatus::BadRequest, "bad chunk `#{line}'." - end - end - - def read_chunked(socket, block) - chunk_size, = read_chunk_size(socket) - while chunk_size > 0 - begin - sz = [ chunk_size, @buffer_size ].min - data = read_data(socket, sz) # read chunk-data - if data.nil? || data.bytesize != sz - raise HTTPStatus::BadRequest, "bad chunk data size." - end - block.call(data) - end while (chunk_size -= sz) > 0 - - read_line(socket) # skip CRLF - chunk_size, = read_chunk_size(socket) - end - read_header(socket) # trailer + CRLF - @header.delete("transfer-encoding") - @remaining_size = 0 - end - - def _read_data(io, method, *arg) - begin - WEBrick::Utils.timeout(@config[:RequestTimeout]){ - return io.__send__(method, *arg) - } - rescue Errno::ECONNRESET - return nil - rescue Timeout::Error - raise HTTPStatus::RequestTimeout - end - end - - def read_line(io, size=4096) - _read_data(io, :gets, LF, size) - end - - def read_data(io, size) - _read_data(io, :read, size) - end - - def parse_query() - begin - if @request_method == "GET" || @request_method == "HEAD" - @query = HTTPUtils::parse_query(@query_string) - elsif self['content-type'] =~ /^application\/x-www-form-urlencoded/ - @query = HTTPUtils::parse_query(body) - elsif self['content-type'] =~ /^multipart\/form-data; boundary=(.+)/ - boundary = HTTPUtils::dequote($1) - @query = HTTPUtils::parse_form_data(body, boundary) - else - @query = Hash.new - end - rescue => ex - raise HTTPStatus::BadRequest, ex.message - end - end - - PrivateNetworkRegexp = / - ^unknown$| - ^((::ffff:)?127.0.0.1|::1)$| - ^(::ffff:)?(10|172\.(1[6-9]|2[0-9]|3[01])|192\.168)\. - /ixo - - # It's said that all X-Forwarded-* headers will contain more than one - # (comma-separated) value if the original request already contained one of - # these headers. Since we could use these values as Host header, we choose - # the initial(first) value. (apr_table_mergen() adds new value after the - # existing value with ", " prefix) - def setup_forwarded_info - if @forwarded_server = self["x-forwarded-server"] - @forwarded_server = @forwarded_server.split(",", 2).first - end - if @forwarded_proto = self["x-forwarded-proto"] - @forwarded_proto = @forwarded_proto.split(",", 2).first - end - if host_port = self["x-forwarded-host"] - host_port = host_port.split(",", 2).first - if host_port =~ /\A(\[[0-9a-fA-F:]+\])(?::(\d+))?\z/ - @forwarded_host = $1 - tmp = $2 - else - @forwarded_host, tmp = host_port.split(":", 2) - end - @forwarded_port = (tmp || (@forwarded_proto == "https" ? 443 : 80)).to_i - end - if addrs = self["x-forwarded-for"] - addrs = addrs.split(",").collect(&:strip) - addrs.reject!{|ip| PrivateNetworkRegexp =~ ip } - @forwarded_for = addrs.first - end - end - - # :startdoc: - end -end diff --git a/tool/lib/webrick/httpresponse.rb b/tool/lib/webrick/httpresponse.rb deleted file mode 100644 index ba4494ab748af1..00000000000000 --- a/tool/lib/webrick/httpresponse.rb +++ /dev/null @@ -1,564 +0,0 @@ -# frozen_string_literal: false -# -# httpresponse.rb -- HTTPResponse Class -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: httpresponse.rb,v 1.45 2003/07/11 11:02:25 gotoyuzo Exp $ - -require 'time' -require 'uri' -require_relative 'httpversion' -require_relative 'htmlutils' -require_relative 'httputils' -require_relative 'httpstatus' - -module WEBrick - ## - # An HTTP response. This is filled in by the service or do_* methods of a - # WEBrick HTTP Servlet. - - class HTTPResponse - class InvalidHeader < StandardError - end - - ## - # HTTP Response version - - attr_reader :http_version - - ## - # Response status code (200) - - attr_reader :status - - ## - # Response header - - attr_reader :header - - ## - # Response cookies - - attr_reader :cookies - - ## - # Response reason phrase ("OK") - - attr_accessor :reason_phrase - - ## - # Body may be: - # * a String; - # * an IO-like object that responds to +#read+ and +#readpartial+; - # * a Proc-like object that responds to +#call+. - # - # In the latter case, either #chunked= should be set to +true+, - # or header['content-length'] explicitly provided. - # Example: - # - # server.mount_proc '/' do |req, res| - # res.chunked = true - # # or - # # res.header['content-length'] = 10 - # res.body = proc { |out| out.write(Time.now.to_s) } - # end - - attr_accessor :body - - ## - # Request method for this response - - attr_accessor :request_method - - ## - # Request URI for this response - - attr_accessor :request_uri - - ## - # Request HTTP version for this response - - attr_accessor :request_http_version - - ## - # Filename of the static file in this response. Only used by the - # FileHandler servlet. - - attr_accessor :filename - - ## - # Is this a keep-alive response? - - attr_accessor :keep_alive - - ## - # Configuration for this response - - attr_reader :config - - ## - # Bytes sent in this response - - attr_reader :sent_size - - ## - # Creates a new HTTP response object. WEBrick::Config::HTTP is the - # default configuration. - - def initialize(config) - @config = config - @buffer_size = config[:OutputBufferSize] - @logger = config[:Logger] - @header = Hash.new - @status = HTTPStatus::RC_OK - @reason_phrase = nil - @http_version = HTTPVersion::convert(@config[:HTTPVersion]) - @body = '' - @keep_alive = true - @cookies = [] - @request_method = nil - @request_uri = nil - @request_http_version = @http_version # temporary - @chunked = false - @filename = nil - @sent_size = 0 - @bodytempfile = nil - end - - ## - # The response's HTTP status line - - def status_line - "HTTP/#@http_version #@status #@reason_phrase".rstrip << CRLF - end - - ## - # Sets the response's status to the +status+ code - - def status=(status) - @status = status - @reason_phrase = HTTPStatus::reason_phrase(status) - end - - ## - # Retrieves the response header +field+ - - def [](field) - @header[field.downcase] - end - - ## - # Sets the response header +field+ to +value+ - - def []=(field, value) - @chunked = value.to_s.downcase == 'chunked' if field.downcase == 'transfer-encoding' - @header[field.downcase] = value.to_s - end - - ## - # The content-length header - - def content_length - if len = self['content-length'] - return Integer(len) - end - end - - ## - # Sets the content-length header to +len+ - - def content_length=(len) - self['content-length'] = len.to_s - end - - ## - # The content-type header - - def content_type - self['content-type'] - end - - ## - # Sets the content-type header to +type+ - - def content_type=(type) - self['content-type'] = type - end - - ## - # Iterates over each header in the response - - def each - @header.each{|field, value| yield(field, value) } - end - - ## - # Will this response body be returned using chunked transfer-encoding? - - def chunked? - @chunked - end - - ## - # Enables chunked transfer encoding. - - def chunked=(val) - @chunked = val ? true : false - end - - ## - # Will this response's connection be kept alive? - - def keep_alive? - @keep_alive - end - - ## - # Sends the response on +socket+ - - def send_response(socket) # :nodoc: - begin - setup_header() - send_header(socket) - send_body(socket) - rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ENOTCONN => ex - @logger.debug(ex) - @keep_alive = false - rescue Exception => ex - @logger.error(ex) - @keep_alive = false - end - end - - ## - # Sets up the headers for sending - - def setup_header() # :nodoc: - @reason_phrase ||= HTTPStatus::reason_phrase(@status) - @header['server'] ||= @config[:ServerSoftware] - @header['date'] ||= Time.now.httpdate - - # HTTP/0.9 features - if @request_http_version < "1.0" - @http_version = HTTPVersion.new("0.9") - @keep_alive = false - end - - # HTTP/1.0 features - if @request_http_version < "1.1" - if chunked? - @chunked = false - ver = @request_http_version.to_s - msg = "chunked is set for an HTTP/#{ver} request. (ignored)" - @logger.warn(msg) - end - end - - # Determine the message length (RFC2616 -- 4.4 Message Length) - if @status == 304 || @status == 204 || HTTPStatus::info?(@status) - @header.delete('content-length') - @body = "" - elsif chunked? - @header["transfer-encoding"] = "chunked" - @header.delete('content-length') - elsif %r{^multipart/byteranges} =~ @header['content-type'] - @header.delete('content-length') - elsif @header['content-length'].nil? - if @body.respond_to? :readpartial - elsif @body.respond_to? :call - make_body_tempfile - else - @header['content-length'] = (@body ? @body.bytesize : 0).to_s - end - end - - # Keep-Alive connection. - if @header['connection'] == "close" - @keep_alive = false - elsif keep_alive? - if chunked? || @header['content-length'] || @status == 304 || @status == 204 || HTTPStatus.info?(@status) - @header['connection'] = "Keep-Alive" - else - msg = "Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true" - @logger.warn(msg) - @header['connection'] = "close" - @keep_alive = false - end - else - @header['connection'] = "close" - end - - # Location is a single absoluteURI. - if location = @header['location'] - if @request_uri - @header['location'] = @request_uri.merge(location).to_s - end - end - end - - def make_body_tempfile # :nodoc: - return if @bodytempfile - bodytempfile = Tempfile.create("webrick") - if @body.nil? - # nothing - elsif @body.respond_to? :readpartial - IO.copy_stream(@body, bodytempfile) - @body.close - elsif @body.respond_to? :call - @body.call(bodytempfile) - else - bodytempfile.write @body - end - bodytempfile.rewind - @body = @bodytempfile = bodytempfile - @header['content-length'] = bodytempfile.stat.size.to_s - end - - def remove_body_tempfile # :nodoc: - if @bodytempfile - @bodytempfile.close - File.unlink @bodytempfile.path - @bodytempfile = nil - end - end - - - ## - # Sends the headers on +socket+ - - def send_header(socket) # :nodoc: - if @http_version.major > 0 - data = status_line() - @header.each{|key, value| - tmp = key.gsub(/\bwww|^te$|\b\w/){ $&.upcase } - data << "#{tmp}: #{check_header(value)}" << CRLF - } - @cookies.each{|cookie| - data << "Set-Cookie: " << check_header(cookie.to_s) << CRLF - } - data << CRLF - socket.write(data) - end - rescue InvalidHeader => e - @header.clear - @cookies.clear - set_error e - retry - end - - ## - # Sends the body on +socket+ - - def send_body(socket) # :nodoc: - if @body.respond_to? :readpartial then - send_body_io(socket) - elsif @body.respond_to?(:call) then - send_body_proc(socket) - else - send_body_string(socket) - end - end - - ## - # Redirects to +url+ with a WEBrick::HTTPStatus::Redirect +status+. - # - # Example: - # - # res.set_redirect WEBrick::HTTPStatus::TemporaryRedirect - - def set_redirect(status, url) - url = URI(url).to_s - @body = "
#{url}.\n" - @header['location'] = url - raise status - end - - ## - # Creates an error page for exception +ex+ with an optional +backtrace+ - - def set_error(ex, backtrace=false) - case ex - when HTTPStatus::Status - @keep_alive = false if HTTPStatus::error?(ex.code) - self.status = ex.code - else - @keep_alive = false - self.status = HTTPStatus::RC_INTERNAL_SERVER_ERROR - end - @header['content-type'] = "text/html; charset=ISO-8859-1" - - if respond_to?(:create_error_page) - create_error_page() - return - end - - if @request_uri - host, port = @request_uri.host, @request_uri.port - else - host, port = @config[:ServerName], @config[:Port] - end - - error_body(backtrace, ex, host, port) - end - - private - - def check_header(header_value) - header_value = header_value.to_s - if /[\r\n]/ =~ header_value - raise InvalidHeader - else - header_value - end - end - - # :stopdoc: - - def error_body(backtrace, ex, host, port) - @body = '' - @body << <<-_end_of_html_ - - - #{HTMLUtils::escape(@reason_phrase)} - -

#{HTMLUtils::escape(@reason_phrase)}

- #{HTMLUtils::escape(ex.message)} -
- _end_of_html_ - - if backtrace && $DEBUG - @body << "backtrace of `#{HTMLUtils::escape(ex.class.to_s)}' " - @body << "#{HTMLUtils::escape(ex.message)}" - @body << "
"
-        ex.backtrace.each{|line| @body << "\t#{line}\n"}
-        @body << "

" - end - - @body << <<-_end_of_html_ -
- #{HTMLUtils::escape(@config[:ServerSoftware])} at - #{host}:#{port} -
- - - _end_of_html_ - end - - def send_body_io(socket) - begin - if @request_method == "HEAD" - # do nothing - elsif chunked? - buf = '' - begin - @body.readpartial(@buffer_size, buf) - size = buf.bytesize - data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}" - socket.write(data) - data.clear - @sent_size += size - rescue EOFError - break - end while true - buf.clear - socket.write("0#{CRLF}#{CRLF}") - else - if %r{\Abytes (\d+)-(\d+)/\d+\z} =~ @header['content-range'] - offset = $1.to_i - size = $2.to_i - offset + 1 - else - offset = nil - size = @header['content-length'] - size = size.to_i if size - end - begin - @sent_size = IO.copy_stream(@body, socket, size, offset) - rescue NotImplementedError - @body.seek(offset, IO::SEEK_SET) - @sent_size = IO.copy_stream(@body, socket, size) - end - end - ensure - @body.close - end - remove_body_tempfile - end - - def send_body_string(socket) - if @request_method == "HEAD" - # do nothing - elsif chunked? - body ? @body.bytesize : 0 - while buf = @body[@sent_size, @buffer_size] - break if buf.empty? - size = buf.bytesize - data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}" - buf.clear - socket.write(data) - @sent_size += size - end - socket.write("0#{CRLF}#{CRLF}") - else - if @body && @body.bytesize > 0 - socket.write(@body) - @sent_size = @body.bytesize - end - end - end - - def send_body_proc(socket) - if @request_method == "HEAD" - # do nothing - elsif chunked? - @body.call(ChunkedWrapper.new(socket, self)) - socket.write("0#{CRLF}#{CRLF}") - else - size = @header['content-length'].to_i - if @bodytempfile - @bodytempfile.rewind - IO.copy_stream(@bodytempfile, socket) - else - @body.call(socket) - end - @sent_size = size - end - end - - class ChunkedWrapper - def initialize(socket, resp) - @socket = socket - @resp = resp - end - - def write(buf) - return 0 if buf.empty? - socket = @socket - @resp.instance_eval { - size = buf.bytesize - data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}" - socket.write(data) - data.clear - @sent_size += size - size - } - end - - def <<(*buf) - write(buf) - self - end - end - - # preserved for compatibility with some 3rd-party handlers - def _write_data(socket, data) - socket << data - end - - # :startdoc: - end - -end diff --git a/tool/lib/webrick/https.rb b/tool/lib/webrick/https.rb deleted file mode 100644 index b0a49bc40bae05..00000000000000 --- a/tool/lib/webrick/https.rb +++ /dev/null @@ -1,152 +0,0 @@ -# frozen_string_literal: false -# -# https.rb -- SSL/TLS enhancement for HTTPServer -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2001 GOTOU Yuuzou -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: https.rb,v 1.15 2003/07/22 19:20:42 gotoyuzo Exp $ - -require_relative 'ssl' -require_relative 'httpserver' - -module WEBrick - module Config - HTTP.update(SSL) - end - - ## - #-- - # Adds SSL functionality to WEBrick::HTTPRequest - - class HTTPRequest - - ## - # HTTP request SSL cipher - - attr_reader :cipher - - ## - # HTTP request server certificate - - attr_reader :server_cert - - ## - # HTTP request client certificate - - attr_reader :client_cert - - # :stopdoc: - - alias orig_parse parse - - def parse(socket=nil) - if socket.respond_to?(:cert) - @server_cert = socket.cert || @config[:SSLCertificate] - @client_cert = socket.peer_cert - @client_cert_chain = socket.peer_cert_chain - @cipher = socket.cipher - end - orig_parse(socket) - end - - alias orig_parse_uri parse_uri - - def parse_uri(str, scheme="https") - if server_cert - return orig_parse_uri(str, scheme) - end - return orig_parse_uri(str) - end - private :parse_uri - - alias orig_meta_vars meta_vars - - def meta_vars - meta = orig_meta_vars - if server_cert - meta["HTTPS"] = "on" - meta["SSL_SERVER_CERT"] = @server_cert.to_pem - meta["SSL_CLIENT_CERT"] = @client_cert ? @client_cert.to_pem : "" - if @client_cert_chain - @client_cert_chain.each_with_index{|cert, i| - meta["SSL_CLIENT_CERT_CHAIN_#{i}"] = cert.to_pem - } - end - meta["SSL_CIPHER"] = @cipher[0] - meta["SSL_PROTOCOL"] = @cipher[1] - meta["SSL_CIPHER_USEKEYSIZE"] = @cipher[2].to_s - meta["SSL_CIPHER_ALGKEYSIZE"] = @cipher[3].to_s - end - meta - end - - # :startdoc: - end - - ## - #-- - # Fake WEBrick::HTTPRequest for lookup_server - - class SNIRequest - - ## - # The SNI hostname - - attr_reader :host - - ## - # The socket address of the server - - attr_reader :addr - - ## - # The port this request is for - - attr_reader :port - - ## - # Creates a new SNIRequest. - - def initialize(sslsocket, hostname) - @host = hostname - @addr = sslsocket.addr - @port = @addr[1] - end - end - - - ## - #-- - # Adds SSL functionality to WEBrick::HTTPServer - - class HTTPServer < ::WEBrick::GenericServer - ## - # ServerNameIndication callback - - def ssl_servername_callback(sslsocket, hostname = nil) - req = SNIRequest.new(sslsocket, hostname) - server = lookup_server(req) - server ? server.ssl_context : nil - end - - # :stopdoc: - - ## - # Check whether +server+ is also SSL server. - # Also +server+'s SSL context will be created. - - alias orig_virtual_host virtual_host - - def virtual_host(server) - if @config[:SSLEnable] && !server.ssl_context - raise ArgumentError, "virtual host must set SSLEnable to true" - end - orig_virtual_host(server) - end - - # :startdoc: - end -end diff --git a/tool/lib/webrick/httpserver.rb b/tool/lib/webrick/httpserver.rb deleted file mode 100644 index f3f948da3bf9ba..00000000000000 --- a/tool/lib/webrick/httpserver.rb +++ /dev/null @@ -1,293 +0,0 @@ -# frozen_string_literal: false -# -# httpserver.rb -- HTTPServer Class -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: httpserver.rb,v 1.63 2002/10/01 17:16:32 gotoyuzo Exp $ - -require_relative 'server' -require_relative 'httputils' -require_relative 'httpstatus' -require_relative 'httprequest' -require_relative 'httpresponse' -require_relative 'httpservlet' -require_relative 'accesslog' - -module WEBrick - class HTTPServerError < ServerError; end - - ## - # An HTTP Server - - class HTTPServer < ::WEBrick::GenericServer - ## - # Creates a new HTTP server according to +config+ - # - # An HTTP server uses the following attributes: - # - # :AccessLog:: An array of access logs. See WEBrick::AccessLog - # :BindAddress:: Local address for the server to bind to - # :DocumentRoot:: Root path to serve files from - # :DocumentRootOptions:: Options for the default HTTPServlet::FileHandler - # :HTTPVersion:: The HTTP version of this server - # :Port:: Port to listen on - # :RequestCallback:: Called with a request and response before each - # request is serviced. - # :RequestTimeout:: Maximum time to wait between requests - # :ServerAlias:: Array of alternate names for this server for virtual - # hosting - # :ServerName:: Name for this server for virtual hosting - - def initialize(config={}, default=Config::HTTP) - super(config, default) - @http_version = HTTPVersion::convert(@config[:HTTPVersion]) - - @mount_tab = MountTable.new - if @config[:DocumentRoot] - mount("/", HTTPServlet::FileHandler, @config[:DocumentRoot], - @config[:DocumentRootOptions]) - end - - unless @config[:AccessLog] - @config[:AccessLog] = [ - [ $stderr, AccessLog::COMMON_LOG_FORMAT ], - [ $stderr, AccessLog::REFERER_LOG_FORMAT ] - ] - end - - @virtual_hosts = Array.new - end - - ## - # Processes requests on +sock+ - - def run(sock) - while true - req = create_request(@config) - res = create_response(@config) - server = self - begin - timeout = @config[:RequestTimeout] - while timeout > 0 - break if sock.to_io.wait_readable(0.5) - break if @status != :Running - timeout -= 0.5 - end - raise HTTPStatus::EOFError if timeout <= 0 || @status != :Running - raise HTTPStatus::EOFError if sock.eof? - req.parse(sock) - res.request_method = req.request_method - res.request_uri = req.request_uri - res.request_http_version = req.http_version - res.keep_alive = req.keep_alive? - server = lookup_server(req) || self - if callback = server[:RequestCallback] - callback.call(req, res) - elsif callback = server[:RequestHandler] - msg = ":RequestHandler is deprecated, please use :RequestCallback" - @logger.warn(msg) - callback.call(req, res) - end - server.service(req, res) - rescue HTTPStatus::EOFError, HTTPStatus::RequestTimeout => ex - res.set_error(ex) - rescue HTTPStatus::Error => ex - @logger.error(ex.message) - res.set_error(ex) - rescue HTTPStatus::Status => ex - res.status = ex.code - rescue StandardError => ex - @logger.error(ex) - res.set_error(ex, true) - ensure - if req.request_line - if req.keep_alive? && res.keep_alive? - req.fixup() - end - res.send_response(sock) - server.access_log(@config, req, res) - end - end - break if @http_version < "1.1" - break unless req.keep_alive? - break unless res.keep_alive? - end - end - - ## - # Services +req+ and fills in +res+ - - def service(req, res) - if req.unparsed_uri == "*" - if req.request_method == "OPTIONS" - do_OPTIONS(req, res) - raise HTTPStatus::OK - end - raise HTTPStatus::NotFound, "`#{req.unparsed_uri}' not found." - end - - servlet, options, script_name, path_info = search_servlet(req.path) - raise HTTPStatus::NotFound, "`#{req.path}' not found." unless servlet - req.script_name = script_name - req.path_info = path_info - si = servlet.get_instance(self, *options) - @logger.debug(format("%s is invoked.", si.class.name)) - si.service(req, res) - end - - ## - # The default OPTIONS request handler says GET, HEAD, POST and OPTIONS - # requests are allowed. - - def do_OPTIONS(req, res) - res["allow"] = "GET,HEAD,POST,OPTIONS" - end - - ## - # Mounts +servlet+ on +dir+ passing +options+ to the servlet at creation - # time - - def mount(dir, servlet, *options) - @logger.debug(sprintf("%s is mounted on %s.", servlet.inspect, dir)) - @mount_tab[dir] = [ servlet, options ] - end - - ## - # Mounts +proc+ or +block+ on +dir+ and calls it with a - # WEBrick::HTTPRequest and WEBrick::HTTPResponse - - def mount_proc(dir, proc=nil, &block) - proc ||= block - raise HTTPServerError, "must pass a proc or block" unless proc - mount(dir, HTTPServlet::ProcHandler.new(proc)) - end - - ## - # Unmounts +dir+ - - def unmount(dir) - @logger.debug(sprintf("unmount %s.", dir)) - @mount_tab.delete(dir) - end - alias umount unmount - - ## - # Finds a servlet for +path+ - - def search_servlet(path) - script_name, path_info = @mount_tab.scan(path) - servlet, options = @mount_tab[script_name] - if servlet - [ servlet, options, script_name, path_info ] - end - end - - ## - # Adds +server+ as a virtual host. - - def virtual_host(server) - @virtual_hosts << server - @virtual_hosts = @virtual_hosts.sort_by{|s| - num = 0 - num -= 4 if s[:BindAddress] - num -= 2 if s[:Port] - num -= 1 if s[:ServerName] - num - } - end - - ## - # Finds the appropriate virtual host to handle +req+ - - def lookup_server(req) - @virtual_hosts.find{|s| - (s[:BindAddress].nil? || req.addr[3] == s[:BindAddress]) && - (s[:Port].nil? || req.port == s[:Port]) && - ((s[:ServerName].nil? || req.host == s[:ServerName]) || - (!s[:ServerAlias].nil? && s[:ServerAlias].find{|h| h === req.host})) - } - end - - ## - # Logs +req+ and +res+ in the access logs. +config+ is used for the - # server name. - - def access_log(config, req, res) - param = AccessLog::setup_params(config, req, res) - @config[:AccessLog].each{|logger, fmt| - logger << AccessLog::format(fmt+"\n", param) - } - end - - ## - # Creates the HTTPRequest used when handling the HTTP - # request. Can be overridden by subclasses. - def create_request(with_webrick_config) - HTTPRequest.new(with_webrick_config) - end - - ## - # Creates the HTTPResponse used when handling the HTTP - # request. Can be overridden by subclasses. - def create_response(with_webrick_config) - HTTPResponse.new(with_webrick_config) - end - - ## - # Mount table for the path a servlet is mounted on in the directory space - # of the server. Users of WEBrick can only access this indirectly via - # WEBrick::HTTPServer#mount, WEBrick::HTTPServer#unmount and - # WEBrick::HTTPServer#search_servlet - - class MountTable # :nodoc: - def initialize - @tab = Hash.new - compile - end - - def [](dir) - dir = normalize(dir) - @tab[dir] - end - - def []=(dir, val) - dir = normalize(dir) - @tab[dir] = val - compile - val - end - - def delete(dir) - dir = normalize(dir) - res = @tab.delete(dir) - compile - res - end - - def scan(path) - @scanner =~ path - [ $&, $' ] - end - - private - - def compile - k = @tab.keys - k.sort! - k.reverse! - k.collect!{|path| Regexp.escape(path) } - @scanner = Regexp.new("\\A(" + k.join("|") +")(?=/|\\z)") - end - - def normalize(dir) - ret = dir ? dir.dup : "" - ret.sub!(%r|/+\z|, "") - ret - end - end - end -end diff --git a/tool/lib/webrick/httpservlet.rb b/tool/lib/webrick/httpservlet.rb deleted file mode 100644 index da49a1405b3af9..00000000000000 --- a/tool/lib/webrick/httpservlet.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: false -# -# httpservlet.rb -- HTTPServlet Utility File -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: httpservlet.rb,v 1.21 2003/02/23 12:24:46 gotoyuzo Exp $ - -require_relative 'httpservlet/abstract' -require_relative 'httpservlet/filehandler' -require_relative 'httpservlet/cgihandler' -require_relative 'httpservlet/erbhandler' -require_relative 'httpservlet/prochandler' - -module WEBrick - module HTTPServlet - FileHandler.add_handler("cgi", CGIHandler) - FileHandler.add_handler("rhtml", ERBHandler) - end -end diff --git a/tool/lib/webrick/httpservlet/abstract.rb b/tool/lib/webrick/httpservlet/abstract.rb deleted file mode 100644 index bccb091861d7fa..00000000000000 --- a/tool/lib/webrick/httpservlet/abstract.rb +++ /dev/null @@ -1,152 +0,0 @@ -# frozen_string_literal: false -# -# httpservlet.rb -- HTTPServlet Module -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: abstract.rb,v 1.24 2003/07/11 11:16:46 gotoyuzo Exp $ - -require_relative '../htmlutils' -require_relative '../httputils' -require_relative '../httpstatus' - -module WEBrick - module HTTPServlet - class HTTPServletError < StandardError; end - - ## - # AbstractServlet allows HTTP server modules to be reused across multiple - # servers and allows encapsulation of functionality. - # - # By default a servlet will respond to GET, HEAD (through an alias to GET) - # and OPTIONS requests. - # - # By default a new servlet is initialized for every request. A servlet - # instance can be reused by overriding ::get_instance in the - # AbstractServlet subclass. - # - # == A Simple Servlet - # - # class Simple < WEBrick::HTTPServlet::AbstractServlet - # def do_GET request, response - # status, content_type, body = do_stuff_with request - # - # response.status = status - # response['Content-Type'] = content_type - # response.body = body - # end - # - # def do_stuff_with request - # return 200, 'text/plain', 'you got a page' - # end - # end - # - # This servlet can be mounted on a server at a given path: - # - # server.mount '/simple', Simple - # - # == Servlet Configuration - # - # Servlets can be configured via initialize. The first argument is the - # HTTP server the servlet is being initialized for. - # - # class Configurable < Simple - # def initialize server, color, size - # super server - # @color = color - # @size = size - # end - # - # def do_stuff_with request - # content = "

Hello, World!" - # - # return 200, "text/html", content - # end - # end - # - # This servlet must be provided two arguments at mount time: - # - # server.mount '/configurable', Configurable, 'red', '2em' - - class AbstractServlet - - ## - # Factory for servlet instances that will handle a request from +server+ - # using +options+ from the mount point. By default a new servlet - # instance is created for every call. - - def self.get_instance(server, *options) - self.new(server, *options) - end - - ## - # Initializes a new servlet for +server+ using +options+ which are - # stored as-is in +@options+. +@logger+ is also provided. - - def initialize(server, *options) - @server = @config = server - @logger = @server[:Logger] - @options = options - end - - ## - # Dispatches to a +do_+ method based on +req+ if such a method is - # available. (+do_GET+ for a GET request). Raises a MethodNotAllowed - # exception if the method is not implemented. - - def service(req, res) - method_name = "do_" + req.request_method.gsub(/-/, "_") - if respond_to?(method_name) - __send__(method_name, req, res) - else - raise HTTPStatus::MethodNotAllowed, - "unsupported method `#{req.request_method}'." - end - end - - ## - # Raises a NotFound exception - - def do_GET(req, res) - raise HTTPStatus::NotFound, "not found." - end - - ## - # Dispatches to do_GET - - def do_HEAD(req, res) - do_GET(req, res) - end - - ## - # Returns the allowed HTTP request methods - - def do_OPTIONS(req, res) - m = self.methods.grep(/\Ado_([A-Z]+)\z/) {$1} - m.sort! - res["allow"] = m.join(",") - end - - private - - ## - # Redirects to a path ending in / - - def redirect_to_directory_uri(req, res) - if req.path[-1] != ?/ - location = WEBrick::HTTPUtils.escape_path(req.path + "/") - if req.query_string && req.query_string.bytesize > 0 - location << "?" << req.query_string - end - res.set_redirect(HTTPStatus::MovedPermanently, location) - end - end - end - - end -end diff --git a/tool/lib/webrick/httpservlet/cgi_runner.rb b/tool/lib/webrick/httpservlet/cgi_runner.rb deleted file mode 100644 index 0398c16749c0c1..00000000000000 --- a/tool/lib/webrick/httpservlet/cgi_runner.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: false -# -# cgi_runner.rb -- CGI launcher. -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU YUUZOU -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: cgi_runner.rb,v 1.9 2002/09/25 11:33:15 gotoyuzo Exp $ - -def sysread(io, size) - buf = "" - while size > 0 - tmp = io.sysread(size) - buf << tmp - size -= tmp.bytesize - end - return buf -end - -STDIN.binmode - -len = sysread(STDIN, 8).to_i -out = sysread(STDIN, len) -STDOUT.reopen(File.open(out, "w")) - -len = sysread(STDIN, 8).to_i -err = sysread(STDIN, len) -STDERR.reopen(File.open(err, "w")) - -len = sysread(STDIN, 8).to_i -dump = sysread(STDIN, len) -hash = Marshal.restore(dump) -ENV.keys.each{|name| ENV.delete(name) } -hash.each{|k, v| ENV[k] = v if v } - -dir = File::dirname(ENV["SCRIPT_FILENAME"]) -Dir::chdir dir - -if ARGV[0] - argv = ARGV.dup - argv << ENV["SCRIPT_FILENAME"] - exec(*argv) - # NOTREACHED -end -exec ENV["SCRIPT_FILENAME"] diff --git a/tool/lib/webrick/httpservlet/cgihandler.rb b/tool/lib/webrick/httpservlet/cgihandler.rb deleted file mode 100644 index 4457770b7a006d..00000000000000 --- a/tool/lib/webrick/httpservlet/cgihandler.rb +++ /dev/null @@ -1,126 +0,0 @@ -# frozen_string_literal: false -# -# cgihandler.rb -- CGIHandler Class -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: cgihandler.rb,v 1.27 2003/03/21 19:56:01 gotoyuzo Exp $ - -require 'rbconfig' -require 'tempfile' -require_relative '../config' -require_relative 'abstract' - -module WEBrick - module HTTPServlet - - ## - # Servlet for handling CGI scripts - # - # Example: - # - # server.mount('/cgi/my_script', WEBrick::HTTPServlet::CGIHandler, - # '/path/to/my_script') - - class CGIHandler < AbstractServlet - Ruby = RbConfig.ruby # :nodoc: - CGIRunner = "\"#{Ruby}\" \"#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb\"" # :nodoc: - CGIRunnerArray = [Ruby, "#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb".freeze].freeze # :nodoc: - - ## - # Creates a new CGI script servlet for the script at +name+ - - def initialize(server, name) - super(server, name) - @script_filename = name - @tempdir = server[:TempDir] - interpreter = server[:CGIInterpreter] - if interpreter.is_a?(Array) - @cgicmd = CGIRunnerArray + interpreter - else - @cgicmd = "#{CGIRunner} #{interpreter}" - end - end - - # :stopdoc: - - def do_GET(req, res) - cgi_in = IO::popen(@cgicmd, "wb") - cgi_out = Tempfile.new("webrick.cgiout.", @tempdir, mode: IO::BINARY) - cgi_out.set_encoding("ASCII-8BIT") - cgi_err = Tempfile.new("webrick.cgierr.", @tempdir, mode: IO::BINARY) - cgi_err.set_encoding("ASCII-8BIT") - begin - cgi_in.sync = true - meta = req.meta_vars - meta["SCRIPT_FILENAME"] = @script_filename - meta["PATH"] = @config[:CGIPathEnv] - meta.delete("HTTP_PROXY") - if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM - meta["SystemRoot"] = ENV["SystemRoot"] - end - dump = Marshal.dump(meta) - - cgi_in.write("%8d" % cgi_out.path.bytesize) - cgi_in.write(cgi_out.path) - cgi_in.write("%8d" % cgi_err.path.bytesize) - cgi_in.write(cgi_err.path) - cgi_in.write("%8d" % dump.bytesize) - cgi_in.write(dump) - - req.body { |chunk| cgi_in.write(chunk) } - ensure - cgi_in.close - status = $?.exitstatus - sleep 0.1 if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM - data = cgi_out.read - cgi_out.close(true) - if errmsg = cgi_err.read - if errmsg.bytesize > 0 - @logger.error("CGIHandler: #{@script_filename}:\n" + errmsg) - end - end - cgi_err.close(true) - end - - if status != 0 - @logger.error("CGIHandler: #{@script_filename} exit with #{status}") - end - - data = "" unless data - raw_header, body = data.split(/^[\xd\xa]+/, 2) - raise HTTPStatus::InternalServerError, - "Premature end of script headers: #{@script_filename}" if body.nil? - - begin - header = HTTPUtils::parse_header(raw_header) - if /^(\d+)/ =~ header['status'][0] - res.status = $1.to_i - header.delete('status') - end - if header.has_key?('location') - # RFC 3875 6.2.3, 6.2.4 - res.status = 302 unless (300...400) === res.status - end - if header.has_key?('set-cookie') - header['set-cookie'].each{|k| - res.cookies << Cookie.parse_set_cookie(k) - } - header.delete('set-cookie') - end - header.each{|key, val| res[key] = val.join(", ") } - rescue => ex - raise HTTPStatus::InternalServerError, ex.message - end - res.body = body - end - alias do_POST do_GET - - # :startdoc: - end - - end -end diff --git a/tool/lib/webrick/httpservlet/erbhandler.rb b/tool/lib/webrick/httpservlet/erbhandler.rb deleted file mode 100644 index cd09e5f2161540..00000000000000 --- a/tool/lib/webrick/httpservlet/erbhandler.rb +++ /dev/null @@ -1,88 +0,0 @@ -# frozen_string_literal: false -# -# erbhandler.rb -- ERBHandler Class -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: erbhandler.rb,v 1.25 2003/02/24 19:25:31 gotoyuzo Exp $ - -require_relative 'abstract' - -require 'erb' - -module WEBrick - module HTTPServlet - - ## - # ERBHandler evaluates an ERB file and returns the result. This handler - # is automatically used if there are .rhtml files in a directory served by - # the FileHandler. - # - # ERBHandler supports GET and POST methods. - # - # The ERB file is evaluated with the local variables +servlet_request+ and - # +servlet_response+ which are a WEBrick::HTTPRequest and - # WEBrick::HTTPResponse respectively. - # - # Example .rhtml file: - # - # Request to <%= servlet_request.request_uri %> - # - # Query params <%= servlet_request.query.inspect %> - - class ERBHandler < AbstractServlet - - ## - # Creates a new ERBHandler on +server+ that will evaluate and serve the - # ERB file +name+ - - def initialize(server, name) - super(server, name) - @script_filename = name - end - - ## - # Handles GET requests - - def do_GET(req, res) - unless defined?(ERB) - @logger.warn "#{self.class}: ERB not defined." - raise HTTPStatus::Forbidden, "ERBHandler cannot work." - end - begin - data = File.open(@script_filename, &:read) - res.body = evaluate(ERB.new(data), req, res) - res['content-type'] ||= - HTTPUtils::mime_type(@script_filename, @config[:MimeTypes]) - rescue StandardError - raise - rescue Exception => ex - @logger.error(ex) - raise HTTPStatus::InternalServerError, ex.message - end - end - - ## - # Handles POST requests - - alias do_POST do_GET - - private - - ## - # Evaluates +erb+ providing +servlet_request+ and +servlet_response+ as - # local variables. - - def evaluate(erb, servlet_request, servlet_response) - Module.new.module_eval{ - servlet_request.meta_vars - servlet_request.query - erb.result(binding) - } - end - end - end -end diff --git a/tool/lib/webrick/httpservlet/filehandler.rb b/tool/lib/webrick/httpservlet/filehandler.rb deleted file mode 100644 index 010df0e918d22f..00000000000000 --- a/tool/lib/webrick/httpservlet/filehandler.rb +++ /dev/null @@ -1,552 +0,0 @@ -# frozen_string_literal: false -# -# filehandler.rb -- FileHandler Module -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2003 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: filehandler.rb,v 1.44 2003/06/07 01:34:51 gotoyuzo Exp $ - -require 'time' - -require_relative '../htmlutils' -require_relative '../httputils' -require_relative '../httpstatus' - -module WEBrick - module HTTPServlet - - ## - # Servlet for serving a single file. You probably want to use the - # FileHandler servlet instead as it handles directories and fancy indexes. - # - # Example: - # - # server.mount('/my_page.txt', WEBrick::HTTPServlet::DefaultFileHandler, - # '/path/to/my_page.txt') - # - # This servlet handles If-Modified-Since and Range requests. - - class DefaultFileHandler < AbstractServlet - - ## - # Creates a DefaultFileHandler instance for the file at +local_path+. - - def initialize(server, local_path) - super(server, local_path) - @local_path = local_path - end - - # :stopdoc: - - def do_GET(req, res) - st = File::stat(@local_path) - mtime = st.mtime - res['etag'] = sprintf("%x-%x-%x", st.ino, st.size, st.mtime.to_i) - - if not_modified?(req, res, mtime, res['etag']) - res.body = '' - raise HTTPStatus::NotModified - elsif req['range'] - make_partial_content(req, res, @local_path, st.size) - raise HTTPStatus::PartialContent - else - mtype = HTTPUtils::mime_type(@local_path, @config[:MimeTypes]) - res['content-type'] = mtype - res['content-length'] = st.size.to_s - res['last-modified'] = mtime.httpdate - res.body = File.open(@local_path, "rb") - end - end - - def not_modified?(req, res, mtime, etag) - if ir = req['if-range'] - begin - if Time.httpdate(ir) >= mtime - return true - end - rescue - if HTTPUtils::split_header_value(ir).member?(res['etag']) - return true - end - end - end - - if (ims = req['if-modified-since']) && Time.parse(ims) >= mtime - return true - end - - if (inm = req['if-none-match']) && - HTTPUtils::split_header_value(inm).member?(res['etag']) - return true - end - - return false - end - - # returns a lambda for webrick/httpresponse.rb send_body_proc - def multipart_body(body, parts, boundary, mtype, filesize) - lambda do |socket| - begin - begin - first = parts.shift - last = parts.shift - socket.write( - "--#{boundary}#{CRLF}" \ - "Content-Type: #{mtype}#{CRLF}" \ - "Content-Range: bytes #{first}-#{last}/#{filesize}#{CRLF}" \ - "#{CRLF}" - ) - - begin - IO.copy_stream(body, socket, last - first + 1, first) - rescue NotImplementedError - body.seek(first, IO::SEEK_SET) - IO.copy_stream(body, socket, last - first + 1) - end - socket.write(CRLF) - end while parts[0] - socket.write("--#{boundary}--#{CRLF}") - ensure - body.close - end - end - end - - def make_partial_content(req, res, filename, filesize) - mtype = HTTPUtils::mime_type(filename, @config[:MimeTypes]) - unless ranges = HTTPUtils::parse_range_header(req['range']) - raise HTTPStatus::BadRequest, - "Unrecognized range-spec: \"#{req['range']}\"" - end - File.open(filename, "rb"){|io| - if ranges.size > 1 - time = Time.now - boundary = "#{time.sec}_#{time.usec}_#{Process::pid}" - parts = [] - ranges.each {|range| - prange = prepare_range(range, filesize) - next if prange[0] < 0 - parts.concat(prange) - } - raise HTTPStatus::RequestRangeNotSatisfiable if parts.empty? - res["content-type"] = "multipart/byteranges; boundary=#{boundary}" - if req.http_version < '1.1' - res['connection'] = 'close' - else - res.chunked = true - end - res.body = multipart_body(io.dup, parts, boundary, mtype, filesize) - elsif range = ranges[0] - first, last = prepare_range(range, filesize) - raise HTTPStatus::RequestRangeNotSatisfiable if first < 0 - res['content-type'] = mtype - res['content-range'] = "bytes #{first}-#{last}/#{filesize}" - res['content-length'] = (last - first + 1).to_s - res.body = io.dup - else - raise HTTPStatus::BadRequest - end - } - end - - def prepare_range(range, filesize) - first = range.first < 0 ? filesize + range.first : range.first - return -1, -1 if first < 0 || first >= filesize - last = range.last < 0 ? filesize + range.last : range.last - last = filesize - 1 if last >= filesize - return first, last - end - - # :startdoc: - end - - ## - # Serves a directory including fancy indexing and a variety of other - # options. - # - # Example: - # - # server.mount('/assets', WEBrick::HTTPServlet::FileHandler, - # '/path/to/assets') - - class FileHandler < AbstractServlet - HandlerTable = Hash.new # :nodoc: - - ## - # Allow custom handling of requests for files with +suffix+ by class - # +handler+ - - def self.add_handler(suffix, handler) - HandlerTable[suffix] = handler - end - - ## - # Remove custom handling of requests for files with +suffix+ - - def self.remove_handler(suffix) - HandlerTable.delete(suffix) - end - - ## - # Creates a FileHandler servlet on +server+ that serves files starting - # at directory +root+ - # - # +options+ may be a Hash containing keys from - # WEBrick::Config::FileHandler or +true+ or +false+. - # - # If +options+ is true or false then +:FancyIndexing+ is enabled or - # disabled respectively. - - def initialize(server, root, options={}, default=Config::FileHandler) - @config = server.config - @logger = @config[:Logger] - @root = File.expand_path(root) - if options == true || options == false - options = { :FancyIndexing => options } - end - @options = default.dup.update(options) - end - - # :stopdoc: - - def set_filesystem_encoding(str) - enc = Encoding.find('filesystem') - if enc == Encoding::US_ASCII - str.b - else - str.dup.force_encoding(enc) - end - end - - def service(req, res) - # if this class is mounted on "/" and /~username is requested. - # we're going to override path information before invoking service. - if defined?(Etc) && @options[:UserDir] && req.script_name.empty? - if %r|^(/~([^/]+))| =~ req.path_info - script_name, user = $1, $2 - path_info = $' - begin - passwd = Etc::getpwnam(user) - @root = File::join(passwd.dir, @options[:UserDir]) - req.script_name = script_name - req.path_info = path_info - rescue - @logger.debug "#{self.class}#do_GET: getpwnam(#{user}) failed" - end - end - end - prevent_directory_traversal(req, res) - super(req, res) - end - - def do_GET(req, res) - unless exec_handler(req, res) - set_dir_list(req, res) - end - end - - def do_POST(req, res) - unless exec_handler(req, res) - raise HTTPStatus::NotFound, "`#{req.path}' not found." - end - end - - def do_OPTIONS(req, res) - unless exec_handler(req, res) - super(req, res) - end - end - - # ToDo - # RFC2518: HTTP Extensions for Distributed Authoring -- WEBDAV - # - # PROPFIND PROPPATCH MKCOL DELETE PUT COPY MOVE - # LOCK UNLOCK - - # RFC3253: Versioning Extensions to WebDAV - # (Web Distributed Authoring and Versioning) - # - # VERSION-CONTROL REPORT CHECKOUT CHECK_IN UNCHECKOUT - # MKWORKSPACE UPDATE LABEL MERGE ACTIVITY - - private - - def trailing_pathsep?(path) - # check for trailing path separator: - # File.dirname("/aaaa/bbbb/") #=> "/aaaa") - # File.dirname("/aaaa/bbbb/x") #=> "/aaaa/bbbb") - # File.dirname("/aaaa/bbbb") #=> "/aaaa") - # File.dirname("/aaaa/bbbbx") #=> "/aaaa") - return File.dirname(path) != File.dirname(path+"x") - end - - def prevent_directory_traversal(req, res) - # Preventing directory traversal on Windows platforms; - # Backslashes (0x5c) in path_info are not interpreted as special - # character in URI notation. So the value of path_info should be - # normalize before accessing to the filesystem. - - # dirty hack for filesystem encoding; in nature, File.expand_path - # should not be used for path normalization. [Bug #3345] - path = req.path_info.dup.force_encoding(Encoding.find("filesystem")) - if trailing_pathsep?(req.path_info) - # File.expand_path removes the trailing path separator. - # Adding a character is a workaround to save it. - # File.expand_path("/aaa/") #=> "/aaa" - # File.expand_path("/aaa/" + "x") #=> "/aaa/x" - expanded = File.expand_path(path + "x") - expanded.chop! # remove trailing "x" - else - expanded = File.expand_path(path) - end - expanded.force_encoding(req.path_info.encoding) - req.path_info = expanded - end - - def exec_handler(req, res) - raise HTTPStatus::NotFound, "`#{req.path}' not found." unless @root - if set_filename(req, res) - handler = get_handler(req, res) - call_callback(:HandlerCallback, req, res) - h = handler.get_instance(@config, res.filename) - h.service(req, res) - return true - end - call_callback(:HandlerCallback, req, res) - return false - end - - def get_handler(req, res) - suffix1 = (/\.(\w+)\z/ =~ res.filename) && $1.downcase - if /\.(\w+)\.([\w\-]+)\z/ =~ res.filename - if @options[:AcceptableLanguages].include?($2.downcase) - suffix2 = $1.downcase - end - end - handler_table = @options[:HandlerTable] - return handler_table[suffix1] || handler_table[suffix2] || - HandlerTable[suffix1] || HandlerTable[suffix2] || - DefaultFileHandler - end - - def set_filename(req, res) - res.filename = @root - path_info = req.path_info.scan(%r|/[^/]*|) - - path_info.unshift("") # dummy for checking @root dir - while base = path_info.first - base = set_filesystem_encoding(base) - break if base == "/" - break unless File.directory?(File.expand_path(res.filename + base)) - shift_path_info(req, res, path_info) - call_callback(:DirectoryCallback, req, res) - end - - if base = path_info.first - base = set_filesystem_encoding(base) - if base == "/" - if file = search_index_file(req, res) - shift_path_info(req, res, path_info, file) - call_callback(:FileCallback, req, res) - return true - end - shift_path_info(req, res, path_info) - elsif file = search_file(req, res, base) - shift_path_info(req, res, path_info, file) - call_callback(:FileCallback, req, res) - return true - else - raise HTTPStatus::NotFound, "`#{req.path}' not found." - end - end - - return false - end - - def check_filename(req, res, name) - if nondisclosure_name?(name) || windows_ambiguous_name?(name) - @logger.warn("the request refers nondisclosure name `#{name}'.") - raise HTTPStatus::NotFound, "`#{req.path}' not found." - end - end - - def shift_path_info(req, res, path_info, base=nil) - tmp = path_info.shift - base = base || set_filesystem_encoding(tmp) - req.path_info = path_info.join - req.script_name << base - res.filename = File.expand_path(res.filename + base) - check_filename(req, res, File.basename(res.filename)) - end - - def search_index_file(req, res) - @config[:DirectoryIndex].each{|index| - if file = search_file(req, res, "/"+index) - return file - end - } - return nil - end - - def search_file(req, res, basename) - langs = @options[:AcceptableLanguages] - path = res.filename + basename - if File.file?(path) - return basename - elsif langs.size > 0 - req.accept_language.each{|lang| - path_with_lang = path + ".#{lang}" - if langs.member?(lang) && File.file?(path_with_lang) - return basename + ".#{lang}" - end - } - (langs - req.accept_language).each{|lang| - path_with_lang = path + ".#{lang}" - if File.file?(path_with_lang) - return basename + ".#{lang}" - end - } - end - return nil - end - - def call_callback(callback_name, req, res) - if cb = @options[callback_name] - cb.call(req, res) - end - end - - def windows_ambiguous_name?(name) - return true if /[. ]+\z/ =~ name - return true if /::\$DATA\z/ =~ name - return false - end - - def nondisclosure_name?(name) - @options[:NondisclosureName].each{|pattern| - if File.fnmatch(pattern, name, File::FNM_CASEFOLD) - return true - end - } - return false - end - - def set_dir_list(req, res) - redirect_to_directory_uri(req, res) - unless @options[:FancyIndexing] - raise HTTPStatus::Forbidden, "no access permission to `#{req.path}'" - end - local_path = res.filename - list = Dir::entries(local_path).collect{|name| - next if name == "." || name == ".." - next if nondisclosure_name?(name) - next if windows_ambiguous_name?(name) - st = (File::stat(File.join(local_path, name)) rescue nil) - if st.nil? - [ name, nil, -1 ] - elsif st.directory? - [ name + "/", st.mtime, -1 ] - else - [ name, st.mtime, st.size ] - end - } - list.compact! - - query = req.query - - d0 = nil - idx = nil - %w[N M S].each_with_index do |q, i| - if d = query.delete(q) - idx ||= i - d0 ||= d - end - end - d0 ||= "A" - idx ||= 0 - d1 = (d0 == "A") ? "D" : "A" - - if d0 == "A" - list.sort!{|a,b| a[idx] <=> b[idx] } - else - list.sort!{|a,b| b[idx] <=> a[idx] } - end - - namewidth = query["NameWidth"] - if namewidth == "*" - namewidth = nil - elsif !namewidth or (namewidth = namewidth.to_i) < 2 - namewidth = 25 - end - query = query.inject('') {|s, (k, v)| s << '&' << HTMLUtils::escape("#{k}=#{v}")} - - type = "text/html" - case enc = Encoding.find('filesystem') - when Encoding::US_ASCII, Encoding::ASCII_8BIT - else - type << "; charset=\"#{enc.name}\"" - end - res['content-type'] = type - - title = "Index of #{HTMLUtils::escape(req.path)}" - res.body = <<-_end_of_html_ - - - - #{title} - - - -

#{title}

- _end_of_html_ - - res.body << "\n" - res.body << "" - res.body << "" - res.body << "\n" - res.body << "\n" - res.body << "\n" - - query.sub!(/\A&/, '?') - list.unshift [ "..", File::mtime(local_path+"/.."), -1 ] - list.each{ |name, time, size| - if name == ".." - dname = "Parent Directory" - elsif namewidth and name.size > namewidth - dname = name[0...(namewidth - 2)] << '..' - else - dname = name - end - s = "" - s << "" - s << "\n" - res.body << s - } - res.body << "
NameLast modifiedSize
#{HTMLUtils::escape(dname)}" << (time ? time.strftime("%Y/%m/%d %H:%M") : "") << "" << (size >= 0 ? size.to_s : "-") << "
" - res.body << "
" - - res.body << <<-_end_of_html_ -
- #{HTMLUtils::escape(@config[:ServerSoftware])}
- at #{req.host}:#{req.port} -
- - - _end_of_html_ - end - - # :startdoc: - end - end -end diff --git a/tool/lib/webrick/httpservlet/prochandler.rb b/tool/lib/webrick/httpservlet/prochandler.rb deleted file mode 100644 index 599ffc43408d37..00000000000000 --- a/tool/lib/webrick/httpservlet/prochandler.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: false -# -# prochandler.rb -- ProcHandler Class -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: prochandler.rb,v 1.7 2002/09/21 12:23:42 gotoyuzo Exp $ - -require_relative 'abstract' - -module WEBrick - module HTTPServlet - - ## - # Mounts a proc at a path that accepts a request and response. - # - # Instead of mounting this servlet with WEBrick::HTTPServer#mount use - # WEBrick::HTTPServer#mount_proc: - # - # server.mount_proc '/' do |req, res| - # res.body = 'it worked!' - # res.status = 200 - # end - - class ProcHandler < AbstractServlet - # :stopdoc: - def get_instance(server, *options) - self - end - - def initialize(proc) - @proc = proc - end - - def do_GET(request, response) - @proc.call(request, response) - end - - alias do_POST do_GET - # :startdoc: - end - - end -end diff --git a/tool/lib/webrick/httpstatus.rb b/tool/lib/webrick/httpstatus.rb deleted file mode 100644 index c811f21964990c..00000000000000 --- a/tool/lib/webrick/httpstatus.rb +++ /dev/null @@ -1,194 +0,0 @@ -# frozen_string_literal: false -#-- -# httpstatus.rb -- HTTPStatus Class -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: httpstatus.rb,v 1.11 2003/03/24 20:18:55 gotoyuzo Exp $ - -require_relative 'accesslog' - -module WEBrick - - ## - # This module is used to manager HTTP status codes. - # - # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html for more - # information. - module HTTPStatus - - ## - # Root of the HTTP status class hierarchy - class Status < StandardError - class << self - attr_reader :code, :reason_phrase # :nodoc: - end - - # Returns the HTTP status code - def code() self::class::code end - - # Returns the HTTP status description - def reason_phrase() self::class::reason_phrase end - - alias to_i code # :nodoc: - end - - # Root of the HTTP info statuses - class Info < Status; end - # Root of the HTTP success statuses - class Success < Status; end - # Root of the HTTP redirect statuses - class Redirect < Status; end - # Root of the HTTP error statuses - class Error < Status; end - # Root of the HTTP client error statuses - class ClientError < Error; end - # Root of the HTTP server error statuses - class ServerError < Error; end - - class EOFError < StandardError; end - - # HTTP status codes and descriptions - StatusMessage = { # :nodoc: - 100 => 'Continue', - 101 => 'Switching Protocols', - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - 207 => 'Multi-Status', - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 307 => 'Temporary Redirect', - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Large', - 415 => 'Unsupported Media Type', - 416 => 'Request Range Not Satisfiable', - 417 => 'Expectation Failed', - 422 => 'Unprocessable Entity', - 423 => 'Locked', - 424 => 'Failed Dependency', - 426 => 'Upgrade Required', - 428 => 'Precondition Required', - 429 => 'Too Many Requests', - 431 => 'Request Header Fields Too Large', - 451 => 'Unavailable For Legal Reasons', - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported', - 507 => 'Insufficient Storage', - 511 => 'Network Authentication Required', - } - - # Maps a status code to the corresponding Status class - CodeToError = {} # :nodoc: - - # Creates a status or error class for each status code and - # populates the CodeToError map. - StatusMessage.each{|code, message| - message.freeze - var_name = message.gsub(/[ \-]/,'_').upcase - err_name = message.gsub(/[ \-]/,'') - - case code - when 100...200; parent = Info - when 200...300; parent = Success - when 300...400; parent = Redirect - when 400...500; parent = ClientError - when 500...600; parent = ServerError - end - - const_set("RC_#{var_name}", code) - err_class = Class.new(parent) - err_class.instance_variable_set(:@code, code) - err_class.instance_variable_set(:@reason_phrase, message) - const_set(err_name, err_class) - CodeToError[code] = err_class - } - - ## - # Returns the description corresponding to the HTTP status +code+ - # - # WEBrick::HTTPStatus.reason_phrase 404 - # => "Not Found" - def reason_phrase(code) - StatusMessage[code.to_i] - end - - ## - # Is +code+ an informational status? - def info?(code) - code.to_i >= 100 and code.to_i < 200 - end - - ## - # Is +code+ a successful status? - def success?(code) - code.to_i >= 200 and code.to_i < 300 - end - - ## - # Is +code+ a redirection status? - def redirect?(code) - code.to_i >= 300 and code.to_i < 400 - end - - ## - # Is +code+ an error status? - def error?(code) - code.to_i >= 400 and code.to_i < 600 - end - - ## - # Is +code+ a client error status? - def client_error?(code) - code.to_i >= 400 and code.to_i < 500 - end - - ## - # Is +code+ a server error status? - def server_error?(code) - code.to_i >= 500 and code.to_i < 600 - end - - ## - # Returns the status class corresponding to +code+ - # - # WEBrick::HTTPStatus[302] - # => WEBrick::HTTPStatus::NotFound - # - def self.[](code) - CodeToError[code] - end - - module_function :reason_phrase - module_function :info?, :success?, :redirect?, :error? - module_function :client_error?, :server_error? - end -end diff --git a/tool/lib/webrick/httputils.rb b/tool/lib/webrick/httputils.rb deleted file mode 100644 index e21284ee7f9097..00000000000000 --- a/tool/lib/webrick/httputils.rb +++ /dev/null @@ -1,512 +0,0 @@ -# frozen_string_literal: false -# -# httputils.rb -- HTTPUtils Module -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: httputils.rb,v 1.34 2003/06/05 21:34:08 gotoyuzo Exp $ - -require 'socket' -require 'tempfile' - -module WEBrick - CR = "\x0d" # :nodoc: - LF = "\x0a" # :nodoc: - CRLF = "\x0d\x0a" # :nodoc: - - ## - # HTTPUtils provides utility methods for working with the HTTP protocol. - # - # This module is generally used internally by WEBrick - - module HTTPUtils - - ## - # Normalizes a request path. Raises an exception if the path cannot be - # normalized. - - def normalize_path(path) - raise "abnormal path `#{path}'" if path[0] != ?/ - ret = path.dup - - ret.gsub!(%r{/+}o, '/') # // => / - while ret.sub!(%r'/\.(?:/|\Z)', '/'); end # /. => / - while ret.sub!(%r'/(?!\.\./)[^/]+/\.\.(?:/|\Z)', '/'); end # /foo/.. => /foo - - raise "abnormal path `#{path}'" if %r{/\.\.(/|\Z)} =~ ret - ret - end - module_function :normalize_path - - ## - # Default mime types - - DefaultMimeTypes = { - "ai" => "application/postscript", - "asc" => "text/plain", - "avi" => "video/x-msvideo", - "bin" => "application/octet-stream", - "bmp" => "image/bmp", - "class" => "application/octet-stream", - "cer" => "application/pkix-cert", - "crl" => "application/pkix-crl", - "crt" => "application/x-x509-ca-cert", - #"crl" => "application/x-pkcs7-crl", - "css" => "text/css", - "dms" => "application/octet-stream", - "doc" => "application/msword", - "dvi" => "application/x-dvi", - "eps" => "application/postscript", - "etx" => "text/x-setext", - "exe" => "application/octet-stream", - "gif" => "image/gif", - "htm" => "text/html", - "html" => "text/html", - "jpe" => "image/jpeg", - "jpeg" => "image/jpeg", - "jpg" => "image/jpeg", - "js" => "application/javascript", - "json" => "application/json", - "lha" => "application/octet-stream", - "lzh" => "application/octet-stream", - "mjs" => "application/javascript", - "mov" => "video/quicktime", - "mpe" => "video/mpeg", - "mpeg" => "video/mpeg", - "mpg" => "video/mpeg", - "pbm" => "image/x-portable-bitmap", - "pdf" => "application/pdf", - "pgm" => "image/x-portable-graymap", - "png" => "image/png", - "pnm" => "image/x-portable-anymap", - "ppm" => "image/x-portable-pixmap", - "ppt" => "application/vnd.ms-powerpoint", - "ps" => "application/postscript", - "qt" => "video/quicktime", - "ras" => "image/x-cmu-raster", - "rb" => "text/plain", - "rd" => "text/plain", - "rtf" => "application/rtf", - "sgm" => "text/sgml", - "sgml" => "text/sgml", - "svg" => "image/svg+xml", - "tif" => "image/tiff", - "tiff" => "image/tiff", - "txt" => "text/plain", - "wasm" => "application/wasm", - "xbm" => "image/x-xbitmap", - "xhtml" => "text/html", - "xls" => "application/vnd.ms-excel", - "xml" => "text/xml", - "xpm" => "image/x-xpixmap", - "xwd" => "image/x-xwindowdump", - "zip" => "application/zip", - } - - ## - # Loads Apache-compatible mime.types in +file+. - - def load_mime_types(file) - # note: +file+ may be a "| command" for now; some people may - # rely on this, but currently we do not use this method by default. - File.open(file){ |io| - hash = Hash.new - io.each{ |line| - next if /^#/ =~ line - line.chomp! - mimetype, ext0 = line.split(/\s+/, 2) - next unless ext0 - next if ext0.empty? - ext0.split(/\s+/).each{ |ext| hash[ext] = mimetype } - } - hash - } - end - module_function :load_mime_types - - ## - # Returns the mime type of +filename+ from the list in +mime_tab+. If no - # mime type was found application/octet-stream is returned. - - def mime_type(filename, mime_tab) - suffix1 = (/\.(\w+)$/ =~ filename && $1.downcase) - suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ filename && $1.downcase) - mime_tab[suffix1] || mime_tab[suffix2] || "application/octet-stream" - end - module_function :mime_type - - ## - # Parses an HTTP header +raw+ into a hash of header fields with an Array - # of values. - - def parse_header(raw) - header = Hash.new([].freeze) - field = nil - raw.each_line{|line| - case line - when /^([A-Za-z0-9!\#$%&'*+\-.^_`|~]+):\s*(.*?)\s*\z/om - field, value = $1, $2 - field.downcase! - header[field] = [] unless header.has_key?(field) - header[field] << value - when /^\s+(.*?)\s*\z/om - value = $1 - unless field - raise HTTPStatus::BadRequest, "bad header '#{line}'." - end - header[field][-1] << " " << value - else - raise HTTPStatus::BadRequest, "bad header '#{line}'." - end - } - header.each{|key, values| - values.each(&:strip!) - } - header - end - module_function :parse_header - - ## - # Splits a header value +str+ according to HTTP specification. - - def split_header_value(str) - str.scan(%r'\G((?:"(?:\\.|[^"])+?"|[^",]+)+) - (?:,\s*|\Z)'xn).flatten - end - module_function :split_header_value - - ## - # Parses a Range header value +ranges_specifier+ - - def parse_range_header(ranges_specifier) - if /^bytes=(.*)/ =~ ranges_specifier - byte_range_set = split_header_value($1) - byte_range_set.collect{|range_spec| - case range_spec - when /^(\d+)-(\d+)/ then $1.to_i .. $2.to_i - when /^(\d+)-/ then $1.to_i .. -1 - when /^-(\d+)/ then -($1.to_i) .. -1 - else return nil - end - } - end - end - module_function :parse_range_header - - ## - # Parses q values in +value+ as used in Accept headers. - - def parse_qvalues(value) - tmp = [] - if value - parts = value.split(/,\s*/) - parts.each {|part| - if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part) - val = m[1] - q = (m[2] or 1).to_f - tmp.push([val, q]) - end - } - tmp = tmp.sort_by{|val, q| -q} - tmp.collect!{|val, q| val} - end - return tmp - end - module_function :parse_qvalues - - ## - # Removes quotes and escapes from +str+ - - def dequote(str) - ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup - ret.gsub!(/\\(.)/, "\\1") - ret - end - module_function :dequote - - ## - # Quotes and escapes quotes in +str+ - - def quote(str) - '"' << str.gsub(/[\\\"]/o, "\\\1") << '"' - end - module_function :quote - - ## - # Stores multipart form data. FormData objects are created when - # WEBrick::HTTPUtils.parse_form_data is called. - - class FormData < String - EmptyRawHeader = [].freeze # :nodoc: - EmptyHeader = {}.freeze # :nodoc: - - ## - # The name of the form data part - - attr_accessor :name - - ## - # The filename of the form data part - - attr_accessor :filename - - attr_accessor :next_data # :nodoc: - protected :next_data - - ## - # Creates a new FormData object. - # - # +args+ is an Array of form data entries. One FormData will be created - # for each entry. - # - # This is called by WEBrick::HTTPUtils.parse_form_data for you - - def initialize(*args) - @name = @filename = @next_data = nil - if args.empty? - @raw_header = [] - @header = nil - super("") - else - @raw_header = EmptyRawHeader - @header = EmptyHeader - super(args.shift) - unless args.empty? - @next_data = self.class.new(*args) - end - end - end - - ## - # Retrieves the header at the first entry in +key+ - - def [](*key) - begin - @header[key[0].downcase].join(", ") - rescue StandardError, NameError - super - end - end - - ## - # Adds +str+ to this FormData which may be the body, a header or a - # header entry. - # - # This is called by WEBrick::HTTPUtils.parse_form_data for you - - def <<(str) - if @header - super - elsif str == CRLF - @header = HTTPUtils::parse_header(@raw_header.join) - if cd = self['content-disposition'] - if /\s+name="(.*?)"/ =~ cd then @name = $1 end - if /\s+filename="(.*?)"/ =~ cd then @filename = $1 end - end - else - @raw_header << str - end - self - end - - ## - # Adds +data+ at the end of the chain of entries - # - # This is called by WEBrick::HTTPUtils.parse_form_data for you. - - def append_data(data) - tmp = self - while tmp - unless tmp.next_data - tmp.next_data = data - break - end - tmp = tmp.next_data - end - self - end - - ## - # Yields each entry in this FormData - - def each_data - tmp = self - while tmp - next_data = tmp.next_data - yield(tmp) - tmp = next_data - end - end - - ## - # Returns all the FormData as an Array - - def list - ret = [] - each_data{|data| - ret << data.to_s - } - ret - end - - ## - # A FormData will behave like an Array - - alias :to_ary :list - - ## - # This FormData's body - - def to_s - String.new(self) - end - end - - ## - # Parses the query component of a URI in +str+ - - def parse_query(str) - query = Hash.new - if str - str.split(/[&;]/).each{|x| - next if x.empty? - key, val = x.split(/=/,2) - key = unescape_form(key) - val = unescape_form(val.to_s) - val = FormData.new(val) - val.name = key - if query.has_key?(key) - query[key].append_data(val) - next - end - query[key] = val - } - end - query - end - module_function :parse_query - - ## - # Parses form data in +io+ with the given +boundary+ - - def parse_form_data(io, boundary) - boundary_regexp = /\A--#{Regexp.quote(boundary)}(--)?#{CRLF}\z/ - form_data = Hash.new - return form_data unless io - data = nil - io.each_line{|line| - if boundary_regexp =~ line - if data - data.chop! - key = data.name - if form_data.has_key?(key) - form_data[key].append_data(data) - else - form_data[key] = data - end - end - data = FormData.new - next - else - if data - data << line - end - end - } - return form_data - end - module_function :parse_form_data - - ##### - - reserved = ';/?:@&=+$,' - num = '0123456789' - lowalpha = 'abcdefghijklmnopqrstuvwxyz' - upalpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' - mark = '-_.!~*\'()' - unreserved = num + lowalpha + upalpha + mark - control = (0x0..0x1f).collect{|c| c.chr }.join + "\x7f" - space = " " - delims = '<>#%"' - unwise = '{}|\\^[]`' - nonascii = (0x80..0xff).collect{|c| c.chr }.join - - module_function - - # :stopdoc: - - def _make_regex(str) /([#{Regexp.escape(str)}])/n end - def _make_regex!(str) /([^#{Regexp.escape(str)}])/n end - def _escape(str, regex) - str = str.b - str.gsub!(regex) {"%%%02X" % $1.ord} - # %-escaped string should contain US-ASCII only - str.force_encoding(Encoding::US_ASCII) - end - def _unescape(str, regex) - str = str.b - str.gsub!(regex) {$1.hex.chr} - # encoding of %-unescaped string is unknown - str - end - - UNESCAPED = _make_regex(control+space+delims+unwise+nonascii) - UNESCAPED_FORM = _make_regex(reserved+control+delims+unwise+nonascii) - NONASCII = _make_regex(nonascii) - ESCAPED = /%([0-9a-fA-F]{2})/ - UNESCAPED_PCHAR = _make_regex!(unreserved+":@&=+$,") - - # :startdoc: - - ## - # Escapes HTTP reserved and unwise characters in +str+ - - def escape(str) - _escape(str, UNESCAPED) - end - - ## - # Unescapes HTTP reserved and unwise characters in +str+ - - def unescape(str) - _unescape(str, ESCAPED) - end - - ## - # Escapes form reserved characters in +str+ - - def escape_form(str) - ret = _escape(str, UNESCAPED_FORM) - ret.gsub!(/ /, "+") - ret - end - - ## - # Unescapes form reserved characters in +str+ - - def unescape_form(str) - _unescape(str.gsub(/\+/, " "), ESCAPED) - end - - ## - # Escapes path +str+ - - def escape_path(str) - result = "" - str.scan(%r{/([^/]*)}).each{|i| - result << "/" << _escape(i[0], UNESCAPED_PCHAR) - } - return result - end - - ## - # Escapes 8 bit characters in +str+ - - def escape8bit(str) - _escape(str, NONASCII) - end - end -end diff --git a/tool/lib/webrick/httpversion.rb b/tool/lib/webrick/httpversion.rb deleted file mode 100644 index 8a251944a2c067..00000000000000 --- a/tool/lib/webrick/httpversion.rb +++ /dev/null @@ -1,76 +0,0 @@ -# frozen_string_literal: false -#-- -# HTTPVersion.rb -- presentation of HTTP version -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: httpversion.rb,v 1.5 2002/09/21 12:23:37 gotoyuzo Exp $ - -module WEBrick - - ## - # Represents an HTTP protocol version - - class HTTPVersion - include Comparable - - ## - # The major protocol version number - - attr_accessor :major - - ## - # The minor protocol version number - - attr_accessor :minor - - ## - # Converts +version+ into an HTTPVersion - - def self.convert(version) - version.is_a?(self) ? version : new(version) - end - - ## - # Creates a new HTTPVersion from +version+. - - def initialize(version) - case version - when HTTPVersion - @major, @minor = version.major, version.minor - when String - if /^(\d+)\.(\d+)$/ =~ version - @major, @minor = $1.to_i, $2.to_i - end - end - if @major.nil? || @minor.nil? - raise ArgumentError, - format("cannot convert %s into %s", version.class, self.class) - end - end - - ## - # Compares this version with +other+ according to the HTTP specification - # rules. - - def <=>(other) - unless other.is_a?(self.class) - other = self.class.new(other) - end - if (ret = @major <=> other.major) == 0 - return @minor <=> other.minor - end - return ret - end - - ## - # The HTTP version as show in the HTTP request and response. For example, - # "1.1" - - def to_s - format("%d.%d", @major, @minor) - end - end -end diff --git a/tool/lib/webrick/log.rb b/tool/lib/webrick/log.rb deleted file mode 100644 index 2c1fdfe602bdc0..00000000000000 --- a/tool/lib/webrick/log.rb +++ /dev/null @@ -1,156 +0,0 @@ -# frozen_string_literal: false -#-- -# log.rb -- Log Class -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: log.rb,v 1.26 2002/10/06 17:06:10 gotoyuzo Exp $ - -module WEBrick - - ## - # A generic logging class - - class BasicLog - - # Fatal log level which indicates a server crash - - FATAL = 1 - - # Error log level which indicates a recoverable error - - ERROR = 2 - - # Warning log level which indicates a possible problem - - WARN = 3 - - # Information log level which indicates possibly useful information - - INFO = 4 - - # Debugging error level for messages used in server development or - # debugging - - DEBUG = 5 - - # log-level, messages above this level will be logged - attr_accessor :level - - ## - # Initializes a new logger for +log_file+ that outputs messages at +level+ - # or higher. +log_file+ can be a filename, an IO-like object that - # responds to #<< or nil which outputs to $stderr. - # - # If no level is given INFO is chosen by default - - def initialize(log_file=nil, level=nil) - @level = level || INFO - case log_file - when String - @log = File.open(log_file, "a+") - @log.sync = true - @opened = true - when NilClass - @log = $stderr - else - @log = log_file # requires "<<". (see BasicLog#log) - end - end - - ## - # Closes the logger (also closes the log device associated to the logger) - def close - @log.close if @opened - @log = nil - end - - ## - # Logs +data+ at +level+ if the given level is above the current log - # level. - - def log(level, data) - if @log && level <= @level - data += "\n" if /\n\Z/ !~ data - @log << data - end - end - - ## - # Synonym for log(INFO, obj.to_s) - def <<(obj) - log(INFO, obj.to_s) - end - - # Shortcut for logging a FATAL message - def fatal(msg) log(FATAL, "FATAL " << format(msg)); end - # Shortcut for logging an ERROR message - def error(msg) log(ERROR, "ERROR " << format(msg)); end - # Shortcut for logging a WARN message - def warn(msg) log(WARN, "WARN " << format(msg)); end - # Shortcut for logging an INFO message - def info(msg) log(INFO, "INFO " << format(msg)); end - # Shortcut for logging a DEBUG message - def debug(msg) log(DEBUG, "DEBUG " << format(msg)); end - - # Will the logger output FATAL messages? - def fatal?; @level >= FATAL; end - # Will the logger output ERROR messages? - def error?; @level >= ERROR; end - # Will the logger output WARN messages? - def warn?; @level >= WARN; end - # Will the logger output INFO messages? - def info?; @level >= INFO; end - # Will the logger output DEBUG messages? - def debug?; @level >= DEBUG; end - - private - - ## - # Formats +arg+ for the logger - # - # * If +arg+ is an Exception, it will format the error message and - # the back trace. - # * If +arg+ responds to #to_str, it will return it. - # * Otherwise it will return +arg+.inspect. - def format(arg) - if arg.is_a?(Exception) - "#{arg.class}: #{AccessLog.escape(arg.message)}\n\t" << - arg.backtrace.join("\n\t") << "\n" - elsif arg.respond_to?(:to_str) - AccessLog.escape(arg.to_str) - else - arg.inspect - end - end - end - - ## - # A logging class that prepends a timestamp to each message. - - class Log < BasicLog - # Format of the timestamp which is applied to each logged line. The - # default is "[%Y-%m-%d %H:%M:%S]" - attr_accessor :time_format - - ## - # Same as BasicLog#initialize - # - # You can set the timestamp format through #time_format - def initialize(log_file=nil, level=nil) - super(log_file, level) - @time_format = "[%Y-%m-%d %H:%M:%S]" - end - - ## - # Same as BasicLog#log - def log(level, data) - tmp = Time.now.strftime(@time_format) - tmp << " " << data - super(level, tmp) - end - end -end diff --git a/tool/lib/webrick/server.rb b/tool/lib/webrick/server.rb deleted file mode 100644 index fd6b7a61b56e2b..00000000000000 --- a/tool/lib/webrick/server.rb +++ /dev/null @@ -1,381 +0,0 @@ -# frozen_string_literal: false -# -# server.rb -- GenericServer Class -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: server.rb,v 1.62 2003/07/22 19:20:43 gotoyuzo Exp $ - -require 'socket' -require_relative 'config' -require_relative 'log' - -module WEBrick - - ## - # Server error exception - - class ServerError < StandardError; end - - ## - # Base server class - - class SimpleServer - - ## - # A SimpleServer only yields when you start it - - def SimpleServer.start - yield - end - end - - ## - # A generic module for daemonizing a process - - class Daemon - - ## - # Performs the standard operations for daemonizing a process. Runs a - # block, if given. - - def Daemon.start - Process.daemon - File.umask(0) - yield if block_given? - end - end - - ## - # Base TCP server class. You must subclass GenericServer and provide a #run - # method. - - class GenericServer - - ## - # The server status. One of :Stop, :Running or :Shutdown - - attr_reader :status - - ## - # The server configuration - - attr_reader :config - - ## - # The server logger. This is independent from the HTTP access log. - - attr_reader :logger - - ## - # Tokens control the number of outstanding clients. The - # :MaxClients configuration sets this. - - attr_reader :tokens - - ## - # Sockets listening for connections. - - attr_reader :listeners - - ## - # Creates a new generic server from +config+. The default configuration - # comes from +default+. - - def initialize(config={}, default=Config::General) - @config = default.dup.update(config) - @status = :Stop - @config[:Logger] ||= Log::new - @logger = @config[:Logger] - - @tokens = Thread::SizedQueue.new(@config[:MaxClients]) - @config[:MaxClients].times{ @tokens.push(nil) } - - webrickv = WEBrick::VERSION - rubyv = "#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]" - @logger.info("WEBrick #{webrickv}") - @logger.info("ruby #{rubyv}") - - @listeners = [] - @shutdown_pipe = nil - unless @config[:DoNotListen] - raise ArgumentError, "Port must an integer" unless @config[:Port].to_s == @config[:Port].to_i.to_s - - @config[:Port] = @config[:Port].to_i - if @config[:Listen] - warn(":Listen option is deprecated; use GenericServer#listen", uplevel: 1) - end - listen(@config[:BindAddress], @config[:Port]) - if @config[:Port] == 0 - @config[:Port] = @listeners[0].addr[1] - end - end - end - - ## - # Retrieves +key+ from the configuration - - def [](key) - @config[key] - end - - ## - # Adds listeners from +address+ and +port+ to the server. See - # WEBrick::Utils::create_listeners for details. - - def listen(address, port) - @listeners += Utils::create_listeners(address, port) - end - - ## - # Starts the server and runs the +block+ for each connection. This method - # does not return until the server is stopped from a signal handler or - # another thread using #stop or #shutdown. - # - # If the block raises a subclass of StandardError the exception is logged - # and ignored. If an IOError or Errno::EBADF exception is raised the - # exception is ignored. If an Exception subclass is raised the exception - # is logged and re-raised which stops the server. - # - # To completely shut down a server call #shutdown from ensure: - # - # server = WEBrick::GenericServer.new - # # or WEBrick::HTTPServer.new - # - # begin - # server.start - # ensure - # server.shutdown - # end - - def start(&block) - raise ServerError, "already started." if @status != :Stop - server_type = @config[:ServerType] || SimpleServer - - setup_shutdown_pipe - - server_type.start{ - @logger.info \ - "#{self.class}#start: pid=#{$$} port=#{@config[:Port]}" - @status = :Running - call_callback(:StartCallback) - - shutdown_pipe = @shutdown_pipe - - thgroup = ThreadGroup.new - begin - while @status == :Running - begin - sp = shutdown_pipe[0] - if svrs = IO.select([sp, *@listeners]) - if svrs[0].include? sp - # swallow shutdown pipe - buf = String.new - nil while String === - sp.read_nonblock([sp.nread, 8].max, buf, exception: false) - break - end - svrs[0].each{|svr| - @tokens.pop # blocks while no token is there. - if sock = accept_client(svr) - unless config[:DoNotReverseLookup].nil? - sock.do_not_reverse_lookup = !!config[:DoNotReverseLookup] - end - th = start_thread(sock, &block) - th[:WEBrickThread] = true - thgroup.add(th) - else - @tokens.push(nil) - end - } - end - rescue Errno::EBADF, Errno::ENOTSOCK, IOError => ex - # if the listening socket was closed in GenericServer#shutdown, - # IO::select raise it. - rescue StandardError => ex - msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}" - @logger.error msg - rescue Exception => ex - @logger.fatal ex - raise - end - end - ensure - cleanup_shutdown_pipe(shutdown_pipe) - cleanup_listener - @status = :Shutdown - @logger.info "going to shutdown ..." - thgroup.list.each{|th| th.join if th[:WEBrickThread] } - call_callback(:StopCallback) - @logger.info "#{self.class}#start done." - @status = :Stop - end - } - end - - ## - # Stops the server from accepting new connections. - - def stop - if @status == :Running - @status = :Shutdown - end - - alarm_shutdown_pipe {|f| f.write_nonblock("\0")} - end - - ## - # Shuts down the server and all listening sockets. New listeners must be - # provided to restart the server. - - def shutdown - stop - - alarm_shutdown_pipe(&:close) - end - - ## - # You must subclass GenericServer and implement \#run which accepts a TCP - # client socket - - def run(sock) - @logger.fatal "run() must be provided by user." - end - - private - - # :stopdoc: - - ## - # Accepts a TCP client socket from the TCP server socket +svr+ and returns - # the client socket. - - def accept_client(svr) - case sock = svr.to_io.accept_nonblock(exception: false) - when :wait_readable - nil - else - if svr.respond_to?(:start_immediately) - sock = OpenSSL::SSL::SSLSocket.new(sock, ssl_context) - sock.sync_close = true - # we cannot do OpenSSL::SSL::SSLSocket#accept here because - # a slow client can prevent us from accepting connections - # from other clients - end - sock - end - rescue Errno::ECONNRESET, Errno::ECONNABORTED, - Errno::EPROTO, Errno::EINVAL - nil - rescue StandardError => ex - msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}" - @logger.error msg - nil - end - - ## - # Starts a server thread for the client socket +sock+ that runs the given - # +block+. - # - # Sets the socket to the :WEBrickSocket thread local variable - # in the thread. - # - # If any errors occur in the block they are logged and handled. - - def start_thread(sock, &block) - Thread.start{ - begin - Thread.current[:WEBrickSocket] = sock - begin - addr = sock.peeraddr - @logger.debug "accept: #{addr[3]}:#{addr[1]}" - rescue SocketError - @logger.debug "accept:
" - raise - end - if sock.respond_to?(:sync_close=) && @config[:SSLStartImmediately] - WEBrick::Utils.timeout(@config[:RequestTimeout]) do - begin - sock.accept # OpenSSL::SSL::SSLSocket#accept - rescue Errno::ECONNRESET, Errno::ECONNABORTED, - Errno::EPROTO, Errno::EINVAL - Thread.exit - end - end - end - call_callback(:AcceptCallback, sock) - block ? block.call(sock) : run(sock) - rescue Errno::ENOTCONN - @logger.debug "Errno::ENOTCONN raised" - rescue ServerError => ex - msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}" - @logger.error msg - rescue Exception => ex - @logger.error ex - ensure - @tokens.push(nil) - Thread.current[:WEBrickSocket] = nil - if addr - @logger.debug "close: #{addr[3]}:#{addr[1]}" - else - @logger.debug "close:
" - end - sock.close - end - } - end - - ## - # Calls the callback +callback_name+ from the configuration with +args+ - - def call_callback(callback_name, *args) - @config[callback_name]&.call(*args) - end - - def setup_shutdown_pipe - return @shutdown_pipe ||= IO.pipe - end - - def cleanup_shutdown_pipe(shutdown_pipe) - @shutdown_pipe = nil - shutdown_pipe&.each(&:close) - end - - def alarm_shutdown_pipe - _, pipe = @shutdown_pipe # another thread may modify @shutdown_pipe. - if pipe - if !pipe.closed? - begin - yield pipe - rescue IOError # closed by another thread. - end - end - end - end - - def cleanup_listener - @listeners.each{|s| - if @logger.debug? - addr = s.addr - @logger.debug("close TCPSocket(#{addr[2]}, #{addr[1]})") - end - begin - s.shutdown - rescue Errno::ENOTCONN - # when `Errno::ENOTCONN: Socket is not connected' on some platforms, - # call #close instead of #shutdown. - # (ignore @config[:ShutdownSocketWithoutClose]) - s.close - else - unless @config[:ShutdownSocketWithoutClose] - s.close - end - end - } - @listeners.clear - end - end # end of GenericServer -end diff --git a/tool/lib/webrick/ssl.rb b/tool/lib/webrick/ssl.rb deleted file mode 100644 index e448095a12f6dd..00000000000000 --- a/tool/lib/webrick/ssl.rb +++ /dev/null @@ -1,215 +0,0 @@ -# frozen_string_literal: false -# -# ssl.rb -- SSL/TLS enhancement for GenericServer -# -# Copyright (c) 2003 GOTOU Yuuzou All rights reserved. -# -# $Id$ - -require 'webrick' -require 'openssl' - -module WEBrick - module Config - svrsoft = General[:ServerSoftware] - osslv = ::OpenSSL::OPENSSL_VERSION.split[1] - - ## - # Default SSL server configuration. - # - # WEBrick can automatically create a self-signed certificate if - # :SSLCertName is set. For more information on the various - # SSL options see OpenSSL::SSL::SSLContext. - # - # :ServerSoftware :: - # The server software name used in the Server: header. - # :SSLEnable :: false, - # Enable SSL for this server. Defaults to false. - # :SSLCertificate :: - # The SSL certificate for the server. - # :SSLPrivateKey :: - # The SSL private key for the server certificate. - # :SSLClientCA :: nil, - # Array of certificates that will be sent to the client. - # :SSLExtraChainCert :: nil, - # Array of certificates that will be added to the certificate chain - # :SSLCACertificateFile :: nil, - # Path to a CA certificate file - # :SSLCACertificatePath :: nil, - # Path to a directory containing CA certificates - # :SSLCertificateStore :: nil, - # OpenSSL::X509::Store used for certificate validation of the client - # :SSLTmpDhCallback :: nil, - # Callback invoked when DH parameters are required. - # :SSLVerifyClient :: - # Sets whether the client is verified. This defaults to VERIFY_NONE - # which is typical for an HTTPS server. - # :SSLVerifyDepth :: - # Number of CA certificates to walk when verifying a certificate chain - # :SSLVerifyCallback :: - # Custom certificate verification callback - # :SSLServerNameCallback:: - # Custom servername indication callback - # :SSLTimeout :: - # Maximum session lifetime - # :SSLOptions :: - # Various SSL options - # :SSLCiphers :: - # Ciphers to be used - # :SSLStartImmediately :: - # Immediately start SSL upon connection? Defaults to true - # :SSLCertName :: - # SSL certificate name. Must be set to enable automatic certificate - # creation. - # :SSLCertComment :: - # Comment used during automatic certificate creation. - - SSL = { - :ServerSoftware => "#{svrsoft} OpenSSL/#{osslv}", - :SSLEnable => false, - :SSLCertificate => nil, - :SSLPrivateKey => nil, - :SSLClientCA => nil, - :SSLExtraChainCert => nil, - :SSLCACertificateFile => nil, - :SSLCACertificatePath => nil, - :SSLCertificateStore => nil, - :SSLTmpDhCallback => nil, - :SSLVerifyClient => ::OpenSSL::SSL::VERIFY_NONE, - :SSLVerifyDepth => nil, - :SSLVerifyCallback => nil, # custom verification - :SSLTimeout => nil, - :SSLOptions => nil, - :SSLCiphers => nil, - :SSLStartImmediately => true, - # Must specify if you use auto generated certificate. - :SSLCertName => nil, - :SSLCertComment => "Generated by Ruby/OpenSSL" - } - General.update(SSL) - end - - module Utils - ## - # Creates a self-signed certificate with the given number of +bits+, - # the issuer +cn+ and a +comment+ to be stored in the certificate. - - def create_self_signed_cert(bits, cn, comment) - rsa = OpenSSL::PKey::RSA.new(bits){|p, n| - case p - when 0; $stderr.putc "." # BN_generate_prime - when 1; $stderr.putc "+" # BN_generate_prime - when 2; $stderr.putc "*" # searching good prime, - # n = #of try, - # but also data from BN_generate_prime - when 3; $stderr.putc "\n" # found good prime, n==0 - p, n==1 - q, - # but also data from BN_generate_prime - else; $stderr.putc "*" # BN_generate_prime - end - } - cert = OpenSSL::X509::Certificate.new - cert.version = 2 - cert.serial = 1 - name = (cn.kind_of? String) ? OpenSSL::X509::Name.parse(cn) - : OpenSSL::X509::Name.new(cn) - cert.subject = name - cert.issuer = name - cert.not_before = Time.now - cert.not_after = Time.now + (365*24*60*60) - cert.public_key = rsa.public_key - - ef = OpenSSL::X509::ExtensionFactory.new(nil,cert) - ef.issuer_certificate = cert - cert.extensions = [ - ef.create_extension("basicConstraints","CA:FALSE"), - ef.create_extension("keyUsage", "keyEncipherment, digitalSignature, keyAgreement, dataEncipherment"), - ef.create_extension("subjectKeyIdentifier", "hash"), - ef.create_extension("extendedKeyUsage", "serverAuth"), - ef.create_extension("nsComment", comment), - ] - aki = ef.create_extension("authorityKeyIdentifier", - "keyid:always,issuer:always") - cert.add_extension(aki) - cert.sign(rsa, "SHA256") - - return [ cert, rsa ] - end - module_function :create_self_signed_cert - end - - ## - #-- - # Updates WEBrick::GenericServer with SSL functionality - - class GenericServer - - ## - # SSL context for the server when run in SSL mode - - def ssl_context # :nodoc: - @ssl_context ||= begin - if @config[:SSLEnable] - ssl_context = setup_ssl_context(@config) - @logger.info("\n" + @config[:SSLCertificate].to_text) - ssl_context - end - end - end - - undef listen - - ## - # Updates +listen+ to enable SSL when the SSL configuration is active. - - def listen(address, port) # :nodoc: - listeners = Utils::create_listeners(address, port) - if @config[:SSLEnable] - listeners.collect!{|svr| - ssvr = ::OpenSSL::SSL::SSLServer.new(svr, ssl_context) - ssvr.start_immediately = @config[:SSLStartImmediately] - ssvr - } - end - @listeners += listeners - setup_shutdown_pipe - end - - ## - # Sets up an SSL context for +config+ - - def setup_ssl_context(config) # :nodoc: - unless config[:SSLCertificate] - cn = config[:SSLCertName] - comment = config[:SSLCertComment] - cert, key = Utils::create_self_signed_cert(2048, cn, comment) - config[:SSLCertificate] = cert - config[:SSLPrivateKey] = key - end - ctx = OpenSSL::SSL::SSLContext.new - ctx.key = config[:SSLPrivateKey] - ctx.cert = config[:SSLCertificate] - ctx.client_ca = config[:SSLClientCA] - ctx.extra_chain_cert = config[:SSLExtraChainCert] - ctx.ca_file = config[:SSLCACertificateFile] - ctx.ca_path = config[:SSLCACertificatePath] - ctx.cert_store = config[:SSLCertificateStore] - ctx.tmp_dh_callback = config[:SSLTmpDhCallback] - ctx.verify_mode = config[:SSLVerifyClient] - ctx.verify_depth = config[:SSLVerifyDepth] - ctx.verify_callback = config[:SSLVerifyCallback] - ctx.servername_cb = config[:SSLServerNameCallback] || proc { |args| ssl_servername_callback(*args) } - ctx.timeout = config[:SSLTimeout] - ctx.options = config[:SSLOptions] - ctx.ciphers = config[:SSLCiphers] - ctx - end - - ## - # ServerNameIndication callback - - def ssl_servername_callback(sslsocket, hostname = nil) - # default - end - - end -end diff --git a/tool/lib/webrick/utils.rb b/tool/lib/webrick/utils.rb deleted file mode 100644 index a96d6f03fd0492..00000000000000 --- a/tool/lib/webrick/utils.rb +++ /dev/null @@ -1,265 +0,0 @@ -# frozen_string_literal: false -# -# utils.rb -- Miscellaneous utilities -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: utils.rb,v 1.10 2003/02/16 22:22:54 gotoyuzo Exp $ - -require 'socket' -require 'io/nonblock' -require 'etc' - -module WEBrick - module Utils - ## - # Sets IO operations on +io+ to be non-blocking - def set_non_blocking(io) - io.nonblock = true if io.respond_to?(:nonblock=) - end - module_function :set_non_blocking - - ## - # Sets the close on exec flag for +io+ - def set_close_on_exec(io) - io.close_on_exec = true if io.respond_to?(:close_on_exec=) - end - module_function :set_close_on_exec - - ## - # Changes the process's uid and gid to the ones of +user+ - def su(user) - if pw = Etc.getpwnam(user) - Process::initgroups(user, pw.gid) - Process::Sys::setgid(pw.gid) - Process::Sys::setuid(pw.uid) - else - warn("WEBrick::Utils::su doesn't work on this platform", uplevel: 1) - end - end - module_function :su - - ## - # The server hostname - def getservername - Socket::gethostname - end - module_function :getservername - - ## - # Creates TCP server sockets bound to +address+:+port+ and returns them. - # - # It will create IPV4 and IPV6 sockets on all interfaces. - def create_listeners(address, port) - unless port - raise ArgumentError, "must specify port" - end - sockets = Socket.tcp_server_sockets(address, port) - sockets = sockets.map {|s| - s.autoclose = false - ts = TCPServer.for_fd(s.fileno) - s.close - ts - } - return sockets - end - module_function :create_listeners - - ## - # Characters used to generate random strings - RAND_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + - "0123456789" + - "abcdefghijklmnopqrstuvwxyz" - - ## - # Generates a random string of length +len+ - def random_string(len) - rand_max = RAND_CHARS.bytesize - ret = "" - len.times{ ret << RAND_CHARS[rand(rand_max)] } - ret - end - module_function :random_string - - ########### - - require "timeout" - require "singleton" - - ## - # Class used to manage timeout handlers across multiple threads. - # - # Timeout handlers should be managed by using the class methods which are - # synchronized. - # - # id = TimeoutHandler.register(10, Timeout::Error) - # begin - # sleep 20 - # puts 'foo' - # ensure - # TimeoutHandler.cancel(id) - # end - # - # will raise Timeout::Error - # - # id = TimeoutHandler.register(10, Timeout::Error) - # begin - # sleep 5 - # puts 'foo' - # ensure - # TimeoutHandler.cancel(id) - # end - # - # will print 'foo' - # - class TimeoutHandler - include Singleton - - ## - # Mutex used to synchronize access across threads - TimeoutMutex = Thread::Mutex.new # :nodoc: - - ## - # Registers a new timeout handler - # - # +time+:: Timeout in seconds - # +exception+:: Exception to raise when timeout elapsed - def TimeoutHandler.register(seconds, exception) - at = Process.clock_gettime(Process::CLOCK_MONOTONIC) + seconds - instance.register(Thread.current, at, exception) - end - - ## - # Cancels the timeout handler +id+ - def TimeoutHandler.cancel(id) - instance.cancel(Thread.current, id) - end - - def self.terminate - instance.terminate - end - - ## - # Creates a new TimeoutHandler. You should use ::register and ::cancel - # instead of creating the timeout handler directly. - def initialize - TimeoutMutex.synchronize{ - @timeout_info = Hash.new - } - @queue = Thread::Queue.new - @watcher = nil - end - - # :nodoc: - private \ - def watch - to_interrupt = [] - while true - now = Process.clock_gettime(Process::CLOCK_MONOTONIC) - wakeup = nil - to_interrupt.clear - TimeoutMutex.synchronize{ - @timeout_info.each {|thread, ary| - next unless ary - ary.each{|info| - time, exception = *info - if time < now - to_interrupt.push [thread, info.object_id, exception] - elsif !wakeup || time < wakeup - wakeup = time - end - } - } - } - to_interrupt.each {|arg| interrupt(*arg)} - if !wakeup - @queue.pop - elsif (wakeup -= now) > 0 - begin - (th = Thread.start {@queue.pop}).join(wakeup) - ensure - th&.kill&.join - end - end - @queue.clear - end - end - - # :nodoc: - private \ - def watcher - (w = @watcher)&.alive? and return w # usual case - TimeoutMutex.synchronize{ - (w = @watcher)&.alive? and next w # pathological check - @watcher = Thread.start(&method(:watch)) - } - end - - ## - # Interrupts the timeout handler +id+ and raises +exception+ - def interrupt(thread, id, exception) - if cancel(thread, id) && thread.alive? - thread.raise(exception, "execution timeout") - end - end - - ## - # Registers a new timeout handler - # - # +time+:: Timeout in seconds - # +exception+:: Exception to raise when timeout elapsed - def register(thread, time, exception) - info = nil - TimeoutMutex.synchronize{ - (@timeout_info[thread] ||= []) << (info = [time, exception]) - } - @queue.push nil - watcher - return info.object_id - end - - ## - # Cancels the timeout handler +id+ - def cancel(thread, id) - TimeoutMutex.synchronize{ - if ary = @timeout_info[thread] - ary.delete_if{|info| info.object_id == id } - if ary.empty? - @timeout_info.delete(thread) - end - return true - end - return false - } - end - - ## - def terminate - TimeoutMutex.synchronize{ - @timeout_info.clear - @watcher&.kill&.join - } - end - end - - ## - # Executes the passed block and raises +exception+ if execution takes more - # than +seconds+. - # - # If +seconds+ is zero or nil, simply executes the block - def timeout(seconds, exception=Timeout::Error) - return yield if seconds.nil? or seconds.zero? - # raise ThreadError, "timeout within critical session" if Thread.critical - id = TimeoutHandler.register(seconds, exception) - begin - yield(seconds) - ensure - TimeoutHandler.cancel(id) - end - end - module_function :timeout - end -end diff --git a/tool/lib/webrick/version.rb b/tool/lib/webrick/version.rb deleted file mode 100644 index b62988bdbb5ec6..00000000000000 --- a/tool/lib/webrick/version.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: false -#-- -# version.rb -- version and release date -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU YUUZOU -# Copyright (c) 2003 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: version.rb,v 1.74 2003/07/22 19:20:43 gotoyuzo Exp $ - -module WEBrick - - ## - # The WEBrick version - - VERSION = "1.7.0" -end diff --git a/tool/m4/ruby_shared_gc.m4 b/tool/m4/ruby_shared_gc.m4 index a27b9b8505ca77..4882bca0575257 100644 --- a/tool/m4/ruby_shared_gc.m4 +++ b/tool/m4/ruby_shared_gc.m4 @@ -1,19 +1,38 @@ dnl -*- Autoconf -*- AC_DEFUN([RUBY_SHARED_GC],[ AC_ARG_WITH(shared-gc, - AS_HELP_STRING([--with-shared-gc], - [Enable replacement of Ruby's GC from a shared library.]), - [with_shared_gc=$withval], [unset with_shared_gc] + AS_HELP_STRING([--with-shared-gc=DIR], + [Enable replacement of Ruby's GC from a shared library in the specified directory.]), + [shared_gc_dir=$withval], [unset shared_gc_dir] ) -AC_SUBST([with_shared_gc]) -AC_MSG_CHECKING([if Ruby is build with shared GC support]) -AS_IF([test "$with_shared_gc" = "yes"], [ +AC_MSG_CHECKING([if building with shared GC support]) +AS_IF([test x"$shared_gc_dir" != x], [ AC_MSG_RESULT([yes]) + + # Ensure that shared_gc_dir is always an absolute path so that Ruby + # never loads a shared GC from a relative path + AS_CASE(["$shared_gc_dir"], + [/*], [shared_gc_dir=$shared_gc_dir], + [shared_gc_dir=`pwd`/$shared_gc_dir] + ) + + # Ensure that shared_gc_dir always terminates with a / + AS_CASE(["$shared_gc_dir"], + [*/], [], + [shared_gc_dir="$shared_gc_dir/"] + ) + AC_DEFINE([USE_SHARED_GC], [1]) + AC_DEFINE_UNQUOTED([SHARED_GC_DIR], "$shared_gc_dir") + + shared_gc_summary="yes (in $shared_gc_dir)" ], [ AC_MSG_RESULT([no]) - with_shared_gc="no" AC_DEFINE([USE_SHARED_GC], [0]) + + shared_gc_summary="no" ]) + +AC_SUBST(shared_gc_dir, "${shared_gc_dir}") ])dnl diff --git a/tool/mk_builtin_loader.rb b/tool/mk_builtin_loader.rb index 4abd497f0eae19..5afef655f4c536 100644 --- a/tool/mk_builtin_loader.rb +++ b/tool/mk_builtin_loader.rb @@ -303,13 +303,7 @@ def mk_builtin_header file collect_iseq RubyVM::InstructionSequence.compile(code).to_a collect_builtin(base, Ripper.sexp(code), 'top', bs = {}, inlines = {}) - begin - f = File.open(ofile, 'w') - rescue SystemCallError # EACCES, EPERM, EROFS, etc. - # Fall back to the current directory - f = File.open(File.basename(ofile), 'w') - end - begin + StringIO.open do |f| if File::ALT_SEPARATOR file = file.tr(File::ALT_SEPARATOR, File::SEPARATOR) ofile = ofile.tr(File::ALT_SEPARATOR, File::SEPARATOR) @@ -390,8 +384,13 @@ def mk_builtin_header file f.puts " rb_load_with_builtin_functions(#{base.dump}, #{table});" f.puts "}" - ensure - f.close + + begin + File.write(ofile, f.string) + rescue SystemCallError # EACCES, EPERM, EROFS, etc. + # Fall back to the current directory + File.write(File.basename(ofile), f.string) + end end end diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index 8328a1680cc1a2..a6082b6e0399e2 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -384,6 +384,14 @@ def sync_default_gems(gem) rm_rf("prism/templates/sig") rm("prism/extconf.rb") + when "resolv" + rm_rf(%w[lib/resolv.* ext/win32/resolv test/resolv ext/win32/lib/win32/resolv.rb]) + cp_r("#{upstream}/lib/resolv.rb", "lib") + cp_r("#{upstream}/resolv.gemspec", "lib") + cp_r("#{upstream}/ext/win32/resolv", "ext/win32") + move("ext/win32/resolv/lib/win32/resolv.rb", "ext/win32/lib/win32") + cp_r("#{upstream}/test/resolv", "test") + `git checkout ext/win32/resolv/depend` else sync_lib gem, upstream end diff --git a/tool/test/testunit/test_launchable.rb b/tool/test/testunit/test_launchable.rb index 70c371e2129f03..76be876456ddb1 100644 --- a/tool/test/testunit/test_launchable.rb +++ b/tool/test/testunit/test_launchable.rb @@ -2,11 +2,12 @@ require 'test/unit' require 'tempfile' require 'json' +require_relative '../../lib/launchable' class TestLaunchable < Test::Unit::TestCase def test_json_stream_writer Tempfile.create(['launchable-test-', '.json']) do |f| - json_stream_writer = Test::Unit::LaunchableOption::JsonStreamWriter.new(f.path) + json_stream_writer = Launchable::JsonStreamWriter.new(f.path) json_stream_writer.write_array('testCases') json_stream_writer.write_object( { diff --git a/tool/test/webrick/.htaccess b/tool/test/webrick/.htaccess deleted file mode 100644 index 69d4659b9f1a33..00000000000000 --- a/tool/test/webrick/.htaccess +++ /dev/null @@ -1 +0,0 @@ -this file should not be published. diff --git a/tool/test/webrick/test_cgi.rb b/tool/test/webrick/test_cgi.rb deleted file mode 100644 index a9be8f353d989e..00000000000000 --- a/tool/test/webrick/test_cgi.rb +++ /dev/null @@ -1,148 +0,0 @@ -# coding: US-ASCII -# frozen_string_literal: false -require_relative "utils" -require "webrick" -require "test/unit" - -class TestWEBrickCGI < Test::Unit::TestCase - CRLF = "\r\n" - - def teardown - WEBrick::Utils::TimeoutHandler.terminate - super - end - - def test_cgi - TestWEBrick.start_cgi_server{|server, addr, port, log| - http = Net::HTTP.new(addr, port) - req = Net::HTTP::Get.new("/webrick.cgi") - http.request(req){|res| assert_equal("/webrick.cgi", res.body, log.call)} - req = Net::HTTP::Get.new("/webrick.cgi/path/info") - http.request(req){|res| assert_equal("/path/info", res.body, log.call)} - req = Net::HTTP::Get.new("/webrick.cgi/%3F%3F%3F?foo=bar") - http.request(req){|res| assert_equal("/???", res.body, log.call)} - unless RUBY_PLATFORM =~ /mswin|mingw|cygwin|bccwin32|java/ - # Path info of res.body is passed via ENV. - # ENV[] returns different value on Windows depending on locale. - req = Net::HTTP::Get.new("/webrick.cgi/%A4%DB%A4%B2/%A4%DB%A4%B2") - http.request(req){|res| - assert_equal("/\xA4\xDB\xA4\xB2/\xA4\xDB\xA4\xB2", res.body, log.call)} - end - req = Net::HTTP::Get.new("/webrick.cgi?a=1;a=2;b=x") - http.request(req){|res| assert_equal("a=1, a=2, b=x", res.body, log.call)} - req = Net::HTTP::Get.new("/webrick.cgi?a=1&a=2&b=x") - http.request(req){|res| assert_equal("a=1, a=2, b=x", res.body, log.call)} - - req = Net::HTTP::Post.new("/webrick.cgi?a=x;a=y;b=1") - req["Content-Type"] = "application/x-www-form-urlencoded" - http.request(req, "a=1;a=2;b=x"){|res| - assert_equal("a=1, a=2, b=x", res.body, log.call)} - req = Net::HTTP::Post.new("/webrick.cgi?a=x&a=y&b=1") - req["Content-Type"] = "application/x-www-form-urlencoded" - http.request(req, "a=1&a=2&b=x"){|res| - assert_equal("a=1, a=2, b=x", res.body, log.call)} - req = Net::HTTP::Get.new("/") - http.request(req){|res| - ary = res.body.lines.to_a - assert_match(%r{/$}, ary[0], log.call) - assert_match(%r{/webrick.cgi$}, ary[1], log.call) - } - - req = Net::HTTP::Get.new("/webrick.cgi") - req["Cookie"] = "CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001" - http.request(req){|res| - assert_equal( - "CUSTOMER=WILE_E_COYOTE\nPART_NUMBER=ROCKET_LAUNCHER_0001\n", - res.body, log.call) - } - - req = Net::HTTP::Get.new("/webrick.cgi") - cookie = %{$Version="1"; } - cookie << %{Customer="WILE_E_COYOTE"; $Path="/acme"; } - cookie << %{Part_Number="Rocket_Launcher_0001"; $Path="/acme"; } - cookie << %{Shipping="FedEx"; $Path="/acme"} - req["Cookie"] = cookie - http.request(req){|res| - assert_equal("Customer=WILE_E_COYOTE, Shipping=FedEx", - res["Set-Cookie"], log.call) - assert_equal("Customer=WILE_E_COYOTE\n" + - "Part_Number=Rocket_Launcher_0001\n" + - "Shipping=FedEx\n", res.body, log.call) - } - } - end - - def test_bad_request - log_tester = lambda {|log, access_log| - assert_match(/BadRequest/, log.join) - } - TestWEBrick.start_cgi_server({}, log_tester) {|server, addr, port, log| - sock = TCPSocket.new(addr, port) - begin - sock << "POST /webrick.cgi HTTP/1.0" << CRLF - sock << "Content-Type: application/x-www-form-urlencoded" << CRLF - sock << "Content-Length: 1024" << CRLF - sock << CRLF - sock << "a=1&a=2&b=x" - sock.close_write - assert_match(%r{\AHTTP/\d.\d 400 Bad Request}, sock.read, log.call) - ensure - sock.close - end - } - end - - def test_cgi_env - TestWEBrick.start_cgi_server do |server, addr, port, log| - http = Net::HTTP.new(addr, port) - req = Net::HTTP::Get.new("/webrick.cgi/dumpenv") - req['proxy'] = 'http://example.com/' - req['hello'] = 'world' - http.request(req) do |res| - env = Marshal.load(res.body) - assert_equal 'world', env['HTTP_HELLO'] - assert_not_operator env, :include?, 'HTTP_PROXY' - end - end - end - - CtrlSeq = [0x7f, *(1..31)].pack("C*").gsub(/\s+/, '') - CtrlPat = /#{Regexp.quote(CtrlSeq)}/o - DumpPat = /#{Regexp.quote(CtrlSeq.dump[1...-1])}/o - - def test_bad_uri - log_tester = lambda {|log, access_log| - assert_equal(1, log.length) - assert_match(/ERROR bad URI/, log[0]) - } - TestWEBrick.start_cgi_server({}, log_tester) {|server, addr, port, log| - res = TCPSocket.open(addr, port) {|sock| - sock << "GET /#{CtrlSeq}#{CRLF}#{CRLF}" - sock.close_write - sock.read - } - assert_match(%r{\AHTTP/\d.\d 400 Bad Request}, res) - s = log.call.each_line.grep(/ERROR bad URI/)[0] - assert_match(DumpPat, s) - assert_not_match(CtrlPat, s) - } - end - - def test_bad_header - log_tester = lambda {|log, access_log| - assert_equal(1, log.length) - assert_match(/ERROR bad header/, log[0]) - } - TestWEBrick.start_cgi_server({}, log_tester) {|server, addr, port, log| - res = TCPSocket.open(addr, port) {|sock| - sock << "GET / HTTP/1.0#{CRLF}#{CtrlSeq}#{CRLF}#{CRLF}" - sock.close_write - sock.read - } - assert_match(%r{\AHTTP/\d.\d 400 Bad Request}, res) - s = log.call.each_line.grep(/ERROR bad header/)[0] - assert_match(DumpPat, s) - assert_not_match(CtrlPat, s) - } - end -end diff --git a/tool/test/webrick/test_config.rb b/tool/test/webrick/test_config.rb deleted file mode 100644 index a54a667452e92b..00000000000000 --- a/tool/test/webrick/test_config.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: false -require "test/unit" -require "webrick/config" - -class TestWEBrickConfig < Test::Unit::TestCase - def test_server_name_default - config = WEBrick::Config::General.dup - assert_equal(false, config.key?(:ServerName)) - assert_equal(WEBrick::Utils.getservername, config[:ServerName]) - assert_equal(true, config.key?(:ServerName)) - end - - def test_server_name_set_nil - config = WEBrick::Config::General.dup.update(ServerName: nil) - assert_equal(nil, config[:ServerName]) - end -end diff --git a/tool/test/webrick/test_cookie.rb b/tool/test/webrick/test_cookie.rb deleted file mode 100644 index e46185f12752a6..00000000000000 --- a/tool/test/webrick/test_cookie.rb +++ /dev/null @@ -1,141 +0,0 @@ -# frozen_string_literal: false -require "test/unit" -require "webrick/cookie" - -class TestWEBrickCookie < Test::Unit::TestCase - def test_new - cookie = WEBrick::Cookie.new("foo","bar") - assert_equal("foo", cookie.name) - assert_equal("bar", cookie.value) - assert_equal("foo=bar", cookie.to_s) - end - - def test_time - cookie = WEBrick::Cookie.new("foo","bar") - t = 1000000000 - cookie.max_age = t - assert_match(t.to_s, cookie.to_s) - - cookie = WEBrick::Cookie.new("foo","bar") - t = Time.at(1000000000) - cookie.expires = t - assert_equal(Time, cookie.expires.class) - assert_equal(t, cookie.expires) - ts = t.httpdate - cookie.expires = ts - assert_equal(Time, cookie.expires.class) - assert_equal(t, cookie.expires) - assert_match(ts, cookie.to_s) - end - - def test_parse - data = "" - data << '$Version="1"; ' - data << 'Customer="WILE_E_COYOTE"; $Path="/acme"; ' - data << 'Part_Number="Rocket_Launcher_0001"; $Path="/acme"; ' - data << 'Shipping="FedEx"; $Path="/acme"' - cookies = WEBrick::Cookie.parse(data) - assert_equal(3, cookies.size) - assert_equal(1, cookies[0].version) - assert_equal("Customer", cookies[0].name) - assert_equal("WILE_E_COYOTE", cookies[0].value) - assert_equal("/acme", cookies[0].path) - assert_equal(1, cookies[1].version) - assert_equal("Part_Number", cookies[1].name) - assert_equal("Rocket_Launcher_0001", cookies[1].value) - assert_equal(1, cookies[2].version) - assert_equal("Shipping", cookies[2].name) - assert_equal("FedEx", cookies[2].value) - - data = "hoge=moge; __div__session=9865ecfd514be7f7" - cookies = WEBrick::Cookie.parse(data) - assert_equal(2, cookies.size) - assert_equal(0, cookies[0].version) - assert_equal("hoge", cookies[0].name) - assert_equal("moge", cookies[0].value) - assert_equal("__div__session", cookies[1].name) - assert_equal("9865ecfd514be7f7", cookies[1].value) - - # don't allow ,-separator - data = "hoge=moge, __div__session=9865ecfd514be7f7" - cookies = WEBrick::Cookie.parse(data) - assert_equal(1, cookies.size) - assert_equal(0, cookies[0].version) - assert_equal("hoge", cookies[0].name) - assert_equal("moge, __div__session=9865ecfd514be7f7", cookies[0].value) - end - - def test_parse_no_whitespace - data = [ - '$Version="1"; ', - 'Customer="WILE_E_COYOTE";$Path="/acme";', # no SP between cookie-string - 'Part_Number="Rocket_Launcher_0001";$Path="/acme";', # no SP between cookie-string - 'Shipping="FedEx";$Path="/acme"' - ].join - cookies = WEBrick::Cookie.parse(data) - assert_equal(1, cookies.size) - end - - def test_parse_too_much_whitespaces - # According to RFC6265, - # cookie-string = cookie-pair *( ";" SP cookie-pair ) - # So single 0x20 is needed after ';'. We allow multiple spaces here for - # compatibility with older WEBrick versions. - data = [ - '$Version="1"; ', - 'Customer="WILE_E_COYOTE";$Path="/acme"; ', # no SP between cookie-string - 'Part_Number="Rocket_Launcher_0001";$Path="/acme"; ', # no SP between cookie-string - 'Shipping="FedEx";$Path="/acme"' - ].join - cookies = WEBrick::Cookie.parse(data) - assert_equal(3, cookies.size) - end - - def test_parse_set_cookie - data = %(Customer="WILE_E_COYOTE"; Version="1"; Path="/acme") - cookie = WEBrick::Cookie.parse_set_cookie(data) - assert_equal("Customer", cookie.name) - assert_equal("WILE_E_COYOTE", cookie.value) - assert_equal(1, cookie.version) - assert_equal("/acme", cookie.path) - - data = %(Shipping="FedEx"; Version="1"; Path="/acme"; Secure) - cookie = WEBrick::Cookie.parse_set_cookie(data) - assert_equal("Shipping", cookie.name) - assert_equal("FedEx", cookie.value) - assert_equal(1, cookie.version) - assert_equal("/acme", cookie.path) - assert_equal(true, cookie.secure) - end - - def test_parse_set_cookies - data = %(Shipping="FedEx"; Version="1"; Path="/acme"; Secure) - data << %(, CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT; path=/; Secure) - data << %(, name="Aaron"; Version="1"; path="/acme") - cookies = WEBrick::Cookie.parse_set_cookies(data) - assert_equal(3, cookies.length) - - fed_ex = cookies.find { |c| c.name == 'Shipping' } - assert_not_nil(fed_ex) - assert_equal("Shipping", fed_ex.name) - assert_equal("FedEx", fed_ex.value) - assert_equal(1, fed_ex.version) - assert_equal("/acme", fed_ex.path) - assert_equal(true, fed_ex.secure) - - name = cookies.find { |c| c.name == 'name' } - assert_not_nil(name) - assert_equal("name", name.name) - assert_equal("Aaron", name.value) - assert_equal(1, name.version) - assert_equal("/acme", name.path) - - customer = cookies.find { |c| c.name == 'CUSTOMER' } - assert_not_nil(customer) - assert_equal("CUSTOMER", customer.name) - assert_equal("WILE_E_COYOTE", customer.value) - assert_equal(0, customer.version) - assert_equal("/", customer.path) - assert_equal(Time.utc(1999, 11, 9, 23, 12, 40), customer.expires) - end -end diff --git a/tool/test/webrick/test_do_not_reverse_lookup.rb b/tool/test/webrick/test_do_not_reverse_lookup.rb deleted file mode 100644 index efcb5a92990194..00000000000000 --- a/tool/test/webrick/test_do_not_reverse_lookup.rb +++ /dev/null @@ -1,71 +0,0 @@ -# frozen_string_literal: false -require "test/unit" -require "webrick" -require_relative "utils" - -class TestDoNotReverseLookup < Test::Unit::TestCase - class DNRL < WEBrick::GenericServer - def run(sock) - sock << sock.do_not_reverse_lookup.to_s - end - end - - @@original_do_not_reverse_lookup_value = Socket.do_not_reverse_lookup - - def teardown - Socket.do_not_reverse_lookup = @@original_do_not_reverse_lookup_value - end - - def do_not_reverse_lookup?(config) - result = nil - TestWEBrick.start_server(DNRL, config) do |server, addr, port, log| - TCPSocket.open(addr, port) do |sock| - result = {'true' => true, 'false' => false}[sock.gets] - end - end - result - end - - # +--------------------------------------------------------------------------+ - # | Expected interaction between Socket.do_not_reverse_lookup | - # | and WEBrick::Config::General[:DoNotReverseLookup] | - # +----------------------------+---------------------------------------------+ - # | |WEBrick::Config::General[:DoNotReverseLookup]| - # +----------------------------+--------------+---------------+--------------+ - # |Socket.do_not_reverse_lookup| TRUE | FALSE | NIL | - # +----------------------------+--------------+---------------+--------------+ - # | TRUE | true | false | true | - # +----------------------------+--------------+---------------+--------------+ - # | FALSE | true | false | false | - # +----------------------------+--------------+---------------+--------------+ - - def test_socket_dnrl_true_server_dnrl_true - Socket.do_not_reverse_lookup = true - assert_equal(true, do_not_reverse_lookup?(:DoNotReverseLookup => true)) - end - - def test_socket_dnrl_true_server_dnrl_false - Socket.do_not_reverse_lookup = true - assert_equal(false, do_not_reverse_lookup?(:DoNotReverseLookup => false)) - end - - def test_socket_dnrl_true_server_dnrl_nil - Socket.do_not_reverse_lookup = true - assert_equal(true, do_not_reverse_lookup?(:DoNotReverseLookup => nil)) - end - - def test_socket_dnrl_false_server_dnrl_true - Socket.do_not_reverse_lookup = false - assert_equal(true, do_not_reverse_lookup?(:DoNotReverseLookup => true)) - end - - def test_socket_dnrl_false_server_dnrl_false - Socket.do_not_reverse_lookup = false - assert_equal(false, do_not_reverse_lookup?(:DoNotReverseLookup => false)) - end - - def test_socket_dnrl_false_server_dnrl_nil - Socket.do_not_reverse_lookup = false - assert_equal(false, do_not_reverse_lookup?(:DoNotReverseLookup => nil)) - end -end diff --git a/tool/test/webrick/test_filehandler.rb b/tool/test/webrick/test_filehandler.rb deleted file mode 100644 index 452667d4f4db10..00000000000000 --- a/tool/test/webrick/test_filehandler.rb +++ /dev/null @@ -1,397 +0,0 @@ -# frozen_string_literal: false -require "test/unit" -require_relative "utils.rb" -require "webrick" -require "stringio" -require "tmpdir" - -class WEBrick::TestFileHandler < Test::Unit::TestCase - def teardown - WEBrick::Utils::TimeoutHandler.terminate - super - end - - def default_file_handler(filename) - klass = WEBrick::HTTPServlet::DefaultFileHandler - klass.new(WEBrick::Config::HTTP, filename) - end - - def windows? - File.directory?("\\") - end - - def get_res_body(res) - sio = StringIO.new - sio.binmode - res.send_body(sio) - sio.string - end - - def make_range_request(range_spec) - msg = <<-END_OF_REQUEST - GET / HTTP/1.0 - Range: #{range_spec} - - END_OF_REQUEST - return StringIO.new(msg.gsub(/^ {6}/, "")) - end - - def make_range_response(file, range_spec) - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(make_range_request(range_spec)) - res = WEBrick::HTTPResponse.new(WEBrick::Config::HTTP) - size = File.size(file) - handler = default_file_handler(file) - handler.make_partial_content(req, res, file, size) - return res - end - - def test_make_partial_content - filename = __FILE__ - filesize = File.size(filename) - - res = make_range_response(filename, "bytes=#{filesize-100}-") - assert_match(%r{^text/plain}, res["content-type"]) - assert_equal(100, get_res_body(res).size) - - res = make_range_response(filename, "bytes=-100") - assert_match(%r{^text/plain}, res["content-type"]) - assert_equal(100, get_res_body(res).size) - - res = make_range_response(filename, "bytes=0-99") - assert_match(%r{^text/plain}, res["content-type"]) - assert_equal(100, get_res_body(res).size) - - res = make_range_response(filename, "bytes=100-199") - assert_match(%r{^text/plain}, res["content-type"]) - assert_equal(100, get_res_body(res).size) - - res = make_range_response(filename, "bytes=0-0") - assert_match(%r{^text/plain}, res["content-type"]) - assert_equal(1, get_res_body(res).size) - - res = make_range_response(filename, "bytes=-1") - assert_match(%r{^text/plain}, res["content-type"]) - assert_equal(1, get_res_body(res).size) - - res = make_range_response(filename, "bytes=0-0, -2") - assert_match(%r{^multipart/byteranges}, res["content-type"]) - body = get_res_body(res) - boundary = /; boundary=(.+)/.match(res['content-type'])[1] - off = filesize - 2 - last = filesize - 1 - - exp = "--#{boundary}\r\n" \ - "Content-Type: text/plain\r\n" \ - "Content-Range: bytes 0-0/#{filesize}\r\n" \ - "\r\n" \ - "#{File.read(__FILE__, 1)}\r\n" \ - "--#{boundary}\r\n" \ - "Content-Type: text/plain\r\n" \ - "Content-Range: bytes #{off}-#{last}/#{filesize}\r\n" \ - "\r\n" \ - "#{File.read(__FILE__, 2, off)}\r\n" \ - "--#{boundary}--\r\n" - assert_equal exp, body - end - - def test_filehandler - config = { :DocumentRoot => File.dirname(__FILE__), } - this_file = File.basename(__FILE__) - filesize = File.size(__FILE__) - this_data = File.binread(__FILE__) - range = nil - bug2593 = '[ruby-dev:40030]' - - TestWEBrick.start_httpserver(config) do |server, addr, port, log| - begin - server[:DocumentRootOptions][:NondisclosureName] = [] - http = Net::HTTP.new(addr, port) - req = Net::HTTP::Get.new("/") - http.request(req){|res| - assert_equal("200", res.code, log.call) - assert_equal("text/html", res.content_type, log.call) - assert_match(/HREF="#{this_file}"/, res.body, log.call) - } - req = Net::HTTP::Get.new("/#{this_file}") - http.request(req){|res| - assert_equal("200", res.code, log.call) - assert_equal("text/plain", res.content_type, log.call) - assert_equal(this_data, res.body, log.call) - } - - req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=#{filesize-100}-") - http.request(req){|res| - assert_equal("206", res.code, log.call) - assert_equal("text/plain", res.content_type, log.call) - assert_nothing_raised(bug2593) {range = res.content_range} - assert_equal((filesize-100)..(filesize-1), range, log.call) - assert_equal(this_data[-100..-1], res.body, log.call) - } - - req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=-100") - http.request(req){|res| - assert_equal("206", res.code, log.call) - assert_equal("text/plain", res.content_type, log.call) - assert_nothing_raised(bug2593) {range = res.content_range} - assert_equal((filesize-100)..(filesize-1), range, log.call) - assert_equal(this_data[-100..-1], res.body, log.call) - } - - req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=0-99") - http.request(req){|res| - assert_equal("206", res.code, log.call) - assert_equal("text/plain", res.content_type, log.call) - assert_nothing_raised(bug2593) {range = res.content_range} - assert_equal(0..99, range, log.call) - assert_equal(this_data[0..99], res.body, log.call) - } - - req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=100-199") - http.request(req){|res| - assert_equal("206", res.code, log.call) - assert_equal("text/plain", res.content_type, log.call) - assert_nothing_raised(bug2593) {range = res.content_range} - assert_equal(100..199, range, log.call) - assert_equal(this_data[100..199], res.body, log.call) - } - - req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=0-0") - http.request(req){|res| - assert_equal("206", res.code, log.call) - assert_equal("text/plain", res.content_type, log.call) - assert_nothing_raised(bug2593) {range = res.content_range} - assert_equal(0..0, range, log.call) - assert_equal(this_data[0..0], res.body, log.call) - } - - req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=-1") - http.request(req){|res| - assert_equal("206", res.code, log.call) - assert_equal("text/plain", res.content_type, log.call) - assert_nothing_raised(bug2593) {range = res.content_range} - assert_equal((filesize-1)..(filesize-1), range, log.call) - assert_equal(this_data[-1, 1], res.body, log.call) - } - - req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=0-0, -2") - http.request(req){|res| - assert_equal("206", res.code, log.call) - assert_equal("multipart/byteranges", res.content_type, log.call) - } - ensure - server[:DocumentRootOptions].delete :NondisclosureName - end - end - end - - def test_non_disclosure_name - config = { :DocumentRoot => File.dirname(__FILE__), } - log_tester = lambda {|log, access_log| - log = log.reject {|s| /ERROR `.*\' not found\./ =~ s } - log = log.reject {|s| /WARN the request refers nondisclosure name/ =~ s } - assert_equal([], log) - } - this_file = File.basename(__FILE__) - TestWEBrick.start_httpserver(config, log_tester) do |server, addr, port, log| - http = Net::HTTP.new(addr, port) - doc_root_opts = server[:DocumentRootOptions] - doc_root_opts[:NondisclosureName] = %w(.ht* *~ test_*) - req = Net::HTTP::Get.new("/") - http.request(req){|res| - assert_equal("200", res.code, log.call) - assert_equal("text/html", res.content_type, log.call) - assert_no_match(/HREF="#{File.basename(__FILE__)}"/, res.body) - } - req = Net::HTTP::Get.new("/#{this_file}") - http.request(req){|res| - assert_equal("404", res.code, log.call) - } - doc_root_opts[:NondisclosureName] = %w(.ht* *~ TEST_*) - http.request(req){|res| - assert_equal("404", res.code, log.call) - } - end - end - - def test_directory_traversal - return if File.executable?(__FILE__) # skip on strange file system - - config = { :DocumentRoot => File.dirname(__FILE__), } - log_tester = lambda {|log, access_log| - log = log.reject {|s| /ERROR bad URI/ =~ s } - log = log.reject {|s| /ERROR `.*\' not found\./ =~ s } - assert_equal([], log) - } - TestWEBrick.start_httpserver(config, log_tester) do |server, addr, port, log| - http = Net::HTTP.new(addr, port) - req = Net::HTTP::Get.new("/../../") - http.request(req){|res| assert_equal("400", res.code, log.call) } - req = Net::HTTP::Get.new("/..%5c../#{File.basename(__FILE__)}") - http.request(req){|res| assert_equal(windows? ? "200" : "404", res.code, log.call) } - req = Net::HTTP::Get.new("/..%5c..%5cruby.c") - http.request(req){|res| assert_equal("404", res.code, log.call) } - end - end - - def test_unwise_in_path - if windows? - config = { :DocumentRoot => File.dirname(__FILE__), } - TestWEBrick.start_httpserver(config) do |server, addr, port, log| - http = Net::HTTP.new(addr, port) - req = Net::HTTP::Get.new("/..%5c..") - http.request(req){|res| assert_equal("301", res.code, log.call) } - end - end - end - - def test_short_filename - return if File.executable?(__FILE__) # skip on strange file system - - log_tester = lambda {|log, access_log| - log = log.reject {|s| /ERROR `.*\' not found\./ =~ s } - log = log.reject {|s| /WARN the request refers nondisclosure name/ =~ s } - assert_equal([], log) - } - TestWEBrick.start_cgi_server({}, log_tester) do |server, addr, port, log| - http = Net::HTTP.new(addr, port) - if windows? - root = File.dirname(__FILE__).tr("/", "\\") - fname = IO.popen(%W[dir /x #{root}\\webrick_long_filename.cgi], encoding: "binary", &:read) - fname.sub!(/\A.*$^$.*$^$/m, '') - if fname - fname = fname[/\s(w.+?cgi)\s/i, 1] - fname.downcase! - end - else - fname = "webric~1.cgi" - end - req = Net::HTTP::Get.new("/#{fname}/test") - http.request(req) do |res| - if windows? - assert_equal("200", res.code, log.call) - assert_equal("/test", res.body, log.call) - else - assert_equal("404", res.code, log.call) - end - end - - req = Net::HTTP::Get.new("/.htaccess") - http.request(req) {|res| assert_equal("404", res.code, log.call) } - req = Net::HTTP::Get.new("/htacce~1") - http.request(req) {|res| assert_equal("404", res.code, log.call) } - req = Net::HTTP::Get.new("/HTACCE~1") - http.request(req) {|res| assert_equal("404", res.code, log.call) } - end - end - - def test_multibyte_char_in_path - if Encoding.default_external == Encoding.find('US-ASCII') - reset_encoding = true - verb = $VERBOSE - $VERBOSE = false - Encoding.default_external = Encoding.find('UTF-8') - end - - c = "\u00a7" - begin - c = c.encode('filesystem') - rescue EncodingError - c = c.b - end - Dir.mktmpdir(c) do |dir| - basename = "#{c}.txt" - File.write("#{dir}/#{basename}", "test_multibyte_char_in_path") - Dir.mkdir("#{dir}/#{c}") - File.write("#{dir}/#{c}/#{basename}", "nested") - config = { - :DocumentRoot => dir, - :DirectoryIndex => [basename], - } - TestWEBrick.start_httpserver(config) do |server, addr, port, log| - http = Net::HTTP.new(addr, port) - path = "/#{basename}" - req = Net::HTTP::Get.new(WEBrick::HTTPUtils::escape(path)) - http.request(req){|res| assert_equal("200", res.code, log.call + "\nFilesystem encoding is #{Encoding.find('filesystem')}") } - path = "/#{c}/#{basename}" - req = Net::HTTP::Get.new(WEBrick::HTTPUtils::escape(path)) - http.request(req){|res| assert_equal("200", res.code, log.call) } - req = Net::HTTP::Get.new('/') - http.request(req){|res| - assert_equal("test_multibyte_char_in_path", res.body, log.call) - } - end - end - ensure - if reset_encoding - Encoding.default_external = Encoding.find('US-ASCII') - $VERBOSE = verb - end - end - - def test_script_disclosure - return if File.executable?(__FILE__) # skip on strange file system - - config = { - :CGIInterpreter => TestWEBrick::RubyBinArray, - :DocumentRoot => File.dirname(__FILE__), - :CGIPathEnv => ENV['PATH'], - :RequestCallback => Proc.new{|req, res| - def req.meta_vars - meta = super - meta["RUBYLIB"] = $:.join(File::PATH_SEPARATOR) - meta[RbConfig::CONFIG['LIBPATHENV']] = ENV[RbConfig::CONFIG['LIBPATHENV']] if RbConfig::CONFIG['LIBPATHENV'] - return meta - end - }, - } - log_tester = lambda {|log, access_log| - log = log.reject {|s| /ERROR `.*\' not found\./ =~ s } - assert_equal([], log) - } - TestWEBrick.start_httpserver(config, log_tester) do |server, addr, port, log| - http = Net::HTTP.new(addr, port) - http.read_timeout = EnvUtil.apply_timeout_scale(60) - http.write_timeout = EnvUtil.apply_timeout_scale(60) if http.respond_to?(:write_timeout=) - - req = Net::HTTP::Get.new("/webrick.cgi/test") - http.request(req) do |res| - assert_equal("200", res.code, log.call) - assert_equal("/test", res.body, log.call) - end - - resok = windows? - response_assertion = Proc.new do |res| - if resok - assert_equal("200", res.code, log.call) - assert_equal("/test", res.body, log.call) - else - assert_equal("404", res.code, log.call) - end - end - req = Net::HTTP::Get.new("/webrick.cgi%20/test") - http.request(req, &response_assertion) - req = Net::HTTP::Get.new("/webrick.cgi./test") - http.request(req, &response_assertion) - resok &&= File.exist?(__FILE__+"::$DATA") - req = Net::HTTP::Get.new("/webrick.cgi::$DATA/test") - http.request(req, &response_assertion) - end - end - - def test_erbhandler - config = { :DocumentRoot => File.dirname(__FILE__) } - log_tester = lambda {|log, access_log| - log = log.reject {|s| /ERROR `.*\' not found\./ =~ s } - assert_equal([], log) - } - TestWEBrick.start_httpserver(config, log_tester) do |server, addr, port, log| - http = Net::HTTP.new(addr, port) - req = Net::HTTP::Get.new("/webrick.rhtml") - http.request(req) do |res| - assert_equal("200", res.code, log.call) - assert_match %r!\Areq to http://[^/]+/webrick\.rhtml {}\n!, res.body - end - end - end -end diff --git a/tool/test/webrick/test_htgroup.rb b/tool/test/webrick/test_htgroup.rb deleted file mode 100644 index 8749711df5f62b..00000000000000 --- a/tool/test/webrick/test_htgroup.rb +++ /dev/null @@ -1,19 +0,0 @@ -require "tempfile" -require "test/unit" -require "webrick/httpauth/htgroup" - -class TestHtgroup < Test::Unit::TestCase - def test_htgroup - Tempfile.create('test_htgroup') do |tmpfile| - tmpfile.close - tmp_group = WEBrick::HTTPAuth::Htgroup.new(tmpfile.path) - tmp_group.add 'superheroes', %w[spiderman batman] - tmp_group.add 'supervillains', %w[joker] - tmp_group.flush - - htgroup = WEBrick::HTTPAuth::Htgroup.new(tmpfile.path) - assert_equal(htgroup.members('superheroes'), %w[spiderman batman]) - assert_equal(htgroup.members('supervillains'), %w[joker]) - end - end -end diff --git a/tool/test/webrick/test_htmlutils.rb b/tool/test/webrick/test_htmlutils.rb deleted file mode 100644 index ae1b8efa954a58..00000000000000 --- a/tool/test/webrick/test_htmlutils.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: false -require "test/unit" -require "webrick/htmlutils" - -class TestWEBrickHTMLUtils < Test::Unit::TestCase - include WEBrick::HTMLUtils - - def test_escape - assert_equal("foo", escape("foo")) - assert_equal("foo bar", escape("foo bar")) - assert_equal("foo&bar", escape("foo&bar")) - assert_equal("foo"bar", escape("foo\"bar")) - assert_equal("foo>bar", escape("foo>bar")) - assert_equal("foo<bar", escape("foo realm, :UserDB => htpasswd, - :Logger => server.logger - ) - auth.authenticate(req, res) - res.body = "hoge" - } - http = Net::HTTP.new(addr, port) - g = Net::HTTP::Get.new(path) - g.basic_auth("webrick", "supersecretpassword") - http.request(g){|res| assert_equal("hoge", res.body, log.call)} - g.basic_auth("webrick", "not super") - http.request(g){|res| assert_not_equal("hoge", res.body, log.call)} - } - } - end - - define_method(:"test_basic_auth_bad_username_htpasswd_#{hash_algo}") do - log_tester = lambda {|log, access_log| - assert_equal(2, log.length) - assert_match(/ERROR Basic WEBrick's realm: foo\\ebar: the user is not allowed\./, log[0]) - assert_match(/ERROR WEBrick::HTTPStatus::Unauthorized/, log[1]) - } - TestWEBrick.start_httpserver({}, log_tester) {|server, addr, port, log| - realm = "WEBrick's realm" - path = "/basic_auth" - - Tempfile.create("test_webrick_auth") {|tmpfile| - tmpfile.close - tmp_pass = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path, password_hash: hash_algo) - tmp_pass.set_passwd(realm, "webrick", "supersecretpassword") - tmp_pass.set_passwd(realm, "foo", "supersecretpassword") - tmp_pass.flush - - htpasswd = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path, password_hash: hash_algo) - users = [] - htpasswd.each{|user, pass| users << user } - server.mount_proc(path){|req, res| - auth = WEBrick::HTTPAuth::BasicAuth.new( - :Realm => realm, :UserDB => htpasswd, - :Logger => server.logger - ) - auth.authenticate(req, res) - res.body = "hoge" - } - http = Net::HTTP.new(addr, port) - g = Net::HTTP::Get.new(path) - g.basic_auth("foo\ebar", "passwd") - http.request(g){|res| assert_not_equal("hoge", res.body, log.call) } - } - } - end - end - - DIGESTRES_ = / - ([a-zA-Z\-]+) - [ \t]*(?:\r\n[ \t]*)* - = - [ \t]*(?:\r\n[ \t]*)* - (?: - "((?:[^"]+|\\[\x00-\x7F])*)" | - ([!\#$%&'*+\-.0-9A-Z^_`a-z|~]+) - )/x - - def test_digest_auth - log_tester = lambda {|log, access_log| - log.reject! {|line| /\A\s*\z/ =~ line } - pats = [ - /ERROR Digest WEBrick's realm: no credentials in the request\./, - /ERROR WEBrick::HTTPStatus::Unauthorized/, - /ERROR Digest WEBrick's realm: webrick: digest unmatch\./ - ] - pats.each {|pat| - assert(!log.grep(pat).empty?, "webrick log doesn't have expected error: #{pat.inspect}") - log.reject! {|line| pat =~ line } - } - assert_equal([], log) - } - TestWEBrick.start_httpserver({}, log_tester) {|server, addr, port, log| - realm = "WEBrick's realm" - path = "/digest_auth" - - Tempfile.create("test_webrick_auth") {|tmpfile| - tmpfile.close - tmp_pass = WEBrick::HTTPAuth::Htdigest.new(tmpfile.path) - tmp_pass.set_passwd(realm, "webrick", "supersecretpassword") - tmp_pass.set_passwd(realm, "foo", "supersecretpassword") - tmp_pass.flush - - htdigest = WEBrick::HTTPAuth::Htdigest.new(tmpfile.path) - users = [] - htdigest.each{|user, pass| users << user } - assert_equal(2, users.size, log.call) - assert(users.member?("webrick"), log.call) - assert(users.member?("foo"), log.call) - - auth = WEBrick::HTTPAuth::DigestAuth.new( - :Realm => realm, :UserDB => htdigest, - :Algorithm => 'MD5', - :Logger => server.logger - ) - server.mount_proc(path){|req, res| - auth.authenticate(req, res) - res.body = "hoge" - } - - Net::HTTP.start(addr, port) do |http| - g = Net::HTTP::Get.new(path) - params = {} - http.request(g) do |res| - assert_equal('401', res.code, log.call) - res["www-authenticate"].scan(DIGESTRES_) do |key, quoted, token| - params[key.downcase] = token || quoted.delete('\\') - end - params['uri'] = "http://#{addr}:#{port}#{path}" - end - - g['Authorization'] = credentials_for_request('webrick', "supersecretpassword", params) - http.request(g){|res| assert_equal("hoge", res.body, log.call)} - - params['algorithm'].downcase! #4936 - g['Authorization'] = credentials_for_request('webrick', "supersecretpassword", params) - http.request(g){|res| assert_equal("hoge", res.body, log.call)} - - g['Authorization'] = credentials_for_request('webrick', "not super", params) - http.request(g){|res| assert_not_equal("hoge", res.body, log.call)} - end - } - } - end - - def test_digest_auth_int - log_tester = lambda {|log, access_log| - log.reject! {|line| /\A\s*\z/ =~ line } - pats = [ - /ERROR Digest wb auth-int realm: no credentials in the request\./, - /ERROR WEBrick::HTTPStatus::Unauthorized/, - /ERROR Digest wb auth-int realm: foo: digest unmatch\./ - ] - pats.each {|pat| - assert(!log.grep(pat).empty?, "webrick log doesn't have expected error: #{pat.inspect}") - log.reject! {|line| pat =~ line } - } - assert_equal([], log) - } - TestWEBrick.start_httpserver({}, log_tester) {|server, addr, port, log| - realm = "wb auth-int realm" - path = "/digest_auth_int" - - Tempfile.create("test_webrick_auth_int") {|tmpfile| - tmpfile.close - tmp_pass = WEBrick::HTTPAuth::Htdigest.new(tmpfile.path) - tmp_pass.set_passwd(realm, "foo", "Hunter2") - tmp_pass.flush - - htdigest = WEBrick::HTTPAuth::Htdigest.new(tmpfile.path) - users = [] - htdigest.each{|user, pass| users << user } - assert_equal %w(foo), users - - auth = WEBrick::HTTPAuth::DigestAuth.new( - :Realm => realm, :UserDB => htdigest, - :Algorithm => 'MD5', - :Logger => server.logger, - :Qop => %w(auth-int), - ) - server.mount_proc(path){|req, res| - auth.authenticate(req, res) - res.body = "bbb" - } - Net::HTTP.start(addr, port) do |http| - post = Net::HTTP::Post.new(path) - params = {} - data = 'hello=world' - body = StringIO.new(data) - post.content_length = data.bytesize - post['Content-Type'] = 'application/x-www-form-urlencoded' - post.body_stream = body - - http.request(post) do |res| - assert_equal('401', res.code, log.call) - res["www-authenticate"].scan(DIGESTRES_) do |key, quoted, token| - params[key.downcase] = token || quoted.delete('\\') - end - params['uri'] = "http://#{addr}:#{port}#{path}" - end - - body.rewind - cred = credentials_for_request('foo', 'Hunter3', params, body) - post['Authorization'] = cred - post.body_stream = body - http.request(post){|res| - assert_equal('401', res.code, log.call) - assert_not_equal("bbb", res.body, log.call) - } - - body.rewind - cred = credentials_for_request('foo', 'Hunter2', params, body) - post['Authorization'] = cred - post.body_stream = body - http.request(post){|res| assert_equal("bbb", res.body, log.call)} - end - } - } - end - - def test_digest_auth_invalid - digest_auth = WEBrick::HTTPAuth::DigestAuth.new(Realm: 'realm', UserDB: '') - - def digest_auth.error(fmt, *) - end - - def digest_auth.try_bad_request(len) - request = {"Authorization" => %[Digest a="#{'\b'*len}]} - authenticate request, nil - end - - bad_request = WEBrick::HTTPStatus::BadRequest - t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC) - assert_raise(bad_request) {digest_auth.try_bad_request(10)} - limit = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0) - [20, 50, 100, 200].each do |len| - assert_raise(bad_request) do - Timeout.timeout(len*limit) {digest_auth.try_bad_request(len)} - end - end - end - - private - def credentials_for_request(user, password, params, body = nil) - cnonce = "hoge" - nonce_count = 1 - ha1 = "#{user}:#{params['realm']}:#{password}" - if body - dig = Digest::MD5.new - while buf = body.read(16384) - dig.update(buf) - end - body.rewind - ha2 = "POST:#{params['uri']}:#{dig.hexdigest}" - else - ha2 = "GET:#{params['uri']}" - end - - request_digest = - "#{Digest::MD5.hexdigest(ha1)}:" \ - "#{params['nonce']}:#{'%08x' % nonce_count}:#{cnonce}:#{params['qop']}:" \ - "#{Digest::MD5.hexdigest(ha2)}" - "Digest username=\"#{user}\"" \ - ", realm=\"#{params['realm']}\"" \ - ", nonce=\"#{params['nonce']}\"" \ - ", uri=\"#{params['uri']}\"" \ - ", qop=#{params['qop']}" \ - ", nc=#{'%08x' % nonce_count}" \ - ", cnonce=\"#{cnonce}\"" \ - ", response=\"#{Digest::MD5.hexdigest(request_digest)}\"" \ - ", opaque=\"#{params['opaque']}\"" \ - ", algorithm=#{params['algorithm']}" - end -end diff --git a/tool/test/webrick/test_httpproxy.rb b/tool/test/webrick/test_httpproxy.rb deleted file mode 100644 index 66dae6f6f66910..00000000000000 --- a/tool/test/webrick/test_httpproxy.rb +++ /dev/null @@ -1,467 +0,0 @@ -# frozen_string_literal: false -require "test/unit" -require "net/http" -require "webrick" -require "webrick/httpproxy" -begin - require "webrick/ssl" - require "net/https" -rescue LoadError - # test_connect will be skipped -end -require File.expand_path("utils.rb", File.dirname(__FILE__)) - -class TestWEBrickHTTPProxy < Test::Unit::TestCase - def teardown - WEBrick::Utils::TimeoutHandler.terminate - super - end - - def test_fake_proxy - assert_nil(WEBrick::FakeProxyURI.scheme) - assert_nil(WEBrick::FakeProxyURI.host) - assert_nil(WEBrick::FakeProxyURI.port) - assert_nil(WEBrick::FakeProxyURI.path) - assert_nil(WEBrick::FakeProxyURI.userinfo) - assert_raise(NoMethodError){ WEBrick::FakeProxyURI.foo } - end - - def test_proxy - # Testing GET or POST to the proxy server - # Note that the proxy server works as the origin server. - # +------+ - # V | - # client -------> proxy ---+ - # GET / POST GET / POST - # - proxy_handler_called = request_handler_called = 0 - config = { - :ServerName => "localhost.localdomain", - :ProxyContentHandler => Proc.new{|req, res| proxy_handler_called += 1 }, - :RequestCallback => Proc.new{|req, res| request_handler_called += 1 } - } - TestWEBrick.start_httpproxy(config){|server, addr, port, log| - server.mount_proc("/"){|req, res| - res.body = "#{req.request_method} #{req.path} #{req.body}" - } - http = Net::HTTP.new(addr, port, addr, port) - - req = Net::HTTP::Get.new("/") - http.request(req){|res| - assert_equal("1.1 localhost.localdomain:#{port}", res["via"], log.call) - assert_equal("GET / ", res.body, log.call) - } - assert_equal(1, proxy_handler_called, log.call) - assert_equal(2, request_handler_called, log.call) - - req = Net::HTTP::Head.new("/") - http.request(req){|res| - assert_equal("1.1 localhost.localdomain:#{port}", res["via"], log.call) - assert_nil(res.body, log.call) - } - assert_equal(2, proxy_handler_called, log.call) - assert_equal(4, request_handler_called, log.call) - - req = Net::HTTP::Post.new("/") - req.body = "post-data" - req.content_type = "application/x-www-form-urlencoded" - http.request(req){|res| - assert_equal("1.1 localhost.localdomain:#{port}", res["via"], log.call) - assert_equal("POST / post-data", res.body, log.call) - } - assert_equal(3, proxy_handler_called, log.call) - assert_equal(6, request_handler_called, log.call) - } - end - - def test_no_proxy - # Testing GET or POST to the proxy server without proxy request. - # - # client -------> proxy - # GET / POST - # - proxy_handler_called = request_handler_called = 0 - config = { - :ServerName => "localhost.localdomain", - :ProxyContentHandler => Proc.new{|req, res| proxy_handler_called += 1 }, - :RequestCallback => Proc.new{|req, res| request_handler_called += 1 } - } - TestWEBrick.start_httpproxy(config){|server, addr, port, log| - server.mount_proc("/"){|req, res| - res.body = "#{req.request_method} #{req.path} #{req.body}" - } - http = Net::HTTP.new(addr, port) - - req = Net::HTTP::Get.new("/") - http.request(req){|res| - assert_nil(res["via"], log.call) - assert_equal("GET / ", res.body, log.call) - } - assert_equal(0, proxy_handler_called, log.call) - assert_equal(1, request_handler_called, log.call) - - req = Net::HTTP::Head.new("/") - http.request(req){|res| - assert_nil(res["via"], log.call) - assert_nil(res.body, log.call) - } - assert_equal(0, proxy_handler_called, log.call) - assert_equal(2, request_handler_called, log.call) - - req = Net::HTTP::Post.new("/") - req.content_type = "application/x-www-form-urlencoded" - req.body = "post-data" - http.request(req){|res| - assert_nil(res["via"], log.call) - assert_equal("POST / post-data", res.body, log.call) - } - assert_equal(0, proxy_handler_called, log.call) - assert_equal(3, request_handler_called, log.call) - } - end - - def test_big_bodies - require 'digest/md5' - rand_str = File.read(__FILE__) - rand_str.freeze - nr = 1024 ** 2 / rand_str.size # bigger works, too - exp = Digest::MD5.new - nr.times { exp.update(rand_str) } - exp = exp.hexdigest - TestWEBrick.start_httpserver do |o_server, o_addr, o_port, o_log| - o_server.mount_proc('/') do |req, res| - case req.request_method - when 'GET' - res['content-type'] = 'application/octet-stream' - if req.path == '/length' - res['content-length'] = (nr * rand_str.size).to_s - else - res.chunked = true - end - res.body = ->(socket) { nr.times { socket.write(rand_str) } } - when 'POST' - dig = Digest::MD5.new - req.body { |buf| dig.update(buf); buf.clear } - res['content-type'] = 'text/plain' - res['content-length'] = '32' - res.body = dig.hexdigest - end - end - - http = Net::HTTP.new(o_addr, o_port) - IO.pipe do |rd, wr| - headers = { - 'Content-Type' => 'application/octet-stream', - 'Transfer-Encoding' => 'chunked', - } - post = Net::HTTP::Post.new('/', headers) - th = Thread.new { nr.times { wr.write(rand_str) }; wr.close } - post.body_stream = rd - http.request(post) do |res| - assert_equal 'text/plain', res['content-type'] - assert_equal 32, res.content_length - assert_equal exp, res.body - end - assert_nil th.value - end - - TestWEBrick.start_httpproxy do |p_server, p_addr, p_port, p_log| - http = Net::HTTP.new(o_addr, o_port, p_addr, p_port) - http.request_get('/length') do |res| - assert_equal(nr * rand_str.size, res.content_length) - dig = Digest::MD5.new - res.read_body { |buf| dig.update(buf); buf.clear } - assert_equal exp, dig.hexdigest - end - http.request_get('/') do |res| - assert_predicate res, :chunked? - dig = Digest::MD5.new - res.read_body { |buf| dig.update(buf); buf.clear } - assert_equal exp, dig.hexdigest - end - - IO.pipe do |rd, wr| - headers = { - 'Content-Type' => 'application/octet-stream', - 'Content-Length' => (nr * rand_str.size).to_s, - } - post = Net::HTTP::Post.new('/', headers) - th = Thread.new { nr.times { wr.write(rand_str) }; wr.close } - post.body_stream = rd - http.request(post) do |res| - assert_equal 'text/plain', res['content-type'] - assert_equal 32, res.content_length - assert_equal exp, res.body - end - assert_nil th.value - end - - IO.pipe do |rd, wr| - headers = { - 'Content-Type' => 'application/octet-stream', - 'Transfer-Encoding' => 'chunked', - } - post = Net::HTTP::Post.new('/', headers) - th = Thread.new { nr.times { wr.write(rand_str) }; wr.close } - post.body_stream = rd - http.request(post) do |res| - assert_equal 'text/plain', res['content-type'] - assert_equal 32, res.content_length - assert_equal exp, res.body - end - assert_nil th.value - end - end - end - end if RUBY_VERSION >= '2.5' - - def test_http10_proxy_chunked - # Testing HTTP/1.0 client request and HTTP/1.1 chunked response - # from origin server. - # +------+ - # V | - # client -------> proxy ---+ - # GET GET - # HTTP/1.0 HTTP/1.1 - # non-chunked chunked - # - proxy_handler_called = request_handler_called = 0 - config = { - :ServerName => "localhost.localdomain", - :ProxyContentHandler => Proc.new{|req, res| proxy_handler_called += 1 }, - :RequestCallback => Proc.new{|req, res| request_handler_called += 1 } - } - log_tester = lambda {|log, access_log| - log.reject! {|str| - %r{WARN chunked is set for an HTTP/1\.0 request\. \(ignored\)} =~ str - } - assert_equal([], log) - } - TestWEBrick.start_httpproxy(config, log_tester){|server, addr, port, log| - body = nil - server.mount_proc("/"){|req, res| - body = "#{req.request_method} #{req.path} #{req.body}" - res.chunked = true - res.body = -> (socket) { body.each_char {|c| socket.write c } } - } - - # Don't use Net::HTTP because it uses HTTP/1.1. - TCPSocket.open(addr, port) {|s| - s.write "GET / HTTP/1.0\r\nHost: localhost.localdomain\r\n\r\n" - response = s.read - assert_equal(body, response[/.*\z/]) - } - } - end - - def make_certificate(key, cn) - subject = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=#{cn}") - exts = [ - ["keyUsage", "keyEncipherment,digitalSignature", true], - ] - cert = OpenSSL::X509::Certificate.new - cert.version = 2 - cert.serial = 1 - cert.subject = subject - cert.issuer = subject - cert.public_key = key - cert.not_before = Time.now - 3600 - cert.not_after = Time.now + 3600 - ef = OpenSSL::X509::ExtensionFactory.new(cert, cert) - exts.each {|args| cert.add_extension(ef.create_extension(*args)) } - cert.sign(key, "sha256") - return cert - end if defined?(OpenSSL::SSL) - - def test_connect - # Testing CONNECT to proxy server - # - # client -----------> proxy -----------> https - # 1. CONNECT establish TCP - # 2. ---- establish SSL session ---> - # 3. ------- GET or POST ----------> - # - key = TEST_KEY_RSA2048 - cert = make_certificate(key, "127.0.0.1") - s_config = { - :SSLEnable =>true, - :ServerName => "localhost", - :SSLCertificate => cert, - :SSLPrivateKey => key, - } - config = { - :ServerName => "localhost.localdomain", - :RequestCallback => Proc.new{|req, res| - assert_equal("CONNECT", req.request_method) - }, - } - TestWEBrick.start_httpserver(s_config){|s_server, s_addr, s_port, s_log| - s_server.mount_proc("/"){|req, res| - res.body = "SSL #{req.request_method} #{req.path} #{req.body}" - } - TestWEBrick.start_httpproxy(config){|server, addr, port, log| - http = Net::HTTP.new("127.0.0.1", s_port, addr, port) - http.use_ssl = true - http.verify_callback = Proc.new do |preverify_ok, store_ctx| - store_ctx.current_cert.to_der == cert.to_der - end - - req = Net::HTTP::Get.new("/") - req["Content-Type"] = "application/x-www-form-urlencoded" - http.request(req){|res| - assert_equal("SSL GET / ", res.body, s_log.call + log.call) - } - - req = Net::HTTP::Post.new("/") - req["Content-Type"] = "application/x-www-form-urlencoded" - req.body = "post-data" - http.request(req){|res| - assert_equal("SSL POST / post-data", res.body, s_log.call + log.call) - } - } - } - end if defined?(OpenSSL::SSL) - - def test_upstream_proxy - return if /mswin/ =~ RUBY_PLATFORM && ENV.key?('GITHUB_ACTIONS') # not working from the beginning - # Testing GET or POST through the upstream proxy server - # Note that the upstream proxy server works as the origin server. - # +------+ - # V | - # client -------> proxy -------> proxy ---+ - # GET / POST GET / POST GET / POST - # - up_proxy_handler_called = up_request_handler_called = 0 - proxy_handler_called = request_handler_called = 0 - up_config = { - :ServerName => "localhost.localdomain", - :ProxyContentHandler => Proc.new{|req, res| up_proxy_handler_called += 1}, - :RequestCallback => Proc.new{|req, res| up_request_handler_called += 1} - } - TestWEBrick.start_httpproxy(up_config){|up_server, up_addr, up_port, up_log| - up_server.mount_proc("/"){|req, res| - res.body = "#{req.request_method} #{req.path} #{req.body}" - } - config = { - :ServerName => "localhost.localdomain", - :ProxyURI => URI.parse("http://localhost:#{up_port}"), - :ProxyContentHandler => Proc.new{|req, res| proxy_handler_called += 1}, - :RequestCallback => Proc.new{|req, res| request_handler_called += 1}, - } - TestWEBrick.start_httpproxy(config){|server, addr, port, log| - http = Net::HTTP.new(up_addr, up_port, addr, port) - - req = Net::HTTP::Get.new("/") - http.request(req){|res| - skip res.message unless res.code == '200' - via = res["via"].split(/,\s+/) - assert(via.include?("1.1 localhost.localdomain:#{up_port}"), up_log.call + log.call) - assert(via.include?("1.1 localhost.localdomain:#{port}"), up_log.call + log.call) - assert_equal("GET / ", res.body) - } - assert_equal(1, up_proxy_handler_called, up_log.call + log.call) - assert_equal(2, up_request_handler_called, up_log.call + log.call) - assert_equal(1, proxy_handler_called, up_log.call + log.call) - assert_equal(1, request_handler_called, up_log.call + log.call) - - req = Net::HTTP::Head.new("/") - http.request(req){|res| - via = res["via"].split(/,\s+/) - assert(via.include?("1.1 localhost.localdomain:#{up_port}"), up_log.call + log.call) - assert(via.include?("1.1 localhost.localdomain:#{port}"), up_log.call + log.call) - assert_nil(res.body, up_log.call + log.call) - } - assert_equal(2, up_proxy_handler_called, up_log.call + log.call) - assert_equal(4, up_request_handler_called, up_log.call + log.call) - assert_equal(2, proxy_handler_called, up_log.call + log.call) - assert_equal(2, request_handler_called, up_log.call + log.call) - - req = Net::HTTP::Post.new("/") - req.body = "post-data" - req.content_type = "application/x-www-form-urlencoded" - http.request(req){|res| - via = res["via"].split(/,\s+/) - assert(via.include?("1.1 localhost.localdomain:#{up_port}"), up_log.call + log.call) - assert(via.include?("1.1 localhost.localdomain:#{port}"), up_log.call + log.call) - assert_equal("POST / post-data", res.body, up_log.call + log.call) - } - assert_equal(3, up_proxy_handler_called, up_log.call + log.call) - assert_equal(6, up_request_handler_called, up_log.call + log.call) - assert_equal(3, proxy_handler_called, up_log.call + log.call) - assert_equal(3, request_handler_called, up_log.call + log.call) - - if defined?(OpenSSL::SSL) - # Testing CONNECT to the upstream proxy server - # - # client -------> proxy -------> proxy -------> https - # 1. CONNECT CONNECT establish TCP - # 2. -------- establish SSL session ------> - # 3. ---------- GET or POST --------------> - # - key = TEST_KEY_RSA2048 - cert = make_certificate(key, "127.0.0.1") - s_config = { - :SSLEnable =>true, - :ServerName => "localhost", - :SSLCertificate => cert, - :SSLPrivateKey => key, - } - TestWEBrick.start_httpserver(s_config){|s_server, s_addr, s_port, s_log| - s_server.mount_proc("/"){|req2, res| - res.body = "SSL #{req2.request_method} #{req2.path} #{req2.body}" - } - http = Net::HTTP.new("127.0.0.1", s_port, addr, port, up_log.call + log.call + s_log.call) - http.use_ssl = true - http.verify_callback = Proc.new do |preverify_ok, store_ctx| - store_ctx.current_cert.to_der == cert.to_der - end - - req2 = Net::HTTP::Get.new("/") - http.request(req2){|res| - assert_equal("SSL GET / ", res.body, up_log.call + log.call + s_log.call) - } - - req2 = Net::HTTP::Post.new("/") - req2.body = "post-data" - req2.content_type = "application/x-www-form-urlencoded" - http.request(req2){|res| - assert_equal("SSL POST / post-data", res.body, up_log.call + log.call + s_log.call) - } - } - end - } - } - end - - if defined?(OpenSSL::SSL) - TEST_KEY_RSA2048 = OpenSSL::PKey.read <<-_end_of_pem_ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAuV9ht9J7k4NBs38jOXvvTKY9gW8nLICSno5EETR1cuF7i4pN -s9I1QJGAFAX0BEO4KbzXmuOvfCpD3CU+Slp1enenfzq/t/e/1IRW0wkJUJUFQign -4CtrkJL+P07yx18UjyPlBXb81ApEmAB5mrJVSrWmqbjs07JbuS4QQGGXLc+Su96D -kYKmSNVjBiLxVVSpyZfAY3hD37d60uG+X8xdW5v68JkRFIhdGlb6JL8fllf/A/bl -NwdJOhVr9mESHhwGjwfSeTDPfd8ZLE027E5lyAVX9KZYcU00mOX+fdxOSnGqS/8J -DRh0EPHDL15RcJjV2J6vZjPb0rOYGDoMcH+94wIDAQABAoIBAAzsamqfYQAqwXTb -I0CJtGg6msUgU7HVkOM+9d3hM2L791oGHV6xBAdpXW2H8LgvZHJ8eOeSghR8+dgq -PIqAffo4x1Oma+FOg3A0fb0evyiACyrOk+EcBdbBeLo/LcvahBtqnDfiUMQTpy6V -seSoFCwuN91TSCeGIsDpRjbG1vxZgtx+uI+oH5+ytqJOmfCksRDCkMglGkzyfcl0 -Xc5CUhIJ0my53xijEUQl19rtWdMnNnnkdbG8PT3LZlOta5Do86BElzUYka0C6dUc -VsBDQ0Nup0P6rEQgy7tephHoRlUGTYamsajGJaAo1F3IQVIrRSuagi7+YpSpCqsW -wORqorkCgYEA7RdX6MDVrbw7LePnhyuaqTiMK+055/R1TqhB1JvvxJ1CXk2rDL6G -0TLHQ7oGofd5LYiemg4ZVtWdJe43BPZlVgT6lvL/iGo8JnrncB9Da6L7nrq/+Rvj -XGjf1qODCK+LmreZWEsaLPURIoR/Ewwxb9J2zd0CaMjeTwafJo1CZvcCgYEAyCgb -aqoWvUecX8VvARfuA593Lsi50t4MEArnOXXcd1RnXoZWhbx5rgO8/ATKfXr0BK/n -h2GF9PfKzHFm/4V6e82OL7gu/kLy2u9bXN74vOvWFL5NOrOKPM7Kg+9I131kNYOw -Ivnr/VtHE5s0dY7JChYWE1F3vArrOw3T00a4CXUCgYEA0SqY+dS2LvIzW4cHCe9k -IQqsT0yYm5TFsUEr4sA3xcPfe4cV8sZb9k/QEGYb1+SWWZ+AHPV3UW5fl8kTbSNb -v4ng8i8rVVQ0ANbJO9e5CUrepein2MPL0AkOATR8M7t7dGGpvYV0cFk8ZrFx0oId -U0PgYDotF/iueBWlbsOM430CgYEAqYI95dFyPI5/AiSkY5queeb8+mQH62sdcCCr -vd/w/CZA/K5sbAo4SoTj8dLk4evU6HtIa0DOP63y071eaxvRpTNqLUOgmLh+D6gS -Cc7TfLuFrD+WDBatBd5jZ+SoHccVrLR/4L8jeodo5FPW05A+9gnKXEXsTxY4LOUC -9bS4e1kCgYAqVXZh63JsMwoaxCYmQ66eJojKa47VNrOeIZDZvd2BPVf30glBOT41 -gBoDG3WMPZoQj9pb7uMcrnvs4APj2FIhMU8U15LcPAj59cD6S6rWnAxO8NFK7HQG -4Jxg3JNNf8ErQoCHb1B3oVdXJkmbJkARoDpBKmTCgKtP8ADYLmVPQw== ------END RSA PRIVATE KEY----- - _end_of_pem_ - end -end diff --git a/tool/test/webrick/test_httprequest.rb b/tool/test/webrick/test_httprequest.rb deleted file mode 100644 index 3c0ea937d9c239..00000000000000 --- a/tool/test/webrick/test_httprequest.rb +++ /dev/null @@ -1,488 +0,0 @@ -# frozen_string_literal: false -require "webrick" -require "stringio" -require "test/unit" - -class TestWEBrickHTTPRequest < Test::Unit::TestCase - def teardown - WEBrick::Utils::TimeoutHandler.terminate - super - end - - def test_simple_request - msg = <<-_end_of_message_ -GET / - _end_of_message_ - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg)) - assert(req.meta_vars) # fails if @header was not initialized and iteration is attempted on the nil reference - end - - def test_parse_09 - msg = <<-_end_of_message_ - GET / - foobar # HTTP/0.9 request don't have header nor entity body. - _end_of_message_ - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg.gsub(/^ {6}/, ""))) - assert_equal("GET", req.request_method) - assert_equal("/", req.unparsed_uri) - assert_equal(WEBrick::HTTPVersion.new("0.9"), req.http_version) - assert_equal(WEBrick::Config::HTTP[:ServerName], req.host) - assert_equal(80, req.port) - assert_equal(false, req.keep_alive?) - assert_equal(nil, req.body) - assert(req.query.empty?) - end - - def test_parse_10 - msg = <<-_end_of_message_ - GET / HTTP/1.0 - - _end_of_message_ - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg.gsub(/^ {6}/, ""))) - assert_equal("GET", req.request_method) - assert_equal("/", req.unparsed_uri) - assert_equal(WEBrick::HTTPVersion.new("1.0"), req.http_version) - assert_equal(WEBrick::Config::HTTP[:ServerName], req.host) - assert_equal(80, req.port) - assert_equal(false, req.keep_alive?) - assert_equal(nil, req.body) - assert(req.query.empty?) - end - - def test_parse_11 - msg = <<-_end_of_message_ - GET /path HTTP/1.1 - - _end_of_message_ - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg.gsub(/^ {6}/, ""))) - assert_equal("GET", req.request_method) - assert_equal("/path", req.unparsed_uri) - assert_equal("", req.script_name) - assert_equal("/path", req.path_info) - assert_equal(WEBrick::HTTPVersion.new("1.1"), req.http_version) - assert_equal(WEBrick::Config::HTTP[:ServerName], req.host) - assert_equal(80, req.port) - assert_equal(true, req.keep_alive?) - assert_equal(nil, req.body) - assert(req.query.empty?) - end - - def test_request_uri_too_large - msg = <<-_end_of_message_ - GET /#{"a"*2084} HTTP/1.1 - _end_of_message_ - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - assert_raise(WEBrick::HTTPStatus::RequestURITooLarge){ - req.parse(StringIO.new(msg.gsub(/^ {6}/, ""))) - } - end - - def test_parse_headers - msg = <<-_end_of_message_ - GET /path HTTP/1.1 - Host: test.ruby-lang.org:8080 - Connection: close - Accept: text/*;q=0.3, text/html;q=0.7, text/html;level=1, - text/html;level=2;q=0.4, */*;q=0.5 - Accept-Encoding: compress;q=0.5 - Accept-Encoding: gzip;q=1.0, identity; q=0.4, *;q=0 - Accept-Language: en;q=0.5, *; q=0 - Accept-Language: ja - Content-Type: text/plain - Content-Length: 7 - X-Empty-Header: - - foobar - _end_of_message_ - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg.gsub(/^ {6}/, ""))) - assert_equal( - URI.parse("http://test.ruby-lang.org:8080/path"), req.request_uri) - assert_equal("test.ruby-lang.org", req.host) - assert_equal(8080, req.port) - assert_equal(false, req.keep_alive?) - assert_equal( - %w(text/html;level=1 text/html */* text/html;level=2 text/*), - req.accept) - assert_equal(%w(gzip compress identity *), req.accept_encoding) - assert_equal(%w(ja en *), req.accept_language) - assert_equal(7, req.content_length) - assert_equal("text/plain", req.content_type) - assert_equal("foobar\n", req.body) - assert_equal("", req["x-empty-header"]) - assert_equal(nil, req["x-no-header"]) - assert(req.query.empty?) - end - - def test_parse_header2() - msg = <<-_end_of_message_ - POST /foo/bar/../baz?q=a HTTP/1.0 - Content-Length: 9 - User-Agent: - FOO BAR - BAZ - - hogehoge - _end_of_message_ - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg.gsub(/^ {6}/, ""))) - assert_equal("POST", req.request_method) - assert_equal("/foo/baz", req.path) - assert_equal("", req.script_name) - assert_equal("/foo/baz", req.path_info) - assert_equal("9", req['content-length']) - assert_equal("FOO BAR BAZ", req['user-agent']) - assert_equal("hogehoge\n", req.body) - end - - def test_parse_headers3 - msg = <<-_end_of_message_ - GET /path HTTP/1.1 - Host: test.ruby-lang.org - - _end_of_message_ - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg.gsub(/^ {6}/, ""))) - assert_equal(URI.parse("http://test.ruby-lang.org/path"), req.request_uri) - assert_equal("test.ruby-lang.org", req.host) - assert_equal(80, req.port) - - msg = <<-_end_of_message_ - GET /path HTTP/1.1 - Host: 192.168.1.1 - - _end_of_message_ - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg.gsub(/^ {6}/, ""))) - assert_equal(URI.parse("http://192.168.1.1/path"), req.request_uri) - assert_equal("192.168.1.1", req.host) - assert_equal(80, req.port) - - msg = <<-_end_of_message_ - GET /path HTTP/1.1 - Host: [fe80::208:dff:feef:98c7] - - _end_of_message_ - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg.gsub(/^ {6}/, ""))) - assert_equal(URI.parse("http://[fe80::208:dff:feef:98c7]/path"), - req.request_uri) - assert_equal("[fe80::208:dff:feef:98c7]", req.host) - assert_equal(80, req.port) - - msg = <<-_end_of_message_ - GET /path HTTP/1.1 - Host: 192.168.1.1:8080 - - _end_of_message_ - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg.gsub(/^ {6}/, ""))) - assert_equal(URI.parse("http://192.168.1.1:8080/path"), req.request_uri) - assert_equal("192.168.1.1", req.host) - assert_equal(8080, req.port) - - msg = <<-_end_of_message_ - GET /path HTTP/1.1 - Host: [fe80::208:dff:feef:98c7]:8080 - - _end_of_message_ - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg.gsub(/^ {6}/, ""))) - assert_equal(URI.parse("http://[fe80::208:dff:feef:98c7]:8080/path"), - req.request_uri) - assert_equal("[fe80::208:dff:feef:98c7]", req.host) - assert_equal(8080, req.port) - end - - def test_parse_get_params - param = "foo=1;foo=2;foo=3;bar=x" - msg = <<-_end_of_message_ - GET /path?#{param} HTTP/1.1 - Host: test.ruby-lang.org:8080 - - _end_of_message_ - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg.gsub(/^ {6}/, ""))) - query = req.query - assert_equal("1", query["foo"]) - assert_equal(["1", "2", "3"], query["foo"].to_ary) - assert_equal(["1", "2", "3"], query["foo"].list) - assert_equal("x", query["bar"]) - assert_equal(["x"], query["bar"].list) - end - - def test_parse_post_params - param = "foo=1;foo=2;foo=3;bar=x" - msg = <<-_end_of_message_ - POST /path?foo=x;foo=y;foo=z;bar=1 HTTP/1.1 - Host: test.ruby-lang.org:8080 - Content-Length: #{param.size} - Content-Type: application/x-www-form-urlencoded - - #{param} - _end_of_message_ - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg.gsub(/^ {6}/, ""))) - query = req.query - assert_equal("1", query["foo"]) - assert_equal(["1", "2", "3"], query["foo"].to_ary) - assert_equal(["1", "2", "3"], query["foo"].list) - assert_equal("x", query["bar"]) - assert_equal(["x"], query["bar"].list) - end - - def test_chunked - crlf = "\x0d\x0a" - expect = File.binread(__FILE__).freeze - msg = <<-_end_of_message_ - POST /path HTTP/1.1 - Host: test.ruby-lang.org:8080 - Transfer-Encoding: chunked - - _end_of_message_ - msg.gsub!(/^ {6}/, "") - File.open(__FILE__){|io| - while chunk = io.read(100) - msg << chunk.size.to_s(16) << crlf - msg << chunk << crlf - end - } - msg << "0" << crlf - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg)) - assert_equal(expect, req.body) - - # chunked req.body_reader - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg)) - dst = StringIO.new - IO.copy_stream(req.body_reader, dst) - assert_equal(expect, dst.string) - end - - def test_forwarded - msg = <<-_end_of_message_ - GET /foo HTTP/1.1 - Host: localhost:10080 - User-Agent: w3m/0.5.2 - X-Forwarded-For: 123.123.123.123 - X-Forwarded-Host: forward.example.com - X-Forwarded-Server: server.example.com - Connection: Keep-Alive - - _end_of_message_ - msg.gsub!(/^ {6}/, "") - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg)) - assert_equal("server.example.com", req.server_name) - assert_equal("http://forward.example.com/foo", req.request_uri.to_s) - assert_equal("forward.example.com", req.host) - assert_equal(80, req.port) - assert_equal("123.123.123.123", req.remote_ip) - assert(!req.ssl?) - - msg = <<-_end_of_message_ - GET /foo HTTP/1.1 - Host: localhost:10080 - User-Agent: w3m/0.5.2 - X-Forwarded-For: 192.168.1.10, 172.16.1.1, 123.123.123.123 - X-Forwarded-Host: forward.example.com:8080 - X-Forwarded-Server: server.example.com - Connection: Keep-Alive - - _end_of_message_ - msg.gsub!(/^ {6}/, "") - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg)) - assert_equal("server.example.com", req.server_name) - assert_equal("http://forward.example.com:8080/foo", req.request_uri.to_s) - assert_equal("forward.example.com", req.host) - assert_equal(8080, req.port) - assert_equal("123.123.123.123", req.remote_ip) - assert(!req.ssl?) - - msg = <<-_end_of_message_ - GET /foo HTTP/1.1 - Host: localhost:10080 - Client-IP: 234.234.234.234 - X-Forwarded-Proto: https, http - X-Forwarded-For: 192.168.1.10, 10.0.0.1, 123.123.123.123 - X-Forwarded-Host: forward.example.com - X-Forwarded-Server: server.example.com - X-Requested-With: XMLHttpRequest - Connection: Keep-Alive - - _end_of_message_ - msg.gsub!(/^ {6}/, "") - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg)) - assert_equal("server.example.com", req.server_name) - assert_equal("https://forward.example.com/foo", req.request_uri.to_s) - assert_equal("forward.example.com", req.host) - assert_equal(443, req.port) - assert_equal("234.234.234.234", req.remote_ip) - assert(req.ssl?) - - msg = <<-_end_of_message_ - GET /foo HTTP/1.1 - Host: localhost:10080 - Client-IP: 234.234.234.234 - X-Forwarded-Proto: https - X-Forwarded-For: 192.168.1.10 - X-Forwarded-Host: forward1.example.com:1234, forward2.example.com:5678 - X-Forwarded-Server: server1.example.com, server2.example.com - X-Requested-With: XMLHttpRequest - Connection: Keep-Alive - - _end_of_message_ - msg.gsub!(/^ {6}/, "") - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg)) - assert_equal("server1.example.com", req.server_name) - assert_equal("https://forward1.example.com:1234/foo", req.request_uri.to_s) - assert_equal("forward1.example.com", req.host) - assert_equal(1234, req.port) - assert_equal("234.234.234.234", req.remote_ip) - assert(req.ssl?) - - msg = <<-_end_of_message_ - GET /foo HTTP/1.1 - Host: localhost:10080 - Client-IP: 234.234.234.234 - X-Forwarded-Proto: https - X-Forwarded-For: 192.168.1.10 - X-Forwarded-Host: [fd20:8b1e:b255:8154:250:56ff:fea8:4d84], forward2.example.com:5678 - X-Forwarded-Server: server1.example.com, server2.example.com - X-Requested-With: XMLHttpRequest - Connection: Keep-Alive - - _end_of_message_ - msg.gsub!(/^ {6}/, "") - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg)) - assert_equal("server1.example.com", req.server_name) - assert_equal("https://[fd20:8b1e:b255:8154:250:56ff:fea8:4d84]/foo", req.request_uri.to_s) - assert_equal("[fd20:8b1e:b255:8154:250:56ff:fea8:4d84]", req.host) - assert_equal(443, req.port) - assert_equal("234.234.234.234", req.remote_ip) - assert(req.ssl?) - - msg = <<-_end_of_message_ - GET /foo HTTP/1.1 - Host: localhost:10080 - Client-IP: 234.234.234.234 - X-Forwarded-Proto: https - X-Forwarded-For: 192.168.1.10 - X-Forwarded-Host: [fd20:8b1e:b255:8154:250:56ff:fea8:4d84]:1234, forward2.example.com:5678 - X-Forwarded-Server: server1.example.com, server2.example.com - X-Requested-With: XMLHttpRequest - Connection: Keep-Alive - - _end_of_message_ - msg.gsub!(/^ {6}/, "") - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg)) - assert_equal("server1.example.com", req.server_name) - assert_equal("https://[fd20:8b1e:b255:8154:250:56ff:fea8:4d84]:1234/foo", req.request_uri.to_s) - assert_equal("[fd20:8b1e:b255:8154:250:56ff:fea8:4d84]", req.host) - assert_equal(1234, req.port) - assert_equal("234.234.234.234", req.remote_ip) - assert(req.ssl?) - end - - def test_continue_sent - msg = <<-_end_of_message_ - POST /path HTTP/1.1 - Expect: 100-continue - - _end_of_message_ - msg.gsub!(/^ {6}/, "") - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg)) - assert req['expect'] - l = msg.size - req.continue - assert_not_equal l, msg.size - assert_match(/HTTP\/1.1 100 continue\r\n\r\n\z/, msg) - assert !req['expect'] - end - - def test_continue_not_sent - msg = <<-_end_of_message_ - POST /path HTTP/1.1 - - _end_of_message_ - msg.gsub!(/^ {6}/, "") - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg)) - assert !req['expect'] - l = msg.size - req.continue - assert_equal l, msg.size - end - - def test_empty_post - msg = <<-_end_of_message_ - POST /path?foo=x;foo=y;foo=z;bar=1 HTTP/1.1 - Host: test.ruby-lang.org:8080 - Content-Type: application/x-www-form-urlencoded - - _end_of_message_ - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg.gsub(/^ {6}/, ""))) - req.body - end - - def test_bad_messages - param = "foo=1;foo=2;foo=3;bar=x" - msg = <<-_end_of_message_ - POST /path?foo=x;foo=y;foo=z;bar=1 HTTP/1.1 - Host: test.ruby-lang.org:8080 - Content-Type: application/x-www-form-urlencoded - - #{param} - _end_of_message_ - assert_raise(WEBrick::HTTPStatus::LengthRequired){ - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg.gsub(/^ {6}/, ""))) - req.body - } - - msg = <<-_end_of_message_ - POST /path?foo=x;foo=y;foo=z;bar=1 HTTP/1.1 - Host: test.ruby-lang.org:8080 - Content-Length: 100000 - - body is too short. - _end_of_message_ - assert_raise(WEBrick::HTTPStatus::BadRequest){ - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg.gsub(/^ {6}/, ""))) - req.body - } - - msg = <<-_end_of_message_ - POST /path?foo=x;foo=y;foo=z;bar=1 HTTP/1.1 - Host: test.ruby-lang.org:8080 - Transfer-Encoding: foobar - - body is too short. - _end_of_message_ - assert_raise(WEBrick::HTTPStatus::NotImplemented){ - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new(msg.gsub(/^ {6}/, ""))) - req.body - } - end - - def test_eof_raised_when_line_is_nil - assert_raise(WEBrick::HTTPStatus::EOFError) { - req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) - req.parse(StringIO.new("")) - } - end -end diff --git a/tool/test/webrick/test_httpresponse.rb b/tool/test/webrick/test_httpresponse.rb deleted file mode 100644 index 4410f63e893d5d..00000000000000 --- a/tool/test/webrick/test_httpresponse.rb +++ /dev/null @@ -1,282 +0,0 @@ -# frozen_string_literal: false -require "webrick" -require "test/unit" -require "stringio" -require "net/http" - -module WEBrick - class TestHTTPResponse < Test::Unit::TestCase - class FakeLogger - attr_reader :messages - - def initialize - @messages = [] - end - - def warn msg - @messages << msg - end - end - - attr_reader :config, :logger, :res - - def setup - super - @logger = FakeLogger.new - @config = Config::HTTP - @config[:Logger] = logger - @res = HTTPResponse.new config - @res.keep_alive = true - end - - def test_prevent_response_splitting_headers_crlf - res['X-header'] = "malicious\r\nCookie: cracked_indicator_for_test" - io = StringIO.new - res.send_response io - io.rewind - res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io)) - assert_equal '500', res.code - refute_match 'cracked_indicator_for_test', io.string - end - - def test_prevent_response_splitting_cookie_headers_crlf - user_input = "malicious\r\nCookie: cracked_indicator_for_test" - res.cookies << WEBrick::Cookie.new('author', user_input) - io = StringIO.new - res.send_response io - io.rewind - res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io)) - assert_equal '500', res.code - refute_match 'cracked_indicator_for_test', io.string - end - - def test_prevent_response_splitting_headers_cr - res['X-header'] = "malicious\rCookie: cracked_indicator_for_test" - io = StringIO.new - res.send_response io - io.rewind - res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io)) - assert_equal '500', res.code - refute_match 'cracked_indicator_for_test', io.string - end - - def test_prevent_response_splitting_cookie_headers_cr - user_input = "malicious\rCookie: cracked_indicator_for_test" - res.cookies << WEBrick::Cookie.new('author', user_input) - io = StringIO.new - res.send_response io - io.rewind - res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io)) - assert_equal '500', res.code - refute_match 'cracked_indicator_for_test', io.string - end - - def test_prevent_response_splitting_headers_lf - res['X-header'] = "malicious\nCookie: cracked_indicator_for_test" - io = StringIO.new - res.send_response io - io.rewind - res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io)) - assert_equal '500', res.code - refute_match 'cracked_indicator_for_test', io.string - end - - def test_prevent_response_splitting_cookie_headers_lf - user_input = "malicious\nCookie: cracked_indicator_for_test" - res.cookies << WEBrick::Cookie.new('author', user_input) - io = StringIO.new - res.send_response io - io.rewind - res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io)) - assert_equal '500', res.code - refute_match 'cracked_indicator_for_test', io.string - end - - def test_set_redirect_response_splitting - url = "malicious\r\nCookie: cracked_indicator_for_test" - assert_raise(URI::InvalidURIError) do - res.set_redirect(WEBrick::HTTPStatus::MultipleChoices, url) - end - end - - def test_set_redirect_html_injection - url = 'http://example.com////?a' - assert_raise(WEBrick::HTTPStatus::MultipleChoices) do - res.set_redirect(WEBrick::HTTPStatus::MultipleChoices, url) - end - res.status = 300 - io = StringIO.new - res.send_response(io) - io.rewind - res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io)) - assert_equal '300', res.code - refute_match(/ "localhost", - :SSLEnable => true, - :SSLCertName => "/CN=localhost", - } - TestWEBrick.start_httpserver(config){|server, addr, port, log| - server.mount_proc("/") {|req, res| res.body = "master" } - - # catch stderr in create_self_signed_cert - stderr_buffer = StringIO.new - old_stderr, $stderr = $stderr, stderr_buffer - - begin - vhost_config1 = { - :ServerName => "vhost1", - :Port => port, - :DoNotListen => true, - :Logger => NoLog, - :AccessLog => [], - :SSLEnable => true, - :SSLCertName => "/CN=vhost1", - } - vhost1 = WEBrick::HTTPServer.new(vhost_config1) - vhost1.mount_proc("/") {|req, res| res.body = "vhost1" } - server.virtual_host(vhost1) - - vhost_config2 = { - :ServerName => "vhost2", - :ServerAlias => ["vhost2alias"], - :Port => port, - :DoNotListen => true, - :Logger => NoLog, - :AccessLog => [], - :SSLEnable => true, - :SSLCertName => "/CN=vhost2", - } - vhost2 = WEBrick::HTTPServer.new(vhost_config2) - vhost2.mount_proc("/") {|req, res| res.body = "vhost2" } - server.virtual_host(vhost2) - ensure - # restore stderr - $stderr = old_stderr - end - - assert_match(/\A([.+*]+\n)+\z/, stderr_buffer.string) - assert_equal("master", https_get(addr, port, "localhost", "/localhost")) - assert_equal("master", https_get(addr, port, "unknown", "/unknown", "localhost")) - assert_equal("vhost1", https_get(addr, port, "vhost1", "/vhost1")) - assert_equal("vhost2", https_get(addr, port, "vhost2", "/vhost2")) - assert_equal("vhost2", https_get(addr, port, "vhost2alias", "/vhost2alias", "vhost2")) - } - end - - def test_check_ssl_virtual - config = { - :ServerName => "localhost", - :SSLEnable => true, - :SSLCertName => "/CN=localhost", - } - TestWEBrick.start_httpserver(config){|server, addr, port, log| - assert_raise ArgumentError do - vhost = WEBrick::HTTPServer.new({:DoNotListen => true, :Logger => NoLog}) - server.virtual_host(vhost) - end - } - end -end diff --git a/tool/test/webrick/test_httpserver.rb b/tool/test/webrick/test_httpserver.rb deleted file mode 100644 index f6b53e142ba99a..00000000000000 --- a/tool/test/webrick/test_httpserver.rb +++ /dev/null @@ -1,543 +0,0 @@ -# frozen_string_literal: false -require "test/unit" -require "net/http" -require "webrick" -require_relative "utils" - -class TestWEBrickHTTPServer < Test::Unit::TestCase - empty_log = Object.new - def empty_log.<<(str) - assert_equal('', str) - self - end - NoLog = WEBrick::Log.new(empty_log, WEBrick::BasicLog::WARN) - - def teardown - WEBrick::Utils::TimeoutHandler.terminate - super - end - - def test_mount - httpd = WEBrick::HTTPServer.new( - :Logger => NoLog, - :DoNotListen=>true - ) - httpd.mount("/", :Root) - httpd.mount("/foo", :Foo) - httpd.mount("/foo/bar", :Bar, :bar1) - httpd.mount("/foo/bar/baz", :Baz, :baz1, :baz2) - - serv, opts, script_name, path_info = httpd.search_servlet("/") - assert_equal(:Root, serv) - assert_equal([], opts) - assert_equal("", script_name) - assert_equal("/", path_info) - - serv, opts, script_name, path_info = httpd.search_servlet("/sub") - assert_equal(:Root, serv) - assert_equal([], opts) - assert_equal("", script_name) - assert_equal("/sub", path_info) - - serv, opts, script_name, path_info = httpd.search_servlet("/sub/") - assert_equal(:Root, serv) - assert_equal([], opts) - assert_equal("", script_name) - assert_equal("/sub/", path_info) - - serv, opts, script_name, path_info = httpd.search_servlet("/foo") - assert_equal(:Foo, serv) - assert_equal([], opts) - assert_equal("/foo", script_name) - assert_equal("", path_info) - - serv, opts, script_name, path_info = httpd.search_servlet("/foo/") - assert_equal(:Foo, serv) - assert_equal([], opts) - assert_equal("/foo", script_name) - assert_equal("/", path_info) - - serv, opts, script_name, path_info = httpd.search_servlet("/foo/sub") - assert_equal(:Foo, serv) - assert_equal([], opts) - assert_equal("/foo", script_name) - assert_equal("/sub", path_info) - - serv, opts, script_name, path_info = httpd.search_servlet("/foo/bar") - assert_equal(:Bar, serv) - assert_equal([:bar1], opts) - assert_equal("/foo/bar", script_name) - assert_equal("", path_info) - - serv, opts, script_name, path_info = httpd.search_servlet("/foo/bar/baz") - assert_equal(:Baz, serv) - assert_equal([:baz1, :baz2], opts) - assert_equal("/foo/bar/baz", script_name) - assert_equal("", path_info) - end - - class Req - attr_reader :port, :host - def initialize(addr, port, host) - @addr, @port, @host = addr, port, host - end - def addr - [0,0,0,@addr] - end - end - - def httpd(addr, port, host, ali) - config ={ - :Logger => NoLog, - :DoNotListen => true, - :BindAddress => addr, - :Port => port, - :ServerName => host, - :ServerAlias => ali, - } - return WEBrick::HTTPServer.new(config) - end - - def assert_eql?(v1, v2) - assert_equal(v1.object_id, v2.object_id) - end - - def test_lookup_server - addr1 = "192.168.100.1" - addr2 = "192.168.100.2" - addrz = "192.168.100.254" - local = "127.0.0.1" - port1 = 80 - port2 = 8080 - port3 = 10080 - portz = 32767 - name1 = "www.example.com" - name2 = "www2.example.com" - name3 = "www3.example.com" - namea = "www.example.co.jp" - nameb = "www.example.jp" - namec = "www2.example.co.jp" - named = "www2.example.jp" - namez = "foobar.example.com" - alias1 = [namea, nameb] - alias2 = [namec, named] - - host1 = httpd(nil, port1, name1, nil) - hosts = [ - host2 = httpd(addr1, port1, name1, nil), - host3 = httpd(addr1, port1, name2, alias1), - host4 = httpd(addr1, port2, name1, nil), - host5 = httpd(addr1, port2, name2, alias1), - httpd(addr1, port2, name3, alias2), - host7 = httpd(addr2, nil, name1, nil), - host8 = httpd(addr2, nil, name2, alias1), - httpd(addr2, nil, name3, alias2), - host10 = httpd(local, nil, nil, nil), - host11 = httpd(nil, port3, nil, nil), - ].sort_by{ rand } - hosts.each{|h| host1.virtual_host(h) } - - # connect to addr1 - assert_eql?(host2, host1.lookup_server(Req.new(addr1, port1, name1))) - assert_eql?(host3, host1.lookup_server(Req.new(addr1, port1, name2))) - assert_eql?(host3, host1.lookup_server(Req.new(addr1, port1, namea))) - assert_eql?(host3, host1.lookup_server(Req.new(addr1, port1, nameb))) - assert_eql?(nil, host1.lookup_server(Req.new(addr1, port1, namez))) - assert_eql?(host4, host1.lookup_server(Req.new(addr1, port2, name1))) - assert_eql?(host5, host1.lookup_server(Req.new(addr1, port2, name2))) - assert_eql?(host5, host1.lookup_server(Req.new(addr1, port2, namea))) - assert_eql?(host5, host1.lookup_server(Req.new(addr1, port2, nameb))) - assert_eql?(nil, host1.lookup_server(Req.new(addr1, port2, namez))) - assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, name1))) - assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, name2))) - assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, namea))) - assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, nameb))) - assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, namez))) - assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, name1))) - assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, name2))) - assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, namea))) - assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, nameb))) - assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, namez))) - - # connect to addr2 - assert_eql?(host7, host1.lookup_server(Req.new(addr2, port1, name1))) - assert_eql?(host8, host1.lookup_server(Req.new(addr2, port1, name2))) - assert_eql?(host8, host1.lookup_server(Req.new(addr2, port1, namea))) - assert_eql?(host8, host1.lookup_server(Req.new(addr2, port1, nameb))) - assert_eql?(nil, host1.lookup_server(Req.new(addr2, port1, namez))) - assert_eql?(host7, host1.lookup_server(Req.new(addr2, port2, name1))) - assert_eql?(host8, host1.lookup_server(Req.new(addr2, port2, name2))) - assert_eql?(host8, host1.lookup_server(Req.new(addr2, port2, namea))) - assert_eql?(host8, host1.lookup_server(Req.new(addr2, port2, nameb))) - assert_eql?(nil, host1.lookup_server(Req.new(addr2, port2, namez))) - assert_eql?(host7, host1.lookup_server(Req.new(addr2, port3, name1))) - assert_eql?(host8, host1.lookup_server(Req.new(addr2, port3, name2))) - assert_eql?(host8, host1.lookup_server(Req.new(addr2, port3, namea))) - assert_eql?(host8, host1.lookup_server(Req.new(addr2, port3, nameb))) - assert_eql?(host11, host1.lookup_server(Req.new(addr2, port3, namez))) - assert_eql?(host7, host1.lookup_server(Req.new(addr2, portz, name1))) - assert_eql?(host8, host1.lookup_server(Req.new(addr2, portz, name2))) - assert_eql?(host8, host1.lookup_server(Req.new(addr2, portz, namea))) - assert_eql?(host8, host1.lookup_server(Req.new(addr2, portz, nameb))) - assert_eql?(nil, host1.lookup_server(Req.new(addr2, portz, namez))) - - # connect to addrz - assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, name1))) - assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, name2))) - assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, namea))) - assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, nameb))) - assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, namez))) - assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, name1))) - assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, name2))) - assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, namea))) - assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, nameb))) - assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, namez))) - assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, name1))) - assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, name2))) - assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, namea))) - assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, nameb))) - assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, namez))) - assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, name1))) - assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, name2))) - assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, namea))) - assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, nameb))) - assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, namez))) - - # connect to localhost - assert_eql?(host10, host1.lookup_server(Req.new(local, port1, name1))) - assert_eql?(host10, host1.lookup_server(Req.new(local, port1, name2))) - assert_eql?(host10, host1.lookup_server(Req.new(local, port1, namea))) - assert_eql?(host10, host1.lookup_server(Req.new(local, port1, nameb))) - assert_eql?(host10, host1.lookup_server(Req.new(local, port1, namez))) - assert_eql?(host10, host1.lookup_server(Req.new(local, port2, name1))) - assert_eql?(host10, host1.lookup_server(Req.new(local, port2, name2))) - assert_eql?(host10, host1.lookup_server(Req.new(local, port2, namea))) - assert_eql?(host10, host1.lookup_server(Req.new(local, port2, nameb))) - assert_eql?(host10, host1.lookup_server(Req.new(local, port2, namez))) - assert_eql?(host10, host1.lookup_server(Req.new(local, port3, name1))) - assert_eql?(host10, host1.lookup_server(Req.new(local, port3, name2))) - assert_eql?(host10, host1.lookup_server(Req.new(local, port3, namea))) - assert_eql?(host10, host1.lookup_server(Req.new(local, port3, nameb))) - assert_eql?(host10, host1.lookup_server(Req.new(local, port3, namez))) - assert_eql?(host10, host1.lookup_server(Req.new(local, portz, name1))) - assert_eql?(host10, host1.lookup_server(Req.new(local, portz, name2))) - assert_eql?(host10, host1.lookup_server(Req.new(local, portz, namea))) - assert_eql?(host10, host1.lookup_server(Req.new(local, portz, nameb))) - assert_eql?(host10, host1.lookup_server(Req.new(local, portz, namez))) - end - - def test_callbacks - accepted = started = stopped = 0 - requested0 = requested1 = 0 - config = { - :ServerName => "localhost", - :AcceptCallback => Proc.new{ accepted += 1 }, - :StartCallback => Proc.new{ started += 1 }, - :StopCallback => Proc.new{ stopped += 1 }, - :RequestCallback => Proc.new{|req, res| requested0 += 1 }, - } - log_tester = lambda {|log, access_log| - assert(log.find {|s| %r{ERROR `/' not found\.} =~ s }) - assert_equal([], log.reject {|s| %r{ERROR `/' not found\.} =~ s }) - } - TestWEBrick.start_httpserver(config, log_tester){|server, addr, port, log| - vhost_config = { - :ServerName => "myhostname", - :BindAddress => addr, - :Port => port, - :DoNotListen => true, - :Logger => NoLog, - :AccessLog => [], - :RequestCallback => Proc.new{|req, res| requested1 += 1 }, - } - server.virtual_host(WEBrick::HTTPServer.new(vhost_config)) - - Thread.pass while server.status != :Running - sleep 1 if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? # server.status behaves unexpectedly with --jit-wait - assert_equal(1, started, log.call) - assert_equal(0, stopped, log.call) - assert_equal(0, accepted, log.call) - - http = Net::HTTP.new(addr, port) - req = Net::HTTP::Get.new("/") - req["Host"] = "myhostname:#{port}" - http.request(req){|res| assert_equal("404", res.code, log.call)} - http.request(req){|res| assert_equal("404", res.code, log.call)} - http.request(req){|res| assert_equal("404", res.code, log.call)} - req["Host"] = "localhost:#{port}" - http.request(req){|res| assert_equal("404", res.code, log.call)} - http.request(req){|res| assert_equal("404", res.code, log.call)} - http.request(req){|res| assert_equal("404", res.code, log.call)} - assert_equal(6, accepted, log.call) - assert_equal(3, requested0, log.call) - assert_equal(3, requested1, log.call) - } - assert_equal(started, 1) - assert_equal(stopped, 1) - end - - class CustomRequest < ::WEBrick::HTTPRequest; end - class CustomResponse < ::WEBrick::HTTPResponse; end - class CustomServer < ::WEBrick::HTTPServer - def create_request(config) - CustomRequest.new(config) - end - - def create_response(config) - CustomResponse.new(config) - end - end - - def test_custom_server_request_and_response - config = { :ServerName => "localhost" } - TestWEBrick.start_server(CustomServer, config){|server, addr, port, log| - server.mount_proc("/", lambda {|req, res| - assert_kind_of(CustomRequest, req) - assert_kind_of(CustomResponse, res) - res.body = "via custom response" - }) - Thread.pass while server.status != :Running - - Net::HTTP.start(addr, port) do |http| - req = Net::HTTP::Get.new("/") - http.request(req){|res| - assert_equal("via custom response", res.body) - } - server.shutdown - end - } - end - - # This class is needed by test_response_io_with_chunked_set method - class EventManagerForChunkedResponseTest - def initialize - @listeners = [] - end - def add_listener( &block ) - @listeners << block - end - def raise_str_event( str ) - @listeners.each{ |e| e.call( :str, str ) } - end - def raise_close_event() - @listeners.each{ |e| e.call( :cls ) } - end - end - def test_response_io_with_chunked_set - evt_man = EventManagerForChunkedResponseTest.new - t = Thread.new do - begin - config = { - :ServerName => "localhost" - } - TestWEBrick.start_httpserver(config) do |server, addr, port, log| - body_strs = [ 'aaaaaa', 'bb', 'cccc' ] - server.mount_proc( "/", ->( req, res ){ - # Test for setting chunked... - res.chunked = true - r,w = IO.pipe - evt_man.add_listener do |type,str| - type == :cls ? ( w.close ) : ( w << str ) - end - res.body = r - } ) - Thread.pass while server.status != :Running - http = Net::HTTP.new(addr, port) - req = Net::HTTP::Get.new("/") - http.request(req) do |res| - i = 0 - evt_man.raise_str_event( body_strs[i] ) - res.read_body do |s| - assert_equal( body_strs[i], s ) - i += 1 - if i < body_strs.length - evt_man.raise_str_event( body_strs[i] ) - else - evt_man.raise_close_event() - end - end - assert_equal( body_strs.length, i ) - end - end - rescue => err - flunk( 'exception raised in thread: ' + err.to_s ) - end - end - if t.join( 3 ).nil? - evt_man.raise_close_event() - flunk( 'timeout' ) - if t.join( 1 ).nil? - Thread.kill t - end - end - end - - def test_response_io_without_chunked_set - config = { - :ServerName => "localhost" - } - log_tester = lambda {|log, access_log| - assert_equal(1, log.length) - assert_match(/WARN Could not determine content-length of response body./, log[0]) - } - TestWEBrick.start_httpserver(config, log_tester){|server, addr, port, log| - server.mount_proc("/", lambda { |req, res| - r,w = IO.pipe - # Test for not setting chunked... - # res.chunked = true - res.body = r - w << "foo" - w.close - }) - Thread.pass while server.status != :Running - http = Net::HTTP.new(addr, port) - req = Net::HTTP::Get.new("/") - req['Connection'] = 'Keep-Alive' - begin - Timeout.timeout(2) do - http.request(req){|res| assert_equal("foo", res.body) } - end - rescue Timeout::Error - flunk('corrupted response') - end - } - end - - def test_request_handler_callback_is_deprecated - requested = 0 - config = { - :ServerName => "localhost", - :RequestHandler => Proc.new{|req, res| requested += 1 }, - } - log_tester = lambda {|log, access_log| - assert_equal(2, log.length) - assert_match(/WARN :RequestHandler is deprecated, please use :RequestCallback/, log[0]) - assert_match(%r{ERROR `/' not found\.}, log[1]) - } - TestWEBrick.start_httpserver(config, log_tester){|server, addr, port, log| - Thread.pass while server.status != :Running - - http = Net::HTTP.new(addr, port) - req = Net::HTTP::Get.new("/") - req["Host"] = "localhost:#{port}" - http.request(req){|res| assert_equal("404", res.code, log.call)} - assert_match(%r{:RequestHandler is deprecated, please use :RequestCallback$}, log.call, log.call) - } - assert_equal(1, requested) - end - - def test_shutdown_with_busy_keepalive_connection - requested = 0 - config = { - :ServerName => "localhost", - } - TestWEBrick.start_httpserver(config){|server, addr, port, log| - server.mount_proc("/", lambda {|req, res| res.body = "heffalump" }) - Thread.pass while server.status != :Running - - Net::HTTP.start(addr, port) do |http| - req = Net::HTTP::Get.new("/") - http.request(req){|res| assert_equal('Keep-Alive', res['Connection'], log.call) } - server.shutdown - begin - 10.times {|n| http.request(req); requested += 1 } - rescue - # Errno::ECONNREFUSED or similar - end - end - } - assert_equal(0, requested, "Server responded to #{requested} requests after shutdown") - end - - def test_cntrl_in_path - log_ary = [] - access_log_ary = [] - config = { - :Port => 0, - :BindAddress => '127.0.0.1', - :Logger => WEBrick::Log.new(log_ary, WEBrick::BasicLog::WARN), - :AccessLog => [[access_log_ary, '']], - } - s = WEBrick::HTTPServer.new(config) - s.mount('/foo', WEBrick::HTTPServlet::FileHandler, __FILE__) - th = Thread.new { s.start } - addr = s.listeners[0].addr - - http = Net::HTTP.new(addr[3], addr[1]) - req = Net::HTTP::Get.new('/notexist%0a/foo') - http.request(req) { |res| assert_equal('404', res.code) } - exp = %Q(ERROR `/notexist\\n/foo' not found.\n) - assert_equal 1, log_ary.size - assert_include log_ary[0], exp - ensure - s&.shutdown - th&.join - end - - def test_gigantic_request_header - log_tester = lambda {|log, access_log| - assert_equal 1, log.size - assert_include log[0], 'ERROR headers too large' - } - TestWEBrick.start_httpserver({}, log_tester){|server, addr, port, log| - server.mount('/', WEBrick::HTTPServlet::FileHandler, __FILE__) - TCPSocket.open(addr, port) do |c| - c.write("GET / HTTP/1.0\r\n") - junk = -"X-Junk: #{' ' * 1024}\r\n" - assert_raise(Errno::ECONNRESET, Errno::EPIPE, Errno::EPROTOTYPE) do - loop { c.write(junk) } - end - end - } - end - - def test_eof_in_chunk - log_tester = lambda do |log, access_log| - assert_equal 1, log.size - assert_include log[0], 'ERROR bad chunk data size' - end - TestWEBrick.start_httpserver({}, log_tester){|server, addr, port, log| - server.mount_proc('/', ->(req, res) { res.body = req.body }) - TCPSocket.open(addr, port) do |c| - c.write("POST / HTTP/1.1\r\nHost: example.com\r\n" \ - "Transfer-Encoding: chunked\r\n\r\n5\r\na") - c.shutdown(Socket::SHUT_WR) # trigger EOF in server - res = c.read - assert_match %r{\AHTTP/1\.1 400 }, res - end - } - end - - def test_big_chunks - nr_out = 3 - buf = 'big' # 3 bytes is bigger than 2! - config = { :InputBufferSize => 2 }.freeze - total = 0 - all = '' - TestWEBrick.start_httpserver(config){|server, addr, port, log| - server.mount_proc('/', ->(req, res) { - err = [] - ret = req.body do |chunk| - n = chunk.bytesize - n > config[:InputBufferSize] and err << "#{n} > :InputBufferSize" - total += n - all << chunk - end - ret.nil? or err << 'req.body should return nil' - (buf * nr_out) == all or err << 'input body does not match expected' - res.header['connection'] = 'close' - res.body = err.join("\n") - }) - TCPSocket.open(addr, port) do |c| - c.write("POST / HTTP/1.1\r\nHost: example.com\r\n" \ - "Transfer-Encoding: chunked\r\n\r\n") - chunk = "#{buf.bytesize.to_s(16)}\r\n#{buf}\r\n" - nr_out.times { c.write(chunk) } - c.write("0\r\n\r\n") - head, body = c.read.split("\r\n\r\n") - assert_match %r{\AHTTP/1\.1 200 OK}, head - assert_nil body - end - } - end -end diff --git a/tool/test/webrick/test_httpstatus.rb b/tool/test/webrick/test_httpstatus.rb deleted file mode 100644 index fd0570d5c663a7..00000000000000 --- a/tool/test/webrick/test_httpstatus.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: false -require "test/unit" -require "webrick" - -class TestWEBrickHTTPStatus < Test::Unit::TestCase - def test_info? - assert WEBrick::HTTPStatus.info?(100) - refute WEBrick::HTTPStatus.info?(200) - end - - def test_success? - assert WEBrick::HTTPStatus.success?(200) - refute WEBrick::HTTPStatus.success?(300) - end - - def test_redirect? - assert WEBrick::HTTPStatus.redirect?(300) - refute WEBrick::HTTPStatus.redirect?(400) - end - - def test_error? - assert WEBrick::HTTPStatus.error?(400) - refute WEBrick::HTTPStatus.error?(600) - end - - def test_client_error? - assert WEBrick::HTTPStatus.client_error?(400) - refute WEBrick::HTTPStatus.client_error?(500) - end - - def test_server_error? - assert WEBrick::HTTPStatus.server_error?(500) - refute WEBrick::HTTPStatus.server_error?(600) - end -end diff --git a/tool/test/webrick/test_httputils.rb b/tool/test/webrick/test_httputils.rb deleted file mode 100644 index 00f297bd09e626..00000000000000 --- a/tool/test/webrick/test_httputils.rb +++ /dev/null @@ -1,101 +0,0 @@ -# frozen_string_literal: false -require "test/unit" -require "webrick/httputils" - -class TestWEBrickHTTPUtils < Test::Unit::TestCase - include WEBrick::HTTPUtils - - def test_normilize_path - assert_equal("/foo", normalize_path("/foo")) - assert_equal("/foo/bar/", normalize_path("/foo/bar/")) - - assert_equal("/", normalize_path("/foo/../")) - assert_equal("/", normalize_path("/foo/..")) - assert_equal("/", normalize_path("/foo/bar/../../")) - assert_equal("/", normalize_path("/foo/bar/../..")) - assert_equal("/", normalize_path("/foo/bar/../..")) - assert_equal("/baz", normalize_path("/foo/bar/../../baz")) - assert_equal("/baz", normalize_path("/foo/../bar/../baz")) - assert_equal("/baz/", normalize_path("/foo/../bar/../baz/")) - assert_equal("/...", normalize_path("/bar/../...")) - assert_equal("/.../", normalize_path("/bar/../.../")) - - assert_equal("/foo/", normalize_path("/foo/./")) - assert_equal("/foo/", normalize_path("/foo/.")) - assert_equal("/foo/", normalize_path("/foo/././")) - assert_equal("/foo/", normalize_path("/foo/./.")) - assert_equal("/foo/bar", normalize_path("/foo/./bar")) - assert_equal("/foo/bar/", normalize_path("/foo/./bar/.")) - assert_equal("/foo/bar/", normalize_path("/./././foo/./bar/.")) - - assert_equal("/foo/bar/", normalize_path("//foo///.//bar/.///.//")) - assert_equal("/", normalize_path("//foo///..///bar/.///..//.//")) - - assert_raise(RuntimeError){ normalize_path("foo/bar") } - assert_raise(RuntimeError){ normalize_path("..") } - assert_raise(RuntimeError){ normalize_path("/..") } - assert_raise(RuntimeError){ normalize_path("/./..") } - assert_raise(RuntimeError){ normalize_path("/./../") } - assert_raise(RuntimeError){ normalize_path("/./../..") } - assert_raise(RuntimeError){ normalize_path("/./../../") } - assert_raise(RuntimeError){ normalize_path("/./../") } - assert_raise(RuntimeError){ normalize_path("/../..") } - assert_raise(RuntimeError){ normalize_path("/../../") } - assert_raise(RuntimeError){ normalize_path("/../../..") } - assert_raise(RuntimeError){ normalize_path("/../../../") } - assert_raise(RuntimeError){ normalize_path("/../foo/../") } - assert_raise(RuntimeError){ normalize_path("/../foo/../../") } - assert_raise(RuntimeError){ normalize_path("/foo/bar/../../../../") } - assert_raise(RuntimeError){ normalize_path("/foo/../bar/../../") } - assert_raise(RuntimeError){ normalize_path("/./../bar/") } - assert_raise(RuntimeError){ normalize_path("/./../") } - end - - def test_split_header_value - assert_equal(['foo', 'bar'], split_header_value('foo, bar')) - assert_equal(['"foo"', 'bar'], split_header_value('"foo", bar')) - assert_equal(['foo', '"bar"'], split_header_value('foo, "bar"')) - assert_equal(['*'], split_header_value('*')) - assert_equal(['W/"xyzzy"', 'W/"r2d2xxxx"', 'W/"c3piozzzz"'], - split_header_value('W/"xyzzy", W/"r2d2xxxx", W/"c3piozzzz"')) - end - - def test_escape - assert_equal("/foo/bar", escape("/foo/bar")) - assert_equal("/~foo/bar", escape("/~foo/bar")) - assert_equal("/~foo%20bar", escape("/~foo bar")) - assert_equal("/~foo%20bar", escape("/~foo bar")) - assert_equal("/~foo%09bar", escape("/~foo\tbar")) - assert_equal("/~foo+bar", escape("/~foo+bar")) - bug8425 = '[Bug #8425] [ruby-core:55052]' - assert_nothing_raised(ArgumentError, Encoding::CompatibilityError, bug8425) { - assert_equal("%E3%83%AB%E3%83%93%E3%83%BC%E3%81%95%E3%82%93", escape("\u{30EB 30D3 30FC 3055 3093}")) - } - end - - def test_escape_form - assert_equal("%2Ffoo%2Fbar", escape_form("/foo/bar")) - assert_equal("%2F~foo%2Fbar", escape_form("/~foo/bar")) - assert_equal("%2F~foo+bar", escape_form("/~foo bar")) - assert_equal("%2F~foo+%2B+bar", escape_form("/~foo + bar")) - end - - def test_unescape - assert_equal("/foo/bar", unescape("%2ffoo%2fbar")) - assert_equal("/~foo/bar", unescape("/%7efoo/bar")) - assert_equal("/~foo/bar", unescape("%2f%7efoo%2fbar")) - assert_equal("/~foo+bar", unescape("/%7efoo+bar")) - end - - def test_unescape_form - assert_equal("//foo/bar", unescape_form("/%2Ffoo/bar")) - assert_equal("//foo/bar baz", unescape_form("/%2Ffoo/bar+baz")) - assert_equal("/~foo/bar baz", unescape_form("/%7Efoo/bar+baz")) - end - - def test_escape_path - assert_equal("/foo/bar", escape_path("/foo/bar")) - assert_equal("/foo/bar/", escape_path("/foo/bar/")) - assert_equal("/%25foo/bar/", escape_path("/%foo/bar/")) - end -end diff --git a/tool/test/webrick/test_httpversion.rb b/tool/test/webrick/test_httpversion.rb deleted file mode 100644 index e50ee179710a71..00000000000000 --- a/tool/test/webrick/test_httpversion.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: false -require "test/unit" -require "webrick/httpversion" - -class TestWEBrickHTTPVersion < Test::Unit::TestCase - def setup - @v09 = WEBrick::HTTPVersion.new("0.9") - @v10 = WEBrick::HTTPVersion.new("1.0") - @v11 = WEBrick::HTTPVersion.new("1.001") - end - - def test_to_s() - assert_equal("0.9", @v09.to_s) - assert_equal("1.0", @v10.to_s) - assert_equal("1.1", @v11.to_s) - end - - def test_major() - assert_equal(0, @v09.major) - assert_equal(1, @v10.major) - assert_equal(1, @v11.major) - end - - def test_minor() - assert_equal(9, @v09.minor) - assert_equal(0, @v10.minor) - assert_equal(1, @v11.minor) - end - - def test_compar() - assert_equal(0, @v09 <=> "0.9") - assert_equal(0, @v09 <=> "0.09") - - assert_equal(-1, @v09 <=> @v10) - assert_equal(-1, @v09 <=> "1.00") - - assert_equal(1, @v11 <=> @v09) - assert_equal(1, @v11 <=> "1.0") - assert_equal(1, @v11 <=> "0.9") - end -end diff --git a/tool/test/webrick/test_server.rb b/tool/test/webrick/test_server.rb deleted file mode 100644 index 3bd8115c61ce4f..00000000000000 --- a/tool/test/webrick/test_server.rb +++ /dev/null @@ -1,191 +0,0 @@ -# frozen_string_literal: false -require "test/unit" -require "tempfile" -require "webrick" -require_relative "utils" - -class TestWEBrickServer < Test::Unit::TestCase - class Echo < WEBrick::GenericServer - def run(sock) - while line = sock.gets - sock << line - end - end - end - - def test_server - TestWEBrick.start_server(Echo){|server, addr, port, log| - TCPSocket.open(addr, port){|sock| - sock.puts("foo"); assert_equal("foo\n", sock.gets, log.call) - sock.puts("bar"); assert_equal("bar\n", sock.gets, log.call) - sock.puts("baz"); assert_equal("baz\n", sock.gets, log.call) - sock.puts("qux"); assert_equal("qux\n", sock.gets, log.call) - } - } - end - - def test_start_exception - stopped = 0 - - log = [] - logger = WEBrick::Log.new(log, WEBrick::BasicLog::WARN) - - assert_raise(SignalException) do - listener = Object.new - def listener.to_io # IO.select invokes #to_io. - raise SignalException, 'SIGTERM' # simulate signal in main thread - end - def listener.shutdown - end - def listener.close - end - - server = WEBrick::HTTPServer.new({ - :BindAddress => "127.0.0.1", :Port => 0, - :StopCallback => Proc.new{ stopped += 1 }, - :Logger => logger, - }) - server.listeners[0].close - server.listeners[0] = listener - - server.start - end - - assert_equal(1, stopped) - assert_equal(1, log.length) - assert_match(/FATAL SignalException: SIGTERM/, log[0]) - end - - def test_callbacks - accepted = started = stopped = 0 - config = { - :AcceptCallback => Proc.new{ accepted += 1 }, - :StartCallback => Proc.new{ started += 1 }, - :StopCallback => Proc.new{ stopped += 1 }, - } - TestWEBrick.start_server(Echo, config){|server, addr, port, log| - true while server.status != :Running - sleep 1 if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? # server.status behaves unexpectedly with --jit-wait - assert_equal(1, started, log.call) - assert_equal(0, stopped, log.call) - assert_equal(0, accepted, log.call) - TCPSocket.open(addr, port){|sock| (sock << "foo\n").gets } - TCPSocket.open(addr, port){|sock| (sock << "foo\n").gets } - TCPSocket.open(addr, port){|sock| (sock << "foo\n").gets } - assert_equal(3, accepted, log.call) - } - assert_equal(1, started) - assert_equal(1, stopped) - end - - def test_daemon - begin - r, w = IO.pipe - pid1 = Process.fork{ - r.close - WEBrick::Daemon.start - w.puts(Process.pid) - sleep 10 - } - pid2 = r.gets.to_i - assert(Process.kill(:KILL, pid2)) - assert_not_equal(pid1, pid2) - rescue NotImplementedError - # snip this test - ensure - Process.wait(pid1) if pid1 - r.close - w.close - end - end - - def test_restart_after_shutdown - address = '127.0.0.1' - port = 0 - log = [] - config = { - :BindAddress => address, - :Port => port, - :Logger => WEBrick::Log.new(log, WEBrick::BasicLog::WARN), - } - server = Echo.new(config) - client_proc = lambda {|str| - begin - ret = server.listeners.first.connect_address.connect {|s| - s.write(str) - s.close_write - s.read - } - assert_equal(str, ret) - ensure - server.shutdown - end - } - server_thread = Thread.new { server.start } - client_thread = Thread.new { client_proc.call("a") } - assert_join_threads([client_thread, server_thread]) - server.listen(address, port) - server_thread = Thread.new { server.start } - client_thread = Thread.new { client_proc.call("b") } - assert_join_threads([client_thread, server_thread]) - assert_equal([], log) - end - - def test_restart_after_stop - log = Object.new - class << log - include Test::Unit::Assertions - def <<(msg) - flunk "unexpected log: #{msg.inspect}" - end - end - client_thread = nil - wakeup = -> {client_thread.wakeup} - warn_flunk = WEBrick::Log.new(log, WEBrick::BasicLog::WARN) - server = WEBrick::HTTPServer.new( - :StartCallback => wakeup, - :StopCallback => wakeup, - :BindAddress => '0.0.0.0', - :Port => 0, - :Logger => warn_flunk) - 2.times { - server_thread = Thread.start { - server.start - } - client_thread = Thread.start { - sleep 0.1 until server.status == :Running || !server_thread.status - server.stop - sleep 0.1 until server.status == :Stop || !server_thread.status - } - assert_join_threads([client_thread, server_thread]) - } - end - - def test_port_numbers - config = { - :BindAddress => '0.0.0.0', - :Logger => WEBrick::Log.new([], WEBrick::BasicLog::WARN), - } - - ports = [0, "0"] - - ports.each do |port| - config[:Port]= port - server = WEBrick::GenericServer.new(config) - server_thread = Thread.start { server.start } - client_thread = Thread.start { - sleep 0.1 until server.status == :Running || !server_thread.status - server_port = server.listeners[0].addr[1] - server.stop - assert_equal server.config[:Port], server_port - sleep 0.1 until server.status == :Stop || !server_thread.status - } - assert_join_threads([client_thread, server_thread]) - end - - assert_raise(ArgumentError) do - config[:Port]= "FOO" - WEBrick::GenericServer.new(config) - end - end -end diff --git a/tool/test/webrick/test_ssl_server.rb b/tool/test/webrick/test_ssl_server.rb deleted file mode 100644 index 4e52598bf59cc2..00000000000000 --- a/tool/test/webrick/test_ssl_server.rb +++ /dev/null @@ -1,67 +0,0 @@ -require "test/unit" -require "webrick" -require "webrick/ssl" -require_relative "utils" -require 'timeout' - -class TestWEBrickSSLServer < Test::Unit::TestCase - class Echo < WEBrick::GenericServer - def run(sock) - while line = sock.gets - sock << line - end - end - end - - def test_self_signed_cert_server - assert_self_signed_cert( - :SSLEnable => true, - :SSLCertName => [["C", "JP"], ["O", "www.ruby-lang.org"], ["CN", "Ruby"]], - ) - end - - def test_self_signed_cert_server_with_string - assert_self_signed_cert( - :SSLEnable => true, - :SSLCertName => "/C=JP/O=www.ruby-lang.org/CN=Ruby", - ) - end - - def assert_self_signed_cert(config) - TestWEBrick.start_server(Echo, config){|server, addr, port, log| - io = TCPSocket.new(addr, port) - sock = OpenSSL::SSL::SSLSocket.new(io) - sock.connect - sock.puts(server.ssl_context.cert.subject.to_s) - assert_equal("/C=JP/O=www.ruby-lang.org/CN=Ruby\n", sock.gets, log.call) - sock.close - io.close - } - end - - def test_slow_connect - poke = lambda do |io, msg| - begin - sock = OpenSSL::SSL::SSLSocket.new(io) - sock.connect - sock.puts(msg) - assert_equal "#{msg}\n", sock.gets, msg - ensure - sock&.close - io.close - end - end - config = { - :SSLEnable => true, - :SSLCertName => "/C=JP/O=www.ruby-lang.org/CN=Ruby", - } - EnvUtil.timeout(10) do - TestWEBrick.start_server(Echo, config) do |server, addr, port, log| - outer = TCPSocket.new(addr, port) - inner = TCPSocket.new(addr, port) - poke.call(inner, 'fast TLS negotiation') - poke.call(outer, 'slow TLS negotiation') - end - end - end -end diff --git a/tool/test/webrick/test_utils.rb b/tool/test/webrick/test_utils.rb deleted file mode 100644 index c2b7a36e8aa98f..00000000000000 --- a/tool/test/webrick/test_utils.rb +++ /dev/null @@ -1,110 +0,0 @@ -# frozen_string_literal: false -require "test/unit" -require "webrick/utils" - -class TestWEBrickUtils < Test::Unit::TestCase - def teardown - WEBrick::Utils::TimeoutHandler.terminate - super - end - - def assert_expired(m) - Thread.handle_interrupt(Timeout::Error => :never, EX => :never) do - assert_empty(m::TimeoutHandler.instance.instance_variable_get(:@timeout_info)) - end - end - - def assert_not_expired(m) - Thread.handle_interrupt(Timeout::Error => :never, EX => :never) do - assert_not_empty(m::TimeoutHandler.instance.instance_variable_get(:@timeout_info)) - end - end - - EX = Class.new(StandardError) - - def test_no_timeout - m = WEBrick::Utils - assert_equal(:foo, m.timeout(10){ :foo }) - assert_expired(m) - end - - def test_nested_timeout_outer - m = WEBrick::Utils - i = 0 - assert_raise(Timeout::Error){ - m.timeout(1){ - assert_raise(Timeout::Error){ m.timeout(0.1){ i += 1; sleep(1) } } - assert_not_expired(m) - i += 1 - sleep(2) - } - } - assert_equal(2, i) - assert_expired(m) - end - - def test_timeout_default_exception - m = WEBrick::Utils - assert_raise(Timeout::Error){ m.timeout(0.01){ sleep } } - assert_expired(m) - end - - def test_timeout_custom_exception - m = WEBrick::Utils - ex = EX - assert_raise(ex){ m.timeout(0.01, ex){ sleep } } - assert_expired(m) - end - - def test_nested_timeout_inner_custom_exception - m = WEBrick::Utils - ex = EX - i = 0 - assert_raise(ex){ - m.timeout(10){ - m.timeout(0.01, ex){ i += 1; sleep } - } - sleep - } - assert_equal(1, i) - assert_expired(m) - end - - def test_nested_timeout_outer_custom_exception - m = WEBrick::Utils - ex = EX - i = 0 - assert_raise(Timeout::Error){ - m.timeout(0.01){ - m.timeout(1.0, ex){ i += 1; sleep } - } - sleep - } - assert_equal(1, i) - assert_expired(m) - end - - def test_create_listeners - addr = listener_address(0) - port = addr.slice!(1) - assert_kind_of(Integer, port, "dynamically chosen port number") - assert_equal(["AF_INET", "127.0.0.1", "127.0.0.1"], addr) - - assert_equal(["AF_INET", port, "127.0.0.1", "127.0.0.1"], - listener_address(port), - "specific port number") - - assert_equal(["AF_INET", port, "127.0.0.1", "127.0.0.1"], - listener_address(port.to_s), - "specific port number string") - end - - def listener_address(port) - listeners = WEBrick::Utils.create_listeners("127.0.0.1", port) - srv = listeners.first - assert_kind_of TCPServer, srv - srv.addr - ensure - listeners.each(&:close) if listeners - end -end diff --git a/tool/test/webrick/utils.rb b/tool/test/webrick/utils.rb deleted file mode 100644 index c8e84c37f1863e..00000000000000 --- a/tool/test/webrick/utils.rb +++ /dev/null @@ -1,104 +0,0 @@ -# frozen_string_literal: false -require "webrick" -begin - require "webrick/https" -rescue LoadError -end -require "webrick/httpproxy" - -module TestWEBrick - NullWriter = Object.new - def NullWriter.<<(msg) - puts msg if $DEBUG - return self - end - - class WEBrick::HTTPServlet::CGIHandler - remove_const :Ruby - require "envutil" unless defined?(EnvUtil) - Ruby = EnvUtil.rubybin - remove_const :CGIRunner - CGIRunner = "\"#{Ruby}\" \"#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb\"" # :nodoc: - remove_const :CGIRunnerArray - CGIRunnerArray = [Ruby, "#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb"] # :nodoc: - end - - RubyBin = "\"#{EnvUtil.rubybin}\"" - RubyBin << " --disable-gems" - RubyBin << " \"-I#{File.expand_path("../..", File.dirname(__FILE__))}/lib\"" - RubyBin << " \"-I#{File.dirname(EnvUtil.rubybin)}/.ext/common\"" - RubyBin << " \"-I#{File.dirname(EnvUtil.rubybin)}/.ext/#{RUBY_PLATFORM}\"" - - RubyBinArray = [EnvUtil.rubybin] - RubyBinArray << "--disable-gems" - RubyBinArray << "-I" << "#{File.expand_path("../..", File.dirname(__FILE__))}/lib" - RubyBinArray << "-I" << "#{File.dirname(EnvUtil.rubybin)}/.ext/common" - RubyBinArray << "-I" << "#{File.dirname(EnvUtil.rubybin)}/.ext/#{RUBY_PLATFORM}" - - require "test/unit" unless defined?(Test::Unit) - include Test::Unit::Assertions - extend Test::Unit::Assertions - include Test::Unit::CoreAssertions - extend Test::Unit::CoreAssertions - - module_function - - DefaultLogTester = lambda {|log, access_log| assert_equal([], log) } - - def start_server(klass, config={}, log_tester=DefaultLogTester, &block) - log_ary = [] - access_log_ary = [] - log = proc { "webrick log start:\n" + (log_ary+access_log_ary).join.gsub(/^/, " ").chomp + "\nwebrick log end" } - config = ({ - :BindAddress => "127.0.0.1", :Port => 0, - :ServerType => Thread, - :Logger => WEBrick::Log.new(log_ary, WEBrick::BasicLog::WARN), - :AccessLog => [[access_log_ary, ""]] - }.update(config)) - server = capture_output {break klass.new(config)} - server_thread = server.start - server_thread2 = Thread.new { - server_thread.join - if log_tester - log_tester.call(log_ary, access_log_ary) - end - } - addr = server.listeners[0].addr - client_thread = Thread.new { - begin - block.yield([server, addr[3], addr[1], log]) - ensure - server.shutdown - end - } - assert_join_threads([client_thread, server_thread2]) - end - - def start_httpserver(config={}, log_tester=DefaultLogTester, &block) - start_server(WEBrick::HTTPServer, config, log_tester, &block) - end - - def start_httpproxy(config={}, log_tester=DefaultLogTester, &block) - start_server(WEBrick::HTTPProxyServer, config, log_tester, &block) - end - - def start_cgi_server(config={}, log_tester=TestWEBrick::DefaultLogTester, &block) - config = { - :CGIInterpreter => TestWEBrick::RubyBin, - :DocumentRoot => File.dirname(__FILE__), - :DirectoryIndex => ["webrick.cgi"], - :RequestCallback => Proc.new{|req, res| - def req.meta_vars - meta = super - meta["RUBYLIB"] = $:.join(File::PATH_SEPARATOR) - meta[RbConfig::CONFIG['LIBPATHENV']] = ENV[RbConfig::CONFIG['LIBPATHENV']] if RbConfig::CONFIG['LIBPATHENV'] - return meta - end - }, - }.merge(config) - if RUBY_PLATFORM =~ /mswin|mingw|cygwin|bccwin32/ - config[:CGIPathEnv] = ENV['PATH'] # runtime dll may not be in system dir. - end - start_server(WEBrick::HTTPServer, config, log_tester, &block) - end -end diff --git a/tool/test/webrick/webrick.cgi b/tool/test/webrick/webrick.cgi deleted file mode 100755 index 45594b7a7bb1d6..00000000000000 --- a/tool/test/webrick/webrick.cgi +++ /dev/null @@ -1,38 +0,0 @@ -#!ruby -require "webrick/cgi" - -class TestApp < WEBrick::CGI - def do_GET(req, res) - res["content-type"] = "text/plain" - if req.path_info == "/dumpenv" - res.body = Marshal.dump(ENV.to_hash) - elsif (p = req.path_info) && p.length > 0 - res.body = p - elsif (q = req.query).size > 0 - res.body = q.keys.sort.collect{|key| - q[key].list.sort.collect{|v| - "#{key}=#{v}" - }.join(", ") - }.join(", ") - elsif %r{/$} =~ req.request_uri.to_s - res.body = +"" - res.body << req.request_uri.to_s << "\n" - res.body << req.script_name - elsif !req.cookies.empty? - res.body = req.cookies.inject(+""){|result, cookie| - result << "%s=%s\n" % [cookie.name, cookie.value] - } - res.cookies << WEBrick::Cookie.new("Customer", "WILE_E_COYOTE") - res.cookies << WEBrick::Cookie.new("Shipping", "FedEx") - else - res.body = req.script_name - end - end - - def do_POST(req, res) - do_GET(req, res) - end -end - -cgi = TestApp.new -cgi.start diff --git a/tool/test/webrick/webrick.rhtml b/tool/test/webrick/webrick.rhtml deleted file mode 100644 index a7bbe43fb5e6b7..00000000000000 --- a/tool/test/webrick/webrick.rhtml +++ /dev/null @@ -1,4 +0,0 @@ -req to <%= -servlet_request.request_uri -%> <%= -servlet_request.query.inspect %> diff --git a/tool/test/webrick/webrick_long_filename.cgi b/tool/test/webrick/webrick_long_filename.cgi deleted file mode 100644 index 43c1af825c336a..00000000000000 --- a/tool/test/webrick/webrick_long_filename.cgi +++ /dev/null @@ -1,36 +0,0 @@ -#!ruby -require "webrick/cgi" - -class TestApp < WEBrick::CGI - def do_GET(req, res) - res["content-type"] = "text/plain" - if (p = req.path_info) && p.length > 0 - res.body = p - elsif (q = req.query).size > 0 - res.body = q.keys.sort.collect{|key| - q[key].list.sort.collect{|v| - "#{key}=#{v}" - }.join(", ") - }.join(", ") - elsif %r{/$} =~ req.request_uri.to_s - res.body = "" - res.body << req.request_uri.to_s << "\n" - res.body << req.script_name - elsif !req.cookies.empty? - res.body = req.cookies.inject(""){|result, cookie| - result << "%s=%s\n" % [cookie.name, cookie.value] - } - res.cookies << WEBrick::Cookie.new("Customer", "WILE_E_COYOTE") - res.cookies << WEBrick::Cookie.new("Shipping", "FedEx") - else - res.body = req.script_name - end - end - - def do_POST(req, res) - do_GET(req, res) - end -end - -cgi = TestApp.new -cgi.start diff --git a/util.c b/util.c index 8a188149378cb6..2e887618b135a8 100644 --- a/util.c +++ b/util.c @@ -564,7 +564,7 @@ ruby_getcwd(void) rb_imemo_tmpbuf_set_ptr(guard, buf); buf = xrealloc(buf, size); } - rb_free_tmp_buffer(&guard); + rb_imemo_tmpbuf_set_ptr(guard, NULL); return buf; } diff --git a/variable.c b/variable.c index fefa3f8ac92a39..acb6da03c2e3bb 100644 --- a/variable.c +++ b/variable.c @@ -1865,12 +1865,6 @@ rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id) return true; } -/** - * Prevents further modifications to the given object. ::rb_eFrozenError shall - * be raised if modification is attempted. - * - * @param[out] x Object in question. - */ void rb_obj_freeze_inline(VALUE x) { if (RB_FL_ABLE(x)) { diff --git a/vm_args.c b/vm_args.c index b4a8fcb8fb34f2..90105f6900a353 100644 --- a/vm_args.c +++ b/vm_args.c @@ -535,6 +535,10 @@ ignore_keyword_hash_p(VALUE keyword_hash, const rb_iseq_t * const iseq, unsigned } } + if (RHASH_EMPTY_P(keyword_hash) && !ISEQ_BODY(iseq)->param.flags.has_kwrest) { + goto ignore; + } + if (!(*kw_flag & VM_CALL_KW_SPLAT_MUT) && (ISEQ_BODY(iseq)->param.flags.has_kwrest || ISEQ_BODY(iseq)->param.flags.ruby2_keywords)) { @@ -856,7 +860,18 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co args_setup_kw_parameters_from_kwsplat(ec, iseq, keyword_hash, klocals, remove_hash_value); } else { - VM_ASSERT(args_argc(args) == 0); +#if VM_CHECK_MODE > 0 + if (args_argc(args) != 0) { + VM_ASSERT(ci_flag & VM_CALL_ARGS_SPLAT); + VM_ASSERT(!(ci_flag & (VM_CALL_KWARG | VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT))); + VM_ASSERT(!kw_flag); + VM_ASSERT(!ISEQ_BODY(iseq)->param.flags.has_rest); + VM_ASSERT(RARRAY_LENINT(args->rest) > 0); + VM_ASSERT(RB_TYPE_P(rest_last, T_HASH)); + VM_ASSERT(FL_TEST_RAW(rest_last, RHASH_PASS_AS_KEYWORDS)); + VM_ASSERT(args_argc(args) == 1); + } +#endif args_setup_kw_parameters(ec, iseq, NULL, 0, NULL, klocals); } } diff --git a/vm_core.h b/vm_core.h index aa6edc3740dcc9..ca1265d4a62bf8 100644 --- a/vm_core.h +++ b/vm_core.h @@ -105,7 +105,6 @@ extern int ruby_assert_critical_section_entered; #include "vm_opts.h" #include "ruby/thread_native.h" - /* * implementation selector of get_insn_info algorithm * 0: linear search @@ -609,10 +608,9 @@ typedef struct rb_at_exit_list { struct rb_at_exit_list *next; } rb_at_exit_list; -struct rb_objspace; -struct rb_objspace *rb_objspace_alloc(void); -void rb_objspace_free(struct rb_objspace *); -void rb_objspace_call_finalizer(struct rb_objspace *); +void *rb_objspace_alloc(void); +void rb_objspace_free(void *objspace); +void rb_objspace_call_finalizer(struct rb_objspace *objspace); struct rb_global_space; struct rb_global_space * rb_global_space_init(void); diff --git a/vm_eval.c b/vm_eval.c index 312ff19142ccf6..2eac8d6aaf1bda 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -1560,6 +1560,37 @@ rb_block_call_kw(VALUE obj, ID mid, int argc, const VALUE * argv, return rb_iterate_internal(iterate_method, (VALUE)&arg, bl_proc, data2); } +/* + * A flexible variant of rb_block_call and rb_block_call_kw. + * This function accepts flags: + * + * RB_NO_KEYWORDS, RB_PASS_KEYWORDS, RB_PASS_CALLED_KEYWORDS: + * Works as the same as rb_block_call_kw. + * + * RB_BLOCK_NO_USE_PACKED_ARGS: + * The given block ("bl_proc") does not use "yielded_arg" of rb_block_call_func_t. + * Instead, the block accesses the yielded arguments via "argc" and "argv". + * This flag allows the called method to yield arguments without allocating an Array. + */ +VALUE +rb_block_call2(VALUE obj, ID mid, int argc, const VALUE *argv, + rb_block_call_func_t bl_proc, VALUE data2, long flags) +{ + struct iter_method_arg arg; + + arg.obj = obj; + arg.mid = mid; + arg.argc = argc; + arg.argv = argv; + arg.kw_splat = flags & 1; + + struct vm_ifunc *ifunc = rb_vm_ifunc_proc_new(bl_proc, (void *)data2); + if (flags & RB_BLOCK_NO_USE_PACKED_ARGS) + ifunc->flags |= IFUNC_YIELD_OPTIMIZABLE; + + return rb_iterate0(iterate_method, (VALUE)&arg, ifunc, GET_EC()); +} + VALUE rb_lambda_call(VALUE obj, ID mid, int argc, const VALUE *argv, rb_block_call_func_t bl_proc, int min_argc, int max_argc, diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 26612687090dec..fbbfe823958e7b 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -423,7 +423,7 @@ vm_push_frame(rb_execution_context_t *ec, This is a no-op in all cases we've looked at (https://godbolt.org/z/3oxd1446K), but should guarantee it for all future/untested compilers/platforms. */ - #ifdef HAVE_DECL_ATOMIC_SIGNAL_FENCE + #if defined HAVE_DECL_ATOMIC_SIGNAL_FENCE && HAVE_DECL_ATOMIC_SIGNAL_FENCE atomic_signal_fence(memory_order_seq_cst); #endif @@ -440,7 +440,6 @@ rb_vm_pop_frame_no_int(rb_execution_context_t *ec) { rb_control_frame_t *cfp = ec->cfp; - if (VM_CHECK_MODE >= 4) rb_gc_verify_internal_consistency(); if (VMDEBUG == 2) SDR(); ec->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); @@ -452,7 +451,6 @@ vm_pop_frame(rb_execution_context_t *ec, rb_control_frame_t *cfp, const VALUE *e { VALUE flags = ep[VM_ENV_DATA_INDEX_FLAGS]; - if (VM_CHECK_MODE >= 4) rb_gc_verify_internal_consistency(); if (VMDEBUG == 2) SDR(); RUBY_VM_CHECK_INTS(ec); @@ -1254,7 +1252,13 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call // and modules. So we can skip locking. // Second, other ractors need to check the shareability of the // values returned from the class ivars. - goto general_path; + + if (default_value == Qundef) { // defined? + return rb_ivar_defined(obj, id) ? Qtrue : Qundef; + } + else { + goto general_path; + } } ivar_list = RCLASS_IVPTR(obj); diff --git a/vm_sync.c b/vm_sync.c index 4b7de8c0edf3c7..46303c1b079e1b 100644 --- a/vm_sync.c +++ b/vm_sync.c @@ -41,6 +41,26 @@ rb_vm_locked_p(void) return vm_locked(GET_VM()); } +static bool +vm_need_barrier_waiting(const rb_vm_t *vm) +{ +#ifdef RUBY_THREAD_PTHREAD_H + return vm->ractor.sched.barrier_waiting; +#else + return vm->ractor.sync.barrier_waiting; +#endif +} + +static bool +vm_need_barrier(bool no_barrier, const rb_ractor_t *cr, const rb_vm_t *vm) +{ +#ifdef RUBY_THREAD_PTHREAD_H + return !no_barrier && cr->threads.sched.running != NULL && vm_need_barrier_waiting(vm); // ractor has running threads. +#else + return !no_barrier && vm_need_barrier_waiting(vm); +#endif +} + static void vm_lock_enter(rb_ractor_t *cr, rb_vm_t *vm, bool locked, bool no_barrier, unsigned int *lev APPEND_LOCATION_ARGS) { @@ -69,23 +89,17 @@ vm_lock_enter(rb_ractor_t *cr, rb_vm_t *vm, bool locked, bool no_barrier, unsign rb_borrowing_status_resume(cr); -#ifdef RUBY_THREAD_PTHREAD_H - if (!no_barrier && - cr->threads.sched.running != NULL // ractor has running threads. - ) { + // barrier + if (vm_need_barrier(no_barrier, cr, vm)) { + rb_execution_context_t *ec = GET_EC(); + RB_VM_SAVE_MACHINE_CONTEXT(rb_ec_thread_ptr(ec)); - while (vm->ractor.sched.barrier_waiting) { + do { + VM_ASSERT(vm_need_barrier_waiting(vm)); RUBY_DEBUG_LOG("barrier serial:%u", vm->ractor.sched.barrier_serial); rb_ractor_sched_barrier_join(vm, cr); - } + } while (vm_need_barrier_waiting(vm)); } -#else - if (!no_barrier) { - while (vm->ractor.sync.barrier_waiting) { - rb_ractor_sched_barrier_join(vm, cr); - } - } -#endif VM_ASSERT(vm->ractor.sync.lock_rec == 0); VM_ASSERT(vm->ractor.sync.lock_owner == NULL); diff --git a/win32/Makefile.sub b/win32/Makefile.sub index 46213466eb8985..46df464232949a 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -443,6 +443,8 @@ PREP = miniruby$(EXEEXT) BUILTIN_BINARY = yes !endif +BUILTIN_GC = default + !if !defined(EXTSTATIC) EXTSTATIC = !endif @@ -592,14 +594,19 @@ $(SCRIPTPROGRAMS): $(STUBPROGRAM) update-src:: @cd "$(srcdir:/=\)" && set LC_TIME=C && $(VCSUP) +!ifndef VCPKG_DEFAULT_TRIPLET +VCPKG_DEFAULT_TRIPLET = $(MACHINE)-windows +!endif +TRIPLET_OPTION = --triplet $(VCPKG_DEFAULT_TRIPLET) + update-vcpkg:: - @cd "$(srcdir:/=\)" && set LC_TIME=C && vcpkg x-update-baseline + @cd "$(srcdir:/=\)" && set LC_TIME=C && vcpkg x-update-baseline $(TRIPLET_OPTION) install-vcpkg:: - @cd "$(srcdir:/=\)" && set LC_TIME=C && vcpkg install + @cd "$(srcdir:/=\)" && set LC_TIME=C && vcpkg install $(TRIPLET_OPTION) prepare-vcpkg:: - for %%I in ($(srcdir:/=\)\vcpkg_installed\x64-windows\bin\*.dll) do ( \ + for %%I in ($(srcdir:/=\)\vcpkg_installed\$(VCPKG_DEFAULT_TRIPLET)\bin\*.dll) do @( \ if not %%~nI == readline mklink %%~nxI %%I \ ) @@ -1336,6 +1343,11 @@ $(ruby_pc): $(RBCONFIG) $(ECHO) compiling $(<:\=/) $(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$(<:\=/) +BUILTIN_GC_PATH = $(srcdir)/gc/$(BUILTIN_GC).c +gc_impl.obj: $(BUILTIN_GC_PATH) + $(ECHO) compiling $(BUILTIN_GC_PATH:\=/) + $(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$(BUILTIN_GC_PATH:\=/) + {$(srcdir)/missing}.c.asm: $(ECHO) translating $(<:\=/) $(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -Fa$@ -c $(CSRCFLAG)$(<:\=/) diff --git a/win32/setup.mak b/win32/setup.mak index 0a1421f0bbd76a..3660e5f0605802 100644 --- a/win32/setup.mak +++ b/win32/setup.mak @@ -130,7 +130,9 @@ int main(void) {FILE *volatile f = stdin; return 0;} -headers-: nul +!ifdef VS2022_FP_BUG_CHECK # Fixed In: Visual Studio 2022 version 17.3 -headers-: vs2022-fp-bug +!endif # Check the bug reported at: # https://developercommunity.visualstudio.com/t/With-__assume-isnan-after-isinf/1515649 diff --git a/yjit.c b/yjit.c index 56cc892e5b3acd..e63b42ea54f3e4 100644 --- a/yjit.c +++ b/yjit.c @@ -1065,7 +1065,7 @@ rb_IMEMO_TYPE_P(VALUE imemo, enum imemo_type imemo_type) void rb_assert_cme_handle(VALUE handle) { - RUBY_ASSERT_ALWAYS(rb_objspace_markable_object_p(handle)); + RUBY_ASSERT_ALWAYS(!rb_objspace_garbage_object_p(handle)); RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(handle, imemo_ment)); } diff --git a/yjit/src/asm/mod.rs b/yjit/src/asm/mod.rs index 524d6341f590ce..bd56074b96e7f7 100644 --- a/yjit/src/asm/mod.rs +++ b/yjit/src/asm/mod.rs @@ -2,16 +2,14 @@ use std::cell::RefCell; use std::fmt; use std::mem; use std::rc::Rc; +use std::collections::BTreeMap; + use crate::core::IseqPayload; use crate::core::for_each_off_stack_iseq_payload; use crate::core::for_each_on_stack_iseq_payload; use crate::invariants::rb_yjit_tracing_invalidate_all; use crate::stats::incr_counter; use crate::virtualmem::WriteError; - -#[cfg(feature = "disasm")] -use std::collections::BTreeMap; - use crate::codegen::CodegenGlobals; use crate::virtualmem::{VirtualMem, CodePtr}; @@ -77,8 +75,10 @@ pub struct CodeBlock { // References to labels label_refs: Vec, + // A switch for keeping comments. They take up memory. + keep_comments: bool, + // Comments for assembly instructions, if that feature is enabled - #[cfg(feature = "disasm")] asm_comments: BTreeMap>, // True for OutlinedCb @@ -107,7 +107,7 @@ impl CodeBlock { const PREFERRED_CODE_PAGE_SIZE: usize = 16 * 1024; /// Make a new CodeBlock - pub fn new(mem_block: Rc>, outlined: bool, freed_pages: Rc>>) -> Self { + pub fn new(mem_block: Rc>, outlined: bool, freed_pages: Rc>>, keep_comments: bool) -> Self { // Pick the code page size let system_page_size = mem_block.borrow().system_page_size(); let page_size = if 0 == Self::PREFERRED_CODE_PAGE_SIZE % system_page_size { @@ -128,7 +128,7 @@ impl CodeBlock { label_addrs: Vec::new(), label_names: Vec::new(), label_refs: Vec::new(), - #[cfg(feature = "disasm")] + keep_comments, asm_comments: BTreeMap::new(), outlined, dropped_bytes: false, @@ -366,9 +366,11 @@ impl CodeBlock { } /// Add an assembly comment if the feature is on. - /// If not, this becomes an inline no-op. - #[cfg(feature = "disasm")] pub fn add_comment(&mut self, comment: &str) { + if !self.keep_comments { + return; + } + let cur_ptr = self.get_write_ptr().raw_addr(self); // If there's no current list of comments for this line number, add one. @@ -379,28 +381,21 @@ impl CodeBlock { this_line_comments.push(comment.to_string()); } } - #[cfg(not(feature = "disasm"))] - #[inline] - pub fn add_comment(&mut self, _: &str) {} - #[cfg(feature = "disasm")] pub fn comments_at(&self, pos: usize) -> Option<&Vec> { self.asm_comments.get(&pos) } - #[allow(unused_variables)] - #[cfg(feature = "disasm")] pub fn remove_comments(&mut self, start_addr: CodePtr, end_addr: CodePtr) { + if self.asm_comments.is_empty() { + return; + } for addr in start_addr.raw_addr(self)..end_addr.raw_addr(self) { self.asm_comments.remove(&addr); } } - #[cfg(not(feature = "disasm"))] - #[inline] - pub fn remove_comments(&mut self, _: CodePtr, _: CodePtr) {} pub fn clear_comments(&mut self) { - #[cfg(feature = "disasm")] self.asm_comments.clear(); } @@ -693,7 +688,7 @@ impl CodeBlock { let mem_start: *const u8 = alloc.mem_start(); let virt_mem = VirtualMem::new(alloc, 1, NonNull::new(mem_start as *mut u8).unwrap(), mem_size); - Self::new(Rc::new(RefCell::new(virt_mem)), false, Rc::new(None)) + Self::new(Rc::new(RefCell::new(virt_mem)), false, Rc::new(None), true) } /// Stubbed CodeBlock for testing conditions that can arise due to code GC. Can't execute generated code. @@ -711,7 +706,7 @@ impl CodeBlock { let mem_start: *const u8 = alloc.mem_start(); let virt_mem = VirtualMem::new(alloc, 1, NonNull::new(mem_start as *mut u8).unwrap(), mem_size); - Self::new(Rc::new(RefCell::new(virt_mem)), false, Rc::new(Some(freed_pages))) + Self::new(Rc::new(RefCell::new(virt_mem)), false, Rc::new(Some(freed_pages)), true) } } diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs index 3bf949ba7d24fc..b695f8da96a982 100644 --- a/yjit/src/backend/arm64/mod.rs +++ b/yjit/src/backend/arm64/mod.rs @@ -381,7 +381,7 @@ impl Assembler } let live_ranges: Vec = take(&mut self.live_ranges); - let mut asm_local = Assembler::new_with_label_names(take(&mut self.label_names), take(&mut self.side_exits)); + let mut asm_local = Assembler::new_with_label_names(take(&mut self.label_names), take(&mut self.side_exits), self.num_locals); let asm = &mut asm_local; let mut iterator = self.into_draining_iter(); @@ -924,9 +924,7 @@ impl Assembler match insn { Insn::Comment(text) => { - if cfg!(feature = "disasm") { - cb.add_comment(text); - } + cb.add_comment(text); }, Insn::Label(target) => { cb.write_label(target.unwrap_label_idx()); @@ -1385,7 +1383,7 @@ mod tests { use crate::disasm::*; fn setup_asm() -> (Assembler, CodeBlock) { - (Assembler::new(), CodeBlock::new_dummy(1024)) + (Assembler::new(0), CodeBlock::new_dummy(1024)) } #[test] @@ -1684,7 +1682,7 @@ mod tests { #[test] fn test_bcond_straddling_code_pages() { const LANDING_PAGE: usize = 65; - let mut asm = Assembler::new(); + let mut asm = Assembler::new(0); let mut cb = CodeBlock::new_dummy_with_freed_pages(vec![0, LANDING_PAGE]); // Skip to near the end of the page. Room for two instructions. diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs index edc0eaf39051ea..599ecfabc92374 100644 --- a/yjit/src/backend/ir.rs +++ b/yjit/src/backend/ir.rs @@ -2,11 +2,11 @@ use std::collections::HashMap; use std::fmt; use std::convert::From; use std::mem::take; -use crate::codegen::{gen_outlined_exit, gen_counted_exit}; -use crate::cruby::{vm_stack_canary, SIZEOF_VALUE_I32, VALUE}; +use crate::codegen::{gen_counted_exit, gen_outlined_exit}; +use crate::cruby::{vm_stack_canary, SIZEOF_VALUE_I32, VALUE, VM_ENV_DATA_SIZE}; use crate::virtualmem::CodePtr; use crate::asm::{CodeBlock, OutlinedCb}; -use crate::core::{Context, RegTemps, MAX_REG_TEMPS}; +use crate::core::{Context, RegMapping, RegOpnd, MAX_CTX_TEMPS}; use crate::options::*; use crate::stats::*; @@ -77,10 +77,12 @@ pub enum Opnd num_bits: u8, /// ctx.stack_size when this operand is made. Used with idx for Opnd::Reg. stack_size: u8, + /// The number of local variables in the current ISEQ. Used only for locals. + num_locals: Option, /// ctx.sp_offset when this operand is made. Used with idx for Opnd::Mem. sp_offset: i8, - /// ctx.reg_temps when this operand is read. Used for register allocation. - reg_temps: Option + /// ctx.reg_mapping when this operand is read. Used for register allocation. + reg_mapping: Option }, // Low-level operands, for lowering @@ -172,7 +174,7 @@ impl Opnd Opnd::Reg(reg) => Some(Opnd::Reg(reg.with_num_bits(num_bits))), Opnd::Mem(Mem { base, disp, .. }) => Some(Opnd::Mem(Mem { base, disp, num_bits })), Opnd::InsnOut { idx, .. } => Some(Opnd::InsnOut { idx, num_bits }), - Opnd::Stack { idx, stack_size, sp_offset, reg_temps, .. } => Some(Opnd::Stack { idx, num_bits, stack_size, sp_offset, reg_temps }), + Opnd::Stack { idx, stack_size, num_locals, sp_offset, reg_mapping, .. } => Some(Opnd::Stack { idx, num_bits, stack_size, num_locals, sp_offset, reg_mapping }), _ => None, } } @@ -227,28 +229,26 @@ impl Opnd Self::match_num_bits_iter(opnds.iter()) } - /// Calculate Opnd::Stack's index from the stack bottom. - pub fn stack_idx(&self) -> u8 { - self.get_stack_idx().unwrap() + /// Convert Opnd::Stack into RegMapping + pub fn reg_opnd(&self) -> RegOpnd { + self.get_reg_opnd().unwrap() } - /// Calculate Opnd::Stack's index from the stack bottom if it's Opnd::Stack. - pub fn get_stack_idx(&self) -> Option { - match self { - Opnd::Stack { idx, stack_size, .. } => { - Some((*stack_size as isize - *idx as isize - 1) as u8) - }, - _ => None - } - } - - /// Get the index for stack temp registers. - pub fn reg_idx(&self) -> usize { - match self { - Opnd::Stack { .. } => { - self.stack_idx() as usize % get_option!(num_temp_regs) - }, - _ => unreachable!(), + /// Convert an operand into RegMapping if it's Opnd::Stack + pub fn get_reg_opnd(&self) -> Option { + match *self { + Opnd::Stack { idx, stack_size, num_locals, .. } => Some( + if let Some(num_locals) = num_locals { + let last_idx = stack_size as i32 + VM_ENV_DATA_SIZE as i32 - 1; + assert!(last_idx <= idx, "Local index {} must be >= last local index {}", idx, last_idx); + assert!(idx <= last_idx + num_locals as i32, "Local index {} must be < last local index {} + local size {}", idx, last_idx, num_locals); + RegOpnd::Local((last_idx + num_locals as i32 - idx) as u8) + } else { + assert!(idx < stack_size as i32); + RegOpnd::Stack((stack_size as i32 - idx - 1) as u8) + } + ), + _ => None, } } } @@ -974,7 +974,7 @@ pub struct SideExitContext { /// Context fields used by get_generic_ctx() pub stack_size: u8, pub sp_offset: i8, - pub reg_temps: RegTemps, + pub reg_mapping: RegMapping, pub is_return_landing: bool, pub is_deferred: bool, } @@ -986,7 +986,7 @@ impl SideExitContext { pc, stack_size: ctx.get_stack_size(), sp_offset: ctx.get_sp_offset(), - reg_temps: ctx.get_reg_temps(), + reg_mapping: ctx.get_reg_mapping(), is_return_landing: ctx.is_return_landing(), is_deferred: ctx.is_deferred(), }; @@ -1002,7 +1002,7 @@ impl SideExitContext { let mut ctx = Context::default(); ctx.set_stack_size(self.stack_size); ctx.set_sp_offset(self.sp_offset); - ctx.set_reg_temps(self.reg_temps); + ctx.set_reg_mapping(self.reg_mapping); if self.is_return_landing { ctx.set_as_return_landing(); } @@ -1031,6 +1031,13 @@ pub struct Assembler { /// Context for generating the current insn pub ctx: Context, + /// The current ISEQ's local table size. asm.local_opnd() uses this, and it's + /// sometimes hard to pass this value, e.g. asm.spill_temps() in asm.ccall(). + /// + /// `None` means we're not assembling for an ISEQ, or that the local size is + /// not relevant. + pub(super) num_locals: Option, + /// Side exit caches for each SideExitContext pub(super) side_exits: HashMap, @@ -1046,16 +1053,31 @@ pub struct Assembler { impl Assembler { - pub fn new() -> Self { - Self::new_with_label_names(Vec::default(), HashMap::default()) + /// Create an Assembler for ISEQ-specific code. + /// It includes all inline code and some outlined code like side exits and stubs. + pub fn new(num_locals: u32) -> Self { + Self::new_with_label_names(Vec::default(), HashMap::default(), Some(num_locals)) + } + + /// Create an Assembler for outlined code that are not specific to any ISEQ, + /// e.g. trampolines that are shared globally. + pub fn new_without_iseq() -> Self { + Self::new_with_label_names(Vec::default(), HashMap::default(), None) } - pub fn new_with_label_names(label_names: Vec, side_exits: HashMap) -> Self { + /// Create an Assembler with parameters that are populated by another Assembler instance. + /// This API is used for copying an Assembler for the next compiler pass. + pub fn new_with_label_names( + label_names: Vec, + side_exits: HashMap, + num_locals: Option + ) -> Self { Self { insns: Vec::with_capacity(ASSEMBLER_INSNS_CAPACITY), live_ranges: Vec::with_capacity(ASSEMBLER_INSNS_CAPACITY), label_names, ctx: Context::default(), + num_locals, side_exits, side_exit_pc: None, side_exit_stack_size: None, @@ -1064,11 +1086,16 @@ impl Assembler } /// Get the list of registers that can be used for stack temps. - pub fn get_temp_regs() -> &'static [Reg] { + pub fn get_temp_regs2() -> &'static [Reg] { let num_regs = get_option!(num_temp_regs); &TEMP_REGS[0..num_regs] } + /// Get the number of locals for the ISEQ being compiled + pub fn get_num_locals(&self) -> Option { + self.num_locals + } + /// Set a context for generating side exits pub fn set_side_exit_context(&mut self, pc: *mut VALUE, stack_size: u8) { self.side_exit_pc = Some(pc); @@ -1090,31 +1117,32 @@ impl Assembler let mut opnd_iter = insn.opnd_iter_mut(); while let Some(opnd) = opnd_iter.next() { - match opnd { + match *opnd { // If we find any InsnOut from previous instructions, we're going to update // the live range of the previous instruction to point to this one. Opnd::InsnOut { idx, .. } => { - assert!(*idx < self.insns.len()); - self.live_ranges[*idx] = insn_idx; + assert!(idx < self.insns.len()); + self.live_ranges[idx] = insn_idx; } Opnd::Mem(Mem { base: MemBase::InsnOut(idx), .. }) => { - assert!(*idx < self.insns.len()); - self.live_ranges[*idx] = insn_idx; + assert!(idx < self.insns.len()); + self.live_ranges[idx] = insn_idx; } - // Set current ctx.reg_temps to Opnd::Stack. - Opnd::Stack { idx, num_bits, stack_size, sp_offset, reg_temps: None } => { + // Set current ctx.reg_mapping to Opnd::Stack. + Opnd::Stack { idx, num_bits, stack_size, num_locals, sp_offset, reg_mapping: None } => { assert_eq!( self.ctx.get_stack_size() as i16 - self.ctx.get_sp_offset() as i16, - *stack_size as i16 - *sp_offset as i16, + stack_size as i16 - sp_offset as i16, "Opnd::Stack (stack_size: {}, sp_offset: {}) expects a different SP position from asm.ctx (stack_size: {}, sp_offset: {})", - *stack_size, *sp_offset, self.ctx.get_stack_size(), self.ctx.get_sp_offset(), + stack_size, sp_offset, self.ctx.get_stack_size(), self.ctx.get_sp_offset(), ); *opnd = Opnd::Stack { - idx: *idx, - num_bits: *num_bits, - stack_size: *stack_size, - sp_offset: *sp_offset, - reg_temps: Some(self.ctx.get_reg_temps()), + idx, + num_bits, + stack_size, + num_locals, + sp_offset, + reg_mapping: Some(self.ctx.get_reg_mapping()), }; } _ => {} @@ -1141,7 +1169,7 @@ impl Assembler // Get a cached side exit let side_exit = match self.side_exits.get(&side_exit_context) { None => { - let exit_code = gen_outlined_exit(side_exit_context.pc, &side_exit_context.get_ctx(), ocb)?; + let exit_code = gen_outlined_exit(side_exit_context.pc, self.num_locals.unwrap(), &side_exit_context.get_ctx(), ocb)?; self.side_exits.insert(*side_exit_context, exit_code); exit_code } @@ -1175,20 +1203,20 @@ impl Assembler } // Convert Opnd::Stack to Opnd::Reg - fn reg_opnd(opnd: &Opnd) -> Opnd { - let regs = Assembler::get_temp_regs(); + fn reg_opnd(opnd: &Opnd, reg_idx: usize) -> Opnd { + let regs = Assembler::get_temp_regs2(); if let Opnd::Stack { num_bits, .. } = *opnd { incr_counter!(temp_reg_opnd); - Opnd::Reg(regs[opnd.reg_idx()]).with_num_bits(num_bits).unwrap() + Opnd::Reg(regs[reg_idx]).with_num_bits(num_bits).unwrap() } else { unreachable!() } } match opnd { - Opnd::Stack { reg_temps, .. } => { - if opnd.stack_idx() < MAX_REG_TEMPS && reg_temps.unwrap().get(opnd.stack_idx()) { - reg_opnd(opnd) + Opnd::Stack { reg_mapping, .. } => { + if let Some(reg_idx) = reg_mapping.unwrap().get_reg(opnd.reg_opnd()) { + reg_opnd(opnd, reg_idx) } else { mem_opnd(opnd) } @@ -1198,18 +1226,11 @@ impl Assembler } /// Allocate a register to a stack temp if available. - pub fn alloc_temp_reg(&mut self, stack_idx: u8) { - if get_option!(num_temp_regs) == 0 { - return; - } - + pub fn alloc_reg(&mut self, mapping: RegOpnd) { // Allocate a register if there's no conflict. - let mut reg_temps = self.ctx.get_reg_temps(); - if reg_temps.conflicts_with(stack_idx) { - assert!(!reg_temps.get(stack_idx)); - } else { - reg_temps.set(stack_idx, true); - self.set_reg_temps(reg_temps); + let mut reg_mapping = self.ctx.get_reg_mapping(); + if reg_mapping.alloc_reg(mapping) { + self.set_reg_mapping(reg_mapping); } } @@ -1220,47 +1241,58 @@ impl Assembler self.ctx.clear_local_types(); } - /// Spill all live stack temps from registers to the stack - pub fn spill_temps(&mut self) { + /// Spill all live registers to the stack + pub fn spill_regs(&mut self) { // Forget registers above the stack top - let mut reg_temps = self.ctx.get_reg_temps(); - for stack_idx in self.ctx.get_stack_size()..MAX_REG_TEMPS { - reg_temps.set(stack_idx, false); + let mut reg_mapping = self.ctx.get_reg_mapping(); + for stack_idx in self.ctx.get_stack_size()..MAX_CTX_TEMPS as u8 { + reg_mapping.dealloc_reg(RegOpnd::Stack(stack_idx)); } - self.set_reg_temps(reg_temps); + self.set_reg_mapping(reg_mapping); // Spill live stack temps - if self.ctx.get_reg_temps() != RegTemps::default() { - asm_comment!(self, "spill_temps: {:08b} -> {:08b}", self.ctx.get_reg_temps().as_u8(), RegTemps::default().as_u8()); - for stack_idx in 0..u8::min(MAX_REG_TEMPS, self.ctx.get_stack_size()) { - if self.ctx.get_reg_temps().get(stack_idx) { + if self.ctx.get_reg_mapping() != RegMapping::default() { + asm_comment!(self, "spill_temps: {:?} -> {:?}", self.ctx.get_reg_mapping(), RegMapping::default()); + + // Spill stack temps + for stack_idx in 0..u8::min(MAX_CTX_TEMPS as u8, self.ctx.get_stack_size()) { + if reg_mapping.dealloc_reg(RegOpnd::Stack(stack_idx)) { let idx = self.ctx.get_stack_size() - 1 - stack_idx; self.spill_temp(self.stack_opnd(idx.into())); - reg_temps.set(stack_idx, false); } } - self.ctx.set_reg_temps(reg_temps); + + // Spill locals + for local_idx in 0..MAX_CTX_TEMPS as u8 { + if reg_mapping.dealloc_reg(RegOpnd::Local(local_idx)) { + let first_local_ep_offset = self.num_locals.unwrap() + VM_ENV_DATA_SIZE - 1; + let ep_offset = first_local_ep_offset - local_idx as u32; + self.spill_temp(self.local_opnd(ep_offset)); + } + } + + self.ctx.set_reg_mapping(reg_mapping); } // Every stack temp should have been spilled - assert_eq!(self.ctx.get_reg_temps(), RegTemps::default()); + assert_eq!(self.ctx.get_reg_mapping(), RegMapping::default()); } /// Spill a stack temp from a register to the stack fn spill_temp(&mut self, opnd: Opnd) { - assert!(self.ctx.get_reg_temps().get(opnd.stack_idx())); + assert_ne!(self.ctx.get_reg_mapping().get_reg(opnd.reg_opnd()), None); - // Use different RegTemps for dest and src operands - let reg_temps = self.ctx.get_reg_temps(); - let mut mem_temps = reg_temps; - mem_temps.set(opnd.stack_idx(), false); + // Use different RegMappings for dest and src operands + let reg_mapping = self.ctx.get_reg_mapping(); + let mut mem_mappings = reg_mapping; + mem_mappings.dealloc_reg(opnd.reg_opnd()); // Move the stack operand from a register to memory match opnd { - Opnd::Stack { idx, num_bits, stack_size, sp_offset, .. } => { + Opnd::Stack { idx, num_bits, stack_size, num_locals, sp_offset, .. } => { self.mov( - Opnd::Stack { idx, num_bits, stack_size, sp_offset, reg_temps: Some(mem_temps) }, - Opnd::Stack { idx, num_bits, stack_size, sp_offset, reg_temps: Some(reg_temps) }, + Opnd::Stack { idx, num_bits, stack_size, num_locals, sp_offset, reg_mapping: Some(mem_mappings) }, + Opnd::Stack { idx, num_bits, stack_size, num_locals, sp_offset, reg_mapping: Some(reg_mapping) }, ); } _ => unreachable!(), @@ -1269,20 +1301,10 @@ impl Assembler } /// Update which stack temps are in a register - pub fn set_reg_temps(&mut self, reg_temps: RegTemps) { - if self.ctx.get_reg_temps() != reg_temps { - asm_comment!(self, "reg_temps: {:08b} -> {:08b}", self.ctx.get_reg_temps().as_u8(), reg_temps.as_u8()); - self.ctx.set_reg_temps(reg_temps); - self.verify_reg_temps(); - } - } - - /// Assert there's no conflict in stack temp register allocation - fn verify_reg_temps(&self) { - for stack_idx in 0..MAX_REG_TEMPS { - if self.ctx.get_reg_temps().get(stack_idx) { - assert!(!self.ctx.get_reg_temps().conflicts_with(stack_idx)); - } + pub fn set_reg_mapping(&mut self, reg_mapping: RegMapping) { + if self.ctx.get_reg_mapping() != reg_mapping { + asm_comment!(self, "reg_mapping: {:?} -> {:?}", self.ctx.get_reg_mapping(), reg_mapping); + self.ctx.set_reg_mapping(reg_mapping); } } @@ -1411,7 +1433,7 @@ impl Assembler let live_ranges: Vec = take(&mut self.live_ranges); // shifted_live_ranges is indexed by mapped indexes in insn operands. let mut shifted_live_ranges: Vec = live_ranges.clone(); - let mut asm = Assembler::new_with_label_names(take(&mut self.label_names), take(&mut self.side_exits)); + let mut asm = Assembler::new_with_label_names(take(&mut self.label_names), take(&mut self.side_exits), self.num_locals); let mut iterator = self.into_draining_iter(); while let Some((index, mut insn)) = iterator.next_mapped() { @@ -1565,13 +1587,10 @@ impl Assembler #[must_use] pub fn compile(self, cb: &mut CodeBlock, ocb: Option<&mut OutlinedCb>) -> Option<(CodePtr, Vec)> { - #[cfg(feature = "disasm")] let start_addr = cb.get_write_ptr(); - let alloc_regs = Self::get_alloc_regs(); let ret = self.compile_with_regs(cb, ocb, alloc_regs); - #[cfg(feature = "disasm")] if let Some(dump_disasm) = get_option_ref!(dump_disasm) { use crate::disasm::dump_disasm_addr_range; let end_addr = cb.get_write_ptr(); @@ -1706,24 +1725,24 @@ impl Assembler { // Let vm_check_canary() assert this ccall's leafness if leaf_ccall is set let canary_opnd = self.set_stack_canary(&opnds); - let old_temps = self.ctx.get_reg_temps(); // with registers + let old_temps = self.ctx.get_reg_mapping(); // with registers // Spill stack temp registers since they are caller-saved registers. // Note that this doesn't spill stack temps that are already popped // but may still be used in the C arguments. - self.spill_temps(); - let new_temps = self.ctx.get_reg_temps(); // all spilled + self.spill_regs(); + let new_temps = self.ctx.get_reg_mapping(); // all spilled - // Temporarily manipulate RegTemps so that we can use registers + // Temporarily manipulate RegMappings so that we can use registers // to pass stack operands that are already spilled above. - self.ctx.set_reg_temps(old_temps); + self.ctx.set_reg_mapping(old_temps); // Call a C function let out = self.next_opnd_out(Opnd::match_num_bits(&opnds)); self.push_insn(Insn::CCall { fptr, opnds, out }); // Registers in old_temps may be clobbered by the above C call, - // so rollback the manipulated RegTemps to a spilled version. - self.ctx.set_reg_temps(new_temps); + // so rollback the manipulated RegMappings to a spilled version. + self.ctx.set_reg_mapping(new_temps); // Clear the canary after use if let Some(canary_opnd) = canary_opnd { @@ -1741,7 +1760,7 @@ impl Assembler { // If the slot is already used, which is a valid optimization to avoid spills, // give up the verification. let canary_opnd = if cfg!(debug_assertions) && self.leaf_ccall && opnds.iter().all(|opnd| - opnd.get_stack_idx() != canary_opnd.get_stack_idx() + opnd.get_reg_opnd() != canary_opnd.get_reg_opnd() ) { asm_comment!(self, "set stack canary"); self.mov(canary_opnd, vm_stack_canary().into()); @@ -1770,9 +1789,9 @@ impl Assembler { pub fn cpop_all(&mut self) { self.push_insn(Insn::CPopAll); - // Re-enable ccall's RegTemps assertion disabled by cpush_all. + // Re-enable ccall's RegMappings assertion disabled by cpush_all. // cpush_all + cpop_all preserve all stack temp registers, so it's safe. - self.set_reg_temps(self.ctx.get_reg_temps()); + self.set_reg_mapping(self.ctx.get_reg_mapping()); } pub fn cpop_into(&mut self, opnd: Opnd) { @@ -1790,7 +1809,7 @@ impl Assembler { // Temps will be marked back as being in registers by cpop_all. // We assume that cpush_all + cpop_all are used for C functions in utils.rs // that don't require spill_temps for GC. - self.set_reg_temps(RegTemps::default()); + self.set_reg_mapping(RegMapping::default()); } pub fn cret(&mut self, opnd: Opnd) { @@ -2057,10 +2076,10 @@ impl Assembler { } /// Macro to use format! for Insn::Comment, which skips a format! call -/// when disasm is not supported. +/// when not dumping disassembly. macro_rules! asm_comment { ($asm:expr, $($fmt:tt)*) => { - if cfg!(feature = "disasm") { + if $crate::options::get_option_ref!(dump_disasm).is_some() { $asm.push_insn(Insn::Comment(format!($($fmt)*))); } }; diff --git a/yjit/src/backend/tests.rs b/yjit/src/backend/tests.rs index 01e87fe26c60e6..ac2f35b3d9c9e4 100644 --- a/yjit/src/backend/tests.rs +++ b/yjit/src/backend/tests.rs @@ -1,19 +1,19 @@ #![cfg(test)] -use crate::asm::{CodeBlock}; +use crate::asm::CodeBlock; use crate::backend::ir::*; use crate::cruby::*; use crate::utils::c_callable; #[test] fn test_add() { - let mut asm = Assembler::new(); + let mut asm = Assembler::new(0); let out = asm.add(SP, Opnd::UImm(1)); let _ = asm.add(out, Opnd::UImm(2)); } #[test] fn test_alloc_regs() { - let mut asm = Assembler::new(); + let mut asm = Assembler::new(0); // Get the first output that we're going to reuse later. let out1 = asm.add(EC, Opnd::UImm(1)); @@ -62,7 +62,7 @@ fn test_alloc_regs() { fn setup_asm() -> (Assembler, CodeBlock) { return ( - Assembler::new(), + Assembler::new(0), CodeBlock::new_dummy(1024) ); } @@ -194,7 +194,7 @@ fn test_c_call() #[test] fn test_alloc_ccall_regs() { - let mut asm = Assembler::new(); + let mut asm = Assembler::new(0); let out1 = asm.ccall(0 as *const u8, vec![]); let out2 = asm.ccall(0 as *const u8, vec![out1]); asm.mov(EC, out2); @@ -283,8 +283,7 @@ fn test_bake_string() { #[test] fn test_draining_iterator() { - - let mut asm = Assembler::new(); + let mut asm = Assembler::new(0); let _ = asm.load(Opnd::None); asm.store(Opnd::None, Opnd::None); @@ -315,7 +314,7 @@ fn test_cmp_8_bit() { fn test_no_pos_marker_callback_when_compile_fails() { // When compilation fails (e.g. when out of memory), the code written out is malformed. // We don't want to invoke the pos_marker callbacks with positions of malformed code. - let mut asm = Assembler::new(); + let mut asm = Assembler::new(0); // Markers around code to exhaust memory limit let fail_if_called = |_code_ptr, _cb: &_| panic!("pos_marker callback should not be called"); diff --git a/yjit/src/backend/x86_64/mod.rs b/yjit/src/backend/x86_64/mod.rs index 4ca5e9be9ccb88..c0d42e79e6543a 100644 --- a/yjit/src/backend/x86_64/mod.rs +++ b/yjit/src/backend/x86_64/mod.rs @@ -79,7 +79,7 @@ impl From<&Opnd> for X86Opnd { } } -/// List of registers that can be used for stack temps. +/// List of registers that can be used for stack temps and locals. pub static TEMP_REGS: [Reg; 5] = [RSI_REG, RDI_REG, R8_REG, R9_REG, R10_REG]; impl Assembler @@ -112,7 +112,7 @@ impl Assembler fn x86_split(mut self) -> Assembler { let live_ranges: Vec = take(&mut self.live_ranges); - let mut asm = Assembler::new_with_label_names(take(&mut self.label_names), take(&mut self.side_exits)); + let mut asm = Assembler::new_with_label_names(take(&mut self.label_names), take(&mut self.side_exits), self.num_locals); let mut iterator = self.into_draining_iter(); while let Some((index, mut insn)) = iterator.next_unmapped() { @@ -492,9 +492,7 @@ impl Assembler match insn { Insn::Comment(text) => { - if cfg!(feature = "disasm") { - cb.add_comment(text); - } + cb.add_comment(text); }, // Write the label at the current position @@ -897,14 +895,14 @@ impl Assembler #[cfg(test)] mod tests { - use crate::disasm::{assert_disasm}; + use crate::disasm::assert_disasm; #[cfg(feature = "disasm")] use crate::disasm::{unindent, disasm_addr_range}; use super::*; fn setup_asm() -> (Assembler, CodeBlock) { - (Assembler::new(), CodeBlock::new_dummy(1024)) + (Assembler::new(0), CodeBlock::new_dummy(1024)) } #[test] diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 7aeaf070e784ec..4abf58fea4b122 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -402,6 +402,11 @@ impl<'a> JITState<'a> { _ => false, } } + + /// Return the number of locals in the current ISEQ + pub fn num_locals(&self) -> u32 { + unsafe { get_iseq_body_local_table_size(self.iseq) } + } } /// Macro to call jit.perf_symbol_push() without evaluating arguments when @@ -646,7 +651,7 @@ fn verify_ctx(jit: &JITState, ctx: &Context) { } // Verify stack operand types - let top_idx = cmp::min(ctx.get_stack_size(), MAX_TEMP_TYPES as u8); + let top_idx = cmp::min(ctx.get_stack_size(), MAX_CTX_TEMPS as u8); for i in 0..top_idx { let learned_mapping = ctx.get_opnd_mapping(StackOpnd(i)); let learned_type = ctx.get_opnd_type(StackOpnd(i)); @@ -693,7 +698,7 @@ fn verify_ctx(jit: &JITState, ctx: &Context) { // Verify local variable types let local_table_size = unsafe { get_iseq_body_local_table_size(jit.iseq) }; - let top_idx: usize = cmp::min(local_table_size as usize, MAX_TEMP_TYPES); + let top_idx: usize = cmp::min(local_table_size as usize, MAX_CTX_TEMPS); 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); @@ -717,7 +722,7 @@ fn verify_ctx(jit: &JITState, ctx: &Context) { // interpreter state. fn gen_stub_exit(ocb: &mut OutlinedCb) -> Option { let ocb = ocb.unwrap(); - let mut asm = Assembler::new(); + let mut asm = Assembler::new_without_iseq(); gen_counter_incr(&mut asm, Counter::exit_from_branch_stub); @@ -748,7 +753,7 @@ fn gen_exit(exit_pc: *mut VALUE, asm: &mut Assembler) { } // Spill stack temps before returning to the interpreter - asm.spill_temps(); + asm.spill_regs(); // Generate the code to exit to the interpreters // Write the adjusted SP back into the CFP @@ -804,11 +809,11 @@ fn gen_exit(exit_pc: *mut VALUE, asm: &mut Assembler) { /// moment, so there is one unique side exit for each context. Note that /// it's incorrect to jump to the side exit after any ctx stack push operations /// since they change the logic required for reconstructing interpreter state. -pub fn gen_outlined_exit(exit_pc: *mut VALUE, ctx: &Context, ocb: &mut OutlinedCb) -> Option { +pub fn gen_outlined_exit(exit_pc: *mut VALUE, num_locals: u32, ctx: &Context, ocb: &mut OutlinedCb) -> Option { let mut cb = ocb.unwrap(); - let mut asm = Assembler::new(); + let mut asm = Assembler::new(num_locals); asm.ctx = *ctx; - asm.set_reg_temps(ctx.get_reg_temps()); + asm.set_reg_mapping(ctx.get_reg_mapping()); gen_exit(exit_pc, &mut asm); @@ -826,7 +831,7 @@ pub fn gen_counted_exit(exit_pc: *mut VALUE, side_exit: CodePtr, ocb: &mut Outli None => return Some(side_exit), }; - let mut asm = Assembler::new(); + let mut asm = Assembler::new_without_iseq(); // Increment a counter gen_counter_incr(&mut asm, counter); @@ -876,7 +881,7 @@ pub fn jit_ensure_block_entry_exit(jit: &mut JITState, asm: &mut Assembler) -> O jit.block_entry_exit = Some(entry_exit?); } else { let block_entry_pc = unsafe { rb_iseq_pc_at_idx(jit.iseq, jit.starting_insn_idx.into()) }; - jit.block_entry_exit = Some(gen_outlined_exit(block_entry_pc, block_starting_context, jit.get_ocb())?); + jit.block_entry_exit = Some(gen_outlined_exit(block_entry_pc, jit.num_locals(), block_starting_context, jit.get_ocb())?); } Some(()) @@ -885,7 +890,7 @@ pub fn jit_ensure_block_entry_exit(jit: &mut JITState, asm: &mut Assembler) -> O // Landing code for when c_return tracing is enabled. See full_cfunc_return(). fn gen_full_cfunc_return(ocb: &mut OutlinedCb) -> Option { let ocb = ocb.unwrap(); - let mut asm = Assembler::new(); + let mut asm = Assembler::new_without_iseq(); // This chunk of code expects REG_EC to be filled properly and // RAX to contain the return value of the C method. @@ -915,7 +920,7 @@ fn gen_full_cfunc_return(ocb: &mut OutlinedCb) -> Option { /// This is used by gen_leave() and gen_entry_prologue() fn gen_leave_exit(ocb: &mut OutlinedCb) -> Option { let ocb = ocb.unwrap(); - let mut asm = Assembler::new(); + let mut asm = Assembler::new_without_iseq(); // gen_leave() fully reconstructs interpreter state and leaves the // return value in C_RET_OPND before coming here. @@ -942,7 +947,7 @@ fn gen_leave_exit(ocb: &mut OutlinedCb) -> Option { // the caller's stack, which is different from gen_stub_exit(). fn gen_leave_exception(ocb: &mut OutlinedCb) -> Option { let ocb = ocb.unwrap(); - let mut asm = Assembler::new(); + let mut asm = Assembler::new_without_iseq(); // gen_leave() leaves the return value in C_RET_OPND before coming here. let ruby_ret_val = asm.live_reg_opnd(C_RET_OPND); @@ -1011,7 +1016,7 @@ pub fn gen_entry_prologue( ) -> Option { let code_ptr = cb.get_write_ptr(); - let mut asm = Assembler::new(); + let mut asm = Assembler::new(unsafe { get_iseq_body_local_table_size(iseq) }); if get_option_ref!(dump_disasm).is_some() { asm_comment!(asm, "YJIT entry point: {}", iseq_get_location(iseq, 0)); } else { @@ -1134,7 +1139,7 @@ fn end_block_with_jump( if jit.record_boundary_patch_point { jit.record_boundary_patch_point = false; let exit_pc = unsafe { rb_iseq_pc_at_idx(jit.iseq, continuation_insn_idx.into())}; - let exit_pos = gen_outlined_exit(exit_pc, &reset_depth, jit.get_ocb()); + let exit_pos = gen_outlined_exit(exit_pc, jit.num_locals(), &reset_depth, jit.get_ocb()); record_global_inval_patch(asm, exit_pos?); } @@ -1180,7 +1185,7 @@ pub fn gen_single_block( jit.iseq = blockid.iseq; // Create a backend assembler instance - let mut asm = Assembler::new(); + let mut asm = Assembler::new(jit.num_locals()); asm.ctx = ctx; #[cfg(feature = "disasm")] @@ -1188,7 +1193,7 @@ pub fn gen_single_block( let blockid_idx = blockid.idx; let chain_depth = if asm.ctx.get_chain_depth() > 0 { format!("(chain_depth: {})", asm.ctx.get_chain_depth()) } else { "".to_string() }; asm_comment!(asm, "Block: {} {}", iseq_get_location(blockid.iseq, blockid_idx), chain_depth); - asm_comment!(asm, "reg_temps: {:08b}", asm.ctx.get_reg_temps().as_u8()); + asm_comment!(asm, "reg_mapping: {:?}", asm.ctx.get_reg_mapping()); } // Mark the start of an ISEQ for --yjit-perf @@ -1233,14 +1238,14 @@ pub fn gen_single_block( // stack_pop doesn't immediately deallocate a register for stack temps, // but it's safe to do so at this instruction boundary. - for stack_idx in asm.ctx.get_stack_size()..MAX_REG_TEMPS { - asm.ctx.dealloc_temp_reg(stack_idx); + for stack_idx in asm.ctx.get_stack_size()..MAX_CTX_TEMPS as u8 { + asm.ctx.dealloc_reg(RegOpnd::Stack(stack_idx)); } // If previous instruction requested to record the boundary if jit.record_boundary_patch_point { // Generate an exit to this instruction and record it - let exit_pos = gen_outlined_exit(jit.pc, &asm.ctx, jit.get_ocb()).ok_or(())?; + let exit_pos = gen_outlined_exit(jit.pc, jit.num_locals(), &asm.ctx, jit.get_ocb()).ok_or(())?; record_global_inval_patch(&mut asm, exit_pos); jit.record_boundary_patch_point = false; } @@ -1803,7 +1808,7 @@ fn gen_splatkw( asm.mov(stack_ret, hash); asm.stack_push(block_type); // Leave block_opnd spilled by ccall as is - asm.ctx.dealloc_temp_reg(asm.ctx.get_stack_size() - 1); + asm.ctx.dealloc_reg(RegOpnd::Stack(asm.ctx.get_stack_size() - 1)); } Some(KeepCompiling) @@ -2278,7 +2283,7 @@ fn gen_getlocal_generic( ) -> Option { let local_opnd = if level == 0 && jit.assume_no_ep_escape(asm) { // Load the local using SP register - asm.ctx.ep_opnd(-(ep_offset as i32)) + asm.local_opnd(ep_offset) } else { // Load environment pointer EP (level 0) from CFP let ep_opnd = gen_get_ep(asm, level); @@ -2359,8 +2364,11 @@ fn gen_setlocal_generic( let (flags_opnd, local_opnd) = if level == 0 && jit.assume_no_ep_escape(asm) { // 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); + let local_opnd = asm.local_opnd(ep_offset); + + // Allocate a register to the new local operand + asm.alloc_reg(local_opnd.reg_opnd()); (flags_opnd, local_opnd) } else { // Load flags and the local for the level @@ -3071,7 +3079,7 @@ fn gen_set_ivar( // If we know the stack value is an immediate, there's no need to // generate WB code. if !stack_type.is_imm() { - asm.spill_temps(); // for ccall (unconditionally spill them for RegTemps consistency) + asm.spill_regs(); // for ccall (unconditionally spill them for RegMappings consistency) let skip_wb = asm.new_label("skip_wb"); // If the value we're writing is an immediate, we don't need to WB asm.test(write_val, (RUBY_IMMEDIATE_MASK as u64).into()); @@ -3516,7 +3524,7 @@ fn gen_equality_specialized( let ret = asm.new_label("ret"); // Spill for ccall. For safety, unconditionally spill temps before branching. - asm.spill_temps(); + asm.spill_regs(); // If they are equal by identity, return true asm.cmp(a_opnd, b_opnd); @@ -5482,7 +5490,7 @@ fn jit_rb_str_uplus( // We allocate when we dup the string jit_prepare_call_with_gc(jit, asm); - asm.spill_temps(); // For ccall. Unconditionally spill them for RegTemps consistency. + asm.spill_regs(); // For ccall. Unconditionally spill them for RegMappings consistency. asm_comment!(asm, "Unary plus on string"); let recv_opnd = asm.stack_pop(1); @@ -5500,7 +5508,7 @@ fn jit_rb_str_uplus( asm.jz(ret_label); // Str is frozen - duplicate it - asm.spill_temps(); // for ccall + asm.spill_regs(); // for ccall let ret_opnd = asm.ccall(rb_str_dup as *const u8, vec![recv_opnd]); asm.mov(stack_ret, ret_opnd); @@ -5782,7 +5790,7 @@ fn jit_rb_str_concat( // rb_str_buf_append may raise Encoding::CompatibilityError, but we accept compromised // backtraces on this method since the interpreter does the same thing on opt_ltlt. jit_prepare_non_leaf_call(jit, asm); - asm.spill_temps(); // For ccall. Unconditionally spill them for RegTemps consistency. + asm.spill_regs(); // For ccall. Unconditionally spill them for RegMappings consistency. let concat_arg = asm.stack_pop(1); let recv = asm.stack_pop(1); @@ -5815,7 +5823,7 @@ fn jit_rb_str_concat( // If encodings are different, use a slower encoding-aware concatenate asm.write_label(enc_mismatch); - asm.spill_temps(); // Ignore the register for the other local branch + asm.spill_regs(); // Ignore the register for the other local branch let ret_opnd = asm.ccall(rb_str_buf_append as *const u8, vec![recv, concat_arg]); let stack_ret = asm.stack_push(Type::TString); asm.mov(stack_ret, ret_opnd); @@ -6321,7 +6329,7 @@ fn gen_push_frame( if frame.iseq.is_some() { // Spill stack temps to let the callee use them (must be done before changing the SP register) - asm.spill_temps(); + asm.spill_regs(); // Saving SP before calculating ep avoids a dependency on a register // However this must be done after referencing frame.recv, which may be SP-relative @@ -7502,7 +7510,7 @@ fn gen_send_iseq( }; // Store rest param to memory to avoid register shuffle as // we won't be reading it for the remainder of the block. - asm.ctx.dealloc_temp_reg(rest_param.stack_idx()); + asm.ctx.dealloc_reg(rest_param.reg_opnd()); asm.store(rest_param, rest_param_array); } @@ -7601,7 +7609,7 @@ fn gen_send_iseq( // Write the CI in to the stack and ensure that it actually gets // flushed to memory let ci_opnd = asm.stack_opnd(-1); - asm.ctx.dealloc_temp_reg(ci_opnd.stack_idx()); + asm.ctx.dealloc_reg(ci_opnd.reg_opnd()); asm.mov(ci_opnd, VALUE(ci as usize).into()); } @@ -7714,7 +7722,7 @@ fn gen_send_iseq( // Pop arguments and receiver in return context and // mark it as a continuation of gen_leave() - let mut return_asm = Assembler::new(); + let mut return_asm = Assembler::new(jit.num_locals()); return_asm.ctx = asm.ctx; return_asm.stack_pop(sp_offset.try_into().unwrap()); return_asm.ctx.set_sp_offset(0); // We set SP on the caller's frame above @@ -7967,7 +7975,7 @@ fn gen_iseq_kw_call( kwargs_order[kwrest_idx] = 0; } // Put kwrest straight into memory, since we might pop it later - asm.ctx.dealloc_temp_reg(stack_kwrest.stack_idx()); + asm.ctx.dealloc_reg(stack_kwrest.reg_opnd()); asm.mov(stack_kwrest, kwrest); if stack_kwrest_idx >= 0 { asm.ctx.set_opnd_mapping(stack_kwrest.into(), TempMapping::map_to_stack(kwrest_type)); @@ -8065,7 +8073,7 @@ fn gen_iseq_kw_call( if let Some(kwrest_type) = kwrest_type { let kwrest = asm.stack_push(kwrest_type); // We put the kwrest parameter in memory earlier - asm.ctx.dealloc_temp_reg(kwrest.stack_idx()); + asm.ctx.dealloc_reg(kwrest.reg_opnd()); argc += 1; } @@ -9858,7 +9866,7 @@ fn gen_getblockparam( // Save the PC and SP because we might allocate jit_prepare_call_with_gc(jit, asm); - asm.spill_temps(); // For ccall. Unconditionally spill them for RegTemps consistency. + asm.spill_regs(); // For ccall. Unconditionally spill them for RegMappings consistency. // A mirror of the interpreter code. Checking for the case // where it's pushing rb_block_param_proxy. @@ -10322,8 +10330,10 @@ impl CodegenGlobals { let mem_block = Rc::new(RefCell::new(mem_block)); let freed_pages = Rc::new(None); - let cb = CodeBlock::new(mem_block.clone(), false, freed_pages.clone()); - let ocb = OutlinedCb::wrap(CodeBlock::new(mem_block, true, freed_pages)); + + let asm_comments = get_option_ref!(dump_disasm).is_some(); + let cb = CodeBlock::new(mem_block.clone(), false, freed_pages.clone(), asm_comments); + let ocb = OutlinedCb::wrap(CodeBlock::new(mem_block, true, freed_pages, asm_comments)); (cb, ocb) }; @@ -10464,7 +10474,7 @@ mod tests { return ( Context::default(), - Assembler::new(), + Assembler::new(0), cb, OutlinedCb::wrap(CodeBlock::new_dummy(256 * 1024)), ); @@ -10532,7 +10542,7 @@ mod tests { assert_eq!(status, Some(KeepCompiling)); let mut default = Context::default(); - default.set_reg_temps(context.get_reg_temps()); + default.set_reg_mapping(context.get_reg_mapping()); assert_eq!(context.diff(&default), TypeDiff::Compatible(0)); } diff --git a/yjit/src/core.rs b/yjit/src/core.rs index f42d7bd784e5b2..eb1c0d585020a7 100644 --- a/yjit/src/core.rs +++ b/yjit/src/core.rs @@ -30,11 +30,11 @@ use YARVOpnd::*; use TempMappingKind::*; use crate::invariants::*; -// Maximum number of temp value types we keep track of -pub const MAX_TEMP_TYPES: usize = 8; +// Maximum number of temp value types or registers we keep track of +pub const MAX_CTX_TEMPS: usize = 8; -// Maximum number of local variable types we keep track of -const MAX_LOCAL_TYPES: usize = 8; +// Maximum number of local variable types or registers we keep track of +const MAX_CTX_LOCALS: usize = 8; /// An index into `ISEQ_BODY(iseq)->iseq_encoded`. Points /// to a YARV instruction or an instruction operand. @@ -411,61 +411,112 @@ impl From for YARVOpnd { } } -/// Maximum index of stack temps that could be in a register -pub const MAX_REG_TEMPS: u8 = 8; +/// Number of registers that can be used for stack temps or locals +pub const MAX_MAPPED_REGS: usize = 5; -/// Bitmap of which stack temps are in a register -#[derive(Copy, Clone, Default, Eq, Hash, PartialEq, Debug)] -pub struct RegTemps(u8); +/// A stack slot or a local variable. u8 represents the index of it (<= 8). +#[derive(Copy, Clone, Eq, Hash, PartialEq, Debug)] +pub enum RegOpnd { + Stack(u8), + Local(u8), +} + +/// RegMappings manages a set of registers used for stack temps and locals. +/// Each element of the array represents each of the registers. +/// If an element is Some, the stack temp or the local uses a register. +/// +/// Note that Opnd::InsnOut uses a separate set of registers at the moment. +#[derive(Copy, Clone, Default, Eq, Hash, PartialEq)] +pub struct RegMapping([Option; MAX_MAPPED_REGS]); -impl RegTemps { - pub fn get(&self, index: u8) -> bool { - assert!(index < MAX_REG_TEMPS); - (self.0 >> index) & 1 == 1 +impl RegMapping { + /// Return the index of the register for a given operand if allocated. + pub fn get_reg(&self, opnd: RegOpnd) -> Option { + self.0.iter().enumerate() + .find(|(_, ®_opnd)| reg_opnd == Some(opnd)) + .map(|(reg_idx, _)| reg_idx) } - pub fn set(&mut self, index: u8, value: bool) { - assert!(index < MAX_REG_TEMPS); - if value { - self.0 = self.0 | (1 << index); - } else { - self.0 = self.0 & !(1 << index); + /// Allocate a register for a given operand if available. + /// Return true if self is updated. + pub fn alloc_reg(&mut self, opnd: RegOpnd) -> bool { + // If a given opnd already has a register, skip allocation. + if self.get_reg(opnd).is_some() { + return false; } - } - pub fn as_u8(&self) -> u8 { - self.0 + // If the index is too large to encode with with 3 bits, give up. + match opnd { + RegOpnd::Stack(stack_idx) => if stack_idx >= MAX_CTX_TEMPS as u8 { + return false; + } + RegOpnd::Local(local_idx) => if local_idx >= MAX_CTX_LOCALS as u8 { + return false; + } + }; + + // Allocate a register if available. + if let Some(reg_idx) = self.find_unused_reg(opnd) { + self.0[reg_idx] = Some(opnd); + return true; + } + false } - /// Return true if there's a register that conflicts with a given stack_idx. - pub fn conflicts_with(&self, stack_idx: u8) -> bool { - let mut other_idx = stack_idx as usize % get_option!(num_temp_regs); - while other_idx < MAX_REG_TEMPS as usize { - if stack_idx as usize != other_idx && self.get(other_idx as u8) { + /// Deallocate a register for a given operand if in use. + /// Return true if self is updated. + pub fn dealloc_reg(&mut self, opnd: RegOpnd) -> bool { + for reg_opnd in self.0.iter_mut() { + if *reg_opnd == Some(opnd) { + *reg_opnd = None; return true; } - other_idx += get_option!(num_temp_regs); } false } + + /// Find an available register and return the index of it. + fn find_unused_reg(&self, opnd: RegOpnd) -> Option { + let num_regs = get_option!(num_temp_regs); + if num_regs == 0 { + return None; + } + assert!(num_regs <= MAX_MAPPED_REGS); + + // If the default index for the operand is available, use that to minimize + // discrepancies among Contexts. + let default_idx = match opnd { + RegOpnd::Stack(stack_idx) => stack_idx.as_usize() % num_regs, + RegOpnd::Local(local_idx) => num_regs - (local_idx.as_usize() % num_regs) - 1, + }; + if self.0[default_idx].is_none() { + return Some(default_idx); + } + + // If not, pick any other available register. Like default indexes, prefer + // lower indexes for Stack, and higher indexes for Local. + let mut index_temps = self.0.iter().enumerate(); + match opnd { + RegOpnd::Stack(_) => index_temps.find(|(_, reg_opnd)| reg_opnd.is_none()), + RegOpnd::Local(_) => index_temps.rev().find(|(_, reg_opnd)| reg_opnd.is_none()), + }.map(|(index, _)| index) + } } -/// Bits for chain_depth_return_landing_defer -const RETURN_LANDING_BIT: u8 = 0b10000000; -const DEFER_BIT: u8 = 0b01000000; -const CHAIN_DEPTH_MASK: u8 = 0b00111111; // 63 +impl fmt::Debug for RegMapping { + /// Print `[None, ...]` instead of the default `RegMappings([None, ...])` + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{:?}", self.0) + } +} + +/// Maximum value of the chain depth (should fit in 5 bits) +const CHAIN_DEPTH_MAX: u8 = 0b11111; // 31 /// Code generation context /// Contains information we can use to specialize/optimize code -/// There are a lot of context objects so we try to keep the size small. #[derive(Copy, Clone, Default, Eq, Hash, PartialEq, Debug)] pub struct Context { - // FIXME: decoded_from breaks == on contexts - /* - // Offset at which this context was previously encoded (zero if not) - decoded_from: u32, - */ - // Number of values currently on the temporary stack stack_size: u8, @@ -473,14 +524,18 @@ pub struct Context { // This represents how far the JIT's SP is from the "real" SP sp_offset: i8, - /// Bitmap of which stack temps are in a register - reg_temps: RegTemps, + /// Which stack temps or locals are in a register + reg_mapping: RegMapping, + + // Depth of this block in the sidechain (eg: inline-cache chain) + // 6 bits, max 63 + chain_depth: u8, + + // Whether this code is the target of a JIT-to-JIT Ruby return ([Self::is_return_landing]) + is_return_landing: bool, - /// Fields packed into u8 - /// - 1st bit from the left: Whether this code is the target of a JIT-to-JIT Ruby return ([Self::is_return_landing]) - /// - 2nd bit from the left: Whether the compilation of this code has been deferred ([Self::is_deferred]) - /// - Last 6 bits (max: 63): Depth of this block in the sidechain (eg: inline-cache chain) - chain_depth_and_flags: u8, + // Whether the compilation of this code has been deferred ([Self::is_deferred]) + is_deferred: bool, // Type we track for self self_type: Type, @@ -585,26 +640,35 @@ impl BitVector { self.push_uint(val as u64, 8); } + fn push_u5(&mut self, val: u8) { + assert!(val <= 0b11111); + self.push_uint(val as u64, 5); + } + fn push_u4(&mut self, val: u8) { - assert!(val < 16); + assert!(val <= 0b1111); self.push_uint(val as u64, 4); } fn push_u3(&mut self, val: u8) { - assert!(val < 8); + assert!(val <= 0b111); self.push_uint(val as u64, 3); } fn push_u2(&mut self, val: u8) { - assert!(val < 4); + assert!(val <= 0b11); self.push_uint(val as u64, 2); } fn push_u1(&mut self, val: u8) { - assert!(val < 2); + assert!(val <= 0b1); self.push_uint(val as u64, 1); } + fn push_bool(&mut self, val: bool) { + self.push_u1(if val { 1 } else { 0 }); + } + // Push a context encoding opcode fn push_op(&mut self, op: CtxOp) { self.push_u4(op as u8); @@ -650,6 +714,10 @@ impl BitVector { self.read_uint(bit_idx, 8) as u8 } + fn read_u5(&self, bit_idx: &mut usize) -> u8 { + self.read_uint(bit_idx, 5) as u8 + } + fn read_u4(&self, bit_idx: &mut usize) -> u8 { self.read_uint(bit_idx, 4) as u8 } @@ -666,6 +734,10 @@ impl BitVector { self.read_uint(bit_idx, 1) as u8 } + fn read_bool(&self, bit_idx: &mut usize) -> bool { + self.read_u1(bit_idx) != 0 + } + fn read_op(&self, bit_idx: &mut usize) -> CtxOp { unsafe { std::mem::transmute(self.read_u4(bit_idx)) } } @@ -786,7 +858,7 @@ mod bitvector_tests { let idx0 = ctx0.encode_into(&mut bits); let mut ctx1 = Context::default(); - ctx1.reg_temps = RegTemps(1); + ctx1.reg_mapping = RegMapping([Some(RegOpnd::Stack(0)), None, None, None, None]); let idx1 = ctx1.encode_into(&mut bits); // Make sure that we can encode two contexts successively @@ -797,10 +869,10 @@ mod bitvector_tests { } #[test] - fn regress_reg_temps() { + fn regress_reg_mapping() { let mut bits = BitVector::new(); let mut ctx = Context::default(); - ctx.reg_temps = RegTemps(1); + ctx.reg_mapping = RegMapping([Some(RegOpnd::Stack(0)), None, None, None, None]); ctx.encode_into(&mut bits); let b0 = bits.read_u1(&mut 0); @@ -842,7 +914,7 @@ enum CtxOp { } // Number of entries in the context cache -const CTX_CACHE_SIZE: usize = 512; +const CTX_CACHE_SIZE: usize = 1024; // Cache of the last contexts encoded // Empirically this saves a few percent of memory @@ -854,6 +926,8 @@ static mut CTX_CACHE: Option> = None; pub const CTX_CACHE_BYTES: usize = std::mem::size_of::(); impl Context { + // Encode a context into the global context data, or return + // a cached previously encoded offset if one is found pub fn encode(&self) -> u32 { incr_counter!(num_contexts_encoded); @@ -879,6 +953,7 @@ impl Context { let idx = self.encode_into(context_data); let idx: u32 = idx.try_into().unwrap(); + // Save this offset into the cache Self::cache_set(self, idx); // In debug mode, check that the round-trip decoding always matches @@ -903,16 +978,21 @@ impl Context { // Store an entry in a cache of recently encoded/decoded contexts fn cache_set(ctx: &Context, idx: u32) { + // Compute the hash for this context + let mut hasher = DefaultHasher::new(); + ctx.hash(&mut hasher); + let ctx_hash = hasher.finish() as usize; + unsafe { + // Lazily initialize the context cache if CTX_CACHE == None { - let empty_tbl = [(Context::default(), 0); CTX_CACHE_SIZE]; - CTX_CACHE = Some(Box::new(empty_tbl)); + // Here we use the vec syntax to avoid allocating the large table on the stack, + // as this can cause a stack overflow + let tbl = vec![(Context::default(), 0); CTX_CACHE_SIZE].into_boxed_slice().try_into().unwrap(); + CTX_CACHE = Some(tbl); } - let mut hasher = DefaultHasher::new(); - ctx.hash(&mut hasher); - let ctx_hash = hasher.finish() as usize; - + // Write a cache entry for this context let cache = CTX_CACHE.as_mut().unwrap(); cache[ctx_hash % CTX_CACHE_SIZE] = (*ctx, idx); } @@ -921,6 +1001,11 @@ impl Context { // Lookup the context in a cache of recently encoded/decoded contexts fn cache_get(ctx: &Context) -> Option { + // Compute the hash for this context + let mut hasher = DefaultHasher::new(); + ctx.hash(&mut hasher); + let ctx_hash = hasher.finish() as usize; + unsafe { if CTX_CACHE == None { return None; @@ -928,12 +1013,10 @@ impl Context { let cache = CTX_CACHE.as_mut().unwrap(); - let mut hasher = DefaultHasher::new(); - ctx.hash(&mut hasher); - let ctx_hash = hasher.finish() as usize; + // Check that the context for this cache entry mmatches let cache_entry = &cache[ctx_hash % CTX_CACHE_SIZE]; - if cache_entry.0 == *ctx { + debug_assert!(cache_entry.1 != 0); return Some(cache_entry.1); } @@ -948,6 +1031,7 @@ impl Context { // Most of the time, the stack size is small and sp offset has the same value if (self.stack_size as i64) == (self.sp_offset as i64) && self.stack_size < 4 { // One single bit to signify a compact stack_size/sp_offset encoding + debug_assert!(self.sp_offset >= 0); bits.push_u1(1); bits.push_u2(self.stack_size); } else { @@ -961,12 +1045,37 @@ impl Context { bits.push_u8(self.sp_offset as u8); } - // Bitmap of which stack temps are in a register - let RegTemps(reg_temps) = self.reg_temps; - bits.push_u8(reg_temps); + // Which stack temps or locals are in a register + for &temp in self.reg_mapping.0.iter() { + if let Some(temp) = temp { + bits.push_u1(1); // Some + match temp { + RegOpnd::Stack(stack_idx) => { + bits.push_u1(0); // Stack + bits.push_u3(stack_idx); + } + RegOpnd::Local(local_idx) => { + bits.push_u1(1); // Local + bits.push_u3(local_idx); + } + } + } else { + bits.push_u1(0); // None + } + } - // chain_depth_and_flags: u8, - bits.push_u8(self.chain_depth_and_flags); + bits.push_bool(self.is_deferred); + bits.push_bool(self.is_return_landing); + + // The chain depth is most often 0 or 1 + if self.chain_depth < 2 { + bits.push_u1(0); + bits.push_u1(self.chain_depth); + + } else { + bits.push_u1(1); + bits.push_u5(self.chain_depth); + } // Encode the self type if known if self.self_type != Type::Unknown { @@ -975,7 +1084,7 @@ impl Context { } // Encode the local types if known - for local_idx in 0..MAX_LOCAL_TYPES { + for local_idx in 0..MAX_CTX_LOCALS { let t = self.get_local_type(local_idx); if t != Type::Unknown { bits.push_op(CtxOp::SetLocalType); @@ -985,7 +1094,7 @@ impl Context { } // Encode stack temps - for stack_idx in 0..MAX_TEMP_TYPES { + for stack_idx in 0..MAX_CTX_TEMPS { let mapping = self.get_temp_mapping(stack_idx); match mapping.get_kind() { @@ -1040,14 +1149,33 @@ impl Context { ctx.sp_offset = ctx.stack_size as i8; } else { ctx.stack_size = bits.read_u8(&mut idx); - ctx.sp_offset = bits.read_u8(&mut idx) as i8; + let sp_offset_bits = bits.read_u8(&mut idx); + ctx.sp_offset = sp_offset_bits as i8; + + // If the top bit is set, then the sp offset must be negative + debug_assert!(!( (sp_offset_bits & 0x80) != 0 && ctx.sp_offset > 0 )); } - // Bitmap of which stack temps are in a register - ctx.reg_temps = RegTemps(bits.read_u8(&mut idx)); + // Which stack temps or locals are in a register + for index in 0..MAX_MAPPED_REGS { + if bits.read_u1(&mut idx) == 1 { // Some + let temp = if bits.read_u1(&mut idx) == 0 { // RegMapping::Stack + RegOpnd::Stack(bits.read_u3(&mut idx)) + } else { + RegOpnd::Local(bits.read_u3(&mut idx)) + }; + ctx.reg_mapping.0[index] = Some(temp); + } + } - // chain_depth_and_flags: u8 - ctx.chain_depth_and_flags = bits.read_u8(&mut idx); + ctx.is_deferred = bits.read_bool(&mut idx); + ctx.is_return_landing = bits.read_bool(&mut idx); + + if bits.read_u1(&mut idx) == 0 { + ctx.chain_depth = bits.read_u1(&mut idx) + } else { + ctx.chain_depth = bits.read_u5(&mut idx) + } loop { //println!("reading op"); @@ -1396,7 +1524,7 @@ impl PendingBranch { target_idx: u32, target: BlockId, ctx: &Context, - ocb: &mut OutlinedCb, + jit: &mut JITState, ) -> Option { // If the block already exists if let Some(blockref) = find_block_version(target, ctx) { @@ -1414,7 +1542,7 @@ impl PendingBranch { // The branch struct is uninitialized right now but as a stable address. // We make sure the stub runs after the branch is initialized. let branch_struct_addr = self.uninit_branch.as_ptr() as usize; - let stub_addr = gen_branch_stub(ctx, ocb, branch_struct_addr, target_idx); + let stub_addr = gen_branch_stub(ctx, jit.iseq, jit.get_ocb(), branch_struct_addr, target_idx); if let Some(stub_addr) = stub_addr { // Fill the branch target with a stub @@ -2346,7 +2474,7 @@ impl Context { let mut generic_ctx = Context::default(); generic_ctx.stack_size = self.stack_size; generic_ctx.sp_offset = self.sp_offset; - generic_ctx.reg_temps = self.reg_temps; + generic_ctx.reg_mapping = self.reg_mapping; if self.is_return_landing() { generic_ctx.set_as_return_landing(); } @@ -2374,48 +2502,48 @@ impl Context { self.sp_offset = offset; } - pub fn get_reg_temps(&self) -> RegTemps { - self.reg_temps + pub fn get_reg_mapping(&self) -> RegMapping { + self.reg_mapping } - pub fn set_reg_temps(&mut self, reg_temps: RegTemps) { - self.reg_temps = reg_temps; + pub fn set_reg_mapping(&mut self, reg_mapping: RegMapping) { + self.reg_mapping = reg_mapping; } pub fn get_chain_depth(&self) -> u8 { - self.chain_depth_and_flags & CHAIN_DEPTH_MASK + self.chain_depth } pub fn reset_chain_depth_and_defer(&mut self) { - self.chain_depth_and_flags &= !CHAIN_DEPTH_MASK; - self.chain_depth_and_flags &= !DEFER_BIT; + self.chain_depth = 0; + self.is_deferred = false; } pub fn increment_chain_depth(&mut self) { - if self.get_chain_depth() == CHAIN_DEPTH_MASK { + if self.get_chain_depth() == CHAIN_DEPTH_MAX { panic!("max block version chain depth reached!"); } - self.chain_depth_and_flags += 1; + self.chain_depth += 1; } pub fn set_as_return_landing(&mut self) { - self.chain_depth_and_flags |= RETURN_LANDING_BIT; + self.is_return_landing = true; } pub fn clear_return_landing(&mut self) { - self.chain_depth_and_flags &= !RETURN_LANDING_BIT; + self.is_return_landing = false; } pub fn is_return_landing(&self) -> bool { - self.chain_depth_and_flags & RETURN_LANDING_BIT != 0 + self.is_return_landing } pub fn mark_as_deferred(&mut self) { - self.chain_depth_and_flags |= DEFER_BIT; + self.is_deferred = true; } pub fn is_deferred(&self) -> bool { - self.chain_depth_and_flags & DEFER_BIT != 0 + self.is_deferred } /// Get an operand for the adjusted stack pointer address @@ -2431,14 +2559,13 @@ impl Context { self.sp_opnd(-ep_offset + offset) } - /// Stop using a register for a given stack temp. + /// Stop using a register for a given stack temp or a local. /// 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). - pub fn dealloc_temp_reg(&mut self, stack_idx: u8) { - if stack_idx < MAX_REG_TEMPS { - let mut reg_temps = self.get_reg_temps(); - reg_temps.set(stack_idx, false); - self.set_reg_temps(reg_temps); + pub fn dealloc_reg(&mut self, opnd: RegOpnd) { + let mut reg_mapping = self.get_reg_mapping(); + if reg_mapping.dealloc_reg(opnd) { + self.set_reg_mapping(reg_mapping); } } @@ -2451,7 +2578,7 @@ impl Context { let stack_idx: usize = (self.stack_size - 1 - idx).into(); // If outside of tracked range, do nothing - if stack_idx >= MAX_TEMP_TYPES { + if stack_idx >= MAX_CTX_TEMPS { return Type::Unknown; } @@ -2462,7 +2589,7 @@ impl Context { MapToStack => mapping.get_type(), MapToLocal => { let idx = mapping.get_local_idx(); - assert!((idx as usize) < MAX_LOCAL_TYPES); + assert!((idx as usize) < MAX_CTX_LOCALS); return self.get_local_type(idx.into()); } } @@ -2472,7 +2599,7 @@ impl Context { /// Get the currently tracked type for a local variable pub fn get_local_type(&self, local_idx: usize) -> Type { - if local_idx >= MAX_LOCAL_TYPES { + if local_idx >= MAX_CTX_LOCALS { return Type::Unknown } else { // Each type is stored in 4 bits @@ -2483,7 +2610,7 @@ impl Context { /// Get the current temp mapping for a given stack slot fn get_temp_mapping(&self, temp_idx: usize) -> TempMapping { - assert!(temp_idx < MAX_TEMP_TYPES); + assert!(temp_idx < MAX_CTX_TEMPS); // Extract the temp mapping kind let kind_bits = (self.temp_mapping_kind >> (2 * temp_idx)) & 0b11; @@ -2511,7 +2638,7 @@ impl Context { /// Get the current temp mapping for a given stack slot fn set_temp_mapping(&mut self, temp_idx: usize, mapping: TempMapping) { - assert!(temp_idx < MAX_TEMP_TYPES); + assert!(temp_idx < MAX_CTX_TEMPS); // Extract the kind bits let mapping_kind = mapping.get_kind(); @@ -2567,7 +2694,7 @@ impl Context { let stack_idx = (self.stack_size - 1 - idx) as usize; // If outside of tracked range, do nothing - if stack_idx >= MAX_TEMP_TYPES { + if stack_idx >= MAX_CTX_TEMPS { return; } @@ -2582,7 +2709,7 @@ impl Context { } MapToLocal => { let idx = mapping.get_local_idx() as usize; - assert!(idx < MAX_LOCAL_TYPES); + assert!(idx < MAX_CTX_LOCALS); let mut new_type = self.get_local_type(idx); new_type.upgrade(opnd_type); self.set_local_type(idx, new_type); @@ -2609,7 +2736,7 @@ impl Context { assert!(idx < self.stack_size); let stack_idx = (self.stack_size - 1 - idx) as usize; - if stack_idx < MAX_TEMP_TYPES { + if stack_idx < MAX_CTX_TEMPS { self.get_temp_mapping(stack_idx) } else { // We can't know the source of this stack operand, so we assume it is @@ -2635,7 +2762,7 @@ impl Context { } // If outside of tracked range, do nothing - if stack_idx >= MAX_TEMP_TYPES { + if stack_idx >= MAX_CTX_TEMPS { return; } @@ -2651,12 +2778,12 @@ impl Context { return; } - if local_idx >= MAX_LOCAL_TYPES { + if local_idx >= MAX_CTX_LOCALS { return } // If any values on the stack map to this local we must detach them - for mapping_idx in 0..MAX_TEMP_TYPES { + for mapping_idx in 0..MAX_CTX_TEMPS { let mapping = self.get_temp_mapping(mapping_idx); let tm = match mapping.get_kind() { MapToStack => mapping, @@ -2688,7 +2815,7 @@ impl Context { // When clearing local types we must detach any stack mappings to those // locals. Even if local values may have changed, stack values will not. - for mapping_idx in 0..MAX_TEMP_TYPES { + for mapping_idx in 0..MAX_CTX_TEMPS { let mapping = self.get_temp_mapping(mapping_idx); if mapping.get_kind() == MapToLocal { let local_idx = mapping.get_local_idx() as usize; @@ -2742,7 +2869,7 @@ impl Context { return TypeDiff::Incompatible; } - if dst.reg_temps != src.reg_temps { + if dst.reg_mapping != src.reg_mapping { return TypeDiff::Incompatible; } @@ -2763,7 +2890,7 @@ impl Context { } // For each local type we track - for i in 0.. MAX_LOCAL_TYPES { + for i in 0.. MAX_CTX_LOCALS { let t_src = src.get_local_type(i); let t_dst = dst.get_local_type(i); diff += match t_src.diff(t_dst) { @@ -2829,24 +2956,23 @@ impl Assembler { let stack_size: usize = self.ctx.stack_size.into(); // Keep track of the type and mapping of the value - if stack_size < MAX_TEMP_TYPES { + if stack_size < MAX_CTX_TEMPS { self.ctx.set_temp_mapping(stack_size, mapping); if mapping.get_kind() == MapToLocal { let idx = mapping.get_local_idx(); - assert!((idx as usize) < MAX_LOCAL_TYPES); + assert!((idx as usize) < MAX_CTX_LOCALS); } } - // Allocate a register to the stack operand - if self.ctx.stack_size < MAX_REG_TEMPS { - self.alloc_temp_reg(self.ctx.stack_size); - } - self.ctx.stack_size += 1; self.ctx.sp_offset += 1; - return self.stack_opnd(0); + // Allocate a register to the new stack operand + let stack_opnd = self.stack_opnd(0); + self.alloc_reg(stack_opnd.reg_opnd()); + + stack_opnd } /// Push one new value on the temp stack @@ -2862,7 +2988,7 @@ impl Assembler { /// Push a local variable on the stack pub fn stack_push_local(&mut self, local_idx: usize) -> Opnd { - if local_idx >= MAX_LOCAL_TYPES { + if local_idx >= MAX_CTX_LOCALS { return self.stack_push(Type::Unknown); } @@ -2880,7 +3006,7 @@ impl Assembler { for i in 0..n { let idx: usize = (self.ctx.stack_size as usize) - i - 1; - if idx < MAX_TEMP_TYPES { + if idx < MAX_CTX_TEMPS { self.ctx.set_temp_mapping(idx, TempMapping::map_to_stack(Type::Unknown)); } } @@ -2898,8 +3024,8 @@ impl Assembler { let method_name_index = (self.ctx.stack_size as usize) - argc - 1; for i in method_name_index..(self.ctx.stack_size - 1) as usize { - if i < MAX_TEMP_TYPES { - let next_arg_mapping = if i + 1 < MAX_TEMP_TYPES { + if i < MAX_CTX_TEMPS { + let next_arg_mapping = if i + 1 < MAX_CTX_TEMPS { self.ctx.get_temp_mapping(i + 1) } else { TempMapping::map_to_stack(Type::Unknown) @@ -2916,8 +3042,22 @@ impl Assembler { idx, num_bits: 64, stack_size: self.ctx.stack_size, + num_locals: None, // not needed for stack temps + sp_offset: self.ctx.sp_offset, + reg_mapping: None, // push_insn will set this + } + } + + /// Get an operand pointing to a local variable + pub fn local_opnd(&self, ep_offset: u32) -> Opnd { + let idx = self.ctx.stack_size as i32 + ep_offset as i32; + Opnd::Stack { + idx, + num_bits: 64, + stack_size: self.ctx.stack_size, + num_locals: Some(self.get_num_locals().unwrap()), // this must exist for locals sp_offset: self.ctx.sp_offset, - reg_temps: None, // push_insn will set this + reg_mapping: None, // push_insn will set this } } } @@ -3116,7 +3256,7 @@ pub fn gen_entry_point(iseq: IseqPtr, ec: EcPtr, jit_exception: bool) -> Option< // Change the entry's jump target from an entry stub to a next entry pub fn regenerate_entry(cb: &mut CodeBlock, entryref: &EntryRef, next_entry: CodePtr) { - let mut asm = Assembler::new(); + let mut asm = Assembler::new_without_iseq(); asm_comment!(asm, "regenerate_entry"); // gen_entry_guard generates cmp + jne. We're rewriting only jne. @@ -3192,7 +3332,7 @@ fn entry_stub_hit_body( // Compile a new entry guard as a next entry let next_entry = cb.get_write_ptr(); - let mut asm = Assembler::new(); + let mut asm = Assembler::new_without_iseq(); let pending_entry = gen_entry_chain_guard(&mut asm, ocb, iseq, insn_idx)?; asm.compile(cb, Some(ocb))?; @@ -3203,7 +3343,7 @@ fn entry_stub_hit_body( let blockref = match find_block_version(blockid, &ctx) { // If an existing block is found, generate a jump to the block. Some(blockref) => { - let mut asm = Assembler::new(); + let mut asm = Assembler::new_without_iseq(); asm.jmp(unsafe { blockref.as_ref() }.start_addr.into()); asm.compile(cb, Some(ocb))?; Some(blockref) @@ -3231,7 +3371,7 @@ fn entry_stub_hit_body( pub fn gen_entry_stub(entry_address: usize, ocb: &mut OutlinedCb) -> Option { let ocb = ocb.unwrap(); - let mut asm = Assembler::new(); + let mut asm = Assembler::new_without_iseq(); asm_comment!(asm, "entry stub hit"); asm.mov(C_ARG_OPNDS[0], entry_address.into()); @@ -3247,7 +3387,7 @@ pub fn gen_entry_stub(entry_address: usize, ocb: &mut OutlinedCb) -> Option Option { let ocb = ocb.unwrap(); - let mut asm = Assembler::new(); + let mut asm = Assembler::new_without_iseq(); // See gen_entry_guard for how it's used. asm_comment!(asm, "entry_stub_hit() trampoline"); @@ -3270,7 +3410,7 @@ fn regenerate_branch(cb: &mut CodeBlock, branch: &Branch) { let branch_terminates_block = branch.end_addr.get() == block.get_end_addr(); // Generate the branch - let mut asm = Assembler::new(); + let mut asm = Assembler::new_without_iseq(); asm_comment!(asm, "regenerate_branch"); branch.gen_fn.call( &mut asm, @@ -3581,15 +3721,16 @@ fn delete_empty_defer_block(branch: &Branch, new_block: &Block, target_ctx: Cont /// A piece of code that redeems for more code; a thunk for code. fn gen_branch_stub( ctx: u32, + iseq: IseqPtr, ocb: &mut OutlinedCb, branch_struct_address: usize, target_idx: u32, ) -> Option { let ocb = ocb.unwrap(); - let mut asm = Assembler::new(); + let mut asm = Assembler::new(unsafe { get_iseq_body_local_table_size(iseq) }); asm.ctx = Context::decode(ctx); - asm.set_reg_temps(asm.ctx.reg_temps); + asm.set_reg_mapping(asm.ctx.reg_mapping); asm_comment!(asm, "branch stub hit"); if asm.ctx.is_return_landing() { @@ -3605,7 +3746,7 @@ fn gen_branch_stub( } // Spill temps to the VM stack as well for jit.peek_at_stack() - asm.spill_temps(); + asm.spill_regs(); // Set up the arguments unique to this stub for: // @@ -3625,7 +3766,7 @@ fn gen_branch_stub( pub fn gen_branch_stub_hit_trampoline(ocb: &mut OutlinedCb) -> Option { let ocb = ocb.unwrap(); - let mut asm = Assembler::new(); + let mut asm = Assembler::new_without_iseq(); // For `branch_stub_hit(branch_ptr, target_idx, ec)`, // `branch_ptr` and `target_idx` is different for each stub, @@ -3662,7 +3803,7 @@ pub fn gen_branch_stub_hit_trampoline(ocb: &mut OutlinedCb) -> Option { /// Return registers to be pushed and popped on branch_stub_hit. pub fn caller_saved_temp_regs() -> impl Iterator + DoubleEndedIterator { - let temp_regs = Assembler::get_temp_regs().iter(); + let temp_regs = Assembler::get_temp_regs2().iter(); let len = temp_regs.len(); // The return value gen_leave() leaves in C_RET_REG // needs to survive the branch_stub_hit() call. @@ -3736,12 +3877,11 @@ pub fn gen_branch( gen_fn: BranchGenFn, ) { let branch = new_pending_branch(jit, gen_fn); - let ocb = jit.get_ocb(); // Get the branch targets or stubs - let target0_addr = branch.set_target(0, target0, ctx0, ocb); + let target0_addr = branch.set_target(0, target0, ctx0, jit); let target1_addr = if let Some(ctx) = ctx1 { - let addr = branch.set_target(1, target1.unwrap(), ctx, ocb); + let addr = branch.set_target(1, target1.unwrap(), ctx, jit); if addr.is_none() { // target1 requested but we're out of memory. // Avoid unwrap() in gen_fn() @@ -3816,7 +3956,7 @@ pub fn defer_compilation( }; // Likely a stub since the context is marked as deferred(). - let target0_address = branch.set_target(0, blockid, &next_ctx, jit.get_ocb()); + let target0_address = branch.set_target(0, blockid, &next_ctx, jit); // 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. @@ -4000,7 +4140,7 @@ pub fn invalidate_block_version(blockref: &BlockRef) { let cur_dropped_bytes = cb.has_dropped_bytes(); cb.set_write_ptr(block_start); - let mut asm = Assembler::new(); + let mut asm = Assembler::new_without_iseq(); asm.jmp(block_entry_exit.as_side_exit()); cb.set_dropped_bytes(false); asm.compile(&mut cb, Some(ocb)).expect("can rewrite existing code"); @@ -4037,7 +4177,7 @@ pub fn invalidate_block_version(blockref: &BlockRef) { } // Create a stub for this branch target - let stub_addr = gen_branch_stub(block.ctx, ocb, branchref.as_ptr() as usize, target_idx as u32); + let stub_addr = gen_branch_stub(block.ctx, block.iseq.get(), ocb, branchref.as_ptr() as usize, target_idx as u32); // In case we were unable to generate a stub (e.g. OOM). Use the block's // exit instead of a stub for the block. It's important that we @@ -4171,7 +4311,7 @@ mod tests { // and all local types in 32 bits assert_eq!(mem::size_of::(), 1); assert!(Type::BlockParamProxy as usize <= 0b1111); - assert!(MAX_LOCAL_TYPES * 4 <= 32); + assert!(MAX_CTX_LOCALS * 4 <= 32); } #[test] @@ -4183,7 +4323,7 @@ mod tests { fn local_types() { let mut ctx = Context::default(); - for i in 0..MAX_LOCAL_TYPES { + for i in 0..MAX_CTX_LOCALS { ctx.set_local_type(i, Type::Fixnum); assert_eq!(ctx.get_local_type(i), Type::Fixnum); ctx.set_local_type(i, Type::BlockParamProxy); @@ -4235,41 +4375,30 @@ mod tests { } #[test] - fn reg_temps() { - let mut reg_temps = RegTemps(0); + fn reg_mapping() { + let mut reg_mapping = RegMapping([None, None, None, None, None]); // 0 means every slot is not spilled - for stack_idx in 0..MAX_REG_TEMPS { - assert_eq!(reg_temps.get(stack_idx), false); + for stack_idx in 0..MAX_CTX_TEMPS as u8 { + assert_eq!(reg_mapping.get_reg(RegOpnd::Stack(stack_idx)), None); } - // Set 0, 2, 7 (RegTemps: 10100001) - reg_temps.set(0, true); - reg_temps.set(2, true); - reg_temps.set(3, true); - reg_temps.set(3, false); - reg_temps.set(7, true); + // Set 0, 2, 6 (RegMapping: [Some(0), Some(6), Some(2), None, None]) + reg_mapping.alloc_reg(RegOpnd::Stack(0)); + reg_mapping.alloc_reg(RegOpnd::Stack(2)); + reg_mapping.alloc_reg(RegOpnd::Stack(3)); + reg_mapping.dealloc_reg(RegOpnd::Stack(3)); + reg_mapping.alloc_reg(RegOpnd::Stack(6)); // Get 0..8 - assert_eq!(reg_temps.get(0), true); - assert_eq!(reg_temps.get(1), false); - assert_eq!(reg_temps.get(2), true); - assert_eq!(reg_temps.get(3), false); - assert_eq!(reg_temps.get(4), false); - assert_eq!(reg_temps.get(5), false); - assert_eq!(reg_temps.get(6), false); - assert_eq!(reg_temps.get(7), true); - - // Test conflicts - assert_eq!(5, get_option!(num_temp_regs)); - assert_eq!(reg_temps.conflicts_with(0), false); // already set, but no conflict - assert_eq!(reg_temps.conflicts_with(1), false); - assert_eq!(reg_temps.conflicts_with(2), true); // already set, and conflicts with 7 - assert_eq!(reg_temps.conflicts_with(3), false); - assert_eq!(reg_temps.conflicts_with(4), false); - assert_eq!(reg_temps.conflicts_with(5), true); // not set, and will conflict with 0 - assert_eq!(reg_temps.conflicts_with(6), false); - assert_eq!(reg_temps.conflicts_with(7), true); // already set, and conflicts with 2 + assert_eq!(reg_mapping.get_reg(RegOpnd::Stack(0)), Some(0)); + assert_eq!(reg_mapping.get_reg(RegOpnd::Stack(1)), None); + assert_eq!(reg_mapping.get_reg(RegOpnd::Stack(2)), Some(2)); + assert_eq!(reg_mapping.get_reg(RegOpnd::Stack(3)), None); + assert_eq!(reg_mapping.get_reg(RegOpnd::Stack(4)), None); + assert_eq!(reg_mapping.get_reg(RegOpnd::Stack(5)), None); + assert_eq!(reg_mapping.get_reg(RegOpnd::Stack(6)), Some(1)); + assert_eq!(reg_mapping.get_reg(RegOpnd::Stack(7)), None); } #[test] @@ -4278,7 +4407,7 @@ mod tests { assert_eq!(Context::default().diff(&Context::default()), TypeDiff::Compatible(0)); // Try pushing an operand and getting its type - let mut asm = Assembler::new(); + let mut asm = Assembler::new(0); asm.stack_push(Type::Fixnum); let top_type = asm.ctx.get_opnd_type(StackOpnd(0)); assert!(top_type == Type::Fixnum); @@ -4288,7 +4417,7 @@ mod tests { #[test] fn context_upgrade_local() { - let mut asm = Assembler::new(); + let mut asm = Assembler::new(0); asm.stack_push_local(0); asm.ctx.upgrade_opnd_type(StackOpnd(0), Type::Nil); assert_eq!(Type::Nil, asm.ctx.get_opnd_type(StackOpnd(0))); @@ -4322,7 +4451,7 @@ mod tests { #[test] fn shift_stack_for_send() { - let mut asm = Assembler::new(); + let mut asm = Assembler::new(0); // Push values to simulate send(:name, arg) with 6 items already on-stack for _ in 0..6 { diff --git a/yjit/src/disasm.rs b/yjit/src/disasm.rs index fb5c46e7cea293..89da07beda46b2 100644 --- a/yjit/src/disasm.rs +++ b/yjit/src/disasm.rs @@ -1,14 +1,10 @@ use crate::core::*; use crate::cruby::*; use crate::yjit::yjit_enabled_p; -#[cfg(feature = "disasm")] use crate::asm::CodeBlock; -#[cfg(feature = "disasm")] use crate::codegen::CodePtr; -#[cfg(feature = "disasm")] use crate::options::DumpDisasm; -#[cfg(feature = "disasm")] use std::fmt::Write; /// Primitive called in yjit.rb @@ -23,11 +19,6 @@ pub extern "C" fn rb_yjit_disasm_iseq(_ec: EcPtr, _ruby_self: VALUE, iseqw: VALU #[cfg(feature = "disasm")] { - // TODO: - //if unsafe { CLASS_OF(iseqw) != rb_cISeq } { - // return Qnil; - //} - if !yjit_enabled_p() { return Qnil; } @@ -116,7 +107,6 @@ pub fn disasm_iseq_insn_range(iseq: IseqPtr, start_idx: u16, end_idx: u16) -> St } /// Dump dissassembly for a range in a [CodeBlock]. VM lock required. -#[cfg(feature = "disasm")] pub fn dump_disasm_addr_range(cb: &CodeBlock, start_addr: CodePtr, end_addr: CodePtr, dump_disasm: &DumpDisasm) { for (start_addr, end_addr) in cb.writable_addrs(start_addr, end_addr) { let disasm = disasm_addr_range(cb, start_addr, end_addr); @@ -192,6 +182,45 @@ pub fn disasm_addr_range(cb: &CodeBlock, start_addr: usize, end_addr: usize) -> return out; } +/// Fallback version without dependency on a disassembler which prints just bytes and comments. +#[cfg(not(feature = "disasm"))] +pub fn disasm_addr_range(cb: &CodeBlock, start_addr: usize, end_addr: usize) -> String { + let mut out = String::new(); + let mut line_byte_idx = 0; + const MAX_BYTES_PER_LINE: usize = 16; + + for addr in start_addr..end_addr { + if let Some(comment_list) = cb.comments_at(addr) { + // Start a new line if we're in the middle of one + if line_byte_idx != 0 { + writeln!(&mut out).unwrap(); + line_byte_idx = 0; + } + for comment in comment_list { + writeln!(&mut out, " \x1b[1m# {comment}\x1b[22m").unwrap(); // Make comments bold + } + } + if line_byte_idx == 0 { + write!(&mut out, " 0x{addr:x}: ").unwrap(); + } else { + write!(&mut out, " ").unwrap(); + } + let byte = unsafe { (addr as *const u8).read() }; + write!(&mut out, "{byte:02x}").unwrap(); + line_byte_idx += 1; + if line_byte_idx == MAX_BYTES_PER_LINE - 1 { + writeln!(&mut out).unwrap(); + line_byte_idx = 0; + } + } + + if !out.is_empty() { + writeln!(&mut out).unwrap(); + } + + out +} + /// Assert that CodeBlock has the code specified with hex. In addition, if tested with /// `cargo test --all-features`, it also checks it generates the specified disasm. #[cfg(test)] @@ -264,43 +293,36 @@ pub fn unindent(string: &str, trim_lines: bool) -> String { /// Produce a list of instructions compiled for an isew #[no_mangle] pub extern "C" fn rb_yjit_insns_compiled(_ec: EcPtr, _ruby_self: VALUE, iseqw: VALUE) -> VALUE { - { - // TODO: - //if unsafe { CLASS_OF(iseqw) != rb_cISeq } { - // return Qnil; - //} - - if !yjit_enabled_p() { - return Qnil; - } - - // Get the iseq pointer from the wrapper - let iseq = unsafe { rb_iseqw_to_iseq(iseqw) }; + if !yjit_enabled_p() { + return Qnil; + } - // Get the list of instructions compiled - let insn_vec = insns_compiled(iseq); + // Get the iseq pointer from the wrapper + let iseq = unsafe { rb_iseqw_to_iseq(iseqw) }; - unsafe { - let insn_ary = rb_ary_new_capa((insn_vec.len() * 2) as i64); + // Get the list of instructions compiled + let insn_vec = insns_compiled(iseq); - // For each instruction compiled - for idx in 0..insn_vec.len() { - let op_name = &insn_vec[idx].0; - let insn_idx = insn_vec[idx].1; + unsafe { + let insn_ary = rb_ary_new_capa((insn_vec.len() * 2) as i64); - let op_sym = rust_str_to_sym(&op_name); + // For each instruction compiled + for idx in 0..insn_vec.len() { + let op_name = &insn_vec[idx].0; + let insn_idx = insn_vec[idx].1; - // Store the instruction index and opcode symbol - rb_ary_store( - insn_ary, - (2 * idx + 0) as i64, - VALUE::fixnum_from_usize(insn_idx as usize), - ); - rb_ary_store(insn_ary, (2 * idx + 1) as i64, op_sym); - } + let op_sym = rust_str_to_sym(&op_name); - insn_ary + // Store the instruction index and opcode symbol + rb_ary_store( + insn_ary, + (2 * idx + 0) as i64, + VALUE::fixnum_from_usize(insn_idx as usize), + ); + rb_ary_store(insn_ary, (2 * idx + 1) as i64, op_sym); } + + insn_ary } } diff --git a/yjit/src/invariants.rs b/yjit/src/invariants.rs index 0a9a18520f83f0..b1da603b27ad3e 100644 --- a/yjit/src/invariants.rs +++ b/yjit/src/invariants.rs @@ -683,7 +683,7 @@ pub extern "C" fn rb_yjit_tracing_invalidate_all() { cb.set_write_ptr(patch.inline_patch_pos); cb.set_dropped_bytes(false); cb.without_page_end_reserve(|cb| { - let mut asm = crate::backend::ir::Assembler::new(); + let mut asm = crate::backend::ir::Assembler::new_without_iseq(); asm.jmp(patch.outlined_target_pos.as_side_exit()); if asm.compile(cb, None).is_none() { panic!("Failed to apply patch at {:?}", patch.inline_patch_pos); diff --git a/yjit/src/options.rs b/yjit/src/options.rs index 5d654f1ee00eeb..3882c8a78655e0 100644 --- a/yjit/src/options.rs +++ b/yjit/src/options.rs @@ -250,7 +250,7 @@ pub fn parse_option(str_ptr: *const std::os::raw::c_char) -> Option<()> { ("dump-disasm", _) => { if !cfg!(feature = "disasm") { - eprintln!("WARNING: the {} option is only available when YJIT is built in dev mode, i.e. ./configure --enable-yjit=dev", opt_name); + eprintln!("WARNING: the {} option works best when YJIT is built in dev mode, i.e. ./configure --enable-yjit=dev", opt_name); } match opt_val { diff --git a/yjit/src/utils.rs b/yjit/src/utils.rs index d76c5198870ee5..c4b5fbd2e7a7d3 100644 --- a/yjit/src/utils.rs +++ b/yjit/src/utils.rs @@ -273,7 +273,7 @@ mod tests { #[test] fn test_print_int() { - let mut asm = Assembler::new(); + let mut asm = Assembler::new_without_iseq(); let mut cb = CodeBlock::new_dummy(1024); print_int(&mut asm, Opnd::Imm(42)); @@ -282,7 +282,7 @@ mod tests { #[test] fn test_print_str() { - let mut asm = Assembler::new(); + let mut asm = Assembler::new_without_iseq(); let mut cb = CodeBlock::new_dummy(1024); print_str(&mut asm, "Hello, world!");