From 90f81994125200d0640cc3bcf7340700144f804b Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 10 Aug 2025 00:55:34 +0900 Subject: [PATCH 01/15] CI: mingw: Tweak misc system & package info --- .github/workflows/mingw.yml | 50 +++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index ce53e33aaea46c..d74bf062b96842 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -86,25 +86,37 @@ jobs: - name: Misc system & package info working-directory: run: | - # show where - result=true - for e in gcc.exe ragel.exe make.exe libcrypto-3-x64.dll libssl-3-x64.dll; do - echo ::group::$'\e[93m'$e$'\e[m' - where $e || result=false - echo ::endgroup:: - done - # show version - for e in gcc ragel make "openssl version"; do - case "$e" in *" "*) ;; *) e="$e --version";; esac - echo ::group::$'\e[93m'$e$'\e[m' - $e || result=false - echo ::endgroup:: - done - # show packages - echo ::group::$'\e[93m'Packages$'\e[m' - pacman -Qs mingw-w64-ucrt-x86_64-* | sed -n "s,local/mingw-w64-ucrt-x86_64-,,p" - echo ::endgroup:: - $result + group() { echo ::group::$'\e[94;1m'"$*"$'\e[m'; } + endgroup() { echo ::endgroup::; } + + group Path + cygpath -wa / . $(type -p cygpath bash sh) + endgroup + + I() { + group $1 + run Where type -pa $1 && { [ $# -eq 1 ] || run Version "$@"; } || + failed+=($1) + endgroup + } + run() { local w m=$1; shift; w="$("$@")" && show "$m" && indent "$w"; } + indent() { [ -z "$1" ] || echo "$1" | /bin/sed '/^$/!s/^/ /'; } + show() { echo $'\e[96m'"$*"$'\e[m'; } + + failed=() + + I gcc.exe --version + I ragel.exe --version + I make.exe --version + I openssl.exe version + I libcrypto-3-x64.dll + I libssl-3-x64.dll + + group Packages + pacman -Qs mingw-w64-ucrt-x86_64-* | /bin/sed -n "s,local/mingw-w64-ucrt-x86_64-,,p" + endgroup + + [ ${#failed[@]} -eq 0 ] - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: From c1f9f0a7ef9cfb4c57fb90b0a04f8f0274856386 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 10 Aug 2025 15:41:35 +0900 Subject: [PATCH 02/15] CI: windows: Windows-2019 or earlier no longer used --- .github/workflows/windows.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 053c37ec5d9f23..d7c88393be52e0 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -39,7 +39,7 @@ jobs: test_task: test-bundled-gems fail-fast: false - runs-on: windows-${{ matrix.os < 2022 && '2019' || matrix.os }} + runs-on: windows-${{ matrix.os }} if: >- ${{!(false @@ -94,7 +94,7 @@ jobs: - name: Install libraries with vcpkg run: | - vcpkg install --vcpkg-root=C:\Users\runneradmin\scoop\apps\vcpkg\current + vcpkg install --vcpkg-root=%USERPROFILE%\scoop\apps\vcpkg\current working-directory: src - name: Save vcpkg artifact @@ -184,7 +184,7 @@ jobs: - name: Set up Launchable uses: ./.github/actions/launchable/setup with: - os: windows-${{ matrix.os < 2022 && '2019' || matrix.os }} + os: windows-${{ matrix.os }} launchable-token: ${{ secrets.LAUNCHABLE_TOKEN }} builddir: build srcdir: src From cc4eba000b18558dc65fb349dce2b92aa7d1760f Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 10 Aug 2025 01:07:40 +0900 Subject: [PATCH 03/15] Win: Use `@` instead of `echo off` in `vssetup.cmd` `echo off` affects the batch files called from this file as well. --- win32/vssetup.cmd | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/win32/vssetup.cmd b/win32/vssetup.cmd index be77c87b294a6c..1ff0a7d10ab6f4 100755 --- a/win32/vssetup.cmd +++ b/win32/vssetup.cmd @@ -1,27 +1,27 @@ -@echo off -setlocal ENABLEEXTENSIONS +@setlocal ENABLEEXTENSIONS +::- do not `echo off` that affects the called batch files ::- check for vswhere -set vswhere=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe -if not exist "%vswhere%" ( +@set vswhere=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe +@if not exist "%vswhere%" ( echo 1>&2 vswhere.exe not found exit /b 1 ) ::- find the latest build tool and its setup batch file. -set VSDEVCMD= -for /f "delims=" %%I in ('"%vswhere%" -products * -latest -property installationPath') do ( +@set VSDEVCMD= +@for /f "delims=" %%I in ('"%vswhere%" -products * -latest -property installationPath') do @( set VSDEVCMD=%%I\Common7\Tools\VsDevCmd.bat ) -if not defined VSDEVCMD ( +@if not defined VSDEVCMD ( echo 1>&2 Visual Studio not found exit /b 1 ) ::- default to the current processor. -set arch=%PROCESSOR_ARCHITECTURE% +@set arch=%PROCESSOR_ARCHITECTURE% ::- `vsdevcmd.bat` requires arch names to be lowercase -for %%i in (a b c d e f g h i j k l m n o p q r s t u v w x y z) do @( +@for %%i in (a b c d e f g h i j k l m n o p q r s t u v w x y z) do @( call set arch=%%arch:%%i=%%i%% ) -echo on && endlocal && "%VSDEVCMD%" -arch=%arch% -host_arch=%arch% %* +@(endlocal && "%VSDEVCMD%" -arch=%arch% -host_arch=%arch% %*) From a443cd012a352a7660ee22f9468082031f354d47 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 9 Aug 2025 20:26:19 +0900 Subject: [PATCH 04/15] CI: mingw: Set `cmd` as the default shell It is used in more steps than `sh`. --- .github/workflows/mingw.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index d74bf062b96842..ca3d6a28593a1a 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -117,6 +117,7 @@ jobs: endgroup [ ${#failed[@]} -eq 0 ] + shell: sh - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: @@ -135,6 +136,7 @@ jobs: run: > ../src/configure --disable-install-doc --prefix=/. --build=$CHOST --host=$CHOST --target=$CHOST + shell: sh - name: make all timeout-minutes: 30 @@ -157,7 +159,6 @@ jobs: - name: test timeout-minutes: 30 run: make test - shell: cmd env: GNUMAKEFLAGS: '' RUBY_TESTOPTS: '-v --tty=no' @@ -165,7 +166,6 @@ jobs: - name: test-all timeout-minutes: 45 - shell: cmd run: | make ${{ StartsWith(matrix.test_task, 'test/') && matrix.test_task || 'test-all' }} env: @@ -180,7 +180,6 @@ jobs: timeout-minutes: 10 run: | make ${{ StartsWith(matrix.test_task, 'spec/') && matrix.test_task || 'test-spec' }} - shell: cmd if: ${{ matrix.test_task == 'check' || matrix.test_task == 'test-spec' || StartsWith(matrix.test_task, 'spec/') }} - uses: ./src/.github/actions/slack @@ -192,4 +191,4 @@ jobs: defaults: run: working-directory: build - shell: sh + shell: cmd From 4adb6f6969d3f58d3987708201acf4f44b870cc9 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 9 Aug 2025 21:37:08 +0900 Subject: [PATCH 05/15] CI: mingw: Set up msys2 environment variables --- .github/workflows/mingw.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index ca3d6a28593a1a..3e889c56c06fff 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -70,6 +70,24 @@ jobs: with: ruby-version: '3.2' + - name: Set up env + id: setup-env + working-directory: + run: | + $msys2 = ${env:MSYS2_LOCATION} + echo $msys2\usr\bin $msys2\ucrt64\bin | + Tee-Object ${env:GITHUB_PATH} -Append -Encoding utf-8 + + # Use the fast device for the temporary directory. + # %TEMP% is inconsistent with %TMP% and test-all expects they are consistent. + # https://github.com/actions/virtual-environments/issues/712#issuecomment-613004302 + $tmp = ${env:RUNNER_TEMP} + echo HOME=$home TMP=$tmp TEMP=$tmp TMPDIR=$tmp | + Tee-Object ${env:GITHUB_ENV} -Append -Encoding utf-8 + shell: pwsh # cmd.exe does not strip spaces before `|`. + env: + MSYS2_LOCATION: ${{ env.RI_DEVKIT }} + - name: Remove Strawberry Perl pkg-config working-directory: # `pkg-config.bat` included in Strawberry Perl is written in From b0a4e2399b34701d4fc05f9543142b0c73d1fb9b Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 24 Jul 2025 14:01:02 +0900 Subject: [PATCH 06/15] CI: mingw: Use the official actions for msys2 Because ruby/setup-ruby is affected to test result. --- .github/workflows/mingw.yml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index 3e889c56c06fff..1c4cb533d3f3c7 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -65,10 +65,21 @@ jobs: )}} steps: - - name: Set up Ruby & MSYS2 - uses: ruby/setup-ruby@d8d83c3960843afb664e821fed6be52f37da5267 # v1.231.0 + - uses: msys2/setup-msys2@40677d36a502eb2cf0fb808cc9dec31bf6152638 # v2.28.0 + id: msys2 with: - ruby-version: '3.2' + msystem: UCRT64 + update: true + install: >- + git + make + ruby + autoconf + mingw-w64-ucrt-x86_64-gcc + mingw-w64-ucrt-x86_64-ragel + mingw-w64-ucrt-x86_64-openssl + mingw-w64-ucrt-x86_64-libyaml + mingw-w64-ucrt-x86_64-libffi - name: Set up env id: setup-env @@ -86,7 +97,7 @@ jobs: Tee-Object ${env:GITHUB_ENV} -Append -Encoding utf-8 shell: pwsh # cmd.exe does not strip spaces before `|`. env: - MSYS2_LOCATION: ${{ env.RI_DEVKIT }} + MSYS2_LOCATION: ${{ steps.msys2.outputs.msys2-location }} - name: Remove Strawberry Perl pkg-config working-directory: From df11c073f37d6bd17be249a7e3ff0e596fb3f42b Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 10 Aug 2025 15:45:49 +0900 Subject: [PATCH 07/15] [DOC] Fix `vssetup.cmd` arguments --- doc/windows.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/windows.md b/doc/windows.md index 13c797875e6a3d..4ea03d0507d2b7 100644 --- a/doc/windows.md +++ b/doc/windows.md @@ -99,16 +99,18 @@ sh ../../ruby/configure -C --disable-install-doc --with-opt-dir=C:\Users\usernam To cross build arm64 binary: ``` - cmd /k win32\vssetup.cmd -arch arm64 + cmd /k win32\vssetup.cmd -arch=arm64 ``` To cross build x64 binary: ``` - cmd /k win32\vssetup.cmd -arch x64 + cmd /k win32\vssetup.cmd -arch=x64 ``` - See `win32\vssetup.cmd -help` for other command line options. + This batch file is a wrapper of `vsdevcmd.bat` and options are + passed to it as-is. `win32\vssetup.cmd -help` for other command + line options. **Note** building ruby requires following commands. From 5e324ac11c2c9c6712e2cdff37f212367f71e094 Mon Sep 17 00:00:00 2001 From: Erim Icel Date: Sun, 10 Aug 2025 13:50:43 +0100 Subject: [PATCH 08/15] Optimize `str_casecmp` length check using pointer end --- benchmark/string_casecmp.yml | 2 ++ string.c | 6 +++--- test/ruby/test_string.rb | 17 +++++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/benchmark/string_casecmp.yml b/benchmark/string_casecmp.yml index 2354040a049e3f..cc75bc803e3e78 100644 --- a/benchmark/string_casecmp.yml +++ b/benchmark/string_casecmp.yml @@ -20,7 +20,9 @@ benchmark: casecmp-10: lstr10.casecmp(ustr10) casecmp-100: lstr100.casecmp(ustr100) casecmp-1000: lstr1000.casecmp(ustr1000) + casecmp-1000vs10: lstr1000.casecmp(ustr10) casecmp-nonascii1: lnonascii1.casecmp(unonascii1) casecmp-nonascii10: lnonascii10.casecmp(unonascii10) casecmp-nonascii100: lnonascii100.casecmp(unonascii100) casecmp-nonascii1000: lnonascii1000.casecmp(unonascii1000) + casecmp-nonascii1000vs10: lnonascii1000.casecmp(unonascii10) \ No newline at end of file diff --git a/string.c b/string.c index fe848d6a4a821c..96a9f96bd3432b 100644 --- a/string.c +++ b/string.c @@ -4381,9 +4381,9 @@ str_casecmp(VALUE str1, VALUE str2) p2 += l2; } } - if (RSTRING_LEN(str1) == RSTRING_LEN(str2)) return INT2FIX(0); - if (RSTRING_LEN(str1) > RSTRING_LEN(str2)) return INT2FIX(1); - return INT2FIX(-1); + if (p1 == p1end && p2 == p2end) return INT2FIX(0); + if (p1 == p1end) return INT2FIX(-1); + return INT2FIX(1); } /* diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index 8fb57bd58e6111..811785bf7ed3fb 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -2832,9 +2832,12 @@ def test_compare_different_encoding_string def test_casecmp assert_equal(0, S("FoO").casecmp("fOO")) assert_equal(1, S("FoO").casecmp("BaR")) + assert_equal(-1, S("foo").casecmp("FOOBAR")) assert_equal(-1, S("baR").casecmp("FoO")) assert_equal(1, S("\u3042B").casecmp("\u3042a")) assert_equal(-1, S("foo").casecmp("foo\0")) + assert_equal(1, S("FOOBAR").casecmp("foo")) + assert_equal(0, S("foo\0bar").casecmp("FOO\0BAR")) assert_nil(S("foo").casecmp(:foo)) assert_nil(S("foo").casecmp(Object.new)) @@ -2842,6 +2845,16 @@ def test_casecmp o = Object.new def o.to_str; "fOO"; end assert_equal(0, S("FoO").casecmp(o)) + + assert_equal(0, S("#" * 128 + "A" * 256 + "b").casecmp("#" * 128 + "a" * 256 + "B")) + assert_equal(0, S("a" * 256 + "B").casecmp("A" * 256 + "b")) + + assert_equal(-1, S("@").casecmp("`")) + assert_equal(0, S("hello\u00E9X").casecmp("HELLO\u00E9x")) + + s1 = S("\xff".force_encoding("UTF-8")) + s2 = S("\xff".force_encoding("ISO-2022-JP")) + assert_nil(s1.casecmp(s2)) end def test_casecmp? @@ -2857,6 +2870,10 @@ def test_casecmp? o = Object.new def o.to_str; "fOO"; end assert_equal(true, S("FoO").casecmp?(o)) + + s1 = S("\xff".force_encoding("UTF-8")) + s2 = S("\xff".force_encoding("ISO-2022-JP")) + assert_nil(s1.casecmp?(s2)) end def test_upcase2 From 09d6cfc55c9048dfe248be0ca71eb5b8968e6b28 Mon Sep 17 00:00:00 2001 From: Erim Icel Date: Sun, 10 Aug 2025 14:08:53 +0100 Subject: [PATCH 09/15] Update test_string.rb --- test/ruby/test_string.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index 811785bf7ed3fb..c7e4b0c1ec7fee 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -2842,6 +2842,9 @@ def test_casecmp assert_nil(S("foo").casecmp(:foo)) assert_nil(S("foo").casecmp(Object.new)) + assert_nil(S("foo").casecmp(0)) + assert_nil(S("foo").casecmp(5.00)) + o = Object.new def o.to_str; "fOO"; end assert_equal(0, S("FoO").casecmp(o)) @@ -2867,6 +2870,9 @@ def test_casecmp? assert_nil(S("foo").casecmp?(:foo)) assert_nil(S("foo").casecmp?(Object.new)) + assert_nil(S("foo").casecmp(0)) + assert_nil(S("foo").casecmp(5.00)) + o = Object.new def o.to_str; "fOO"; end assert_equal(true, S("FoO").casecmp?(o)) From c914389ae83d0a5b8ddc00d2c1577c937de72743 Mon Sep 17 00:00:00 2001 From: Erim Icel Date: Sun, 10 Aug 2025 15:36:04 +0100 Subject: [PATCH 10/15] Update string_casecmp.yml --- benchmark/string_casecmp.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/string_casecmp.yml b/benchmark/string_casecmp.yml index cc75bc803e3e78..88a3555c8a8837 100644 --- a/benchmark/string_casecmp.yml +++ b/benchmark/string_casecmp.yml @@ -25,4 +25,4 @@ benchmark: casecmp-nonascii10: lnonascii10.casecmp(unonascii10) casecmp-nonascii100: lnonascii100.casecmp(unonascii100) casecmp-nonascii1000: lnonascii1000.casecmp(unonascii1000) - casecmp-nonascii1000vs10: lnonascii1000.casecmp(unonascii10) \ No newline at end of file + casecmp-nonascii1000vs10: lnonascii1000.casecmp(unonascii10) From 62b5fe8984db6c36919ff6f0a69ccca37090ce0c Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Wed, 6 Aug 2025 17:59:15 -0500 Subject: [PATCH 11/15] [DOC] Tweaks for GC.config --- gc.rb | 85 +++++++++++++++++++++++++---------------------------------- 1 file changed, 36 insertions(+), 49 deletions(-) diff --git a/gc.rb b/gc.rb index 1298e3005660b6..54a88cf8f7dbd4 100644 --- a/gc.rb +++ b/gc.rb @@ -258,71 +258,58 @@ def self.stat_heap heap_name = nil, hash_or_key = nil # call-seq: # GC.config -> hash - # GC.config(hash) -> hash + # GC.config(hash_to_merge) -> merged_hash # - # Sets or gets information about the current \GC config. + # This method is expected to be defined, useful, and well-behaved + # only in the CRuby implementation. # - # Configuration parameters are \GC implementation-specific and may change - # without notice. + # Sets or gets information about the current \GC configuration. # - # This method can be called without parameters to retrieve the current config - # as a +Hash+ with +Symbol+ keys. + # With no argument given, returns a hash containing the configuration: # - # 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. + # GC.config + # # => {rgengc_allow_full_mark: true, implementation: "default"} # - # 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 its value will - # be +nil+. This is to facilitate easy migration between \GC implementations. + # With argument +hash_to_merge+ given, + # merges that hash into the stored configuration hash; + # ignores unknown hash keys; + # returns the implementation-specific configuration hash (see below): # - # In both call-seqs, the return value of GC.config will be a +Hash+ - # containing the most recent full configuration, i.e., 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. + # GC.config(rgengc_allow_full_mark: false) + # # => {rgengc_allow_full_mark: false} + # GC.config + # # => {rgengc_allow_full_mark: false, implementation: "default"} + # GC.config(foo: 'bar') + # # => {rgengc_allow_full_mark: false} + # GC.config + # # => {rgengc_allow_full_mark: false, implementation: "default"} # - # This method is only expected to work on CRuby. - # - # === \GC Implementation independent values - # - # The GC.config hash can also contain keys that are global and - # read-only. These keys are not specific to any one \GC library implementation - # and attempting to write to them will raise +ArgumentError+. + # All-Implementations Configuration # - # There is currently only one global, read-only key: + # The single read-only entry for all implementations is: # - # [implementation] - # Returns a +String+ containing the name of the currently loaded \GC library, - # if one has been loaded using +RUBY_GC_LIBRARY+, and "default" in all other - # cases + # - +implementation+: + # the string name of the implementation; + # for the Ruby default implementation, 'default'. # - # === \GC Implementation specific values + # Implementation-Specific Configuration # - # \GC libraries are expected to document their own configuration. Valid keys - # for Ruby's default \GC implementation are: + # A \GC implementation maintains its own implementation-specific configuration. # - # [rgengc_allow_full_mark] - # Controls whether the \GC is allowed to run a full mark (young & old objects). + # For Ruby's default implementation the single entry is: # - # When +true+, \GC interleaves major and minor collections. This is the default. \GC - # will function as intended. + # - +rgengc_allow_full_mark+: + # Controls whether the \GC is allowed to run a full mark (young & old objects): # - # 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. + # - +true+ (default): \GC interleaves major and minor collections. + # - +false+: \GC does not initiate a full marking cycle unless + # explicitly directed by user code; + # see GC.start. # - # A flag will be set to notify that a full mark has been - # requested. This flag is accessible using - # GC.latest_gc_info(:need_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 warm up an application using +Process.warmup+ + # Setting this parameter to +false+ disables young-to-old promotion . + # For performance reasons, we recommended warming up the application using Process.warmup # before setting this parameter to +false+. + # def self.config hash = nil return Primitive.gc_config_get unless hash From ad146320957cd84311d5ce857591e05f50f87ca6 Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Fri, 8 Aug 2025 11:19:40 -0500 Subject: [PATCH 12/15] [DOC] Tweaks for GC.config --- gc.rb | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/gc.rb b/gc.rb index 54a88cf8f7dbd4..a7620fd9ace9a3 100644 --- a/gc.rb +++ b/gc.rb @@ -260,11 +260,12 @@ def self.stat_heap heap_name = nil, hash_or_key = nil # GC.config -> hash # GC.config(hash_to_merge) -> merged_hash # - # This method is expected to be defined, useful, and well-behaved - # only in the CRuby implementation. + # This method is implementation-specific to CRuby. # # Sets or gets information about the current \GC configuration. # + # Configuration parameters are \GC implementation-specific and may change without notice. + # # With no argument given, returns a hash containing the configuration: # # GC.config @@ -302,13 +303,13 @@ def self.stat_heap heap_name = nil, hash_or_key = nil # Controls whether the \GC is allowed to run a full mark (young & old objects): # # - +true+ (default): \GC interleaves major and minor collections. - # - +false+: \GC does not initiate a full marking cycle unless - # explicitly directed by user code; + # A flag is set to notify GC that a full mark has been requested. + # This flag is accessible via GC.latest_gc_info(:need_major_by). + # - +false+: \GC does not initiate a full marking cycle unless explicitly directed by user code; # see GC.start. - # - # Setting this parameter to +false+ disables young-to-old promotion . - # For performance reasons, we recommended warming up the application using Process.warmup - # before setting this parameter to +false+. + # Setting this parameter to +false+ disables young-to-old promotion. + # For performance reasons, we recommended warming up the application using Process.warmup + # before setting this parameter to +false+. # def self.config hash = nil return Primitive.gc_config_get unless hash From e0b72ad2f1f4836e3303596ecae1f62aa60b2d80 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Mon, 11 Aug 2025 08:25:07 -0500 Subject: [PATCH 13/15] [DOC] Update JIT options --- doc/ruby/options.md | 46 +++++---------------------------------------- 1 file changed, 5 insertions(+), 41 deletions(-) diff --git a/doc/ruby/options.md b/doc/ruby/options.md index bfbd2530de397e..95f8cf453c9f14 100644 --- a/doc/ruby/options.md +++ b/doc/ruby/options.md @@ -672,6 +672,11 @@ $ ruby --internal-encoding=cesu-8 -e 'puts Encoding::default_internal' CESU-8 ``` +### `--jit` + +Option `--jit` is an alias for option `--yjit`, which enables YJIT; +see additional YJIT options in the [YJIT documentation](rdoc-ref:yjit/yjit.md). + ### `--verbose`: Set `$VERBOSE` Option `--verbose` sets global variable `$VERBOSE` to `true` @@ -681,44 +686,3 @@ and disables input from `$stdin`. Option `--version` prints the version of the Ruby interpreter, then exits. -## Experimental Options - -These options are experimental in the current Ruby release, -and may be modified or withdrawn in later releases. - -### `--jit` - -Option `-jit` enables JIT compilation with the default option. - -#### `--jit-debug` - -Option `--jit-debug` enables JIT debugging (very slow); -adds compiler flags if given. - -#### `--jit-max-cache=num` - -Option `--jit-max-cache=num` sets the maximum number of methods -to be JIT-ed in a cache; default: 100). - -#### `--jit-min-calls=num` - -Option `jit-min-calls=num` sets the minimum number of calls to trigger JIT -(for testing); default: 10000). - -#### `--jit-save-temps` - -Option `--jit-save-temps` saves JIT temporary files in $TMP or /tmp (for testing). - -#### `--jit-verbose` - -Option `--jit-verbose` prints JIT logs of level `num` or less -to `$stderr`; default: 0. - -#### `--jit-wait` - -Option `--jit-wait` waits until JIT compilation finishes every time (for testing). - -#### `--jit-warnings` - -Option `--jit-warnings` enables printing of JIT warnings. - From 4775d1ffa8a34f0bca3f6124c98426d56eb8e1b6 Mon Sep 17 00:00:00 2001 From: S-H-GAMELINKS Date: Sun, 10 Aug 2025 21:30:41 +0900 Subject: [PATCH 14/15] Add NODE IN locations Add locations to struct `RNode_IN`. memo: ```bash > ruby -e 'case 1; in 2 then 3; end' --parser=prism --dump=parsetree @ ProgramNode (location: (1,0)-(1,24)) +-- locals: [] +-- statements: @ StatementsNode (location: (1,0)-(1,24)) +-- body: (length: 1) +-- @ CaseMatchNode (location: (1,0)-(1,24)) +-- predicate: | @ IntegerNode (location: (1,5)-(1,6)) | +-- IntegerBaseFlags: decimal | +-- value: 1 +-- conditions: (length: 1) | +-- @ InNode (location: (1,8)-(1,19)) | +-- pattern: | | @ IntegerNode (location: (1,11)-(1,12)) | | +-- IntegerBaseFlags: decimal | | +-- value: 2 | +-- statements: | | @ StatementsNode (location: (1,18)-(1,19)) | | +-- body: (length: 1) | | +-- @ IntegerNode (location: (1,18)-(1,19)) | | +-- IntegerBaseFlags: decimal | | +-- value: 3 | +-- in_loc: (1,8)-(1,10) = "in" | +-- then_loc: (1,13)-(1,17) = "then" +-- else_clause: nil +-- case_keyword_loc: (1,0)-(1,4) = "case" +-- end_keyword_loc: (1,21)-(1,24) = "end" ``` --- ast.c | 6 ++++++ node_dump.c | 5 ++++- parse.y | 15 +++++++++------ rubyparser.h | 3 +++ test/ruby/test_ast.rb | 14 ++++++++++++++ 5 files changed, 36 insertions(+), 7 deletions(-) diff --git a/ast.c b/ast.c index bc2adeacd630a4..04b954385477e1 100644 --- a/ast.c +++ b/ast.c @@ -866,6 +866,12 @@ node_locations(VALUE ast_value, const NODE *node) location_new(&RNODE_IF(node)->if_keyword_loc), location_new(&RNODE_IF(node)->then_keyword_loc), location_new(&RNODE_IF(node)->end_keyword_loc)); + case NODE_IN: + return rb_ary_new_from_args(4, + location_new(nd_code_loc(node)), + location_new(&RNODE_IN(node)->in_keyword_loc), + location_new(&RNODE_IN(node)->then_keyword_loc), + location_new(&RNODE_IN(node)->operator_loc)); case NODE_MODULE: return rb_ary_new_from_args(3, location_new(nd_code_loc(node)), diff --git a/node_dump.c b/node_dump.c index c318baeeede009..18ac3d7b35b4e5 100644 --- a/node_dump.c +++ b/node_dump.c @@ -309,8 +309,11 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node) ANN("example: case x; in 1; foo; in 2; bar; else baz; end"); F_NODE(nd_head, RNODE_IN, "in pattern"); F_NODE(nd_body, RNODE_IN, "in body"); - LAST_NODE; F_NODE(nd_next, RNODE_IN, "next in clause"); + F_LOC(in_keyword_loc, RNODE_IN); + F_LOC(then_keyword_loc, RNODE_IN); + LAST_NODE; + F_LOC(operator_loc, RNODE_IN); return; case NODE_WHILE: diff --git a/parse.y b/parse.y index e77dc790bcd1cd..c0f46a395f9f59 100644 --- a/parse.y +++ b/parse.y @@ -1070,7 +1070,7 @@ static rb_node_case_t *rb_node_case_new(struct parser_params *p, NODE *nd_head, static rb_node_case2_t *rb_node_case2_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc, const YYLTYPE *case_keyword_loc, const YYLTYPE *end_keyword_loc); static rb_node_case3_t *rb_node_case3_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, const YYLTYPE *loc, const YYLTYPE *case_keyword_loc, const YYLTYPE *end_keyword_loc); static rb_node_when_t *rb_node_when_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, NODE *nd_next, const YYLTYPE *loc, const YYLTYPE *keyword_loc, const YYLTYPE *then_keyword_loc); -static rb_node_in_t *rb_node_in_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, NODE *nd_next, const YYLTYPE *loc); +static rb_node_in_t *rb_node_in_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, NODE *nd_next, const YYLTYPE *loc, const YYLTYPE *in_keyword_loc, const YYLTYPE *then_keyword_loc, const YYLTYPE *operator_loc); static rb_node_while_t *rb_node_while_new(struct parser_params *p, NODE *nd_cond, NODE *nd_body, long nd_state, const YYLTYPE *loc, const YYLTYPE *keyword_loc, const YYLTYPE *closing_loc); static rb_node_until_t *rb_node_until_new(struct parser_params *p, NODE *nd_cond, NODE *nd_body, long nd_state, const YYLTYPE *loc, const YYLTYPE *keyword_loc, const YYLTYPE *closing_loc); static rb_node_iter_t *rb_node_iter_new(struct parser_params *p, rb_node_args_t *nd_args, NODE *nd_body, const YYLTYPE *loc); @@ -1178,7 +1178,7 @@ static rb_node_error_t *rb_node_error_new(struct parser_params *p, const YYLTYPE #define NEW_CASE2(b,loc,ck_loc,ek_loc) (NODE *)rb_node_case2_new(p,b,loc,ck_loc,ek_loc) #define NEW_CASE3(h,b,loc,ck_loc,ek_loc) (NODE *)rb_node_case3_new(p,h,b,loc,ck_loc,ek_loc) #define NEW_WHEN(c,t,e,loc,k_loc,t_loc) (NODE *)rb_node_when_new(p,c,t,e,loc,k_loc,t_loc) -#define NEW_IN(c,t,e,loc) (NODE *)rb_node_in_new(p,c,t,e,loc) +#define NEW_IN(c,t,e,loc,ik_loc,tk_loc,o_loc) (NODE *)rb_node_in_new(p,c,t,e,loc,ik_loc,tk_loc,o_loc) #define NEW_WHILE(c,b,n,loc,k_loc,c_loc) (NODE *)rb_node_while_new(p,c,b,n,loc,k_loc,c_loc) #define NEW_UNTIL(c,b,n,loc,k_loc,c_loc) (NODE *)rb_node_until_new(p,c,b,n,loc,k_loc,c_loc) #define NEW_ITER(a,b,loc) (NODE *)rb_node_iter_new(p,a,b,loc) @@ -3472,7 +3472,7 @@ expr : command_call pop_pktbl(p, $p_pktbl); pop_pvtbl(p, $p_pvtbl); p->ctxt.in_kwarg = $ctxt.in_kwarg; - $$ = NEW_CASE3($arg, NEW_IN($body, 0, 0, &@body), &@$, &NULL_LOC, &NULL_LOC); + $$ = NEW_CASE3($arg, NEW_IN($body, 0, 0, &@body, &NULL_LOC, &NULL_LOC, &@2), &@$, &NULL_LOC, &NULL_LOC); /*% ripper: case!($:arg, in!($:body, Qnil, Qnil)) %*/ } | arg keyword_in @@ -3485,7 +3485,7 @@ expr : command_call pop_pktbl(p, $p_pktbl); pop_pvtbl(p, $p_pvtbl); p->ctxt.in_kwarg = $ctxt.in_kwarg; - $$ = NEW_CASE3($arg, NEW_IN($body, NEW_TRUE(&@body), NEW_FALSE(&@body), &@body), &@$, &NULL_LOC, &NULL_LOC); + $$ = NEW_CASE3($arg, NEW_IN($body, NEW_TRUE(&@body), NEW_FALSE(&@body), &@body, &@keyword_in, &NULL_LOC, &NULL_LOC), &@$, &NULL_LOC, &NULL_LOC); /*% ripper: case!($:arg, in!($:body, Qnil, Qnil)) %*/ } | arg %prec tLBRACE_ARG @@ -5399,7 +5399,7 @@ p_case_body : keyword_in compstmt(stmts) p_cases[cases] { - $$ = NEW_IN($expr, $compstmt, $cases, &@$); + $$ = NEW_IN($expr, $compstmt, $cases, &@$, &@keyword_in, &@then, &NULL_LOC); /*% ripper: in!($:expr, $:compstmt, $:cases) %*/ } ; @@ -11528,12 +11528,15 @@ rb_node_when_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, NODE *nd } static rb_node_in_t * -rb_node_in_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, NODE *nd_next, const YYLTYPE *loc) +rb_node_in_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, NODE *nd_next, const YYLTYPE *loc, const YYLTYPE *in_keyword_loc, const YYLTYPE *then_keyword_loc, const YYLTYPE *operator_loc) { rb_node_in_t *n = NODE_NEWNODE(NODE_IN, rb_node_in_t, loc); n->nd_head = nd_head; n->nd_body = nd_body; n->nd_next = nd_next; + n->in_keyword_loc = *in_keyword_loc; + n->then_keyword_loc = *then_keyword_loc; + n->operator_loc = *operator_loc; return n; } diff --git a/rubyparser.h b/rubyparser.h index e436d1c404b2a2..cc63efd3f85998 100644 --- a/rubyparser.h +++ b/rubyparser.h @@ -324,6 +324,9 @@ typedef struct RNode_IN { struct RNode *nd_head; struct RNode *nd_body; struct RNode *nd_next; + rb_code_location_t in_keyword_loc; + rb_code_location_t then_keyword_loc; + rb_code_location_t operator_loc; } rb_node_in_t; typedef struct RNode_LOOP { diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb index 6372b0d34e5e71..9a7d75c270b661 100644 --- a/test/ruby/test_ast.rb +++ b/test/ruby/test_ast.rb @@ -1514,6 +1514,20 @@ def test_if_locations assert_locations(node.children[-1].children[1].children[0].locations, [[1, 11, 1, 17], [1, 13, 1, 15], nil, nil]) end + def test_in_locations + node = ast_parse("case 1; in 2 then 3; end") + assert_locations(node.children[-1].children[1].locations, [[1, 8, 1, 20], [1, 8, 1, 10], [1, 13, 1, 17], nil]) + + node = ast_parse("1 => a") + assert_locations(node.children[-1].children[1].locations, [[1, 5, 1, 6], nil, nil, [1, 2, 1, 4]]) + + node = ast_parse("1 in a") + assert_locations(node.children[-1].children[1].locations, [[1, 5, 1, 6], [1, 2, 1, 4], nil, nil]) + + node = ast_parse("case 1; in 2; 3; end") + assert_locations(node.children[-1].children[1].locations, [[1, 8, 1, 16], [1, 8, 1, 10], [1, 12, 1, 13], nil]) + end + def test_next_locations node = ast_parse("loop { next 1 }") assert_locations(node.children[-1].children[-1].children[-1].locations, [[1, 7, 1, 13], [1, 7, 1, 11]]) From 61fff8a92f5b7fbcdd0bea46150ce0845637483e Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Fri, 8 Aug 2025 11:15:15 -0400 Subject: [PATCH 15/15] Fix return value of setting in GC.config gc_config_set returned rb_gc_impl_config_get, but gc_config_get also added the implementation key to the return value. This caused the return value of GC.config to differ depending on whether the optional hash argument is provided or not. --- gc.c | 2 +- gc.rb | 8 ++++---- test/ruby/test_gc.rb | 9 +++------ 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/gc.c b/gc.c index 4c8a042c1e8513..7663e82f41b5ab 100644 --- a/gc.c +++ b/gc.c @@ -4357,7 +4357,7 @@ gc_config_set(rb_execution_context_t *ec, VALUE self, VALUE hash) rb_gc_impl_config_set(objspace, hash); - return rb_gc_impl_config_get(objspace); + return Qnil; } static VALUE diff --git a/gc.rb b/gc.rb index a7620fd9ace9a3..603520df5312ab 100644 --- a/gc.rb +++ b/gc.rb @@ -312,17 +312,17 @@ def self.stat_heap heap_name = nil, hash_or_key = nil # before setting this parameter to +false+. # def self.config hash = nil - return Primitive.gc_config_get unless hash - - if(Primitive.cexpr!("RBOOL(RB_TYPE_P(hash, T_HASH))")) + if Primitive.cexpr!("RBOOL(RB_TYPE_P(hash, T_HASH))") if hash.include?(:implementation) raise ArgumentError, 'Attempting to set read-only key "Implementation"' end Primitive.gc_config_set hash - else + elsif hash != nil raise ArgumentError end + + Primitive.gc_config_get end # call-seq: diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb index 85022cbc4d8924..ccccd212b692eb 100644 --- a/test/ruby/test_gc.rb +++ b/test/ruby/test_gc.rb @@ -75,12 +75,9 @@ def test_gc_config_setting_returns_updated_config_hash GC.start end - def test_gc_config_setting_returns_nil_for_missing_keys - missing_value = GC.config(no_such_key: true)[:no_such_key] - assert_nil(missing_value) - ensure - GC.config(full_mark: true) - GC.start + def test_gc_config_setting_returns_config_hash + hash = GC.config(no_such_key: true) + assert_equal(GC.config, hash) end def test_gc_config_disable_major