Merge branch 'master' into feature/enumerator-lazy-peek

This commit is contained in:
Red 2025-08-13 12:54:25 +09:00 committed by GitHub
commit 3a9ee4a553
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
141 changed files with 4446 additions and 2107 deletions

View file

@ -93,7 +93,7 @@ runs:
path: ${{ inputs.srcdir }}
fetch-depth: ${{ inputs.fetch-depth }}
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
- uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
with:
path: ${{ inputs.srcdir }}/.downloaded-cache
key: ${{ runner.os }}-${{ runner.arch }}-downloaded-cache

View file

@ -61,14 +61,11 @@ jobs:
exit $fail
working-directory: include
- name: Generate docs
id: docs
- name: Check if to generate documents
id: rdoc
run: |
$RDOC -C -x ^ext -x ^lib .
$RDOC --op html .
echo htmlout=ruby-html-${GITHUB_SHA:0:10} >> $GITHUB_OUTPUT
env:
RDOC: ruby -W0 --disable-gems tool/rdoc-srcdir -q
ref=$(sed 's/#.*//;/^rdoc /!d' gems/bundled_gems | awk '{print $4}')
echo ref=$ref >> $GITHUB_OUTPUT
# Generate only when document commit/PR
if: >-
${{false
@ -80,6 +77,36 @@ jobs:
|| contains(github.event.pull_request.labels.*.name, 'Documentation')
}}
- name: Checkout rdoc
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
repository: ruby/rdoc
ref: ${{ steps.rdoc.outputs.ref }}
path: .bundle/gems/rdoc-0
if: ${{ steps.rdoc.outputs.ref != '' }}
- name: Generate rdoc
run: |
set -x
gempath=$(ruby -e 'print Gem.user_dir, "/bin"')
PATH=$gempath:$PATH
gem install --user bundler
bundle config --local path vendor/bundle
bundle install --jobs 4
bundle exec rake generate
working-directory: .bundle/gems/rdoc-0
if: ${{ steps.rdoc.outputs.ref != '' }}
- name: Generate docs
id: docs
run: |
$RDOC -C -x ^ext -x ^lib .
$RDOC --op html .
echo htmlout=ruby-html-${GITHUB_SHA:0:10} >> $GITHUB_OUTPUT
env:
RDOC: ruby -W0 --disable-gems tool/rdoc-srcdir -q
if: ${{ steps.rdoc.outcome == 'success' }}
- name: Upload docs
uses: actions/upload-artifact@v4
with:

View file

@ -107,6 +107,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with: { sparse-checkout-cone-mode: false, sparse-checkout: /.github }
- { uses: './.github/actions/setup/directories', with: { srcdir: 'src', builddir: 'build', makeup: true, fetch-depth: 10 } }
- { uses: './.github/actions/compilers', name: 'clang 22', with: { tag: 'clang-22' } }
- { uses: './.github/actions/compilers', name: 'clang 21', with: { tag: 'clang-21' } }
- { uses: './.github/actions/compilers', name: 'clang 20', with: { tag: 'clang-20' } }
- { uses: './.github/actions/compilers', name: 'clang 19', with: { tag: 'clang-19' } }

View file

@ -65,33 +65,88 @@ 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
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: ${{ steps.msys2.outputs.msys2-location }}
- name: Remove Strawberry Perl pkg-config
working-directory:
# `pkg-config.bat` included in Strawberry Perl is written in
# Perl and doesn't work when another msys2 `perl` precede its
# own `perl`.
#
# ```
# Can't find C:\Strawberry\perl\bin\pkg-config.bat on PATH, '.' not in PATH.
# ```
run: |
Get-Command pkg-config.bat | % { ren $_.path ($_.path + "~") }
shell: pwsh
- 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::$'\033[93m'$e$'\033[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::$'\033[93m'$e$'\033[m'
$e || result=false
echo ::endgroup::
done
# show packages
echo ::group::$'\033[93m'Packages$'\033[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 ]
shell: sh
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
@ -110,6 +165,7 @@ jobs:
run: >
../src/configure --disable-install-doc --prefix=/.
--build=$CHOST --host=$CHOST --target=$CHOST
shell: sh
- name: make all
timeout-minutes: 30
@ -132,7 +188,6 @@ jobs:
- name: test
timeout-minutes: 30
run: make test
shell: cmd
env:
GNUMAKEFLAGS: ''
RUBY_TESTOPTS: '-v --tty=no'
@ -140,7 +195,6 @@ jobs:
- name: test-all
timeout-minutes: 45
shell: cmd
run: |
make ${{ StartsWith(matrix.test_task, 'test/') && matrix.test_task || 'test-all' }}
env:
@ -155,7 +209,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
@ -167,4 +220,4 @@ jobs:
defaults:
run:
working-directory: build
shell: sh
shell: cmd

53
.github/workflows/rust-warnings.yml vendored Normal file
View file

@ -0,0 +1,53 @@
# Surface Rust warnings on PRs that touch any Rust code.
# Not a required check so we never block people over new warnings
# that might come from a new Rust version being released.
name: Rust warnings
on:
pull_request:
types:
- opened
- synchronize
- reopened
paths:
- '**.rs'
- '!**.inc.rs'
merge_group:
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
permissions:
contents: read
jobs:
make:
env:
GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
runs-on: ubuntu-24.04
if: >-
${{!(false
|| contains(github.event.head_commit.message, '[DOC]')
|| contains(github.event.head_commit.message, 'Document')
|| contains(github.event.pull_request.title, '[DOC]')
|| contains(github.event.pull_request.title, 'Document')
|| contains(github.event.pull_request.labels.*.name, 'Documentation')
|| (github.event_name == 'push' && github.event.pull_request.user.login == 'dependabot[bot]')
)}}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Install Rust
run: rustup default beta
- name: Rust warnings
run: |
set -euo pipefail
cargo check --quiet --all-features --message-format=json \
| jq -r 'select(.reason == "compiler-message" and .message.level == "warning") | .message.rendered' \
> warnings.txt
cat warnings.txt
! grep --quiet '[^[:space:]]' warnings.txt

View file

@ -24,6 +24,22 @@ jobs:
make:
strategy:
matrix:
test_task: [check]
configure: ['']
arch: ['']
os:
- ubuntu-24.04
- ubuntu-24.04-arm
# FIXME Comment out ppc64le due to failing tests on GitHub Actions
# ppc64le
# https://bugs.ruby-lang.org/issues/21534
# - ubuntu-24.04-ppc64le
- ubuntu-24.04-s390x
# The ppc64le/s390x runners work only in the registered repositories.
# They don't work in forked repositories.
# https://github.com/IBM/actionspz/blob/main/docs/FAQ.md#what-about-forked-repos
upstream:
- ${{ github.repository == 'ruby/ruby' }}
include:
- test_task: check
configure: 'cppflags=-DVM_CHECK_MODE'
@ -36,10 +52,11 @@ jobs:
- test_task: test-bundler-parallel
timeout: 50
- test_task: test-bundled-gems
- test_task: check
os: ubuntu-24.04
- test_task: check
os: ubuntu-24.04-arm
exclude:
- os: ubuntu-24.04-ppc64le
upstream: false
- os: ubuntu-24.04-s390x
upstream: false
fail-fast: false
env:
@ -72,7 +89,25 @@ jobs:
with:
ruby-version: '3.1'
bundler: none
if: ${{ !endsWith(matrix.os, 'arm') }}
if: ${{ !endsWith(matrix.os, 'arm') && !endsWith(matrix.os, 'ppc64le') && !endsWith(matrix.os, 's390x') }}
# Avoid possible test failures with the zlib applying the following patch
# on s390x CPU architecture.
# https://github.com/madler/zlib/pull/410
- name: Disable DFLTCC
run: echo "DFLTCC=0" >> $GITHUB_ENV
working-directory:
if: ${{ endsWith(matrix.os, 's390x') }}
# A temporary workaround: Set HOME env to pass the step
# ./.github/actions/setup/directories.
# https://github.com/IBM/actionspz/issues/30
- name: Set HOME env
run: |
echo "HOME: #{HOME}"
echo "HOME=$(ls -d ~)" >> $GITHUB_ENV
working-directory:
if: ${{ endsWith(matrix.os, 'ppc64le') || endsWith(matrix.os, 's390x') }}
- uses: ./.github/actions/setup/directories
with:
@ -122,6 +157,17 @@ jobs:
continue-on-error: true
timeout-minutes: 3
# A temporary workaround: Skip user ground id test
# There is a mismatch between the group IDs of "id -g" and C function
# getpwuid(uid_t uid) pw_gid.
# https://github.com/IBM/actionspz/issues/31
- name: Skip user group id test
run: |
sed -i.orig '/^ it "returns user group id" do/a\ skip' \
../src/spec/ruby/library/etc/struct_passwd_spec.rb
diff -u ../src/spec/ruby/library/etc/struct_passwd_spec.rb{.orig,} || :
if: ${{ endsWith(matrix.os, 'ppc64le') || endsWith(matrix.os, 's390x') }}
- name: make ${{ matrix.test_task }}
run: |
test -n "${LAUNCHABLE_STDOUT}" && exec 1> >(tee "${LAUNCHABLE_STDOUT}")

View file

@ -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

View file

@ -34,6 +34,7 @@ jobs:
include:
- test_task: 'zjit-check'
configure: '--enable-yjit=dev --enable-zjit'
rust_version: "1.85.0"
- test_task: 'ruby' # build test for combo build
configure: '--enable-yjit --enable-zjit'
@ -81,14 +82,17 @@ jobs:
# Set fetch-depth: 10 so that Launchable can receive commits information.
fetch-depth: 10
- name: Install Rust
if: ${{ matrix.rust_version }}
run: |
rustup install ${{ matrix.rust_version }} --profile minimal
rustup default ${{ matrix.rust_version }}
- uses: taiki-e/install-action@v2
with:
tool: nextest@0.9
if: ${{ matrix.test_task == 'zjit-check' }}
- name: Install Rust # TODO(alan): remove when GitHub images catch up past 1.85.0
run: rustup default 1.85.0
- name: Run configure
run: ../src/configure -C --disable-install-doc ${{ matrix.configure }}

View file

@ -39,6 +39,7 @@ jobs:
- test_task: 'zjit-check'
configure: '--enable-yjit --enable-zjit=dev'
rust_version: '1.85.0'
- test_task: 'zjit-test-all'
configure: '--enable-zjit=dev'
@ -98,7 +99,10 @@ jobs:
fetch-depth: 10
- name: Install Rust
run: rustup default 1.85.0
if: ${{ matrix.rust_version }}
run: |
rustup install ${{ matrix.rust_version }} --profile minimal
rustup default ${{ matrix.rust_version }}
- name: Install rustfmt
if: ${{ matrix.test_task == 'zjit-bindgen' }}

View file

@ -19,5 +19,7 @@ autolink_excluded_words:
- RDoc
- Ruby
- Set
- ZJIT
- YJIT
canonical_root: https://docs.ruby-lang.org/en/master

12
array.c
View file

@ -3659,9 +3659,9 @@ rb_ary_collect(VALUE ary)
/*
* call-seq:
* collect! {|element| ... } -> new_array
* collect! {|element| ... } -> self
* collect! -> new_enumerator
* map! {|element| ... } -> new_array
* map! {|element| ... } -> self
* map! -> new_enumerator
*
* With a block given, calls the block with each element of +self+
@ -4755,10 +4755,10 @@ rb_ary_clear(VALUE ary)
/*
* call-seq:
* fill(object, start = nil, count = nil) -> new_array
* fill(object, range) -> new_array
* fill(start = nil, count = nil) {|element| ... } -> new_array
* fill(range) {|element| ... } -> new_array
* fill(object, start = nil, count = nil) -> self
* fill(object, range) -> self
* fill(start = nil, count = nil) {|element| ... } -> self
* fill(range) {|element| ... } -> self
*
* Replaces selected elements in +self+;
* may add elements to +self+;

11
ast.c
View file

@ -866,6 +866,17 @@ 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)),
location_new(&RNODE_MODULE(node)->module_keyword_loc),
location_new(&RNODE_MODULE(node)->end_keyword_loc));
case NODE_NEXT:
return rb_ary_new_from_args(2,
location_new(nd_code_loc(node)),

View file

@ -10,7 +10,7 @@ case "$0" in
* ) srcdir="";; # Otherwise
esac
# If install-only is explicitly requested, disbale symlink flags
# If install-only is explicitly requested, disable symlink flags
case " $* " in
*" -i "* | *" --install"* ) symlink_flags="" ;;
* ) symlink_flags="--install --symlink" ;;

View file

@ -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)

View file

@ -1,5 +1,12 @@
prelude: |
C = Struct.new(:x) do
def initialize(...)
super
@ivar = 42
end
attr_accessor :ivar
class_eval <<-END
def r
#{'x;'*256}
@ -15,11 +22,16 @@ prelude: |
m = method(:x=)
#{'m.call(nil);'*256}
end
def r_ivar
#{'ivar;'*256}
end
END
end
C.new(nil) # ensure common shape is known
obj = C.new(nil)
benchmark:
member_reader: "obj.r"
member_writer: "obj.w"
member_reader_method: "obj.rm"
member_writer_method: "obj.wm"
ivar_reader: "obj.r_ivar"

View file

@ -1353,11 +1353,11 @@ preludes: {$(VPATH)}miniprelude.c
{$(srcdir)}.rb.rbbin:
$(ECHO) making $@
$(Q) $(MINIRUBY) $(tooldir)/mk_rbbin.rb $< > $@
$(Q) $(MINIRUBY) $(tooldir)/mk_rbbin.rb $(SRC_FILE) > $(OS_DEST_FILE)
{$(srcdir)}.rb.rbinc:
$(ECHO) making $@
$(Q) $(BASERUBY) $(tooldir)/mk_builtin_loader.rb $<
$(Q) $(BASERUBY) $(tooldir)/mk_builtin_loader.rb $(SRC_FILE)
$(BUILTIN_BINARY:yes=built)in_binary.rbbin: $(PREP) $(BUILTIN_RB_SRCS) $(srcdir)/template/builtin_binary.rbbin.tmpl
$(Q) $(MINIRUBY) $(tooldir)/generic_erb.rb -o $@ \
@ -1539,12 +1539,14 @@ prepare-gems: $(HAVE_BASERUBY:yes=update-gems) $(HAVE_BASERUBY:yes=extract-gems)
extract-gems: $(HAVE_BASERUBY:yes=update-gems) $(HAVE_BASERUBY:yes=outdate-bundled-gems)
update-gems: $(HAVE_BASERUBY:yes=outdate-bundled-gems)
split_option = -F"\s+|$(HASH_SIGN).*"
update-gems$(sequential): PHONY
$(ECHO) Downloading bundled gem files...
$(Q) $(BASERUBY) -C "$(srcdir)" \
-I./tool -rdownloader -answ \
-I./tool -rdownloader $(split_option) -answ \
-e 'gem, ver = *$$F' \
-e 'next if !ver or /^#/=~gem' \
-e 'next if !ver' \
-e 'old = Dir.glob("gems/#{gem}-*.gem")' \
-e 'gem = "#{gem}-#{ver}.gem"' \
-e 'Downloader::RubyGems.download(gem, "gems", nil) and' \
@ -1556,10 +1558,10 @@ update-gems$(sequential): PHONY
extract-gems$(sequential): PHONY
$(ECHO) Extracting bundled gem files...
$(Q) $(BASERUBY) -C "$(srcdir)" \
-Itool/lib -rfileutils -rbundled_gem -answ \
-Itool/lib -rfileutils -rbundled_gem $(split_option) -answ \
-e 'BEGIN {d = ".bundle/gems"}' \
-e 'gem, ver, _, rev = *$$F' \
-e 'next if !ver or /^#/=~gem' \
-e 'next if !ver' \
-e 'g = "#{gem}-#{ver}"' \
-e 'unless File.directory?("#{d}/#{g}")' \
-e 'if rev and File.exist?(gs = "gems/src/#{gem}/#{gem}.gemspec")' \
@ -1623,7 +1625,7 @@ yes-install-for-test-bundled-gems: yes-update-default-gemspecs
test-bundled-gems-fetch: yes-test-bundled-gems-fetch
yes-test-bundled-gems-fetch: clone-bundled-gems-src
clone-bundled-gems-src: PHONY
$(Q) $(BASERUBY) -C $(srcdir)/gems ../tool/fetch-bundled_gems.rb BUNDLED_GEMS="$(BUNDLED_GEMS)" src bundled_gems
$(Q) $(BASERUBY) -C $(srcdir) tool/fetch-bundled_gems.rb BUNDLED_GEMS="$(BUNDLED_GEMS)" gems/src gems/bundled_gems
no-test-bundled-gems-fetch:
test-bundled-gems-prepare: $(TEST_RUNNABLE)-test-bundled-gems-prepare

View file

@ -309,7 +309,7 @@ HELP_EXTRA_TASKS = \
# 4. "gem x.y.z URL" -> "gem-x.y.z"
bundled-gems := $(shell sed \
-e 's/[ ][ ]*/ /g' \
-e 's/^ //;/\#/d;s/ *$$//;/^$$/d' \
-e 's/^ //;s/\#.*//;s/ *$$//;/^$$/d' \
$(if $(filter yes,$(HAVE_GIT)), \
-e 's/^\(.*\) \(.*\) \(.*\) \(.*\)/\1|\2|\4|\3/' \
) \

9
depend
View file

@ -6065,6 +6065,7 @@ hash.$(OBJEXT): $(top_srcdir)/internal/set_table.h
hash.$(OBJEXT): $(top_srcdir)/internal/st.h
hash.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
hash.$(OBJEXT): $(top_srcdir)/internal/string.h
hash.$(OBJEXT): $(top_srcdir)/internal/struct.h
hash.$(OBJEXT): $(top_srcdir)/internal/symbol.h
hash.$(OBJEXT): $(top_srcdir)/internal/thread.h
hash.$(OBJEXT): $(top_srcdir)/internal/time.h
@ -6288,6 +6289,7 @@ hash.$(OBJEXT): {$(VPATH)}symbol.h
hash.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
hash.$(OBJEXT): {$(VPATH)}thread_native.h
hash.$(OBJEXT): {$(VPATH)}util.h
hash.$(OBJEXT): {$(VPATH)}variable.h
hash.$(OBJEXT): {$(VPATH)}vm_core.h
hash.$(OBJEXT): {$(VPATH)}vm_debug.h
hash.$(OBJEXT): {$(VPATH)}vm_opts.h
@ -12700,6 +12702,7 @@ ractor.$(OBJEXT): {$(VPATH)}vm_debug.h
ractor.$(OBJEXT): {$(VPATH)}vm_opts.h
ractor.$(OBJEXT): {$(VPATH)}vm_sync.h
ractor.$(OBJEXT): {$(VPATH)}yjit.h
ractor.$(OBJEXT): {$(VPATH)}zjit.h
random.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
random.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
random.$(OBJEXT): $(CCAN_DIR)/list/list.h
@ -12926,6 +12929,7 @@ range.$(OBJEXT): $(top_srcdir)/internal/enumerator.h
range.$(OBJEXT): $(top_srcdir)/internal/error.h
range.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
range.$(OBJEXT): $(top_srcdir)/internal/gc.h
range.$(OBJEXT): $(top_srcdir)/internal/imemo.h
range.$(OBJEXT): $(top_srcdir)/internal/numeric.h
range.$(OBJEXT): $(top_srcdir)/internal/range.h
range.$(OBJEXT): $(top_srcdir)/internal/serial.h
@ -12948,6 +12952,7 @@ range.$(OBJEXT): {$(VPATH)}config.h
range.$(OBJEXT): {$(VPATH)}defines.h
range.$(OBJEXT): {$(VPATH)}encoding.h
range.$(OBJEXT): {$(VPATH)}id.h
range.$(OBJEXT): {$(VPATH)}id_table.h
range.$(OBJEXT): {$(VPATH)}intern.h
range.$(OBJEXT): {$(VPATH)}internal.h
range.$(OBJEXT): {$(VPATH)}internal/abi.h
@ -15580,6 +15585,7 @@ shape.$(OBJEXT): $(top_srcdir)/internal/serial.h
shape.$(OBJEXT): $(top_srcdir)/internal/set_table.h
shape.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
shape.$(OBJEXT): $(top_srcdir)/internal/string.h
shape.$(OBJEXT): $(top_srcdir)/internal/struct.h
shape.$(OBJEXT): $(top_srcdir)/internal/symbol.h
shape.$(OBJEXT): $(top_srcdir)/internal/variable.h
shape.$(OBJEXT): $(top_srcdir)/internal/vm.h
@ -16569,6 +16575,7 @@ string.$(OBJEXT): $(top_srcdir)/internal/serial.h
string.$(OBJEXT): $(top_srcdir)/internal/set_table.h
string.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
string.$(OBJEXT): $(top_srcdir)/internal/string.h
string.$(OBJEXT): $(top_srcdir)/internal/struct.h
string.$(OBJEXT): $(top_srcdir)/internal/transcode.h
string.$(OBJEXT): $(top_srcdir)/internal/variable.h
string.$(OBJEXT): $(top_srcdir)/internal/vm.h
@ -16766,6 +16773,7 @@ string.$(OBJEXT): {$(VPATH)}thread.h
string.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
string.$(OBJEXT): {$(VPATH)}thread_native.h
string.$(OBJEXT): {$(VPATH)}util.h
string.$(OBJEXT): {$(VPATH)}variable.h
string.$(OBJEXT): {$(VPATH)}vm_core.h
string.$(OBJEXT): {$(VPATH)}vm_debug.h
string.$(OBJEXT): {$(VPATH)}vm_opts.h
@ -18103,6 +18111,7 @@ variable.$(OBJEXT): $(top_srcdir)/internal/serial.h
variable.$(OBJEXT): $(top_srcdir)/internal/set_table.h
variable.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
variable.$(OBJEXT): $(top_srcdir)/internal/string.h
variable.$(OBJEXT): $(top_srcdir)/internal/struct.h
variable.$(OBJEXT): $(top_srcdir)/internal/symbol.h
variable.$(OBJEXT): $(top_srcdir)/internal/thread.h
variable.$(OBJEXT): $(top_srcdir)/internal/variable.h

View file

@ -367,11 +367,11 @@ Outstanding ones only.
* Fiber.blocking? tells whether the current execution context is
blocking. [[Feature #16786]]
* Thread
* Thread#join invokes the scheduler hooks `block`/`unblock` in a
non-blocking execution context. [[Feature #16786]]
* Thread
* Thread.ignore_deadlock accessor has been added for disabling the
default deadlock detection, allowing the use of signal handlers to
break deadlock. [[Bug #13768]]

View file

@ -502,7 +502,7 @@ An added _quantifier_ specifies how many matches are required or allowed:
/\w*/.match('x')
# => #<MatchData "x">
/\w*/.match('xyz')
# => #<MatchData "yz">
# => #<MatchData "xyz">
- <tt>+</tt> - Matches one or more times:

572
doc/globals.md Normal file
View file

@ -0,0 +1,572 @@
# Pre-Defined Global Variables
Some of the pre-defined global variables have synonyms
that are available via module English.
For each of those, the \English synonym is given.
To use the module:
```ruby
require 'English'
```
## Summary
### Exceptions
| Variable | English | Contains |
|-------------|-------------------|----------------------------------------------------|
| `$!` | `$ERROR_INFO` | Exception object; set by Kernel#raise. |
| `$@` | `$ERROR_POSITION` | Array of backtrace positions; set by Kernel#raise. |
### Pattern Matching
| Variable | English | Contains |
|---------------|---------------------|--------------------------------------------------|
| `$~` | `$LAST_MATCH_INFO` | MatchData object; set by matcher method. |
| `$&` | `$MATCH` | Matched substring; set by matcher method. |
| `` $` `` | `$PRE_MATCH` | Substring left of match; set by matcher method. |
| `$'` | `$POST_MATCH` | Substring right of match; set by matcher method. |
| `$+` | `$LAST_PAREN_MATCH` | Last group matched; set by matcher method. |
| `$1` | | First group matched; set by matcher method. |
| `$2` | | Second group matched; set by matcher method. |
| <tt>$_n_</tt> | | <i>n</i>th group matched; set by matcher method. |
### Separators
| Variable | English | Contains |
|----------|----------------------------|--------------------------------------------|
| `$/` | `$INPUT_RECORD_SEPARATOR` | Input record separator; initially newline. |
| `$\` | `$OUTPUT_RECORD_SEPARATOR` | Output record separator; initially `nil`. |
### Streams
| Variable | English | Contains |
|-----------|-----------------------------|-----------------------------------------------|
| `$stdin` | | Standard input stream; initially `STDIN`. |
| `$stdout` | | Standard input stream; initially `STDIOUT`. |
| `$stderr` | | Standard input stream; initially `STDERR`. |
| `$<` | `$DEFAULT_INPUT` | Default standard input; `ARGF` or `$stdin`. |
| `$>` | `$DEFAULT_OUTPUT` | Default standard output; initially `$stdout`. |
| `$.` | `$INPUT_LINE_NUMBER`, `$NR` | Input position of most recently read stream. |
| `$_` | `$LAST_READ_LINE` | String from most recently read stream. |
### Processes
| Variable | English | Contains |
|---------------------------|-----------------------|--------------------------------------------------------|
| `$0` | | Initially, the name of the executing program. |
| `$*` | `$ARGV` | Points to the `ARGV` array. |
| `$$` | `$PROCESS_ID`, `$PID` | Process ID of the current process. |
| `$?` | `$CHILD_STATUS` | Process::Status of most recently exited child process. |
| `$LOAD_PATH`, `$:`, `$-I` | | Array of paths to be searched. |
| `$LOADED_FEATURES`, `$"` | | Array of paths to loaded files. |
### Debugging
| Variable | English | Contains |
|-------------|---------|--------------------------------------------------------|
| `$FILENAME` | | The value returned by method ARGF.filename. |
| `$DEBUG` | | Initially, whether option `-d` or `--debug` was given. |
| `$VERBOSE` | | Initially, whether option `-V` or `-W` was given. |
### Other Variables
| Variable | English | Contains |
|----------|---------|------------------------------------------------|
| `$-a` | | Whether option `-a` was given. |
| `$-i` | | Extension given with command-line option `-i`. |
| `$-l` | | Whether option `-l` was given. |
| `$-p` | | Whether option `-p` was given. |
## Exceptions
### `$!` (\Exception)
Contains the Exception object set by Kernel#raise:
```ruby
begin
raise RuntimeError.new('Boo!')
rescue RuntimeError
p $!
end
```
Output:
```
#<RuntimeError: Boo!>
```
English - `$ERROR_INFO`
### `$@` (Backtrace)
Same as `$!.backtrace`;
returns an array of backtrace positions:
```ruby
begin
raise RuntimeError.new('Boo!')
rescue RuntimeError
pp $@.take(4)
end
```
Output:
```
["(irb):338:in `<top (required)>'",
"/snap/ruby/317/lib/ruby/3.2.0/irb/workspace.rb:119:in `eval'",
"/snap/ruby/317/lib/ruby/3.2.0/irb/workspace.rb:119:in `evaluate'",
"/snap/ruby/317/lib/ruby/3.2.0/irb/context.rb:502:in `evaluate'"]
```
English - `$ERROR_POSITION`.
## Pattern Matching
These global variables store information about the most recent
successful match in the current scope.
For details and examples,
see {Regexp Global Variables}[rdoc-ref:Regexp@Global+Variables].
### `$~` (\MatchData)
MatchData object created from the match;
thread-local and frame-local.
English - `$LAST_MATCH_INFO`.
### `$&` (Matched Substring)
The matched string.
English - `$MATCH`.
### `` $` `` (Pre-Match Substring)
The string to the left of the match.
English - `$PREMATCH`.
### `$'` (Post-Match Substring)
The string to the right of the match.
English - `$POSTMATCH`.
### `$+` (Last Matched Group)
The last group matched.
English - `$LAST_PAREN_MATCH`.
### `$1`, `$2`, \Etc. (Matched Group)
For <tt>$_n_</tt> the <i>n</i>th group of the match.
No \English.
## Separators
### `$/` (Input Record Separator)
An input record separator, initially newline.
English - `$INPUT_RECORD_SEPARATOR`, `$RS`.
Aliased as `$-0`.
### `$\` (Output Record Separator)
An output record separator, initially `nil`.
English - `$OUTPUT_RECORD_SEPARATOR`, `$ORS`.
## Streams
### `$stdin` (Standard Input)
The current standard input stream; initially:
```ruby
$stdin # => #<IO:<STDIN>>
```
### `$stdout` (Standard Output)
The current standard output stream; initially:
```ruby
$stdout # => #<IO:<STDOUT>>
```
### `$stderr` (Standard Error)
The current standard error stream; initially:
```ruby
$stderr # => #<IO:<STDERR>>
```
### `$<` (\ARGF or $stdin)
Points to stream ARGF if not empty, else to stream $stdin; read-only.
English - `$DEFAULT_INPUT`.
### `$>` (Default Standard Output)
An output stream, initially `$stdout`.
English - `$DEFAULT_OUTPUT`
### `$.` (Input Position)
The input position (line number) in the most recently read stream.
English - `$INPUT_LINE_NUMBER`, `$NR`
### `$_` (Last Read Line)
The line (string) from the most recently read stream.
English - `$LAST_READ_LINE`.
## Processes
### `$0`
Initially, contains the name of the script being executed;
may be reassigned.
### `$*` (\ARGV)
Points to ARGV.
English - `$ARGV`.
### `$$` (Process ID)
The process ID of the current process. Same as Process.pid.
English - `$PROCESS_ID`, `$PID`.
### `$?` (Child Status)
Initially `nil`, otherwise the Process::Status object
created for the most-recently exited child process;
thread-local.
English - `$CHILD_STATUS`.
### `$LOAD_PATH` (Load Path)
Contains the array of paths to be searched
by Kernel#load and Kernel#require.
Singleton method `$LOAD_PATH.resolve_feature_path(feature)`
returns:
- <tt>[:rb, _path_]</tt>, where `path` is the path to the Ruby file to be
loaded for the given `feature`.
- <tt>[:so, _path_]</tt>, where `path` is the path to the shared object file
to be loaded for the given `feature`.
- `nil` if there is no such `feature` and `path`.
Examples:
```ruby
$LOAD_PATH.resolve_feature_path('timeout')
# => [:rb, "/snap/ruby/317/lib/ruby/3.2.0/timeout.rb"]
$LOAD_PATH.resolve_feature_path('date_core')
# => [:so, "/snap/ruby/317/lib/ruby/3.2.0/x86_64-linux/date_core.so"]
$LOAD_PATH.resolve_feature_path('foo')
# => nil
```
Aliased as `$:` and `$-I`.
### `$LOADED_FEATURES`
Contains an array of the paths to the loaded files:
```ruby
$LOADED_FEATURES.take(10)
# =>
["enumerator.so",
"thread.rb",
"fiber.so",
"rational.so",
"complex.so",
"ruby2_keywords.rb",
"/snap/ruby/317/lib/ruby/3.2.0/x86_64-linux/enc/encdb.so",
"/snap/ruby/317/lib/ruby/3.2.0/x86_64-linux/enc/trans/transdb.so",
"/snap/ruby/317/lib/ruby/3.2.0/x86_64-linux/rbconfig.rb",
"/snap/ruby/317/lib/ruby/3.2.0/rubygems/compatibility.rb"]
```
Aliased as `$"`.
## Debugging
### `$FILENAME`
The value returned by method ARGF.filename.
### `$DEBUG`
Initially `true` if command-line option `-d` or `--debug` is given,
otherwise initially `false`;
may be set to either value in the running program.
When `true`, prints each raised exception to `$stderr`.
Aliased as `$-d`.
### `$VERBOSE`
Initially `true` if command-line option `-v` or `-w` is given,
otherwise initially `false`;
may be set to either value, or to `nil`, in the running program.
When `true`, enables Ruby warnings.
When `nil`, disables warnings, including those from Kernel#warn.
Aliased as `$-v` and `$-w`.
## Other Variables
### `$-a`
Whether command-line option `-a` was given; read-only.
### `$-i`
Contains the extension given with command-line option `-i`,
or `nil` if none.
An alias of ARGF.inplace_mode.
### `$-l`
Whether command-line option `-l` was set; read-only.
### `$-p`
Whether command-line option `-p` was given; read-only.
## Deprecated
### `$=`
### `$,`
### `$;`
# Pre-Defined Global Constants
## Summary
### Streams
| Constant | Contains |
|----------|-------------------------|
| `STDIN` | Standard input stream. |
| `STDOUT` | Standard output stream. |
| `STDERR` | Standard error stream. |
### Environment
| Constant | Contains |
|-----------------------|-------------------------------------------------------------------------------|
| `ENV` | Hash of current environment variable names and values. |
| `ARGF` | String concatenation of files given on the command line, or `$stdin` if none. |
| `ARGV` | Array of the given command-line arguments. |
| `TOPLEVEL_BINDING` | Binding of the top level scope. |
| `RUBY_VERSION` | String Ruby version. |
| `RUBY_RELEASE_DATE` | String Ruby release date. |
| `RUBY_PLATFORM` | String Ruby platform. |
| `RUBY_PATCH_LEVEL` | String Ruby patch level. |
| `RUBY_REVISION` | String Ruby revision. |
| `RUBY_COPYRIGHT` | String Ruby copyright. |
| `RUBY_ENGINE` | String Ruby engine. |
| `RUBY_ENGINE_VERSION` | String Ruby engine version. |
| `RUBY_DESCRIPTION` | String Ruby description. |
### Embedded Data
| Constant | Contains |
|----------|--------------------------------------------------------------------|
| `DATA` | File containing embedded data (lines following `__END__`, if any). |
## Streams
### `STDIN`
The standard input stream (the default value for `$stdin`):
```ruby
STDIN # => #<IO:<STDIN>>
```
### `STDOUT`
The standard output stream (the default value for `$stdout`):
```ruby
STDOUT # => #<IO:<STDOUT>>
```
### `STDERR`
The standard error stream (the default value for `$stderr`):
```ruby
STDERR # => #<IO:<STDERR>>
```
## Environment
### `ENV`
A hash of the contains current environment variables names and values:
```ruby
ENV.take(5)
# =>
[["COLORTERM", "truecolor"],
["DBUS_SESSION_BUS_ADDRESS", "unix:path=/run/user/1000/bus"],
["DESKTOP_SESSION", "ubuntu"],
["DISPLAY", ":0"],
["GDMSESSION", "ubuntu"]]
```
### `ARGF`
The virtual concatenation of the files given on the command line, or from
`$stdin` if no files were given, `"-"` is given, or after
all files have been read.
### `ARGV`
An array of the given command-line arguments.
### `TOPLEVEL_BINDING`
The Binding of the top level scope:
```ruby
TOPLEVEL_BINDING # => #<Binding:0x00007f58da0da7c0>
```
### `RUBY_VERSION`
The Ruby version:
```ruby
RUBY_VERSION # => "3.2.2"
```
### `RUBY_RELEASE_DATE`
The release date string:
```ruby
RUBY_RELEASE_DATE # => "2023-03-30"
```
### `RUBY_PLATFORM`
The platform identifier:
```ruby
RUBY_PLATFORM # => "x86_64-linux"
```
### `RUBY_PATCHLEVEL`
The integer patch level for this Ruby:
```ruby
RUBY_PATCHLEVEL # => 53
```
For a development build the patch level will be -1.
### `RUBY_REVISION`
The git commit hash for this Ruby:
```ruby
RUBY_REVISION # => "e51014f9c05aa65cbf203442d37fef7c12390015"
```
### `RUBY_COPYRIGHT`
The copyright string:
```ruby
RUBY_COPYRIGHT
# => "ruby - Copyright (C) 1993-2023 Yukihiro Matsumoto"
```
### `RUBY_ENGINE`
The name of the Ruby implementation:
```ruby
RUBY_ENGINE # => "ruby"
```
### `RUBY_ENGINE_VERSION`
The version of the Ruby implementation:
```ruby
RUBY_ENGINE_VERSION # => "3.2.2"
```
### `RUBY_DESCRIPTION`
The description of the Ruby implementation:
```ruby
RUBY_DESCRIPTION
# => "ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-linux]"
```
## Embedded \Data
### `DATA`
Defined if and only if the program has this line:
```ruby
__END__
```
When defined, `DATA` is a File object
containing the lines following the `__END__`,
positioned at the first of those lines:
```ruby
p DATA
DATA.each_line { |line| p line }
__END__
Foo
Bar
Baz
```
Output:
```
#<File:t.rb>
"Foo\n"
"Bar\n"
"Baz\n"
```

View file

@ -1,416 +0,0 @@
= Pre-Defined Global Variables
Some of the pre-defined global variables have synonyms
that are available via module English.
For each of those, the \English synonym is given.
To use the module:
require 'English'
== Exceptions
=== <tt>$!</tt> (\Exception)
Contains the Exception object set by Kernel#raise:
begin
raise RuntimeError.new('Boo!')
rescue RuntimeError
p $!
end
Output:
#<RuntimeError: Boo!>
English - <tt>$ERROR_INFO</tt>
=== <tt>$@</tt> (Backtrace)
Same as <tt>$!.backtrace</tt>;
returns an array of backtrace positions:
begin
raise RuntimeError.new('Boo!')
rescue RuntimeError
pp $@.take(4)
end
Output:
["(irb):338:in `<top (required)>'",
"/snap/ruby/317/lib/ruby/3.2.0/irb/workspace.rb:119:in `eval'",
"/snap/ruby/317/lib/ruby/3.2.0/irb/workspace.rb:119:in `evaluate'",
"/snap/ruby/317/lib/ruby/3.2.0/irb/context.rb:502:in `evaluate'"]
English - <tt>$ERROR_POSITION</tt>.
== Pattern Matching
These global variables store information about the most recent
successful match in the current scope.
For details and examples,
see {Regexp Global Variables}[rdoc-ref:Regexp@Global+Variables].
=== <tt>$~</tt> (\MatchData)
MatchData object created from the match;
thread-local and frame-local.
English - <tt>$LAST_MATCH_INFO</tt>.
=== <tt>$&</tt> (Matched Substring)
The matched string.
English - <tt>$MATCH</tt>.
=== <tt>$`</tt> (Pre-Match Substring)
The string to the left of the match.
English - <tt>$PREMATCH</tt>.
=== <tt>$'</tt> (Post-Match Substring)
The string to the right of the match.
English - <tt>$POSTMATCH</tt>.
=== <tt>$+</tt> (Last Matched Group)
The last group matched.
English - <tt>$LAST_PAREN_MATCH</tt>.
=== <tt>$1</tt>, <tt>$2</tt>, \Etc. (Matched Group)
For <tt>$_n_</tt> the _nth_ group of the match.
No \English.
== Separators
=== <tt>$/</tt> (Input Record Separator)
An input record separator, initially newline.
English - <tt>$INPUT_RECORD_SEPARATOR</tt>, <tt>$RS</tt>.
Aliased as <tt>$-0</tt>.
=== <tt>$\\</tt> (Output Record Separator)
An output record separator, initially +nil+.
English - <tt>$OUTPUT_RECORD_SEPARATOR</tt>, <tt>$ORS</tt>.
== Streams
=== <tt>$stdin</tt> (Standard Input)
The current standard input stream; initially:
$stdin # => #<IO:<STDIN>>
=== <tt>$stdout</tt> (Standard Output)
The current standard output stream; initially:
$stdout # => #<IO:<STDOUT>>
=== <tt>$stderr</tt> (Standard Error)
The current standard error stream; initially:
$stderr # => #<IO:<STDERR>>
=== <tt>$<</tt> (\ARGF or $stdin)
Points to stream ARGF if not empty, else to stream $stdin; read-only.
English - <tt>$DEFAULT_INPUT</tt>.
=== <tt>$></tt> (Default Standard Output)
An output stream, initially <tt>$stdout</tt>.
English - <tt>$DEFAULT_OUTPUT</tt>
=== <tt>$.</tt> (Input Position)
The input position (line number) in the most recently read stream.
English - <tt>$INPUT_LINE_NUMBER</tt>, <tt>$NR</tt>
=== <tt>$_</tt> (Last Read Line)
The line (string) from the most recently read stream.
English - <tt>$LAST_READ_LINE</tt>.
== Processes
=== <tt>$0</tt>
Initially, contains the name of the script being executed;
may be reassigned.
=== <tt>$*</tt> (\ARGV)
Points to ARGV.
English - <tt>$ARGV</tt>.
=== <tt>$$</tt> (Process ID)
The process ID of the current process. Same as Process.pid.
English - <tt>$PROCESS_ID</tt>, <tt>$PID</tt>.
=== <tt>$?</tt> (Child Status)
Initially +nil+, otherwise the Process::Status object
created for the most-recently exited child process;
thread-local.
English - <tt>$CHILD_STATUS</tt>.
=== <tt>$LOAD_PATH</tt> (Load Path)
Contains the array of paths to be searched
by Kernel#load and Kernel#require.
Singleton method <tt>$LOAD_PATH.resolve_feature_path(feature)</tt>
returns:
- <tt>[:rb, _path_]</tt>, where +path+ is the path to the Ruby file
to be loaded for the given +feature+.
- <tt>[:so+ _path_]</tt>, where +path+ is the path to the shared object file
to be loaded for the given +feature+.
- +nil+ if there is no such +feature+ and +path+.
Examples:
$LOAD_PATH.resolve_feature_path('timeout')
# => [:rb, "/snap/ruby/317/lib/ruby/3.2.0/timeout.rb"]
$LOAD_PATH.resolve_feature_path('date_core')
# => [:so, "/snap/ruby/317/lib/ruby/3.2.0/x86_64-linux/date_core.so"]
$LOAD_PATH.resolve_feature_path('foo')
# => nil
Aliased as <tt>$:</tt> and <tt>$-I</tt>.
=== <tt>$LOADED_FEATURES</tt>
Contains an array of the paths to the loaded files:
$LOADED_FEATURES.take(10)
# =>
["enumerator.so",
"thread.rb",
"fiber.so",
"rational.so",
"complex.so",
"ruby2_keywords.rb",
"/snap/ruby/317/lib/ruby/3.2.0/x86_64-linux/enc/encdb.so",
"/snap/ruby/317/lib/ruby/3.2.0/x86_64-linux/enc/trans/transdb.so",
"/snap/ruby/317/lib/ruby/3.2.0/x86_64-linux/rbconfig.rb",
"/snap/ruby/317/lib/ruby/3.2.0/rubygems/compatibility.rb"]
Aliased as <tt>$"</tt>.
== Debugging
=== <tt>$FILENAME</tt>
The value returned by method ARGF.filename.
=== <tt>$DEBUG</tt>
Initially +true+ if command-line option <tt>-d</tt> or <tt>--debug</tt> is given,
otherwise initially +false+;
may be set to either value in the running program.
When +true+, prints each raised exception to <tt>$stderr</tt>.
Aliased as <tt>$-d</tt>.
=== <tt>$VERBOSE</tt>
Initially +true+ if command-line option <tt>-v</tt> or <tt>-w</tt> is given,
otherwise initially +false+;
may be set to either value, or to +nil+, in the running program.
When +true+, enables Ruby warnings.
When +nil+, disables warnings, including those from Kernel#warn.
Aliased as <tt>$-v</tt> and <tt>$-w</tt>.
== Other Variables
=== <tt>$-a</tt>
Whether command-line option <tt>-a</tt> was given; read-only.
=== <tt>$-i</tt>
Contains the extension given with command-line option <tt>-i</tt>,
or +nil+ if none.
An alias of ARGF.inplace_mode.
=== <tt>$-l</tt>
Whether command-line option <tt>-l</tt> was set; read-only.
=== <tt>$-p</tt>
Whether command-line option <tt>-p</tt> was given; read-only.
== Deprecated
=== <tt>$=</tt>
=== <tt>$,</tt>
=== <tt>$;</tt>
= Pre-Defined Global Constants
= Streams
=== <tt>STDIN</tt>
The standard input stream (the default value for <tt>$stdin</tt>):
STDIN # => #<IO:<STDIN>>
=== <tt>STDOUT</tt>
The standard output stream (the default value for <tt>$stdout</tt>):
STDOUT # => #<IO:<STDOUT>>
=== <tt>STDERR</tt>
The standard error stream (the default value for <tt>$stderr</tt>):
STDERR # => #<IO:<STDERR>>
== Environment
=== ENV
A hash of the contains current environment variables names and values:
ENV.take(5)
# =>
[["COLORTERM", "truecolor"],
["DBUS_SESSION_BUS_ADDRESS", "unix:path=/run/user/1000/bus"],
["DESKTOP_SESSION", "ubuntu"],
["DISPLAY", ":0"],
["GDMSESSION", "ubuntu"]]
=== ARGF
The virtual concatenation of the files given on the command line, or from
<tt>$stdin</tt> if no files were given, <tt>"-"</tt> is given, or after
all files have been read.
=== <tt>ARGV</tt>
An array of the given command-line arguments.
=== <tt>TOPLEVEL_BINDING</tt>
The Binding of the top level scope:
TOPLEVEL_BINDING # => #<Binding:0x00007f58da0da7c0>
=== <tt>RUBY_VERSION</tt>
The Ruby version:
RUBY_VERSION # => "3.2.2"
=== <tt>RUBY_RELEASE_DATE</tt>
The release date string:
RUBY_RELEASE_DATE # => "2023-03-30"
=== <tt>RUBY_PLATFORM</tt>
The platform identifier:
RUBY_PLATFORM # => "x86_64-linux"
=== <tt>RUBY_PATCHLEVEL</tt>
The integer patch level for this Ruby:
RUBY_PATCHLEVEL # => 53
For a development build the patch level will be -1.
=== <tt>RUBY_REVISION</tt>
The git commit hash for this Ruby:
RUBY_REVISION # => "e51014f9c05aa65cbf203442d37fef7c12390015"
=== <tt>RUBY_COPYRIGHT</tt>
The copyright string:
RUBY_COPYRIGHT
# => "ruby - Copyright (C) 1993-2023 Yukihiro Matsumoto"
=== <tt>RUBY_ENGINE</tt>
The name of the Ruby implementation:
RUBY_ENGINE # => "ruby"
=== <tt>RUBY_ENGINE_VERSION</tt>
The version of the Ruby implementation:
RUBY_ENGINE_VERSION # => "3.2.2"
=== <tt>RUBY_DESCRIPTION</tt>
The description of the Ruby implementation:
RUBY_DESCRIPTION
# => "ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-linux]"
== Embedded \Data
=== <tt>DATA</tt>
Defined if and only if the program has this line:
__END__
When defined, <tt>DATA</tt> is a File object
containing the lines following the <tt>__END__</tt>,
positioned at the first of those lines:
p DATA
DATA.each_line { |line| p line }
__END__
Foo
Bar
Baz
Output:
#<File:t.rb>
"Foo\n"
"Bar\n"
"Baz\n"

View file

@ -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.

View file

@ -1,6 +1,19 @@
Returns an array of the grapheme clusters in +self+
(see {Unicode Grapheme Cluster Boundaries}[https://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries]):
s = "\u0061\u0308-pqr-\u0062\u0308-xyz-\u0063\u0308" # => "ä-pqr-b̈-xyz-c̈"
s = "ä-pqr-b̈-xyz-c̈"
s.size # => 16
s.bytesize # => 19
s.grapheme_clusters.size # => 13
s.grapheme_clusters
# => ["ä", "-", "p", "q", "r", "-", "b̈", "-", "x", "y", "z", "-", "c̈"]
Details:
s = "ä"
s.grapheme_clusters # => ["ä"] # One grapheme cluster.
s.bytes # => [97, 204, 136] # Three bytes.
s.chars # => ["a", "̈"] # Two characters.
s.chars.map {|char| char.ord } # => [97, 776] # Their values.
Related: see {Converting to Non-String}[rdoc-ref:String@Converting+to+Non--5CString].

View file

@ -279,7 +279,7 @@ An uninitialized global variable has a value of +nil+.
Ruby has some special globals that behave differently depending on context
such as the regular expression match variables or that have a side-effect when
assigned to. See the {global variables documentation}[rdoc-ref:globals.rdoc]
assigned to. See the {global variables documentation}[rdoc-ref:globals.md]
for details.
== Assignment Methods

View file

@ -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.

View file

@ -29,6 +29,7 @@
#include "ruby/util.h"
#include "ruby_assert.h"
#include "vm_sync.h"
#include "ruby_atomic.h"
#ifndef ENC_DEBUG
#define ENC_DEBUG 0
@ -144,10 +145,14 @@ enc_list_update(int index, rb_raw_encoding *encoding)
{
RUBY_ASSERT(index < ENCODING_LIST_CAPA);
VALUE list = rb_encoding_list;
VALUE list = RUBY_ATOMIC_VALUE_LOAD(rb_encoding_list);
if (list && NIL_P(rb_ary_entry(list, index))) {
VALUE new_list = rb_ary_dup(list);
RBASIC_CLEAR_CLASS(new_list);
/* initialize encoding data */
rb_ary_store(list, index, enc_new(encoding));
rb_ary_store(new_list, index, enc_new(encoding));
RUBY_ATOMIC_VALUE_SET(rb_encoding_list, new_list);
}
}
@ -157,7 +162,7 @@ enc_list_lookup(int idx)
VALUE list, enc = Qnil;
if (idx < ENCODING_LIST_CAPA) {
list = rb_encoding_list;
list = RUBY_ATOMIC_VALUE_LOAD(rb_encoding_list);
RUBY_ASSERT(list);
enc = rb_ary_entry(list, idx);
}
@ -258,6 +263,7 @@ must_encindex(int index)
int
rb_to_encoding_index(VALUE enc)
{
ASSERT_vm_unlocking(); // can load encoding, so must not hold VM lock
int idx;
const char *name;
@ -667,15 +673,15 @@ int
rb_enc_alias(const char *alias, const char *orig)
{
int idx, r;
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
enc_check_addable(enc_table, alias); // can raise
}
idx = rb_enc_find_index(orig);
if (idx < 0) return -1;
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
enc_check_addable(enc_table, alias);
if ((idx = rb_enc_find_index(orig)) < 0) {
r = -1;
}
else {
r = enc_alias(enc_table, alias, idx);
}
r = enc_alias(enc_table, alias, idx);
}
return r;
@ -742,6 +748,7 @@ int rb_require_internal_silent(VALUE fname);
static int
load_encoding(const char *name)
{
ASSERT_vm_unlocking();
VALUE enclib = rb_sprintf("enc/%s.so", name);
VALUE debug = ruby_debug;
VALUE errinfo;
@ -757,7 +764,7 @@ load_encoding(const char *name)
enclib = rb_fstring(enclib);
ruby_debug = Qfalse;
errinfo = rb_errinfo();
loaded = rb_require_internal_silent(enclib);
loaded = rb_require_internal_silent(enclib); // must run without VM_LOCK
ruby_debug = debug;
rb_set_errinfo(errinfo);
@ -781,6 +788,7 @@ enc_autoload_body(rb_encoding *enc)
{
rb_encoding *base;
int i = 0;
ASSERT_vm_unlocking();
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
base = enc_table->list[ENC_TO_ENCINDEX(enc)].base;
@ -792,30 +800,32 @@ enc_autoload_body(rb_encoding *enc)
}
} while (enc_table->list[i].enc != base && (++i, 1));
}
}
if (i != -1) {
if (base) {
bool do_register = true;
if (rb_enc_autoload_p(base)) {
if (rb_enc_autoload(base) < 0) {
do_register = false;
i = -1;
}
if (i != -1) {
if (base) {
bool do_register = true;
if (rb_enc_autoload_p(base)) {
if (rb_enc_autoload(base) < 0) {
do_register = false;
i = -1;
}
}
i = enc->ruby_encoding_index;
if (do_register) {
if (do_register) {
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
i = enc->ruby_encoding_index;
enc_register_at(enc_table, i & ENC_INDEX_MASK, rb_enc_name(enc), base);
((rb_raw_encoding *)enc)->ruby_encoding_index = i;
}
}
i &= ENC_INDEX_MASK;
}
else {
i = -2;
}
i &= ENC_INDEX_MASK;
}
else {
i = -2;
}
}
return i;
@ -824,6 +834,7 @@ enc_autoload_body(rb_encoding *enc)
int
rb_enc_autoload(rb_encoding *enc)
{
ASSERT_vm_unlocking();
int i = enc_autoload_body(enc);
if (i == -2) {
i = load_encoding(rb_enc_name(enc));
@ -844,6 +855,7 @@ int
rb_enc_find_index(const char *name)
{
int i;
ASSERT_vm_unlocking(); // it needs to be unlocked so it can call `load_encoding` if necessary
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
i = enc_registered(enc_table, name);
}
@ -1019,7 +1031,6 @@ rb_enc_associate_index(VALUE obj, int idx)
rb_encoding *enc;
int oldidx, oldtermlen, termlen;
/* enc_check_capable(obj);*/
rb_check_frozen(obj);
oldidx = rb_enc_get_index(obj);
if (oldidx == idx)
@ -1355,7 +1366,10 @@ enc_names(VALUE self)
args[0] = (VALUE)rb_to_encoding_index(self);
args[1] = rb_ary_new2(0);
st_foreach(global_enc_table.names, enc_names_i, (st_data_t)args);
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
st_foreach(enc_table->names, enc_names_i, (st_data_t)args);
}
return args[1];
}
@ -1380,8 +1394,9 @@ enc_names(VALUE self)
static VALUE
enc_list(VALUE klass)
{
VALUE ary = rb_ary_new2(0);
rb_ary_replace(ary, rb_encoding_list);
VALUE ary = rb_ary_new2(ENCODING_LIST_CAPA);
VALUE list = RUBY_ATOMIC_VALUE_LOAD(rb_encoding_list);
rb_ary_replace(ary, list);
return ary;
}
@ -1526,6 +1541,9 @@ int rb_locale_charmap_index(void);
int
rb_locale_encindex(void)
{
// `rb_locale_charmap_index` can call `enc_find_index`, which can
// load an encoding. This needs to be done without VM lock held.
ASSERT_vm_unlocking();
int idx = rb_locale_charmap_index();
if (idx < 0) idx = ENCINDEX_UTF_8;
@ -1584,6 +1602,10 @@ enc_set_default_encoding(struct default_encoding *def, VALUE encoding, const cha
/* Already set */
overridden = TRUE;
if (!NIL_P(encoding)) {
enc_check_encoding(encoding); // loads it if necessary. Needs to be done outside of VM lock.
}
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
if (NIL_P(encoding)) {
def->index = -1;
@ -1854,8 +1876,11 @@ rb_enc_name_list_i(st_data_t name, st_data_t idx, st_data_t arg)
static VALUE
rb_enc_name_list(VALUE klass)
{
VALUE ary = rb_ary_new2(global_enc_table.names->num_entries);
st_foreach(global_enc_table.names, rb_enc_name_list_i, (st_data_t)ary);
VALUE ary;
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
ary = rb_ary_new2(enc_table->names->num_entries);
st_foreach(enc_table->names, rb_enc_name_list_i, (st_data_t)ary);
}
return ary;
}
@ -1901,7 +1926,9 @@ rb_enc_aliases(VALUE klass)
aliases[0] = rb_hash_new();
aliases[1] = rb_ary_new();
st_foreach(global_enc_table.names, rb_enc_aliases_enc_i, (st_data_t)aliases);
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
st_foreach(enc_table->names, rb_enc_aliases_enc_i, (st_data_t)aliases);
}
return aliases[0];
}
@ -1969,9 +1996,9 @@ Init_Encoding(void)
struct enc_table *enc_table = &global_enc_table;
rb_gc_register_address(&rb_encoding_list);
list = rb_encoding_list = rb_ary_new2(ENCODING_LIST_CAPA);
RBASIC_CLEAR_CLASS(list);
rb_vm_register_global_object(list);
for (i = 0; i < enc_table->count; ++i) {
rb_ary_push(list, enc_new(enc_table->list[i].enc));
@ -2003,5 +2030,7 @@ Init_encodings(void)
void
rb_enc_foreach_name(int (*func)(st_data_t name, st_data_t idx, st_data_t arg), st_data_t arg)
{
st_foreach(global_enc_table.names, func, arg);
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
st_foreach(enc_table->names, func, arg);
}
}

View file

@ -280,28 +280,20 @@ enumerator_ptr(VALUE obj)
}
static void
proc_entry_mark(void *p)
proc_entry_mark_and_move(void *p)
{
struct proc_entry *ptr = p;
rb_gc_mark_movable(ptr->proc);
rb_gc_mark_movable(ptr->memo);
}
static void
proc_entry_compact(void *p)
{
struct proc_entry *ptr = p;
ptr->proc = rb_gc_location(ptr->proc);
ptr->memo = rb_gc_location(ptr->memo);
rb_gc_mark_and_move(&ptr->proc);
rb_gc_mark_and_move(&ptr->memo);
}
static const rb_data_type_t proc_entry_data_type = {
"proc_entry",
{
proc_entry_mark,
proc_entry_mark_and_move,
RUBY_TYPED_DEFAULT_FREE,
NULL, // Nothing allocated externally, so don't need a memsize function
proc_entry_compact,
proc_entry_mark_and_move,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
};
@ -1280,26 +1272,19 @@ enumerator_size(VALUE obj)
* Yielder
*/
static void
yielder_mark(void *p)
yielder_mark_and_move(void *p)
{
struct yielder *ptr = p;
rb_gc_mark_movable(ptr->proc);
}
static void
yielder_compact(void *p)
{
struct yielder *ptr = p;
ptr->proc = rb_gc_location(ptr->proc);
rb_gc_mark_and_move(&ptr->proc);
}
static const rb_data_type_t yielder_data_type = {
"yielder",
{
yielder_mark,
yielder_mark_and_move,
RUBY_TYPED_DEFAULT_FREE,
NULL,
yielder_compact,
yielder_mark_and_move,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
};
@ -1410,28 +1395,20 @@ yielder_new(void)
* Generator
*/
static void
generator_mark(void *p)
generator_mark_and_move(void *p)
{
struct generator *ptr = p;
rb_gc_mark_movable(ptr->proc);
rb_gc_mark_movable(ptr->obj);
}
static void
generator_compact(void *p)
{
struct generator *ptr = p;
ptr->proc = rb_gc_location(ptr->proc);
ptr->obj = rb_gc_location(ptr->obj);
rb_gc_mark_and_move(&ptr->proc);
rb_gc_mark_and_move(&ptr->obj);
}
static const rb_data_type_t generator_data_type = {
"generator",
{
generator_mark,
generator_mark_and_move,
RUBY_TYPED_DEFAULT_FREE,
NULL,
generator_compact,
generator_mark_and_move,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
};
@ -2940,19 +2917,11 @@ stop_result(VALUE self)
*/
static void
producer_mark(void *p)
producer_mark_and_move(void *p)
{
struct producer *ptr = p;
rb_gc_mark_movable(ptr->init);
rb_gc_mark_movable(ptr->proc);
}
static void
producer_compact(void *p)
{
struct producer *ptr = p;
ptr->init = rb_gc_location(ptr->init);
ptr->proc = rb_gc_location(ptr->proc);
rb_gc_mark_and_move(&ptr->init);
rb_gc_mark_and_move(&ptr->proc);
}
#define producer_free RUBY_TYPED_DEFAULT_FREE
@ -2966,10 +2935,10 @@ producer_memsize(const void *p)
static const rb_data_type_t producer_data_type = {
"producer",
{
producer_mark,
producer_mark_and_move,
producer_free,
producer_memsize,
producer_compact,
producer_mark_and_move,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
};
@ -3128,17 +3097,10 @@ enumerator_s_produce(int argc, VALUE *argv, VALUE klass)
*/
static void
enum_chain_mark(void *p)
enum_chain_mark_and_move(void *p)
{
struct enum_chain *ptr = p;
rb_gc_mark_movable(ptr->enums);
}
static void
enum_chain_compact(void *p)
{
struct enum_chain *ptr = p;
ptr->enums = rb_gc_location(ptr->enums);
rb_gc_mark_and_move(&ptr->enums);
}
#define enum_chain_free RUBY_TYPED_DEFAULT_FREE
@ -3152,12 +3114,12 @@ enum_chain_memsize(const void *p)
static const rb_data_type_t enum_chain_data_type = {
"chain",
{
enum_chain_mark,
enum_chain_mark_and_move,
enum_chain_free,
enum_chain_memsize,
enum_chain_compact,
enum_chain_mark_and_move,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
};
static struct enum_chain *
@ -3207,7 +3169,7 @@ enum_chain_initialize(VALUE obj, VALUE enums)
if (!ptr) rb_raise(rb_eArgError, "unallocated chain");
ptr->enums = rb_ary_freeze(enums);
RB_OBJ_WRITE(obj, &ptr->enums, rb_ary_freeze(enums));
ptr->pos = -1;
return obj;
@ -3241,7 +3203,7 @@ enum_chain_init_copy(VALUE obj, VALUE orig)
if (!ptr1) rb_raise(rb_eArgError, "unallocated chain");
ptr1->enums = ptr0->enums;
RB_OBJ_WRITE(obj, &ptr1->enums, ptr0->enums);
ptr1->pos = ptr0->pos;
return obj;
@ -3450,17 +3412,10 @@ enumerator_plus(VALUE obj, VALUE eobj)
*/
static void
enum_product_mark(void *p)
enum_product_mark_and_move(void *p)
{
struct enum_product *ptr = p;
rb_gc_mark_movable(ptr->enums);
}
static void
enum_product_compact(void *p)
{
struct enum_product *ptr = p;
ptr->enums = rb_gc_location(ptr->enums);
rb_gc_mark_and_move(&ptr->enums);
}
#define enum_product_free RUBY_TYPED_DEFAULT_FREE
@ -3474,12 +3429,12 @@ enum_product_memsize(const void *p)
static const rb_data_type_t enum_product_data_type = {
"product",
{
enum_product_mark,
enum_product_mark_and_move,
enum_product_free,
enum_product_memsize,
enum_product_compact,
enum_product_mark_and_move,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
};
static struct enum_product *
@ -3535,7 +3490,7 @@ enum_product_initialize(int argc, VALUE *argv, VALUE obj)
if (!ptr) rb_raise(rb_eArgError, "unallocated product");
ptr->enums = rb_ary_freeze(enums);
RB_OBJ_WRITE(obj, &ptr->enums, rb_ary_freeze(enums));
return obj;
}
@ -3553,7 +3508,7 @@ enum_product_init_copy(VALUE obj, VALUE orig)
if (!ptr1) rb_raise(rb_eArgError, "unallocated product");
ptr1->enums = ptr0->enums;
RB_OBJ_WRITE(obj, &ptr1->enums, ptr0->enums);
return obj;
}

23
error.c
View file

@ -1883,7 +1883,7 @@ exc_inspect(VALUE exc)
* # String
* end
*
* The value returned by this method migth be adjusted when raising (see Kernel#raise),
* The value returned by this method might be adjusted when raising (see Kernel#raise),
* or during intermediate handling by #set_backtrace.
*
* See also #backtrace_locations that provide the same value, as structured objects.
@ -2517,30 +2517,21 @@ typedef struct name_error_message_struct {
} name_error_message_t;
static void
name_err_mesg_mark(void *p)
name_err_mesg_mark_and_move(void *p)
{
name_error_message_t *ptr = (name_error_message_t *)p;
rb_gc_mark_movable(ptr->mesg);
rb_gc_mark_movable(ptr->recv);
rb_gc_mark_movable(ptr->name);
}
static void
name_err_mesg_update(void *p)
{
name_error_message_t *ptr = (name_error_message_t *)p;
ptr->mesg = rb_gc_location(ptr->mesg);
ptr->recv = rb_gc_location(ptr->recv);
ptr->name = rb_gc_location(ptr->name);
rb_gc_mark_and_move(&ptr->mesg);
rb_gc_mark_and_move(&ptr->recv);
rb_gc_mark_and_move(&ptr->name);
}
static const rb_data_type_t name_err_mesg_data_type = {
"name_err_mesg",
{
name_err_mesg_mark,
name_err_mesg_mark_and_move,
RUBY_TYPED_DEFAULT_FREE,
NULL, // No external memory to report,
name_err_mesg_update,
name_err_mesg_mark_and_move,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
};

View file

@ -451,13 +451,16 @@ dump_object(VALUE obj, struct dump_config *dc)
break;
case imemo_callcache:
mid = vm_cc_cme((const struct rb_callcache *)obj)->called_id;
if (mid != 0) {
dump_append(dc, ", \"called_id\":");
dump_append_id(dc, mid);
{
VALUE klass = ((const struct rb_callcache *)obj)->klass;
if (klass != 0) {
if (klass != Qundef) {
mid = vm_cc_cme((const struct rb_callcache *)obj)->called_id;
if (mid != 0) {
dump_append(dc, ", \"called_id\":");
dump_append_id(dc, mid);
}
dump_append(dc, ", \"receiver_class\":");
dump_append_ref(dc, klass);
}

View file

@ -203,6 +203,18 @@ check_modifiable(struct StringIO *ptr)
}
}
static inline bool
outside_p(struct StringIO *ptr, long pos)
{
return NIL_P(ptr->string) || pos >= RSTRING_LEN(ptr->string);
}
static inline bool
eos_p(struct StringIO *ptr)
{
return outside_p(ptr, ptr->pos);
}
static VALUE
strio_s_allocate(VALUE klass)
{
@ -628,9 +640,8 @@ static struct StringIO *
strio_to_read(VALUE self)
{
struct StringIO *ptr = readable(self);
if (NIL_P(ptr->string)) return NULL;
if (ptr->pos < RSTRING_LEN(ptr->string)) return ptr;
return NULL;
if (eos_p(ptr)) return NULL;
return ptr;
}
/*
@ -837,7 +848,11 @@ strio_seek(int argc, VALUE *argv, VALUE self)
offset = ptr->pos;
break;
case 2:
offset = RSTRING_LEN(ptr->string);
if (NIL_P(ptr->string)) {
offset = 0;
} else {
offset = RSTRING_LEN(ptr->string);
}
break;
default:
error_inval("invalid whence");
@ -906,7 +921,7 @@ strio_getc(VALUE self)
int len;
char *p;
if (NIL_P(str) || pos >= RSTRING_LEN(str)) {
if (eos_p(ptr)) {
return Qnil;
}
p = RSTRING_PTR(str)+pos;
@ -927,7 +942,7 @@ strio_getbyte(VALUE self)
{
struct StringIO *ptr = readable(self);
int c;
if (NIL_P(ptr->string) || ptr->pos >= RSTRING_LEN(ptr->string)) {
if (eos_p(ptr)) {
return Qnil;
}
c = RSTRING_PTR(ptr->string)[ptr->pos++];
@ -1605,10 +1620,9 @@ strio_read(int argc, VALUE *argv, VALUE self)
if (len < 0) {
rb_raise(rb_eArgError, "negative length %ld given", len);
}
if (len > 0 &&
(NIL_P(ptr->string) || ptr->pos >= RSTRING_LEN(ptr->string))) {
if (eos_p(ptr)) {
if (!NIL_P(str)) rb_str_resize(str, 0);
return Qnil;
return len > 0 ? Qnil : rb_str_new(0, 0);
}
binary = 1;
break;
@ -1684,7 +1698,7 @@ strio_pread(int argc, VALUE *argv, VALUE self)
struct StringIO *ptr = readable(self);
if (offset >= RSTRING_LEN(ptr->string)) {
if (outside_p(ptr, offset)) {
rb_eof_error();
}
@ -1921,7 +1935,7 @@ Init_stringio(void)
#undef rb_intern
#ifdef HAVE_RB_EXT_RACTOR_SAFE
rb_ext_ractor_safe(true);
rb_ext_ractor_safe(true);
#endif
VALUE StringIO = rb_define_class("StringIO", rb_cObject);

63
gc.c
View file

@ -1047,7 +1047,7 @@ rb_data_object_wrap(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FU
{
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)datap, (VALUE)dfree, !dmark, sizeof(struct RTypedData));
return newobj_of(GET_RACTOR(), klass, T_DATA, (VALUE)dmark, (VALUE)dfree, (VALUE)datap, !dmark, sizeof(struct RTypedData));
}
VALUE
@ -1064,7 +1064,7 @@ typed_data_alloc(VALUE klass, VALUE typed_flag, void *datap, const rb_data_type_
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) | IS_TYPED_DATA | typed_flag, (VALUE)datap, 0, wb_protected, size);
return newobj_of(GET_RACTOR(), klass, T_DATA, 0, ((VALUE)type) | IS_TYPED_DATA | typed_flag, (VALUE)datap, wb_protected, size);
}
VALUE
@ -1755,7 +1755,6 @@ rb_gc_pointer_to_heap_p(VALUE obj)
#define LAST_OBJECT_ID() (object_id_counter * OBJ_ID_INCREMENT)
static VALUE id2ref_value = 0;
static st_table *id2ref_tbl = NULL;
static bool id2ref_tbl_built = false;
#if SIZEOF_SIZE_T == SIZEOF_LONG_LONG
static size_t object_id_counter = 1;
@ -1947,6 +1946,7 @@ build_id2ref_i(VALUE obj, void *data)
case T_CLASS:
case T_MODULE:
if (RCLASS(obj)->object_id) {
RUBY_ASSERT(!rb_objspace_garbage_object_p(obj));
st_insert(id2ref_tbl, RCLASS(obj)->object_id, obj);
}
break;
@ -1955,6 +1955,7 @@ build_id2ref_i(VALUE obj, void *data)
break;
default:
if (rb_shape_obj_has_id(obj)) {
RUBY_ASSERT(!rb_objspace_garbage_object_p(obj));
st_insert(id2ref_tbl, rb_obj_id(obj), obj);
}
break;
@ -1974,17 +1975,20 @@ object_id_to_ref(void *objspace_ptr, VALUE object_id)
// GC Must not trigger while we build the table, otherwise if we end
// up freeing an object that had an ID, we might try to delete it from
// the table even though it wasn't inserted yet.
id2ref_tbl = st_init_table(&object_id_hash_type);
id2ref_value = TypedData_Wrap_Struct(0, &id2ref_tbl_type, id2ref_tbl);
st_table *tmp_id2ref_tbl = st_init_table(&object_id_hash_type);
VALUE tmp_id2ref_value = TypedData_Wrap_Struct(0, &id2ref_tbl_type, tmp_id2ref_tbl);
// build_id2ref_i will most certainly malloc, which could trigger GC and sweep
// objects we just added to the table.
bool gc_disabled = RTEST(rb_gc_disable_no_rest());
// By calling rb_gc_disable() we also save having to handle potentially garbage objects.
bool gc_disabled = RTEST(rb_gc_disable());
{
id2ref_tbl = tmp_id2ref_tbl;
id2ref_value = tmp_id2ref_value;
rb_gc_impl_each_object(objspace, build_id2ref_i, (void *)id2ref_tbl);
}
if (!gc_disabled) rb_gc_enable();
id2ref_tbl_built = true;
}
VALUE obj;
@ -2036,10 +2040,9 @@ obj_free_object_id(VALUE obj)
RUBY_ASSERT(FIXNUM_P(obj_id) || RB_TYPE_P(obj_id, T_BIGNUM));
if (!st_delete(id2ref_tbl, (st_data_t *)&obj_id, NULL)) {
// If we're currently building the table then it's not a bug.
// The the object is a T_IMEMO/fields, then it's possible the actual object
// has been garbage collected already.
if (id2ref_tbl_built && !RB_TYPE_P(obj, T_IMEMO)) {
if (!RB_TYPE_P(obj, T_IMEMO)) {
rb_bug("Object ID seen, but not in _id2ref table: object_id=%llu object=%s", NUM2ULL(obj_id), rb_obj_info(obj));
}
}
@ -3028,7 +3031,7 @@ rb_gc_mark_roots(void *objspace, const char **categoryp)
mark_current_machine_context(ec);
MARK_CHECKPOINT("global_symbols");
rb_sym_global_symbols_mark();
rb_sym_global_symbols_mark_and_move();
MARK_CHECKPOINT("finish");
@ -3170,10 +3173,15 @@ rb_gc_mark_children(void *objspace, VALUE obj)
break;
case T_DATA: {
void *const ptr = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj);
bool typed_data = RTYPEDDATA_P(obj);
void *const ptr = typed_data ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj);
if (typed_data) {
gc_mark_internal(RTYPEDDATA(obj)->fields_obj);
}
if (ptr) {
if (RTYPEDDATA_P(obj) && gc_declarative_marking_p(RTYPEDDATA_TYPE(obj))) {
if (typed_data && gc_declarative_marking_p(RTYPEDDATA_TYPE(obj))) {
size_t *offset_list = TYPED_DATA_REFS_OFFSET_LIST(obj);
for (size_t offset = *offset_list; offset != RUBY_REF_END; offset = *offset_list++) {
@ -3181,7 +3189,7 @@ rb_gc_mark_children(void *objspace, VALUE obj)
}
}
else {
RUBY_DATA_FUNC mark_func = RTYPEDDATA_P(obj) ?
RUBY_DATA_FUNC mark_func = typed_data ?
RTYPEDDATA_TYPE(obj)->function.dmark :
RDATA(obj)->dmark;
if (mark_func) (*mark_func)(ptr);
@ -3260,6 +3268,10 @@ rb_gc_mark_children(void *objspace, VALUE obj)
gc_mark_internal(ptr[i]);
}
if (!FL_TEST_RAW(obj, RSTRUCT_GEN_FIELDS)) {
gc_mark_internal(RSTRUCT_FIELDS_OBJ(obj));
}
break;
}
@ -4041,7 +4053,7 @@ rb_gc_update_vm_references(void *objspace)
rb_vm_update_references(vm);
rb_gc_update_global_tbl();
rb_sym_global_symbols_update_references();
rb_sym_global_symbols_mark_and_move();
#if USE_YJIT
void rb_yjit_root_update_references(void); // in Rust
@ -4114,9 +4126,15 @@ rb_gc_update_object_references(void *objspace, VALUE obj)
case T_DATA:
/* Call the compaction callback, if it exists */
{
void *const ptr = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj);
bool typed_data = RTYPEDDATA_P(obj);
void *const ptr = typed_data ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj);
if (typed_data) {
UPDATE_IF_MOVED(objspace, RTYPEDDATA(obj)->fields_obj);
}
if (ptr) {
if (RTYPEDDATA_P(obj) && gc_declarative_marking_p(RTYPEDDATA_TYPE(obj))) {
if (typed_data && gc_declarative_marking_p(RTYPEDDATA_TYPE(obj))) {
size_t *offset_list = TYPED_DATA_REFS_OFFSET_LIST(obj);
for (size_t offset = *offset_list; offset != RUBY_REF_END; offset = *offset_list++) {
@ -4124,7 +4142,7 @@ rb_gc_update_object_references(void *objspace, VALUE obj)
*ref = gc_location_internal(objspace, *ref);
}
}
else if (RTYPEDDATA_P(obj)) {
else if (typed_data) {
RUBY_DATA_FUNC compact_func = RTYPEDDATA_TYPE(obj)->function.dcompact;
if (compact_func) (*compact_func)(ptr);
}
@ -4188,6 +4206,15 @@ rb_gc_update_object_references(void *objspace, VALUE obj)
for (i = 0; i < len; i++) {
UPDATE_IF_MOVED(objspace, ptr[i]);
}
if (RSTRUCT_EMBED_LEN(obj)) {
if (!FL_TEST_RAW(obj, RSTRUCT_GEN_FIELDS)) {
UPDATE_IF_MOVED(objspace, ptr[len]);
}
}
else {
UPDATE_IF_MOVED(objspace, RSTRUCT(obj)->as.heap.fields_obj);
}
}
break;
default:
@ -4341,7 +4368,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

210
gc.rb
View file

@ -7,31 +7,39 @@
# You may obtain information about the operation of the \GC through GC::Profiler.
module GC
# Initiates garbage collection, even if manually disabled.
# Initiates garbage collection, even if explicitly disabled by GC.disable.
#
# The +full_mark+ keyword argument determines whether or not to perform a
# major garbage collection cycle. When set to +true+, a major garbage
# collection cycle is run, meaning all objects are marked. When set to
# +false+, a minor garbage collection cycle is run, meaning only young
# objects are marked.
# Keyword arguments:
#
# The +immediate_mark+ keyword argument determines whether or not to perform
# incremental marking. When set to +true+, marking is completed during the
# call to this method. When set to +false+, marking is performed in steps
# that are interleaved with future Ruby code execution, so marking might not
# be completed during this method call. Note that if +full_mark+ is +false+,
# then marking will always be immediate, regardless of the value of
# +immediate_mark+.
# - +full_mark+:
# its boolean value determines whether to perform a major garbage collection cycle:
#
# The +immediate_sweep+ keyword argument determines whether or not to defer
# sweeping (using lazy sweep). When set to +false+, sweeping is performed in
# steps that are interleaved with future Ruby code execution, so sweeping might
# not be completed during this method call. When set to +true+, sweeping is
# completed during the call to this method.
# - +true+: initiates a major garbage collection cycle,
# meaning all objects (old and new) are marked.
# - +false+: initiates a minor garbage collection cycle,
# meaning only young objects are marked.
#
# Note: These keyword arguments are implementation and version-dependent. They
# are not guaranteed to be future-compatible and may be ignored if the
# underlying implementation does not support them.
# - +immediate_mark+:
# its boolean value determines whether to perform incremental marking:
#
# - +true+: marking is completed before the method returns.
# - +false+: marking is performed by parts,
# interleaved with program execution both before the method returns and afterward;
# therefore marking may not be completed before the return.
# Note that if +full_mark+ is +false+, marking will always be immediate,
# regardless of the value of +immediate_mark+.
#
# - +immediate_sweep+:
# its boolean value determines whether to defer sweeping (using lazy sweep):
#
# - +true+: sweeping is completed before the method returns.
# - +false+: sweeping is performed by parts,
# interleaved with program execution both before the method returns and afterward;
# therefore sweeping may not be completed before the return.
#
# Note that these keword arguments are implementation- and version-dependent,
# are not guaranteed to be future-compatible,
# and may be ignored in some implementations.
def self.start full_mark: true, immediate_mark: true, immediate_sweep: true
Primitive.gc_start_internal full_mark, immediate_mark, immediate_sweep, false
end
@ -42,14 +50,14 @@ module GC
end
# call-seq:
# GC.enable -> true or false
# GC.enable -> true or false
#
# Enables garbage collection, returning +true+ if garbage
# collection was previously disabled.
# Enables garbage collection;
# returns whether garbage collection was disabled:
#
# GC.disable #=> false
# GC.enable #=> true
# GC.enable #=> false
# GC.disable
# GC.enable # => true
# GC.enable # => false
#
def self.enable
Primitive.gc_enable
@ -58,11 +66,13 @@ module GC
# call-seq:
# GC.disable -> true or false
#
# Disables garbage collection, returning +true+ if garbage
# collection was already disabled.
# Disables garbage collection (but GC.start remains potent):
# returns whether garbage collection was already disabled.
#
# GC.enable
# GC.disable # => false
# GC.disable # => true
#
# GC.disable #=> false
# GC.disable #=> true
def self.disable
Primitive.gc_disable
end
@ -250,99 +260,111 @@ module GC
# call-seq:
# GC.config -> hash
# GC.config(hash) -> hash
# GC.config(hash_to_merge) -> hash
#
# Sets or gets information about the current \GC config.
# This method is implementation-specific to CRuby.
#
# 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.
# Configuration parameters are \GC implementation-specific and may change without notice.
#
# 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.
# With no argument given, returns a hash containing the configuration:
#
# 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.
# GC.config
# # => {rgengc_allow_full_mark: true, implementation: "default"}
#
# In both call-seqs, the return value of <code>GC.config</code> 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.
# With argument +hash_to_merge+ given,
# merges that hash into the stored configuration hash;
# ignores unknown hash keys;
# returns the configuration hash:
#
# This method is only expected to work on CRuby.
# GC.config(rgengc_allow_full_mark: false)
# # => {rgengc_allow_full_mark: false, implementation: "default"}
# GC.config(foo: 'bar')
# # => {rgengc_allow_full_mark: false, implementation: "default"}
#
# === \GC Implementation independent values
# <b>All-Implementations Configuration</b>
#
# The <code>GC.config</code> 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+.
# The single read-only entry for all implementations is:
#
# There is currently only one global, read-only key:
# - +implementation+:
# the string name of the implementation;
# for the Ruby default implementation, <tt>'default'</tt>.
#
# [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
# <b>Implementation-Specific Configuration</b>
#
# === \GC Implementation specific values
# A \GC implementation maintains its own implementation-specific configuration.
#
# \GC libraries are expected to document their own configuration. Valid keys
# for Ruby's default \GC implementation are:
# For Ruby's default implementation the single entry is:
#
# [rgengc_allow_full_mark]
# Controls whether the \GC is allowed to run a full mark (young & old objects).
# - +rgengc_allow_full_mark+:
# Controls whether the \GC is allowed to run a full mark (young & old objects):
#
# When +true+, \GC interleaves major and minor collections. This is the default. \GC
# will function as intended.
# - +true+ (default): \GC interleaves major and minor collections.
# 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+.
#
# 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
# <code>GC.latest_gc_info(:need_major_by)</code>
#
# The user can trigger a major collection at any time using
# <code>GC.start(full_mark: true)</code>
#
# When +false+, Young to Old object promotion is disabled. For performance
# reasons, it is recommended to warm up an application using +Process.warmup+
# 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:
# GC.latest_gc_info -> hash
# GC.latest_gc_info(hash) -> hash
# GC.latest_gc_info(key) -> value
# GC.latest_gc_info -> new_hash
# GC.latest_gc_info(key) -> value
# GC.latest_gc_info(hash) -> hash
#
# Returns information about the most recent garbage collection.
# With no argument given,
# returns information about the most recent garbage collection:
#
# If the argument +hash+ is given and is a Hash object,
# it is overwritten and returned.
# This is intended to avoid the probe effect.
# GC.latest_gc_info
# # =>
# {major_by: :force,
# need_major_by: nil,
# gc_by: :method,
# have_finalizer: false,
# immediate_sweep: true,
# state: :none,
# weak_references_count: 0,
# retained_weak_references_count: 0}
#
# With symbol argument +key+ given,
# returns the value for that key:
#
# GC.latest_gc_info(:gc_by) # => :newobj
#
# With hash argument +hash+ given,
# returns that hash with GC information merged into its content;
# this form may be useful in minimizing {probe effects}[https://en.wikipedia.org/wiki/Probe_effect]:
#
# h = {foo: 0, bar: 1}
# GC.latest_gc_info(h)
# # =>
# {foo: 0,
# bar: 1,
# major_by: nil,
# need_major_by: nil,
# gc_by: :newobj,
# have_finalizer: false,
# immediate_sweep: false,
# state: :sweeping,
# weak_references_count: 0,
# retained_weak_references_count: 0}
#
# If the argument +key+ is given and is a Symbol object,
# it returns the value associated with the key.
# This is equivalent to <tt>GC.latest_gc_info[key]</tt>.
def self.latest_gc_info hash_or_key = nil
if hash_or_key == nil
hash_or_key = {}

View file

@ -4420,7 +4420,10 @@ rb_gc_impl_mark_and_move(void *objspace_ptr, VALUE *ptr)
GC_ASSERT(objspace->flags.during_compacting);
GC_ASSERT(during_gc);
*ptr = rb_gc_impl_location(objspace, *ptr);
VALUE destination = rb_gc_impl_location(objspace, *ptr);
if (destination != *ptr) {
*ptr = destination;
}
}
else {
gc_mark(objspace, *ptr);
@ -6107,15 +6110,19 @@ rb_gc_impl_writebarrier_remember(void *objspace_ptr, VALUE obj)
gc_report(1, objspace, "rb_gc_writebarrier_remember: %s\n", rb_obj_info(obj));
if (is_incremental_marking(objspace)) {
if (RVALUE_BLACK_P(objspace, obj)) {
gc_grey(objspace, obj);
}
}
else {
if (RVALUE_OLD_P(objspace, obj)) {
rgengc_remember(objspace, obj);
if (is_incremental_marking(objspace) || RVALUE_OLD_P(objspace, obj)) {
int lev = RB_GC_VM_LOCK_NO_BARRIER();
{
if (is_incremental_marking(objspace)) {
if (RVALUE_BLACK_P(objspace, obj)) {
gc_grey(objspace, obj);
}
}
else if (RVALUE_OLD_P(objspace, obj)) {
rgengc_remember(objspace, obj);
}
}
RB_GC_VM_UNLOCK_NO_BARRIER(lev);
}
}

View file

@ -39,7 +39,7 @@ ostruct 0.6.3 https://github.com/ruby/ostruct
pstore 0.2.0 https://github.com/ruby/pstore
benchmark 0.4.1 https://github.com/ruby/benchmark
logger 1.7.0 https://github.com/ruby/logger
rdoc 6.14.2 https://github.com/ruby/rdoc
rdoc 6.14.2 https://github.com/ruby/rdoc f4a90c6010b2346cb5426d4496f5a37a136a82fb # for markdown
win32ole 1.9.2 https://github.com/ruby/win32ole
irb 1.15.2 https://github.com/ruby/irb 331c4e851296b115db766c291e8cf54a2492fb36
reline 0.6.2 https://github.com/ruby/reline

37
hash.c
View file

@ -5192,25 +5192,26 @@ env_enc_str_new(const char *ptr, long len, rb_encoding *enc)
}
static VALUE
env_str_new(const char *ptr, long len)
env_str_new(const char *ptr, long len, rb_encoding *enc)
{
return env_enc_str_new(ptr, len, env_encoding());
return env_enc_str_new(ptr, len, enc);
}
static VALUE
env_str_new2(const char *ptr)
env_str_new2(const char *ptr, rb_encoding *enc)
{
if (!ptr) return Qnil;
return env_str_new(ptr, strlen(ptr));
return env_str_new(ptr, strlen(ptr), enc);
}
static VALUE
getenv_with_lock(const char *name)
{
VALUE ret;
rb_encoding *enc = env_encoding();
ENV_LOCKING() {
const char *val = getenv(name);
ret = env_str_new2(val);
ret = env_str_new2(val, enc);
}
return ret;
}
@ -5773,13 +5774,14 @@ env_values(void)
{
VALUE ary = rb_ary_new();
rb_encoding *enc = env_encoding();
ENV_LOCKING() {
char **env = GET_ENVIRON(environ);
while (*env) {
char *s = strchr(*env, '=');
if (s) {
rb_ary_push(ary, env_str_new2(s+1));
rb_ary_push(ary, env_str_new2(s+1, enc));
}
env++;
}
@ -5865,14 +5867,15 @@ env_each_pair(VALUE ehash)
VALUE ary = rb_ary_new();
rb_encoding *enc = env_encoding();
ENV_LOCKING() {
char **env = GET_ENVIRON(environ);
while (*env) {
char *s = strchr(*env, '=');
if (s) {
rb_ary_push(ary, env_str_new(*env, s-*env));
rb_ary_push(ary, env_str_new2(s+1));
rb_ary_push(ary, env_str_new(*env, s-*env, enc));
rb_ary_push(ary, env_str_new2(s+1, enc));
}
env++;
}
@ -6255,13 +6258,14 @@ env_to_a(VALUE _)
{
VALUE ary = rb_ary_new();
rb_encoding *enc = env_encoding();
ENV_LOCKING() {
char **env = GET_ENVIRON(environ);
while (*env) {
char *s = strchr(*env, '=');
if (s) {
rb_ary_push(ary, rb_assoc_new(env_str_new(*env, s-*env),
env_str_new2(s+1)));
rb_ary_push(ary, rb_assoc_new(env_str_new(*env, s-*env, enc),
env_str_new2(s+1, enc)));
}
env++;
}
@ -6509,6 +6513,7 @@ env_key(VALUE dmy, VALUE value)
StringValue(value);
VALUE str = Qnil;
rb_encoding *enc = env_encoding();
ENV_LOCKING() {
char **env = GET_ENVIRON(environ);
while (*env) {
@ -6516,7 +6521,7 @@ env_key(VALUE dmy, VALUE value)
if (s++) {
long len = strlen(s);
if (RSTRING_LEN(value) == len && strncmp(s, RSTRING_PTR(value), len) == 0) {
str = env_str_new(*env, s-*env-1);
str = env_str_new(*env, s-*env-1, enc);
break;
}
}
@ -6533,13 +6538,14 @@ env_to_hash(void)
{
VALUE hash = rb_hash_new();
rb_encoding *enc = env_encoding();
ENV_LOCKING() {
char **env = GET_ENVIRON(environ);
while (*env) {
char *s = strchr(*env, '=');
if (s) {
rb_hash_aset(hash, env_str_new(*env, s-*env),
env_str_new2(s+1));
rb_hash_aset(hash, env_str_new(*env, s-*env, enc),
env_str_new2(s+1, enc));
}
env++;
}
@ -6684,14 +6690,15 @@ env_shift(VALUE _)
VALUE result = Qnil;
VALUE key = Qnil;
rb_encoding *enc = env_encoding();
ENV_LOCKING() {
char **env = GET_ENVIRON(environ);
if (*env) {
const char *p = *env;
char *s = strchr(p, '=');
if (s) {
key = env_str_new(p, s-p);
VALUE val = env_str_new2(getenv(RSTRING_PTR(key)));
key = env_str_new(p, s-p, enc);
VALUE val = env_str_new2(getenv(RSTRING_PTR(key)), enc);
result = rb_assoc_new(key, val);
}
}

36
imemo.c
View file

@ -337,32 +337,44 @@ rb_imemo_mark_and_move(VALUE obj, bool reference_updating)
* cc->klass (klass) should not be marked because if the klass is
* free'ed, the cc->klass will be cleared by `vm_cc_invalidate()`.
*
* cc->cme (cme) should not be marked because if cc is invalidated
* when cme is free'ed.
* For "normal" CCs cc->cme (cme) should not be marked because the cc is
* invalidated through the klass when the cme is free'd.
* - klass marks cme if klass uses cme.
* - caller classe's ccs->cme marks cc->cme.
* - if cc is invalidated (klass doesn't refer the cc),
* cc is invalidated by `vm_cc_invalidate()` and cc->cme is
* not be accessed.
* - On the multi-Ractors, cme will be collected with global GC
* - caller class's ccs->cme marks cc->cme.
* - if cc is invalidated (klass doesn't refer the cc), cc is
* invalidated by `vm_cc_invalidate()` after which cc->cme must not
* be accessed.
* - With multi-Ractors, cme will be collected with global GC
* so that it is safe if GC is not interleaving while accessing
* cc and cme.
* - However, cc_type_super and cc_type_refinement are not chained
* from ccs so cc->cme should be marked; the cme might be
* reachable only through cc in these cases.
*
* However cc_type_super and cc_type_refinement are not chained
* from ccs so cc->cme should be marked as long as the cc is valid;
* the cme might be reachable only through cc in these cases.
*/
struct rb_callcache *cc = (struct rb_callcache *)obj;
if (reference_updating) {
if (UNDEF_P(cc->klass)) {
/* If it's invalidated, we must not mark anything.
* All fields should are considered invalid
*/
}
else if (reference_updating) {
if (moved_or_living_object_strictly_p((VALUE)cc->cme_)) {
*((VALUE *)&cc->klass) = rb_gc_location(cc->klass);
*((struct rb_callable_method_entry_struct **)&cc->cme_) =
(struct rb_callable_method_entry_struct *)rb_gc_location((VALUE)cc->cme_);
RUBY_ASSERT(RB_TYPE_P(cc->klass, T_CLASS) || RB_TYPE_P(cc->klass, T_ICLASS));
RUBY_ASSERT(IMEMO_TYPE_P((VALUE)cc->cme_, imemo_ment));
}
else if (vm_cc_valid(cc)) {
else {
vm_cc_invalidate(cc);
}
}
else {
RUBY_ASSERT(RB_TYPE_P(cc->klass, T_CLASS) || RB_TYPE_P(cc->klass, T_ICLASS));
RUBY_ASSERT(IMEMO_TYPE_P((VALUE)cc->cme_, imemo_ment));
rb_gc_mark_weak((VALUE *)&cc->klass);
if ((vm_cc_super_p(cc) || vm_cc_refinement_p(cc))) {
rb_gc_mark_movable((VALUE)cc->cme_);

View file

@ -84,6 +84,28 @@ typedef unsigned int rb_atomic_t;
# error No atomic operation found
#endif
/* Memory ordering constants */
#if defined(HAVE_GCC_ATOMIC_BUILTINS)
# define RBIMPL_ATOMIC_RELAXED __ATOMIC_RELAXED
# define RBIMPL_ATOMIC_ACQUIRE __ATOMIC_ACQUIRE
# define RBIMPL_ATOMIC_RELEASE __ATOMIC_RELEASE
# define RBIMPL_ATOMIC_ACQ_REL __ATOMIC_ACQ_REL
# define RBIMPL_ATOMIC_SEQ_CST __ATOMIC_SEQ_CST
#elif defined(HAVE_STDATOMIC_H)
# define RBIMPL_ATOMIC_RELAXED memory_order_relaxed
# define RBIMPL_ATOMIC_ACQUIRE memory_order_acquire
# define RBIMPL_ATOMIC_RELEASE memory_order_release
# define RBIMPL_ATOMIC_ACQ_REL memory_order_acq_rel
# define RBIMPL_ATOMIC_SEQ_CST memory_order_seq_cst
#else
/* Dummy values for unsupported platforms */
# define RBIMPL_ATOMIC_RELAXED 0
# define RBIMPL_ATOMIC_ACQUIRE 1
# define RBIMPL_ATOMIC_RELEASE 2
# define RBIMPL_ATOMIC_ACQ_REL 3
# define RBIMPL_ATOMIC_SEQ_CST 4
#endif
/**
* Atomically replaces the value pointed by `var` with the result of addition
* of `val` to the old value of `var`.
@ -93,7 +115,7 @@ typedef unsigned int rb_atomic_t;
* @return What was stored in `var` before the addition.
* @post `var` holds `var + val`.
*/
#define RUBY_ATOMIC_FETCH_ADD(var, val) rbimpl_atomic_fetch_add(&(var), (val))
#define RUBY_ATOMIC_FETCH_ADD(var, val) rbimpl_atomic_fetch_add(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
/**
* Atomically replaces the value pointed by `var` with the result of
@ -104,7 +126,7 @@ typedef unsigned int rb_atomic_t;
* @return What was stored in `var` before the subtraction.
* @post `var` holds `var - val`.
*/
#define RUBY_ATOMIC_FETCH_SUB(var, val) rbimpl_atomic_fetch_sub(&(var), (val))
#define RUBY_ATOMIC_FETCH_SUB(var, val) rbimpl_atomic_fetch_sub(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
/**
* Atomically replaces the value pointed by `var` with the result of
@ -116,7 +138,7 @@ typedef unsigned int rb_atomic_t;
* @post `var` holds `var | val`.
* @note For portability, this macro can return void.
*/
#define RUBY_ATOMIC_OR(var, val) rbimpl_atomic_or(&(var), (val))
#define RUBY_ATOMIC_OR(var, val) rbimpl_atomic_or(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
/**
* Atomically replaces the value pointed by `var` with `val`. This is just an
@ -127,7 +149,7 @@ typedef unsigned int rb_atomic_t;
* @return What was stored in `var` before the assignment.
* @post `var` holds `val`.
*/
#define RUBY_ATOMIC_EXCHANGE(var, val) rbimpl_atomic_exchange(&(var), (val))
#define RUBY_ATOMIC_EXCHANGE(var, val) rbimpl_atomic_exchange(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
/**
* Atomic compare-and-swap. This stores `val` to `var` if and only if the
@ -141,7 +163,7 @@ typedef unsigned int rb_atomic_t;
* @retval otherwise Something else is at `var`; not updated.
*/
#define RUBY_ATOMIC_CAS(var, oldval, newval) \
rbimpl_atomic_cas(&(var), (oldval), (newval))
rbimpl_atomic_cas(&(var), (oldval), (newval), RBIMPL_ATOMIC_SEQ_CST, RBIMPL_ATOMIC_SEQ_CST)
/**
* Atomic load. This loads `var` with an atomic intrinsic and returns
@ -150,7 +172,7 @@ typedef unsigned int rb_atomic_t;
* @param var A variable of ::rb_atomic_t
* @return What was stored in `var`j
*/
#define RUBY_ATOMIC_LOAD(var) rbimpl_atomic_load(&(var))
#define RUBY_ATOMIC_LOAD(var) rbimpl_atomic_load(&(var), RBIMPL_ATOMIC_SEQ_CST)
/**
* Identical to #RUBY_ATOMIC_EXCHANGE, except for the return type.
@ -160,7 +182,7 @@ typedef unsigned int rb_atomic_t;
* @return void
* @post `var` holds `val`.
*/
#define RUBY_ATOMIC_SET(var, val) rbimpl_atomic_set(&(var), (val))
#define RUBY_ATOMIC_SET(var, val) rbimpl_atomic_store(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
/**
* Identical to #RUBY_ATOMIC_FETCH_ADD, except for the return type.
@ -170,7 +192,7 @@ typedef unsigned int rb_atomic_t;
* @return void
* @post `var` holds `var + val`.
*/
#define RUBY_ATOMIC_ADD(var, val) rbimpl_atomic_add(&(var), (val))
#define RUBY_ATOMIC_ADD(var, val) rbimpl_atomic_add(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
/**
* Identical to #RUBY_ATOMIC_FETCH_SUB, except for the return type.
@ -180,7 +202,7 @@ typedef unsigned int rb_atomic_t;
* @return void
* @post `var` holds `var - val`.
*/
#define RUBY_ATOMIC_SUB(var, val) rbimpl_atomic_sub(&(var), (val))
#define RUBY_ATOMIC_SUB(var, val) rbimpl_atomic_sub(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
/**
* Atomically increments the value pointed by `var`.
@ -189,7 +211,7 @@ typedef unsigned int rb_atomic_t;
* @return void
* @post `var` holds `var + 1`.
*/
#define RUBY_ATOMIC_INC(var) rbimpl_atomic_inc(&(var))
#define RUBY_ATOMIC_INC(var) rbimpl_atomic_inc(&(var), RBIMPL_ATOMIC_SEQ_CST)
/**
* Atomically decrements the value pointed by `var`.
@ -198,7 +220,7 @@ typedef unsigned int rb_atomic_t;
* @return void
* @post `var` holds `var - 1`.
*/
#define RUBY_ATOMIC_DEC(var) rbimpl_atomic_dec(&(var))
#define RUBY_ATOMIC_DEC(var) rbimpl_atomic_dec(&(var), RBIMPL_ATOMIC_SEQ_CST)
/**
* Identical to #RUBY_ATOMIC_FETCH_ADD, except it expects its arguments to be `size_t`.
@ -210,7 +232,7 @@ typedef unsigned int rb_atomic_t;
* @return What was stored in `var` before the addition.
* @post `var` holds `var + val`.
*/
#define RUBY_ATOMIC_SIZE_FETCH_ADD(var, val) rbimpl_atomic_size_fetch_add(&(var), (val))
#define RUBY_ATOMIC_SIZE_FETCH_ADD(var, val) rbimpl_atomic_size_fetch_add(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
/**
* Identical to #RUBY_ATOMIC_INC, except it expects its argument is `size_t`.
@ -221,7 +243,7 @@ typedef unsigned int rb_atomic_t;
* @return void
* @post `var` holds `var + 1`.
*/
#define RUBY_ATOMIC_SIZE_INC(var) rbimpl_atomic_size_inc(&(var))
#define RUBY_ATOMIC_SIZE_INC(var) rbimpl_atomic_size_inc(&(var), RBIMPL_ATOMIC_SEQ_CST)
/**
* Identical to #RUBY_ATOMIC_DEC, except it expects its argument is `size_t`.
@ -232,7 +254,7 @@ typedef unsigned int rb_atomic_t;
* @return void
* @post `var` holds `var - 1`.
*/
#define RUBY_ATOMIC_SIZE_DEC(var) rbimpl_atomic_size_dec(&(var))
#define RUBY_ATOMIC_SIZE_DEC(var) rbimpl_atomic_size_dec(&(var), RBIMPL_ATOMIC_SEQ_CST)
/**
* Identical to #RUBY_ATOMIC_EXCHANGE, except it expects its arguments are
@ -246,7 +268,7 @@ typedef unsigned int rb_atomic_t;
* @post `var` holds `val`.
*/
#define RUBY_ATOMIC_SIZE_EXCHANGE(var, val) \
rbimpl_atomic_size_exchange(&(var), (val))
rbimpl_atomic_size_exchange(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
/**
* Identical to #RUBY_ATOMIC_CAS, except it expects its arguments are `size_t`.
@ -260,7 +282,7 @@ typedef unsigned int rb_atomic_t;
* @retval otherwise Something else is at `var`; not updated.
*/
#define RUBY_ATOMIC_SIZE_CAS(var, oldval, newval) \
rbimpl_atomic_size_cas(&(var), (oldval), (newval))
rbimpl_atomic_size_cas(&(var), (oldval), (newval), RBIMPL_ATOMIC_SEQ_CST, RBIMPL_ATOMIC_SEQ_CST)
/**
* Identical to #RUBY_ATOMIC_ADD, except it expects its arguments are `size_t`.
@ -272,7 +294,7 @@ typedef unsigned int rb_atomic_t;
* @return void
* @post `var` holds `var + val`.
*/
#define RUBY_ATOMIC_SIZE_ADD(var, val) rbimpl_atomic_size_add(&(var), (val))
#define RUBY_ATOMIC_SIZE_ADD(var, val) rbimpl_atomic_size_add(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
/**
* Identical to #RUBY_ATOMIC_SUB, except it expects its arguments are `size_t`.
@ -284,7 +306,7 @@ typedef unsigned int rb_atomic_t;
* @return void
* @post `var` holds `var - val`.
*/
#define RUBY_ATOMIC_SIZE_SUB(var, val) rbimpl_atomic_size_sub(&(var), (val))
#define RUBY_ATOMIC_SIZE_SUB(var, val) rbimpl_atomic_size_sub(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
/**
* Identical to #RUBY_ATOMIC_EXCHANGE, except it expects its arguments are
@ -303,7 +325,7 @@ typedef unsigned int rb_atomic_t;
* some pointers, most notably function pointers.
*/
#define RUBY_ATOMIC_PTR_EXCHANGE(var, val) \
RBIMPL_CAST(rbimpl_atomic_ptr_exchange((void **)&(var), (void *)val))
RBIMPL_CAST(rbimpl_atomic_ptr_exchange((void **)&(var), (void *)val, RBIMPL_ATOMIC_SEQ_CST))
/**
* Identical to #RUBY_ATOMIC_LOAD, except it expects its arguments are `void*`.
@ -314,7 +336,7 @@ typedef unsigned int rb_atomic_t;
* @return The value of `var` (without tearing)
*/
#define RUBY_ATOMIC_PTR_LOAD(var) \
RBIMPL_CAST(rbimpl_atomic_ptr_load((void **)&var))
RBIMPL_CAST(rbimpl_atomic_ptr_load((void **)&var, RBIMPL_ATOMIC_SEQ_CST))
/**
* Identical to #RUBY_ATOMIC_SET, except it expects its arguments are
@ -327,7 +349,7 @@ typedef unsigned int rb_atomic_t;
* @post `var` holds `val`.
*/
#define RUBY_ATOMIC_PTR_SET(var, val) \
rbimpl_atomic_ptr_set((volatile void **)&(var), (val))
rbimpl_atomic_ptr_store((volatile void **)&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
/**
* Identical to #RUBY_ATOMIC_CAS, except it expects its arguments are `void*`.
@ -341,7 +363,7 @@ typedef unsigned int rb_atomic_t;
* @retval otherwise Something else is at `var`; not updated.
*/
#define RUBY_ATOMIC_PTR_CAS(var, oldval, newval) \
RBIMPL_CAST(rbimpl_atomic_ptr_cas((void **)&(var), (void *)(oldval), (void *)(newval)))
RBIMPL_CAST(rbimpl_atomic_ptr_cas((void **)&(var), (void *)(oldval), (void *)(newval), RBIMPL_ATOMIC_SEQ_CST, RBIMPL_ATOMIC_SEQ_CST))
/**
* Identical to #RUBY_ATOMIC_SET, except it expects its arguments are
@ -354,7 +376,7 @@ typedef unsigned int rb_atomic_t;
* @post `var` holds `val`.
*/
#define RUBY_ATOMIC_VALUE_SET(var, val) \
rbimpl_atomic_value_set(&(var), (val))
rbimpl_atomic_value_store(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
/**
* Identical to #RUBY_ATOMIC_EXCHANGE, except it expects its arguments are
@ -368,7 +390,7 @@ typedef unsigned int rb_atomic_t;
* @post `var` holds `val`.
*/
#define RUBY_ATOMIC_VALUE_EXCHANGE(var, val) \
rbimpl_atomic_value_exchange(&(var), (val))
rbimpl_atomic_value_exchange(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
/**
* Identical to #RUBY_ATOMIC_CAS, except it expects its arguments are ::VALUE.
@ -382,19 +404,20 @@ typedef unsigned int rb_atomic_t;
* @retval otherwise Something else is at `var`; not updated.
*/
#define RUBY_ATOMIC_VALUE_CAS(var, oldval, newval) \
rbimpl_atomic_value_cas(&(var), (oldval), (newval))
rbimpl_atomic_value_cas(&(var), (oldval), (newval), RBIMPL_ATOMIC_SEQ_CST, RBIMPL_ATOMIC_SEQ_CST)
/** @cond INTERNAL_MACRO */
RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline rb_atomic_t
rbimpl_atomic_fetch_add(volatile rb_atomic_t *ptr, rb_atomic_t val)
rbimpl_atomic_fetch_add(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
{
(void)memory_order;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
return __atomic_fetch_add(ptr, val, __ATOMIC_SEQ_CST);
return __atomic_fetch_add(ptr, val, memory_order);
#elif defined(HAVE_GCC_SYNC_BUILTINS)
return __sync_fetch_and_add(ptr, val);
@ -412,7 +435,7 @@ rbimpl_atomic_fetch_add(volatile rb_atomic_t *ptr, rb_atomic_t val)
return atomic_add_int_nv(ptr, val) - val;
#elif defined(HAVE_STDATOMIC_H)
return atomic_fetch_add((_Atomic volatile rb_atomic_t *)ptr, val);
return atomic_fetch_add_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order);
#else
# error Unsupported platform.
@ -424,12 +447,13 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline size_t
rbimpl_atomic_size_fetch_add(volatile size_t *ptr, size_t val)
rbimpl_atomic_size_fetch_add(volatile size_t *ptr, size_t val, int memory_order)
{
(void)memory_order;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
return __atomic_fetch_add(ptr, val, __ATOMIC_SEQ_CST);
return __atomic_fetch_add(ptr, val, memory_order);
#elif defined(HAVE_GCC_SYNC_BUILTINS)
return __sync_fetch_and_add(ptr, val);
@ -446,10 +470,10 @@ rbimpl_atomic_size_fetch_add(volatile size_t *ptr, size_t val)
RBIMPL_STATIC_ASSERT(size_of_rb_atomic_t, sizeof *ptr == sizeof(rb_atomic_t));
volatile rb_atomic_t *const tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr);
rbimpl_atomic_fetch_add(tmp, val);
rbimpl_atomic_fetch_add(tmp, val, memory_order);
#elif defined(HAVE_STDATOMIC_H)
return atomic_fetch_add((_Atomic volatile size_t *)ptr, val);
return atomic_fetch_add_explicit((_Atomic volatile size_t *)ptr, val, memory_order);
#else
# error Unsupported platform.
@ -460,8 +484,9 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline void
rbimpl_atomic_add(volatile rb_atomic_t *ptr, rb_atomic_t val)
rbimpl_atomic_add(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
{
(void)memory_order;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
@ -470,7 +495,7 @@ rbimpl_atomic_add(volatile rb_atomic_t *ptr, rb_atomic_t val)
* return value is not used, then compiles it into single `LOCK ADD`
* instruction.
*/
__atomic_add_fetch(ptr, val, __ATOMIC_SEQ_CST);
__atomic_add_fetch(ptr, val, memory_order);
#elif defined(HAVE_GCC_SYNC_BUILTINS)
__sync_add_and_fetch(ptr, val);
@ -489,7 +514,7 @@ rbimpl_atomic_add(volatile rb_atomic_t *ptr, rb_atomic_t val)
atomic_add_int(ptr, val);
#elif defined(HAVE_STDATOMIC_H)
*(_Atomic volatile rb_atomic_t *)ptr += val;
atomic_fetch_add_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order);
#else
# error Unsupported platform.
@ -500,12 +525,13 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline void
rbimpl_atomic_size_add(volatile size_t *ptr, size_t val)
rbimpl_atomic_size_add(volatile size_t *ptr, size_t val, int memory_order)
{
(void)memory_order;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
__atomic_add_fetch(ptr, val, __ATOMIC_SEQ_CST);
__atomic_add_fetch(ptr, val, memory_order);
#elif defined(HAVE_GCC_SYNC_BUILTINS)
__sync_add_and_fetch(ptr, val);
@ -523,10 +549,10 @@ rbimpl_atomic_size_add(volatile size_t *ptr, size_t val)
RBIMPL_STATIC_ASSERT(size_of_rb_atomic_t, sizeof *ptr == sizeof(rb_atomic_t));
volatile rb_atomic_t *const tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr);
rbimpl_atomic_add(tmp, val);
rbimpl_atomic_add(tmp, val, memory_order);
#elif defined(HAVE_STDATOMIC_H)
*(_Atomic volatile size_t *)ptr += val;
atomic_fetch_add_explicit((_Atomic volatile size_t *)ptr, val, memory_order);
#else
# error Unsupported platform.
@ -537,12 +563,13 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline void
rbimpl_atomic_inc(volatile rb_atomic_t *ptr)
rbimpl_atomic_inc(volatile rb_atomic_t *ptr, int memory_order)
{
(void)memory_order;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS)
rbimpl_atomic_add(ptr, 1);
rbimpl_atomic_add(ptr, 1, memory_order);
#elif defined(_WIN32)
InterlockedIncrement(ptr);
@ -551,7 +578,7 @@ rbimpl_atomic_inc(volatile rb_atomic_t *ptr)
atomic_inc_uint(ptr);
#elif defined(HAVE_STDATOMIC_H)
rbimpl_atomic_add(ptr, 1);
rbimpl_atomic_add(ptr, 1, memory_order);
#else
# error Unsupported platform.
@ -562,12 +589,13 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline void
rbimpl_atomic_size_inc(volatile size_t *ptr)
rbimpl_atomic_size_inc(volatile size_t *ptr, int memory_order)
{
(void)memory_order;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS)
rbimpl_atomic_size_add(ptr, 1);
rbimpl_atomic_size_add(ptr, 1, memory_order);
#elif defined(_WIN64)
InterlockedIncrement64(ptr);
@ -578,10 +606,10 @@ rbimpl_atomic_size_inc(volatile size_t *ptr)
#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H))
RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t));
rbimpl_atomic_size_add(ptr, 1);
rbimpl_atomic_size_add(ptr, 1, memory_order);
#elif defined(HAVE_STDATOMIC_H)
rbimpl_atomic_size_add(ptr, 1);
rbimpl_atomic_size_add(ptr, 1, memory_order);
#else
# error Unsupported platform.
@ -592,12 +620,13 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline rb_atomic_t
rbimpl_atomic_fetch_sub(volatile rb_atomic_t *ptr, rb_atomic_t val)
rbimpl_atomic_fetch_sub(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
{
(void)memory_order;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
return __atomic_fetch_sub(ptr, val, __ATOMIC_SEQ_CST);
return __atomic_fetch_sub(ptr, val, memory_order);
#elif defined(HAVE_GCC_SYNC_BUILTINS)
return __sync_fetch_and_sub(ptr, val);
@ -613,7 +642,7 @@ rbimpl_atomic_fetch_sub(volatile rb_atomic_t *ptr, rb_atomic_t val)
return atomic_add_int_nv(ptr, neg * val) + val;
#elif defined(HAVE_STDATOMIC_H)
return atomic_fetch_sub((_Atomic volatile rb_atomic_t *)ptr, val);
return atomic_fetch_sub_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order);
#else
# error Unsupported platform.
@ -624,12 +653,13 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline void
rbimpl_atomic_sub(volatile rb_atomic_t *ptr, rb_atomic_t val)
rbimpl_atomic_sub(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
{
(void)memory_order;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
__atomic_sub_fetch(ptr, val, __ATOMIC_SEQ_CST);
__atomic_sub_fetch(ptr, val, memory_order);
#elif defined(HAVE_GCC_SYNC_BUILTINS)
__sync_sub_and_fetch(ptr, val);
@ -643,7 +673,7 @@ rbimpl_atomic_sub(volatile rb_atomic_t *ptr, rb_atomic_t val)
atomic_add_int(ptr, neg * val);
#elif defined(HAVE_STDATOMIC_H)
*(_Atomic volatile rb_atomic_t *)ptr -= val;
atomic_fetch_sub_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order);
#else
# error Unsupported platform.
@ -654,12 +684,13 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline void
rbimpl_atomic_size_sub(volatile size_t *ptr, size_t val)
rbimpl_atomic_size_sub(volatile size_t *ptr, size_t val, int memory_order)
{
(void)memory_order;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
__atomic_sub_fetch(ptr, val, __ATOMIC_SEQ_CST);
__atomic_sub_fetch(ptr, val, memory_order);
#elif defined(HAVE_GCC_SYNC_BUILTINS)
__sync_sub_and_fetch(ptr, val);
@ -677,10 +708,10 @@ rbimpl_atomic_size_sub(volatile size_t *ptr, size_t val)
RBIMPL_STATIC_ASSERT(size_of_rb_atomic_t, sizeof *ptr == sizeof(rb_atomic_t));
volatile rb_atomic_t *const tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr);
rbimpl_atomic_sub(tmp, val);
rbimpl_atomic_sub(tmp, val, memory_order);
#elif defined(HAVE_STDATOMIC_H)
*(_Atomic volatile size_t *)ptr -= val;
atomic_fetch_sub_explicit((_Atomic volatile size_t *)ptr, val, memory_order);
#else
# error Unsupported platform.
@ -691,12 +722,13 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline void
rbimpl_atomic_dec(volatile rb_atomic_t *ptr)
rbimpl_atomic_dec(volatile rb_atomic_t *ptr, int memory_order)
{
(void)memory_order;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS)
rbimpl_atomic_sub(ptr, 1);
rbimpl_atomic_sub(ptr, 1, memory_order);
#elif defined(_WIN32)
InterlockedDecrement(ptr);
@ -705,7 +737,7 @@ rbimpl_atomic_dec(volatile rb_atomic_t *ptr)
atomic_dec_uint(ptr);
#elif defined(HAVE_STDATOMIC_H)
rbimpl_atomic_sub(ptr, 1);
rbimpl_atomic_sub(ptr, 1, memory_order);
#else
# error Unsupported platform.
@ -716,12 +748,13 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline void
rbimpl_atomic_size_dec(volatile size_t *ptr)
rbimpl_atomic_size_dec(volatile size_t *ptr, int memory_order)
{
(void)memory_order;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS)
rbimpl_atomic_size_sub(ptr, 1);
rbimpl_atomic_size_sub(ptr, 1, memory_order);
#elif defined(_WIN64)
InterlockedDecrement64(ptr);
@ -732,10 +765,10 @@ rbimpl_atomic_size_dec(volatile size_t *ptr)
#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H))
RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t));
rbimpl_atomic_size_sub(ptr, 1);
rbimpl_atomic_size_sub(ptr, 1, memory_order);
#elif defined(HAVE_STDATOMIC_H)
rbimpl_atomic_size_sub(ptr, 1);
rbimpl_atomic_size_sub(ptr, 1, memory_order);
#else
# error Unsupported platform.
@ -746,12 +779,13 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline void
rbimpl_atomic_or(volatile rb_atomic_t *ptr, rb_atomic_t val)
rbimpl_atomic_or(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
{
(void)memory_order;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
__atomic_or_fetch(ptr, val, __ATOMIC_SEQ_CST);
__atomic_or_fetch(ptr, val, memory_order);
#elif defined(HAVE_GCC_SYNC_BUILTINS)
__sync_or_and_fetch(ptr, val);
@ -776,7 +810,7 @@ rbimpl_atomic_or(volatile rb_atomic_t *ptr, rb_atomic_t val)
atomic_or_uint(ptr, val);
#elif !defined(_WIN32) && defined(HAVE_STDATOMIC_H)
*(_Atomic volatile rb_atomic_t *)ptr |= val;
atomic_fetch_or_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order);
#else
# error Unsupported platform.
@ -796,12 +830,13 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline rb_atomic_t
rbimpl_atomic_exchange(volatile rb_atomic_t *ptr, rb_atomic_t val)
rbimpl_atomic_exchange(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
{
(void)memory_order;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
return __atomic_exchange_n(ptr, val, __ATOMIC_SEQ_CST);
return __atomic_exchange_n(ptr, val, memory_order);
#elif defined(HAVE_GCC_SYNC_BUILTINS)
return __sync_lock_test_and_set(ptr, val);
@ -813,7 +848,7 @@ rbimpl_atomic_exchange(volatile rb_atomic_t *ptr, rb_atomic_t val)
return atomic_swap_uint(ptr, val);
#elif defined(HAVE_STDATOMIC_H)
return atomic_exchange((_Atomic volatile rb_atomic_t *)ptr, val);
return atomic_exchange_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order);
#else
# error Unsupported platform.
@ -824,12 +859,13 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline size_t
rbimpl_atomic_size_exchange(volatile size_t *ptr, size_t val)
rbimpl_atomic_size_exchange(volatile size_t *ptr, size_t val, int memory_order)
{
(void)memory_order;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
return __atomic_exchange_n(ptr, val, __ATOMIC_SEQ_CST);
return __atomic_exchange_n(ptr, val, memory_order);
#elif defined(HAVE_GCC_SYNC_BUILTINS)
return __sync_lock_test_and_set(ptr, val);
@ -844,11 +880,11 @@ rbimpl_atomic_size_exchange(volatile size_t *ptr, size_t val)
RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t));
volatile rb_atomic_t *const tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr);
const rb_atomic_t ret = rbimpl_atomic_exchange(tmp, val);
const rb_atomic_t ret = rbimpl_atomic_exchange(tmp, val, memory_order);
return RBIMPL_CAST((size_t)ret);
#elif defined(HAVE_STDATOMIC_H)
return atomic_exchange((_Atomic volatile size_t *)ptr, val);
return atomic_exchange_explicit((_Atomic volatile size_t *)ptr, val, memory_order);
#else
# error Unsupported platform.
@ -859,15 +895,16 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline void
rbimpl_atomic_size_set(volatile size_t *ptr, size_t val)
rbimpl_atomic_size_store(volatile size_t *ptr, size_t val, int memory_order)
{
(void)memory_order;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
__atomic_store_n(ptr, val, __ATOMIC_SEQ_CST);
__atomic_store_n(ptr, val, memory_order);
#else
rbimpl_atomic_size_exchange(ptr, val);
rbimpl_atomic_size_exchange(ptr, val, memory_order);
#endif
}
@ -876,8 +913,9 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline void *
rbimpl_atomic_ptr_exchange(void *volatile *ptr, const void *val)
rbimpl_atomic_ptr_exchange(void *volatile *ptr, const void *val, int memory_order)
{
(void)memory_order;
#if 0
#elif defined(InterlockedExchangePointer)
@ -894,7 +932,7 @@ rbimpl_atomic_ptr_exchange(void *volatile *ptr, const void *val)
const size_t sval = RBIMPL_CAST((size_t)val);
volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr);
const size_t sret = rbimpl_atomic_size_exchange(sptr, sval);
const size_t sret = rbimpl_atomic_size_exchange(sptr, sval, memory_order);
return RBIMPL_CAST((void *)sret);
#endif
@ -904,26 +942,26 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline void
rbimpl_atomic_ptr_set(volatile void **ptr, void *val)
rbimpl_atomic_ptr_store(volatile void **ptr, void *val, int memory_order)
{
RBIMPL_STATIC_ASSERT(sizeof_value, sizeof *ptr == sizeof(size_t));
const size_t sval = RBIMPL_CAST((size_t)val);
volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr);
rbimpl_atomic_size_set(sptr, sval);
rbimpl_atomic_size_store(sptr, sval, memory_order);
}
RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline VALUE
rbimpl_atomic_value_exchange(volatile VALUE *ptr, VALUE val)
rbimpl_atomic_value_exchange(volatile VALUE *ptr, VALUE val, int memory_order)
{
RBIMPL_STATIC_ASSERT(sizeof_value, sizeof *ptr == sizeof(size_t));
const size_t sval = RBIMPL_CAST((size_t)val);
volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr);
const size_t sret = rbimpl_atomic_size_exchange(sptr, sval);
const size_t sret = rbimpl_atomic_size_exchange(sptr, sval, memory_order);
return RBIMPL_CAST((VALUE)sret);
}
@ -931,27 +969,28 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline void
rbimpl_atomic_value_set(volatile VALUE *ptr, VALUE val)
rbimpl_atomic_value_store(volatile VALUE *ptr, VALUE val, int memory_order)
{
RBIMPL_STATIC_ASSERT(sizeof_value, sizeof *ptr == sizeof(size_t));
const size_t sval = RBIMPL_CAST((size_t)val);
volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr);
rbimpl_atomic_size_set(sptr, sval);
rbimpl_atomic_size_store(sptr, sval, memory_order);
}
RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline rb_atomic_t
rbimpl_atomic_load(volatile rb_atomic_t *ptr)
rbimpl_atomic_load(volatile rb_atomic_t *ptr, int memory_order)
{
(void)memory_order;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
return __atomic_load_n(ptr, __ATOMIC_SEQ_CST);
return __atomic_load_n(ptr, memory_order);
#else
return rbimpl_atomic_fetch_add(ptr, 0);
return rbimpl_atomic_fetch_add(ptr, 0, memory_order);
#endif
}
@ -959,16 +998,17 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline void
rbimpl_atomic_set(volatile rb_atomic_t *ptr, rb_atomic_t val)
rbimpl_atomic_store(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
{
(void)memory_order;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
__atomic_store_n(ptr, val, __ATOMIC_SEQ_CST);
__atomic_store_n(ptr, val, memory_order);
#else
/* Maybe std::atomic<rb_atomic_t>::store can be faster? */
rbimpl_atomic_exchange(ptr, val);
rbimpl_atomic_exchange(ptr, val, memory_order);
#endif
}
@ -977,13 +1017,15 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline rb_atomic_t
rbimpl_atomic_cas(volatile rb_atomic_t *ptr, rb_atomic_t oldval, rb_atomic_t newval)
rbimpl_atomic_cas(volatile rb_atomic_t *ptr, rb_atomic_t oldval, rb_atomic_t newval, int success_memorder, int failure_memorder)
{
(void)success_memorder;
(void)failure_memorder;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
__atomic_compare_exchange_n(
ptr, &oldval, newval, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
ptr, &oldval, newval, 0, success_memorder, failure_memorder);
return oldval;
#elif defined(HAVE_GCC_SYNC_BUILTINS)
@ -1003,8 +1045,8 @@ rbimpl_atomic_cas(volatile rb_atomic_t *ptr, rb_atomic_t oldval, rb_atomic_t new
return atomic_cas_uint(ptr, oldval, newval);
#elif defined(HAVE_STDATOMIC_H)
atomic_compare_exchange_strong(
(_Atomic volatile rb_atomic_t *)ptr, &oldval, newval);
atomic_compare_exchange_strong_explicit(
(_Atomic volatile rb_atomic_t *)ptr, &oldval, newval, success_memorder, failure_memorder);
return oldval;
#else
@ -1025,13 +1067,15 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline size_t
rbimpl_atomic_size_cas(volatile size_t *ptr, size_t oldval, size_t newval)
rbimpl_atomic_size_cas(volatile size_t *ptr, size_t oldval, size_t newval, int success_memorder, int failure_memorder)
{
(void)success_memorder;
(void)failure_memorder;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
__atomic_compare_exchange_n(
ptr, &oldval, newval, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
ptr, &oldval, newval, 0, success_memorder, failure_memorder);
return oldval;
#elif defined(HAVE_GCC_SYNC_BUILTINS)
@ -1047,11 +1091,11 @@ rbimpl_atomic_size_cas(volatile size_t *ptr, size_t oldval, size_t newval)
RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t));
volatile rb_atomic_t *tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr);
return rbimpl_atomic_cas(tmp, oldval, newval);
return rbimpl_atomic_cas(tmp, oldval, newval, success_memorder, failure_memorder);
#elif defined(HAVE_STDATOMIC_H)
atomic_compare_exchange_strong(
(_Atomic volatile size_t *)ptr, &oldval, newval);
atomic_compare_exchange_strong_explicit(
(_Atomic volatile size_t *)ptr, &oldval, newval, success_memorder, failure_memorder);
return oldval;
#else
@ -1063,8 +1107,10 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline void *
rbimpl_atomic_ptr_cas(void **ptr, const void *oldval, const void *newval)
rbimpl_atomic_ptr_cas(void **ptr, const void *oldval, const void *newval, int success_memorder, int failure_memorder)
{
(void)success_memorder;
(void)failure_memorder;
#if 0
#elif defined(InterlockedExchangePointer)
@ -1087,7 +1133,7 @@ rbimpl_atomic_ptr_cas(void **ptr, const void *oldval, const void *newval)
const size_t snew = RBIMPL_CAST((size_t)newval);
const size_t sold = RBIMPL_CAST((size_t)oldval);
volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr);
const size_t sret = rbimpl_atomic_size_cas(sptr, sold, snew);
const size_t sret = rbimpl_atomic_size_cas(sptr, sold, snew, success_memorder, failure_memorder);
return RBIMPL_CAST((void *)sret);
#endif
@ -1097,15 +1143,16 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline void *
rbimpl_atomic_ptr_load(void **ptr)
rbimpl_atomic_ptr_load(void **ptr, int memory_order)
{
(void)memory_order;
#if 0
#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
return __atomic_load_n(ptr, __ATOMIC_SEQ_CST);
return __atomic_load_n(ptr, memory_order);
#else
void *val = *ptr;
return rbimpl_atomic_ptr_cas(ptr, val, val);
return rbimpl_atomic_ptr_cas(ptr, val, val, memory_order, memory_order);
#endif
}
@ -1113,14 +1160,23 @@ RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline VALUE
rbimpl_atomic_value_cas(volatile VALUE *ptr, VALUE oldval, VALUE newval)
rbimpl_atomic_value_load(volatile VALUE *ptr, int memory_order)
{
return RBIMPL_CAST((VALUE)rbimpl_atomic_ptr_load((void **)ptr, memory_order));
}
RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
static inline VALUE
rbimpl_atomic_value_cas(volatile VALUE *ptr, VALUE oldval, VALUE newval, int success_memorder, int failure_memorder)
{
RBIMPL_STATIC_ASSERT(sizeof_value, sizeof *ptr == sizeof(size_t));
const size_t snew = RBIMPL_CAST((size_t)newval);
const size_t sold = RBIMPL_CAST((size_t)oldval);
volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr);
const size_t sret = rbimpl_atomic_size_cas(sptr, sold, snew);
const size_t sret = rbimpl_atomic_size_cas(sptr, sold, snew, success_memorder, failure_memorder);
return RBIMPL_CAST((VALUE)sret);
}
/** @endcond */

View file

@ -24,7 +24,7 @@
* In released versions of Ruby, this number is not defined since teeny
* versions of Ruby should guarantee ABI compatibility.
*/
#define RUBY_ABI_VERSION 2
#define RUBY_ABI_VERSION 3
/* Windows does not support weak symbols so ruby_abi_version will not exist
* in the shared library. */

View file

@ -133,12 +133,6 @@ struct RData {
*/
RUBY_DATA_FUNC dmark;
/** Pointer to the actual C level struct that you want to wrap.
* This is in between dmark and dfree to allow DATA_PTR to continue
* to work for both RData and non-embedded RTypedData.
*/
void *data;
/**
* This function is called when the object is no longer used. You need to
* do whatever necessary to avoid memory leaks.
@ -147,6 +141,12 @@ struct RData {
* impossible at that moment (that is why GC runs).
*/
RUBY_DATA_FUNC dfree;
/** Pointer to the actual C level struct that you want to wrap.
* This is in between dmark and dfree to allow DATA_PTR to continue
* to work for both RData and non-embedded RTypedData.
*/
void *data;
};
RBIMPL_SYMBOL_EXPORT_BEGIN()

View file

@ -355,6 +355,9 @@ struct RTypedData {
/** The part that all ruby objects have in common. */
struct RBasic basic;
/** Direct reference to the slots that holds instance variables, if any **/
VALUE fields_obj;
/**
* This is a `const rb_data_type_t *const` value, with the low bits set:
*

View file

@ -15,13 +15,16 @@ struct set_table {
const struct st_hash_type *type;
/* Number of entries currently in the table. */
st_index_t num_entries;
/* Array of bins used for access by keys. */
st_index_t *bins;
/* Start and bound index of entries in array entries.
entries_starts and entries_bound are in interval
[0,allocated_entries]. */
st_index_t entries_start, entries_bound;
/* Array of size 2^entry_power. */
/**
* Array of size 2^entry_power.
* Followed by st_index_t *bins, Array of bins used for access by keys.
*/
set_table_entry *entries;
};

View file

@ -11,10 +11,23 @@
#include "ruby/internal/stdbool.h" /* for bool */
#include "ruby/ruby.h" /* for struct RBasic */
/* Flags of RStruct
*
* 1-7: RSTRUCT_EMBED_LEN
* If non-zero, the struct is embedded (its contents follow the
* header, rather than being on a separately allocated buffer) and
* these bits are the length of the Struct.
* 8: RSTRUCT_GEN_FIELDS
* The struct is embedded and has no space left to store the
* IMEMO/fields reference. Any ivar this struct may have will be in
* the generic_fields_tbl. This flag doesn't imply the struct has
* ivars.
*/
enum {
RSTRUCT_EMBED_LEN_MASK = RUBY_FL_USER7 | RUBY_FL_USER6 | RUBY_FL_USER5 | RUBY_FL_USER4 |
RUBY_FL_USER3 | RUBY_FL_USER2 | RUBY_FL_USER1,
RSTRUCT_EMBED_LEN_SHIFT = (RUBY_FL_USHIFT+1),
RSTRUCT_GEN_FIELDS = RUBY_FL_USER8,
};
struct RStruct {
@ -23,6 +36,7 @@ struct RStruct {
struct {
long len;
const VALUE *ptr;
VALUE fields_obj;
} heap;
/* This is a length 1 array because:
* 1. GCC has a bug that does not optimize C flexible array members
@ -116,4 +130,31 @@ RSTRUCT_GET(VALUE st, long k)
return RSTRUCT_CONST_PTR(st)[k];
}
static inline VALUE
RSTRUCT_FIELDS_OBJ(VALUE st)
{
const long embed_len = RSTRUCT_EMBED_LEN(st);
VALUE fields_obj;
if (embed_len) {
RUBY_ASSERT(!FL_TEST_RAW(st, RSTRUCT_GEN_FIELDS));
fields_obj = RSTRUCT_GET(st, embed_len);
}
else {
fields_obj = RSTRUCT(st)->as.heap.fields_obj;
}
return fields_obj;
}
static inline void
RSTRUCT_SET_FIELDS_OBJ(VALUE st, VALUE fields_obj)
{
const long embed_len = RSTRUCT_EMBED_LEN(st);
if (embed_len) {
RUBY_ASSERT(!FL_TEST_RAW(st, RSTRUCT_GEN_FIELDS));
RSTRUCT_SET(st, embed_len, fields_obj);
}
else {
RB_OBJ_WRITE(st, &RSTRUCT(st)->as.heap.fields_obj, fields_obj);
}
}
#endif /* INTERNAL_STRUCT_H */

View file

@ -17,8 +17,7 @@
#endif
/* symbol.c */
void rb_sym_global_symbols_mark(void);
void rb_sym_global_symbols_update_references(void);
void rb_sym_global_symbols_mark_and_move(void);
VALUE rb_to_symbol_type(VALUE obj);
VALUE rb_sym_intern(const char *ptr, long len, rb_encoding *enc);
VALUE rb_sym_intern_ascii(const char *ptr, long len);
@ -35,6 +34,7 @@ bool rb_obj_is_symbol_table(VALUE obj);
void rb_sym_global_symbol_table_foreach_weak_reference(int (*callback)(VALUE *key, void *data), void *data);
void rb_gc_free_dsymbol(VALUE);
int rb_static_id_valid_p(ID id);
void rb_free_global_symbol_table(void);
#if __has_builtin(__builtin_constant_p)
#define rb_sym_intern_ascii_cstr(ptr) \

View file

@ -46,7 +46,6 @@ void rb_gvar_namespace_ready(const char *name);
*/
VALUE rb_mod_set_temporary_name(VALUE, VALUE);
int rb_gen_fields_tbl_get(VALUE obj, ID id, VALUE *fields_obj);
void rb_obj_copy_ivs_to_hash_table(VALUE obj, st_table *table);
void rb_obj_init_too_complex(VALUE obj, st_table *table);
void rb_evict_ivars_to_hash(VALUE obj);

View file

@ -2049,10 +2049,16 @@ XXX
basename = File.basename($0, '.*')
return true if load(File.expand_path("~/.options/#{basename}"), **keywords) rescue nil
basename << ".options"
if !(xdg = ENV['XDG_CONFIG_HOME']) or xdg.empty?
# https://specifications.freedesktop.org/basedir-spec/latest/#variables
#
# If $XDG_CONFIG_HOME is either not set or empty, a default
# equal to $HOME/.config should be used.
xdg = ['~/.config', true]
end
return [
# XDG
ENV['XDG_CONFIG_HOME'],
['~/.config', true],
xdg,
*ENV['XDG_CONFIG_DIRS']&.split(File::PATH_SEPARATOR),
# Haiku

View file

@ -12,9 +12,6 @@ module Gem
VERSION = "3.8.0.dev"
end
# Must be first since it unloads the prelude from 1.9.2
require_relative "rubygems/compatibility"
require_relative "rubygems/defaults"
require_relative "rubygems/deprecate"
require_relative "rubygems/errors"

View file

@ -199,6 +199,9 @@ class Gem::BasicSpecification
File.expand_path(File.join(gems_dir, full_name, "data", name))
end
extend Gem::Deprecate
rubygems_deprecate :datadir, :none, "4.1"
##
# Full path of the target library file.
# If the file is not in this gem, return nil.

View file

@ -1,41 +0,0 @@
# frozen_string_literal: true
#--
# This file contains all sorts of little compatibility hacks that we've
# had to introduce over the years. Quarantining them into one file helps
# us know when we can get rid of them.
#
# Ruby 1.9.x has introduced some things that are awkward, and we need to
# support them, so we define some constants to use later.
#
# TODO remove at RubyGems 4
#++
module Gem
# :stopdoc:
RubyGemsVersion = VERSION
deprecate_constant(:RubyGemsVersion)
RbConfigPriorities = %w[
MAJOR
MINOR
TEENY
EXEEXT RUBY_SO_NAME arch bindir datadir libdir ruby_install_name
ruby_version rubylibprefix sitedir sitelibdir vendordir vendorlibdir
rubylibdir
].freeze
if defined?(ConfigMap)
RbConfigPriorities.each do |key|
ConfigMap[key.to_sym] = RbConfig::CONFIG[key]
end
else
##
# Configuration settings from ::RbConfig
ConfigMap = Hash.new do |cm, key|
cm[key] = RbConfig::CONFIG[key.to_s]
end
deprecate_constant(:ConfigMap)
end
end

View file

@ -126,17 +126,18 @@ module Gem
# telling the user of +repl+ (unless +repl+ is :none) and the
# Rubygems version that it is planned to go away.
def rubygems_deprecate(name, replacement=:none)
def rubygems_deprecate(name, replacement=:none, version=nil)
class_eval do
old = "_deprecated_#{name}"
alias_method old, name
define_method name do |*args, &block|
klass = is_a? Module
target = klass ? "#{self}." : "#{self.class}#"
version ||= Gem::Deprecate.next_rubygems_major_version
msg = [
"NOTE: #{target}#{name} is deprecated",
replacement == :none ? " with no replacement" : "; use #{replacement} instead",
". It will be removed in Rubygems #{Gem::Deprecate.next_rubygems_major_version}",
". It will be removed in Rubygems #{version}",
"\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}",
]
warn "#{msg.join}." unless Gem::Deprecate.skip
@ -147,13 +148,14 @@ module Gem
end
# Deprecation method to deprecate Rubygems commands
def rubygems_deprecate_command(version = Gem::Deprecate.next_rubygems_major_version)
def rubygems_deprecate_command(version = nil)
class_eval do
define_method "deprecated?" do
true
end
define_method "deprecation_warning" do
version ||= Gem::Deprecate.next_rubygems_major_version
msg = [
"#{command} command is deprecated",
". It will be removed in Rubygems #{version}.\n",

View file

@ -1,11 +1,14 @@
# frozen_string_literal: true
require_relative "openssl"
require_relative "user_interaction"
##
# S3URISigner implements AWS SigV4 for S3 Source to avoid a dependency on the aws-sdk-* gems
# More on AWS SigV4: https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html
class Gem::S3URISigner
include Gem::UserInteraction
class ConfigurationError < Gem::Exception
def initialize(message)
super message
@ -147,17 +150,40 @@ class Gem::S3URISigner
require_relative "request/connection_pools"
require "json"
iam_info = ec2_metadata_request(EC2_IAM_INFO)
# Expected format: arn:aws:iam::<id>:instance-profile/<role_name>
role_name = iam_info["InstanceProfileArn"].split("/").last
ec2_metadata_request(EC2_IAM_SECURITY_CREDENTIALS + role_name)
# First try V2 fallback to V1
res = nil
begin
res = ec2_metadata_credentials_imds_v2
rescue InstanceProfileError
alert_warning "Unable to access ec2 credentials via IMDSv2, falling back to IMDSv1"
res = ec2_metadata_credentials_imds_v1
end
res
end
def ec2_metadata_request(url)
uri = Gem::URI(url)
@request_pool ||= create_request_pool(uri)
request = Gem::Request.new(uri, Gem::Net::HTTP::Get, nil, @request_pool)
response = request.fetch
def ec2_metadata_credentials_imds_v2
token = ec2_metadata_token
iam_info = ec2_metadata_request(EC2_IAM_INFO, token:)
# Expected format: arn:aws:iam::<id>:instance-profile/<role_name>
role_name = iam_info["InstanceProfileArn"].split("/").last
ec2_metadata_request(EC2_IAM_SECURITY_CREDENTIALS + role_name, token:)
end
def ec2_metadata_credentials_imds_v1
iam_info = ec2_metadata_request(EC2_IAM_INFO, token: nil)
# Expected format: arn:aws:iam::<id>:instance-profile/<role_name>
role_name = iam_info["InstanceProfileArn"].split("/").last
ec2_metadata_request(EC2_IAM_SECURITY_CREDENTIALS + role_name, token: nil)
end
def ec2_metadata_request(url, token:)
request = ec2_iam_request(Gem::URI(url), Gem::Net::HTTP::Get)
response = request.fetch do |req|
if token
req.add_field "X-aws-ec2-metadata-token", token
end
end
case response
when Gem::Net::HTTPOK then
@ -167,6 +193,26 @@ class Gem::S3URISigner
end
end
def ec2_metadata_token
request = ec2_iam_request(Gem::URI(EC2_IAM_TOKEN), Gem::Net::HTTP::Put)
response = request.fetch do |req|
req.add_field "X-aws-ec2-metadata-token-ttl-seconds", 60
end
case response
when Gem::Net::HTTPOK then
response.body
else
raise InstanceProfileError.new("Unable to fetch AWS metadata from #{uri}: #{response.message} #{response.code}")
end
end
def ec2_iam_request(uri, verb)
@request_pool ||= create_request_pool(uri)
Gem::Request.new(uri, verb, nil, @request_pool)
end
def create_request_pool(uri)
proxy_uri = Gem::Request.proxy_uri(Gem::Request.get_proxy_from_env(uri.scheme))
certs = Gem::Request.get_cert_files
@ -174,6 +220,7 @@ class Gem::S3URISigner
end
BASE64_URI_TRANSLATE = { "+" => "%2B", "/" => "%2F", "=" => "%3D", "\n" => "" }.freeze
EC2_IAM_TOKEN = "http://169.254.169.254/latest/api/token"
EC2_IAM_INFO = "http://169.254.169.254/latest/meta-data/iam/info"
EC2_IAM_SECURITY_CREDENTIALS = "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
end

8
load.c
View file

@ -1318,7 +1318,7 @@ no_feature_p(vm_ns_t *vm_ns, const char *feature, const char *ext, int rb, int e
return 0;
}
// Documented in doc/globals.rdoc
// Documented in doc/globals.md
VALUE
rb_resolve_feature_path(VALUE klass, VALUE fname)
{
@ -1346,14 +1346,14 @@ rb_resolve_feature_path(VALUE klass, VALUE fname)
}
static void
ext_config_push(rb_thread_t *th, struct rb_ext_config *prev)
ext_config_push(rb_thread_t *th, volatile struct rb_ext_config *prev)
{
*prev = th->ext_config;
th->ext_config = (struct rb_ext_config){0};
}
static void
ext_config_pop(rb_thread_t *th, struct rb_ext_config *prev)
ext_config_pop(rb_thread_t *th, volatile struct rb_ext_config *prev)
{
th->ext_config = *prev;
}
@ -1407,7 +1407,7 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa
VALUE realpaths = get_loaded_features_realpaths(vm_ns);
VALUE realpath_map = get_loaded_features_realpath_map(vm_ns);
volatile bool reset_ext_config = false;
struct rb_ext_config prev_ext_config;
volatile struct rb_ext_config prev_ext_config;
path = rb_str_encode_ospath(fname);
RUBY_DTRACE_HOOK(REQUIRE_ENTRY, RSTRING_PTR(fname));

View file

@ -2572,19 +2572,19 @@ Init_marshal(void)
}
static int
marshal_compat_table_mark_i(st_data_t key, st_data_t value, st_data_t _)
marshal_compat_table_mark_and_move_i(st_data_t key, st_data_t value, st_data_t _)
{
marshal_compat_t *p = (marshal_compat_t *)value;
rb_gc_mark_movable(p->newclass);
rb_gc_mark_movable(p->oldclass);
rb_gc_mark_and_move(&p->newclass);
rb_gc_mark_and_move(&p->oldclass);
return ST_CONTINUE;
}
static void
marshal_compat_table_mark(void *tbl)
marshal_compat_table_mark_and_move(void *tbl)
{
if (!tbl) return;
st_foreach(tbl, marshal_compat_table_mark_i, 0);
st_foreach(tbl, marshal_compat_table_mark_and_move_i, 0);
}
static int
@ -2607,29 +2607,13 @@ marshal_compat_table_memsize(const void *data)
return st_memsize(data) + sizeof(marshal_compat_t) * st_table_size(data);
}
static int
marshal_compat_table_compact_i(st_data_t key, st_data_t value, st_data_t _)
{
marshal_compat_t *p = (marshal_compat_t *)value;
p->newclass = rb_gc_location(p->newclass);
p->oldclass = rb_gc_location(p->oldclass);
return ST_CONTINUE;
}
static void
marshal_compat_table_compact(void *tbl)
{
if (!tbl) return;
st_foreach(tbl, marshal_compat_table_compact_i, 0);
}
static const rb_data_type_t marshal_compat_type = {
.wrap_struct_name = "marshal_compat_table",
.function = {
.dmark = marshal_compat_table_mark,
.dmark = marshal_compat_table_mark_and_move,
.dfree = marshal_compat_table_free,
.dsize = marshal_compat_table_memsize,
.dcompact = marshal_compat_table_compact,
.dcompact = marshal_compat_table_mark_and_move,
},
.flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY,
};

View file

@ -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:
@ -1009,8 +1012,10 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node)
ANN("format: module [nd_cpath]; [nd_body]; end");
ANN("example: module M; ..; end");
F_NODE(nd_cpath, RNODE_MODULE, "module path");
LAST_NODE;
F_NODE(nd_body, RNODE_MODULE, "module definition");
F_LOC(module_keyword_loc, RNODE_MODULE);
LAST_NODE;
F_LOC(end_keyword_loc, RNODE_MODULE);
return;
case NODE_SCLASS:

25
parse.y
View file

@ -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);
@ -1145,7 +1145,7 @@ static rb_node_alias_t *rb_node_alias_new(struct parser_params *p, NODE *nd_1st,
static rb_node_valias_t *rb_node_valias_new(struct parser_params *p, ID nd_alias, ID nd_orig, const YYLTYPE *loc, const YYLTYPE *keyword_loc);
static rb_node_undef_t *rb_node_undef_new(struct parser_params *p, NODE *nd_undef, const YYLTYPE *loc);
static rb_node_class_t *rb_node_class_new(struct parser_params *p, NODE *nd_cpath, NODE *nd_body, NODE *nd_super, const YYLTYPE *loc, const YYLTYPE *class_keyword_loc, const YYLTYPE *inheritance_operator_loc, const YYLTYPE *end_keyword_loc);
static rb_node_module_t *rb_node_module_new(struct parser_params *p, NODE *nd_cpath, NODE *nd_body, const YYLTYPE *loc);
static rb_node_module_t *rb_node_module_new(struct parser_params *p, NODE *nd_cpath, NODE *nd_body, const YYLTYPE *loc, const YYLTYPE *module_keyword_loc, const YYLTYPE *end_keyword_loc);
static rb_node_sclass_t *rb_node_sclass_new(struct parser_params *p, NODE *nd_recv, NODE *nd_body, const YYLTYPE *loc);
static rb_node_colon2_t *rb_node_colon2_new(struct parser_params *p, NODE *nd_head, ID nd_mid, const YYLTYPE *loc, const YYLTYPE *delimiter_loc, const YYLTYPE *name_loc);
static rb_node_colon3_t *rb_node_colon3_new(struct parser_params *p, ID nd_mid, const YYLTYPE *loc, const YYLTYPE *delimiter_loc, const YYLTYPE *name_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)
@ -1253,7 +1253,7 @@ static rb_node_error_t *rb_node_error_new(struct parser_params *p, const YYLTYPE
#define NEW_VALIAS(n,o,loc,k_loc) (NODE *)rb_node_valias_new(p,n,o,loc,k_loc)
#define NEW_UNDEF(i,loc) (NODE *)rb_node_undef_new(p,i,loc)
#define NEW_CLASS(n,b,s,loc,ck_loc,io_loc,ek_loc) (NODE *)rb_node_class_new(p,n,b,s,loc,ck_loc,io_loc,ek_loc)
#define NEW_MODULE(n,b,loc) (NODE *)rb_node_module_new(p,n,b,loc)
#define NEW_MODULE(n,b,loc,mk_loc,ek_loc) (NODE *)rb_node_module_new(p,n,b,loc,mk_loc,ek_loc)
#define NEW_SCLASS(r,b,loc) (NODE *)rb_node_sclass_new(p,r,b,loc)
#define NEW_COLON2(c,i,loc,d_loc,n_loc) (NODE *)rb_node_colon2_new(p,c,i,loc,d_loc,n_loc)
#define NEW_COLON3(i,loc,d_loc,n_loc) (NODE *)rb_node_colon3_new(p,i,loc,d_loc,n_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
@ -4621,7 +4621,7 @@ primary : inline_primary
bodystmt
k_end
{
$$ = NEW_MODULE($cpath, $bodystmt, &@$);
$$ = NEW_MODULE($cpath, $bodystmt, &@$, &@k_module, &@k_end);
nd_set_line(RNODE_MODULE($$)->nd_body, @k_end.end_pos.lineno);
set_line_body($bodystmt, @cpath.end_pos.lineno);
nd_set_line($$, @cpath.end_pos.lineno);
@ -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) %*/
}
;
@ -11438,13 +11438,15 @@ rb_node_sclass_new(struct parser_params *p, NODE *nd_recv, NODE *nd_body, const
}
static rb_node_module_t *
rb_node_module_new(struct parser_params *p, NODE *nd_cpath, NODE *nd_body, const YYLTYPE *loc)
rb_node_module_new(struct parser_params *p, NODE *nd_cpath, NODE *nd_body, const YYLTYPE *loc, const YYLTYPE *module_keyword_loc, const YYLTYPE *end_keyword_loc)
{
/* Keep the order of node creation */
NODE *scope = NEW_SCOPE(0, nd_body, loc);
rb_node_module_t *n = NODE_NEWNODE(NODE_MODULE, rb_node_module_t, loc);
n->nd_cpath = nd_cpath;
n->nd_body = scope;
n->module_keyword_loc = *module_keyword_loc;
n->end_keyword_loc = *end_keyword_loc;
return n;
}
@ -11526,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;
}

View file

@ -322,13 +322,42 @@ warnings:
- UNUSED_LOCAL_VARIABLE
- VOID_STATEMENT
tokens:
# The order of the tokens at the beginning is important, because we use them
# for a lookup table.
- name: EOF
value: 1
comment: final token in the file
- name: MISSING
comment: "a token that was expected but not found"
- name: NOT_PROVIDED
comment: "a token that was not present but it is okay"
- name: BRACE_RIGHT
comment: "}"
- name: COMMA
comment: ","
- name: EMBEXPR_END
comment: "}"
- name: KEYWORD_DO
comment: "do"
- name: KEYWORD_ELSE
comment: "else"
- name: KEYWORD_ELSIF
comment: "elsif"
- name: KEYWORD_END
comment: "end"
- name: KEYWORD_ENSURE
comment: "ensure"
- name: KEYWORD_IN
comment: "in"
- name: KEYWORD_RESCUE
comment: "rescue"
- name: KEYWORD_THEN
comment: "then"
- name: KEYWORD_WHEN
comment: "when"
- name: NEWLINE
comment: "a newline character outside of other tokens"
- name: PARENTHESIS_RIGHT
comment: ")"
- name: SEMICOLON
comment: ";"
# Tokens from here on are not used for lookup, and can be in any order.
- name: AMPERSAND
comment: "&"
- name: AMPERSAND_AMPERSAND
@ -351,8 +380,6 @@ tokens:
comment: "!~"
- name: BRACE_LEFT
comment: "{"
- name: BRACE_RIGHT
comment: "}"
- name: BRACKET_LEFT
comment: "["
- name: BRACKET_LEFT_ARRAY
@ -375,8 +402,6 @@ tokens:
comment: ":"
- name: COLON_COLON
comment: "::"
- name: COMMA
comment: ","
- name: COMMENT
comment: "a comment"
- name: CONSTANT
@ -395,8 +420,6 @@ tokens:
comment: "a line inside of embedded documentation"
- name: EMBEXPR_BEGIN
comment: "#{"
- name: EMBEXPR_END
comment: "}"
- name: EMBVAR
comment: "#"
- name: EQUAL
@ -463,20 +486,10 @@ tokens:
comment: "def"
- name: KEYWORD_DEFINED
comment: "defined?"
- name: KEYWORD_DO
comment: "do"
- name: KEYWORD_DO_LOOP
comment: "do keyword for a predicate in a while, until, or for loop"
- name: KEYWORD_ELSE
comment: "else"
- name: KEYWORD_ELSIF
comment: "elsif"
- name: KEYWORD_END
comment: "end"
- name: KEYWORD_END_UPCASE
comment: "END"
- name: KEYWORD_ENSURE
comment: "ensure"
- name: KEYWORD_FALSE
comment: "false"
- name: KEYWORD_FOR
@ -485,8 +498,6 @@ tokens:
comment: "if"
- name: KEYWORD_IF_MODIFIER
comment: "if in the modifier form"
- name: KEYWORD_IN
comment: "in"
- name: KEYWORD_MODULE
comment: "module"
- name: KEYWORD_NEXT
@ -499,8 +510,6 @@ tokens:
comment: "or"
- name: KEYWORD_REDO
comment: "redo"
- name: KEYWORD_RESCUE
comment: "rescue"
- name: KEYWORD_RESCUE_MODIFIER
comment: "rescue in the modifier form"
- name: KEYWORD_RETRY
@ -511,8 +520,6 @@ tokens:
comment: "self"
- name: KEYWORD_SUPER
comment: "super"
- name: KEYWORD_THEN
comment: "then"
- name: KEYWORD_TRUE
comment: "true"
- name: KEYWORD_UNDEF
@ -525,8 +532,6 @@ tokens:
comment: "until"
- name: KEYWORD_UNTIL_MODIFIER
comment: "until in the modifier form"
- name: KEYWORD_WHEN
comment: "when"
- name: KEYWORD_WHILE
comment: "while"
- name: KEYWORD_WHILE_MODIFIER
@ -563,16 +568,12 @@ tokens:
comment: "-="
- name: MINUS_GREATER
comment: "->"
- name: NEWLINE
comment: "a newline character outside of other tokens"
- name: NUMBERED_REFERENCE
comment: "a numbered reference to a capture group in the previous regular expression match"
- name: PARENTHESIS_LEFT
comment: "("
- name: PARENTHESIS_LEFT_PARENTHESES
comment: "( for a parentheses node"
- name: PARENTHESIS_RIGHT
comment: ")"
- name: PERCENT
comment: "%"
- name: PERCENT_EQUAL
@ -605,8 +606,6 @@ tokens:
comment: "the beginning of a regular expression"
- name: REGEXP_END
comment: "the end of a regular expression"
- name: SEMICOLON
comment: ";"
- name: SLASH
comment: "/"
- name: SLASH_EQUAL
@ -651,6 +650,10 @@ tokens:
comment: "a separator between words in a list"
- name: __END__
comment: "marker for the point in the file at which the parser should stop"
- name: MISSING
comment: "a token that was expected but not found"
- name: NOT_PROVIDED
comment: "a token that was not present but it is okay"
flags:
- name: ArgumentsNodeFlags
values:

View file

@ -8586,85 +8586,66 @@ parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) {
/* Context manipulations */
/******************************************************************************/
static bool
context_terminator(pm_context_t context, pm_token_t *token) {
switch (context) {
case PM_CONTEXT_MAIN:
case PM_CONTEXT_DEF_PARAMS:
case PM_CONTEXT_DEFINED:
case PM_CONTEXT_MULTI_TARGET:
case PM_CONTEXT_TERNARY:
case PM_CONTEXT_RESCUE_MODIFIER:
return token->type == PM_TOKEN_EOF;
case PM_CONTEXT_DEFAULT_PARAMS:
return token->type == PM_TOKEN_COMMA || token->type == PM_TOKEN_PARENTHESIS_RIGHT;
case PM_CONTEXT_PREEXE:
case PM_CONTEXT_POSTEXE:
return token->type == PM_TOKEN_BRACE_RIGHT;
case PM_CONTEXT_MODULE:
case PM_CONTEXT_CLASS:
case PM_CONTEXT_SCLASS:
case PM_CONTEXT_LAMBDA_DO_END:
case PM_CONTEXT_DEF:
case PM_CONTEXT_BLOCK_KEYWORDS:
return token->type == PM_TOKEN_KEYWORD_END || token->type == PM_TOKEN_KEYWORD_RESCUE || token->type == PM_TOKEN_KEYWORD_ENSURE;
case PM_CONTEXT_WHILE:
case PM_CONTEXT_UNTIL:
case PM_CONTEXT_ELSE:
case PM_CONTEXT_FOR:
case PM_CONTEXT_BEGIN_ENSURE:
case PM_CONTEXT_BLOCK_ENSURE:
case PM_CONTEXT_CLASS_ENSURE:
case PM_CONTEXT_DEF_ENSURE:
case PM_CONTEXT_LAMBDA_ENSURE:
case PM_CONTEXT_MODULE_ENSURE:
case PM_CONTEXT_SCLASS_ENSURE:
return token->type == PM_TOKEN_KEYWORD_END;
case PM_CONTEXT_LOOP_PREDICATE:
return token->type == PM_TOKEN_KEYWORD_DO || token->type == PM_TOKEN_KEYWORD_THEN;
case PM_CONTEXT_FOR_INDEX:
return token->type == PM_TOKEN_KEYWORD_IN;
case PM_CONTEXT_CASE_WHEN:
return token->type == PM_TOKEN_KEYWORD_WHEN || token->type == PM_TOKEN_KEYWORD_END || token->type == PM_TOKEN_KEYWORD_ELSE;
case PM_CONTEXT_CASE_IN:
return token->type == PM_TOKEN_KEYWORD_IN || token->type == PM_TOKEN_KEYWORD_END || token->type == PM_TOKEN_KEYWORD_ELSE;
case PM_CONTEXT_IF:
case PM_CONTEXT_ELSIF:
return token->type == PM_TOKEN_KEYWORD_ELSE || token->type == PM_TOKEN_KEYWORD_ELSIF || token->type == PM_TOKEN_KEYWORD_END;
case PM_CONTEXT_UNLESS:
return token->type == PM_TOKEN_KEYWORD_ELSE || token->type == PM_TOKEN_KEYWORD_END;
case PM_CONTEXT_EMBEXPR:
return token->type == PM_TOKEN_EMBEXPR_END;
case PM_CONTEXT_BLOCK_BRACES:
return token->type == PM_TOKEN_BRACE_RIGHT;
case PM_CONTEXT_PARENS:
return token->type == PM_TOKEN_PARENTHESIS_RIGHT;
case PM_CONTEXT_BEGIN:
case PM_CONTEXT_BEGIN_RESCUE:
case PM_CONTEXT_BLOCK_RESCUE:
case PM_CONTEXT_CLASS_RESCUE:
case PM_CONTEXT_DEF_RESCUE:
case PM_CONTEXT_LAMBDA_RESCUE:
case PM_CONTEXT_MODULE_RESCUE:
case PM_CONTEXT_SCLASS_RESCUE:
return token->type == PM_TOKEN_KEYWORD_ENSURE || token->type == PM_TOKEN_KEYWORD_RESCUE || token->type == PM_TOKEN_KEYWORD_ELSE || token->type == PM_TOKEN_KEYWORD_END;
case PM_CONTEXT_BEGIN_ELSE:
case PM_CONTEXT_BLOCK_ELSE:
case PM_CONTEXT_CLASS_ELSE:
case PM_CONTEXT_DEF_ELSE:
case PM_CONTEXT_LAMBDA_ELSE:
case PM_CONTEXT_MODULE_ELSE:
case PM_CONTEXT_SCLASS_ELSE:
return token->type == PM_TOKEN_KEYWORD_ENSURE || token->type == PM_TOKEN_KEYWORD_END;
case PM_CONTEXT_LAMBDA_BRACES:
return token->type == PM_TOKEN_BRACE_RIGHT;
case PM_CONTEXT_PREDICATE:
return token->type == PM_TOKEN_KEYWORD_THEN || token->type == PM_TOKEN_NEWLINE || token->type == PM_TOKEN_SEMICOLON;
case PM_CONTEXT_NONE:
return false;
}
static const uint32_t context_terminators[] = {
[PM_CONTEXT_NONE] = 0,
[PM_CONTEXT_BEGIN] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_BEGIN_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_BEGIN_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_BEGIN_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_BLOCK_BRACES] = (1 << PM_TOKEN_BRACE_RIGHT),
[PM_CONTEXT_BLOCK_KEYWORDS] = (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ENSURE),
[PM_CONTEXT_BLOCK_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_BLOCK_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_BLOCK_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_CASE_WHEN] = (1 << PM_TOKEN_KEYWORD_WHEN) | (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_ELSE),
[PM_CONTEXT_CASE_IN] = (1 << PM_TOKEN_KEYWORD_IN) | (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_ELSE),
[PM_CONTEXT_CLASS] = (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ENSURE),
[PM_CONTEXT_CLASS_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_CLASS_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_CLASS_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_DEF] = (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ENSURE),
[PM_CONTEXT_DEF_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_DEF_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_DEF_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_DEF_PARAMS] = (1 << PM_TOKEN_EOF),
[PM_CONTEXT_DEFINED] = (1 << PM_TOKEN_EOF),
[PM_CONTEXT_DEFAULT_PARAMS] = (1 << PM_TOKEN_COMMA) | (1 << PM_TOKEN_PARENTHESIS_RIGHT),
[PM_CONTEXT_ELSE] = (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_ELSIF] = (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_ELSIF) | (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_EMBEXPR] = (1 << PM_TOKEN_EMBEXPR_END),
[PM_CONTEXT_FOR] = (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_FOR_INDEX] = (1 << PM_TOKEN_KEYWORD_IN),
[PM_CONTEXT_IF] = (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_ELSIF) | (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_LAMBDA_BRACES] = (1 << PM_TOKEN_BRACE_RIGHT),
[PM_CONTEXT_LAMBDA_DO_END] = (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ENSURE),
[PM_CONTEXT_LAMBDA_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_LAMBDA_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_LAMBDA_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_LOOP_PREDICATE] = (1 << PM_TOKEN_KEYWORD_DO) | (1 << PM_TOKEN_KEYWORD_THEN),
[PM_CONTEXT_MAIN] = (1 << PM_TOKEN_EOF),
[PM_CONTEXT_MODULE] = (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ENSURE),
[PM_CONTEXT_MODULE_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_MODULE_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_MODULE_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_MULTI_TARGET] = (1 << PM_TOKEN_EOF),
[PM_CONTEXT_PARENS] = (1 << PM_TOKEN_PARENTHESIS_RIGHT),
[PM_CONTEXT_POSTEXE] = (1 << PM_TOKEN_BRACE_RIGHT),
[PM_CONTEXT_PREDICATE] = (1 << PM_TOKEN_KEYWORD_THEN) | (1 << PM_TOKEN_NEWLINE) | (1 << PM_TOKEN_SEMICOLON),
[PM_CONTEXT_PREEXE] = (1 << PM_TOKEN_BRACE_RIGHT),
[PM_CONTEXT_RESCUE_MODIFIER] = (1 << PM_TOKEN_EOF),
[PM_CONTEXT_SCLASS] = (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ENSURE),
[PM_CONTEXT_SCLASS_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_SCLASS_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_SCLASS_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_TERNARY] = (1 << PM_TOKEN_EOF),
[PM_CONTEXT_UNLESS] = (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_UNTIL] = (1 << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_WHILE] = (1 << PM_TOKEN_KEYWORD_END),
};
return false;
static inline bool
context_terminator(pm_context_t context, pm_token_t *token) {
return token->type < 32 && (context_terminators[context] & (1 << token->type));
}
/**
@ -21202,6 +21183,13 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
}
PRISM_FALLTHROUGH
case PM_CASE_WRITABLE: {
// When we have `it = value`, we need to add `it` as a local
// variable before parsing the value, in case the value
// references the variable.
if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
pm_parser_local_add_location(parser, node->location.start, node->location.end, 0);
}
parser_lex(parser);
pm_node_t *value = parse_assignment_values(parser, previous_binding_power, PM_NODE_TYPE_P(node, PM_MULTI_TARGET_NODE) ? PM_BINDING_POWER_MULTI_ASSIGNMENT + 1 : binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1));
@ -22195,6 +22183,12 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc
) {
node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right, accepts_command_call, (uint16_t) (depth + 1));
if (context_terminator(parser->current_context->context, &parser->current)) {
// If this token terminates the current context, then we need to
// stop parsing the expression, as it has become a statement.
return node;
}
switch (PM_NODE_TYPE(node)) {
case PM_MULTI_WRITE_NODE:
// Multi-write nodes are statements, and cannot be followed by

View file

@ -19,6 +19,7 @@
#include "internal/thread.h"
#include "variable.h"
#include "yjit.h"
#include "zjit.h"
VALUE rb_cRactor;
static VALUE rb_cRactorSelector;
@ -511,6 +512,7 @@ ractor_create(rb_execution_context_t *ec, VALUE self, VALUE loc, VALUE name, VAL
r->debug = cr->debug;
rb_yjit_before_ractor_spawn();
rb_zjit_before_ractor_spawn();
rb_thread_create_ractor(r, args, block);
RB_GC_GUARD(rv);
@ -1679,8 +1681,7 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
} while (0)
if (UNLIKELY(rb_obj_exivar_p(obj))) {
VALUE fields_obj;
rb_ivar_generic_fields_tbl_lookup(obj, &fields_obj);
VALUE fields_obj = rb_obj_fields_no_ractor_check(obj);
if (UNLIKELY(rb_shape_obj_too_complex_p(obj))) {
struct obj_traverse_replace_callback_data d = {
@ -2303,7 +2304,7 @@ static const rb_data_type_t cross_ractor_require_data_type = {
NULL, // memsize
NULL, // compact
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_DECL_MARKING
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_DECL_MARKING | RUBY_TYPED_EMBEDDABLE
};
static VALUE

View file

@ -1273,7 +1273,7 @@ static const rb_data_type_t ractor_selector_data_type = {
ractor_selector_memsize,
NULL, // update
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static struct ractor_selector *
@ -1318,6 +1318,8 @@ ractor_selector_add(VALUE selv, VALUE rpv)
}
st_insert(s->ports, (st_data_t)rpv, (st_data_t)rp);
RB_OBJ_WRITTEN(selv, Qundef, rpv);
return selv;
}

View file

@ -263,7 +263,7 @@ const rb_data_type_t rb_random_data_type = {
random_free,
random_memsize,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
};
#define random_mt_mark rb_random_mark
@ -284,7 +284,7 @@ static const rb_data_type_t random_mt_type = {
},
&rb_random_data_type,
(void *)&random_mt_if,
RUBY_TYPED_FREE_IMMEDIATELY
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
};
static rb_random_t *
@ -422,10 +422,10 @@ random_init(int argc, VALUE *argv, VALUE obj)
argc = rb_check_arity(argc, 0, 1);
rb_check_frozen(obj);
if (argc == 0) {
rnd->seed = rand_init_default(rng, rnd);
RB_OBJ_WRITE(obj, &rnd->seed, rand_init_default(rng, rnd));
}
else {
rnd->seed = rand_init(rng, rnd, rb_to_int(argv[0]));
RB_OBJ_WRITE(obj, &rnd->seed, rand_init(rng, rnd, rb_to_int(argv[0])));
}
return obj;
}

6
ruby.c
View file

@ -1819,8 +1819,10 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
if (rb_namespace_available())
rb_initialize_main_namespace();
rb_namespace_init_done();
ruby_init_prelude();
// Initialize JITs after prelude because JITing prelude is typically not optimal.
// Initialize JITs after ruby_init_prelude() because JITing prelude is typically not optimal.
#if USE_YJIT
rb_yjit_init(opt->yjit);
#endif
@ -1831,8 +1833,6 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
}
#endif
rb_namespace_init_done();
ruby_init_prelude();
ruby_set_script_name(opt->script_name);
require_libraries(&opt->req_list);
}

View file

@ -3,7 +3,7 @@
#include "ruby/atomic.h"
#define RUBY_ATOMIC_VALUE_LOAD(x) (VALUE)(RUBY_ATOMIC_PTR_LOAD(x))
#define RUBY_ATOMIC_VALUE_LOAD(x) rbimpl_atomic_value_load(&(x), RBIMPL_ATOMIC_SEQ_CST)
/* shim macros only */
#define ATOMIC_ADD(var, val) RUBY_ATOMIC_ADD(var, val)
@ -27,16 +27,7 @@
#define ATOMIC_VALUE_CAS(var, oldval, val) RUBY_ATOMIC_VALUE_CAS(var, oldval, val)
#define ATOMIC_VALUE_EXCHANGE(var, val) RUBY_ATOMIC_VALUE_EXCHANGE(var, val)
static inline rb_atomic_t
rbimpl_atomic_load_relaxed(volatile rb_atomic_t *ptr)
{
#if defined(HAVE_GCC_ATOMIC_BUILTINS)
return __atomic_load_n(ptr, __ATOMIC_RELAXED);
#else
return *ptr;
#endif
}
#define ATOMIC_LOAD_RELAXED(var) rbimpl_atomic_load_relaxed(&(var))
#define ATOMIC_LOAD_RELAXED(var) rbimpl_atomic_load(&(var), RBIMPL_ATOMIC_RELAXED)
typedef RBIMPL_ALIGNAS(8) uint64_t rbimpl_atomic_uint64_t;

View file

@ -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 {
@ -901,6 +904,8 @@ typedef struct RNode_MODULE {
struct RNode *nd_cpath;
struct RNode *nd_body;
rb_code_location_t module_keyword_loc;
rb_code_location_t end_keyword_loc;
} rb_node_module_t;
typedef struct RNode_SCLASS {

5
set.c
View file

@ -139,7 +139,6 @@ set_mark(void *ptr)
static void
set_free_embedded(struct set_object *sobj)
{
free((&sobj->table)->bins);
free((&sobj->table)->entries);
}
@ -172,9 +171,7 @@ set_foreach_replace(st_data_t key, st_data_t argp, int error)
static int
set_replace_ref(st_data_t *key, st_data_t argp, int existing)
{
if (rb_gc_location((VALUE)*key) != (VALUE)*key) {
*key = rb_gc_location((VALUE)*key);
}
rb_gc_mark_and_move((VALUE *)key);
return ST_CONTINUE;
}

21
shape.c
View file

@ -296,26 +296,13 @@ rb_shape_get_root_shape(void)
}
static void
shape_tree_mark(void *data)
shape_tree_mark_and_move(void *data)
{
rb_shape_t *cursor = rb_shape_get_root_shape();
rb_shape_t *end = RSHAPE(rb_shape_tree.next_shape_id - 1);
while (cursor <= end) {
if (cursor->edges && !SINGLE_CHILD_P(cursor->edges)) {
rb_gc_mark_movable(cursor->edges);
}
cursor++;
}
}
static void
shape_tree_compact(void *data)
{
rb_shape_t *cursor = rb_shape_get_root_shape();
rb_shape_t *end = RSHAPE(rb_shape_tree.next_shape_id - 1);
while (cursor <= end) {
if (cursor->edges && !SINGLE_CHILD_P(cursor->edges)) {
cursor->edges = rb_gc_location(cursor->edges);
rb_gc_mark_and_move(&cursor->edges);
}
cursor++;
}
@ -330,10 +317,10 @@ shape_tree_memsize(const void *data)
static const rb_data_type_t shape_tree_type = {
.wrap_struct_name = "VM/shape_tree",
.function = {
.dmark = shape_tree_mark,
.dmark = shape_tree_mark_and_move,
.dfree = NULL, // Nothing to free, done at VM exit in rb_shape_free_all,
.dsize = shape_tree_memsize,
.dcompact = shape_tree_compact,
.dcompact = shape_tree_mark_and_move,
},
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};

96
st.c
View file

@ -2395,18 +2395,44 @@ set_get_allocated_entries(const set_table *tab)
return ((st_index_t) 1)<<tab->entry_power;
}
static inline size_t
set_allocated_entries_size(const set_table *tab)
{
return set_get_allocated_entries(tab) * sizeof(set_table_entry);
}
static inline bool
set_has_bins(const set_table *tab)
{
return tab->entry_power > MAX_POWER2_FOR_TABLES_WITHOUT_BINS;
}
/* Return size of the allocated bins of table TAB. */
static inline st_index_t
set_bins_size(const set_table *tab)
{
return features[tab->entry_power].bins_words * sizeof (st_index_t);
if (set_has_bins(tab)) {
return features[tab->entry_power].bins_words * sizeof (st_index_t);
}
return 0;
}
static inline st_index_t *
set_bins_ptr(const set_table *tab)
{
if (set_has_bins(tab)) {
return (st_index_t *)(((char *)tab->entries) + set_allocated_entries_size(tab));
}
return NULL;
}
/* Mark all bins of table TAB as empty. */
static void
set_initialize_bins(set_table *tab)
{
memset(tab->bins, 0, set_bins_size(tab));
memset(set_bins_ptr(tab), 0, set_bins_size(tab));
}
/* Make table TAB empty. */
@ -2415,7 +2441,7 @@ set_make_tab_empty(set_table *tab)
{
tab->num_entries = 0;
tab->entries_start = tab->entries_bound = 0;
if (tab->bins != NULL)
if (set_bins_ptr(tab) != NULL)
set_initialize_bins(tab);
}
@ -2443,13 +2469,13 @@ set_init_existing_table_with_size(set_table *tab, const struct st_hash_type *typ
tab->entry_power = n;
tab->bin_power = features[n].bin_power;
tab->size_ind = features[n].size_ind;
if (n <= MAX_POWER2_FOR_TABLES_WITHOUT_BINS)
tab->bins = NULL;
else {
tab->bins = (st_index_t *) malloc(set_bins_size(tab));
size_t memsize = 0;
if (set_has_bins(tab)) {
memsize += set_bins_size(tab);
}
tab->entries = (set_table_entry *) malloc(set_get_allocated_entries(tab)
* sizeof(set_table_entry));
memsize += set_get_allocated_entries(tab) * sizeof(set_table_entry);
tab->entries = (set_table_entry *)malloc(memsize);
set_make_tab_empty(tab);
tab->rebuilds_num = 0;
return tab;
@ -2499,7 +2525,6 @@ set_table_clear(set_table *tab)
void
set_free_table(set_table *tab)
{
free(tab->bins);
free(tab->entries);
free(tab);
}
@ -2509,7 +2534,7 @@ size_t
set_memsize(const set_table *tab)
{
return(sizeof(set_table)
+ (tab->bins == NULL ? 0 : set_bins_size(tab))
+ (tab->entry_power <= MAX_POWER2_FOR_TABLES_WITHOUT_BINS ? 0 : set_bins_size(tab))
+ set_get_allocated_entries(tab) * sizeof(set_table_entry));
}
@ -2542,7 +2567,7 @@ set_rebuild_table(set_table *tab)
|| tab->num_entries < (1 << MINIMAL_POWER2)) {
/* Compaction: */
tab->num_entries = 0;
if (tab->bins != NULL)
if (set_has_bins(tab))
set_initialize_bins(tab);
set_rebuild_table_with(tab, tab);
}
@ -2572,7 +2597,7 @@ set_rebuild_table_with(set_table *const new_tab, set_table *const tab)
new_entries = new_tab->entries;
ni = 0;
bins = new_tab->bins;
bins = set_bins_ptr(new_tab);
size_ind = set_get_size_ind(new_tab);
st_index_t bound = tab->entries_bound;
set_table_entry *entries = tab->entries;
@ -2602,8 +2627,6 @@ set_rebuild_move_table(set_table *const new_tab, set_table *const tab)
tab->entry_power = new_tab->entry_power;
tab->bin_power = new_tab->bin_power;
tab->size_ind = new_tab->size_ind;
free(tab->bins);
tab->bins = new_tab->bins;
free(tab->entries);
tab->entries = new_tab->entries;
free(new_tab);
@ -2688,7 +2711,7 @@ set_find_table_entry_ind(set_table *tab, st_hash_t hash_value, st_data_t key)
perturb = hash_value;
#endif
for (;;) {
bin = get_bin(tab->bins, set_get_size_ind(tab), ind);
bin = get_bin(set_bins_ptr(tab), set_get_size_ind(tab), ind);
if (! EMPTY_OR_DELETED_BIN_P(bin)) {
DO_PTR_EQUAL_CHECK(tab, &entries[bin - ENTRY_BASE], hash_value, key, eq_p, rebuilt_p);
if (EXPECT(rebuilt_p, 0))
@ -2732,7 +2755,7 @@ set_find_table_bin_ind(set_table *tab, st_hash_t hash_value, st_data_t key)
perturb = hash_value;
#endif
for (;;) {
bin = get_bin(tab->bins, set_get_size_ind(tab), ind);
bin = get_bin(set_bins_ptr(tab), set_get_size_ind(tab), ind);
if (! EMPTY_OR_DELETED_BIN_P(bin)) {
DO_PTR_EQUAL_CHECK(tab, &entries[bin - ENTRY_BASE], hash_value, key, eq_p, rebuilt_p);
if (EXPECT(rebuilt_p, 0))
@ -2773,7 +2796,7 @@ set_find_table_bin_ind_direct(set_table *tab, st_hash_t hash_value, st_data_t ke
perturb = hash_value;
#endif
for (;;) {
bin = get_bin(tab->bins, set_get_size_ind(tab), ind);
bin = get_bin(set_bins_ptr(tab), set_get_size_ind(tab), ind);
if (EMPTY_OR_DELETED_BIN_P(bin))
return ind;
#ifdef QUADRATIC_PROBE
@ -2787,7 +2810,7 @@ set_find_table_bin_ind_direct(set_table *tab, st_hash_t hash_value, st_data_t ke
/* Mark I-th bin of table TAB as empty, in other words not
corresponding to any entry. */
#define MARK_SET_BIN_EMPTY(tab, i) (set_bin((tab)->bins, set_get_size_ind(tab), i, EMPTY_BIN))
#define MARK_SET_BIN_EMPTY(tab, i) (set_bin(set_bins_ptr(tab), set_get_size_ind(tab), i, EMPTY_BIN))
/* Return index of table TAB bin for HASH_VALUE and KEY through
BIN_IND and the pointed value as the function result. Reserve the
@ -2823,7 +2846,7 @@ set_find_table_bin_ptr_and_reserve(set_table *tab, st_hash_t *hash_value,
firset_deleted_bin_ind = UNDEFINED_BIN_IND;
entries = tab->entries;
for (;;) {
entry_index = get_bin(tab->bins, set_get_size_ind(tab), ind);
entry_index = get_bin(set_bins_ptr(tab), set_get_size_ind(tab), ind);
if (EMPTY_BIN_P(entry_index)) {
tab->num_entries++;
entry_index = UNDEFINED_ENTRY_IND;
@ -2863,7 +2886,7 @@ set_table_lookup(set_table *tab, st_data_t key)
st_hash_t hash = set_do_hash(key, tab);
retry:
if (tab->bins == NULL) {
if (!set_has_bins(tab)) {
bin = set_find_entry(tab, hash, key);
if (EXPECT(bin == REBUILT_TABLE_ENTRY_IND, 0))
goto retry;
@ -2907,7 +2930,7 @@ set_insert(set_table *tab, st_data_t key)
hash_value = set_do_hash(key, tab);
retry:
set_rebuild_table_if_necessary(tab);
if (tab->bins == NULL) {
if (!set_has_bins(tab)) {
bin = set_find_entry(tab, hash_value, key);
if (EXPECT(bin == REBUILT_TABLE_ENTRY_IND, 0))
goto retry;
@ -2930,7 +2953,7 @@ set_insert(set_table *tab, st_data_t key)
entry->hash = hash_value;
entry->key = key;
if (bin_ind != UNDEFINED_BIN_IND)
set_bin(tab->bins, set_get_size_ind(tab), bin_ind, ind + ENTRY_BASE);
set_bin(set_bins_ptr(tab), set_get_size_ind(tab), bin_ind, ind + ENTRY_BASE);
return 0;
}
return 1;
@ -2941,18 +2964,9 @@ static set_table *
set_replace(set_table *new_tab, set_table *old_tab)
{
*new_tab = *old_tab;
if (old_tab->bins == NULL)
new_tab->bins = NULL;
else {
new_tab->bins = (st_index_t *) malloc(set_bins_size(old_tab));
}
new_tab->entries = (set_table_entry *) malloc(set_get_allocated_entries(old_tab)
* sizeof(set_table_entry));
MEMCPY(new_tab->entries, old_tab->entries, set_table_entry,
set_get_allocated_entries(old_tab));
if (old_tab->bins != NULL)
MEMCPY(new_tab->bins, old_tab->bins, char, set_bins_size(old_tab));
size_t memsize = set_allocated_entries_size(old_tab) + set_bins_size(old_tab);
new_tab->entries = (set_table_entry *)malloc(memsize);
MEMCPY(new_tab->entries, old_tab->entries, char, memsize);
return new_tab;
}
@ -2991,7 +3005,7 @@ set_update_range_for_deleted(set_table *tab, st_index_t n)
corresponding to deleted entries. */
#define MARK_SET_BIN_DELETED(tab, i) \
do { \
set_bin((tab)->bins, set_get_size_ind(tab), i, DELETED_BIN); \
set_bin(set_bins_ptr(tab), set_get_size_ind(tab), i, DELETED_BIN); \
} while (0)
/* Delete entry with KEY from table TAB, and return non-zero. If
@ -3006,7 +3020,7 @@ set_table_delete(set_table *tab, st_data_t *key)
hash = set_do_hash(*key, tab);
retry:
if (tab->bins == NULL) {
if (!set_has_bins(tab)) {
bin = set_find_entry(tab, hash, *key);
if (EXPECT(bin == REBUILT_TABLE_ENTRY_IND, 0))
goto retry;
@ -3021,7 +3035,7 @@ set_table_delete(set_table *tab, st_data_t *key)
if (bin_ind == UNDEFINED_BIN_IND) {
return 0;
}
bin = get_bin(tab->bins, set_get_size_ind(tab), bin_ind) - ENTRY_BASE;
bin = get_bin(set_bins_ptr(tab), set_get_size_ind(tab), bin_ind) - ENTRY_BASE;
MARK_SET_BIN_DELETED(tab, bin_ind);
}
entry = &tab->entries[bin];
@ -3052,7 +3066,7 @@ set_general_foreach(set_table *tab, set_foreach_check_callback_func *func,
st_index_t i, rebuilds_num;
st_hash_t hash;
st_data_t key;
int error_p, packed_p = tab->bins == NULL;
int error_p, packed_p = !set_has_bins(tab);
entries = tab->entries;
/* The bound can change inside the loop even without rebuilding
@ -3074,7 +3088,7 @@ set_general_foreach(set_table *tab, set_foreach_check_callback_func *func,
if (rebuilds_num != tab->rebuilds_num) {
retry:
entries = tab->entries;
packed_p = tab->bins == NULL;
packed_p = !set_has_bins(tab);
if (packed_p) {
i = set_find_entry(tab, hash, key);
if (EXPECT(i == REBUILT_TABLE_ENTRY_IND, 0))
@ -3122,7 +3136,7 @@ set_general_foreach(set_table *tab, set_foreach_check_callback_func *func,
goto again;
if (bin_ind == UNDEFINED_BIN_IND)
break;
bin = get_bin(tab->bins, set_get_size_ind(tab), bin_ind) - ENTRY_BASE;
bin = get_bin(set_bins_ptr(tab), set_get_size_ind(tab), bin_ind) - ENTRY_BASE;
MARK_SET_BIN_DELETED(tab, bin_ind);
}
curr_entry_ptr = &entries[bin];

View file

@ -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);
}
/*
@ -6576,15 +6576,12 @@ str_gsub(int argc, VALUE *argv, VALUE str, int bang)
* gsub!(pattern) {|match| ... } -> self or nil
* gsub!(pattern) -> an_enumerator
*
* Performs the specified substring replacement(s) on +self+;
* returns +self+ if any replacement occurred, +nil+ otherwise.
* Like String#gsub, except that:
*
* See {Substitution Methods}[rdoc-ref:String@Substitution+Methods].
*
* Returns an Enumerator if no +replacement+ and no block given.
*
* Related: String#sub, String#gsub, String#sub!.
* - Performs substitutions in +self+ (not in a copy of +self+).
* - Returns +self+ if any characters are removed, +nil+ otherwise.
*
* Related: see {Modifying}[rdoc-ref:String@Modifying].
*/
static VALUE
@ -6601,14 +6598,41 @@ rb_str_gsub_bang(int argc, VALUE *argv, VALUE str)
* gsub(pattern) {|match| ... } -> new_string
* gsub(pattern) -> enumerator
*
* Returns a copy of +self+ with all occurrences of the given +pattern+ replaced.
* Returns a copy of +self+ with zero or more substrings replaced.
*
* See {Substitution Methods}[rdoc-ref:String@Substitution+Methods].
* Argument +pattern+ may be a string or a Regexp;
* argument +replacement+ may be a string or a Hash.
* Varying types for the argument values makes this method very versatile.
*
* Returns an Enumerator if no +replacement+ and no block given.
* Below are some simple examples;
* for many more examples, see {Substitution Methods}[rdoc-ref:String@Substitution+Methods].
*
* Related: String#sub, String#sub!, String#gsub!.
* With arguments +pattern+ and string +replacement+ given,
* replaces each matching substring with the given +replacement+ string:
*
* s = 'abracadabra'
* s.gsub('ab', 'AB') # => "ABracadABra"
* s.gsub(/[a-c]/, 'X') # => "XXrXXXdXXrX"
*
* With arguments +pattern+ and hash +replacement+ given,
* replaces each matching substring with a value from the given +replacement+ hash,
* or removes it:
*
* h = {'a' => 'A', 'b' => 'B', 'c' => 'C'}
* s.gsub(/[a-c]/, h) # => "ABrACAdABrA" # 'a', 'b', 'c' replaced.
* s.gsub(/[a-d]/, h) # => "ABrACAABrA" # 'd' removed.
*
* With argument +pattern+ and a block given,
* calls the block with each matching substring;
* replaces that substring with the block's return value:
*
* s.gsub(/[a-d]/) {|substring| substring.upcase }
* # => "ABrACADABrA"
*
* With argument +pattern+ and no block given,
* returns a new Enumerator.
*
* Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String].
*/
static VALUE

View file

@ -811,13 +811,22 @@ struct_alloc(VALUE klass)
{
long n = num_members(klass);
size_t embedded_size = offsetof(struct RStruct, as.ary) + (sizeof(VALUE) * n);
if (RCLASS_MAX_IV_COUNT(klass) > 0) {
embedded_size += sizeof(VALUE);
}
VALUE flags = T_STRUCT | (RGENGC_WB_PROTECTED_STRUCT ? FL_WB_PROTECTED : 0);
if (n > 0 && rb_gc_size_allocatable_p(embedded_size)) {
flags |= n << RSTRUCT_EMBED_LEN_SHIFT;
NEWOBJ_OF(st, struct RStruct, klass, flags, embedded_size, 0);
if (RCLASS_MAX_IV_COUNT(klass) == 0 && embedded_size == rb_gc_obj_slot_size((VALUE)st)) {
FL_SET_RAW((VALUE)st, RSTRUCT_GEN_FIELDS);
}
else {
RSTRUCT_SET_FIELDS_OBJ((VALUE)st, 0);
}
rb_mem_clear((VALUE *)st->as.ary, n);
return (VALUE)st;

View file

@ -99,7 +99,9 @@ typedef struct {
VALUE ids;
} rb_symbols_t;
rb_symbols_t ruby_global_symbols = {tNEXT_ID-1};
rb_symbols_t ruby_global_symbols = {
.next_id = tNEXT_ID,
};
struct sym_set_static_sym_entry {
VALUE sym;
@ -369,21 +371,26 @@ Init_sym(void)
}
void
rb_sym_global_symbols_mark(void)
rb_sym_global_symbols_mark_and_move(void)
{
rb_symbols_t *symbols = &ruby_global_symbols;
rb_gc_mark_movable(symbols->sym_set);
rb_gc_mark_movable(symbols->ids);
rb_gc_mark_and_move(&symbols->sym_set);
rb_gc_mark_and_move(&symbols->ids);
}
static int
rb_free_global_symbol_table_i(VALUE *sym_ptr, void *data)
{
sym_set_free(*sym_ptr);
return ST_DELETE;
}
void
rb_sym_global_symbols_update_references(void)
rb_free_global_symbol_table(void)
{
rb_symbols_t *symbols = &ruby_global_symbols;
symbols->sym_set = rb_gc_location(symbols->sym_set);
symbols->ids = rb_gc_location(symbols->ids);
rb_concurrent_set_foreach_with_replace(ruby_global_symbols.sym_set, rb_free_global_symbol_table_i, NULL);
}
WARN_UNUSED_RESULT(static ID lookup_str_id(VALUE str));

View file

@ -289,6 +289,8 @@ ABI_VERSION_HDR = $(hdrdir)/ruby/internal/abi.h
CAT_DEPEND = sed -e 's/{\$$([^(){}]*)[^{}]*}//g' -e /AUTOGENERATED/q
HASH_SIGN = \#
.SUFFIXES: .inc .h .c .y .i .$(ASMEXT) .$(DTRACE_EXT)
all:

View file

@ -1,2 +0,0 @@
# Issue: https://github.com/Shopify/ruby/issues/646
exclude(/test_/, 'Tests make ZJIT panic on Ubuntu')

View file

@ -46,21 +46,26 @@ class TestMkmfPkgConfig < TestMkmf
def test_pkgconfig_with_libs_option_returns_output
pend("skipping because pkg-config is not installed") unless PKG_CONFIG
expected = ["-L#{@fixtures_lib_dir}", "-ltest1-public"].sort
actual = pkg_config("test1", "libs").shellsplit.sort
assert_equal(expected, actual, MKMFLOG)
actual = pkg_config("test1", "libs")
assert_equal_sorted(expected, actual, MKMFLOG)
end
def test_pkgconfig_with_cflags_option_returns_output
pend("skipping because pkg-config is not installed") unless PKG_CONFIG
expected = ["--cflags-other", "-I#{@fixtures_inc_dir}/cflags-I"].sort
actual = pkg_config("test1", "cflags").shellsplit.sort
assert_equal(expected, actual, MKMFLOG)
actual = pkg_config("test1", "cflags")
assert_equal_sorted(expected, actual, MKMFLOG)
end
def test_pkgconfig_with_multiple_options
pend("skipping because pkg-config is not installed") unless PKG_CONFIG
expected = ["-L#{@fixtures_lib_dir}", "-ltest1-public", "-ltest1-private"].sort
actual = pkg_config("test1", "libs", "static").shellsplit.sort
assert_equal(expected, actual, MKMFLOG)
actual = pkg_config("test1", "libs", "static")
assert_equal_sorted(expected, actual, MKMFLOG)
end
private def assert_equal_sorted(expected, actual, msg = nil)
actual = actual.shellsplit.sort if actual
assert_equal(expected, actual, msg)
end
end

View file

@ -103,6 +103,8 @@ class OpenSSL::TestPKeyDH < OpenSSL::PKeyTestCase
end if !openssl?(3, 0, 0)
def test_params_ok?
omit_on_fips
# Skip the tests in old OpenSSL version 1.1.1c or early versions before
# applying the following commits in OpenSSL 1.1.1d to make `DH_check`
# function pass the RFC 7919 FFDHE group texts.

View file

@ -72,6 +72,8 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase
end
def test_check_key
omit_on_fips
key0 = Fixtures.pkey("p256")
assert_equal(true, key0.check_key)
assert_equal(true, key0.private?)

View file

@ -47,7 +47,7 @@ class TestOptionParserLoad < Test::Unit::TestCase
begin
yield dir, optdir
ensure
File.unlink(file)
File.unlink(file) rescue nil
Dir.rmdir(optdir) rescue nil
end
else
@ -101,7 +101,7 @@ class TestOptionParserLoad < Test::Unit::TestCase
end
def test_load_xdg_config_home
result, = setup_options_xdg_config_home
result, dir = setup_options_xdg_config_home
assert_load(result)
setup_options_home_config do
@ -115,6 +115,11 @@ class TestOptionParserLoad < Test::Unit::TestCase
setup_options_home_config_settings do
assert_load(result)
end
File.unlink("#{dir}/#{@basename}.options")
setup_options_home_config do
assert_load_nothing
end
end
def test_load_home_config
@ -128,6 +133,11 @@ class TestOptionParserLoad < Test::Unit::TestCase
setup_options_home_config_settings do
assert_load(result)
end
setup_options_xdg_config_home do |_, dir|
File.unlink("#{dir}/#{@basename}.options")
assert_load_nothing
end
end
def test_load_xdg_config_dirs

View file

@ -0,0 +1,6 @@
case 1
in 2
A.print message:
in 3
A.print message:
end

View file

@ -0,0 +1 @@
42.tap { it = it; p it }

View file

@ -0,0 +1 @@
42.tap { p it; it = it; p it }

View file

@ -62,7 +62,11 @@ module Prism
if reader
reader.gets.chomp
else
puts(ignore_warnings { Ractor.new(*arguments, &block) }.value)
ractor = ignore_warnings { Ractor.new(*arguments, &block) }
# Somewhere in the Ruby 3.5.* series, Ractor#take was removed and
# Ractor#value was added.
puts(ractor.respond_to?(:value) ? ractor.value : ractor.take)
end
end
end

View file

@ -54,7 +54,7 @@ module Prism
assert_parameters([[:keyrest, :**]], "**")
end
if RUBY_ENGINE != "truffleruby"
if RUBY_ENGINE == "ruby"
def test_key_ordering
assert_parameters([[:keyreq, :a], [:keyreq, :b], [:key, :c], [:key, :d]], "a:, c: 1, b:, d: 2")
end

View file

@ -99,16 +99,6 @@ module Prism
"seattlerb/regexp_esc_C_slash.txt",
]
# These files are either failing to parse or failing to translate, so we'll
# skip them for now.
skip_all = skip_incorrect | [
]
# Not sure why these files are failing on JRuby, but skipping them for now.
if RUBY_ENGINE == "jruby"
skip_all.push("emoji_method_calls.txt", "symbols.txt")
end
# These files are failing to translate their lexer output into the lexer
# output expected by the parser gem, so we'll skip them for now.
skip_tokens = [
@ -147,7 +137,7 @@ module Prism
define_method(fixture.test_name) do
assert_equal_parses(
fixture,
compare_asts: !skip_all.include?(fixture.path),
compare_asts: !skip_incorrect.include?(fixture.path),
compare_tokens: !skip_tokens.include?(fixture.path),
compare_comments: fixture.path != "embdoc_no_newline_at_end.txt"
)

View file

@ -1,6 +1,6 @@
# frozen_string_literal: true
return if RUBY_VERSION < "3.3" || RUBY_ENGINE == "truffleruby"
return if RUBY_VERSION < "3.3" || RUBY_ENGINE != "ruby"
require_relative "../test_helper"

View file

@ -1491,6 +1491,11 @@ dummy
assert_locations(node.children[-1].locations, [[1, 0, 1, 20], [1, 0, 1, 2], [1, 10, 1, 12], [1, 17, 1, 20]])
end
def test_module_locations
node = ast_parse('module A end')
assert_locations(node.children[-1].locations, [[1, 0, 1, 12], [1, 0, 1, 6], [1, 9, 1, 12]])
end
def test_if_locations
node = ast_parse("if cond then 1 else 2 end")
assert_locations(node.children[-1].locations, [[1, 0, 1, 25], [1, 0, 1, 2], [1, 8, 1, 12], [1, 22, 1, 25]])
@ -1509,6 +1514,20 @@ dummy
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]])

View file

@ -75,12 +75,9 @@ class TestGc < Test::Unit::TestCase
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

View file

@ -252,3 +252,52 @@ class TestObjectIdRactor < Test::Unit::TestCase
end;
end
end
class TestObjectIdStruct < TestObjectId
EmbeddedStruct = Struct.new(:embedded_field)
def setup
@obj = EmbeddedStruct.new
end
end
class TestObjectIdStructGenIvar < TestObjectId
GenIvarStruct = Struct.new(:a, :b, :c)
def setup
@obj = GenIvarStruct.new
end
end
class TestObjectIdStructNotEmbed < TestObjectId
MANY_IVS = 80
StructNotEmbed = Struct.new(*MANY_IVS.times.map { |i| :"field_#{i}" })
def setup
@obj = StructNotEmbed.new
end
end
class TestObjectIdStructTooComplex < TestObjectId
StructTooComplex = Struct.new(:a) do
def initialize
@too_complex_obj_id_test = 1
end
end
def setup
if defined?(RubyVM::Shape::SHAPE_MAX_VARIATIONS)
assert_equal 8, RubyVM::Shape::SHAPE_MAX_VARIATIONS
end
8.times do |i|
StructTooComplex.new.instance_variable_set("@TestObjectIdStructTooComplex#{i}", 1)
end
@obj = StructTooComplex.new
@obj.instance_variable_set("@a#{rand(10_000)}", 1)
if defined?(RubyVM::Shape)
assert_predicate(RubyVM::Shape.of(@obj), :too_complex?)
end
end
end

View file

@ -284,6 +284,21 @@ End
end;
end
def test_id2ref_table_build
assert_separately([], <<-End)
10.times do
Object.new.object_id
end
GC.start(immediate_mark: false)
obj = Object.new
EnvUtil.suppress_warning do
assert_equal obj, ObjectSpace._id2ref(obj.object_id)
end
End
end
def test_each_object_singleton_class
assert_separately([], <<-End)
class C

View file

@ -99,6 +99,24 @@ class TestRactor < Test::Unit::TestCase
RUBY
end
def test_struct_instance_variables
assert_ractor(<<~'RUBY')
StructIvar = Struct.new(:member) do
def initialize(*)
super
@ivar = "ivar"
end
attr_reader :ivar
end
obj = StructIvar.new("member")
obj_copy = Ractor.new { Ractor.receive }.send(obj).value
assert_equal obj.ivar, obj_copy.ivar
refute_same obj.ivar, obj_copy.ivar
assert_equal obj.member, obj_copy.member
refute_same obj.member, obj_copy.member
RUBY
end
def test_fork_raise_isolation_error
assert_ractor(<<~'RUBY')
ractor = Ractor.new do
@ -144,6 +162,45 @@ class TestRactor < Test::Unit::TestCase
RUBY
end
# [Bug #21398]
def test_port_receive_dnt_with_port_send
assert_ractor(<<~'RUBY', timeout: 30)
THREADS = 10
JOBS_PER_THREAD = 50
ARRAY_SIZE = 20_000
def ractor_job(job_count, array_size)
port = Ractor::Port.new
workers = (1..4).map do |i|
Ractor.new(port) do |job_port|
while job = Ractor.receive
result = job.map { |x| x * 2 }.sum
job_port.send result
end
end
end
jobs = Array.new(job_count) { Array.new(array_size) { rand(1000) } }
jobs.each_with_index do |job, i|
w_idx = i % 4
workers[w_idx].send(job)
end
results = []
jobs.size.times do
result = port.receive # dnt receive
results << result
end
results
end
threads = []
# creates 40 ractors (THREADSx4)
THREADS.times do
threads << Thread.new do
ractor_job(JOBS_PER_THREAD, ARRAY_SIZE)
end
end
threads.each(&:join)
RUBY
end
def assert_make_shareable(obj)
refute Ractor.shareable?(obj), "object was already shareable"
Ractor.make_shareable(obj)

View file

@ -924,6 +924,18 @@ class TC_Set < Test::Unit::TestCase
end
end;
end
def test_larger_sets
set = Set.new
10_000.times do |i|
set << i
end
set = set.dup
10_000.times do |i|
assert_includes set, i
end
end
end
class TC_Enumerable < Test::Unit::TestCase

View file

@ -2832,16 +2832,32 @@ CODE
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))
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))
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?
@ -2854,9 +2870,16 @@ CODE
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))
s1 = S("\xff".force_encoding("UTF-8"))
s2 = S("\xff".force_encoding("ISO-2022-JP"))
assert_nil(s1.casecmp?(s2))
end
def test_upcase2

View file

@ -2320,6 +2320,46 @@ class TestTranscode < Test::Unit::TestCase
assert_equal("A\nB\nC", s.encode(usascii, newline: :lf))
end
def test_ractor_lazy_load_encoding
assert_ractor("#{<<~"begin;"}\n#{<<~'end;'}")
begin;
rs = []
autoload_encodings = Encoding.list.select { |e| e.inspect.include?("(autoload)") }.freeze
7.times do
rs << Ractor.new(autoload_encodings) do |encodings|
str = "\u0300"
encodings.each do |enc|
str.encode(enc) rescue Encoding::UndefinedConversionError
end
end
end
while rs.any?
r, _obj = Ractor.select(*rs)
rs.delete(r)
end
assert rs.empty?
end;
end
def test_ractor_lazy_load_encoding_random
assert_ractor("#{<<~"begin;"}\n#{<<~'end;'}")
begin;
rs = []
100.times do
rs << Ractor.new do
"\u0300".encode(Encoding.list.sample) rescue Encoding::UndefinedConversionError
end
end
while rs.any?
r, _obj = Ractor.select(*rs)
rs.delete(r)
end
assert rs.empty?
end;
end
private
def assert_conversion_both_ways_utf8(utf8, raw, encoding)

View file

@ -9,6 +9,15 @@ require_relative '../lib/jit_support'
return unless JITSupport.zjit_supported?
class TestZJIT < Test::Unit::TestCase
def test_enabled
assert_runs 'false', <<~RUBY, zjit: false
RubyVM::ZJIT.enabled?
RUBY
assert_runs 'true', <<~RUBY, zjit: true
RubyVM::ZJIT.enabled?
RUBY
end
def test_call_itself
assert_compiles '42', <<~RUBY, call_threshold: 2
def test = 42.itself
@ -52,6 +61,30 @@ class TestZJIT < Test::Unit::TestCase
}
end
def test_setglobal
assert_compiles '1', %q{
def test
$a = 1
$a
end
test
}, insns: [:setglobal]
end
def test_setglobal_with_trace_var_exception
assert_compiles '"rescued"', %q{
def test
$a = 1
rescue
"rescued"
end
trace_var(:$a) { raise }
test
}, insns: [:setglobal]
end
def test_setlocal
assert_compiles '3', %q{
def test(n)
@ -70,6 +103,15 @@ class TestZJIT < Test::Unit::TestCase
}
end
def test_call_a_forwardable_method
assert_runs '[]', %q{
def test_root = forwardable
def forwardable(...) = Array.[](...)
test_root
test_root
}, call_threshold: 2
end
def test_setlocal_on_eval_with_spill
assert_compiles '1', %q{
@b = binding
@ -148,6 +190,18 @@ class TestZJIT < Test::Unit::TestCase
}, call_threshold: 2
end
def test_send_on_heap_object_in_spilled_arg
# This leads to a register spill, so not using `assert_compiles`
assert_runs 'Hash', %q{
def entry(a1, a2, a3, a4, a5, a6, a7, a8, a9)
a9.itself.class
end
entry(1, 2, 3, 4, 5, 6, 7, 8, {}) # profile
entry(1, 2, 3, 4, 5, 6, 7, 8, {})
}, call_threshold: 2
end
def test_invokebuiltin
omit 'Test fails at the moment due to not handling optional parameters'
assert_compiles '["."]', %q{
@ -283,6 +337,14 @@ class TestZJIT < Test::Unit::TestCase
}, insns: [:opt_eq], call_threshold: 2
end
def test_opt_eq_with_minus_one
assert_compiles '[false, true]', %q{
def test(a) = a == -1
test(1) # profile opt_eq
[test(0), test(-1)]
}, insns: [:opt_eq], call_threshold: 2
end
def test_opt_neq_dynamic
# TODO(max): Don't split this test; instead, run all tests with and without
# profiling.
@ -879,6 +941,38 @@ class TestZJIT < Test::Unit::TestCase
}
end
def test_attr_reader
assert_compiles '[4, 4]', %q{
class C
attr_reader :foo
def initialize
@foo = 4
end
end
def test(c) = c.foo
c = C.new
[test(c), test(c)]
}, call_threshold: 2, insns: [:opt_send_without_block]
end
def test_attr_accessor
assert_compiles '[4, 4]', %q{
class C
attr_accessor :foo
def initialize
@foo = 4
end
end
def test(c) = c.foo
c = C.new
[test(c), test(c)]
}, call_threshold: 2, insns: [:opt_send_without_block]
end
def test_uncached_getconstant_path
assert_compiles RUBY_COPYRIGHT.dump, %q{
def test = RUBY_COPYRIGHT
@ -942,6 +1036,26 @@ class TestZJIT < Test::Unit::TestCase
RUBY
end
def test_single_ractor_mode_invalidation
# Without invalidating the single-ractor mode, the test would crash
assert_compiles '"errored but not crashed"', <<~RUBY, call_threshold: 2, insns: [:opt_getconstant_path]
C = Object.new
def test
C
rescue Ractor::IsolationError
"errored but not crashed"
end
test
test
Ractor.new {
test
}.value
RUBY
end
def test_dupn
assert_compiles '[[1], [1, 1], :rhs, [nil, :rhs]]', <<~RUBY, insns: [:dupn]
def test(array) = (array[1, 2] ||= :rhs)
@ -983,6 +1097,26 @@ class TestZJIT < Test::Unit::TestCase
}
end
def test_defined_with_defined_values
assert_compiles '["constant", "method", "global-variable"]', %q{
class Foo; end
def bar; end
$ruby = 1
def test = return defined?(Foo), defined?(bar), defined?($ruby)
test
}, insns: [:defined]
end
def test_defined_with_undefined_values
assert_compiles '[nil, nil, nil]', %q{
def test = return defined?(Foo), defined?(bar), defined?($ruby)
test
}, insns: [:defined]
end
def test_defined_yield
assert_compiles "nil", "defined?(yield)"
assert_compiles '[nil, nil, "yield"]', %q{
@ -1372,6 +1506,46 @@ class TestZJIT < Test::Unit::TestCase
}, call_threshold: 2, insns: [:opt_nil_p]
end
def test_basic_object_guard_works_with_immediate
assert_compiles 'NilClass', %q{
class Foo; end
def test(val) = val.class
test(Foo.new)
test(Foo.new)
test(nil)
}, call_threshold: 2
end
def test_basic_object_guard_works_with_false
assert_compiles 'FalseClass', %q{
class Foo; end
def test(val) = val.class
test(Foo.new)
test(Foo.new)
test(false)
}, call_threshold: 2
end
def test_string_concat
assert_compiles '"123"', %q{
def test = "#{1}#{2}#{3}"
test
}, insns: [:concatstrings]
end
def test_string_concat_empty
assert_compiles '""', %q{
def test = "#{}"
test
}, insns: [:concatstrings]
end
private
# Assert that every method call in `test_script` can be compiled by ZJIT
@ -1422,14 +1596,23 @@ class TestZJIT < Test::Unit::TestCase
end
# Run a Ruby process with ZJIT options and a pipe for writing test results
def eval_with_jit(script, call_threshold: 1, num_profiles: 1, stats: false, debug: true, timeout: 1000, pipe_fd:)
args = [
"--disable-gems",
"--zjit-call-threshold=#{call_threshold}",
"--zjit-num-profiles=#{num_profiles}",
]
args << "--zjit-stats" if stats
args << "--zjit-debug" if debug
def eval_with_jit(
script,
call_threshold: 1,
num_profiles: 1,
zjit: true,
stats: false,
debug: true,
timeout: 1000,
pipe_fd:
)
args = ["--disable-gems"]
if zjit
args << "--zjit-call-threshold=#{call_threshold}"
args << "--zjit-num-profiles=#{num_profiles}"
args << "--zjit-stats" if stats
args << "--zjit-debug" if debug
end
args << "-e" << script_shell_encode(script)
pipe_r, pipe_w = IO.pipe
# Separate thread so we don't deadlock when

View file

@ -5,13 +5,6 @@ require "rubygems"
require "shellwords"
class TestGemConfig < Gem::TestCase
def test_datadir
util_make_gems
spec = Gem::Specification.find_by_name("a")
spec.activate
assert_equal "#{spec.full_gem_path}/data/a", spec.datadir
end
def test_good_rake_path_is_escaped
path = Gem::TestCase.class_variable_get(:@@good_rake)
ruby, rake = path.shellsplit

View file

@ -527,35 +527,6 @@ class TestGem < Gem::TestCase
assert_equal expected, Gem.configuration
end
def test_self_datadir
foo = nil
Dir.chdir @tempdir do
FileUtils.mkdir_p "data"
File.open File.join("data", "foo.txt"), "w" do |fp|
fp.puts "blah"
end
foo = util_spec "foo" do |s|
s.files = %w[data/foo.txt]
end
install_gem foo
end
gem "foo"
expected = File.join @gemhome, "gems", foo.full_name, "data", "foo"
assert_equal expected, Gem::Specification.find_by_name("foo").datadir
end
def test_self_datadir_nonexistent_package
assert_raise(Gem::MissingSpecError) do
Gem::Specification.find_by_name("xyzzy").datadir
end
end
def test_self_default_exec_format
ruby_install_name "ruby" do
assert_equal "%s", Gem.default_exec_format

View file

@ -8,6 +8,100 @@ require "rubygems/package"
class TestGemRemoteFetcherS3 < Gem::TestCase
include Gem::DefaultUserInteraction
class FakeGemRequest < Gem::Request
attr_reader :last_request, :uri
# Override perform_request to stub things
def perform_request(request)
@last_request = request
@response
end
def set_response(response)
@response = response
end
end
class FakeS3URISigner < Gem::S3URISigner
class << self
attr_accessor :return_token, :instance_profile
end
# Convenience method to output the recent aws iam queries made in tests
# this outputs the verb, path, and any non-generic headers
def recent_aws_query_logs
sreqs = @aws_iam_calls.map do |c|
r = c.last_request
s = +"#{r.method} #{c.uri}\n"
r.each_header do |key, v|
# Only include headers that start with x-
next unless key.start_with?("x-")
s << " #{key}=#{v}\n"
end
s
end
sreqs.join("")
end
def initialize(uri, method)
@aws_iam_calls = []
super
end
def ec2_iam_request(uri, verb)
fake_s3_request = FakeGemRequest.new(uri, verb, nil, nil)
@aws_iam_calls << fake_s3_request
case uri.to_s
when "http://169.254.169.254/latest/api/token"
if FakeS3URISigner.return_token.nil?
res = Gem::Net::HTTPUnauthorized.new nil, 401, nil
def res.body = "you got a 401! panic!"
else
res = Gem::Net::HTTPOK.new nil, 200, nil
def res.body = FakeS3URISigner.return_token
end
when "http://169.254.169.254/latest/meta-data/iam/info"
res = Gem::Net::HTTPOK.new nil, 200, nil
def res.body
<<~JSON
{
"Code": "Success",
"LastUpdated": "2023-05-27:05:05",
"InstanceProfileArn": "arn:aws:iam::somesecretid:instance-profile/TestRole",
"InstanceProfileId": "SOMEPROFILEID"
}
JSON
end
when "http://169.254.169.254/latest/meta-data/iam/security-credentials/TestRole"
res = Gem::Net::HTTPOK.new nil, 200, nil
def res.body = FakeS3URISigner.instance_profile
else
raise "Unexpected request to #{uri}"
end
fake_s3_request.set_response(res)
fake_s3_request
end
end
class FakeGemFetcher < Gem::RemoteFetcher
attr_reader :fetched_uri, :last_s3_uri_signer
def request(uri, request_class, last_modified = nil)
@fetched_uri = uri
res = Gem::Net::HTTPOK.new nil, 200, nil
def res.body = "success"
res
end
def s3_uri_signer(uri, method)
@last_s3_uri_signer = FakeS3URISigner.new(uri, method)
end
end
def setup
super
@ -18,43 +112,61 @@ class TestGemRemoteFetcherS3 < Gem::TestCase
@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, method="GET")
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
$fetched_uri = nil
$instance_profile = instance_profile_json
def assert_fetched_s3_with_imds_v2(expected_token)
# Three API requests:
# 1. Get the token
# 2. Lookup profile details
# 3. Query the credentials
expected = <<~TEXT
PUT http://169.254.169.254/latest/api/token
x-aws-ec2-metadata-token-ttl-seconds=60
GET http://169.254.169.254/latest/meta-data/iam/info
x-aws-ec2-metadata-token=#{expected_token}
GET http://169.254.169.254/latest/meta-data/iam/security-credentials/TestRole
x-aws-ec2-metadata-token=#{expected_token}
TEXT
recent_aws_query_logs = @fetcher.last_s3_uri_signer.recent_aws_query_logs
assert_equal(expected.strip, recent_aws_query_logs.strip)
end
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 assert_fetched_s3_with_imds_v1
# Three API requests:
# 1. Get the token (which fails)
# 2. Lookup profile details without token
# 3. Query the credentials without token
expected = <<~TEXT
PUT http://169.254.169.254/latest/api/token
x-aws-ec2-metadata-token-ttl-seconds=60
GET http://169.254.169.254/latest/meta-data/iam/info
GET http://169.254.169.254/latest/meta-data/iam/security-credentials/TestRole
TEXT
recent_aws_query_logs = @fetcher.last_s3_uri_signer.recent_aws_query_logs
assert_equal(expected.strip, recent_aws_query_logs.strip)
end
def fetcher.s3_uri_signer(uri, method)
require "json"
s3_uri_signer = Gem::S3URISigner.new(uri, method)
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
def with_imds_v2_failure
FakeS3URISigner.should_fail = true
yield(fetcher)
ensure
FakeS3URISigner.should_fail = false
end
res = fetcher.fetch_s3 Gem::URI.parse(url), nil, (method == "HEAD")
def assert_fetch_s3(url:, signature:, token: nil, region: "us-east-1", instance_profile_json: nil, fetcher: nil, method: "GET")
FakeS3URISigner.instance_profile = instance_profile_json
FakeS3URISigner.return_token = token
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=20190624T051941Z&X-Amz-Expires=86400#{token ? "&X-Amz-Security-Token=" + token : ""}&X-Amz-SignedHeaders=host&X-Amz-Signature=#{signature}", $fetched_uri.to_s
@fetcher = fetcher || FakeGemFetcher.new(nil)
res = @fetcher.fetch_s3 Gem::URI.parse(url), nil, (method == "HEAD")
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=20190624T051941Z&X-Amz-Expires=86400#{token ? "&X-Amz-Security-Token=" + token : ""}&X-Amz-SignedHeaders=host&X-Amz-Signature=#{signature}", @fetcher.fetched_uri.to_s
if method == "HEAD"
assert_equal 200, res.code
else
assert_equal "success", res
end
ensure
$fetched_uri = nil
FakeS3URISigner.instance_profile = nil
FakeS3URISigner.return_token = nil
end
def test_fetch_s3_config_creds
@ -63,7 +175,10 @@ class TestGemRemoteFetcherS3 < Gem::TestCase
}
url = "s3://my-bucket/gems/specs.4.8.gz"
Time.stub :now, Time.at(1_561_353_581) do
assert_fetch_s3 url, "b5cb80c1301f7b1c50c4af54f1f6c034f80b56d32f000a855f0a903dc5a8413c"
assert_fetch_s3(
url: url,
signature: "b5cb80c1301f7b1c50c4af54f1f6c034f80b56d32f000a855f0a903dc5a8413c",
)
end
ensure
Gem.configuration[:s3_source] = nil
@ -79,7 +194,15 @@ class TestGemRemoteFetcherS3 < Gem::TestCase
region = "us-east-1"
instance_profile_json = nil
method = "HEAD"
assert_fetch_s3 url, "a3c6cf9a2db62e85f4e57f8fc8ac8b5ff5c1fdd4aeef55935d05e05174d9c885", token, region, instance_profile_json, method
assert_fetch_s3(
url: url,
signature: "a3c6cf9a2db62e85f4e57f8fc8ac8b5ff5c1fdd4aeef55935d05e05174d9c885",
token: token,
region: region,
instance_profile_json: instance_profile_json,
method: method
)
end
ensure
Gem.configuration[:s3_source] = nil
@ -91,7 +214,11 @@ class TestGemRemoteFetcherS3 < Gem::TestCase
}
url = "s3://my-bucket/gems/specs.4.8.gz"
Time.stub :now, Time.at(1_561_353_581) do
assert_fetch_s3 url, "ef07487bfd8e3ca594f8fc29775b70c0a0636f51318f95d4f12b2e6e1fd8c716", nil, "us-west-2"
assert_fetch_s3(
url: url,
signature: "ef07487bfd8e3ca594f8fc29775b70c0a0636f51318f95d4f12b2e6e1fd8c716",
region: "us-west-2"
)
end
ensure
Gem.configuration[:s3_source] = nil
@ -103,7 +230,11 @@ class TestGemRemoteFetcherS3 < Gem::TestCase
}
url = "s3://my-bucket/gems/specs.4.8.gz"
Time.stub :now, Time.at(1_561_353_581) do
assert_fetch_s3 url, "e709338735f9077edf8f6b94b247171c266a9605975e08e4a519a123c3322625", "testtoken"
assert_fetch_s3(
url: url,
signature: "e709338735f9077edf8f6b94b247171c266a9605975e08e4a519a123c3322625",
token: "testtoken"
)
end
ensure
Gem.configuration[:s3_source] = nil
@ -118,7 +249,10 @@ class TestGemRemoteFetcherS3 < Gem::TestCase
}
url = "s3://my-bucket/gems/specs.4.8.gz"
Time.stub :now, Time.at(1_561_353_581) do
assert_fetch_s3 url, "b5cb80c1301f7b1c50c4af54f1f6c034f80b56d32f000a855f0a903dc5a8413c"
assert_fetch_s3(
url: url,
signature: "b5cb80c1301f7b1c50c4af54f1f6c034f80b56d32f000a855f0a903dc5a8413c"
)
end
ensure
ENV.each_key {|key| ENV.delete(key) if key.start_with?("AWS") }
@ -134,7 +268,12 @@ class TestGemRemoteFetcherS3 < Gem::TestCase
}
url = "s3://my-bucket/gems/specs.4.8.gz"
Time.stub :now, Time.at(1_561_353_581) do
assert_fetch_s3 url, "ef07487bfd8e3ca594f8fc29775b70c0a0636f51318f95d4f12b2e6e1fd8c716", nil, "us-west-2"
assert_fetch_s3(
url: url,
signature: "ef07487bfd8e3ca594f8fc29775b70c0a0636f51318f95d4f12b2e6e1fd8c716",
token: nil,
region: "us-west-2"
)
end
ensure
ENV.each_key {|key| ENV.delete(key) if key.start_with?("AWS") }
@ -150,7 +289,11 @@ class TestGemRemoteFetcherS3 < Gem::TestCase
}
url = "s3://my-bucket/gems/specs.4.8.gz"
Time.stub :now, Time.at(1_561_353_581) do
assert_fetch_s3 url, "e709338735f9077edf8f6b94b247171c266a9605975e08e4a519a123c3322625", "testtoken"
assert_fetch_s3(
url: url,
signature: "e709338735f9077edf8f6b94b247171c266a9605975e08e4a519a123c3322625",
token: "testtoken"
)
end
ensure
ENV.each_key {|key| ENV.delete(key) if key.start_with?("AWS") }
@ -160,7 +303,10 @@ class TestGemRemoteFetcherS3 < Gem::TestCase
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, "b5cb80c1301f7b1c50c4af54f1f6c034f80b56d32f000a855f0a903dc5a8413c"
assert_fetch_s3(
url: url,
signature: "b5cb80c1301f7b1c50c4af54f1f6c034f80b56d32f000a855f0a903dc5a8413c"
)
end
end
@ -171,8 +317,14 @@ class TestGemRemoteFetcherS3 < Gem::TestCase
url = "s3://my-bucket/gems/specs.4.8.gz"
Time.stub :now, Time.at(1_561_353_581) do
assert_fetch_s3 url, "b5cb80c1301f7b1c50c4af54f1f6c034f80b56d32f000a855f0a903dc5a8413c", nil, "us-east-1",
'{"AccessKeyId": "testuser", "SecretAccessKey": "testpass"}'
assert_fetch_s3(
url: url,
signature: "da82e098bdaed0d3087047670efc98eaadc20559a473b5eac8d70190d2a9e8fd",
region: "us-east-1",
token: "mysecrettoken",
instance_profile_json: '{"AccessKeyId": "testuser", "SecretAccessKey": "testpass", "Token": "mysecrettoken"}'
)
assert_fetched_s3_with_imds_v2("mysecrettoken")
end
ensure
Gem.configuration[:s3_source] = nil
@ -185,8 +337,14 @@ class TestGemRemoteFetcherS3 < Gem::TestCase
url = "s3://my-bucket/gems/specs.4.8.gz"
Time.stub :now, Time.at(1_561_353_581) do
assert_fetch_s3 url, "ef07487bfd8e3ca594f8fc29775b70c0a0636f51318f95d4f12b2e6e1fd8c716", nil, "us-west-2",
'{"AccessKeyId": "testuser", "SecretAccessKey": "testpass"}'
assert_fetch_s3(
url: url,
signature: "532960594dbfe31d1bbfc0e8e7a666c3cbdd8b00a143774da51b7f920704afd2",
region: "us-west-2",
token: "mysecrettoken",
instance_profile_json: '{"AccessKeyId": "testuser", "SecretAccessKey": "testpass", "Token": "mysecrettoken"}'
)
assert_fetched_s3_with_imds_v2("mysecrettoken")
end
ensure
Gem.configuration[:s3_source] = nil
@ -199,14 +357,40 @@ class TestGemRemoteFetcherS3 < Gem::TestCase
url = "s3://my-bucket/gems/specs.4.8.gz"
Time.stub :now, Time.at(1_561_353_581) do
assert_fetch_s3 url, "e709338735f9077edf8f6b94b247171c266a9605975e08e4a519a123c3322625", "testtoken", "us-east-1",
'{"AccessKeyId": "testuser", "SecretAccessKey": "testpass", "Token": "testtoken"}'
assert_fetch_s3(
url: url,
signature: "e709338735f9077edf8f6b94b247171c266a9605975e08e4a519a123c3322625",
token: "testtoken",
region: "us-east-1",
instance_profile_json: '{"AccessKeyId": "testuser", "SecretAccessKey": "testpass", "Token": "testtoken"}'
)
assert_fetched_s3_with_imds_v2("testtoken")
end
ensure
Gem.configuration[:s3_source] = nil
end
def refute_fetch_s3(url, expected_message)
def test_fetch_s3_instance_profile_creds_with_fallback
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: url,
signature: "b5cb80c1301f7b1c50c4af54f1f6c034f80b56d32f000a855f0a903dc5a8413c",
token: nil,
region: "us-east-1",
instance_profile_json: '{"AccessKeyId": "testuser", "SecretAccessKey": "testpass"}'
)
assert_fetched_s3_with_imds_v1
end
ensure
Gem.configuration[:s3_source] = nil
end
def refute_fetch_s3(url:, expected_message:)
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
@ -219,7 +403,7 @@ class TestGemRemoteFetcherS3 < Gem::TestCase
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"
refute_fetch_s3(url: url, expected_message: "no s3_source key exists in .gemrc")
end
def test_fetch_s3_no_host
@ -228,7 +412,7 @@ class TestGemRemoteFetcherS3 < Gem::TestCase
}
url = "s3://other-bucket/gems/specs.4.8.gz"
refute_fetch_s3 url, "no key for host other-bucket in s3_source in .gemrc"
refute_fetch_s3(url: url, expected_message: "no key for host other-bucket in s3_source in .gemrc")
ensure
Gem.configuration[:s3_source] = nil
end
@ -237,7 +421,7 @@ class TestGemRemoteFetcherS3 < Gem::TestCase
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"
refute_fetch_s3(url: url, expected_message: "s3_source for my-bucket missing id or secret")
ensure
Gem.configuration[:s3_source] = nil
end
@ -246,7 +430,7 @@ class TestGemRemoteFetcherS3 < Gem::TestCase
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"
refute_fetch_s3(url: url, expected_message: "s3_source for my-bucket missing id or secret")
ensure
Gem.configuration[:s3_source] = nil
end

View file

@ -70,6 +70,31 @@ class TestStringIO < Test::Unit::TestCase
assert_nil io.getc
end
def test_eof_null
io = StringIO.new(nil)
assert_predicate io, :eof?
end
def test_pread_null
io = StringIO.new(nil)
assert_raise(EOFError) { io.pread(1, 0) }
end
def test_read_null
io = StringIO.new(nil)
assert_equal "", io.read(0)
end
def test_seek_null
io = StringIO.new(nil)
assert_equal(0, io.seek(0, IO::SEEK_SET))
assert_equal(0, io.pos)
assert_equal(0, io.seek(0, IO::SEEK_CUR))
assert_equal(0, io.pos)
assert_equal(0, io.seek(0, IO::SEEK_END)) # This should not segfault
assert_equal(0, io.pos)
end
def test_truncate
io = StringIO.new("")
io.puts "abc"

View file

@ -1351,6 +1351,7 @@ rb_ractor_sched_wait(rb_execution_context_t *ec, rb_ractor_t *cr, rb_unblock_fun
}
thread_sched_lock(sched, th);
rb_ractor_unlock_self(cr);
{
// setup sleep
bool can_direct_transfer = !th_has_dedicated_nt(th);
@ -1358,16 +1359,12 @@ rb_ractor_sched_wait(rb_execution_context_t *ec, rb_ractor_t *cr, rb_unblock_fun
th->status = THREAD_STOPPED_FOREVER;
RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_SUSPENDED, th);
thread_sched_wakeup_next_thread(sched, th, can_direct_transfer);
rb_ractor_unlock_self(cr);
{
// sleep
thread_sched_wait_running_turn(sched, th, can_direct_transfer);
th->status = THREAD_RUNNABLE;
}
rb_ractor_lock_self(cr);
// sleep
thread_sched_wait_running_turn(sched, th, can_direct_transfer);
th->status = THREAD_RUNNABLE;
}
thread_sched_unlock(sched, th);
rb_ractor_lock_self(cr);
ubf_clear(th);

32
time.c
View file

@ -1888,39 +1888,25 @@ force_make_tm(VALUE time, struct time_object *tobj)
}
static void
time_mark(void *ptr)
time_mark_and_move(void *ptr)
{
struct time_object *tobj = ptr;
if (!FIXWV_P(tobj->timew)) {
rb_gc_mark_movable(w2v(tobj->timew));
if (!WIDEVALUE_IS_WIDER || !FIXWV_P(tobj->timew)) {
rb_gc_mark_and_move((VALUE *)&WIDEVAL_GET(tobj->timew));
}
rb_gc_mark_movable(tobj->vtm.year);
rb_gc_mark_movable(tobj->vtm.subsecx);
rb_gc_mark_movable(tobj->vtm.utc_offset);
rb_gc_mark_movable(tobj->vtm.zone);
}
static void
time_compact(void *ptr)
{
struct time_object *tobj = ptr;
if (!FIXWV_P(tobj->timew)) {
WIDEVAL_GET(tobj->timew) = WIDEVAL_WRAP(rb_gc_location(w2v(tobj->timew)));
}
tobj->vtm.year = rb_gc_location(tobj->vtm.year);
tobj->vtm.subsecx = rb_gc_location(tobj->vtm.subsecx);
tobj->vtm.utc_offset = rb_gc_location(tobj->vtm.utc_offset);
tobj->vtm.zone = rb_gc_location(tobj->vtm.zone);
rb_gc_mark_and_move(&tobj->vtm.year);
rb_gc_mark_and_move(&tobj->vtm.subsecx);
rb_gc_mark_and_move(&tobj->vtm.utc_offset);
rb_gc_mark_and_move(&tobj->vtm.zone);
}
static const rb_data_type_t time_data_type = {
.wrap_struct_name = "time",
.function = {
.dmark = time_mark,
.dmark = time_mark_and_move,
.dfree = RUBY_TYPED_DEFAULT_FREE,
.dsize = NULL,
.dcompact = time_compact,
.dcompact = time_mark_and_move,
},
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE,
};

View file

@ -1,4 +1,4 @@
#!ruby -an
#!ruby -alnF\s+|#.*
BEGIN {
require 'fileutils'
require_relative 'lib/colorize'
@ -21,7 +21,6 @@ BEGIN {
n, v, u, r = $F
next unless n
next if n =~ /^#/
next if bundled_gems&.all? {|pat| !File.fnmatch?(pat, n)}
unless File.exist?("#{n}/.git")

View file

@ -690,17 +690,15 @@ eom
assert_warning(*args) {$VERBOSE = false; yield}
end
def assert_deprecated_warning(mesg = /deprecated/)
def assert_deprecated_warning(mesg = /deprecated/, &block)
assert_warning(mesg) do
Warning[:deprecated] = true if Warning.respond_to?(:[]=)
yield
EnvUtil.deprecation_warning(&block)
end
end
def assert_deprecated_warn(mesg = /deprecated/)
def assert_deprecated_warn(mesg = /deprecated/, &block)
assert_warn(mesg) do
Warning[:deprecated] = true if Warning.respond_to?(:[]=)
yield
EnvUtil.deprecation_warning(&block)
end
end

View file

@ -297,6 +297,21 @@ module EnvUtil
end
module_function :verbose_warning
if defined?(Warning.[]=)
def deprecation_warning
previous_deprecated = Warning[:deprecated]
Warning[:deprecated] = true
yield
ensure
Warning[:deprecated] = previous_deprecated
end
else
def deprecation_warning
yield
end
end
module_function :deprecation_warning
def default_warning
$VERBOSE = false
yield

Some files were not shown because too many files have changed in this diff Show more