Merge branch 'master' into refinement-symbol-proc

This commit is contained in:
Alan Wu 2025-07-17 10:17:19 -04:00 committed by GitHub
commit 0ce874b329
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
1109 changed files with 30618 additions and 20653 deletions

View file

@ -22,6 +22,7 @@ marshal.rb
numeric.rb
nilclass.rb
pack.rb
pathname_builtin.rb
ractor.rb
string.rb
symbol.rb

View file

@ -185,8 +185,8 @@ define rp
print (struct RBasic *)($arg0)
else
if ($flags & RUBY_T_MASK) == RUBY_T_DATA
if ((struct RTypedData *)($arg0))->typed_flag == 1
printf "%sT_DATA%s(%s): ", $color_type, $color_end, ((struct RTypedData *)($arg0))->type->wrap_struct_name
if ((struct RTypedData *)($arg0))->type & 1
printf "%sT_DATA%s(%s): ", $color_type, $color_end, ((const rb_data_type_t *)(((struct RTypedData *)($arg0))->type & ~1))->wrap_struct_name
print (struct RTypedData *)($arg0)
else
printf "%sT_DATA%s: ", $color_type, $color_end

View file

@ -34,3 +34,5 @@ d2c5867357ed88eccc28c2b3bd4a46e206e7ff85
# Miss-and-revived commits
a0f7de814ae5c299d6ce99bed5fb308a05d50ba0
d4e24021d39e1f80f0055b55d91f8d5f22e15084
7a56c316418980b8a41fcbdc94067b2bda2ad112
e90282be7ba1bc8e3119f6e1a2c80356ceb3f80a

View file

@ -74,81 +74,6 @@ btests=''
tests=''
spec_opts=''
# Launchable
setup_launchable() {
pushd ${srcdir}
# To prevent a slowdown in CI, disable request retries when the Launchable server is unstable.
export LAUNCHABLE_SKIP_TIMEOUT_RETRY=1
# Launchable creates .launchable file in the current directory, but cannot a file to ${srcdir} directory.
# As a workaround, we set LAUNCHABLE_SESSION_DIR to ${builddir}.
export LAUNCHABLE_SESSION_DIR=${builddir}
local github_ref="${GITHUB_REF//\//_}"
local build_name="${github_ref}"_"${GITHUB_PR_HEAD_SHA}"
btest_report_path='launchable_bootstraptest.json'
test_report_path='launchable_test_all.json'
test_spec_report_path='launchable_test_spec_report'
test_all_session_file='launchable_test_all_session.txt'
btest_session_file='launchable_btest_session.txt'
test_spec_session_file='launchable_test_spec_session.txt'
btests+=--launchable-test-reports="${btest_report_path}"
echo "::group::Setup Launchable"
launchable record build --name "${build_name}" || true
launchable record session \
--build "${build_name}" \
--flavor test_task=test \
--flavor workflow=Compilations \
--flavor with-gcc="${INPUT_WITH_GCC}" \
--flavor CFLAGS="${INPUT_CFLAGS}" \
--flavor CXXFLAGS="${INPUT_CXXFLAGS}" \
--flavor optflags="${INPUT_OPTFLAGS}" \
--flavor cppflags="${INPUT_CPPFLAGS}" \
--test-suite btest \
> "${builddir}"/${btest_session_file} \
|| true
if [ "$INPUT_CHECK" = "true" ]; then
tests+=--launchable-test-reports="${test_report_path}"
launchable record session \
--build "${build_name}" \
--flavor test_task=test-all \
--flavor workflow=Compilations \
--flavor with-gcc="${INPUT_WITH_GCC}" \
--flavor CFLAGS="${INPUT_CFLAGS}" \
--flavor CXXFLAGS="${INPUT_CXXFLAGS}" \
--flavor optflags="${INPUT_OPTFLAGS}" \
--flavor cppflags="${INPUT_CPPFLAGS}" \
--test-suite test-all \
> "${builddir}"/${test_all_session_file} \
|| true
mkdir "${builddir}"/"${test_spec_report_path}"
spec_opts+=--launchable-test-reports="${test_spec_report_path}"
launchable record session \
--build "${build_name}" \
--flavor test_task=test-spec \
--flavor workflow=Compilations \
--flavor with-gcc="${INPUT_WITH_GCC}" \
--flavor CFLAGS="${INPUT_CFLAGS}" \
--flavor CXXFLAGS="${INPUT_CXXFLAGS}" \
--flavor optflags="${INPUT_OPTFLAGS}" \
--flavor cppflags="${INPUT_CPPFLAGS}" \
--test-suite test-spec \
> "${builddir}"/${test_spec_session_file} \
|| true
fi
echo "::endgroup::"
trap launchable_record_test EXIT
}
launchable_record_test() {
pushd "${builddir}"
grouped launchable record tests --session "$(cat "${btest_session_file}")" raw "${btest_report_path}" || true
if [ "$INPUT_CHECK" = "true" ]; then
grouped launchable record tests --session "$(cat "${test_all_session_file}")" raw "${test_report_path}" || true
grouped launchable record tests --session "$(cat "${test_spec_session_file}")" raw "${test_spec_report_path}"/* || true
fi
}
if [ "$LAUNCHABLE_ENABLED" = "true" ]; then
setup_launchable
fi
pushd ${builddir}
grouped make showflags

View file

@ -55,6 +55,17 @@ inputs:
description: >-
Whether this workflow is executed on YJIT.
outputs:
stdout_report_path:
value: ${{ steps.global.outputs.stdout_report_path }}
description: >-
Report file path for standard output.
stderr_report_path:
value: ${{ steps.global.outputs.stderr_report_path }}
description: >-
Report file path for standard error.
runs:
using: composite
@ -100,12 +111,11 @@ runs:
echo test_all_enabled="${test_all_enabled}" >> $GITHUB_OUTPUT
echo btest_enabled="${btest_enabled}" >> $GITHUB_OUTPUT
echo test_spec_enabled="${test_spec_enabled}" >> $GITHUB_OUTPUT
echo test_all_session_file='launchable_test_all_session.txt' >> $GITHUB_OUTPUT
echo btest_session_file='launchable_btest_session.txt' >> $GITHUB_OUTPUT
echo test_spec_session_file='launchable_test_spec_session.txt' >> $GITHUB_OUTPUT
echo test_all_report_file='launchable_test_all_report.json' >> $GITHUB_OUTPUT
echo btest_report_file='launchable_btest_report.json' >> $GITHUB_OUTPUT
echo test_spec_report_dir='launchable_test_spec_report' >> $GITHUB_OUTPUT
echo stdout_report_path="launchable_stdout.log" >> $GITHUB_OUTPUT
echo stderr_report_path="launchable_stderr.log" >> $GITHUB_OUTPUT
if: steps.enable-launchable.outputs.enable-launchable
- name: Set environment variables for Launchable
@ -123,6 +133,7 @@ runs:
echo "LAUNCHABLE_TOKEN=${{ inputs.launchable-token }}" >> $GITHUB_ENV
: # To prevent a slowdown in CI, disable request retries when the Launchable server is unstable.
echo "LAUNCHABLE_SKIP_TIMEOUT_RETRY=1" >> $GITHUB_ENV
echo "LAUNCHABLE_COMMIT_TIMEOUT=1" >> $GITHUB_ENV
if: steps.enable-launchable.outputs.enable-launchable
- name: Set up path
@ -134,6 +145,7 @@ runs:
if: steps.enable-launchable.outputs.enable-launchable && startsWith(inputs.os, 'macos')
- name: Set up Launchable
id: setup-launchable
shell: bash
working-directory: ${{ inputs.srcdir }}
run: |
@ -156,110 +168,72 @@ runs:
btest_test_suite="yjit-${btest_test_suite}"
test_spec_test_suite="yjit-${test_spec_test_suite}"
fi
# launchable_setup target var -- refers ${target} prefixed variables
launchable_setup() {
local target=$1 session
eval [ "\${${target}_enabled}" = "true" ] || return
eval local suite=\${${target}_test_suite}
session=$(launchable record session \
--build "${build_name}" \
--observation \
--flavor os="${{ inputs.os }}" \
--flavor test_task="${{ inputs.test-task }}" \
--flavor test_opts="${test_opts}" \
--flavor workflow="${{ github.workflow }}" \
--test-suite ${suite} \
)
launchable subset \
--get-tests-from-previous-sessions \
--non-blocking \
--target 90% \
--session "${session}" \
raw > /dev/null
echo "${target}_session=${session}" >> $GITHUB_OUTPUT
}
launchable record build --name "${build_name}"
if [ "${test_all_enabled}" = "true" ]; then
launchable record session \
--build "${build_name}" \
--observation \
--flavor os="${{ inputs.os }}" \
--flavor test_task="${{ inputs.test-task }}" \
--flavor test_opts="${test_opts}" \
--flavor workflow="${{ github.workflow }}" \
--test-suite ${test_all_test_suite} \
> "${test_all_session_file}"
launchable subset \
--get-tests-from-previous-sessions \
--non-blocking \
--target 90% \
--session "$(cat "${test_all_session_file}")" \
raw > /dev/null
echo "TESTS=${TESTS} --launchable-test-reports=${test_all_report_file}" >> $GITHUB_ENV
if launchable_setup test_all; then
echo "TESTS=${TESTS:+$TESTS }--launchable-test-reports=${test_all_report_file}" >> $GITHUB_ENV
fi
if [ "${btest_enabled}" = "true" ]; then
launchable record session \
--build "${build_name}" \
--observation \
--flavor os="${{ inputs.os }}" \
--flavor test_task="${{ inputs.test-task }}" \
--flavor test_opts="${test_opts}" \
--flavor workflow="${{ github.workflow }}" \
--test-suite ${btest_test_suite} \
> "${btest_session_file}"
launchable subset \
--get-tests-from-previous-sessions \
--non-blocking \
--target 90% \
--session "$(cat "${btest_session_file}")" \
raw > /dev/null
echo "BTESTS=${BTESTS} --launchable-test-reports=${btest_report_file}" >> $GITHUB_ENV
if launchable_setup btest; then
echo "BTESTS=${BTESTS:+$BTESTS }--launchable-test-reports=${btest_report_file}" >> $GITHUB_ENV
fi
if [ "${test_spec_enabled}" = "true" ]; then
launchable record session \
--build "${build_name}" \
--observation \
--flavor os="${{ inputs.os }}" \
--flavor test_task="${{ inputs.test-task }}" \
--flavor test_opts="${test_opts}" \
--flavor workflow="${{ github.workflow }}" \
--test-suite ${test_spec_test_suite} \
> "${test_spec_session_file}"
launchable subset \
--get-tests-from-previous-sessions \
--non-blocking \
--target 90% \
--session "$(cat "${test_spec_session_file}")" \
raw > /dev/null
echo "SPECOPTS=${SPECOPTS} --launchable-test-reports=${test_spec_report_dir}" >> $GITHUB_ENV
if launchable_setup test_spec; then
echo "SPECOPTS=${SPECOPTS:$SPECOPTS }--launchable-test-reports=${test_spec_report_dir}" >> $GITHUB_ENV
echo test_spec_enabled=true >> $GITHUB_OUTPUT
fi
echo launchable_setup_dir=$(pwd) >> $GITHUB_OUTPUT
if: steps.enable-launchable.outputs.enable-launchable
env:
test_all_enabled: ${{ steps.global.outputs.test_all_enabled }}
btest_enabled: ${{ steps.global.outputs.btest_enabled }}
test_spec_enabled: ${{ steps.global.outputs.test_spec_enabled }}
test_all_session_file: ${{ steps.global.outputs.test_all_session_file }}
btest_session_file: ${{ steps.global.outputs.btest_session_file }}
test_spec_session_file: ${{ steps.global.outputs.test_spec_session_file }}
test_all_report_file: ${{ steps.global.outputs.test_all_report_file }}
btest_report_file: ${{ steps.global.outputs.btest_report_file }}
test_spec_report_dir: ${{ steps.global.outputs.test_spec_report_dir }}
- name: Variables to report Launchable
id: variables
- name: make test-spec report directory in build directory
shell: bash
working-directory: ${{ inputs.srcdir }}
run: |
set -x
: # report-path from srcdir
if [ "${srcdir}" = "${{ github.workspace }}" ]; then
dir=
else
# srcdir must be equal to or under workspace
dir=$(echo ${srcdir:+${srcdir}/} | sed 's:[^/][^/]*/:../:g')
fi
if [ "${test_all_enabled}" = "true" ]; then
test_report_path="${dir}${builddir:+${builddir}/}${test_all_report_file}"
echo test_report_path="${test_report_path}" >> $GITHUB_OUTPUT
fi
if [ "${btest_enabled}" = "true" ]; then
btest_report_path="${dir}${builddir:+${builddir}/}${btest_report_file}"
echo btest_report_path="${btest_report_path}" >> $GITHUB_OUTPUT
fi
if [ "${test_spec_enabled}" = "true" ]; then
test_spec_report_path="${dir}${builddir:+${builddir}/}${test_spec_report_dir}"
mkdir "${test_spec_report_path}"
echo test_spec_report_path="${test_spec_report_path}" >> $GITHUB_OUTPUT
fi
stdout_report_path="${dir}${builddir:+${builddir}/}launchable_stdout.log"
stderr_report_path="${dir}${builddir:+${builddir}/}launchable_stderr.log"
echo stdout_report_path="${stdout_report_path}" >> $GITHUB_OUTPUT
echo stderr_report_path="${stderr_report_path}" >> $GITHUB_OUTPUT
if: steps.enable-launchable.outputs.enable-launchable
working-directory: ${{ inputs.builddir }}
run: mkdir "${test_spec_report_dir}"
if: ${{ steps.setup-launchable.outputs.test_spec_enabled == 'true' }}
env:
test_spec_report_dir: ${{ steps.global.outputs.test_spec_report_dir }}
- name: Clean up test results in Launchable
uses: gacts/run-and-post-run@674528335da98a7afc80915ff2b4b860a0b3553a # v1.4.0
with:
shell: bash
working-directory: ${{ inputs.builddir }}
post: |
rm -f "${test_all_report_file}"
rm -f "${btest_report_file}"
rm -fr "${test_spec_report_dir}"
rm -f launchable_stdout.log
rm -f launchable_stderr.log
if: always() && steps.setup-launchable.outcome == 'success'
env:
srcdir: ${{ inputs.srcdir }}
builddir: ${{ inputs.builddir }}
test_all_enabled: ${{ steps.global.outputs.test_all_enabled }}
btest_enabled: ${{ steps.global.outputs.btest_enabled }}
test_spec_enabled: ${{ steps.global.outputs.test_spec_enabled }}
test_all_report_file: ${{ steps.global.outputs.test_all_report_file }}
btest_report_file: ${{ steps.global.outputs.btest_report_file }}
test_spec_report_dir: ${{ steps.global.outputs.test_spec_report_dir }}
@ -268,56 +242,48 @@ runs:
uses: gacts/run-and-post-run@674528335da98a7afc80915ff2b4b860a0b3553a # v1.4.0
with:
shell: bash
working-directory: ${{ inputs.srcdir }}
working-directory: ${{ inputs.builddir }}
post: |
if [[ "${test_all_enabled}" = "true" ]]; then \
launchable record attachment \
--session "$(cat "${test_all_session_file}")" \
--session "${test_all_session}" \
"${stdout_report_path}" \
"${stderr_report_path}"; \
launchable record tests \
--session "$(cat "${test_all_session_file}")" \
raw "${test_report_path}" || true; \
--session "${test_all_session}" \
raw "${test_all_report_file}" || true; \
fi
if [[ "${btest_enabled}" = "true" ]]; then \
launchable record attachment \
--session "$(cat "${btest_session_file}")" \
--session "${btest_session}" \
"${stdout_report_path}" \
"${stderr_report_path}"; \
launchable record tests \
--session "$(cat "${btest_session_file}")" \
raw "${btest_report_path}" || true; \
--session "${btest_session}" \
raw "${btest_report_file}" || true; \
fi
if [[ "${test_spec_enabled}" = "true" ]]; then \
launchable record attachment \
--session "$(cat "${test_spec_session_file}")" \
--session "${test_spec_session}" \
"${stdout_report_path}" \
"${stderr_report_path}"; \
launchable record tests \
--session "$(cat "${test_spec_session_file}")" \
raw ${test_spec_report_path}/* || true; \
--session "${test_spec_session}" \
raw ${test_spec_report_dir}/* || true; \
fi
rm -f "${test_all_session_file}"
rm -f "${btest_session_file}"
rm -f "${test_spec_session_file}"
rm -f "${test_report_path}"
rm -f "${btest_report_path}"
rm -fr "${test_spec_report_path}"
rm -f "${stdout_report_path}"
rm -f "${stderr_report_path}"
if: ${{ always() && steps.enable-launchable.outputs.enable-launchable }}
if: ${{ always() && steps.setup-launchable.outcome == 'success' }}
env:
test_report_path: ${{ steps.variables.outputs.test_report_path }}
btest_report_path: ${{ steps.variables.outputs.btest_report_path }}
test_spec_report_path: ${{ steps.variables.outputs.test_spec_report_path }}
test_all_report_file: ${{ steps.global.outputs.test_all_report_file }}
btest_report_file: ${{ steps.global.outputs.btest_report_file }}
test_spec_report_dir: ${{ steps.global.outputs.test_spec_report_dir }}
test_all_enabled: ${{ steps.global.outputs.test_all_enabled }}
btest_enabled: ${{ steps.global.outputs.btest_enabled }}
test_spec_enabled: ${{ steps.global.outputs.test_spec_enabled }}
test_all_session_file: ${{ steps.global.outputs.test_all_session_file }}
btest_session_file: ${{ steps.global.outputs.btest_session_file }}
test_spec_session_file: ${{ steps.global.outputs.test_spec_session_file }}
stdout_report_path: ${{ steps.variables.outputs.stdout_report_path }}
stderr_report_path: ${{ steps.variables.outputs.stderr_report_path }}
test_all_session: ${{ steps.setup-launchable.outputs.test_all_session }}
btest_session: ${{ steps.setup-launchable.outputs.btest_session }}
test_spec_session: ${{ steps.setup-launchable.outputs.test_spec_session }}
stdout_report_path: ${{ steps.global.outputs.stdout_report_path }}
stderr_report_path: ${{ steps.global.outputs.stderr_report_path }}
LAUNCHABLE_SETUP_DIR: ${{ steps.setup-launchable.outputs.launchable_setup_dir }}

View file

@ -76,7 +76,7 @@ runs:
shell: bash
run: |
echo "git=`command -v git`" >> "$GITHUB_OUTPUT"
echo "sudo=`command -v sudo`" >> "$GITHUB_OUTPUT"
echo "sudo=`sudo true && command -v sudo`" >> "$GITHUB_OUTPUT"
echo "autoreconf=`command -v autoreconf`" >> "$GITHUB_OUTPUT"
- if: steps.which.outputs.git
@ -183,3 +183,5 @@ runs:
${{ steps.clean.outputs.distclean }}
${{ steps.clean.outputs.remained-files }}
${{ steps.clean.outputs.final }}
# rmdir randomly fails due to launchable files
continue-on-error: true

View file

@ -21,7 +21,7 @@ runs:
dir_config() {
local args=() lib var="$1"; shift
for lib in "$@"; do
args+="--with-${lib%@*}-dir=$(brew --prefix $lib)"
args+=("--with-${lib%@*}-dir=$(brew --prefix $lib)")
done
echo "$var=${args[*]}" >> $GITHUB_ENV
}

View file

@ -10,6 +10,8 @@ files:
'zjit/src/cruby_bindings.inc.rs': []
'doc/zjit*': [team:jit]
'test/ruby/test_zjit*': [team:jit]
'test/.excludes-zjit/*': [team:jit]
'defs/jit.mk': [team:jit]
options:
ignore_draft: true
# This currently doesn't work as intended. We want to skip reviews when only

View file

@ -74,7 +74,7 @@ jobs:
builddir: build
makeup: true
- uses: ruby/setup-ruby@d8d83c3960843afb664e821fed6be52f37da5267 # v1.231.0
- uses: ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651 # v1.245.0
with:
ruby-version: '3.1'
bundler: none

View file

@ -17,4 +17,4 @@ jobs:
uses: necojackarc/auto-request-review@e89da1a8cd7c8c16d9de9c6e763290b6b0e3d424 # v0.13.0
with:
# scope: public_repo
token: ${{ secrets.MATZBOT_GITHUB_TOKEN }}
token: ${{ secrets.MATZBOT_AUTO_REQUEST_REVIEW_TOKEN }}

View file

@ -50,7 +50,7 @@ jobs:
- ruby-3.3
steps:
- uses: ruby/setup-ruby@d8d83c3960843afb664e821fed6be52f37da5267 # v1.231.0
- uses: ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651 # v1.245.0
with:
ruby-version: ${{ matrix.ruby }}
bundler: none

View file

@ -33,11 +33,11 @@ jobs:
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
token: ${{ (github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull')) && secrets.MATZBOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
token: ${{ (github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull')) && secrets.MATZBOT_AUTO_UPDATE_TOKEN || secrets.GITHUB_TOKEN }}
- uses: ./.github/actions/setup/directories
with:
# Skip overwriting MATZBOT_GITHUB_TOKEN
# Skip overwriting MATZBOT_AUTO_UPDATE_TOKEN
checkout: '' # false (ref: https://github.com/actions/runner/issues/2238)
- name: Set ENV
@ -104,7 +104,7 @@ jobs:
timeout-minutes: 30
env:
RUBY_TESTOPTS: '-q --tty=no'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: 'typeprof,rbs,repl_type_completor'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: ''
if: ${{ steps.diff.outputs.gems }}
- name: Commit
@ -112,7 +112,7 @@ jobs:
git pull --ff-only origin ${GITHUB_REF#refs/heads/}
message="Update bundled gems list"
if [ -z "${gems}" ]; then
git commit --message="${message} at ${GITHUB_SHA:0:30} [ci skip]"
git commit --message="[DOC] ${message} at ${GITHUB_SHA:0:30}"
else
git commit --message="${message} as of ${TODAY}"
fi

View file

@ -40,7 +40,7 @@ jobs:
- uses: ./.github/actions/setup/directories
- uses: ruby/setup-ruby@d8d83c3960843afb664e821fed6be52f37da5267 # v1.231.0
- uses: ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651 # v1.245.0
with:
ruby-version: '3.1'
bundler: none

View file

@ -20,12 +20,12 @@ jobs:
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
token: ${{ (github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull')) && secrets.MATZBOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
token: ${{ (github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull')) && secrets.MATZBOT_AUTO_UPDATE_TOKEN || secrets.GITHUB_TOKEN }}
- uses: ./.github/actions/setup/directories
with:
makeup: true
# Skip overwriting MATZBOT_GITHUB_TOKEN
# Skip overwriting MATZBOT_AUTO_UPDATE_TOKEN
checkout: '' # false (ref: https://github.com/actions/runner/issues/2238)
# Run this step first to make sure auto-style commits are pushed
@ -37,10 +37,10 @@ jobs:
EMAIL: svn-admin@ruby-lang.org
GIT_AUTHOR_NAME: git
GIT_COMMITTER_NAME: git
GITHUB_OLD_SHA: ${{ startsWith(github.event_name, 'pull') && github.event.pull_request.base.sha || github.event.before }}
GITHUB_NEW_SHA: ${{ startsWith(github.event_name, 'pull') && github.event.pull_request.merge_commit_sha || github.event.after }}
GITHUB_OLD_SHA: ${{ github.event.pull_request.base.sha }}
GITHUB_NEW_SHA: ${{ github.event.pull_request.merge_commit_sha }}
PUSH_REF: ${{ github.ref == 'refs/heads/master' && github.ref || '' }}
if: ${{ github.repository == 'ruby/ruby' }}
if: ${{ github.repository == 'ruby/ruby' && startsWith(github.event_name, 'pull') }}
- name: Check if C-sources are US-ASCII
run: |
@ -64,7 +64,7 @@ jobs:
- name: Generate docs
id: docs
run: |
ruby -W0 --disable-gems -I./lib tool/rdoc-srcdir -q --op html .
ruby -W0 --disable-gems tool/rdoc-srcdir -q --op html .
echo htmlout=ruby-html-${GITHUB_SHA:0:10} >> $GITHUB_OUTPUT
# Generate only when document commit/PR
if: >-

View file

@ -80,6 +80,7 @@ jobs:
uses: github/codeql-action/init@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9
with:
languages: ${{ matrix.language }}
trap-caching: false
debug: true
- name: Autobuild
@ -120,9 +121,3 @@ jobs:
with:
sarif_file: sarif-results/${{ matrix.language }}.sarif
continue-on-error: true
- name: Purge the oldest TRAP cache
if: ${{ github.repository == 'ruby/ruby' && matrix.language == 'cpp'}}
run: gh cache list --key codeql --order asc --limit 1 --json key --jq '.[].key' | xargs -I{} gh cache delete {}
env:
GH_TOKEN: ${{ secrets.MATZBOT_GITHUB_WORKFLOW_TOKEN }}

View file

@ -78,11 +78,11 @@ 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 } }
- name: 'GCC 13 LTO'
- name: 'GCC 15 LTO'
uses: './.github/actions/compilers'
with:
tag: gcc-13
with_gcc: 'gcc-13 -flto=auto -ffat-lto-objects -Werror=lto-type-mismatch'
tag: gcc-15
with_gcc: 'gcc-15 -flto=auto -ffat-lto-objects -Werror=lto-type-mismatch'
optflags: '-O2'
enable_shared: false
- { uses: './.github/actions/compilers', name: 'ext/Setup', with: { static_exts: 'etc json/* */escape' } }
@ -299,6 +299,8 @@ jobs:
- { uses: './.github/actions/compilers', name: 'VM_DEBUG_BP_CHECK', with: { cppflags: '-DVM_DEBUG_BP_CHECK' } }
- { uses: './.github/actions/compilers', name: 'VM_DEBUG_VERIFY_METHOD_CACHE', with: { cppflags: '-DVM_DEBUG_VERIFY_METHOD_CACHE' } }
- { uses: './.github/actions/compilers', name: 'enable-yjit', with: { append_configure: '--enable-yjit' } }
- { uses: './.github/actions/compilers', name: 'enable-{y,z}jit', with: { append_configure: '--enable-yjit --enable-zjit' } }
- { uses: './.github/actions/compilers', name: 'enable-{y,z}jit=dev', with: { append_configure: '--enable-yjit=dev --enable-zjit' } }
- { uses: './.github/actions/compilers', name: 'YJIT_FORCE_ENABLE', with: { cppflags: '-DYJIT_FORCE_ENABLE' } }
- { uses: './.github/actions/compilers', name: 'UNIVERSAL_PARSER', with: { cppflags: '-DUNIVERSAL_PARSER' } }

View file

@ -22,18 +22,19 @@ jobs:
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
token: ${{ (github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull')) && secrets.MATZBOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- uses: ./.github/actions/setup/directories
with:
makeup: true
# Skip overwriting MATZBOT_GITHUB_TOKEN
checkout: '' # false (ref: https://github.com/actions/runner/issues/2238)
token: ${{ (github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull')) && secrets.MATZBOT_AUTO_UPDATE_TOKEN || secrets.GITHUB_TOKEN }}
- id: gems
run: true
if: ${{ github.ref == 'refs/heads/master' }}
- uses: ./.github/actions/setup/directories
with:
makeup: true
# Skip overwriting MATZBOT_AUTO_UPDATE_TOKEN
checkout: '' # false (ref: https://github.com/actions/runner/issues/2238)
if: ${{ steps.gems.outcome == 'success' }}
- name: Download previous gems list
run: |
data=default_gems.json

View file

@ -13,13 +13,13 @@ jobs:
if: github.event.pull_request.user.login == 'dependabot[bot]' && github.repository == 'ruby/ruby'
steps:
- name: Dependabot metadata
uses: dependabot/fetch-metadata@d7267f607e9d3fb96fc2fbe83e0af444713e90b7 # v2.3.0
uses: dependabot/fetch-metadata@08eff52bf64351f401fb50d4972fa95b9f2c2d1b # v2.4.0
id: metadata
- name: Wait for status checks
uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc # v1.3.4
with:
repo-token: ${{ secrets.MATZBOT_GITHUB_TOKEN }}
repo-token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ github.event.pull_request.head.sha || github.sha }}
check-regexp: 'make \(check, .*\)'
wait-interval: 30
@ -29,4 +29,4 @@ jobs:
run: gh pr merge --auto --rebase "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GITHUB_TOKEN: ${{ secrets.MATZBOT_GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.MATZBOT_DEPENDABOT_MERGE_TOKEN }}

View file

@ -107,6 +107,10 @@ jobs:
- run: make hello
- name: runirb
run: |
echo IRB::VERSION | make runirb RUNOPT="-- -f"
- name: Set test options for skipped tests
run: |
set -x
@ -115,6 +119,7 @@ jobs:
if: ${{ matrix.test_task == 'check' && matrix.skipped_tests }}
- name: Set up Launchable
id: launchable
uses: ./.github/actions/launchable/setup
with:
os: ${{ matrix.os }}
@ -123,6 +128,7 @@ jobs:
builddir: build
srcdir: src
continue-on-error: true
timeout-minutes: 3
- name: Set extra test options
run: |
@ -132,19 +138,18 @@ jobs:
- name: make ${{ matrix.test_task }}
run: |
if [ -n "${LAUNCHABLE_ORGANIZATION}" ]; then
exec \
> >(tee launchable_stdout.log) \
2> >(tee launchable_stderr.log)
fi
test -n "${LAUNCHABLE_STDOUT}" && exec 1> >(tee "${LAUNCHABLE_STDOUT}")
test -n "${LAUNCHABLE_STDERR}" && exec 2> >(tee "${LAUNCHABLE_STDERR}")
ulimit -c unlimited
make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"}
timeout-minutes: 60
env:
RUBY_TESTOPTS: '-q --tty=no'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: 'typeprof,rbs,repl_type_completor'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: ''
PRECHECK_BUNDLED_GEMS: 'no'
LAUNCHABLE_STDOUT: ${{ steps.launchable.outputs.stdout_report_path }}
LAUNCHABLE_STDERR: ${{ steps.launchable.outputs.stderr_report_path }}
- name: make skipped tests
run: |

View file

@ -127,6 +127,7 @@ jobs:
srcdir: src
test-tasks: '["test", "test-all", "test-spec"]'
continue-on-error: true
timeout-minutes: 3
- name: test
timeout-minutes: 30

View file

@ -26,8 +26,8 @@ jobs:
matrix:
gc:
- name: default
# - name: mmtk
# mmtk_build: release
- name: mmtk
mmtk_build: release
os: [macos-latest, ubuntu-latest]
include:
- test_task: check
@ -63,7 +63,7 @@ jobs:
uses: ./.github/actions/setup/ubuntu
if: ${{ contains(matrix.os, 'ubuntu') }}
- uses: ruby/setup-ruby@d8d83c3960843afb664e821fed6be52f37da5267 # v1.231.0
- uses: ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651 # v1.245.0
with:
ruby-version: '3.1'
bundler: none
@ -131,6 +131,7 @@ jobs:
if: ${{ matrix.test_task == 'check' && matrix.skipped_tests }}
- name: Set up Launchable
id: launchable
uses: ./.github/actions/launchable/setup
with:
os: ${{ matrix.os || 'ubuntu-22.04' }}
@ -139,14 +140,12 @@ jobs:
builddir: build
srcdir: src
continue-on-error: true
timeout-minutes: 3
- name: make ${{ matrix.test_task }}
run: |
if [ -n "${LAUNCHABLE_ORGANIZATION}" ]; then
exec \
> >(tee launchable_stdout.log) \
2> >(tee launchable_stderr.log)
fi
test -n "${LAUNCHABLE_STDOUT}" && exec 1> >(tee "${LAUNCHABLE_STDOUT}")
test -n "${LAUNCHABLE_STDERR}" && exec 2> >(tee "${LAUNCHABLE_STDERR}")
$SETARCH make -s ${{ matrix.test_task }} \
${TESTS:+TESTS="$TESTS"} \
@ -154,8 +153,10 @@ jobs:
timeout-minutes: ${{ matrix.gc.timeout || 40 }}
env:
RUBY_TESTOPTS: '-q --tty=no'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: 'typeprof,rbs,repl_type_completor'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: ''
PRECHECK_BUNDLED_GEMS: 'no'
LAUNCHABLE_STDOUT: ${{ steps.launchable.outputs.stdout_report_path }}
LAUNCHABLE_STDERR: ${{ steps.launchable.outputs.stderr_report_path }}
- name: make skipped tests
run: |

View file

@ -60,7 +60,7 @@ jobs:
- uses: ./.github/actions/setup/ubuntu
- uses: ruby/setup-ruby@d8d83c3960843afb664e821fed6be52f37da5267 # v1.231.0
- uses: ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651 # v1.245.0
with:
ruby-version: '3.1'
bundler: none
@ -87,7 +87,7 @@ jobs:
EXCLUDES: '../src/test/.excludes-parsey'
RUN_OPTS: ${{ matrix.run_opts || '--parser=parse.y' }}
SPECOPTS: ${{ matrix.specopts || '-T --parser=parse.y' }}
TEST_BUNDLED_GEMS_ALLOW_FAILURES: 'typeprof,rbs,repl_type_completor'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: ''
- uses: ./.github/actions/slack
with:

View file

@ -55,7 +55,7 @@ jobs:
echo $PREVIOUS_RELEASE_TAG
tool/gen-github-release.rb $PREVIOUS_RELEASE_TAG $RELEASE_TAG --no-dry-run
env:
GITHUB_TOKEN: ${{ secrets.MATZBOT_GITHUB_WORKFLOW_TOKEN }}
GITHUB_TOKEN: ${{ secrets.MATZBOT_AUTO_UPDATE_TOKEN }}
- name: Update versions index
run: |

View file

@ -2,7 +2,7 @@
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.
name: Scorecards supply-chain security
name: Scorecard supply-chain security
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
@ -10,7 +10,7 @@ on:
# To guarantee Maintained check is occasionally updated. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
schedule:
- cron: '22 4 * * 2'
- cron: '39 3 * * 5'
# push:
# branches: [ "master" ]
@ -19,8 +19,10 @@ permissions: read-all
jobs:
analysis:
name: Scorecards analysis
name: Scorecard analysis
runs-on: ubuntu-latest
# `publish_results: true` only works when run from the default branch. conditional can be removed if disabled.
if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request'
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
@ -31,21 +33,21 @@ jobs:
# actions: read
steps:
- name: 'Checkout code'
- name: "Checkout code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: 'Run analysis'
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
- name: "Run analysis"
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
with:
results_file: results.sarif
results_format: sarif
# (Optional) Read-only PAT token. Uncomment the `repo_token` line below if:
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecards on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
repo_token: ${{ secrets.SCORECARD_READ_TOKEN }}
# - you are installing Scorecard on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
@ -56,17 +58,21 @@ jobs:
# of the value entered here.
publish_results: true
# (Optional) Uncomment file_mode if you have a .gitattributes with files marked export-ignore
# file_mode: git
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
# - name: "Upload artifact"
# uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
# with:
# name: SARIF file
# path: results.sarif
# retention-days: 5
- name: "Upload artifact"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- name: 'Upload to code-scanning'
uses: github/codeql-action/upload-sarif@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9
# Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif

View file

@ -48,7 +48,7 @@ jobs:
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: ruby/setup-ruby@d8d83c3960843afb664e821fed6be52f37da5267 # v1.231.0
- uses: ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651 # v1.245.0
with:
ruby-version: ${{ matrix.ruby }}
bundler: none

View file

@ -68,7 +68,7 @@ jobs:
with:
arch: ${{ matrix.arch }}
- uses: ruby/setup-ruby@d8d83c3960843afb664e821fed6be52f37da5267 # v1.231.0
- uses: ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651 # v1.245.0
with:
ruby-version: '3.1'
bundler: none
@ -99,6 +99,10 @@ jobs:
- run: $SETARCH make hello
- name: runirb
run: |
echo IRB::VERSION | $SETARCH make runirb RUNOPT="-- -f"
- name: Set test options for skipped tests
run: |
set -x
@ -107,6 +111,7 @@ jobs:
if: ${{ matrix.test_task == 'check' && matrix.skipped_tests }}
- name: Set up Launchable
id: launchable
uses: ./.github/actions/launchable/setup
with:
os: ${{ matrix.os || 'ubuntu-22.04' }}
@ -115,14 +120,12 @@ jobs:
builddir: build
srcdir: src
continue-on-error: true
timeout-minutes: 3
- name: make ${{ matrix.test_task }}
run: |
if [ -n "${LAUNCHABLE_ORGANIZATION}" ]; then
exec \
> >(tee launchable_stdout.log) \
2> >(tee launchable_stderr.log)
fi
test -n "${LAUNCHABLE_STDOUT}" && exec 1> >(tee "${LAUNCHABLE_STDOUT}")
test -n "${LAUNCHABLE_STDERR}" && exec 2> >(tee "${LAUNCHABLE_STDERR}")
$SETARCH make -s ${{ matrix.test_task }} \
${TESTS:+TESTS="$TESTS"} \
@ -130,8 +133,10 @@ jobs:
timeout-minutes: ${{ matrix.timeout || 40 }}
env:
RUBY_TESTOPTS: '-q --tty=no'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: 'typeprof,rbs,repl_type_completor'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: ''
PRECHECK_BUNDLED_GEMS: 'no'
LAUNCHABLE_STDOUT: ${{ steps.launchable.outputs.stdout_report_path }}
LAUNCHABLE_STDERR: ${{ steps.launchable.outputs.stderr_report_path }}
- name: make skipped tests
run: |

View file

@ -100,7 +100,7 @@ jobs:
run: |
echo "WASI_SDK_PATH=/opt/wasi-sdk" >> $GITHUB_ENV
- uses: ruby/setup-ruby@d8d83c3960843afb664e821fed6be52f37da5267 # v1.231.0
- uses: ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651 # v1.245.0
with:
ruby-version: '3.1'
bundler: none
@ -142,7 +142,7 @@ jobs:
- run: tar cfz ../install.tar.gz -C ../install .
- name: Upload artifacts
uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ruby-wasm-install
path: ${{ github.workspace }}/install.tar.gz
@ -152,8 +152,6 @@ jobs:
- name: Run basictest
run: wasmtime run ./../build/miniruby --mapdir /::./ -- basictest/test.rb
env:
WASMTIME_BACKTRACE_DETAILS: '1'
working-directory: src
- name: Run bootstraptest (no thread)
@ -172,7 +170,7 @@ jobs:
- name: Save Pull Request number
if: ${{ github.event_name == 'pull_request' }}
run: echo "${{ github.event.pull_request.number }}" >> ${{ github.workspace }}/github-pr-info.txt
- uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: ${{ github.event_name == 'pull_request' }}
with:
name: github-pr-info

View file

@ -26,19 +26,16 @@ jobs:
matrix:
include:
- os: 2022
vc: 2019
vcvars: '10.0.22621.0 -vcvars_ver=14.2' # The defautl Windows 11 SDK and toolset are broken at windows-2022
vc: 2022
test_task: check
- os: 2025
vc: 2019
vcvars: '10.0.22621.0 -vcvars_ver=14.2'
vc: 2022
test_task: check
- os: 11-arm
test_task: 'btest test-basic test-tool' # check and test-spec are broken yet.
target: arm64
- os: 2022
vc: 2019
vcvars: '10.0.22621.0 -vcvars_ver=14.2'
- os: 2025
vc: 2022
test_task: test-bundled-gems
fail-fast: false
@ -59,27 +56,18 @@ jobs:
env:
GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
VCPKG_DEFAULT_TRIPLET: ${{ matrix.target || 'x64' }}-windows
RUBY_OPT_DIR: ${{ matrix.os == '11-arm' && 'C' || 'D' }}:/a/ruby/ruby/src/vcpkg_installed/%VCPKG_DEFAULT_TRIPLET%
steps:
- run: md build
working-directory:
- uses: ruby/setup-ruby@e34163cd15f4bb403dcd72d98e295997e6a55798 # v1.238.0
- uses: ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651 # v1.245.0
with:
# windows-11-arm has only 3.4.1, 3.4.2, 3.4.3, head
ruby-version: ${{ matrix.os != '11-arm' && '3.1' || '3.4' }}
ruby-version: ${{ !endsWith(matrix.os, 'arm') && '3.1' || '3.4' }}
bundler: none
windows-toolchain: none
- name: Install libraries with scoop
run: |
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
iwr -useb get.scoop.sh | iex
Join-Path (Resolve-Path ~).Path "scoop\shims" >> $Env:GITHUB_PATH
scoop install vcpkg uutils-coreutils cmake@3.31.6
shell: pwsh
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
sparse-checkout-cone-mode: false
@ -90,27 +78,47 @@ jobs:
srcdir: src
builddir: build
- name: Install tools with scoop
run: |
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
iwr -useb get.scoop.sh | iex
Join-Path (Resolve-Path ~).Path "scoop\shims" >> $Env:GITHUB_PATH
scoop install vcpkg uutils-coreutils cmake@3.31.6
shell: pwsh
- name: Restore vcpkg artifact
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: src\vcpkg_installed
key: windows-${{ matrix.os }}-vcpkg-${{ hashFiles('src/vcpkg.json') }}
- name: Install libraries with vcpkg
run: |
vcpkg install --vcpkg-root=C:\Users\runneradmin\scoop\apps\vcpkg\current
working-directory: src
- name: Save vcpkg artifact
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: src\vcpkg_installed
key: windows-${{ matrix.os }}-vcpkg-${{ hashFiles('src/vcpkg.json') }}
- name: setup env
# Available Ruby versions: https://github.com/actions/runner-images/blob/main/images/windows/Windows2019-Readme.md#ruby
# %TEMP% is inconsistent with %TMP% and test-all expects they are consistent.
# https://github.com/actions/virtual-environments/issues/712#issuecomment-613004302
run: |
::- Set up VC ${{ matrix.vc }}
set vswhere="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe"
for /f "delims=;" %%I in ('%vswhere% -latest -property installationPath') do (
set VCVARS="%%I\VC\Auxiliary\Build\vcvars64.bat"
)
if "${{ matrix.os }}" == "11-arm" (
set VCVARS="C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsarm64.bat"
)
set VCVARS
set | uutils sort > old.env
call %VCVARS% ${{ matrix.vcvars || '' }}
call ..\src\win32\vssetup.cmd ^
-arch=${{ matrix.target || 'amd64' }} ^
${{ matrix.vcvars && '-vcvars_ver=' || '' }}${{ matrix.vcvars }}
nmake -f nul
set TMP=%USERPROFILE%\AppData\Local\Temp
set TEMP=%USERPROFILE%\AppData\Local\Temp
set MAKEFLAGS=l
set /a TEST_JOBS=(15 * %NUMBER_OF_PROCESSORS% / 10) > nul
set RUBY_OPT_DIR=%GITHUB_WORKSPACE:\=/%/src/vcpkg_installed/%VCPKG_DEFAULT_TRIPLET%
set | uutils sort > new.env
uutils comm -13 old.env new.env >> %GITHUB_ENV%
del *.env
@ -125,18 +133,6 @@ jobs:
run: Get-Volume
shell: pwsh
# vcpkg built-in cache is not working now
- name: Restore vcpkg artifact
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: C:\Users\runneradmin\AppData\Local\vcpkg\archives
key: windows-${{ matrix.os }}-vcpkg-${{ hashFiles('src/vcpkg.json') }}
- name: Install libraries with vcpkg
run: |
vcpkg install --vcpkg-root=C:\Users\runneradmin\scoop\apps\vcpkg\current
working-directory: src
# TODO: We should use `../src` instead of `D:/a/ruby/ruby/src`
- name: Configure
run: >-
@ -157,26 +153,30 @@ jobs:
# windows-11-arm runner cannot run `ruby tool/file2lastrev.rb --revision.h --output=revision.h`
- name: make revision.h
run: |
if not exist revision.h (
for /f "tokens=1-3" %%I in ('git log -1 "--date=format-local:%%F %%T" "--format=%%H %%cd" @') do (
set rev=%%I
set dt=%%J
set tm=%%K
)
set yy=%dt:~0,4%
set /a mm=100%dt:~5,2% %% 100
set /a dd=100%dt:~8,2% %% 100
call set yy=%%dt:~0,4%%
call set /a mm=100%%dt:~5,2%% %%%% 100
call set /a dd=100%%dt:~8,2%% %%%% 100
call set branch=%%GITHUB_REF:refs/heads/=%%
(
echo #define RUBY_REVISION "%rev:~,10%"
echo #define RUBY_FULL_REVISION "%rev%"
echo #define RUBY_BRANCH_NAME "%GITHUB_REF%"
echo #define RUBY_RELEASE_DATETIME "%dt%T%tm%"
echo #define RUBY_RELEASE_YEAR %yy%
echo #define RUBY_RELEASE_MONTH %mm%
echo #define RUBY_RELEASE_DAY %dd%
call echo #define RUBY_REVISION "%%rev:~,10%%"
call echo #define RUBY_FULL_REVISION "%%rev%%"
call echo #define RUBY_BRANCH_NAME "%%branch%%"
call echo #define RUBY_RELEASE_DATETIME "%%dt%%T%%tm%%"
call echo #define RUBY_RELEASE_YEAR %%yy%%
call echo #define RUBY_RELEASE_MONTH %%mm%%
call echo #define RUBY_RELEASE_DAY %%dd%%
) > revision.h
copy /y NUL .revision.time
)
type revision.h
env:
TZ: UTC
if: ${{ matrix.os == '11-arm' }}
- run: nmake
@ -190,6 +190,7 @@ jobs:
test-task: ${{ matrix.test_task || 'check' }}
continue-on-error: true
if: ${{ matrix.test_task != 'test-bundled-gems' }}
timeout-minutes: 3
- run: nmake ${{ matrix.test_task || 'check' }}
env:

View file

@ -16,7 +16,7 @@ on:
jobs:
wsl:
runs-on: windows-latest
runs-on: windows-2025
if: >-
${{!(false
@ -29,9 +29,6 @@ jobs:
)}}
steps:
- name: Install winget
uses: Cyberboss/install-winget@v1
- name: Install or update WSL
uses: Ubuntu/WSL/.github/actions/wsl-install@main
with:

View file

@ -112,6 +112,13 @@ jobs:
- run: make
- name: Verify that --yjit-dump-disasm works
run: |
./miniruby --yjit-call-threshold=1 --yjit-dump-disasm -e0 | \
wc -l | \
ruby -ne 'raise "Disassembly seems broken in dev build (output has too few lines)" unless $_.to_i > 10'
if: ${{ contains(matrix.configure, 'jit=dev') }}
- name: Enable YJIT through ENV
run: echo "RUBY_YJIT_ENABLE=1" >> $GITHUB_ENV
@ -123,6 +130,7 @@ jobs:
if: ${{ matrix.test_task == 'check' && matrix.skipped_tests }}
- name: Set up Launchable
id: launchable
uses: ./.github/actions/launchable/setup
with:
os: macos-14
@ -132,14 +140,12 @@ jobs:
srcdir: src
is-yjit: true
continue-on-error: true
timeout-minutes: 3
- name: make ${{ matrix.test_task }}
run: |
if [ -n "${LAUNCHABLE_ORGANIZATION}" ]; then
exec \
> >(tee launchable_stdout.log) \
2> >(tee launchable_stderr.log)
fi
test -n "${LAUNCHABLE_STDOUT}" && exec 1> >(tee "${LAUNCHABLE_STDOUT}")
test -n "${LAUNCHABLE_STDERR}" && exec 2> >(tee "${LAUNCHABLE_STDERR}")
make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"} \
RUN_OPTS="$RUN_OPTS" \
@ -147,9 +153,11 @@ jobs:
timeout-minutes: 60
env:
RUBY_TESTOPTS: '-q --tty=no'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: 'typeprof,rbs,repl_type_completor'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: ''
SYNTAX_SUGGEST_TIMEOUT: '5'
PRECHECK_BUNDLED_GEMS: 'no'
LAUNCHABLE_STDOUT: ${{ steps.launchable.outputs.stdout_report_path }}
LAUNCHABLE_STDERR: ${{ steps.launchable.outputs.stderr_report_path }}
continue-on-error: ${{ matrix.continue-on-test_task || false }}
- name: make skipped tests

View file

@ -135,7 +135,7 @@ jobs:
- uses: ./.github/actions/setup/ubuntu
- uses: ruby/setup-ruby@d8d83c3960843afb664e821fed6be52f37da5267 # v1.231.0
- uses: ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651 # v1.245.0
with:
ruby-version: '3.1'
bundler: none
@ -163,6 +163,13 @@ jobs:
- run: make
- name: Verify that --yjit-dump-disasm works
run: |
./miniruby --yjit-call-threshold=1 --yjit-dump-disasm -e0 | \
wc -l | \
ruby -ne 'raise "Disassembly seems broken in dev build (output has too few lines)" unless $_.to_i > 10'
if: ${{ contains(matrix.configure, 'jit=dev') }}
- name: Enable YJIT through ENV
run: echo "RUBY_YJIT_ENABLE=1" >> $GITHUB_ENV
@ -171,6 +178,7 @@ jobs:
run: ./miniruby --yjit -v | grep "+YJIT"
- name: Set up Launchable
id: launchable
uses: ./.github/actions/launchable/setup
with:
os: ubuntu-22.04
@ -180,14 +188,12 @@ jobs:
srcdir: src
is-yjit: true
continue-on-error: true
timeout-minutes: 3
- name: make ${{ matrix.test_task }}
run: |
if [ -n "${LAUNCHABLE_ORGANIZATION}" ]; then
exec \
> >(tee launchable_stdout.log) \
2> >(tee launchable_stderr.log)
fi
test -n "${LAUNCHABLE_STDOUT}" && exec 1> >(tee "${LAUNCHABLE_STDOUT}")
test -n "${LAUNCHABLE_STDERR}" && exec 2> >(tee "${LAUNCHABLE_STDERR}")
make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"} \
RUN_OPTS="$RUN_OPTS" MSPECOPT=--debug SPECOPTS="$SPECOPTS" \
@ -195,11 +201,13 @@ jobs:
timeout-minutes: 90
env:
RUBY_TESTOPTS: '-q --tty=no'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: 'typeprof,rbs,repl_type_completor'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: ''
PRECHECK_BUNDLED_GEMS: 'no'
SYNTAX_SUGGEST_TIMEOUT: '5'
YJIT_BINDGEN_DIFF_OPTS: '--exit-code'
LIBCLANG_PATH: ${{ matrix.libclang_path }}
LAUNCHABLE_STDOUT: ${{ steps.launchable.outputs.stdout_report_path }}
LAUNCHABLE_STDERR: ${{ steps.launchable.outputs.stderr_report_path }}
continue-on-error: ${{ matrix.continue-on-test_task || false }}
- name: Show ${{ github.event.pull_request.base.ref }} GitHub URL for yjit-bench comparison

View file

@ -33,16 +33,23 @@ jobs:
matrix:
include:
- test_task: 'zjit-test'
configure: '--enable-zjit=dev'
configure: '--enable-yjit=dev --enable-zjit'
- test_task: 'test-all'
- test_task: 'ruby' # build test for combo build
configure: '--enable-yjit --enable-zjit'
- test_task: 'zjit-test-all'
configure: '--enable-zjit=dev'
testopts: '--seed=11831'
- test_task: 'btest'
configure: '--enable-zjit=dev'
tests: '../src/test/ruby/test_zjit.rb'
env:
GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
RUN_OPTS: ${{ matrix.zjit_opts }}
SPECOPTS: ${{ matrix.specopts }}
TESTOPTS: ${{ matrix.testopts }}
runs-on: macos-14
@ -57,7 +64,7 @@ jobs:
)}}
steps:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
sparse-checkout-cone-mode: false
sparse-checkout: /.github
@ -90,19 +97,67 @@ jobs:
- run: make
- name: Verify that --zjit-dump-disasm works
run: |
./miniruby --zjit-call-threshold=1 --zjit-dump-disasm -e0 | \
wc -l | \
ruby -ne 'raise "Disassembly seems broken in dev build (output has too few lines)" unless $_.to_i > 10'
if: ${{ contains(matrix.configure, 'jit=dev') }}
- name: btest
run: |
RUST_BACKTRACE=1 ruby --disable=gems ../src/bootstraptest/runner.rb --ruby="./miniruby -I../src/lib -I. -I.ext/common --zjit-call-threshold=1" \
../src/bootstraptest/test_attr.rb \
../src/bootstraptest/test_autoload.rb \
../src/bootstraptest/test_block.rb \
../src/bootstraptest/test_class.rb \
../src/bootstraptest/test_constant_cache.rb \
../src/bootstraptest/test_env.rb \
../src/bootstraptest/test_eval.rb \
../src/bootstraptest/test_exception.rb \
../src/bootstraptest/test_fiber.rb \
../src/bootstraptest/test_finalizer.rb \
../src/bootstraptest/test_flip.rb \
../src/bootstraptest/test_flow.rb \
../src/bootstraptest/test_fork.rb \
../src/bootstraptest/test_gc.rb \
../src/bootstraptest/test_insns.rb \
../src/bootstraptest/test_io.rb \
../src/bootstraptest/test_jump.rb \
../src/bootstraptest/test_literal.rb \
../src/bootstraptest/test_literal_suffix.rb \
../src/bootstraptest/test_load.rb \
../src/bootstraptest/test_marshal.rb \
../src/bootstraptest/test_massign.rb \
../src/bootstraptest/test_method.rb \
../src/bootstraptest/test_objectspace.rb \
../src/bootstraptest/test_proc.rb \
../src/bootstraptest/test_ractor.rb \
../src/bootstraptest/test_string.rb \
../src/bootstraptest/test_struct.rb \
../src/bootstraptest/test_syntax.rb \
../src/bootstraptest/test_thread.rb \
../src/bootstraptest/test_yjit_30k_ifelse.rb \
../src/bootstraptest/test_yjit_30k_methods.rb \
../src/bootstraptest/test_yjit_rust_port.rb
# ../src/bootstraptest/test_yjit.rb \
if: ${{ matrix.test_task == 'btest' }}
- name: make ${{ matrix.test_task }}
run: >-
make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"}
RUN_OPTS="$RUN_OPTS"
SPECOPTS="$SPECOPTS"
TESTOPTS="$TESTOPTS"
timeout-minutes: 60
env:
RUBY_TESTOPTS: '-q --tty=no'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: 'typeprof'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: ''
SYNTAX_SUGGEST_TIMEOUT: '5'
PRECHECK_BUNDLED_GEMS: 'no'
TESTS: ${{ matrix.tests }}
continue-on-error: ${{ matrix.continue-on-test_task || false }}
if: ${{ matrix.test_task != 'btest' }}
result:
if: ${{ always() }}

View file

@ -38,17 +38,21 @@ jobs:
libclang_path: '/usr/lib/llvm-14/lib/libclang.so.1'
- test_task: 'zjit-test'
configure: '--enable-zjit=dev'
configure: '--enable-yjit --enable-zjit=dev'
- test_task: 'test-all'
- test_task: 'zjit-test-all'
configure: '--enable-zjit=dev'
testopts: '--seed=18140'
- test_task: 'btest'
configure: '--enable-zjit=dev'
tests: '../src/test/ruby/test_zjit.rb'
env:
GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
RUN_OPTS: ${{ matrix.zjit_opts }}
YJIT_BENCH_OPTS: ${{ matrix.yjit_bench_opts }}
SPECOPTS: ${{ matrix.specopts }}
TESTOPTS: ${{ matrix.testopts }}
RUBY_DEBUG: ci
BUNDLE_JOBS: 8 # for yjit-bench
RUST_BACKTRACE: 1
@ -66,14 +70,14 @@ jobs:
)}}
steps:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
sparse-checkout-cone-mode: false
sparse-checkout: /.github
- uses: ./.github/actions/setup/ubuntu
- uses: ruby/setup-ruby@a6e6f86333f0a2523ece813039b8b4be04560854 # v1.190.0
- uses: ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651 # v1.245.0
with:
ruby-version: '3.1'
bundler: none
@ -110,26 +114,74 @@ jobs:
- run: make
- name: Verify that --zjit-dump-disasm works
run: |
./miniruby --zjit-call-threshold=1 --zjit-dump-disasm -e0 | \
wc -l | \
ruby -ne 'raise "Disassembly seems broken in dev build (output has too few lines)" unless $_.to_i > 10'
if: ${{ contains(matrix.configure, 'jit=dev') }}
# Check that the binary was built with ZJIT
- name: Check ZJIT enabled
run: ./miniruby --zjit -v | grep "+ZJIT"
if: ${{ matrix.configure != '--disable-zjit' }}
- name: btest
run: |
RUST_BACKTRACE=1 ruby --disable=gems ../src/bootstraptest/runner.rb --ruby="./miniruby -I../src/lib -I. -I.ext/common --zjit-call-threshold=1" \
../src/bootstraptest/test_attr.rb \
../src/bootstraptest/test_autoload.rb \
../src/bootstraptest/test_block.rb \
../src/bootstraptest/test_class.rb \
../src/bootstraptest/test_constant_cache.rb \
../src/bootstraptest/test_env.rb \
../src/bootstraptest/test_env.rb \
../src/bootstraptest/test_exception.rb \
../src/bootstraptest/test_fiber.rb \
../src/bootstraptest/test_finalizer.rb \
../src/bootstraptest/test_flip.rb \
../src/bootstraptest/test_flow.rb \
../src/bootstraptest/test_fork.rb \
../src/bootstraptest/test_gc.rb \
../src/bootstraptest/test_insns.rb \
../src/bootstraptest/test_io.rb \
../src/bootstraptest/test_jump.rb \
../src/bootstraptest/test_literal.rb \
../src/bootstraptest/test_literal_suffix.rb \
../src/bootstraptest/test_load.rb \
../src/bootstraptest/test_marshal.rb \
../src/bootstraptest/test_massign.rb \
../src/bootstraptest/test_method.rb \
../src/bootstraptest/test_objectspace.rb \
../src/bootstraptest/test_proc.rb \
../src/bootstraptest/test_ractor.rb \
../src/bootstraptest/test_string.rb \
../src/bootstraptest/test_struct.rb \
../src/bootstraptest/test_syntax.rb \
../src/bootstraptest/test_thread.rb \
../src/bootstraptest/test_yjit_30k_ifelse.rb \
../src/bootstraptest/test_yjit_30k_methods.rb \
../src/bootstraptest/test_yjit_rust_port.rb
# ../src/bootstraptest/test_yjit.rb \
if: ${{ matrix.test_task == 'btest' }}
- name: make ${{ matrix.test_task }}
run: >-
make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"}
RUN_OPTS="$RUN_OPTS" MSPECOPT=--debug SPECOPTS="$SPECOPTS"
TESTOPTS="$TESTOPTS"
ZJIT_BINDGEN_DIFF_OPTS="$ZJIT_BINDGEN_DIFF_OPTS"
timeout-minutes: 90
env:
RUBY_TESTOPTS: '-q --tty=no'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: 'typeprof'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: ''
PRECHECK_BUNDLED_GEMS: 'no'
SYNTAX_SUGGEST_TIMEOUT: '5'
ZJIT_BINDGEN_DIFF_OPTS: '--exit-code'
LIBCLANG_PATH: ${{ matrix.libclang_path }}
TESTS: ${{ matrix.tests }}
continue-on-error: ${{ matrix.continue-on-test_task || false }}
if: ${{ matrix.test_task != 'btest' }}
result:
if: ${{ always() }}

3
.gitignore vendored
View file

@ -246,6 +246,9 @@ lcov*.info
/yjit-bench
/yjit_exit_locations.dump
# Rust
/target
# /wasm/
/wasm/tests/*.wasm

View file

@ -10,7 +10,7 @@ rdoc_include:
exclude:
- \Alib/irb
- .gemspec\z
- \.gemspec\z
autolink_excluded_words:
- Class
@ -20,3 +20,5 @@ autolink_excluded_words:
- RDoc
- Ruby
- Set
canonical_root: https://docs.ruby-lang.org/en/master

89
Cargo.lock generated Normal file
View file

@ -0,0 +1,89 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "capstone"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "015ef5d5ca1743e3f94af9509ba6bd2886523cfee46e48d15c2ef5216fd4ac9a"
dependencies = [
"capstone-sys",
"libc",
]
[[package]]
name = "capstone-sys"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2267cb8d16a1e4197863ec4284ffd1aec26fe7e57c58af46b02590a0235809a0"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "cc"
version = "1.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c"
dependencies = [
"shlex",
]
[[package]]
name = "dissimilar"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8975ffdaa0ef3661bfe02dbdcc06c9f829dfafe6a3c474de366a8d5e44276921"
[[package]]
name = "expect-test"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63af43ff4431e848fb47472a920f14fa71c24de13255a5692e93d4e90302acb0"
dependencies = [
"dissimilar",
"once_cell",
]
[[package]]
name = "jit"
version = "0.0.0"
dependencies = [
"yjit",
"zjit",
]
[[package]]
name = "libc"
version = "0.2.171"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "yjit"
version = "0.1.0"
dependencies = [
"capstone",
]
[[package]]
name = "zjit"
version = "0.0.1"
dependencies = [
"capstone",
"expect-test",
]

51
Cargo.toml Normal file
View file

@ -0,0 +1,51 @@
# Using Cargo's workspace feature to build all the Rust code in
# into a single package.
# TODO(alan) notes about rust version requirements. Undecided yet.
[workspace]
members = ["zjit", "yjit"]
[package]
name = "jit"
version = "0.0.0"
edition = "2024"
rust-version = "1.85.0"
publish = false # Don't publish to crates.io
[dependencies]
yjit = { path = "yjit", optional = true }
zjit = { path = "zjit", optional = true }
[lib]
crate-type = ["staticlib"]
path = "jit.rs"
[features]
disasm = ["yjit?/disasm", "zjit?/disasm"]
runtime_checks = ["yjit?/runtime_checks", "zjit?/runtime_checks"]
yjit = [ "dep:yjit" ]
zjit = [ "dep:zjit" ]
[profile.dev]
opt-level = 0
debug = true
debug-assertions = true
overflow-checks = true
[profile.dev_nodebug]
inherits = "dev"
[profile.stats]
inherits = "release"
[profile.release]
# NOTE: --enable-yjit and zjit builds use `rustc` without going through Cargo. You
# might want to update the `rustc` invocation if you change this profile.
opt-level = 3
# The extra robustness that comes from checking for arithmetic overflow is
# worth the performance cost for the compiler.
overflow-checks = true
# Generate debug info
debug = true
# Use ThinLTO. Much smaller output for a small amount of build time increase.
lto = "thin"

155
NEWS.md
View file

@ -14,17 +14,86 @@ Note that each entry is kept to a minimum, see links for details.
Note: We're only listing outstanding class updates.
* Kernel
* `Kernel#inspect` now checks for the existence of a `#instance_variables_to_inspect` method,
allowing control over which instance variables are displayed in the `#inspect` string:
```ruby
class DatabaseConfig
def initialize(host, user, password)
@host = host
@user = user
@password = password
end
private def instance_variables_to_inspect = [:@host, :@user]
end
conf = DatabaseConfig.new("localhost", "root", "hunter2")
conf.inspect #=> #<DatabaseConfig:0x0000000104def350 @host="localhost", @user="root">
```
[[Feature #21219]]
* Binding
* `Binding#local_variables` does no longer include numbered parameters.
Also, `Binding#local_variable_get` and `Binding#local_variable_set` reject to handle numbered parameters.
[[Bug #21049]]
Also, `Binding#local_variable_get` and `Binding#local_variable_set` reject
to handle numbered parameters. [[Bug #21049]]
* IO
* `IO.select` accepts +Float::INFINITY+ as a timeout argument.
[[Feature #20610]]
* Socket
* `Socket.tcp` & `TCPSocket.new` accepts `open_timeout` as a keyword argument to specify
the timeout for the initial connection. [[Feature #21347]]
* Ractor
* `Ractor::Port` class was added for a new synchronization mechanism
to communicate between Ractors. [[Feature #21262]]
```ruby
port1 = Ractor::Port.new
port2 = Ractor::Port.new
Ractor.new port1, port2 do |port1, port2|
port1 << 1
port2 << 11
port1 << 2
port2 << 12
end
2.times{ p port1.receive } #=> 1, 2
2.times{ p port2.receive } #=> 11, 12
```
`Ractor::Port` provides the following methods:
* `Ractor::Port#receive`
* `Ractor::Port#send` (or `Ractor::Port#<<`)
* `Ractor::Port#close`
* `Ractor::Port#closed?`
As result, `Ractor.yield` and `Ractor#take` were removed.
* `Ractor#join` and `Ractor#value` were added to wait for the
termination of a Ractor. These are similar to `Thread#join`
and `Thread#value`.
* `Ractor#monitor` and `Ractor#unmonitor` were added as low-level
interfaces used internally to implement `Ractor#join`.
* `Ractor.select` now only accepts Ractors and Ports. If Ractors are given,
it returns when a Ractor terminates.
* `Ractor#default_port` was added. Each `Ractor` has a default port,
which is used by `Ractor.send`, `Ractor.receive`.
* `Ractor#close_incoming` and `Ractor#close_outgoing` were removed.
* Set
* Set is now a core class, instead of an autoloaded stdlib class.
@ -32,18 +101,30 @@ Note: We're only listing outstanding class updates.
* String
* Update Unicode to Version 16.0.0 and Emoji Version 16.0. [[Feature #19908]][[Feature #20724]]
(also applies to Regexp)
* Update Unicode to Version 16.0.0 and Emoji Version 16.0.
[[Feature #19908]][[Feature #20724]] (also applies to Regexp)
* Fiber::Scheduler
* Introduce `Fiber::Scheduler#fiber_interrupt` to interrupt a fiber with a
given exception. The initial use case is to interrupt a fiber that is
waiting on a blocking IO operation when the IO operation is closed.
[[Feature #21166]]
* Pathname
* Pathname has been promoted from a default gem to a core class of Ruby.
[[Feature #17473]]
## Stdlib updates
The following bundled gems are promoted from default gems.
* ostruct 0.6.1
* ostruct 0.6.3
* pstore 0.2.0
* benchmark 0.4.0
* benchmark 0.4.1
* logger 1.7.0
* rdoc 6.13.1
* rdoc 6.14.2
* win32ole 1.9.2
* irb 1.15.2
* reline 0.6.1
@ -52,7 +133,9 @@ The following bundled gems are promoted from default gems.
We only list stdlib changes that are notable feature changes.
Other changes are listed in the following sections. we also listed release history from the previous bundled version that is Ruby 3.3.0 if it has GitHub releases.
Other changes are listed in the following sections. We also listed release
history from the previous bundled version that is Ruby 3.3.0 if it has GitHub
releases.
The following default gem is added.
@ -60,16 +143,22 @@ The following default gem is added.
The following default gems are updated.
* RubyGems 3.7.0.dev
* bundler 2.7.0.dev
* erb 5.0.0
* json 2.12.0
* RubyGems 3.8.0.dev
* bundler 2.8.0.dev
* erb 5.0.2
* etc 1.4.6
* io-console 0.8.1
* io-nonblock 0.3.2
* io-wait 0.3.2
* json 2.12.2
* optparse 0.7.0.dev.2
* prism 1.4.0
* psych 5.2.6
* resolv 0.6.2
* stringio 3.1.8.dev
* strscan 3.1.5.dev
* strscan 3.1.6.dev
* uri 1.0.3
* weakref 0.1.4
The following bundled gems are added.
@ -77,30 +166,52 @@ The following bundled gems are added.
The following bundled gems are updated.
* minitest 5.25.5
* test-unit 3.6.8
* rake 13.3.0
* test-unit 3.7.0
* rexml 3.4.1
* net-imap 0.5.8
* net-imap 0.5.9
* net-smtp 0.5.1
* rbs 3.9.3
* bigdecimal 3.1.9
* matrix 0.4.3
* prime 0.1.4
* rbs 3.9.4
* debug 1.11.0
* base64 0.3.0
* bigdecimal 3.2.2
* drb 2.2.3
* syslog 0.3.0
* csv 3.3.4
* csv 3.3.5
* repl_type_completor 0.1.11
## Supported platforms
## Compatibility issues
* The following methods were removed from Ractor due because of `Ractor::Port`:
* `Ractor.yield`
* `Ractor#take`
* `Ractor#close_incoming`
* `Ractor#close_outgoging`
[[Feature #21262]]
## Stdlib compatibility issues
* CGI library is removed from the default gems. Now we only provide `cgi/escape` for
the following methods:
* `CGI.escape` and `CGI.unescape`
* `CGI.escapeHTML` and `CGI.unescapeHTML`
* `CGI.escapeURIComponent` and `CGI.unescapeURIComponent`
* `CGI.escapeElement` and `CGI.unescapeElement`
[[Feature #21258]]
* With the move of `Set` from stdlib to core class, `set/sorted_set.rb` has
been removed, and `SortedSet` is no longer an autoloaded constant. Please
install the `sorted_set` gem and `require 'sorted_set'` to use `SortedSet`.
[[Feature #21287]]
## C API updates
* IO
@ -110,7 +221,7 @@ The following bundled gems are updated.
using `RUBY_IO_MODE_EXTERNAL` and use `rb_io_close(io)` to close it (this
also interrupts and waits for all pending operations on the `IO`
instance). Directly closing file descriptors does not interrupt pending
operations, and may lead to undefined beahviour. In other words, if two
operations, and may lead to undefined behaviour. In other words, if two
`IO` objects share the same file descriptor, closing one does not affect
the other. [[Feature #18455]]
@ -118,11 +229,17 @@ The following bundled gems are updated.
## JIT
[Feature #17473]: https://bugs.ruby-lang.org/issues/17473
[Feature #18455]: https://bugs.ruby-lang.org/issues/18455
[Feature #19908]: https://bugs.ruby-lang.org/issues/19908
[Feature #20610]: https://bugs.ruby-lang.org/issues/20610
[Feature #20724]: https://bugs.ruby-lang.org/issues/20724
[Feature #21047]: https://bugs.ruby-lang.org/issues/21047
[Bug #21049]: https://bugs.ruby-lang.org/issues/21049
[Feature #21166]: https://bugs.ruby-lang.org/issues/21166
[Feature #21216]: https://bugs.ruby-lang.org/issues/21216
[Feature #21219]: https://bugs.ruby-lang.org/issues/21219
[Feature #21258]: https://bugs.ruby-lang.org/issues/21258
[Feature #21262]: https://bugs.ruby-lang.org/issues/21262
[Feature #21287]: https://bugs.ruby-lang.org/issues/21287
[Feature #21347]: https://bugs.ruby-lang.org/issues/21347

View file

@ -3439,10 +3439,9 @@ rb_ary_sort_bang(VALUE ary)
ARY_SET_CAPA(ary, ARY_HEAP_LEN(tmp));
}
/* tmp was lost ownership for the ptr */
FL_UNSET(tmp, FL_FREEZE);
FL_SET_EMBED(tmp);
ARY_SET_EMBED_LEN(tmp, 0);
FL_SET(tmp, FL_FREEZE);
OBJ_FREEZE(tmp);
}
/* tmp will be GC'ed. */
RBASIC_SET_CLASS_RAW(tmp, rb_cArray); /* rb_cArray must be marked */

10
ast.c
View file

@ -812,6 +812,16 @@ node_locations(VALUE ast_value, const NODE *node)
location_new(&RNODE_CLASS(node)->class_keyword_loc),
location_new(&RNODE_CLASS(node)->inheritance_operator_loc),
location_new(&RNODE_CLASS(node)->end_keyword_loc));
case NODE_COLON2:
return rb_ary_new_from_args(3,
location_new(nd_code_loc(node)),
location_new(&RNODE_COLON2(node)->delimiter_loc),
location_new(&RNODE_COLON2(node)->name_loc));
case NODE_COLON3:
return rb_ary_new_from_args(3,
location_new(nd_code_loc(node)),
location_new(&RNODE_COLON3(node)->delimiter_loc),
location_new(&RNODE_COLON3(node)->name_loc));
case NODE_DOT2:
return rb_ary_new_from_args(2,
location_new(nd_code_loc(node)),

View file

@ -40,7 +40,7 @@ Usage: benchmark-driver [options] RUBY|YAML...
--filter REGEXP Filter out benchmarks with given regexp
--run-duration SECONDS Warmup estimates loop_count to run for this duration (default: 3)
--timeout SECONDS Timeout ruby command execution with timeout(1)
-v, --verbose Verbose mode. Multiple -v options increase visilibity (max: 2)
-v, --verbose Verbose mode. Multiple -v options increase visibility (max: 2)
```
## make benchmark

View file

@ -0,0 +1,23 @@
prelude: |
class SimpleClass; end
class OneModuleClass
1.times { include Module.new }
end
class MediumClass
10.times { include Module.new }
end
class LargeClass
100.times { include Module.new }
end
benchmark:
object_class_superclass: |
Object.superclass
simple_class_superclass: |
SimpleClass.superclass
one_module_class: |
OneModuleClass.superclass
medium_class_superclass: |
MediumClass.superclass
large_class_superclass: |
LargeClass.superclass
loop_count: 20000000

13
benchmark/io_close.yml Normal file
View file

@ -0,0 +1,13 @@
prelude: |
ios = 1000.times.map do
100.times.map{IO.pipe}
end
benchmark:
# Close IO
io_close: |
# Process each batch of ios per iteration of the benchmark.
ios.pop.each do |r, w|
r.close
w.close
end
loop_count: 100

View file

@ -0,0 +1,21 @@
prelude: |
ios = 100.times.map do
10.times.map do
pipe = IO.pipe.tap do |r, w|
Thread.new do
r.read
rescue IOError
# Ignore
end
end
end
end
benchmark:
# Close IO
io_close_contended: |
# Process each batch of ios per iteration of the benchmark.
ios.pop.each do |r, w|
r.close
w.close
end
loop_count: 10

View file

@ -87,7 +87,7 @@ __bmdv_ractors << Ractor.new(__bmdv_loop_after - __bmdv_loop_before) { |__bmdv_l
<% end %>
# Wait for all Ractors before executing code to write results
__bmdv_ractors.map!(&:take)
__bmdv_ractors.map!(&:value)
<% results.each do |result| %>
File.write(<%= result.dump %>, __bmdv_ractors.shift)

View file

@ -1,10 +1,16 @@
prelude: |
def a = nil
benchmark:
rationalize:
nil.rationalize
to_c: |
nil.to_c
to_i: |
nil.to_i
to_f: |
nil.to_f
to_r: |
nil.to_r
splat: |
a(*nil)
loop_count: 100000

View file

@ -625,6 +625,8 @@ class Assertion < Struct.new(:src, :path, :lineno, :proc)
end
end
class Timeout < StandardError; end
def get_result_string(opt = '', timeout: BT.timeout, **argh)
if BT.ruby
timeout = BT.apply_timeout_scale(timeout)
@ -634,7 +636,11 @@ class Assertion < Struct.new(:src, :path, :lineno, :proc)
out = IO.popen("#{BT.ruby} -W0 #{opt} #{filename}", **kw)
pid = out.pid
th = Thread.new {out.read.tap {Process.waitpid(pid); out.close}}
th.value if th.join(timeout)
if th.join(timeout)
th.value
else
Timeout.new("timed out after #{timeout} seconds")
end
ensure
raise Interrupt if $? and $?.signaled? && $?.termsig == Signal.list["INT"]
@ -891,4 +897,8 @@ def yjit_enabled?
ENV.key?('RUBY_YJIT_ENABLE') || ENV.fetch('RUN_OPTS', '').include?('yjit') || BT.ruby.include?('yjit')
end
def zjit_enabled?
ENV.key?('RUBY_ZJIT_ENABLE') || ENV.fetch('RUN_OPTS', '').include?('zjit') || BT.ruby.include?('zjit')
end
exit main

View file

@ -37,3 +37,8 @@ assert_normal_exit %q{
assert_normal_exit %q{
Fiber.new(&Object.method(:class_eval)).resume("foo")
}, '[ruby-dev:34128]'
# [Bug #21400]
assert_normal_exit %q{
Thread.new { Fiber.current.kill }.join
}

File diff suppressed because it is too large Load diff

View file

@ -220,7 +220,7 @@ assert_equal 'Sub', %q{
call(Sub.new('o')).class
}
# String#dup with FL_EXIVAR
# String#dup with generic ivars
assert_equal '["str", "ivar"]', %q{
def str_dup(str) = str.dup
str = "str"
@ -3018,15 +3018,16 @@ assert_equal '[:itself]', %q{
itself
end
tracing_ractor = Ractor.new do
port = Ractor::Port.new
tracing_ractor = Ractor.new port do |port|
# 1: start tracing
events = []
tp = TracePoint.new(:c_call) { events << _1.method_id }
tp.enable
Ractor.yield(nil)
port << nil
# 3: run compiled method on tracing ractor
Ractor.yield(nil)
port << nil
traced_method
events
@ -3034,13 +3035,13 @@ assert_equal '[:itself]', %q{
tp&.disable
end
tracing_ractor.take
port.receive
# 2: compile on non tracing ractor
traced_method
tracing_ractor.take
tracing_ractor.take
port.receive
tracing_ractor.value
}
# Try to hit a lazy branch stub while another ractor enables tracing
@ -3054,17 +3055,18 @@ assert_equal '42', %q{
end
end
ractor = Ractor.new do
port = Ractor::Port.new
ractor = Ractor.new port do |port|
compiled(false)
Ractor.yield(nil)
port << nil
compiled(41)
end
tp = TracePoint.new(:line) { itself }
ractor.take
port.receive
tp.enable
ractor.take
ractor.value
}
# Test equality with changing types
@ -3140,7 +3142,7 @@ assert_equal '42', %q{
A.foo
A.foo
Ractor.new { A.foo }.take
Ractor.new { A.foo }.value
}
assert_equal '["plain", "special", "sub", "plain"]', %q{
@ -3859,36 +3861,6 @@ assert_equal '3,12', %q{
pt_inspect(p)
}
# Regression test for deadlock between branch_stub_hit and ractor_receive_if
assert_equal '10', %q{
r = Ractor.new Ractor.current do |main|
main << 1
main << 2
main << 3
main << 4
main << 5
main << 6
main << 7
main << 8
main << 9
main << 10
end
a = []
a << Ractor.receive_if{|msg| msg == 10}
a << Ractor.receive_if{|msg| msg == 9}
a << Ractor.receive_if{|msg| msg == 8}
a << Ractor.receive_if{|msg| msg == 7}
a << Ractor.receive_if{|msg| msg == 6}
a << Ractor.receive_if{|msg| msg == 5}
a << Ractor.receive_if{|msg| msg == 4}
a << Ractor.receive_if{|msg| msg == 3}
a << Ractor.receive_if{|msg| msg == 2}
a << Ractor.receive_if{|msg| msg == 1}
a.length
}
# checktype
assert_equal 'false', %q{
def function()

View file

@ -374,7 +374,7 @@ assert_equal 'ok', %q{
r = Ractor.new do
'ok'
end
r.take
r.value
}
# Passed arguments to Ractor.new will be a block parameter
@ -384,7 +384,7 @@ assert_equal 'ok', %q{
r = Ractor.new 'ok' do |msg|
msg
end
r.take
r.value
}
# Pass multiple arguments to Ractor.new
@ -393,7 +393,7 @@ assert_equal 'ok', %q{
r = Ractor.new 'ping', 'pong' do |msg, msg2|
[msg, msg2]
end
'ok' if r.take == ['ping', 'pong']
'ok' if r.value == ['ping', 'pong']
}
# Ractor#send passes an object with copy to a Ractor
@ -403,7 +403,7 @@ assert_equal 'ok', %q{
msg = Ractor.receive
end
r.send 'ok'
r.take
r.value
}
assert_equal '[1, 2, 3]', %q{

200
class.c
View file

@ -42,10 +42,10 @@
* 2: RCLASS_PRIME_CLASSEXT_PRIME_WRITABLE
* This class's prime classext is the only classext and writable from any namespaces.
* If unset, the prime classext is writable only from the root namespace.
* if !SHAPE_IN_BASIC_FLAGS
* 4-19: SHAPE_FLAG_MASK
* Shape ID for the class.
* endif
* 3: RCLASS_IS_INITIALIZED
* Class has been initialized.
* 4: RCLASS_NAMESPACEABLE
* Is a builtin class that may be namespaced. It larger than a normal class.
*/
/* Flags of T_ICLASS
@ -53,10 +53,8 @@
* 2: RCLASS_PRIME_CLASSEXT_PRIME_WRITABLE
* This module's prime classext is the only classext and writable from any namespaces.
* If unset, the prime classext is writable only from the root namespace.
* if !SHAPE_IN_BASIC_FLAGS
* 4-19: SHAPE_FLAG_MASK
* Shape ID. This is set but not used.
* endif
* 4: RCLASS_NAMESPACEABLE
* Is a builtin class that may be namespaced. It larger than a normal class.
*/
/* Flags of T_MODULE
@ -64,17 +62,15 @@
* 0: RCLASS_IS_ROOT
* The class has been added to the VM roots. Will always be marked and pinned.
* This is done for classes defined from C to allow storing them in global variables.
* 1: RMODULE_ALLOCATED_BUT_NOT_INITIALIZED
* Module has not been initialized.
* 1: RMODULE_IS_REFINEMENT
* Module is used for refinements.
* 2: RCLASS_PRIME_CLASSEXT_PRIME_WRITABLE
* This module's prime classext is the only classext and writable from any namespaces.
* If unset, the prime classext is writable only from the root namespace.
* 3: RMODULE_IS_REFINEMENT
* Module is used for refinements.
* if !SHAPE_IN_BASIC_FLAGS
* 4-19: SHAPE_FLAG_MASK
* Shape ID for the module.
* endif
* 3: RCLASS_IS_INITIALIZED
* Module has been initialized.
* 4: RCLASS_NAMESPACEABLE
* Is a builtin class that may be namespaced. It larger than a normal class.
*/
#define METACLASS_OF(k) RBASIC(k)->klass
@ -183,16 +179,6 @@ duplicate_classext_const_tbl(struct rb_id_table *src, VALUE klass)
return dst;
}
static void
duplicate_classext_superclasses(rb_classext_t *orig, rb_classext_t *copy)
{
RCLASSEXT_SUPERCLASSES(copy) = RCLASSEXT_SUPERCLASSES(orig);
RCLASSEXT_SUPERCLASS_DEPTH(copy) = RCLASSEXT_SUPERCLASS_DEPTH(orig);
// the copy is always not the owner and the orig (or its parent class) will maintain the superclasses array
RCLASSEXT_SUPERCLASSES_OWNER(copy) = false;
RCLASSEXT_SUPERCLASSES_WITH_SELF(copy) = RCLASSEXT_SUPERCLASSES_WITH_SELF(orig);
}
static VALUE
namespace_subclasses_tbl_key(const rb_namespace_t *ns)
{
@ -256,6 +242,8 @@ duplicate_classext_subclasses(rb_classext_t *orig, rb_classext_t *copy)
static void
class_duplicate_iclass_classext(VALUE iclass, rb_classext_t *mod_ext, const rb_namespace_t *ns)
{
RUBY_ASSERT(RB_TYPE_P(iclass, T_ICLASS));
rb_classext_t *src = RCLASS_EXT_PRIME(iclass);
rb_classext_t *ext = RCLASS_EXT_TABLE_LOOKUP_INTERNAL(iclass, ns);
int first_set = 0;
@ -278,7 +266,7 @@ class_duplicate_iclass_classext(VALUE iclass, rb_classext_t *mod_ext, const rb_n
else {
RCLASSEXT_M_TBL(ext) = RCLASSEXT_M_TBL(mod_ext);
}
RCLASSEXT_FIELDS(ext) = (VALUE *)st_init_numtable();
RCLASSEXT_CONST_TBL(ext) = RCLASSEXT_CONST_TBL(mod_ext);
RCLASSEXT_CVC_TBL(ext) = RCLASSEXT_CVC_TBL(mod_ext);
@ -315,13 +303,8 @@ rb_class_duplicate_classext(rb_classext_t *orig, VALUE klass, const rb_namespace
RCLASSEXT_M_TBL(ext) = duplicate_classext_m_tbl(RCLASSEXT_M_TBL(orig), klass, dup_iclass);
// TODO: consider shapes for performance
if (RCLASSEXT_FIELDS(orig)) {
RCLASSEXT_FIELDS(ext) = (VALUE *)st_copy((st_table *)RCLASSEXT_FIELDS(orig));
rb_autoload_copy_table_for_namespace((st_table *)RCLASSEXT_FIELDS(ext), ns);
}
else {
RCLASSEXT_FIELDS(ext) = (VALUE *)st_init_numtable();
if (orig->fields_obj) {
RB_OBJ_WRITE(klass, &ext->fields_obj, rb_imemo_fields_clone(orig->fields_obj));
}
if (RCLASSEXT_SHARED_CONST_TBL(orig)) {
@ -344,9 +327,6 @@ rb_class_duplicate_classext(rb_classext_t *orig, VALUE klass, const rb_namespace
RCLASSEXT_CVC_TBL(ext) = duplicate_classext_id_table(RCLASSEXT_CVC_TBL(orig), dup_iclass);
// superclass_depth, superclasses
duplicate_classext_superclasses(orig, ext);
// subclasses, subclasses_index
duplicate_classext_subclasses(orig, ext);
@ -356,9 +336,9 @@ rb_class_duplicate_classext(rb_classext_t *orig, VALUE klass, const rb_namespace
* * refined_class
* * as.class.allocator / as.singleton_class.attached_object
* * includer
* * max IV count
* * variation count
*/
RCLASSEXT_MAX_IV_COUNT(ext) = RCLASSEXT_MAX_IV_COUNT(orig);
RCLASSEXT_VARIATION_COUNT(ext) = RCLASSEXT_VARIATION_COUNT(orig);
RCLASSEXT_PERMANENT_CLASSPATH(ext) = RCLASSEXT_PERMANENT_CLASSPATH(orig);
RCLASSEXT_CLONED(ext) = RCLASSEXT_CLONED(orig);
RCLASSEXT_CLASSPATH(ext) = RCLASSEXT_CLASSPATH(orig);
@ -378,6 +358,8 @@ rb_class_duplicate_classext(rb_classext_t *orig, VALUE klass, const rb_namespace
if (subclass_entry->klass && RB_TYPE_P(subclass_entry->klass, T_ICLASS)) {
iclass = subclass_entry->klass;
if (RBASIC_CLASS(iclass) == klass) {
// Is the subclass an ICLASS including this module into another class
// If so we need to re-associate it under our namespace with the new ext
class_duplicate_iclass_classext(iclass, ext, ns);
}
}
@ -412,7 +394,7 @@ class_classext_foreach_i(st_data_t key, st_data_t value, st_data_t arg)
void
rb_class_classext_foreach(VALUE klass, rb_class_classext_foreach_callback_func *func, void *arg)
{
st_table *tbl = RCLASS(klass)->ns_classext_tbl;
st_table *tbl = RCLASS_CLASSEXT_TBL(klass);
struct class_classext_foreach_arg foreach_arg;
if (tbl) {
foreach_arg.func = func;
@ -452,8 +434,7 @@ push_subclass_entry_to_list(VALUE super, VALUE klass, bool is_module)
entry = ZALLOC(rb_subclass_entry_t);
entry->klass = klass;
RB_VM_LOCK_ENTER();
{
RB_VM_LOCKING() {
anchor = RCLASS_WRITABLE_SUBCLASSES(super);
VM_ASSERT(anchor);
ns_subclasses = (rb_ns_subclasses_t *)anchor->ns_subclasses;
@ -470,7 +451,6 @@ push_subclass_entry_to_list(VALUE super, VALUE klass, bool is_module)
entry->prev = head;
st_insert(tbl, namespace_subclasses_tbl_key(ns), (st_data_t)entry);
}
RB_VM_LOCK_LEAVE();
if (is_module) {
RCLASS_WRITE_NS_MODULE_SUBCLASSES(klass, anchor->ns_subclasses);
@ -654,24 +634,30 @@ class_switch_superclass(VALUE super, VALUE klass)
}
/**
* Allocates a struct RClass for a new class.
* Allocates a struct RClass for a new class, iclass, or module.
*
* @param flags initial value for basic.flags of the returned class.
* @param klass the class of the returned class.
* @return an uninitialized Class object.
* @pre `klass` must refer `Class` class or an ancestor of Class.
* @pre `(flags | T_CLASS) != 0`
* @post the returned class can safely be `#initialize` 'd.
* @param type The type of the RClass (T_CLASS, T_ICLASS, or T_MODULE)
* @param klass value for basic.klass of the returned object.
* @return an uninitialized Class/IClass/Module object.
* @pre `klass` must refer to a class or module
*
* @note this function is not Class#allocate.
*/
static VALUE
class_alloc(VALUE flags, VALUE klass)
class_alloc0(enum ruby_value_type type, VALUE klass, bool namespaceable)
{
rb_ns_subclasses_t *ns_subclasses;
rb_subclass_anchor_t *anchor;
const rb_namespace_t *ns = rb_definition_namespace();
size_t alloc_size = sizeof(struct RClass) + sizeof(rb_classext_t);
if (!ruby_namespace_init_done) {
namespaceable = true;
}
size_t alloc_size = sizeof(struct RClass_and_rb_classext_t);
if (namespaceable) {
alloc_size = sizeof(struct RClass_namespaceable);
}
// class_alloc is supposed to return a new object that is not promoted yet.
// So, we need to avoid GC after NEWOBJ_OF.
@ -686,8 +672,12 @@ class_alloc(VALUE flags, VALUE klass)
anchor->ns_subclasses = ns_subclasses;
anchor->head = ZALLOC(rb_subclass_entry_t);
flags &= T_MASK;
RUBY_ASSERT(type == T_CLASS || type == T_ICLASS || type == T_MODULE);
VALUE flags = type;
if (RGENGC_WB_PROTECTED_CLASS) flags |= FL_WB_PROTECTED;
if (namespaceable) flags |= RCLASS_NAMESPACEABLE;
NEWOBJ_OF(obj, struct RClass, klass, flags, alloc_size, 0);
memset(RCLASS_EXT_PRIME(obj), 0, sizeof(rb_classext_t));
@ -702,17 +692,22 @@ class_alloc(VALUE flags, VALUE klass)
RCLASS_PRIME_NS((VALUE)obj) = ns;
// Classes/Modules defined in user namespaces are
// writable directly because it exists only in a namespace.
RCLASS_SET_PRIME_CLASSEXT_WRITABLE((VALUE)obj, NAMESPACE_USER_P(ns) ? true : false);
RCLASS_SET_PRIME_CLASSEXT_WRITABLE((VALUE)obj, !namespaceable || NAMESPACE_USER_P(ns));
RCLASS_SET_ORIGIN((VALUE)obj, (VALUE)obj);
RCLASS_SET_REFINED_CLASS((VALUE)obj, Qnil);
RCLASS_SET_ALLOCATOR((VALUE)obj, 0);
RCLASS_SET_SUBCLASSES((VALUE)obj, anchor);
return (VALUE)obj;
}
static VALUE
class_alloc(enum ruby_value_type type, VALUE klass)
{
return class_alloc0(type, klass, false);
}
static VALUE
class_associate_super(VALUE klass, VALUE super, bool init)
{
@ -748,6 +743,23 @@ class_clear_method_table(VALUE c)
RCLASS_WRITE_M_TBL_EVEN_WHEN_PROMOTED(c, rb_id_table_create(0));
}
static VALUE
class_boot_namespaceable(VALUE super, bool namespaceable)
{
VALUE klass = class_alloc0(T_CLASS, rb_cClass, namespaceable);
// initialize method table prior to class_associate_super()
// because class_associate_super() may cause GC and promote klass
class_initialize_method_table(klass);
class_associate_super(klass, super, true);
if (super && !UNDEF_P(super)) {
rb_class_set_initialized(klass);
}
return (VALUE)klass;
}
/**
* A utility function that wraps class_alloc.
*
@ -760,15 +772,7 @@ class_clear_method_table(VALUE c)
VALUE
rb_class_boot(VALUE super)
{
VALUE klass = class_alloc(T_CLASS, rb_cClass);
// initialize method table prior to class_associate_super()
// because class_associate_super() may cause GC and promote klass
class_initialize_method_table(klass);
class_associate_super(klass, super, true);
return (VALUE)klass;
return class_boot_namespaceable(super, false);
}
static VALUE *
@ -825,11 +829,11 @@ rb_class_update_superclasses(VALUE klass)
}
else {
superclasses = class_superclasses_including_self(super);
RCLASS_WRITE_SUPERCLASSES(super, super_depth, superclasses, true, true);
RCLASS_WRITE_SUPERCLASSES(super, super_depth, superclasses, true);
}
size_t depth = super_depth == RCLASS_MAX_SUPERCLASS_DEPTH ? super_depth : super_depth + 1;
RCLASS_WRITE_SUPERCLASSES(klass, depth, superclasses, false, false);
RCLASS_WRITE_SUPERCLASSES(klass, depth, superclasses, false);
}
void
@ -858,6 +862,8 @@ rb_class_new(VALUE super)
RCLASS_SET_MAX_IV_COUNT(klass, RCLASS_MAX_IV_COUNT(super));
}
RUBY_ASSERT(getenv("RUBY_NAMESPACE") || RCLASS_PRIME_CLASSEXT_WRITABLE_P(klass));
return klass;
}
@ -871,8 +877,7 @@ static void
clone_method(VALUE old_klass, VALUE new_klass, ID mid, const rb_method_entry_t *me)
{
if (me->def->type == VM_METHOD_TYPE_ISEQ) {
rb_cref_t *new_cref;
rb_vm_rewrite_cref(me->def->body.iseq.cref, old_klass, new_klass, &new_cref);
rb_cref_t *new_cref = rb_vm_rewrite_cref(me->def->body.iseq.cref, old_klass, new_klass);
rb_add_method_iseq(new_klass, mid, me->def->body.iseq.iseqptr, new_cref, METHOD_ENTRY_VISI(me));
}
else {
@ -922,7 +927,7 @@ class_init_copy_check(VALUE clone, VALUE orig)
if (orig == rb_cBasicObject) {
rb_raise(rb_eTypeError, "can't copy the root class");
}
if (RCLASS_SUPER(clone) != 0 || clone == rb_cBasicObject) {
if (RCLASS_INITIALIZED_P(clone)) {
rb_raise(rb_eTypeError, "already initialized class");
}
if (RCLASS_SINGLETON_P(orig)) {
@ -975,48 +980,33 @@ copy_tables(VALUE clone, VALUE orig)
rb_id_table_free(RCLASS_M_TBL(clone));
RCLASS_WRITE_M_TBL_EVEN_WHEN_PROMOTED(clone, 0);
if (!RB_TYPE_P(clone, T_ICLASS)) {
st_data_t id;
rb_fields_tbl_copy(clone, orig);
CONST_ID(id, "__tmp_classpath__");
rb_attr_delete(clone, id);
CONST_ID(id, "__classpath__");
rb_attr_delete(clone, id);
}
if (RCLASS_CONST_TBL(orig)) {
struct clone_const_arg arg;
struct rb_id_table *const_tbl;
arg.tbl = const_tbl = rb_id_table_create(0);
struct rb_id_table *orig_tbl = RCLASS_CONST_TBL(orig);
arg.tbl = const_tbl = rb_id_table_create(rb_id_table_size(orig_tbl));
arg.klass = clone;
rb_id_table_foreach(RCLASS_CONST_TBL(orig), clone_const_i, &arg);
rb_id_table_foreach(orig_tbl, clone_const_i, &arg);
RCLASS_WRITE_CONST_TBL(clone, const_tbl, false);
}
}
static bool ensure_origin(VALUE klass);
/**
* If this flag is set, that module is allocated but not initialized yet.
*/
enum {RMODULE_ALLOCATED_BUT_NOT_INITIALIZED = RUBY_FL_USER1};
static inline bool
RMODULE_UNINITIALIZED(VALUE module)
{
return FL_TEST_RAW(module, RMODULE_ALLOCATED_BUT_NOT_INITIALIZED);
}
void
rb_module_set_initialized(VALUE mod)
rb_class_set_initialized(VALUE klass)
{
FL_UNSET_RAW(mod, RMODULE_ALLOCATED_BUT_NOT_INITIALIZED);
RUBY_ASSERT(RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_MODULE));
FL_SET_RAW(klass, RCLASS_IS_INITIALIZED);
/* no more re-initialization */
}
void
rb_module_check_initializable(VALUE mod)
{
if (!RMODULE_UNINITIALIZED(mod)) {
if (RCLASS_INITIALIZED_P(mod)) {
rb_raise(rb_eTypeError, "already initialized module");
}
}
@ -1025,9 +1015,11 @@ rb_module_check_initializable(VALUE mod)
VALUE
rb_mod_init_copy(VALUE clone, VALUE orig)
{
/* Only class or module is valid here, but other classes may enter here and
* only hit an exception on the OBJ_INIT_COPY checks
*/
switch (BUILTIN_TYPE(clone)) {
case T_CLASS:
case T_ICLASS:
class_init_copy_check(clone, orig);
break;
case T_MODULE:
@ -1038,6 +1030,11 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
}
if (!OBJ_INIT_COPY(clone, orig)) return clone;
RUBY_ASSERT(RB_TYPE_P(orig, T_CLASS) || RB_TYPE_P(orig, T_MODULE));
RUBY_ASSERT(BUILTIN_TYPE(clone) == BUILTIN_TYPE(orig));
rb_class_set_initialized(clone);
/* cloned flag is refer at constant inline cache
* see vm_get_const_key_cref() in vm_insnhelper.c
*/
@ -1048,7 +1045,9 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
RBASIC_SET_CLASS(clone, rb_singleton_class_clone(orig));
rb_singleton_class_attached(METACLASS_OF(clone), (VALUE)clone);
}
if (BUILTIN_TYPE(clone) == T_CLASS) {
RCLASS_SET_ALLOCATOR(clone, RCLASS_ALLOCATOR(orig));
}
copy_tables(clone, orig);
if (RCLASS_M_TBL(orig)) {
struct clone_method_arg arg;
@ -1081,7 +1080,7 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
if (BUILTIN_TYPE(p) != T_ICLASS) {
rb_bug("non iclass between module/class and origin");
}
clone_p = class_alloc(RBASIC(p)->flags, METACLASS_OF(p));
clone_p = class_alloc(T_ICLASS, METACLASS_OF(p));
/* We should set the m_tbl right after allocation before anything
* that can trigger GC to avoid clone_p from becoming old and
* needing to fire write barriers. */
@ -1089,7 +1088,6 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
rb_class_set_super(prev_clone_p, clone_p);
prev_clone_p = clone_p;
RCLASS_SET_CONST_TBL(clone_p, RCLASS_CONST_TBL(p), false);
RCLASS_SET_ALLOCATOR(clone_p, RCLASS_ALLOCATOR(p));
if (RB_TYPE_P(clone, T_CLASS)) {
RCLASS_SET_INCLUDER(clone_p, clone);
}
@ -1159,7 +1157,8 @@ rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach)
else {
/* copy singleton(unnamed) class */
bool klass_of_clone_is_new;
VALUE clone = class_alloc(RBASIC(klass)->flags, 0);
RUBY_ASSERT(RB_TYPE_P(klass, T_CLASS));
VALUE clone = class_alloc(T_CLASS, 0);
if (BUILTIN_TYPE(obj) == T_CLASS) {
klass_of_clone_is_new = true;
@ -1265,7 +1264,7 @@ static inline VALUE
make_metaclass(VALUE klass)
{
VALUE super;
VALUE metaclass = rb_class_boot(Qundef);
VALUE metaclass = class_boot_namespaceable(Qundef, FL_TEST_RAW(klass, RCLASS_NAMESPACEABLE));
FL_SET(metaclass, FL_SINGLETON);
rb_singleton_class_attached(metaclass, klass);
@ -1283,6 +1282,7 @@ make_metaclass(VALUE klass)
super = RCLASS_SUPER(klass);
while (RB_TYPE_P(super, T_ICLASS)) super = RCLASS_SUPER(super);
class_associate_super(metaclass, super ? ENSURE_EIGENCLASS(super) : rb_cClass, true);
rb_class_set_initialized(klass);
// Full class ancestry may not have been filled until we reach here.
rb_class_update_superclasses(METACLASS_OF(metaclass));
@ -1300,7 +1300,7 @@ static inline VALUE
make_singleton_class(VALUE obj)
{
VALUE orig_class = METACLASS_OF(obj);
VALUE klass = rb_class_boot(orig_class);
VALUE klass = class_boot_namespaceable(orig_class, FL_TEST_RAW(orig_class, RCLASS_NAMESPACEABLE));
FL_SET(klass, FL_SINGLETON);
RBASIC_SET_CLASS(obj, klass);
@ -1565,7 +1565,6 @@ rb_module_s_alloc(VALUE klass)
{
VALUE mod = class_alloc(T_MODULE, klass);
class_initialize_method_table(mod);
FL_SET(mod, RMODULE_ALLOCATED_BUT_NOT_INITIALIZED);
return mod;
}
@ -1689,7 +1688,7 @@ ensure_includable(VALUE klass, VALUE module)
{
rb_class_modify_check(klass);
Check_Type(module, T_MODULE);
rb_module_set_initialized(module);
rb_class_set_initialized(module);
if (!NIL_P(rb_refinement_module_get_refined_class(module))) {
rb_raise(rb_eArgError, "refinement module is not allowed");
}
@ -2766,7 +2765,8 @@ rb_freeze_singleton_class(VALUE x)
if (!RCLASS_SINGLETON_P(x)) {
VALUE klass = RBASIC_CLASS(x);
if (klass && // no class when hidden from ObjectSpace
FL_TEST(klass, (FL_SINGLETON|FL_FREEZE)) == FL_SINGLETON) {
FL_TEST_RAW(klass, FL_SINGLETON) &&
!OBJ_FROZEN_RAW(klass)) {
OBJ_FREEZE(klass);
}
}

549
common.mk

File diff suppressed because it is too large Load diff

View file

@ -2178,13 +2178,14 @@ iseq_set_local_table(rb_iseq_t *iseq, const rb_ast_id_table_t *tbl, const NODE *
// then its local table should only be `...`
// FIXME: I think this should be fixed in the AST rather than special case here.
if (args->forwarding && args->pre_args_num == 0 && !args->opt_args) {
CHECK(size >= 3);
size -= 3;
offset += 3;
}
}
if (size > 0) {
ID *ids = (ID *)ALLOC_N(ID, size);
ID *ids = ALLOC_N(ID, size);
MEMCPY(ids, tbl->ids + offset, ID, size);
ISEQ_BODY(iseq)->local_table = ids;
}
@ -2481,7 +2482,7 @@ array_to_idlist(VALUE arr)
RUBY_ASSERT(RB_TYPE_P(arr, T_ARRAY));
long size = RARRAY_LEN(arr);
ID *ids = (ID *)ALLOC_N(ID, size + 1);
for (int i = 0; i < size; i++) {
for (long i = 0; i < size; i++) {
VALUE sym = RARRAY_AREF(arr, i);
ids[i] = SYM2ID(sym);
}
@ -3490,7 +3491,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
iobj->insn_id = BIN(opt_ary_freeze);
iobj->operand_size = 2;
iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE));
iobj->operands[0] = rb_cArray_empty_frozen;
RB_OBJ_WRITE(iseq, &iobj->operands[0], rb_cArray_empty_frozen);
iobj->operands[1] = (VALUE)ci;
ELEM_REMOVE(next);
}
@ -3513,7 +3514,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
iobj->insn_id = BIN(opt_hash_freeze);
iobj->operand_size = 2;
iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE));
iobj->operands[0] = rb_cHash_empty_frozen;
RB_OBJ_WRITE(iseq, &iobj->operands[0], rb_cHash_empty_frozen);
iobj->operands[1] = (VALUE)ci;
ELEM_REMOVE(next);
}
@ -4091,7 +4092,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
unsigned int flags = vm_ci_flag(ci);
if ((flags & set_flags) == set_flags && !(flags & unset_flags)) {
((INSN*)niobj)->insn_id = BIN(putobject);
OPERAND_AT(niobj, 0) = rb_hash_freeze(rb_hash_resurrect(OPERAND_AT(niobj, 0)));
RB_OBJ_WRITE(iseq, &OPERAND_AT(niobj, 0), rb_hash_freeze(rb_hash_resurrect(OPERAND_AT(niobj, 0))));
const struct rb_callinfo *nci = vm_ci_new(vm_ci_mid(ci),
flags & ~VM_CALL_KW_SPLAT_MUT, vm_ci_argc(ci), vm_ci_kwarg(ci));
@ -6640,6 +6641,14 @@ setup_args_dup_rest_p(const NODE *argn)
return false;
case NODE_COLON2:
return setup_args_dup_rest_p(RNODE_COLON2(argn)->nd_head);
case NODE_LIST:
while (argn) {
if (setup_args_dup_rest_p(RNODE_LIST(argn)->nd_head)) {
return true;
}
argn = RNODE_LIST(argn)->nd_next;
}
return false;
default:
return true;
}
@ -9254,12 +9263,13 @@ compile_builtin_mandatory_only_method(rb_iseq_t *iseq, const NODE *node, const N
VALUE ast_value = rb_ruby_ast_new(RNODE(&scope_node));
ISEQ_BODY(iseq)->mandatory_only_iseq =
const rb_iseq_t *mandatory_only_iseq =
rb_iseq_new_with_opt(ast_value, rb_iseq_base_label(iseq),
rb_iseq_path(iseq), rb_iseq_realpath(iseq),
nd_line(line_node), NULL, 0,
ISEQ_TYPE_METHOD, ISEQ_COMPILE_DATA(iseq)->option,
ISEQ_BODY(iseq)->variable.script_lines);
RB_OBJ_WRITE(iseq, &ISEQ_BODY(iseq)->mandatory_only_iseq, (VALUE)mandatory_only_iseq);
ALLOCV_END(idtmp);
return COMPILE_OK;
@ -9530,7 +9540,8 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co
ADD_LABEL(ret, not_basic_new_finish);
ADD_INSN(ret, line_node, pop);
} else {
}
else {
ADD_SEND_R(ret, line_node, mid, argc, parent_block, INT2FIX(flag), keywords);
}
@ -12589,8 +12600,13 @@ static ibf_offset_t
ibf_dump_write(struct ibf_dump *dump, const void *buff, unsigned long size)
{
ibf_offset_t pos = ibf_dump_pos(dump);
#if SIZEOF_LONG > SIZEOF_INT
/* ensure the resulting dump does not exceed UINT_MAX */
if (size >= UINT_MAX || pos + size >= UINT_MAX) {
rb_raise(rb_eRuntimeError, "dump size exceeds");
}
#endif
rb_str_cat(dump->current_buffer->str, (const char *)buff, size);
/* TODO: overflow check */
return pos;
}
@ -13283,12 +13299,13 @@ ibf_dump_catch_table(struct ibf_dump *dump, const rb_iseq_t *iseq)
}
}
static struct iseq_catch_table *
ibf_load_catch_table(const struct ibf_load *load, ibf_offset_t catch_table_offset, unsigned int size)
static void
ibf_load_catch_table(const struct ibf_load *load, ibf_offset_t catch_table_offset, unsigned int size, const rb_iseq_t *parent_iseq)
{
if (size) {
struct iseq_catch_table *table = ruby_xmalloc(iseq_catch_table_bytes(size));
struct iseq_catch_table *table = ruby_xcalloc(1, iseq_catch_table_bytes(size));
table->size = size;
ISEQ_BODY(parent_iseq)->catch_table = table;
ibf_offset_t reading_pos = catch_table_offset;
@ -13301,12 +13318,12 @@ ibf_load_catch_table(const struct ibf_load *load, ibf_offset_t catch_table_offse
table->entries[i].cont = (unsigned int)ibf_load_small_value(load, &reading_pos);
table->entries[i].sp = (unsigned int)ibf_load_small_value(load, &reading_pos);
table->entries[i].iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)iseq_index);
rb_iseq_t *catch_iseq = (rb_iseq_t *)ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)iseq_index);
RB_OBJ_WRITE(parent_iseq, UNALIGNED_MEMBER_PTR(&table->entries[i], iseq), catch_iseq);
}
return table;
}
else {
return NULL;
ISEQ_BODY(parent_iseq)->catch_table = NULL;
}
}
@ -13377,6 +13394,14 @@ outer_variable_cmp(const void *a, const void *b, void *arg)
{
const struct outer_variable_pair *ap = (const struct outer_variable_pair *)a;
const struct outer_variable_pair *bp = (const struct outer_variable_pair *)b;
if (!ap->name) {
return -1;
}
else if (!bp->name) {
return 1;
}
return rb_str_cmp(ap->name, bp->name);
}
@ -13811,10 +13836,15 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
load_body->insns_info.body = ibf_load_insns_info_body(load, insns_info_body_offset, insns_info_size);
load_body->insns_info.positions = ibf_load_insns_info_positions(load, insns_info_positions_offset, insns_info_size);
load_body->local_table = ibf_load_local_table(load, local_table_offset, local_table_size);
load_body->catch_table = ibf_load_catch_table(load, catch_table_offset, catch_table_size);
load_body->parent_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)parent_iseq_index);
load_body->local_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)local_iseq_index);
load_body->mandatory_only_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)mandatory_only_iseq_index);
ibf_load_catch_table(load, catch_table_offset, catch_table_size, iseq);
const rb_iseq_t *parent_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)parent_iseq_index);
const rb_iseq_t *local_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)local_iseq_index);
const rb_iseq_t *mandatory_only_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)mandatory_only_iseq_index);
RB_OBJ_WRITE(iseq, &load_body->parent_iseq, parent_iseq);
RB_OBJ_WRITE(iseq, &load_body->local_iseq, local_iseq);
RB_OBJ_WRITE(iseq, &load_body->mandatory_only_iseq, mandatory_only_iseq);
// This must be done after the local table is loaded.
if (load_body->param.keyword != NULL) {
@ -14430,7 +14460,7 @@ ibf_dump_object_object(struct ibf_dump *dump, VALUE obj)
else {
obj_header.internal = SPECIAL_CONST_P(obj) ? FALSE : (RBASIC_CLASS(obj) == 0) ? TRUE : FALSE;
obj_header.special_const = FALSE;
obj_header.frozen = FL_TEST(obj, FL_FREEZE) ? TRUE : FALSE;
obj_header.frozen = OBJ_FROZEN(obj) ? TRUE : FALSE;
ibf_dump_object_object_header(dump, obj_header);
(*dump_object_functions[obj_header.type])(dump, obj);
}

View file

@ -1925,21 +1925,6 @@ nucomp_to_c(VALUE self)
return self;
}
/*
* call-seq:
* to_c -> (0+0i)
*
* Returns zero as a Complex:
*
* nil.to_c # => (0+0i)
*
*/
static VALUE
nilclass_to_c(VALUE self)
{
return rb_complex_new1(INT2FIX(0));
}
/*
* call-seq:
* to_c -> complex
@ -2693,7 +2678,6 @@ Init_Complex(void)
rb_define_method(rb_cComplex, "to_r", nucomp_to_r, 0);
rb_define_method(rb_cComplex, "rationalize", nucomp_rationalize, -1);
rb_define_method(rb_cComplex, "to_c", nucomp_to_c, 0);
rb_define_method(rb_cNilClass, "to_c", nilclass_to_c, 0);
rb_define_method(rb_cNumeric, "to_c", numeric_to_c, 0);
rb_define_method(rb_cString, "to_c", string_to_c, 0);

325
concurrent_set.c Normal file
View file

@ -0,0 +1,325 @@
#include "internal.h"
#include "internal/gc.h"
#include "internal/concurrent_set.h"
#include "ruby_atomic.h"
#include "ruby/atomic.h"
#include "vm_sync.h"
enum concurrent_set_special_values {
CONCURRENT_SET_EMPTY,
CONCURRENT_SET_DELETED,
CONCURRENT_SET_MOVED,
CONCURRENT_SET_SPECIAL_VALUE_COUNT
};
struct concurrent_set_entry {
VALUE hash;
VALUE key;
};
struct concurrent_set {
rb_atomic_t size;
unsigned int capacity;
unsigned int deleted_entries;
const struct rb_concurrent_set_funcs *funcs;
struct concurrent_set_entry *entries;
};
static void
concurrent_set_free(void *ptr)
{
struct concurrent_set *set = ptr;
xfree(set->entries);
}
static size_t
concurrent_set_size(const void *ptr)
{
const struct concurrent_set *set = ptr;
return sizeof(struct concurrent_set) +
(set->capacity * sizeof(struct concurrent_set_entry));
}
static const rb_data_type_t concurrent_set_type = {
.wrap_struct_name = "VM/concurrent_set",
.function = {
.dmark = NULL,
.dfree = concurrent_set_free,
.dsize = concurrent_set_size,
},
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
};
VALUE
rb_concurrent_set_new(const struct rb_concurrent_set_funcs *funcs, int capacity)
{
struct concurrent_set *set;
VALUE obj = TypedData_Make_Struct(0, struct concurrent_set, &concurrent_set_type, set);
set->funcs = funcs;
set->entries = ZALLOC_N(struct concurrent_set_entry, capacity);
set->capacity = capacity;
return obj;
}
struct concurrent_set_probe {
int idx;
int d;
int mask;
};
static int
concurrent_set_probe_start(struct concurrent_set_probe *probe, struct concurrent_set *set, VALUE hash)
{
RUBY_ASSERT((set->capacity & (set->capacity - 1)) == 0);
probe->d = 0;
probe->mask = set->capacity - 1;
probe->idx = hash & probe->mask;
return probe->idx;
}
static int
concurrent_set_probe_next(struct concurrent_set_probe *probe)
{
probe->d++;
probe->idx = (probe->idx + probe->d) & probe->mask;
return probe->idx;
}
static void
concurrent_set_try_resize_without_locking(VALUE old_set_obj, VALUE *set_obj_ptr)
{
// Check if another thread has already resized.
if (RUBY_ATOMIC_VALUE_LOAD(*set_obj_ptr) != old_set_obj) {
return;
}
struct concurrent_set *old_set = RTYPEDDATA_GET_DATA(old_set_obj);
// This may overcount by up to the number of threads concurrently attempting to insert
// GC may also happen between now and the set being rebuilt
int expected_size = RUBY_ATOMIC_LOAD(old_set->size) - old_set->deleted_entries;
struct concurrent_set_entry *old_entries = old_set->entries;
int old_capacity = old_set->capacity;
int new_capacity = old_capacity * 2;
if (new_capacity > expected_size * 8) {
new_capacity = old_capacity / 2;
}
else if (new_capacity > expected_size * 4) {
new_capacity = old_capacity;
}
// May cause GC and therefore deletes, so must hapen first.
VALUE new_set_obj = rb_concurrent_set_new(old_set->funcs, new_capacity);
struct concurrent_set *new_set = RTYPEDDATA_GET_DATA(new_set_obj);
for (int i = 0; i < old_capacity; i++) {
struct concurrent_set_entry *entry = &old_entries[i];
VALUE key = RUBY_ATOMIC_VALUE_EXCHANGE(entry->key, CONCURRENT_SET_MOVED);
RUBY_ASSERT(key != CONCURRENT_SET_MOVED);
if (key < CONCURRENT_SET_SPECIAL_VALUE_COUNT) continue;
if (rb_objspace_garbage_object_p(key)) continue;
VALUE hash = RUBY_ATOMIC_VALUE_LOAD(entry->hash);
if (hash == 0) {
// Either in-progress insert or extremely unlikely 0 hash.
// Re-calculate the hash.
hash = old_set->funcs->hash(key);
}
RUBY_ASSERT(hash == old_set->funcs->hash(key));
// Insert key into new_set.
struct concurrent_set_probe probe;
int idx = concurrent_set_probe_start(&probe, new_set, hash);
while (true) {
struct concurrent_set_entry *entry = &new_set->entries[idx];
if (entry->key == CONCURRENT_SET_EMPTY) {
new_set->size++;
RUBY_ASSERT(new_set->size < new_set->capacity / 2);
RUBY_ASSERT(entry->hash == 0);
entry->key = key;
entry->hash = hash;
break;
}
else {
RUBY_ASSERT(entry->key >= CONCURRENT_SET_SPECIAL_VALUE_COUNT);
}
idx = concurrent_set_probe_next(&probe);
}
}
RUBY_ATOMIC_VALUE_SET(*set_obj_ptr, new_set_obj);
RB_GC_GUARD(old_set_obj);
}
static void
concurrent_set_try_resize(VALUE old_set_obj, VALUE *set_obj_ptr)
{
RB_VM_LOCKING() {
concurrent_set_try_resize_without_locking(old_set_obj, set_obj_ptr);
}
}
VALUE
rb_concurrent_set_find_or_insert(VALUE *set_obj_ptr, VALUE key, void *data)
{
RUBY_ASSERT(key >= CONCURRENT_SET_SPECIAL_VALUE_COUNT);
bool inserting = false;
VALUE set_obj;
retry:
set_obj = RUBY_ATOMIC_VALUE_LOAD(*set_obj_ptr);
RUBY_ASSERT(set_obj);
struct concurrent_set *set = RTYPEDDATA_GET_DATA(set_obj);
struct concurrent_set_probe probe;
VALUE hash = set->funcs->hash(key);
int idx = concurrent_set_probe_start(&probe, set, hash);
while (true) {
struct concurrent_set_entry *entry = &set->entries[idx];
VALUE curr_key = RUBY_ATOMIC_VALUE_LOAD(entry->key);
switch (curr_key) {
case CONCURRENT_SET_EMPTY: {
// Not in set
if (!inserting) {
key = set->funcs->create(key, data);
RUBY_ASSERT(hash == set->funcs->hash(key));
inserting = true;
}
rb_atomic_t prev_size = RUBY_ATOMIC_FETCH_ADD(set->size, 1);
if (UNLIKELY(prev_size > set->capacity / 2)) {
concurrent_set_try_resize(set_obj, set_obj_ptr);
goto retry;
}
curr_key = RUBY_ATOMIC_VALUE_CAS(entry->key, CONCURRENT_SET_EMPTY, key);
if (curr_key == CONCURRENT_SET_EMPTY) {
RUBY_ATOMIC_VALUE_SET(entry->hash, hash);
RB_GC_GUARD(set_obj);
return key;
}
else {
// Entry was not inserted.
RUBY_ATOMIC_DEC(set->size);
// Another thread won the race, try again at the same location.
continue;
}
}
case CONCURRENT_SET_DELETED:
break;
case CONCURRENT_SET_MOVED:
// Wait
RB_VM_LOCKING();
goto retry;
default: {
VALUE curr_hash = RUBY_ATOMIC_VALUE_LOAD(entry->hash);
if ((curr_hash == hash || curr_hash == 0) && set->funcs->cmp(key, curr_key)) {
// We've found a match.
if (UNLIKELY(rb_objspace_garbage_object_p(curr_key))) {
// This is a weakref set, so after marking but before sweeping is complete we may find a matching garbage object.
// Skip it and mark it as deleted.
RUBY_ATOMIC_VALUE_CAS(entry->key, curr_key, CONCURRENT_SET_DELETED);
// Fall through and continue our search.
}
else {
RB_GC_GUARD(set_obj);
return curr_key;
}
}
break;
}
}
idx = concurrent_set_probe_next(&probe);
}
}
VALUE
rb_concurrent_set_delete_by_identity(VALUE set_obj, VALUE key)
{
// Assume locking and barrier (which there is no assert for).
ASSERT_vm_locking();
struct concurrent_set *set = RTYPEDDATA_GET_DATA(set_obj);
VALUE hash = set->funcs->hash(key);
struct concurrent_set_probe probe;
int idx = concurrent_set_probe_start(&probe, set, hash);
while (true) {
struct concurrent_set_entry *entry = &set->entries[idx];
VALUE curr_key = RUBY_ATOMIC_VALUE_LOAD(entry->key);
switch (curr_key) {
case CONCURRENT_SET_EMPTY:
// We didn't find our entry to delete.
return 0;
case CONCURRENT_SET_DELETED:
break;
case CONCURRENT_SET_MOVED:
rb_bug("rb_concurrent_set_delete_by_identity: moved entry");
break;
default:
if (key == curr_key) {
entry->key = CONCURRENT_SET_DELETED;
set->deleted_entries++;
return curr_key;
}
break;
}
idx = concurrent_set_probe_next(&probe);
}
}
void
rb_concurrent_set_foreach_with_replace(VALUE set_obj, int (*callback)(VALUE *key, void *data), void *data)
{
// Assume locking and barrier (which there is no assert for).
ASSERT_vm_locking();
struct concurrent_set *set = RTYPEDDATA_GET_DATA(set_obj);
for (unsigned int i = 0; i < set->capacity; i++) {
VALUE key = set->entries[i].key;
switch (key) {
case CONCURRENT_SET_EMPTY:
case CONCURRENT_SET_DELETED:
continue;
case CONCURRENT_SET_MOVED:
rb_bug("rb_concurrent_set_foreach_with_replace: moved entry");
break;
default: {
int ret = callback(&set->entries[i].key, data);
switch (ret) {
case ST_STOP:
return;
case ST_DELETE:
set->entries[i].key = CONCURRENT_SET_DELETED;
break;
}
break;
}
}
}
}

View file

@ -1006,7 +1006,7 @@ AS_IF([test "x$OPT_DIR" != x], [
save_IFS="$IFS" IFS="$PATH_SEPARATOR" val= PWD=
for dir in $OPT_DIR; do
test -z "$dir" && continue
dir=`eval $CHDIR '"$dir"' && pwd` || continue
dir=`eval $CHDIR '"$dir"' 2>/dev/null && pwd` || continue
val="${val:+$val$PATH_SEPARATOR}$dir"
done
IFS="$save_IFS" OPT_DIR="$val"
@ -1743,6 +1743,18 @@ AS_IF([test "$GCC" = yes], [
[rb_cv_gcc_atomic_builtins=no])])
AS_IF([test "$rb_cv_gcc_atomic_builtins" = yes], [
AC_DEFINE(HAVE_GCC_ATOMIC_BUILTINS)
AC_CACHE_CHECK([for 64bit __atomic builtins], [rb_cv_gcc_atomic_builtins_64], [
AC_LINK_IFELSE([AC_LANG_PROGRAM([[@%:@include <stdint.h>
uint64_t atomic_var;]],
[[
__atomic_load_n(&atomic_var, __ATOMIC_RELAXED);
__atomic_store_n(&atomic_var, 0, __ATOMIC_RELAXED);
]])],
[rb_cv_gcc_atomic_builtins_64=yes],
[rb_cv_gcc_atomic_builtins_64=no])])
AS_IF([test "$rb_cv_gcc_atomic_builtins_64" = yes], [
AC_DEFINE(HAVE_GCC_ATOMIC_BUILTINS_64)
])
])
AC_CACHE_CHECK([for __sync builtins], [rb_cv_gcc_sync_builtins], [
@ -3924,46 +3936,33 @@ AC_ARG_ENABLE(yjit,
CARGO=
CARGO_BUILD_ARGS=
YJIT_LIBS=
JIT_CARGO_SUPPORT=no
AS_CASE(["${YJIT_SUPPORT}"],
[yes|dev|stats|dev_nodebug], [
AS_IF([test x"$RUSTC" = "xno"],
AC_MSG_ERROR([rustc is required. Installation instructions available at https://www.rust-lang.org/tools/install])
)
AS_IF([test x"$ZJIT_SUPPORT" != "xno"],
AC_MSG_ERROR([YJIT cannot be enabled when ZJIT is enabled])
)
AS_CASE(["${YJIT_SUPPORT}"],
[yes], [
rb_rust_target_subdir=release
],
[dev], [
rb_rust_target_subdir=debug
CARGO_BUILD_ARGS='--features disasm,runtime_checks'
rb_cargo_features='disasm,runtime_checks'
JIT_CARGO_SUPPORT=dev
AC_DEFINE(RUBY_DEBUG, 1)
],
[dev_nodebug], [
rb_rust_target_subdir=dev_nodebug
CARGO_BUILD_ARGS='--profile dev_nodebug --features disasm'
rb_cargo_features='disasm'
JIT_CARGO_SUPPORT=dev_nodebug
AC_DEFINE(YJIT_STATS, 1)
],
[stats], [
rb_rust_target_subdir=stats
CARGO_BUILD_ARGS='--profile stats'
JIT_CARGO_SUPPORT=stats
AC_DEFINE(YJIT_STATS, 1)
])
AS_IF([test -n "${CARGO_BUILD_ARGS}"], [
AC_CHECK_TOOL(CARGO, [cargo], [no])
AS_IF([test x"$CARGO" = "xno"],
AC_MSG_ERROR([cargo is required. Installation instructions available at https://www.rust-lang.org/tools/install])
]))
YJIT_LIBS="yjit/target/${rb_rust_target_subdir}/libyjit.a"
AS_CASE(["$target_os"],[openbsd*],[
# Link libc++abi (which requires libpthread) for _Unwind_* functions needed by yjit
LDFLAGS="$LDFLAGS -lpthread -lc++abi"
])
YJIT_LIBS="target/release/libyjit.a"
RUST_LIB='$(YJIT_LIBS)'
YJIT_OBJ='yjit.$(OBJEXT)'
JIT_OBJ='jit.$(OBJEXT)'
AS_IF([test x"$YJIT_SUPPORT" != "xyes" ], [
@ -3974,38 +3973,28 @@ AS_CASE(["${YJIT_SUPPORT}"],
AC_DEFINE(USE_YJIT, 0)
])
ZJIT_CARGO_BUILD_ARGS=
ZJIT_LIBS=
AS_CASE(["${ZJIT_SUPPORT}"],
[yes|dev], [
[yes|dev|dev_nodebug], [
AS_IF([test x"$RUSTC" = "xno"],
AC_MSG_ERROR([rustc is required. Installation instructions available at https://www.rust-lang.org/tools/install])
)
AS_IF([test x"$YJIT_SUPPORT" != "xno"],
AC_MSG_ERROR([ZJIT cannot be enabled when YJIT is enabled])
)
AS_CASE(["${ZJIT_SUPPORT}"],
[yes], [
rb_rust_target_subdir=release
],
[dev], [
rb_rust_target_subdir=debug
ZJIT_CARGO_BUILD_ARGS='--profile dev --features disasm'
rb_cargo_features="$rb_cargo_features,disasm"
JIT_CARGO_SUPPORT=dev
AC_DEFINE(RUBY_DEBUG, 1)
],
[dev_nodebug], [
rb_cargo_features="$rb_cargo_features,disasm"
JIT_CARGO_SUPPORT=dev_nodebug
])
AS_IF([test -n "${ZJIT_CARGO_BUILD_ARGS}"], [
AC_CHECK_TOOL(CARGO, [cargo], [no])
AS_IF([test x"$CARGO" = "xno"],
AC_MSG_ERROR([cargo is required. Installation instructions available at https://www.rust-lang.org/tools/install])
]))
ZJIT_LIBS="zjit/target/${rb_rust_target_subdir}/libzjit.a"
AS_CASE(["$target_os"],[openbsd*],[
# Link libc++abi (which requires libpthread) for _Unwind_* functions needed by yjit
LDFLAGS="$LDFLAGS -lpthread -lc++abi"
])
ZJIT_LIBS="target/release/libzjit.a"
RUST_LIB='$(ZJIT_LIBS)'
ZJIT_OBJ='zjit.$(OBJEXT)'
JIT_OBJ='jit.$(OBJEXT)'
AS_IF([test x"$ZJIT_SUPPORT" != "xyes" ], [
@ -4016,18 +4005,63 @@ AS_CASE(["${ZJIT_SUPPORT}"],
AC_DEFINE(USE_ZJIT, 0)
])
# if YJIT+ZJIT release build, or any build that requires Cargo
AS_IF([test x"$JIT_CARGO_SUPPORT" != "xno" -o \( x"$YJIT_SUPPORT" != "xno" -a x"$ZJIT_SUPPORT" != "xno" \)], [
AC_CHECK_TOOL(CARGO, [cargo], [no])
AS_IF([test x"$CARGO" = "xno"],
AC_MSG_ERROR([cargo is required. Installation instructions available at https://www.rust-lang.org/tools/install]))
YJIT_LIBS=
ZJIT_LIBS=
# There's more processing below to get the feature set for the
# top-level crate, so capture at this point for feature set of
# just the zjit crate.
ZJIT_TEST_FEATURES="${rb_cargo_features}"
AS_IF([test x"${YJIT_SUPPORT}" != x"no"], [
rb_cargo_features="$rb_cargo_features,yjit"
])
AS_IF([test x"${ZJIT_SUPPORT}" != x"no"], [
AC_SUBST(ZJIT_TEST_FEATURES)
rb_cargo_features="$rb_cargo_features,zjit"
])
# if YJIT and ZJIT release mode
AS_IF([test "${YJIT_SUPPORT}:${ZJIT_SUPPORT}" = "yes:yes"], [
JIT_CARGO_SUPPORT=release
])
CARGO_BUILD_ARGS="--profile ${JIT_CARGO_SUPPORT} --features ${rb_cargo_features}"
AS_IF([test "${JIT_CARGO_SUPPORT}" = "dev"], [
RUST_LIB="target/debug/libjit.a"
], [
RUST_LIB="target/${JIT_CARGO_SUPPORT}/libjit.a"
])
])
# In case either we're linking rust code
AS_IF([test -n "$RUST_LIB"], [
AS_CASE(["$target_os"],[openbsd*],[
# Link libc++abi (which requires libpthread) for _Unwind_* functions needed by rust stdlib
LDFLAGS="$LDFLAGS -lpthread -lc++abi"
])
# absolute path to stop the "target" dir in src dir from interfering through VPATH
RUST_LIB="$(pwd)/${RUST_LIB}"
])
dnl These variables end up in ::RbConfig::CONFIG
AC_SUBST(YJIT_SUPPORT)dnl what flavor of YJIT the Ruby build includes
AC_SUBST(RUSTC)dnl Rust compiler command
AC_SUBST(CARGO)dnl Cargo command for Rust builds
AC_SUBST(CARGO_BUILD_ARGS)dnl for selecting Rust build profiles
AC_SUBST(ZJIT_CARGO_BUILD_ARGS)dnl for selecting Rust build profiles
AC_SUBST(YJIT_LIBS)dnl for optionally building the Rust parts of YJIT
AC_SUBST(YJIT_SUPPORT)dnl what flavor of YJIT the Ruby build includes
AC_SUBST(YJIT_LIBS)dnl the .a library of YJIT
AC_SUBST(YJIT_OBJ)dnl for optionally building the C parts of YJIT
AC_SUBST(ZJIT_SUPPORT)dnl what flavor of ZJIT the Ruby build includes
AC_SUBST(ZJIT_LIBS)dnl for optionally building the Rust parts of ZJIT
AC_SUBST(ZJIT_LIBS)dnl path to the .a library of ZJIT
AC_SUBST(ZJIT_OBJ)dnl for optionally building the C parts of ZJIT
AC_SUBST(JIT_OBJ)dnl for optionally building C glue code for Rust FFI
AC_SUBST(RUST_LIB)dnl path to the rust .a library that contains either or both JITs
AC_SUBST(JIT_CARGO_SUPPORT)dnl "no" or the cargo profile of the rust code
}
[begin]_group "build section" && {

19
cont.c
View file

@ -508,6 +508,9 @@ fiber_pool_allocate_memory(size_t * count, size_t stride)
// @sa fiber_pool_allocation_free
static struct fiber_pool_allocation *
fiber_pool_expand(struct fiber_pool * fiber_pool, size_t count)
{
struct fiber_pool_allocation * allocation;
RB_VM_LOCK_ENTER();
{
STACK_GROW_DIR_DETECTION;
@ -522,7 +525,7 @@ fiber_pool_expand(struct fiber_pool * fiber_pool, size_t count)
}
struct fiber_pool_vacancy * vacancies = fiber_pool->vacancies;
struct fiber_pool_allocation * allocation = RB_ALLOC(struct fiber_pool_allocation);
allocation = RB_ALLOC(struct fiber_pool_allocation);
// Initialize fiber pool allocation:
allocation->base = base;
@ -543,7 +546,6 @@ fiber_pool_expand(struct fiber_pool * fiber_pool, size_t count)
for (size_t i = 0; i < count; i += 1) {
void * base = (char*)allocation->base + (stride * i);
void * page = (char*)base + STACK_DIR_UPPER(size, 0);
#if defined(_WIN32)
DWORD old_protect;
@ -586,6 +588,8 @@ fiber_pool_expand(struct fiber_pool * fiber_pool, size_t count)
fiber_pool->allocations = allocation;
fiber_pool->vacancies = vacancies;
fiber_pool->count += count;
}
RB_VM_LOCK_LEAVE();
return allocation;
}
@ -659,7 +663,10 @@ fiber_pool_allocation_free(struct fiber_pool_allocation * allocation)
static struct fiber_pool_stack
fiber_pool_stack_acquire(struct fiber_pool * fiber_pool)
{
struct fiber_pool_vacancy * vacancy = fiber_pool_vacancy_pop(fiber_pool);
struct fiber_pool_vacancy * vacancy ;
RB_VM_LOCK_ENTER();
{
vacancy = fiber_pool_vacancy_pop(fiber_pool);
if (DEBUG) fprintf(stderr, "fiber_pool_stack_acquire: %p used=%"PRIuSIZE"\n", (void*)fiber_pool->vacancies, fiber_pool->used);
@ -694,6 +701,8 @@ fiber_pool_stack_acquire(struct fiber_pool * fiber_pool)
#endif
fiber_pool_stack_reset(&vacancy->stack);
}
RB_VM_LOCK_LEAVE();
return vacancy->stack;
}
@ -764,6 +773,8 @@ static void
fiber_pool_stack_release(struct fiber_pool_stack * stack)
{
struct fiber_pool * pool = stack->pool;
RB_VM_LOCK_ENTER();
{
struct fiber_pool_vacancy * vacancy = fiber_pool_vacancy_pointer(stack->base, stack->size);
if (DEBUG) fprintf(stderr, "fiber_pool_stack_release: %p used=%"PRIuSIZE"\n", stack->base, stack->pool->used);
@ -799,6 +810,8 @@ fiber_pool_stack_release(struct fiber_pool_stack * stack)
}
#endif
}
RB_VM_LOCK_LEAVE();
}
static inline void
ec_switch(rb_thread_t *th, rb_fiber_t *fiber)

View file

@ -315,6 +315,7 @@ RB_DEBUG_COUNTER(obj_imemo_parser_strterm)
RB_DEBUG_COUNTER(obj_imemo_callinfo)
RB_DEBUG_COUNTER(obj_imemo_callcache)
RB_DEBUG_COUNTER(obj_imemo_constcache)
RB_DEBUG_COUNTER(obj_imemo_fields)
RB_DEBUG_COUNTER(opt_new_hit)
RB_DEBUG_COUNTER(opt_new_miss)

View file

@ -365,7 +365,7 @@ $(srcdir)/.bundle/.timestamp:
define build-gem
$(srcdir)/gems/src/$(1)/.git: | $(srcdir)/gems/src
$(ECHO) Cloning $(4)
$(Q) $(GIT) clone $(4) $$(@D)
$(Q) $(GIT) clone --depth=1 --no-tags $(4) $$(@D)
$(bundled-gem-revision): \
$(if $(if $(wildcard $$(@)),$(filter $(3),$(shell cat $$(@)))),,PHONY) \
@ -443,6 +443,7 @@ endif
include $(top_srcdir)/yjit/yjit.mk
include $(top_srcdir)/zjit/zjit.mk
include $(top_srcdir)/defs/jit.mk
# Query on the generated rdoc
#

View file

@ -63,6 +63,8 @@ firstline, predefined = __LINE__+1, %[\
pack
buffer
include?
aborted
exited
_ UScore

64
defs/jit.mk Normal file
View file

@ -0,0 +1,64 @@
# Make recipes that deal with the rust code of YJIT and ZJIT.
# Because of Cargo cache, if the actual binary is not changed from the
# previous build, the mtime is preserved as the cached file.
# This means the target is not updated actually, and it will need to
# rebuild at the next build.
RUST_LIB_TOUCH = touch $@
ifneq ($(JIT_CARGO_SUPPORT),no)
# Show Cargo progress when doing `make V=1`
CARGO_VERBOSE_0 = -q
CARGO_VERBOSE_1 =
CARGO_VERBOSE = $(CARGO_VERBOSE_$(V))
# NOTE: MACOSX_DEPLOYMENT_TARGET to match `rustc --print deployment-target` to avoid the warning below.
# ld: warning: object file (target/debug/libjit.a(<libcapstone object>)) was built for
# newer macOS version (15.2) than being linked (15.0)
# This limits us to an older set of macOS API in the rust code, but we don't use any.
$(RUST_LIB): $(srcdir)/jit.rs
$(Q)if [ '$(ZJIT_SUPPORT)' != no -a '$(YJIT_SUPPORT)' != no ]; then \
echo 'building YJIT and ZJIT ($(JIT_CARGO_SUPPORT:yes=release) mode)'; \
elif [ '$(ZJIT_SUPPORT)' != no ]; then \
echo 'building ZJIT ($(JIT_CARGO_SUPPORT) mode)'; \
elif [ '$(YJIT_SUPPORT)' != no ]; then \
echo 'building YJIT ($(JIT_CARGO_SUPPORT) mode)'; \
fi
+$(Q)CARGO_TARGET_DIR='$(CARGO_TARGET_DIR)' \
CARGO_TERM_PROGRESS_WHEN='never' \
MACOSX_DEPLOYMENT_TARGET=11.0 \
$(CARGO) $(CARGO_VERBOSE) build --manifest-path '$(top_srcdir)/Cargo.toml' $(CARGO_BUILD_ARGS)
$(RUST_LIB_TOUCH)
endif
RUST_LIB_SYMBOLS = $(RUST_LIB:.a=).symbols
$(RUST_LIBOBJ): $(RUST_LIB)
$(ECHO) 'partial linking $(RUST_LIB) into $@'
ifneq ($(findstring darwin,$(target_os)),)
$(Q) $(CC) -nodefaultlibs -r -o $@ -exported_symbols_list $(RUST_LIB_SYMBOLS) $(RUST_LIB)
else
$(Q) $(LD) -r -o $@ --whole-archive $(RUST_LIB)
-$(Q) $(OBJCOPY) --wildcard --keep-global-symbol='$(SYMBOL_PREFIX)rb_*' $(@)
endif
rust-libobj: $(RUST_LIBOBJ)
rust-lib: $(RUST_LIB)
# For Darwin only: a list of symbols that we want the glommed Rust static lib to export.
# Unfortunately, using wildcard like '_rb_*' with -exported-symbol does not work, at least
# not on version 820.1. Assume llvm-nm, so XCode 8.0 (from 2016) or newer.
#
# The -exported_symbols_list pulls out the right archive members. Symbols not listed
# in the list are made private extern, which are in turn made local as we're using `ld -r`.
# Note, section about -keep_private_externs in ld's man page hints at this behavior on which
# we rely.
ifneq ($(findstring darwin,$(target_os)),)
$(RUST_LIB_SYMBOLS): $(RUST_LIB)
$(Q) $(tooldir)/darwin-ar $(NM) --defined-only --extern-only $(RUST_LIB) | \
sed -n -e 's/.* //' -e '/^$(SYMBOL_PREFIX)rb_/p' \
-e '/^$(SYMBOL_PREFIX)rust_eh_personality/p' \
> $@
$(RUST_LIBOBJ): $(RUST_LIB_SYMBOLS)
endif

4
dir.rb
View file

@ -224,8 +224,8 @@ class Dir
end
# call-seq:
# Dir.glob(*patterns, flags: 0, base: nil, sort: true) -> array
# Dir.glob(*patterns, flags: 0, base: nil, sort: true) {|entry_name| ... } -> nil
# Dir.glob(patterns, flags: 0, base: nil, sort: true) -> array
# Dir.glob(patterns, flags: 0, base: nil, sort: true) {|entry_name| ... } -> nil
#
# Forms an array _entry_names_ of the entry names selected by the arguments.
#

View file

@ -439,6 +439,10 @@ without including the tags in the match:
/(?<=<b>)\w+(?=<\/b>)/.match("Fortune favors the <b>bold</b>.")
# => #<MatchData "bold">
The pattern in lookbehind must be fixed-width.
But top-level alternatives can be of various lengths.
ex. (?<=a|bc) is OK. (?<=aaa(?:b|cd)) is not allowed.
==== Match-Reset Anchor
- <tt>\K</tt>: Match reset:

View file

@ -37,7 +37,7 @@ Context-dependent case mapping as described in
{Table 3-17 (Context Specification for Casing) of the Unicode standard}[https://www.unicode.org/versions/latest/ch03.pdf]
is currently not supported.
In most cases, case conversions of a string have the same number of characters.
In most cases, the case conversion of a string has the same number of characters as before.
There are exceptions (see also +:fold+ below):
s = "\u00DF" # => "ß"
@ -58,25 +58,18 @@ Case changes may not be reversible:
s.downcase.upcase # => "HELLO WORLD!" # Different from original s.
Case changing methods may not maintain Unicode normalization.
See String#unicode_normalize).
See String#unicode_normalize.
== Options for Case Mapping
== Case Mappings
Except for +casecmp+ and +casecmp?+,
each of the case-mapping methods listed above
accepts optional arguments, <tt>*options</tt>.
accepts an optional argument, <tt>mapping</tt>.
The arguments may be:
The argument is one of:
- +:ascii+ only.
- +:fold+ only.
- +:turkic+ or +:lithuanian+ or both.
The options:
- +:ascii+:
ASCII-only mapping:
uppercase letters ('A'..'Z') are mapped to lowercase letters ('a'..'z);
- +:ascii+: ASCII-only mapping.
Uppercase letters ('A'..'Z') are mapped to lowercase letters ('a'..'z);
other characters are not changed
s = "Foo \u00D8 \u00F8 Bar" # => "Foo Ø ø Bar"
@ -85,8 +78,8 @@ The options:
s.upcase(:ascii) # => "FOO Ø ø BAR"
s.downcase(:ascii) # => "foo Ø ø bar"
- +:turkic+:
Full Unicode case mapping, adapted for the Turkic languages
- +:turkic+: Full Unicode case mapping.
For the Turkic languages
that distinguish dotted and dotless I, for example Turkish and Azeri.
s = 'Türkiye' # => "Türkiye"
@ -97,11 +90,8 @@ The options:
s.downcase # => "türkiye"
s.downcase(:turkic) # => "türkıye" # No dot above.
- +:lithuanian+:
Not yet implemented.
- +:fold+ (available only for String#downcase, String#downcase!,
and Symbol#downcase):
and Symbol#downcase).
Unicode case folding,
which is more far-reaching than Unicode case mapping.

View file

@ -260,11 +260,25 @@ This will add launch configurations for debugging Ruby itself by running `test.r
### Compiling for Debugging
You should configure Ruby without optimization and other flags that may
interfere with debugging:
You can compile Ruby with the `RUBY_DEBUG` macro to enable debugging on some
features. One example is debugging object shapes in Ruby with
`RubyVM::Shape.of(object)`.
Additionally Ruby can be compiled to support the `RUBY_DEBUG` environment
variable to enable debugging on some features. An example is using
`RUBY_DEBUG=gc_stress` to debug GC-related issues.
There is also support for the `RUBY_DEBUG_LOG` environment variable to log a
lot of information about what the VM is doing, via the `USE_RUBY_DEBUG_LOG`
macro.
You should also configure Ruby without optimization and other flags that may
interfere with debugging by changing the optimization flags.
Bringing it all together:
```sh
./configure --enable-debug-env optflags="-O0 -fno-omit-frame-pointer"
./configure cppflags="-DRUBY_DEBUG=1 -DUSE_RUBY_DEBUG_LOG=1" --enable-debug-env optflags="-O0 -fno-omit-frame-pointer"
```
### Building with Address Sanitizer

View file

@ -7,7 +7,7 @@ in the Ruby core and in the Ruby standard library.
## Generating documentation
Most Ruby documentation lives in the source files and is written in
[RDoc format](rdoc-ref:RDoc::MarkupReference).
[RDoc format](https://ruby.github.io/rdoc/RDoc/MarkupReference.html).
Some pages live under the `doc` folder and can be written in either
`.rdoc` or `.md` format, determined by the file extension.
@ -44,14 +44,13 @@ Use your judgment about what the user needs to know.
- Group sentences into (ideally short) paragraphs,
each covering a single topic.
- Organize material with
[headings](rdoc-ref:RDoc::MarkupReference@Headings).
[headings].
- Refer to authoritative and relevant sources using
[links](rdoc-ref:RDoc::MarkupReference@Links).
[links](https://ruby.github.io/rdoc/RDoc/MarkupReference.html#class-RDoc::MarkupReference-label-Links).
- Use simple verb tenses: simple present, simple past, simple future.
- Use simple sentence structure, not compound or complex structure.
- Avoid:
- Excessive comma-separated phrases;
consider a [list](rdoc-ref:RDoc::MarkupReference@Lists).
- Excessive comma-separated phrases; consider a [list].
- Idioms and culture-specific references.
- Overuse of headings.
- Using US-ASCII-incompatible characters in C source files;
@ -110,7 +109,7 @@ involving new files `doc/*.rdoc`:
Ruby is documented using RDoc.
For information on \RDoc syntax and features, see the
[RDoc Markup Reference](rdoc-ref:RDoc::MarkupReference).
[RDoc Markup Reference](https://ruby.github.io/rdoc/RDoc/MarkupReference.html).
### Output from `irb`
@ -129,22 +128,21 @@ a #=> [2, 3, 1]
### Headings
Organize a long discussion for a class or module with [headings](rdoc-ref:RDoc::MarkupReference@Headings).
Organize a long discussion for a class or module with [headings].
Do not use formal headings in the documentation for a method or constant.
In the rare case where heading-like structures are needed
within the documentation for a method or constant, use
[bold text](rdoc-ref:RDoc::MarkupReference@Bold)
[bold text](https://ruby.github.io/rdoc/RDoc/MarkupReference.html#class-RDoc::MarkupReference-label-Bold)
as pseudo-headings.
### Blank Lines
A blank line begins a new paragraph.
A [code block](rdoc-ref:RDoc::MarkupReference@Code+Blocks)
or [list](rdoc-ref:RDoc::MarkupReference@Lists)
should be preceded by and followed by a blank line.
A [code block](https://ruby.github.io/rdoc/RDoc/MarkupReference.html#class-RDoc::MarkupReference-label-Code+Blocks)
or [list] should be preceded by and followed by a blank line.
This is unnecessary for the HTML output, but helps in the `ri` output.
### \Method Names
@ -164,7 +162,7 @@ For a method name in text:
Code or commands embedded in running text (i.e., not in a code block)
should marked up as
[monofont](rdoc-ref:RDoc::MarkupReference@Monofont).
[monofont].
Code that is a simple string should include the quote marks.
@ -214,7 +212,7 @@ refers to the current document
(e.g., _Float_ in the documentation for class Float).
In this case you may consider forcing the name to
[monofont](rdoc-ref:RDoc::MarkupReference@Monofont),
[monofont],
which suppresses auto-linking, and also emphasizes that the word is a class name:
```rdoc
@ -276,7 +274,7 @@ Use the +rdoc-ref+ scheme for:
- A link in a standard library package to other documentation in that same
standard library package.
See section "+rdoc-ref+ Scheme" in {Links}[rdoc-ref:RDoc::MarkupReference@Links].
See section "+rdoc-ref+ Scheme" in [links].
#### URL-Based Link
@ -296,7 +294,7 @@ Also use a full URL-based link for a link to an off-site document.
### Variable Names
The name of a variable (as specified in its call-seq) should be marked up as
[monofont](rdoc-ref:RDoc::MarkupReference@Monofont).
[monofont].
Also, use monofont text for the name of a transient variable
(i.e., one defined and used only in the discussion, such as +n+).
@ -314,9 +312,9 @@ In particular, avoid building tables with HTML tags
Alternatives:
- A {verbatim text block}[rdoc-ref:RDoc::MarkupReference@Verbatim+Text+Blocks],
- A {verbatim text block}[https://ruby.github.io/rdoc/RDoc/MarkupReference.html#class-RDoc::MarkupReference-label-Verbatim+Text+Blocks],
using spaces and punctuation to format the text;
note that {text markup}[rdoc-ref:RDoc::MarkupReference@Text+Markup]
note that {text markup}[https://ruby.github.io/rdoc/RDoc/MarkupReference.html#class-RDoc::MarkupReference-label-Text+Markup]
will not be honored:
- Example {source}[https://github.com/ruby/ruby/blob/34d802f32f00df1ac0220b62f72605827c16bad8/file.c#L6570-L6596].
@ -356,8 +354,7 @@ Guidelines:
- The section title is `What's Here`.
- Consider listing the parent class and any included modules; consider
[links](rdoc-ref:RDoc::MarkupReference@Links)
to their "What's Here" sections if those exist.
[links] to their "What's Here" sections if those exist.
- All methods mentioned in the left-pane table of contents
should be listed (including any methods extended from another class).
- Attributes (which are not included in the TOC) may also be listed.
@ -369,7 +366,7 @@ Guidelines:
(and do not list the aliases separately).
- Check the rendered documentation to determine whether \RDoc has recognized
the method and linked to it; if not, manually insert a
[link](rdoc-ref:RDoc::MarkupReference@Links).
[link](https://ruby.github.io/rdoc/RDoc/MarkupReference.html#class-RDoc::MarkupReference-label-Links).
- If there are numerous entries, consider grouping them into subsections with headings.
- If there are more than a few such subsections,
@ -395,7 +392,7 @@ For methods written in Ruby, \RDoc documents the calling sequence automatically.
For methods written in C, \RDoc cannot determine what arguments
the method accepts, so those need to be documented using \RDoc directive
[`call-seq:`](rdoc-ref:RDoc::MarkupReference@Directives+for+Method+Documentation).
[`call-seq:`](https://ruby.github.io/rdoc/RDoc/MarkupReference.html#class-RDoc::MarkupReference-label-Directives+for+Method+Documentation).
For a singleton method, use the form:
@ -570,7 +567,7 @@ argument passed if it is not obvious, not explicitly mentioned in the
details, and not implicitly shown in the examples.
If there is more than one argument or block argument, use a
[labeled list](rdoc-ref:RDoc::MarkupReference@Labeled+Lists).
[labeled list](https://ruby.github.io/rdoc/RDoc/MarkupReference.html#class-RDoc::MarkupReference-label-Labeled+Lists).
### Corner Cases and Exceptions
@ -610,3 +607,7 @@ mention `Hash#fetch` as a related method, and `Hash#merge` might mention
For methods that accept multiple argument types, in some cases it can
be useful to document the different argument types separately. It's
best to use a separate paragraph for each case you are discussing.
[headings]: https://ruby.github.io/rdoc/RDoc/MarkupReference.html#class-RDoc::MarkupReference-label-Headings
[list]: https://ruby.github.io/rdoc/RDoc/MarkupReference.html#class-RDoc::MarkupReference-label-Lists
[monofont]: https://ruby.github.io/rdoc/RDoc/MarkupReference.html#class-RDoc::MarkupReference-label-Monofont

View file

@ -8,6 +8,7 @@ Just a list of acronyms I've run across in the Ruby source code and their meanin
| `bop` | Basic Operator. Relates to methods like `Integer` plus and minus which can be optimized as long as they haven't been redefined. |
| `cc` | Call Cache. An inline cache structure for the call site. Stored in the `cd` |
| `cd` | Call Data. A data structure that points at the `ci` and the `cc`. `iseq` objects points at the `cd`, and access call information and call caches via this structure |
| CFG | Control Flow Graph. Representation of the program where all control-flow and data dependencies have been made explicit by unrolling the stack and local variables. |
| `cfp`| Control Frame Pointer. Represents a Ruby stack frame. Calling a method pushes a new frame (cfp), returning pops a frame. Points at the `pc`, `sp`, `ep`, and the corresponding `iseq`|
| `ci` | Call Information. Refers to an `rb_callinfo` struct. Contains call information about the call site, including number of parameters to be passed, whether it they are keyword arguments or not, etc. Used in conjunction with the `cc` and `cd`. |
| `cref` | Class reference. A structure pointing to the class reference where `klass_or_self`, visibility scope, and refinements are stored. It also stores a pointer to the next class in the hierarchy referenced by `rb_cref_struct * next`. The Class reference is lexically scoped. |
@ -25,10 +26,11 @@ Just a list of acronyms I've run across in the Ruby source code and their meanin
| `insns` | Instructions. Usually an array of YARV instructions. |
| `ivar` | Instance Variable. Refers to a Ruby instance variable like `@foo` |
| `imemo` | Internal Memo. A tagged struct whose memory is managed by Ruby's GC, but contains internal information and isn't meant to be exposed to Ruby programs. Contains various information depending on the type. See the `imemo_type` enum for different types. |
| `IVC` | Instance Variable Cache. Cache specifically for instance variable access |
| JIT | Just In Time compiler |
| `lep` | Local Environment Pointer. An `ep` which is tagged `VM_ENV_FLAG_LOCAL`. Usually this is the `ep` of a method (rather than a block, whose `ep` isn't "local") |
| `local` | Local. Refers to a local variable. |
| `me` | Method Entry. Refers to an `rb_method_entry_t` struct, the internal representation of a Ruby method.
| `me` | Method Entry. Refers to an `rb_method_entry_t` struct, the internal representation of a Ruby method. |
| MRI | Matz's Ruby Implementation |
| `pc` | Program Counter. Usually the instruction that will be executed _next_ by the VM. Pointed to by the `cfp` and incremented by the VM |
| `sp` | Stack Pointer. The top of the stack. The VM executes instructions in the `iseq` and instructions will push and pop values on the stack. The VM updates the `sp` on the `cfp` to point at the top of the stack|

View file

@ -8,7 +8,7 @@ This document outlines the expected way to distribute Ruby, with a specific focu
The tarball for official releases is created by the release manager. The release manager uploads the tarball to the [Ruby website](https://www.ruby-lang.org/en/downloads/).
Downstream distributors should use the official release tarballs as part of their build process. This ensures that the tarball is created in a consistent way, and that the tarball is crytographically verified.
Downstream distributors should use the official release tarballs as part of their build process. This ensures that the tarball is created in a consistent way, and that the tarball is cryptographically verified.
### Using the nightly tarball for testing

View file

@ -212,6 +212,64 @@ I/O. Windows is a notable example where socket I/O can be non-blocking but pipe
I/O is blocking. Provided that there *is* a scheduler and the current thread *is
non-blocking*, the operation will invoke the scheduler.
##### `IO#close`
Closing an IO interrupts all blocking operations on that IO. When a thread calls `IO#close`, it first attempts to interrupt any threads or fibers that are blocked on that IO. The closing thread waits until all blocked threads and fibers have been properly interrupted and removed from the IO's blocking list. Each interrupted thread or fiber receives an `IOError` and is cleanly removed from the blocking operation. Only after all blocking operations have been interrupted and cleaned up will the actual file descriptor be closed, ensuring proper resource cleanup and preventing potential race conditions.
For fibers managed by a scheduler, the interruption process involves calling `rb_fiber_scheduler_fiber_interrupt` on the scheduler. This allows the scheduler to handle the interruption in a way that's appropriate for its event loop implementation. The scheduler can then notify the fiber, which will receive an `IOError` and be removed from the blocking operation. This mechanism ensures that fiber-based concurrency works correctly with IO operations, even when those operations are interrupted by `IO#close`.
```mermaid
sequenceDiagram
participant ThreadB
participant ThreadA
participant Scheduler
participant IO
participant Fiber1
participant Fiber2
Note over ThreadA: Thread A has a fiber scheduler
activate Scheduler
ThreadA->>Fiber1: Schedule Fiber 1
activate Fiber1
Fiber1->>IO: IO.read
IO->>Scheduler: rb_thread_io_blocking_region
deactivate Fiber1
ThreadA->>Fiber2: Schedule Fiber 2
activate Fiber2
Fiber2->>IO: IO.read
IO->>Scheduler: rb_thread_io_blocking_region
deactivate Fiber2
Note over Fiber1,Fiber2: Both fibers blocked on same IO
Note over ThreadB: IO.close
activate ThreadB
ThreadB->>IO: thread_io_close_notify_all
Note over ThreadB: rb_mutex_sleep
IO->>Scheduler: rb_fiber_scheduler_fiber_interrupt(Fiber1)
Scheduler->>Fiber1: fiber_interrupt with IOError
activate Fiber1
Note over IO: fiber_interrupt causes removal from blocking list
Fiber1->>IO: rb_io_blocking_operation_exit()
IO-->>ThreadB: Wakeup thread
deactivate Fiber1
IO->>Scheduler: rb_fiber_scheduler_fiber_interrupt(Fiber2)
Scheduler->>Fiber2: fiber_interrupt with IOError
activate Fiber2
Note over IO: fiber_interrupt causes removal from blocking list
Fiber2->>IO: rb_io_blocking_operation_exit()
IO-->>ThreadB: Wakeup thread
deactivate Fiber2
deactivate Scheduler
Note over ThreadB: Blocking operations list empty
ThreadB->>IO: close(fd)
deactivate ThreadB
```
#### Mutex
The `Mutex` class can be used in a non-blocking context and is fiber specific.

View file

@ -137,7 +137,7 @@ English - <tt>$DEFAULT_INPUT</tt>.
An output stream, initially <tt>$stdout</tt>.
English - <tt>$DEFAULT_OUTPUT
English - <tt>$DEFAULT_OUTPUT</tt>
=== <tt>$.</tt> (Input Position)

View file

@ -107,11 +107,9 @@ have commit right, others don't.
* https://github.com/rubygems/rubygems
* https://rubygems.org/gems/bundler
#### lib/cgi.rb, lib/cgi/*
#### lib/cgi/escape.rb
* *unmaintained*
* https://github.com/ruby/cgi
* https://rubygems.org/gems/cgi
#### lib/English.rb
@ -234,12 +232,6 @@ have commit right, others don't.
* https://github.com/ruby/securerandom
* https://rubygems.org/gems/securerandom
#### lib/set.rb
* Akinori MUSHA ([knu])
* https://github.com/ruby/set
* https://rubygems.org/gems/set
#### lib/shellwords.rb
* Akinori MUSHA ([knu])
@ -318,8 +310,6 @@ have commit right, others don't.
#### ext/cgi
* Nobuyoshi Nakada ([nobu])
* https://github.com/ruby/cgi
* https://rubygems.org/gems/cgi
#### ext/date

View file

@ -10,9 +10,9 @@ You can make multiple Ractors and they run in parallel.
* `Ractor.new{ expr }` creates a new Ractor and `expr` is run in parallel on a parallel computer.
* Interpreter invokes with the first Ractor (called *main Ractor*).
* If main Ractor terminated, all Ractors receive terminate request like Threads (if main thread (first invoked Thread), Ruby interpreter sends all running threads to terminate execution).
* Each Ractor has 1 or more Threads.
* Threads in a Ractor shares a Ractor-wide global lock like GIL (GVL in MRI terminology), so they can't run in parallel (without releasing GVL explicitly in C-level). Threads in different ractors run in parallel.
* If the main Ractor terminates, all other Ractors receive termination requests, similar to how threads behave. (if main thread (first invoked Thread), Ruby interpreter sends all running threads to terminate execution).
* Each Ractor contains one or more Threads.
* Threads within the same Ractor share a Ractor-wide global lock like GIL (GVL in MRI terminology), so they can't run in parallel (without releasing GVL explicitly in C-level). Threads in different ractors run in parallel.
* The overhead of creating a Ractor is similar to overhead of one Thread creation.
### Limited sharing between multiple ractors
@ -31,20 +31,23 @@ Ractors don't share everything, unlike threads.
* Ractor object itself.
* And more...
### Two-types communication between Ractors
### Communication between Ractors with `Ractor::Port`
Ractors communicate with each other and synchronize the execution by message exchanging between Ractors. There are two message exchange protocols: push type (message passing) and pull type.
Ractors communicate with each other and synchronize the execution by message exchanging between Ractors. `Ractor::Port` is provided for this communication.
* Push type message passing: `Ractor#send(obj)` and `Ractor.receive()` pair.
* Sender ractor passes the `obj` to the ractor `r` by `r.send(obj)` and receiver ractor receives the message with `Ractor.receive`.
* Sender knows the destination Ractor `r` and the receiver does not know the sender (accept all messages from any ractors).
* Receiver has infinite queue and sender enqueues the message. Sender doesn't block to put message into this queue.
* This type of message exchanging is employed by many other Actor-based languages.
* `Ractor.receive_if{ filter_expr }` is a variant of `Ractor.receive` to select a message.
* Pull type communication: `Ractor.yield(obj)` and `Ractor#take()` pair.
* Sender ractor declare to yield the `obj` by `Ractor.yield(obj)` and receiver Ractor take it with `r.take`.
* Sender doesn't know a destination Ractor and receiver knows the sender Ractor `r`.
* Sender or receiver will block if there is no other side.
```ruby
port = Ractor::Port.new
Ractor.new port do |port|
# Other ractors can send to the port
port << 42
end
port.receive # get a message to the port. Only the creator Ractor can receive from the port
#=> 42
```
Ractors have its own deafult port and `Ractor#send`, `Ractor.receive` will use it.
### Copy & Move semantics to send messages
@ -66,7 +69,7 @@ Ractor helps to write a thread-safe concurrent program, but we can make thread-u
* To make it compatible with old behavior, classes and modules can introduce data-race and so on.
* Ruby programmers should take care if they modify class/module objects on multi Ractor programs.
* BAD: Ractor can't solve all thread-safety problems
* There are several blocking operations (waiting send, waiting yield and waiting take) so you can make a program which has dead-lock and live-lock issues.
* There are several blocking operations (waiting send) so you can make a program which has dead-lock and live-lock issues.
* Some kind of shareable objects can introduce transactions (STM, for example). However, misusing transactions will generate inconsistent state.
Without Ractor, we need to trace all state-mutations to debug thread-safety issues.
@ -105,7 +108,7 @@ begin
r = Ractor.new do
a #=> ArgumentError because this block accesses `a`.
end
r.take # see later
r.join # see later
rescue ArgumentError
end
```
@ -117,7 +120,7 @@ r = Ractor.new do
p self.class #=> Ractor
self.object_id
end
r.take == self.object_id #=> false
r.value == self.object_id #=> false
```
Passed arguments to `Ractor.new()` becomes block parameters for the given block. However, an interpreter does not pass the parameter object references, but send them as messages (see below for details).
@ -126,7 +129,7 @@ Passed arguments to `Ractor.new()` becomes block parameters for the given block.
r = Ractor.new 'ok' do |msg|
msg #=> 'ok'
end
r.take #=> 'ok'
r.value #=> 'ok'
```
```ruby
@ -136,7 +139,7 @@ r = Ractor.new do
msg
end
r.send 'ok'
r.take #=> 'ok'
r.value #=> 'ok'
```
### An execution result of given block
@ -147,15 +150,7 @@ Return value of the given block becomes an outgoing message (see below for detai
r = Ractor.new do
'ok'
end
r.take #=> `ok`
```
```ruby
# almost similar to the last example
r = Ractor.new do
Ractor.yield 'ok'
end
r.take #=> 'ok'
r.value #=> `ok`
```
Error in the given block will be propagated to the receiver of an outgoing message.
@ -166,7 +161,7 @@ r = Ractor.new do
end
begin
r.take
r.value
rescue Ractor::RemoteError => e
e.cause.class #=> RuntimeError
e.cause.message #=> 'ok'
@ -178,9 +173,7 @@ end
Communication between Ractors is achieved by sending and receiving messages. There are two ways to communicate with each other.
* (1) Message sending/receiving
* (1-1) push type send/receive (sender knows receiver). Similar to the Actor model.
* (1-2) pull type yield/take (receiver knows sender).
* (1) Message sending/receiving via `Ractor::Port`
* (2) Using shareable container objects
* Ractor::TVar gem ([ko1/ractor-tvar](https://github.com/ko1/ractor-tvar))
* more?
@ -189,18 +182,12 @@ Users can control program execution timing with (1), but should not control with
For message sending and receiving, there are two types of APIs: push type and pull type.
* (1-1) send/receive (push type)
* `Ractor#send(obj)` (`Ractor#<<(obj)` is an alias) send a message to the Ractor's incoming port. Incoming port is connected to the infinite size incoming queue so `Ractor#send` will never block.
* `Ractor.receive` dequeue a message from its own incoming queue. If the incoming queue is empty, `Ractor.receive` calling will block.
* `Ractor.receive_if{|msg| filter_expr }` is variant of `Ractor.receive`. `receive_if` only receives a message which `filter_expr` is true (So `Ractor.receive` is the same as `Ractor.receive_if{ true }`.
* (1-2) yield/take (pull type)
* `Ractor.yield(obj)` send an message to a Ractor which are calling `Ractor#take` via outgoing port . If no Ractors are waiting for it, the `Ractor.yield(obj)` will block. If multiple Ractors are waiting for `Ractor.yield(obj)`, only one Ractor can receive the message.
* `Ractor#take` receives a message which is waiting by `Ractor.yield(obj)` method from the specified Ractor. If the Ractor does not call `Ractor.yield` yet, the `Ractor#take` call will block.
* `Ractor.select()` can wait for the success of `take`, `yield` and `receive`.
* You can close the incoming port or outgoing port.
* You can close then with `Ractor#close_incoming` and `Ractor#close_outgoing`.
* If the incoming port is closed for a Ractor, you can't `send` to the Ractor. If `Ractor.receive` is blocked for the closed incoming port, then it will raise an exception.
* If the outgoing port is closed for a Ractor, you can't call `Ractor#take` and `Ractor.yield` on the Ractor. If ractors are blocking by `Ractor#take` or `Ractor.yield`, closing outgoing port will raise an exception on these blocking ractors.
* (1) send/receive via `Ractor::Port`.
* `Ractor::Port#send(obj)` (`Ractor::Port#<<(obj)` is an alias) send a message to the port. Ports are connected to the infinite size incoming queue so `Ractor::Port#send` will never block.
* `Ractor::Port#receive` dequeue a message from its own incoming queue. If the incoming queue is empty, `Ractor::Port#receive` calling will block the execution of a thread.
* `Ractor.select()` can wait for the success of `Ractor::Port#receive`.
* You can close `Ractor::Port` by `Ractor::Port#close` only by the creator Ractor of the port.
* If the port is closed, you can't `send` to the port. If `Ractor::Port#receive` is blocked for the closed port, then it will raise an exception.
* When a Ractor is terminated, the Ractor's ports are closed.
* There are 3 ways to send an object as a message
* (1) Send a reference: Sending a shareable object, send only a reference to the object (fast)
@ -208,104 +195,15 @@ For message sending and receiving, there are two types of APIs: push type and pu
* (3) Move an object: Sending an unshareable object reference with a membership. Sender Ractor can not access moved objects anymore (raise an exception) after moving it. Current implementation makes new object as a moved object for receiver Ractor and copies references of sending object to moved object. `T_DATA` objects are not supported.
* You can choose "Copy" and "Move" by the `move:` keyword, `Ractor#send(obj, move: true/false)` and `Ractor.yield(obj, move: true/false)` (default is `false` (COPY)).
### Sending/Receiving ports
Each Ractor has _incoming-port_ and _outgoing-port_. Incoming-port is connected to the infinite sized incoming queue.
```
Ractor r
+-------------------------------------------+
| incoming outgoing |
| port port |
r.send(obj) ->*->[incoming queue] Ractor.yield(obj) ->*-> r.take
| | |
| v |
| Ractor.receive |
+-------------------------------------------+
Connection example: r2.send obj on r1、Ractor.receive on r2
+----+ +----+
* r1 |---->* r2 *
+----+ +----+
Connection example: Ractor.yield(obj) on r1, r1.take on r2
+----+ +----+
* r1 *---->- r2 *
+----+ +----+
Connection example: Ractor.yield(obj) on r1 and r2,
and waiting for both simultaneously by Ractor.select(r1, r2)
+----+
* r1 *------+
+----+ |
+----> Ractor.select(r1, r2)
+----+ |
* r2 *------|
+----+
```
```ruby
r = Ractor.new do
msg = Ractor.receive # Receive from r's incoming queue
msg # send back msg as block return value
end
r.send 'ok' # Send 'ok' to r's incoming port -> incoming queue
r.take # Receive from r's outgoing port
```
The last example shows the following ractor network.
```
+------+ +---+
* main |------> * r *---+
+------+ +---+ |
^ |
+-------------------+
```
And this code can be simplified by using an argument for `Ractor.new`.
```ruby
# Actual argument 'ok' for `Ractor.new()` will be sent to created Ractor.
r = Ractor.new 'ok' do |msg|
# Values for formal parameters will be received from incoming queue.
# Similar to: msg = Ractor.receive
msg # Return value of the given block will be sent via outgoing port
end
# receive from the r's outgoing port.
r.take #=> `ok`
```
### Return value of a block for `Ractor.new`
As already explained, the return value of `Ractor.new` (an evaluated value of `expr` in `Ractor.new{ expr }`) can be taken by `Ractor#take`.
```ruby
Ractor.new{ 42 }.take #=> 42
```
When the block return value is available, the Ractor is dead so that no ractors except taken Ractor can touch the return value, so any values can be sent with this communication path without any modification.
```ruby
r = Ractor.new do
a = "hello"
binding
end
r.take.eval("p a") #=> "hello" (other communication path can not send a Binding object directly)
```
### Wait for multiple Ractors with `Ractor.select`
You can wait multiple Ractor's `yield` with `Ractor.select(*ractors)`.
The return value of `Ractor.select()` is `[r, msg]` where `r` is yielding Ractor and `msg` is yielded message.
You can wait multiple Ractor port's receiving.
The return value of `Ractor.select()` is `[port, msg]` where `port` is a ready port and `msg` is received message.
Wait for a single ractor (same as `Ractor.take`):
To make convenient, `Ractor.select` can also accept Ractors to wait the termination of Ractors.
The return value of `Ractor.select()` is `[r, msg]` where `r` is a terminated Ractor and `msg` is the value of Ractor's blcok.
Wait for a single ractor (same as `Ractor#value`):
```ruby
r1 = Ractor.new{'r1'}
@ -314,7 +212,7 @@ r, obj = Ractor.select(r1)
r == r1 and obj == 'r1' #=> true
```
Wait for two ractors:
Waiting for two ractors:
```ruby
r1 = Ractor.new{'r1'}
@ -334,85 +232,29 @@ as << obj
as.sort == ['r1', 'r2'] #=> true
```
\Complex example:
```ruby
pipe = Ractor.new do
loop do
Ractor.yield Ractor.receive
end
end
RN = 10
rs = RN.times.map{|i|
Ractor.new pipe, i do |pipe, i|
msg = pipe.take
msg # ping-pong
end
}
RN.times{|i|
pipe << i
}
RN.times.map{
r, n = Ractor.select(*rs)
rs.delete r
n
}.sort #=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
```
Multiple Ractors can send to one Ractor.
```ruby
# Create 10 ractors and they send objects to pipe ractor.
# pipe ractor yield received objects
pipe = Ractor.new do
loop do
Ractor.yield Ractor.receive
end
end
RN = 10
rs = RN.times.map{|i|
Ractor.new pipe, i do |pipe, i|
pipe << i
end
}
RN.times.map{
pipe.take
}.sort #=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
```
TODO: Current `Ractor.select()` has the same issue of `select(2)`, so this interface should be refined.
TODO: `select` syntax of go-language uses round-robin technique to make fair scheduling. Now `Ractor.select()` doesn't use it.
### Closing Ractor's ports
* `Ractor#close_incoming/outgoing` close incoming/outgoing ports (similar to `Queue#close`).
* `Ractor#close_incoming`
* `r.send(obj)` where `r`'s incoming port is closed, will raise an exception.
* When the incoming queue is empty and incoming port is closed, `Ractor.receive` raises an exception. If the incoming queue is not empty, it dequeues an object without exceptions.
* `Ractor#close_outgoing`
* `Ractor.yield` on a Ractor which closed the outgoing port, it will raise an exception.
* `Ractor#take` for a Ractor which closed the outgoing port, it will raise an exception. If `Ractor#take` is blocking, it will raise an exception.
* `Ractor::Port#close` close the ports (similar to `Queue#close`).
* `port.send(obj)` where `port` is closed, will raise an exception.
* When the queue connected to the port is empty and port is closed, `Ractor::Port#receive` raises an exception. If the queue is not empty, it dequeues an object without exceptions.
* When a Ractor terminates, the ports are closed automatically.
* Return value of the Ractor's block will be yielded as `Ractor.yield(ret_val)`, even if the implementation terminates the based native thread.
Example (try to take from closed Ractor):
Example (try to get a result from closed Ractor):
```ruby
r = Ractor.new do
'finish'
end
r.take # success (will return 'finish')
begin
o = r.take # try to take from closed Ractor
rescue Ractor::ClosedError
'ok'
else
"ng: #{o}"
r.join # success (wait for the termination)
r.value # success (will return 'finish')
# the first Ractor which success the `Ractor#value` can get the result
Ractor.new r do |r|
r.value #=> Ractor::Error
end
```
@ -422,7 +264,7 @@ Example (try to send to closed (terminated) Ractor):
r = Ractor.new do
end
r.take # wait terminate
r.join # wait terminate
begin
r.send(1)
@ -433,11 +275,9 @@ else
end
```
When multiple Ractors are waiting for `Ractor.yield()`, `Ractor#close_outgoing` will cancel all blocking by raising an exception (`ClosedError`).
### Send a message by copying
`Ractor#send(obj)` or `Ractor.yield(obj)` copy `obj` deeply if `obj` is an unshareable object.
`Ractor::Port#send(obj)` copy `obj` deeply if `obj` is an unshareable object.
```ruby
obj = 'str'.dup
@ -446,7 +286,7 @@ r = Ractor.new obj do |msg|
msg.object_id
end
obj.object_id == r.take #=> false
obj.object_id == r.value #=> false
```
Some objects are not supported to copy the value, and raise an exception.
@ -466,7 +306,7 @@ end
### Send a message by moving
`Ractor#send(obj, move: true)` or `Ractor.yield(obj, move: true)` move `obj` to the destination Ractor.
`Ractor::Port#send(obj, move: true)` moves `obj` to the destination Ractor.
If the source Ractor touches the moved object (for example, call the method like `obj.foo()`), it will be an error.
```ruby
@ -478,7 +318,7 @@ end
str = 'hello'
r.send str, move: true
modified = r.take #=> 'hello world'
modified = r.value #=> 'hello world'
# str is moved, and accessing str from this Ractor is prohibited
@ -492,22 +332,6 @@ else
end
```
```ruby
# move with Ractor.yield
r = Ractor.new do
obj = 'hello'
Ractor.yield obj, move: true
obj << 'world' # raise Ractor::MovedError
end
str = r.take
begin
r.take
rescue Ractor::RemoteError
p str #=> "hello"
end
```
Some objects are not supported to move, and an exception will be raised.
```ruby
@ -554,13 +378,13 @@ r = Ractor.new do
end
begin
r.take
r.join
rescue Ractor::RemoteError => e
e.cause.message #=> 'can not access global variables from non-main Ractors'
end
```
Note that some special global variables are ractor-local, like `$stdin`, `$stdout`, `$stderr`. See [[Bug #17268]](https://bugs.ruby-lang.org/issues/17268) for more details.
Note that some special global variables, such as `$stdin`, `$stdout` and `$stderr` are Ractor-lcoal. See [[Bug #17268]](https://bugs.ruby-lang.org/issues/17268) for more details.
### Instance variables of shareable objects
@ -575,7 +399,7 @@ p Ractor.new do
class C
@iv
end
end.take #=> 1
end.value #=> 1
```
Otherwise, only the main Ractor can access instance variables of shareable objects.
@ -601,7 +425,7 @@ Ractor.new do
#=> "can not set instance variables of classes/modules by non-main Ractors"
end
end
end.take
end.join
```
@ -615,7 +439,7 @@ r = Ractor.new shared do |shared|
end
begin
r.take
r.join
rescue Ractor::RemoteError => e
e.cause.message #=> can not access instance variables of shareable objects from non-main Ractors (Ractor::IsolationError)
end
@ -640,7 +464,7 @@ end
begin
r.take
r.join
rescue => e
e.class #=> Ractor::IsolationError
end
@ -658,7 +482,7 @@ r = Ractor.new do
C::CONST
end
begin
r.take
r.join
rescue => e
e.class #=> Ractor::IsolationError
end
@ -673,7 +497,7 @@ r = Ractor.new do
C::CONST = 'str'
end
begin
r.take
r.join
rescue => e
e.class #=> Ractor::IsolationError
end
@ -770,54 +594,51 @@ end
### Worker pool
(1) One ractor has a pool
```ruby
require 'prime'
pipe = Ractor.new do
loop do
Ractor.yield Ractor.receive
end
end
N = 1000
RN = 10
# make RN workers
workers = (1..RN).map do
Ractor.new pipe do |pipe|
while n = pipe.take
Ractor.yield [n, n.prime?]
Ractor.new do |; result_port|
loop do
n, result_port = Ractor.receive
result_port << [n, n.prime?, Ractor.current]
end
end
end
(1..N).each{|i|
pipe << i
}
result_port = Ractor::Port.new
results = []
pp (1..N).map{
_r, (n, b) = Ractor.select(*workers)
[n, b]
}.sort_by{|(n, b)| n}
(1..N).each do |i|
if workers.empty?
# receive a result
n, result, w = result_port.receive
results << [n, result]
else
w = workers.pop
end
# send a task to the idle worker ractor
w << [i, result_port]
end
# receive a result
while results.size != N
n, result, _w = result_port.receive
results << [n, result]
end
pp results.sort_by{|n, result| n}
```
### Pipeline
```ruby
# pipeline with yield/take
r1 = Ractor.new do
'r1'
end
r2 = Ractor.new r1 do |r1|
r1.take + 'r2'
end
r3 = Ractor.new r2 do |r2|
r2.take + 'r3'
end
p r3.take #=> 'r1r2r3'
```
```ruby
# pipeline with send/receive

File diff suppressed because it is too large Load diff

View file

@ -37,7 +37,7 @@ programs for configuration and database persistence of Ruby object trees.
Similar to +Marshal+, it is able to deserialize into arbitrary Ruby classes.
For example, the following YAML data will create an +ERB+ object when
deserialized, using the `unsafe_load` method:
deserialized, using the +unsafe_load+ method:
!ruby/object:ERB
src: puts `uname`
@ -53,19 +53,16 @@ method, variable and constant names. The reason for this is that symbols are
simply integers with names attached to them, so they are faster to look up in
hashtables.
Starting in version 2.2, most symbols can be garbage collected; these are
called <i>mortal</i> symbols. Most symbols you create (e.g. by calling
+to_sym+) are mortal.
Most symbols can be garbage collected; these are called _mortal_
symbols. Most symbols you create (e.g. by calling +to_sym+) are mortal.
<i>Immortal</i> symbols on the other hand will never be garbage collected.
_Immortal_ symbols on the other hand will never be garbage collected.
They are created when modifying code:
* defining a method (e.g. with +define_method+),
* setting an instance variable (e.g. with +instance_variable_set+),
* creating a variable or constant (e.g. with +const_set+)
C extensions that have not been updated and are still calling `SYM2ID`
C extensions that have not been updated and are still calling +SYM2ID+
will create immortal symbols.
Bugs in 2.2.0: +send+ and +__send__+ also created immortal symbols,
and calling methods with keyword arguments could also create some.
Don't create immortal symbols from user inputs. Otherwise, this would
allow a user to mount a denial of service attack against your application by
@ -128,12 +125,3 @@ Note that the use of +public_send+ is also dangerous, as +send+ itself is
public:
1.public_send("send", "eval", "...ruby code to be executed...")
== DRb
As DRb allows remote clients to invoke arbitrary methods, it is not suitable to
expose to untrusted clients.
When using DRb, try to avoid exposing it over the network if possible. If this
isn't possible and you need to expose DRb to the world, you *must* configure an
appropriate security policy with <code>DRb::ACL</code>.

View file

@ -34,7 +34,6 @@ of each.
## Libraries
- Bundler ([GitHub][bundler]): Manage your Ruby application's gem dependencies
- CGI ([GitHub][cgi]): Support for the Common Gateway Interface protocol
- Delegator ([GitHub][delegate]): Provides three abilities to delegate method calls to an object
- DidYouMean ([GitHub][did_you_mean]): "Did you mean?" experience in Ruby
- English ([GitHub][English]): Provides references to special global variables with less cryptic names
@ -53,12 +52,12 @@ of each.
- Prism ([GitHub][prism]): A portable, error-tolerant Ruby parser
- Resolv ([GitHub][resolv]): Thread-aware DNS resolver library in Ruby
- SecureRandom ([GitHub][securerandom]): Interface for a secure random number generator
- [Set](rdoc-ref:Set) ([GitHub][set]): Provides a class to deal with collections of unordered, unique values
- Shellwords ([GitHub][shellwords]): Manipulates strings with the word parsing rules of the UNIX Bourne shell
- Singleton ([GitHub][singleton]): Implementation of the Singleton pattern for Ruby
- Tempfile ([GitHub][tempfile]): A utility class for managing temporary files
- Time ([GitHub][time]): Extends the Time class with methods for parsing and conversion
- Timeout ([GitHub][timeout]): Auto-terminate potentially long-running operations in Ruby
- TmpDir ([GitHub][tmpdir]): Extends the Dir class to manage the OS temporary file path
- TSort ([GitHub][tsort]): Topological sorting using Tarjan's algorithm
- UN ([GitHub][un]): Utilities to replace common UNIX commands
- URI ([GitHub][uri]): A Ruby module providing support for Uniform Resource Identifiers
@ -72,13 +71,14 @@ of each.
- Etc ([GitHub][etc]): Provides access to information typically stored in the UNIX /etc directory
- Fcntl ([GitHub][fcntl]): Loads constants defined in the OS fcntl.h C header file
- IO.console ([GitHub][io-console]): Extensions for the IO class, including `IO.console`, `IO.winsize`, etc.
- IO#nonblock ([GitHub][io-nonblock]): Enable non-blocking mode with IO class.
- IO#wait ([GitHub][io-wait]): Provides the feature for waiting until IO is readable or writable without blocking.
- JSON ([GitHub][json]): Implements JavaScript Object Notation for Ruby
- OpenSSL ([GitHub][openssl]): Provides SSL, TLS, and general-purpose cryptography for Ruby
- Pathname ([GitHub][pathname]): Representation of the name of a file or directory on the filesystem
- Psych ([GitHub][psych]): A YAML parser and emitter for Ruby
- StringIO ([GitHub][stringio]): Pseudo-I/O on String objects
- StringScanner ([GitHub][strscan]): Provides lexical scanning operations on a String
- TmpDir ([GitHub][tmpdir]): Extends the Dir class to manage the OS temporary file path
- Zlib ([GitHub][zlib]): Ruby interface for the zlib compression/decompression library
# Bundled gems
@ -92,9 +92,9 @@ of each.
- [minitest]: A test library supporting TDD, BDD, mocking, and benchmarking
- [power_assert]: Power Assert for Ruby
- [rake]: Ruby build program with capabilities similar to make
- [rake][rake-doc] ([GitHub][rake]): Ruby build program with capabilities similar to make
- [test-unit]: A compatibility layer for MiniTest
- [rexml]: An XML toolkit for Ruby
- [rexml][rexml-doc] ([GitHub][rexml]): An XML toolkit for Ruby
- [rss]: A family of libraries supporting various XML-based "feeds"
- [net-ftp]: Support for the File Transfer Protocol
- [net-imap]: Ruby client API for the Internet Message Access Protocol
@ -105,7 +105,7 @@ of each.
- [rbs]: RBS is a language to describe the structure of Ruby programs
- [typeprof]: A type analysis tool for Ruby code based on abstract interpretation
- [debug]: Debugging functionality for Ruby
- [racc]: A LALR(1) parser generator written in Ruby
- [racc][racc-doc] ([GitHub][racc]): A LALR(1) parser generator written in Ruby
- [mutex_m]: Mixin to extend objects to be handled like a Mutex
- [getoptlong]: Parse command line options similar to the GNU C getopt_long()
- [base64]: Support for encoding and decoding binary data using a Base64 representation
@ -117,13 +117,13 @@ of each.
- [drb]: Distributed object system for Ruby
- [nkf]: Ruby extension for the Network Kanji Filter
- [syslog]: Ruby interface for the POSIX system logging facility
- [csv]: Provides an interface to read and write CSV files and data
- [csv][csv-doc] ([GitHub][csv]): Provides an interface to read and write CSV files and data
- [ostruct]: A class to build custom data structures, similar to a Hash
- [benchmark]: Provides methods to measure and report the time used to execute code
- [logger]: Provides a simple logging utility for outputting messages
- [logger][logger-doc] ([GitHub][logger]): Provides a simple logging utility for outputting messages
- [pstore]: Implements a file-based persistence mechanism based on a Hash
- [win32ole]: Provides an interface for OLE Automation in Ruby
- [reline]: GNU Readline and Editline in a pure Ruby implementation
- [reline][reline-doc] ([GitHub][reline]): GNU Readline and Editline in a pure Ruby implementation
- [readline]: Wrapper for the Readline extension and Reline
- [fiddle]: A libffi wrapper for Ruby
@ -137,7 +137,6 @@ of each.
[benchmark]: https://github.com/ruby/benchmark
[bigdecimal]: https://github.com/ruby/bigdecimal
[bundler]: https://github.com/rubygems/rubygems
[cgi]: https://github.com/ruby/cgi
[csv]: https://github.com/ruby/csv
[date]: https://github.com/ruby/date
[debug]: https://github.com/ruby/debug
@ -156,6 +155,8 @@ of each.
[forwardable]: https://github.com/ruby/forwardable
[getoptlong]: https://github.com/ruby/getoptlong
[io-console]: https://github.com/ruby/io-console
[io-nonblock]: https://github.com/ruby/io-nonblock
[io-wait]: https://github.com/ruby/io-wait
[ipaddr]: https://github.com/ruby/ipaddr
[irb]: https://github.com/ruby/irb
[json]: https://github.com/ruby/json
@ -195,7 +196,6 @@ of each.
[rinda]: https://github.com/ruby/rinda
[rss]: https://github.com/ruby/rss
[securerandom]: https://github.com/ruby/securerandom
[set]: https://github.com/ruby/set
[shellwords]: https://github.com/ruby/shellwords
[singleton]: https://github.com/ruby/singleton
[stringio]: https://github.com/ruby/stringio
@ -215,5 +215,11 @@ of each.
[yaml]: https://github.com/ruby/yaml
[zlib]: https://github.com/ruby/zlib
[reline-doc]: https://ruby.github.io/reline/
[rake-doc]: https://ruby.github.io/rake/
[irb-doc]: https://ruby.github.io/irb/
[rdoc-doc]: https://ruby.github.io/rdoc/
[logger-doc]: https://ruby.github.io/logger/
[racc-doc]: https://ruby.github.io/racc/
[csv-doc]: https://ruby.github.io/csv/
[rexml-doc]: https://ruby.github.io/rexml/

View file

@ -342,6 +342,8 @@
#
# - #=~: Returns the index of the first substring that matches a given
# Regexp or other object; returns +nil+ if no match is found.
# - #byteindex: Returns the byte index of the first occurrence of a given substring.
# - #byterindex: Returns the byte index of the last occurrence of a given substring.
# - #index: Returns the index of the _first_ occurrence of a given substring;
# returns +nil+ if none found.
# - #rindex: Returns the index of the _last_ occurrence of a given substring;
@ -371,10 +373,9 @@
# - #eql?: Returns +true+ if the content is the same as the given other string.
# - #<=>: Returns -1, 0, or 1 as a given other string is smaller than,
# equal to, or larger than +self+.
# - #casecmp: Ignoring case, returns -1, 0, or 1 as a given
# other string is smaller than, equal to, or larger than +self+.
# - #casecmp?: Returns +true+ if the string is equal to a given string after Unicode case folding;
# +false+ otherwise.
# - #casecmp: Ignoring case, returns -1, 0, or 1 as
# +self+ is smaller than, equal to, or larger than a given other string.
# - #casecmp?: Ignoring case, returns whether a given other string is equal to +self+.
#
# === Modifying
#
@ -389,6 +390,7 @@
#
# _Substitution_
#
# - #bytesplice: Replaces bytes of +self+ with bytes from a given string; returns +self+.
# - #sub!: Replaces the first substring that matches a given pattern with a given replacement string;
# returns +self+ if any changes, +nil+ otherwise.
# - #gsub!: Replaces each substring that matches a given pattern with a given replacement string;
@ -425,6 +427,8 @@
# - #slice!, #[]=: Removes a substring determined by a given index, start/length, range, regexp, or substring.
# - #squeeze!: Removes contiguous duplicate characters; returns +self+.
# - #delete!: Removes characters as determined by the intersection of substring arguments.
# - #delete_prefix!: Removes leading prefix; returns +self+ if any changes, +nil+ otherwise.
# - #delete_suffix!: Removes trailing suffix; returns +self+ if any changes, +nil+ otherwise.
# - #lstrip!: Removes leading whitespace; returns +self+ if any changes, +nil+ otherwise.
# - #rstrip!: Removes trailing whitespace; returns +self+ if any changes, +nil+ otherwise.
# - #strip!: Removes leading and trailing whitespace; returns +self+ if any changes, +nil+ otherwise.
@ -441,7 +445,7 @@
#
# - #*: Returns the concatenation of multiple copies of +self+.
# - #+: Returns the concatenation of +self+ and a given other string.
# - #center: Returns a copy of +self+ centered between pad substrings.
# - #center: Returns a copy of +self+, centered by specified padding.
# - #concat: Returns the concatenation of +self+ with given other strings.
# - #prepend: Returns the concatenation of a given other string with +self+.
# - #ljust: Returns a copy of +self+ of a given length, right-padded with a given other string.

View file

@ -12,3 +12,5 @@ the underlying bytes are not modified:
t = s.b # => "\xE4\x82\x95"
t.encoding # => #<Encoding:ASCII-8BIT>
t.bytes # => [228, 130, 149]
Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String].

View file

@ -4,3 +4,5 @@ Returns an array of the bytes in +self+:
'тест'.bytes # => [209, 130, 208, 181, 209, 129, 209, 130]
'こんにちは'.bytes
# => [227, 129, 147, 227, 130, 147, 227, 129, 171, 227, 129, 161, 227, 129, 175]
Related: see {Converting to Non-String}[rdoc-ref:String@Converting+to+Non--5CString].

View file

@ -1,11 +1,15 @@
Returns the count of bytes (not characters) in +self+:
Returns the count of bytes in +self+.
'foo'.bytesize # => 3
'тест'.bytesize # => 8
'こんにちは'.bytesize # => 15
Note that the byte count may be different from the character count (returned by #size):
Contrast with String#length:
s = 'foo'
s.bytesize # => 3
s.size # => 3
s = 'тест'
s.bytesize # => 8
s.size # => 4
s = 'こんにちは'
s.bytesize # => 15
s.size # => 5
'foo'.length # => 3
'тест'.length # => 4
'こんにちは'.length # => 5
Related: see {Querying}[rdoc-ref:String@Querying].

54
doc/string/byteslice.rdoc Normal file
View file

@ -0,0 +1,54 @@
Returns a substring of +self+, or +nil+ if the substring cannot be constructed.
With integer arguments +offset+ and +length+ given,
returns the substring beginning at the given +offset+
and of the given +length+ (as available):
s = '0123456789' # => "0123456789"
s.byteslice(2) # => "2"
s.byteslice(200) # => nil
s.byteslice(4, 3) # => "456"
s.byteslice(4, 30) # => "456789"
Returns +nil+ if +length+ is negative or +offset+ falls outside of +self+:
s.byteslice(4, -1) # => nil
s.byteslice(40, 2) # => nil
Counts backwards from the end of +self+
if +offset+ is negative:
s = '0123456789' # => "0123456789"
s.byteslice(-4) # => "6"
s.byteslice(-4, 3) # => "678"
With Range argument +range+ given, returns
<tt>byteslice(range.begin, range.size)</tt>:
s = '0123456789' # => "0123456789"
s.byteslice(4..6) # => "456"
s.byteslice(-6..-4) # => "456"
s.byteslice(5..2) # => "" # range.size is zero.
s.byteslice(40..42) # => nil
The starting and ending offsets need not be on character boundaries:
s = 'こんにちは'
s.byteslice(0, 3) # => "こ"
s.byteslice(1, 3) # => "\x81\x93\xE3"
The encodings of +self+ and the returned substring
are always the same:
s.encoding # => #<Encoding:UTF-8>
s.byteslice(0, 3).encoding # => #<Encoding:UTF-8>
s.byteslice(1, 3).encoding # => #<Encoding:UTF-8>
But, depending on the character boundaries,
the encoding of the returned substring may not be valid:
s.valid_encoding? # => true
s.byteslice(0, 3).valid_encoding? # => true
s.byteslice(1, 3).valid_encoding? # => false
Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String].

View file

@ -0,0 +1,66 @@
Replaces <i>target bytes</i> in +self+ with <i>source bytes</i> from the given string +str+;
returns +self+.
In the first form, arguments +offset+ and +length+ determine the target bytes,
and the source bytes are all of the given +str+:
'0123456789'.bytesplice(0, 3, 'abc') # => "abc3456789"
'0123456789'.bytesplice(3, 3, 'abc') # => "012abc6789"
'0123456789'.bytesplice(0, 50, 'abc') # => "abc"
'0123456789'.bytesplice(50, 3, 'abc') # Raises IndexError.
The counts of the target bytes and source source bytes may be different:
'0123456789'.bytesplice(0, 6, 'abc') # => "abc6789" # Shorter source.
'0123456789'.bytesplice(0, 1, 'abc') # => "abc123456789" # Shorter target.
And either count may be zero (i.e., specifying an empty string):
'0123456789'.bytesplice(0, 3, '') # => "3456789" # Empty source.
'0123456789'.bytesplice(0, 0, 'abc') # => "abc0123456789" # Empty target.
In the second form, just as in the first,
arugments +offset+ and +length+ determine the target bytes;
argument +str+ _contains_ the source bytes,
and the additional arguments +str_offset+ and +str_length+
determine the actual source bytes:
'0123456789'.bytesplice(0, 3, 'abc', 0, 3) # => "abc3456789"
'0123456789'.bytesplice(0, 3, 'abc', 1, 1) # => "b3456789" # Shorter source.
'0123456789'.bytesplice(0, 1, 'abc', 0, 3) # => "abc123456789" # Shorter target.
'0123456789'.bytesplice(0, 3, 'abc', 1, 0) # => "3456789" # Empty source.
'0123456789'.bytesplice(0, 0, 'abc', 0, 3) # => "abc0123456789" # Empty target.
In the third form, argument +range+ determines the target bytes
and the source bytes are all of the given +str+:
'0123456789'.bytesplice(0..2, 'abc') # => "abc3456789"
'0123456789'.bytesplice(3..5, 'abc') # => "012abc6789"
'0123456789'.bytesplice(0..5, 'abc') # => "abc6789" # Shorter source.
'0123456789'.bytesplice(0..0, 'abc') # => "abc123456789" # Shorter target.
'0123456789'.bytesplice(0..2, '') # => "3456789" # Empty source.
'0123456789'.bytesplice(0...0, 'abc') # => "abc0123456789" # Empty target.
In the fourth form, just as in the third,
arugment +range+ determines the target bytes;
argument +str+ _contains_ the source bytes,
and the additional argument +str_range+
determines the actual source bytes:
'0123456789'.bytesplice(0..2, 'abc', 0..2) # => "abc3456789"
'0123456789'.bytesplice(3..5, 'abc', 0..2) # => "012abc6789"
'0123456789'.bytesplice(0..2, 'abc', 0..1) # => "ab3456789" # Shorter source.
'0123456789'.bytesplice(0..1, 'abc', 0..2) # => "abc23456789" # Shorter target.
'0123456789'.bytesplice(0..2, 'abc', 0...0) # => "3456789" # Empty source.
'0123456789'.bytesplice(0...0, 'abc', 0..2) # => "abc0123456789" # Empty target.
In any of the forms, the beginnings and endings of both source and target
must be on character boundaries.
In these examples, +self+ has five 3-byte characters,
and so has character boundaries at offsets 0, 3, 6, 9, 12, and 15.
'こんにちは'.bytesplice(0, 3, 'abc') # => "abcんにちは"
'こんにちは'.bytesplice(1, 3, 'abc') # Raises IndexError.
'こんにちは'.bytesplice(0, 2, 'abc') # Raises IndexError.

View file

@ -2,15 +2,19 @@ Returns a centered copy of +self+.
If integer argument +size+ is greater than the size (in characters) of +self+,
returns a new string of length +size+ that is a copy of +self+,
centered and padded on both ends with +pad_string+:
centered and padded on one or both ends with +pad_string+:
'hello'.center(10) # => " hello "
' hello'.center(10) # => " hello "
'hello'.center(10, 'ab') # => "abhelloaba"
'hello'.center(6) # => "hello " # Padded on one end.
'hello'.center(10) # => " hello " # Padded on both ends.
'hello'.center(20, '-|') # => "-|-|-|-hello-|-|-|-|" # Some padding repeated.
'hello'.center(10, 'abcdefg') # => "abhelloabc" # Some padding not used.
' hello '.center(13) # => " hello "
'тест'.center(10) # => " тест "
'こんにちは'.center(10) # => " こんにちは "
'こんにちは'.center(10) # => " こんにちは " # Multi-byte characters.
If +size+ is not greater than the size of +self+, returns a copy of +self+:
If +size+ is less than or equal to the size of +self+, returns an unpadded copy of +self+:
'hello'.center(5) # => "hello"
'hello'.center(1) # => "hello"
'hello'.center(-10) # => "hello"
Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String].

View file

@ -3,3 +3,6 @@ Returns an array of the characters in +self+:
'hello'.chars # => ["h", "e", "l", "l", "o"]
'тест'.chars # => ["т", "е", "с", "т"]
'こんにちは'.chars # => ["こ", "ん", "に", "ち", "は"]
''.chars # => []
Related: see {Converting to Non-String}[rdoc-ref:String@Converting+to+Non--5CString].

View file

@ -25,5 +25,8 @@ removes multiple trailing occurrences of <tt>"\n"</tt> or <tt>"\r\n"</tt>
When +line_sep+ is neither <tt>"\n"</tt> nor <tt>''</tt>,
removes a single trailing line separator if there is one:
'abcd'.chomp('d') # => "abc"
'abcdd'.chomp('d') # => "abcd"
'abcd'.chomp('cd') # => "ab"
'abcdcd'.chomp('cd') # => "abcd"
'abcd'.chomp('xx') # => "abcd"
Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String].

View file

@ -13,4 +13,7 @@ Otherwise removes the last character if it exists.
'こんにちは'.chop # => "こんにち"
''.chop # => ""
If you only need to remove the newline separator at the end of the string, String#chomp is a better alternative.
If you only need to remove the newline separator at the end of the string,
String#chomp is a better alternative.
Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String].

8
doc/string/chr.rdoc Normal file
View file

@ -0,0 +1,8 @@
Returns a string containing the first character of +self+:
'hello'.chr # => "h"
'тест'.chr # => "т"
'こんにちは'.chr # => "こ"
''.chr # => ""
Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String].

View file

@ -4,3 +4,6 @@ each codepoint is the integer value for a character:
'hello'.codepoints # => [104, 101, 108, 108, 111]
'тест'.codepoints # => [1090, 1077, 1089, 1090]
'こんにちは'.codepoints # => [12371, 12435, 12395, 12385, 12399]
''.codepoints # => []
Related: see {Converting to Non-String}[rdoc-ref:String@Converting+to+Non--5CString].

12
doc/string/concat.rdoc Normal file
View file

@ -0,0 +1,12 @@
Concatenates each object in +objects+ to +self+; returns +self+:
'foo'.concat('bar', 'baz') # => "foobarbaz"
For each given object +object+ that is an integer,
the value is considered a codepoint and converted to a character before concatenation:
'foo'.concat(32, 'bar', 32, 'baz') # => "foo bar baz" # Embeds spaces.
'те'.concat(1089, 1090) # => "тест"
'こん'.concat(12395, 12385, 12399) # => "こんにちは"
Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String].

78
doc/string/count.rdoc Normal file
View file

@ -0,0 +1,78 @@
Returns the total number of characters in +self+ that are specified by the given selectors.
For one 1-character selector,
returns the count of instances of that character:
s = 'abracadabra'
s.count('a') # => 5
s.count('b') # => 2
s.count('x') # => 0
s.count('') # => 0
s = 'тест'
s.count('т') # => 2
s.count('е') # => 1
s = 'よろしくお願いします'
s.count('よ') # => 1
s.count('し') # => 2
For one multi-character selector,
returns the count of instances for all specified characters:
s = 'abracadabra'
s.count('ab') # => 7
s.count('abc') # => 8
s.count('abcd') # => 9
s.count('abcdr') # => 11
s.count('abcdrx') # => 11
Order and repetition do not matter:
s.count('ba') == s.count('ab') # => true
s.count('baab') == s.count('ab') # => true
For multiple selectors,
forms a single selector that is the intersection of characters in all selectors
and returns the count of instances for that selector:
s = 'abcdefg'
s.count('abcde', 'dcbfg') == s.count('bcd') # => true
s.count('abc', 'def') == s.count('') # => true
In a character selector, three characters get special treatment:
- A caret (<tt>'^'</tt>) functions as a _negation_ operator
for the immediately following characters:
s = 'abracadabra'
s.count('^bc') # => 8 # Count of all except 'b' and 'c'.
- A hyphen (<tt>'-'</tt>) between two other characters defines a _range_ of characters:
s = 'abracadabra'
s.count('a-c') # => 8 # Count of all 'a', 'b', and 'c'.
- A backslash (<tt>'\'</tt>) acts as an escape for a caret, a hyphen,
or another backslash:
s = 'abracadabra'
s.count('\^bc') # => 3 # Count of '^', 'b', and 'c'.
s.count('a\-c') # => 6 # Count of 'a', '-', and 'c'.
'foo\bar\baz'.count('\\') # => 2 # Count of '\'.
These usages may be mixed:
s = 'abracadabra'
s.count('a-cq-t') # => 10 # Multiple ranges.
s.count('ac-d') # => 7 # Range mixed with plain characters.
s.count('^a-c') # => 3 # Range mixed with negation.
For multiple selectors, all forms may be used, including negations, ranges, and escapes.
s = 'abracadabra'
s.count('^abc', '^def') == s.count('^abcdef') # => true
s.count('a-e', 'c-g') == s.count('cde') # => true
s.count('^abc', 'c-g') == s.count('defg') # => true
Related: see {Querying}[rdoc-ref:String@Querying].

79
doc/string/delete.rdoc Normal file
View file

@ -0,0 +1,79 @@
Returns a new string that is a copy of +self+ with certain characters removed;
the removed characters are all instances of those specified by the given string +selectors+.
For one 1-character selector,
removes all instances of that character:
s = 'abracadabra'
s.delete('a') # => "brcdbr"
s.delete('b') # => "aracadara"
s.delete('x') # => "abracadabra"
s.delete('') # => "abracadabra"
s = 'тест'
s.delete('т') # => "ес"
s.delete('е') # => "тст"
s = 'よろしくお願いします'
s.delete('よ') # => "ろしくお願いします"
s.delete('し') # => "よろくお願います"
For one multi-character selector,
removes all instances of the specified characters:
s = 'abracadabra'
s.delete('ab') # => "rcdr"
s.delete('abc') # => "rdr"
s.delete('abcd') # => "rr"
s.delete('abcdr') # => ""
s.delete('abcdrx') # => ""
Order and repetition do not matter:
s.delete('ba') == s.delete('ab') # => true
s.delete('baab') == s.delete('ab') # => true
For multiple selectors,
forms a single selector that is the intersection of characters in all selectors
and removes all instances of characters specified by that selector:
s = 'abcdefg'
s.delete('abcde', 'dcbfg') == s.delete('bcd') # => true
s.delete('abc', 'def') == s.delete('') # => true
In a character selector, three characters get special treatment:
- A caret (<tt>'^'</tt>) functions as a _negation_ operator
for the immediately following characters:
s = 'abracadabra'
s.delete('^bc') # => "bcb" # Deletes all except 'b' and 'c'.
- A hyphen (<tt>'-'</tt>) between two other characters defines a _range_ of characters:
s = 'abracadabra'
s.delete('a-c') # => "rdr" # Deletes all 'a', 'b', and 'c'.
- A backslash (<tt>'\'</tt>) acts as an escape for a caret, a hyphen,
or another backslash:
s = 'abracadabra'
s.delete('\^bc') # => "araadara" # Deletes all '^', 'b', and 'c'.
s.delete('a\-c') # => "brdbr" # Deletes all 'a', '-', and 'c'.
'foo\bar\baz'.delete('\\') # => "foobarbaz" # Deletes all '\'.
These usages may be mixed:
s = 'abracadabra'
s.delete('a-cq-t') # => "d" # Multiple ranges.
s.delete('ac-d') # => "brbr" # Range mixed with plain characters.
s.delete('^a-c') # => "abacaaba" # Range mixed with negation.
For multiple selectors, all forms may be used, including negations, ranges, and escapes.
s = 'abracadabra'
s.delete('^abc', '^def') == s.delete('^abcdef') # => true
s.delete('a-e', 'c-g') == s.delete('cde') # => true
s.delete('^abc', 'c-g') == s.delete('defg') # => true
Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String].

View file

@ -1,8 +1,10 @@
Returns a copy of +self+ with leading substring <tt>prefix</tt> removed:
Returns a copy of +self+ with leading substring +prefix+ removed:
'hello'.delete_prefix('hel') # => "lo"
'hello'.delete_prefix('llo') # => "hello"
'oof'.delete_prefix('o') # => "of"
'oof'.delete_prefix('oo') # => "f"
'oof'.delete_prefix('oof') # => ""
'oof'.delete_prefix('x') # => "oof"
'тест'.delete_prefix('те') # => "ст"
'こんにちは'.delete_prefix('こん') # => "にちは"
Related: String#delete_prefix!, String#delete_suffix.
Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String].

View file

@ -1,8 +1,11 @@
Returns a copy of +self+ with trailing substring <tt>suffix</tt> removed:
'hello'.delete_suffix('llo') # => "he"
'hello'.delete_suffix('hel') # => "hello"
'foo'.delete_suffix('o') # => "fo"
'foo'.delete_suffix('oo') # => "f"
'foo'.delete_suffix('foo') # => ""
'foo'.delete_suffix('f') # => "foo"
'foo'.delete_suffix('x') # => "foo"
'тест'.delete_suffix('ст') # => "те"
'こんにちは'.delete_suffix('ちは') # => "こんに"
Related: String#delete_suffix!, String#delete_prefix.
Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String].

12
doc/string/downcase.rdoc Normal file
View file

@ -0,0 +1,12 @@
Returns a new string containing the downcased characters in +self+:
'Hello, World!'.downcase # => "hello, world!"
'ТЕСТ'.downcase # => "тест"
'よろしくお願いします'.downcase # => "よろしくお願いします"
Some characters do not have upcased and downcased versions.
The casing may be affected by the given +mapping+;
see {Case Mapping}[rdoc-ref:case_mapping.rdoc].
Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String].

View file

@ -17,6 +17,7 @@ editor.
Ruby core development can be done either in Windows `cmd` like:
```batch
ridk install
ridk enable ucrt64
pacman -S --needed %MINGW_PACKAGE_PREFIX%-openssl %MINGW_PACKAGE_PREFIX%-libyaml %MINGW_PACKAGE_PREFIX%-libffi
@ -37,6 +38,7 @@ make
or in MSYS2 `bash` like:
```bash
ridk install
ridk enable ucrt64
bash
@ -76,14 +78,37 @@ sh ../../ruby/configure -C --disable-install-doc --with-opt-dir=C:\Users\usernam
x64.
The minimum requirement is here:
* VC++/MSVC on VS 2017/2019 version build tools.
* Visual Studio 2022 17.13.x is broken. see https://bugs.ruby-lang.org/issues/21167
* VC++/MSVC on VS 2017/2019/2022 version build tools.
* Windows 10/11 SDK
* 10.0.26100 is broken, 10.0.22621 is recommended. see https://bugs.ruby-lang.org/issues/21255
3. Please set environment variable `INCLUDE`, `LIB`, `PATH`
to run required commands properly from the command line.
These are set properly by `vcvarall*.bat` usually.
You can install Visual Studio Build Tools with `winget`.
`win32\install-buildtools.cmd` is a batch file to install the
minimum requirements excluding the IDE etc.
3. Please set environment variable `INCLUDE`, `LIB`, `PATH` to run
required commands properly from the command line. These are set
properly by `vsdevcmd.bat` or `vcvarall*.bat` usually. You can run
the following command to set them in your command line.
To native build:
```
cmd /k win32\vssetup.cmd
```
To cross build arm64 binary:
```
cmd /k win32\vssetup.cmd -arch arm64
```
To cross build x64 binary:
```
cmd /k win32\vssetup.cmd -arch x64
```
See `win32\vssetup.cmd -help` for other command line options.
**Note** building ruby requires following commands.

View file

@ -28,12 +28,12 @@ in a way that can be easily shared with other team members.
Make sure you have a `--enable-zjit=dev` build, and run `brew install cargo-nextest` first.
### make zjit-test-all
### make zjit-check
This command runs all ZJIT tests: `make zjit-test` and `test/ruby/test_zjit.rb`.
```
make zjit-test-all
make zjit-check
```
### make zjit-test
@ -78,6 +78,16 @@ use `make`.
</details>
### make zjit-test-all
```
make zjit-test-all
```
This command runs all Ruby tests under `/test/ruby/` with ZJIT enabled.
Certain tests are excluded under `/test/.excludes-zjit`.
### test/ruby/test\_zjit.rb
This command runs Ruby execution tests.
@ -91,3 +101,33 @@ You can also run a single test case by matching the method name:
```
make test-all TESTS="test/ruby/test_zjit.rb -n TestZJIT#test_putobject"
```
## ZJIT Glossary
This glossary contains terms that are helpful for understanding ZJIT.
Please note that some terms may appear in CRuby internals too but with different meanings.
| Term | Definition |
| --- | -----------|
| HIR | High-level Intermediate Representation. High-level (Ruby semantics) graph representation in static single-assignment (SSA) form |
| LIR | Low-level Intermediate Representation. Low-level IR used in the backend for assembly generation |
| SSA | Static Single Assignment. A form where each variable is assigned exactly once |
| `opnd` | Operand. An operand to an IR instruction (can be register, memory, immediate, etc.) |
| `dst` | Destination. The output operand of an instruction where the result is stored |
| VReg | Virtual Register. A virtual register that gets lowered to physical register or memory |
| `insn_id` | Instruction ID. An index of an instruction in a function |
| `block_id` | The index of a basic block, which effectively acts like a pointer |
| `branch` | Control flow edge between basic blocks in the compiled code |
| `cb` | Code Block. Memory region for generated machine code |
| `entry` | The starting address of compiled code for an ISEQ |
| Patch Point | Location in generated code that can be modified later in case assumptions get invalidated |
| Frame State | Captured state of the Ruby stack frame at a specific point for deoptimization |
| Guard | A run-time check that ensures assumptions are still valid |
| `invariant` | An assumption that JIT code relies on, requiring invalidation if broken |
| Deopt | Deoptimization. Process of falling back from JIT code to interpreter |
| Side Exit | Exit from JIT code back to interpreter |
| Type Lattice | Hierarchy of types used for type inference and optimization |
| Constant Folding | Optimization that evaluates constant expressions at compile time |
| RSP | x86-64 stack pointer register used for native stack operations |
| Register Spilling | Process of moving register values to memory when running out of physical registers |

View file

@ -317,6 +317,7 @@ enc/ascii.$(OBJEXT): internal/intern/re.h
enc/ascii.$(OBJEXT): internal/intern/ruby.h
enc/ascii.$(OBJEXT): internal/intern/select.h
enc/ascii.$(OBJEXT): internal/intern/select/largesize.h
enc/ascii.$(OBJEXT): internal/intern/set.h
enc/ascii.$(OBJEXT): internal/intern/signal.h
enc/ascii.$(OBJEXT): internal/intern/sprintf.h
enc/ascii.$(OBJEXT): internal/intern/string.h
@ -479,6 +480,7 @@ enc/big5.$(OBJEXT): internal/intern/re.h
enc/big5.$(OBJEXT): internal/intern/ruby.h
enc/big5.$(OBJEXT): internal/intern/select.h
enc/big5.$(OBJEXT): internal/intern/select/largesize.h
enc/big5.$(OBJEXT): internal/intern/set.h
enc/big5.$(OBJEXT): internal/intern/signal.h
enc/big5.$(OBJEXT): internal/intern/sprintf.h
enc/big5.$(OBJEXT): internal/intern/string.h
@ -651,6 +653,7 @@ enc/cesu_8.$(OBJEXT): internal/intern/re.h
enc/cesu_8.$(OBJEXT): internal/intern/ruby.h
enc/cesu_8.$(OBJEXT): internal/intern/select.h
enc/cesu_8.$(OBJEXT): internal/intern/select/largesize.h
enc/cesu_8.$(OBJEXT): internal/intern/set.h
enc/cesu_8.$(OBJEXT): internal/intern/signal.h
enc/cesu_8.$(OBJEXT): internal/intern/sprintf.h
enc/cesu_8.$(OBJEXT): internal/intern/string.h
@ -813,6 +816,7 @@ enc/cp949.$(OBJEXT): internal/intern/re.h
enc/cp949.$(OBJEXT): internal/intern/ruby.h
enc/cp949.$(OBJEXT): internal/intern/select.h
enc/cp949.$(OBJEXT): internal/intern/select/largesize.h
enc/cp949.$(OBJEXT): internal/intern/set.h
enc/cp949.$(OBJEXT): internal/intern/signal.h
enc/cp949.$(OBJEXT): internal/intern/sprintf.h
enc/cp949.$(OBJEXT): internal/intern/string.h
@ -974,6 +978,7 @@ enc/emacs_mule.$(OBJEXT): internal/intern/re.h
enc/emacs_mule.$(OBJEXT): internal/intern/ruby.h
enc/emacs_mule.$(OBJEXT): internal/intern/select.h
enc/emacs_mule.$(OBJEXT): internal/intern/select/largesize.h
enc/emacs_mule.$(OBJEXT): internal/intern/set.h
enc/emacs_mule.$(OBJEXT): internal/intern/signal.h
enc/emacs_mule.$(OBJEXT): internal/intern/sprintf.h
enc/emacs_mule.$(OBJEXT): internal/intern/string.h
@ -1145,6 +1150,7 @@ enc/encdb.$(OBJEXT): internal/intern/re.h
enc/encdb.$(OBJEXT): internal/intern/ruby.h
enc/encdb.$(OBJEXT): internal/intern/select.h
enc/encdb.$(OBJEXT): internal/intern/select/largesize.h
enc/encdb.$(OBJEXT): internal/intern/set.h
enc/encdb.$(OBJEXT): internal/intern/signal.h
enc/encdb.$(OBJEXT): internal/intern/sprintf.h
enc/encdb.$(OBJEXT): internal/intern/string.h
@ -1309,6 +1315,7 @@ enc/euc_jp.$(OBJEXT): internal/intern/re.h
enc/euc_jp.$(OBJEXT): internal/intern/ruby.h
enc/euc_jp.$(OBJEXT): internal/intern/select.h
enc/euc_jp.$(OBJEXT): internal/intern/select/largesize.h
enc/euc_jp.$(OBJEXT): internal/intern/set.h
enc/euc_jp.$(OBJEXT): internal/intern/signal.h
enc/euc_jp.$(OBJEXT): internal/intern/sprintf.h
enc/euc_jp.$(OBJEXT): internal/intern/string.h
@ -1470,6 +1477,7 @@ enc/euc_kr.$(OBJEXT): internal/intern/re.h
enc/euc_kr.$(OBJEXT): internal/intern/ruby.h
enc/euc_kr.$(OBJEXT): internal/intern/select.h
enc/euc_kr.$(OBJEXT): internal/intern/select/largesize.h
enc/euc_kr.$(OBJEXT): internal/intern/set.h
enc/euc_kr.$(OBJEXT): internal/intern/signal.h
enc/euc_kr.$(OBJEXT): internal/intern/sprintf.h
enc/euc_kr.$(OBJEXT): internal/intern/string.h
@ -1631,6 +1639,7 @@ enc/euc_tw.$(OBJEXT): internal/intern/re.h
enc/euc_tw.$(OBJEXT): internal/intern/ruby.h
enc/euc_tw.$(OBJEXT): internal/intern/select.h
enc/euc_tw.$(OBJEXT): internal/intern/select/largesize.h
enc/euc_tw.$(OBJEXT): internal/intern/set.h
enc/euc_tw.$(OBJEXT): internal/intern/signal.h
enc/euc_tw.$(OBJEXT): internal/intern/sprintf.h
enc/euc_tw.$(OBJEXT): internal/intern/string.h
@ -1792,6 +1801,7 @@ enc/gb18030.$(OBJEXT): internal/intern/re.h
enc/gb18030.$(OBJEXT): internal/intern/ruby.h
enc/gb18030.$(OBJEXT): internal/intern/select.h
enc/gb18030.$(OBJEXT): internal/intern/select/largesize.h
enc/gb18030.$(OBJEXT): internal/intern/set.h
enc/gb18030.$(OBJEXT): internal/intern/signal.h
enc/gb18030.$(OBJEXT): internal/intern/sprintf.h
enc/gb18030.$(OBJEXT): internal/intern/string.h
@ -1953,6 +1963,7 @@ enc/gb2312.$(OBJEXT): internal/intern/re.h
enc/gb2312.$(OBJEXT): internal/intern/ruby.h
enc/gb2312.$(OBJEXT): internal/intern/select.h
enc/gb2312.$(OBJEXT): internal/intern/select/largesize.h
enc/gb2312.$(OBJEXT): internal/intern/set.h
enc/gb2312.$(OBJEXT): internal/intern/signal.h
enc/gb2312.$(OBJEXT): internal/intern/sprintf.h
enc/gb2312.$(OBJEXT): internal/intern/string.h
@ -2114,6 +2125,7 @@ enc/gbk.$(OBJEXT): internal/intern/re.h
enc/gbk.$(OBJEXT): internal/intern/ruby.h
enc/gbk.$(OBJEXT): internal/intern/select.h
enc/gbk.$(OBJEXT): internal/intern/select/largesize.h
enc/gbk.$(OBJEXT): internal/intern/set.h
enc/gbk.$(OBJEXT): internal/intern/signal.h
enc/gbk.$(OBJEXT): internal/intern/sprintf.h
enc/gbk.$(OBJEXT): internal/intern/string.h
@ -2276,6 +2288,7 @@ enc/iso_8859_1.$(OBJEXT): internal/intern/re.h
enc/iso_8859_1.$(OBJEXT): internal/intern/ruby.h
enc/iso_8859_1.$(OBJEXT): internal/intern/select.h
enc/iso_8859_1.$(OBJEXT): internal/intern/select/largesize.h
enc/iso_8859_1.$(OBJEXT): internal/intern/set.h
enc/iso_8859_1.$(OBJEXT): internal/intern/signal.h
enc/iso_8859_1.$(OBJEXT): internal/intern/sprintf.h
enc/iso_8859_1.$(OBJEXT): internal/intern/string.h
@ -2438,6 +2451,7 @@ enc/iso_8859_10.$(OBJEXT): internal/intern/re.h
enc/iso_8859_10.$(OBJEXT): internal/intern/ruby.h
enc/iso_8859_10.$(OBJEXT): internal/intern/select.h
enc/iso_8859_10.$(OBJEXT): internal/intern/select/largesize.h
enc/iso_8859_10.$(OBJEXT): internal/intern/set.h
enc/iso_8859_10.$(OBJEXT): internal/intern/signal.h
enc/iso_8859_10.$(OBJEXT): internal/intern/sprintf.h
enc/iso_8859_10.$(OBJEXT): internal/intern/string.h
@ -2599,6 +2613,7 @@ enc/iso_8859_11.$(OBJEXT): internal/intern/re.h
enc/iso_8859_11.$(OBJEXT): internal/intern/ruby.h
enc/iso_8859_11.$(OBJEXT): internal/intern/select.h
enc/iso_8859_11.$(OBJEXT): internal/intern/select/largesize.h
enc/iso_8859_11.$(OBJEXT): internal/intern/set.h
enc/iso_8859_11.$(OBJEXT): internal/intern/signal.h
enc/iso_8859_11.$(OBJEXT): internal/intern/sprintf.h
enc/iso_8859_11.$(OBJEXT): internal/intern/string.h
@ -2761,6 +2776,7 @@ enc/iso_8859_13.$(OBJEXT): internal/intern/re.h
enc/iso_8859_13.$(OBJEXT): internal/intern/ruby.h
enc/iso_8859_13.$(OBJEXT): internal/intern/select.h
enc/iso_8859_13.$(OBJEXT): internal/intern/select/largesize.h
enc/iso_8859_13.$(OBJEXT): internal/intern/set.h
enc/iso_8859_13.$(OBJEXT): internal/intern/signal.h
enc/iso_8859_13.$(OBJEXT): internal/intern/sprintf.h
enc/iso_8859_13.$(OBJEXT): internal/intern/string.h
@ -2923,6 +2939,7 @@ enc/iso_8859_14.$(OBJEXT): internal/intern/re.h
enc/iso_8859_14.$(OBJEXT): internal/intern/ruby.h
enc/iso_8859_14.$(OBJEXT): internal/intern/select.h
enc/iso_8859_14.$(OBJEXT): internal/intern/select/largesize.h
enc/iso_8859_14.$(OBJEXT): internal/intern/set.h
enc/iso_8859_14.$(OBJEXT): internal/intern/signal.h
enc/iso_8859_14.$(OBJEXT): internal/intern/sprintf.h
enc/iso_8859_14.$(OBJEXT): internal/intern/string.h
@ -3085,6 +3102,7 @@ enc/iso_8859_15.$(OBJEXT): internal/intern/re.h
enc/iso_8859_15.$(OBJEXT): internal/intern/ruby.h
enc/iso_8859_15.$(OBJEXT): internal/intern/select.h
enc/iso_8859_15.$(OBJEXT): internal/intern/select/largesize.h
enc/iso_8859_15.$(OBJEXT): internal/intern/set.h
enc/iso_8859_15.$(OBJEXT): internal/intern/signal.h
enc/iso_8859_15.$(OBJEXT): internal/intern/sprintf.h
enc/iso_8859_15.$(OBJEXT): internal/intern/string.h
@ -3247,6 +3265,7 @@ enc/iso_8859_16.$(OBJEXT): internal/intern/re.h
enc/iso_8859_16.$(OBJEXT): internal/intern/ruby.h
enc/iso_8859_16.$(OBJEXT): internal/intern/select.h
enc/iso_8859_16.$(OBJEXT): internal/intern/select/largesize.h
enc/iso_8859_16.$(OBJEXT): internal/intern/set.h
enc/iso_8859_16.$(OBJEXT): internal/intern/signal.h
enc/iso_8859_16.$(OBJEXT): internal/intern/sprintf.h
enc/iso_8859_16.$(OBJEXT): internal/intern/string.h
@ -3409,6 +3428,7 @@ enc/iso_8859_2.$(OBJEXT): internal/intern/re.h
enc/iso_8859_2.$(OBJEXT): internal/intern/ruby.h
enc/iso_8859_2.$(OBJEXT): internal/intern/select.h
enc/iso_8859_2.$(OBJEXT): internal/intern/select/largesize.h
enc/iso_8859_2.$(OBJEXT): internal/intern/set.h
enc/iso_8859_2.$(OBJEXT): internal/intern/signal.h
enc/iso_8859_2.$(OBJEXT): internal/intern/sprintf.h
enc/iso_8859_2.$(OBJEXT): internal/intern/string.h
@ -3571,6 +3591,7 @@ enc/iso_8859_3.$(OBJEXT): internal/intern/re.h
enc/iso_8859_3.$(OBJEXT): internal/intern/ruby.h
enc/iso_8859_3.$(OBJEXT): internal/intern/select.h
enc/iso_8859_3.$(OBJEXT): internal/intern/select/largesize.h
enc/iso_8859_3.$(OBJEXT): internal/intern/set.h
enc/iso_8859_3.$(OBJEXT): internal/intern/signal.h
enc/iso_8859_3.$(OBJEXT): internal/intern/sprintf.h
enc/iso_8859_3.$(OBJEXT): internal/intern/string.h
@ -3733,6 +3754,7 @@ enc/iso_8859_4.$(OBJEXT): internal/intern/re.h
enc/iso_8859_4.$(OBJEXT): internal/intern/ruby.h
enc/iso_8859_4.$(OBJEXT): internal/intern/select.h
enc/iso_8859_4.$(OBJEXT): internal/intern/select/largesize.h
enc/iso_8859_4.$(OBJEXT): internal/intern/set.h
enc/iso_8859_4.$(OBJEXT): internal/intern/signal.h
enc/iso_8859_4.$(OBJEXT): internal/intern/sprintf.h
enc/iso_8859_4.$(OBJEXT): internal/intern/string.h
@ -3894,6 +3916,7 @@ enc/iso_8859_5.$(OBJEXT): internal/intern/re.h
enc/iso_8859_5.$(OBJEXT): internal/intern/ruby.h
enc/iso_8859_5.$(OBJEXT): internal/intern/select.h
enc/iso_8859_5.$(OBJEXT): internal/intern/select/largesize.h
enc/iso_8859_5.$(OBJEXT): internal/intern/set.h
enc/iso_8859_5.$(OBJEXT): internal/intern/signal.h
enc/iso_8859_5.$(OBJEXT): internal/intern/sprintf.h
enc/iso_8859_5.$(OBJEXT): internal/intern/string.h
@ -4055,6 +4078,7 @@ enc/iso_8859_6.$(OBJEXT): internal/intern/re.h
enc/iso_8859_6.$(OBJEXT): internal/intern/ruby.h
enc/iso_8859_6.$(OBJEXT): internal/intern/select.h
enc/iso_8859_6.$(OBJEXT): internal/intern/select/largesize.h
enc/iso_8859_6.$(OBJEXT): internal/intern/set.h
enc/iso_8859_6.$(OBJEXT): internal/intern/signal.h
enc/iso_8859_6.$(OBJEXT): internal/intern/sprintf.h
enc/iso_8859_6.$(OBJEXT): internal/intern/string.h
@ -4216,6 +4240,7 @@ enc/iso_8859_7.$(OBJEXT): internal/intern/re.h
enc/iso_8859_7.$(OBJEXT): internal/intern/ruby.h
enc/iso_8859_7.$(OBJEXT): internal/intern/select.h
enc/iso_8859_7.$(OBJEXT): internal/intern/select/largesize.h
enc/iso_8859_7.$(OBJEXT): internal/intern/set.h
enc/iso_8859_7.$(OBJEXT): internal/intern/signal.h
enc/iso_8859_7.$(OBJEXT): internal/intern/sprintf.h
enc/iso_8859_7.$(OBJEXT): internal/intern/string.h
@ -4377,6 +4402,7 @@ enc/iso_8859_8.$(OBJEXT): internal/intern/re.h
enc/iso_8859_8.$(OBJEXT): internal/intern/ruby.h
enc/iso_8859_8.$(OBJEXT): internal/intern/select.h
enc/iso_8859_8.$(OBJEXT): internal/intern/select/largesize.h
enc/iso_8859_8.$(OBJEXT): internal/intern/set.h
enc/iso_8859_8.$(OBJEXT): internal/intern/signal.h
enc/iso_8859_8.$(OBJEXT): internal/intern/sprintf.h
enc/iso_8859_8.$(OBJEXT): internal/intern/string.h
@ -4539,6 +4565,7 @@ enc/iso_8859_9.$(OBJEXT): internal/intern/re.h
enc/iso_8859_9.$(OBJEXT): internal/intern/ruby.h
enc/iso_8859_9.$(OBJEXT): internal/intern/select.h
enc/iso_8859_9.$(OBJEXT): internal/intern/select/largesize.h
enc/iso_8859_9.$(OBJEXT): internal/intern/set.h
enc/iso_8859_9.$(OBJEXT): internal/intern/signal.h
enc/iso_8859_9.$(OBJEXT): internal/intern/sprintf.h
enc/iso_8859_9.$(OBJEXT): internal/intern/string.h
@ -4700,6 +4727,7 @@ enc/koi8_r.$(OBJEXT): internal/intern/re.h
enc/koi8_r.$(OBJEXT): internal/intern/ruby.h
enc/koi8_r.$(OBJEXT): internal/intern/select.h
enc/koi8_r.$(OBJEXT): internal/intern/select/largesize.h
enc/koi8_r.$(OBJEXT): internal/intern/set.h
enc/koi8_r.$(OBJEXT): internal/intern/signal.h
enc/koi8_r.$(OBJEXT): internal/intern/sprintf.h
enc/koi8_r.$(OBJEXT): internal/intern/string.h
@ -4861,6 +4889,7 @@ enc/koi8_u.$(OBJEXT): internal/intern/re.h
enc/koi8_u.$(OBJEXT): internal/intern/ruby.h
enc/koi8_u.$(OBJEXT): internal/intern/select.h
enc/koi8_u.$(OBJEXT): internal/intern/select/largesize.h
enc/koi8_u.$(OBJEXT): internal/intern/set.h
enc/koi8_u.$(OBJEXT): internal/intern/signal.h
enc/koi8_u.$(OBJEXT): internal/intern/sprintf.h
enc/koi8_u.$(OBJEXT): internal/intern/string.h
@ -5025,6 +5054,7 @@ enc/shift_jis.$(OBJEXT): internal/intern/re.h
enc/shift_jis.$(OBJEXT): internal/intern/ruby.h
enc/shift_jis.$(OBJEXT): internal/intern/select.h
enc/shift_jis.$(OBJEXT): internal/intern/select/largesize.h
enc/shift_jis.$(OBJEXT): internal/intern/set.h
enc/shift_jis.$(OBJEXT): internal/intern/signal.h
enc/shift_jis.$(OBJEXT): internal/intern/sprintf.h
enc/shift_jis.$(OBJEXT): internal/intern/string.h
@ -5185,6 +5215,7 @@ enc/trans/big5.$(OBJEXT): internal/intern/re.h
enc/trans/big5.$(OBJEXT): internal/intern/ruby.h
enc/trans/big5.$(OBJEXT): internal/intern/select.h
enc/trans/big5.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/big5.$(OBJEXT): internal/intern/set.h
enc/trans/big5.$(OBJEXT): internal/intern/signal.h
enc/trans/big5.$(OBJEXT): internal/intern/sprintf.h
enc/trans/big5.$(OBJEXT): internal/intern/string.h
@ -5344,6 +5375,7 @@ enc/trans/cesu_8.$(OBJEXT): internal/intern/re.h
enc/trans/cesu_8.$(OBJEXT): internal/intern/ruby.h
enc/trans/cesu_8.$(OBJEXT): internal/intern/select.h
enc/trans/cesu_8.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/cesu_8.$(OBJEXT): internal/intern/set.h
enc/trans/cesu_8.$(OBJEXT): internal/intern/signal.h
enc/trans/cesu_8.$(OBJEXT): internal/intern/sprintf.h
enc/trans/cesu_8.$(OBJEXT): internal/intern/string.h
@ -5503,6 +5535,7 @@ enc/trans/chinese.$(OBJEXT): internal/intern/re.h
enc/trans/chinese.$(OBJEXT): internal/intern/ruby.h
enc/trans/chinese.$(OBJEXT): internal/intern/select.h
enc/trans/chinese.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/chinese.$(OBJEXT): internal/intern/set.h
enc/trans/chinese.$(OBJEXT): internal/intern/signal.h
enc/trans/chinese.$(OBJEXT): internal/intern/sprintf.h
enc/trans/chinese.$(OBJEXT): internal/intern/string.h
@ -5662,6 +5695,7 @@ enc/trans/ebcdic.$(OBJEXT): internal/intern/re.h
enc/trans/ebcdic.$(OBJEXT): internal/intern/ruby.h
enc/trans/ebcdic.$(OBJEXT): internal/intern/select.h
enc/trans/ebcdic.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/ebcdic.$(OBJEXT): internal/intern/set.h
enc/trans/ebcdic.$(OBJEXT): internal/intern/signal.h
enc/trans/ebcdic.$(OBJEXT): internal/intern/sprintf.h
enc/trans/ebcdic.$(OBJEXT): internal/intern/string.h
@ -5821,6 +5855,7 @@ enc/trans/emoji.$(OBJEXT): internal/intern/re.h
enc/trans/emoji.$(OBJEXT): internal/intern/ruby.h
enc/trans/emoji.$(OBJEXT): internal/intern/select.h
enc/trans/emoji.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/emoji.$(OBJEXT): internal/intern/set.h
enc/trans/emoji.$(OBJEXT): internal/intern/signal.h
enc/trans/emoji.$(OBJEXT): internal/intern/sprintf.h
enc/trans/emoji.$(OBJEXT): internal/intern/string.h
@ -5980,6 +6015,7 @@ enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/intern/re.h
enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/intern/ruby.h
enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/intern/select.h
enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/intern/set.h
enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/intern/signal.h
enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/intern/sprintf.h
enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/intern/string.h
@ -6139,6 +6175,7 @@ enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/intern/re.h
enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/intern/ruby.h
enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/intern/select.h
enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/intern/set.h
enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/intern/signal.h
enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/intern/sprintf.h
enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/intern/string.h
@ -6298,6 +6335,7 @@ enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/intern/re.h
enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/intern/ruby.h
enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/intern/select.h
enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/intern/set.h
enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/intern/signal.h
enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/intern/sprintf.h
enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/intern/string.h
@ -6457,6 +6495,7 @@ enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/intern/re.h
enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/intern/ruby.h
enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/intern/select.h
enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/intern/set.h
enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/intern/signal.h
enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/intern/sprintf.h
enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/intern/string.h
@ -6616,6 +6655,7 @@ enc/trans/escape.$(OBJEXT): internal/intern/re.h
enc/trans/escape.$(OBJEXT): internal/intern/ruby.h
enc/trans/escape.$(OBJEXT): internal/intern/select.h
enc/trans/escape.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/escape.$(OBJEXT): internal/intern/set.h
enc/trans/escape.$(OBJEXT): internal/intern/signal.h
enc/trans/escape.$(OBJEXT): internal/intern/sprintf.h
enc/trans/escape.$(OBJEXT): internal/intern/string.h
@ -6775,6 +6815,7 @@ enc/trans/gb18030.$(OBJEXT): internal/intern/re.h
enc/trans/gb18030.$(OBJEXT): internal/intern/ruby.h
enc/trans/gb18030.$(OBJEXT): internal/intern/select.h
enc/trans/gb18030.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/gb18030.$(OBJEXT): internal/intern/set.h
enc/trans/gb18030.$(OBJEXT): internal/intern/signal.h
enc/trans/gb18030.$(OBJEXT): internal/intern/sprintf.h
enc/trans/gb18030.$(OBJEXT): internal/intern/string.h
@ -6934,6 +6975,7 @@ enc/trans/gbk.$(OBJEXT): internal/intern/re.h
enc/trans/gbk.$(OBJEXT): internal/intern/ruby.h
enc/trans/gbk.$(OBJEXT): internal/intern/select.h
enc/trans/gbk.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/gbk.$(OBJEXT): internal/intern/set.h
enc/trans/gbk.$(OBJEXT): internal/intern/signal.h
enc/trans/gbk.$(OBJEXT): internal/intern/sprintf.h
enc/trans/gbk.$(OBJEXT): internal/intern/string.h
@ -7094,6 +7136,7 @@ enc/trans/iso2022.$(OBJEXT): internal/intern/re.h
enc/trans/iso2022.$(OBJEXT): internal/intern/ruby.h
enc/trans/iso2022.$(OBJEXT): internal/intern/select.h
enc/trans/iso2022.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/iso2022.$(OBJEXT): internal/intern/set.h
enc/trans/iso2022.$(OBJEXT): internal/intern/signal.h
enc/trans/iso2022.$(OBJEXT): internal/intern/sprintf.h
enc/trans/iso2022.$(OBJEXT): internal/intern/string.h
@ -7253,6 +7296,7 @@ enc/trans/japanese.$(OBJEXT): internal/intern/re.h
enc/trans/japanese.$(OBJEXT): internal/intern/ruby.h
enc/trans/japanese.$(OBJEXT): internal/intern/select.h
enc/trans/japanese.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/japanese.$(OBJEXT): internal/intern/set.h
enc/trans/japanese.$(OBJEXT): internal/intern/signal.h
enc/trans/japanese.$(OBJEXT): internal/intern/sprintf.h
enc/trans/japanese.$(OBJEXT): internal/intern/string.h
@ -7412,6 +7456,7 @@ enc/trans/japanese_euc.$(OBJEXT): internal/intern/re.h
enc/trans/japanese_euc.$(OBJEXT): internal/intern/ruby.h
enc/trans/japanese_euc.$(OBJEXT): internal/intern/select.h
enc/trans/japanese_euc.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/japanese_euc.$(OBJEXT): internal/intern/set.h
enc/trans/japanese_euc.$(OBJEXT): internal/intern/signal.h
enc/trans/japanese_euc.$(OBJEXT): internal/intern/sprintf.h
enc/trans/japanese_euc.$(OBJEXT): internal/intern/string.h
@ -7571,6 +7616,7 @@ enc/trans/japanese_sjis.$(OBJEXT): internal/intern/re.h
enc/trans/japanese_sjis.$(OBJEXT): internal/intern/ruby.h
enc/trans/japanese_sjis.$(OBJEXT): internal/intern/select.h
enc/trans/japanese_sjis.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/japanese_sjis.$(OBJEXT): internal/intern/set.h
enc/trans/japanese_sjis.$(OBJEXT): internal/intern/signal.h
enc/trans/japanese_sjis.$(OBJEXT): internal/intern/sprintf.h
enc/trans/japanese_sjis.$(OBJEXT): internal/intern/string.h
@ -7730,6 +7776,7 @@ enc/trans/korean.$(OBJEXT): internal/intern/re.h
enc/trans/korean.$(OBJEXT): internal/intern/ruby.h
enc/trans/korean.$(OBJEXT): internal/intern/select.h
enc/trans/korean.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/korean.$(OBJEXT): internal/intern/set.h
enc/trans/korean.$(OBJEXT): internal/intern/signal.h
enc/trans/korean.$(OBJEXT): internal/intern/sprintf.h
enc/trans/korean.$(OBJEXT): internal/intern/string.h
@ -7888,6 +7935,7 @@ enc/trans/newline.$(OBJEXT): internal/intern/re.h
enc/trans/newline.$(OBJEXT): internal/intern/ruby.h
enc/trans/newline.$(OBJEXT): internal/intern/select.h
enc/trans/newline.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/newline.$(OBJEXT): internal/intern/set.h
enc/trans/newline.$(OBJEXT): internal/intern/signal.h
enc/trans/newline.$(OBJEXT): internal/intern/sprintf.h
enc/trans/newline.$(OBJEXT): internal/intern/string.h
@ -8047,6 +8095,7 @@ enc/trans/single_byte.$(OBJEXT): internal/intern/re.h
enc/trans/single_byte.$(OBJEXT): internal/intern/ruby.h
enc/trans/single_byte.$(OBJEXT): internal/intern/select.h
enc/trans/single_byte.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/single_byte.$(OBJEXT): internal/intern/set.h
enc/trans/single_byte.$(OBJEXT): internal/intern/signal.h
enc/trans/single_byte.$(OBJEXT): internal/intern/sprintf.h
enc/trans/single_byte.$(OBJEXT): internal/intern/string.h
@ -8206,6 +8255,7 @@ enc/trans/transdb.$(OBJEXT): internal/intern/re.h
enc/trans/transdb.$(OBJEXT): internal/intern/ruby.h
enc/trans/transdb.$(OBJEXT): internal/intern/select.h
enc/trans/transdb.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/transdb.$(OBJEXT): internal/intern/set.h
enc/trans/transdb.$(OBJEXT): internal/intern/signal.h
enc/trans/transdb.$(OBJEXT): internal/intern/sprintf.h
enc/trans/transdb.$(OBJEXT): internal/intern/string.h
@ -8366,6 +8416,7 @@ enc/trans/utf8_mac.$(OBJEXT): internal/intern/re.h
enc/trans/utf8_mac.$(OBJEXT): internal/intern/ruby.h
enc/trans/utf8_mac.$(OBJEXT): internal/intern/select.h
enc/trans/utf8_mac.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/utf8_mac.$(OBJEXT): internal/intern/set.h
enc/trans/utf8_mac.$(OBJEXT): internal/intern/signal.h
enc/trans/utf8_mac.$(OBJEXT): internal/intern/sprintf.h
enc/trans/utf8_mac.$(OBJEXT): internal/intern/string.h
@ -8525,6 +8576,7 @@ enc/trans/utf_16_32.$(OBJEXT): internal/intern/re.h
enc/trans/utf_16_32.$(OBJEXT): internal/intern/ruby.h
enc/trans/utf_16_32.$(OBJEXT): internal/intern/select.h
enc/trans/utf_16_32.$(OBJEXT): internal/intern/select/largesize.h
enc/trans/utf_16_32.$(OBJEXT): internal/intern/set.h
enc/trans/utf_16_32.$(OBJEXT): internal/intern/signal.h
enc/trans/utf_16_32.$(OBJEXT): internal/intern/sprintf.h
enc/trans/utf_16_32.$(OBJEXT): internal/intern/string.h
@ -8687,6 +8739,7 @@ enc/unicode.$(OBJEXT): internal/intern/re.h
enc/unicode.$(OBJEXT): internal/intern/ruby.h
enc/unicode.$(OBJEXT): internal/intern/select.h
enc/unicode.$(OBJEXT): internal/intern/select/largesize.h
enc/unicode.$(OBJEXT): internal/intern/set.h
enc/unicode.$(OBJEXT): internal/intern/signal.h
enc/unicode.$(OBJEXT): internal/intern/sprintf.h
enc/unicode.$(OBJEXT): internal/intern/string.h
@ -8858,6 +8911,7 @@ enc/us_ascii.$(OBJEXT): internal/intern/re.h
enc/us_ascii.$(OBJEXT): internal/intern/ruby.h
enc/us_ascii.$(OBJEXT): internal/intern/select.h
enc/us_ascii.$(OBJEXT): internal/intern/select/largesize.h
enc/us_ascii.$(OBJEXT): internal/intern/set.h
enc/us_ascii.$(OBJEXT): internal/intern/signal.h
enc/us_ascii.$(OBJEXT): internal/intern/sprintf.h
enc/us_ascii.$(OBJEXT): internal/intern/string.h
@ -9021,6 +9075,7 @@ enc/utf_16be.$(OBJEXT): internal/intern/re.h
enc/utf_16be.$(OBJEXT): internal/intern/ruby.h
enc/utf_16be.$(OBJEXT): internal/intern/select.h
enc/utf_16be.$(OBJEXT): internal/intern/select/largesize.h
enc/utf_16be.$(OBJEXT): internal/intern/set.h
enc/utf_16be.$(OBJEXT): internal/intern/signal.h
enc/utf_16be.$(OBJEXT): internal/intern/sprintf.h
enc/utf_16be.$(OBJEXT): internal/intern/string.h
@ -9183,6 +9238,7 @@ enc/utf_16le.$(OBJEXT): internal/intern/re.h
enc/utf_16le.$(OBJEXT): internal/intern/ruby.h
enc/utf_16le.$(OBJEXT): internal/intern/select.h
enc/utf_16le.$(OBJEXT): internal/intern/select/largesize.h
enc/utf_16le.$(OBJEXT): internal/intern/set.h
enc/utf_16le.$(OBJEXT): internal/intern/signal.h
enc/utf_16le.$(OBJEXT): internal/intern/sprintf.h
enc/utf_16le.$(OBJEXT): internal/intern/string.h
@ -9345,6 +9401,7 @@ enc/utf_32be.$(OBJEXT): internal/intern/re.h
enc/utf_32be.$(OBJEXT): internal/intern/ruby.h
enc/utf_32be.$(OBJEXT): internal/intern/select.h
enc/utf_32be.$(OBJEXT): internal/intern/select/largesize.h
enc/utf_32be.$(OBJEXT): internal/intern/set.h
enc/utf_32be.$(OBJEXT): internal/intern/signal.h
enc/utf_32be.$(OBJEXT): internal/intern/sprintf.h
enc/utf_32be.$(OBJEXT): internal/intern/string.h
@ -9507,6 +9564,7 @@ enc/utf_32le.$(OBJEXT): internal/intern/re.h
enc/utf_32le.$(OBJEXT): internal/intern/ruby.h
enc/utf_32le.$(OBJEXT): internal/intern/select.h
enc/utf_32le.$(OBJEXT): internal/intern/select/largesize.h
enc/utf_32le.$(OBJEXT): internal/intern/set.h
enc/utf_32le.$(OBJEXT): internal/intern/signal.h
enc/utf_32le.$(OBJEXT): internal/intern/sprintf.h
enc/utf_32le.$(OBJEXT): internal/intern/string.h
@ -9678,6 +9736,7 @@ enc/utf_8.$(OBJEXT): internal/intern/re.h
enc/utf_8.$(OBJEXT): internal/intern/ruby.h
enc/utf_8.$(OBJEXT): internal/intern/select.h
enc/utf_8.$(OBJEXT): internal/intern/select/largesize.h
enc/utf_8.$(OBJEXT): internal/intern/set.h
enc/utf_8.$(OBJEXT): internal/intern/signal.h
enc/utf_8.$(OBJEXT): internal/intern/sprintf.h
enc/utf_8.$(OBJEXT): internal/intern/string.h
@ -9841,6 +9900,7 @@ enc/windows_1250.$(OBJEXT): internal/intern/re.h
enc/windows_1250.$(OBJEXT): internal/intern/ruby.h
enc/windows_1250.$(OBJEXT): internal/intern/select.h
enc/windows_1250.$(OBJEXT): internal/intern/select/largesize.h
enc/windows_1250.$(OBJEXT): internal/intern/set.h
enc/windows_1250.$(OBJEXT): internal/intern/signal.h
enc/windows_1250.$(OBJEXT): internal/intern/sprintf.h
enc/windows_1250.$(OBJEXT): internal/intern/string.h
@ -10002,6 +10062,7 @@ enc/windows_1251.$(OBJEXT): internal/intern/re.h
enc/windows_1251.$(OBJEXT): internal/intern/ruby.h
enc/windows_1251.$(OBJEXT): internal/intern/select.h
enc/windows_1251.$(OBJEXT): internal/intern/select/largesize.h
enc/windows_1251.$(OBJEXT): internal/intern/set.h
enc/windows_1251.$(OBJEXT): internal/intern/signal.h
enc/windows_1251.$(OBJEXT): internal/intern/sprintf.h
enc/windows_1251.$(OBJEXT): internal/intern/string.h
@ -10164,6 +10225,7 @@ enc/windows_1252.$(OBJEXT): internal/intern/re.h
enc/windows_1252.$(OBJEXT): internal/intern/ruby.h
enc/windows_1252.$(OBJEXT): internal/intern/select.h
enc/windows_1252.$(OBJEXT): internal/intern/select/largesize.h
enc/windows_1252.$(OBJEXT): internal/intern/set.h
enc/windows_1252.$(OBJEXT): internal/intern/signal.h
enc/windows_1252.$(OBJEXT): internal/intern/sprintf.h
enc/windows_1252.$(OBJEXT): internal/intern/string.h
@ -10325,6 +10387,7 @@ enc/windows_1253.$(OBJEXT): internal/intern/re.h
enc/windows_1253.$(OBJEXT): internal/intern/ruby.h
enc/windows_1253.$(OBJEXT): internal/intern/select.h
enc/windows_1253.$(OBJEXT): internal/intern/select/largesize.h
enc/windows_1253.$(OBJEXT): internal/intern/set.h
enc/windows_1253.$(OBJEXT): internal/intern/signal.h
enc/windows_1253.$(OBJEXT): internal/intern/sprintf.h
enc/windows_1253.$(OBJEXT): internal/intern/string.h
@ -10487,6 +10550,7 @@ enc/windows_1254.$(OBJEXT): internal/intern/re.h
enc/windows_1254.$(OBJEXT): internal/intern/ruby.h
enc/windows_1254.$(OBJEXT): internal/intern/select.h
enc/windows_1254.$(OBJEXT): internal/intern/select/largesize.h
enc/windows_1254.$(OBJEXT): internal/intern/set.h
enc/windows_1254.$(OBJEXT): internal/intern/signal.h
enc/windows_1254.$(OBJEXT): internal/intern/sprintf.h
enc/windows_1254.$(OBJEXT): internal/intern/string.h
@ -10649,6 +10713,7 @@ enc/windows_1257.$(OBJEXT): internal/intern/re.h
enc/windows_1257.$(OBJEXT): internal/intern/ruby.h
enc/windows_1257.$(OBJEXT): internal/intern/select.h
enc/windows_1257.$(OBJEXT): internal/intern/select/largesize.h
enc/windows_1257.$(OBJEXT): internal/intern/set.h
enc/windows_1257.$(OBJEXT): internal/intern/signal.h
enc/windows_1257.$(OBJEXT): internal/intern/sprintf.h
enc/windows_1257.$(OBJEXT): internal/intern/string.h
@ -10813,6 +10878,7 @@ enc/windows_31j.$(OBJEXT): internal/intern/re.h
enc/windows_31j.$(OBJEXT): internal/intern/ruby.h
enc/windows_31j.$(OBJEXT): internal/intern/select.h
enc/windows_31j.$(OBJEXT): internal/intern/select/largesize.h
enc/windows_31j.$(OBJEXT): internal/intern/set.h
enc/windows_31j.$(OBJEXT): internal/intern/signal.h
enc/windows_31j.$(OBJEXT): internal/intern/sprintf.h
enc/windows_31j.$(OBJEXT): internal/intern/string.h

View file

@ -3943,7 +3943,7 @@ static const OnigCodePoint CR_XDigit[] = {
/* 'Word': [[:Word:]] */
static const OnigCodePoint CR_Word[] = {
795,
796,
0x0030, 0x0039,
0x0041, 0x005a,
0x005f, 0x005f,
@ -4241,6 +4241,7 @@ static const OnigCodePoint CR_Word[] = {
0x1fe0, 0x1fec,
0x1ff2, 0x1ff4,
0x1ff6, 0x1ffc,
0x200c, 0x200d,
0x203f, 0x2040,
0x2054, 0x2054,
0x2071, 0x2071,

View file

@ -24,6 +24,7 @@
#include "internal/string.h"
#include "internal/vm.h"
#include "regenc.h"
#include "ruby/atomic.h"
#include "ruby/encoding.h"
#include "ruby/util.h"
#include "ruby_assert.h"
@ -60,6 +61,7 @@ VALUE rb_cEncoding;
static VALUE rb_encoding_list;
struct rb_encoding_entry {
rb_atomic_t loaded;
const char *name;
rb_encoding *enc;
rb_encoding *base;
@ -93,15 +95,11 @@ static rb_encoding *global_enc_ascii,
*global_enc_utf_8,
*global_enc_us_ascii;
#define GLOBAL_ENC_TABLE_ENTER(enc_table) struct enc_table *enc_table = &global_enc_table; RB_VM_LOCK_ENTER()
#define GLOBAL_ENC_TABLE_LEAVE() RB_VM_LOCK_LEAVE()
#define GLOBAL_ENC_TABLE_EVAL(enc_table, expr) do { \
GLOBAL_ENC_TABLE_ENTER(enc_table); \
{ \
expr; \
} \
GLOBAL_ENC_TABLE_LEAVE(); \
} while (0)
#define GLOBAL_ENC_TABLE_LOCKING(tbl) \
for (struct enc_table *tbl = &global_enc_table, **locking = &tbl; \
locking; \
locking = NULL) \
RB_VM_LOCKING()
#define ENC_DUMMY_FLAG (1<<24)
@ -348,6 +346,8 @@ enc_table_expand(struct enc_table *enc_table, int newsize)
static int
enc_register_at(struct enc_table *enc_table, int index, const char *name, rb_encoding *base_encoding)
{
ASSERT_vm_locking();
struct rb_encoding_entry *ent = &enc_table->list[index];
rb_raw_encoding *encoding;
@ -362,6 +362,7 @@ enc_register_at(struct enc_table *enc_table, int index, const char *name, rb_enc
if (!encoding) {
encoding = xmalloc(sizeof(rb_encoding));
}
if (base_encoding) {
*encoding = *base_encoding;
}
@ -374,12 +375,18 @@ enc_register_at(struct enc_table *enc_table, int index, const char *name, rb_enc
st_insert(enc_table->names, (st_data_t)name, (st_data_t)index);
enc_list_update(index, encoding);
// max_enc_len is used to mark a fully loaded encoding.
RUBY_ATOMIC_SET(ent->loaded, encoding->max_enc_len);
return index;
}
static int
enc_register(struct enc_table *enc_table, const char *name, rb_encoding *encoding)
{
ASSERT_vm_locking();
int index = enc_table->count;
enc_table->count = enc_table_expand(enc_table, index + 1);
@ -409,8 +416,7 @@ rb_enc_register(const char *name, rb_encoding *encoding)
{
int index;
GLOBAL_ENC_TABLE_ENTER(enc_table);
{
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
index = enc_registered(enc_table, name);
if (index >= 0) {
@ -430,13 +436,13 @@ rb_enc_register(const char *name, rb_encoding *encoding)
set_encoding_const(name, rb_enc_from_index(index));
}
}
GLOBAL_ENC_TABLE_LEAVE();
return index;
}
int
enc_registered(struct enc_table *enc_table, const char *name)
{
ASSERT_vm_locking();
st_data_t idx = 0;
if (!name) return -1;
@ -450,15 +456,13 @@ enc_registered(struct enc_table *enc_table, const char *name)
void
rb_encdb_declare(const char *name)
{
GLOBAL_ENC_TABLE_ENTER(enc_table);
{
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
int idx = enc_registered(enc_table, name);
if (idx < 0) {
idx = enc_register(enc_table, name, 0);
}
set_encoding_const(name, rb_enc_from_index(idx));
}
GLOBAL_ENC_TABLE_LEAVE();
}
static void
@ -490,13 +494,11 @@ set_base_encoding(struct enc_table *enc_table, int index, rb_encoding *base)
void
rb_enc_set_base(const char *name, const char *orig)
{
GLOBAL_ENC_TABLE_ENTER(enc_table);
{
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
int idx = enc_registered(enc_table, name);
int origidx = enc_registered(enc_table, orig);
set_base_encoding(enc_table, idx, rb_enc_from_index(origidx));
}
GLOBAL_ENC_TABLE_LEAVE();
}
/* for encdb.h
@ -547,8 +549,7 @@ rb_encdb_replicate(const char *name, const char *orig)
{
int r;
GLOBAL_ENC_TABLE_ENTER(enc_table);
{
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
int origidx = enc_registered(enc_table, orig);
int idx = enc_registered(enc_table, name);
@ -557,7 +558,6 @@ rb_encdb_replicate(const char *name, const char *orig)
}
r = enc_replicate_with_index(enc_table, name, rb_enc_from_index(origidx), idx);
}
GLOBAL_ENC_TABLE_LEAVE();
return r;
}
@ -567,13 +567,11 @@ rb_define_dummy_encoding(const char *name)
{
int index;
GLOBAL_ENC_TABLE_ENTER(enc_table);
{
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
index = enc_replicate(enc_table, name, rb_ascii8bit_encoding());
rb_encoding *enc = enc_table->list[index].enc;
ENC_SET_DUMMY((rb_raw_encoding *)enc);
}
GLOBAL_ENC_TABLE_LEAVE();
return index;
}
@ -583,15 +581,13 @@ rb_encdb_dummy(const char *name)
{
int index;
GLOBAL_ENC_TABLE_ENTER(enc_table);
{
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
index = enc_replicate_with_index(enc_table, name,
rb_ascii8bit_encoding(),
enc_registered(enc_table, name));
rb_encoding *enc = enc_table->list[index].enc;
ENC_SET_DUMMY((rb_raw_encoding *)enc);
}
GLOBAL_ENC_TABLE_LEAVE();
return index;
}
@ -653,6 +649,7 @@ enc_dup_name(st_data_t name)
static int
enc_alias_internal(struct enc_table *enc_table, const char *alias, int idx)
{
ASSERT_vm_locking();
return st_insert2(enc_table->names, (st_data_t)alias, (st_data_t)idx,
enc_dup_name);
}
@ -671,8 +668,7 @@ rb_enc_alias(const char *alias, const char *orig)
{
int idx, r;
GLOBAL_ENC_TABLE_ENTER(enc_table);
{
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
enc_check_addable(enc_table, alias);
if ((idx = rb_enc_find_index(orig)) < 0) {
r = -1;
@ -681,7 +677,6 @@ rb_enc_alias(const char *alias, const char *orig)
r = enc_alias(enc_table, alias, idx);
}
}
GLOBAL_ENC_TABLE_LEAVE();
return r;
}
@ -691,8 +686,7 @@ rb_encdb_alias(const char *alias, const char *orig)
{
int r;
GLOBAL_ENC_TABLE_ENTER(enc_table);
{
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
int idx = enc_registered(enc_table, orig);
if (idx < 0) {
@ -700,7 +694,6 @@ rb_encdb_alias(const char *alias, const char *orig)
}
r = enc_alias(enc_table, alias, idx);
}
GLOBAL_ENC_TABLE_LEAVE();
return r;
}
@ -708,6 +701,7 @@ rb_encdb_alias(const char *alias, const char *orig)
static void
rb_enc_init(struct enc_table *enc_table)
{
ASSERT_vm_locking();
enc_table_expand(enc_table, ENCODING_COUNT + 1);
if (!enc_table->names) {
enc_table->names = st_init_strcasetable_with_size(ENCODING_LIST_CAPA);
@ -767,8 +761,7 @@ load_encoding(const char *name)
ruby_debug = debug;
rb_set_errinfo(errinfo);
GLOBAL_ENC_TABLE_ENTER(enc_table);
{
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
if (loaded < 0 || 1 < loaded) {
idx = -1;
}
@ -779,51 +772,74 @@ load_encoding(const char *name)
idx = -1;
}
}
GLOBAL_ENC_TABLE_LEAVE();
return idx;
}
static int
enc_autoload_body(struct enc_table *enc_table, rb_encoding *enc)
enc_autoload_body(rb_encoding *enc)
{
rb_encoding *base = enc_table->list[ENC_TO_ENCINDEX(enc)].base;
rb_encoding *base;
int i = 0;
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
base = enc_table->list[ENC_TO_ENCINDEX(enc)].base;
if (base) {
do {
if (i >= enc_table->count) {
i = -1;
break;
}
} while (enc_table->list[i].enc != base && (++i, 1));
}
}
if (i == -1) return -1;
if (base) {
int i = 0;
do {
if (i >= enc_table->count) return -1;
} while (enc_table->list[i].enc != base && (++i, 1));
if (rb_enc_autoload_p(base)) {
if (rb_enc_autoload(base) < 0) return -1;
}
i = enc->ruby_encoding_index;
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
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;
return i;
}
else {
return -2;
}
}
int
rb_enc_autoload(rb_encoding *enc)
{
int i;
GLOBAL_ENC_TABLE_EVAL(enc_table, i = enc_autoload_body(enc_table, enc));
int i = enc_autoload_body(enc);
if (i == -2) {
i = load_encoding(rb_enc_name(enc));
}
return i;
}
bool
rb_enc_autoload_p(rb_encoding *enc)
{
int idx = ENC_TO_ENCINDEX(enc);
RUBY_ASSERT(rb_enc_from_index(idx) == enc);
return !RUBY_ATOMIC_LOAD(global_enc_table.list[idx].loaded);
}
/* Return encoding index or UNSPECIFIED_ENCODING from encoding name */
int
rb_enc_find_index(const char *name)
{
int i = enc_registered(&global_enc_table, name);
int i;
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
i = enc_registered(enc_table, name);
}
rb_encoding *enc;
if (i < 0) {
@ -1504,16 +1520,16 @@ rb_locale_encindex(void)
if (idx < 0) idx = ENCINDEX_UTF_8;
if (enc_registered(&global_enc_table, "locale") < 0) {
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
if (enc_registered(enc_table, "locale") < 0) {
# if defined _WIN32
void Init_w32_codepage(void);
Init_w32_codepage();
# endif
GLOBAL_ENC_TABLE_ENTER(enc_table);
{
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
enc_alias_internal(enc_table, "locale", idx);
}
GLOBAL_ENC_TABLE_LEAVE();
}
}
return idx;
@ -1528,7 +1544,10 @@ rb_locale_encoding(void)
int
rb_filesystem_encindex(void)
{
int idx = enc_registered(&global_enc_table, "filesystem");
int idx;
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
idx = enc_registered(enc_table, "filesystem");
}
if (idx < 0) idx = ENCINDEX_ASCII_8BIT;
return idx;
}
@ -1555,8 +1574,7 @@ enc_set_default_encoding(struct default_encoding *def, VALUE encoding, const cha
/* Already set */
overridden = TRUE;
GLOBAL_ENC_TABLE_ENTER(enc_table);
{
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
if (NIL_P(encoding)) {
def->index = -1;
def->enc = 0;
@ -1580,7 +1598,6 @@ enc_set_default_encoding(struct default_encoding *def, VALUE encoding, const cha
enc_alias_internal(enc_table, "filesystem", Init_enc_set_filesystem_encoding());
}
}
GLOBAL_ENC_TABLE_LEAVE();
return overridden;
}

5
enum.c
View file

@ -1215,14 +1215,15 @@ tally_up(st_data_t *group, st_data_t *value, st_data_t arg, int existing)
RB_OBJ_WRITTEN(hash, Qundef, tally);
}
*value = (st_data_t)tally;
if (!SPECIAL_CONST_P(*group)) RB_OBJ_WRITTEN(hash, Qundef, *group);
return ST_CONTINUE;
}
static VALUE
rb_enum_tally_up(VALUE hash, VALUE group)
{
rb_hash_stlike_update(hash, group, tally_up, (st_data_t)hash);
if (!rb_hash_stlike_update(hash, group, tally_up, (st_data_t)hash)) {
RB_OBJ_WRITTEN(hash, Qundef, group);
}
return hash;
}

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