diff options
1753 files changed, 79005 insertions, 59084 deletions
diff --git a/.appveyor.yml b/.appveyor.yml index 9bc135873c..c8bf89e0d6 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -10,21 +10,29 @@ shallow_clone: true clone_depth: 10 platform: - x64 +skip_commits: + message: /^\[DOC\]/ + files: + - doc/* + - '**/*.md' + - '**/*.rdoc' environment: ruby_version: "24-%Platform%" - zlib_version: "1.2.11" + zlib_version: "1.2.12" matrix: - build: vs vs: 120 ssl: OpenSSL - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 GEMS_FOR_TEST: "" - build: vs vs: 140 ssl: OpenSSL-v111 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 GEMS_FOR_TEST: "" - RELINE_TEST_ENCODING: "Windows-31J" + RELINE_TEST_ENCODING: "UTF-8" +cache: + - c:\Tools\vcpkg\installed\ for: - matrix: @@ -36,6 +44,11 @@ for: - SET BITS=%Platform:x86=32% - SET BITS=%BITS:x=% - SET OPENSSL_DIR=C:\%ssl%-Win%BITS% + - cd C:\Tools\vcpkg + - git pull -q + - .\bootstrap-vcpkg.bat + - cd %APPVEYOR_BUILD_FOLDER% + - vcpkg --triplet %Platform%-windows install libffi libyaml readline zlib - CALL SET vcvars=%%^VS%VS%COMNTOOLS^%%..\..\VC\vcvarsall.bat - SET vcvars - '"%vcvars%" %Platform:x64=amd64%' @@ -59,12 +72,18 @@ for: - if not exist %ZLIB_ZIP% curl -fsSL -o %ZLIB_ZIP% --retry 10 https://zlib.net/zlib%zlib_version:.=%.zip - 7z x -aos -o%APPVEYOR_BUILD_FOLDER%\ext\zlib %ZLIB_ZIP% - for %%I in (%OPENSSL_DIR%\*.dll) do mklink /h \usr\local\bin\%%~nxI %%I + - for %%I in (c:\Tools\vcpkg\installed\%Platform%-windows\bin\*.dll) do ( + if not %%~nI == readline mklink \usr\local\bin\%%~nxI %%I + ) - attrib +r /s /d - mkdir %Platform%-mswin_%vs% build_script: - cd %APPVEYOR_BUILD_FOLDER% - cd %Platform%-mswin_%vs% - - ..\win32\configure.bat --without-ext=+,dbm,gdbm,readline --with-opt-dir=/usr/local --with-openssl-dir=%OPENSSL_DIR:\=/% + - >- + ..\win32\configure.bat + --with-opt-dir="/usr/local;c:/Tools/vcpkg/installed/%Platform%-windows" + --with-openssl-dir=%OPENSSL_DIR:\=/% - nmake -l - nmake install-nodoc - \usr\bin\ruby -v -e "p :locale => Encoding.find('locale'), :filesystem => Encoding.find('filesystem')" @@ -74,9 +93,26 @@ for: - set /a JOBS=%NUMBER_OF_PROCESSORS% - nmake -l "TESTOPTS=-v -q" btest - nmake -l "TESTOPTS=-v -q" test-basic - - nmake -l "TESTOPTS=-v --timeout-scale=3.0 --excludes=../test/excludes/_appveyor -j%JOBS% --exclude readline --exclude win32ole --exclude test_bignum --exclude test_syntax --exclude test_open-uri --exclude test_bundled_ca" test-all + - >- + nmake -l "TESTOPTS=-v --timeout-scale=3.0 + --excludes=../test/excludes/_appveyor -j%JOBS% + --exclude win32ole + --exclude test_bignum + --exclude test_syntax + --exclude test_open-uri + --exclude test_bundled_ca + " test-all # separately execute tests without -j which may crash worker with -j. - - nmake -l "TESTOPTS=-v --timeout-scale=3.0 --excludes=../test/excludes/_appveyor" test-all TESTS="../test/win32ole ../test/ruby/test_bignum.rb ../test/ruby/test_syntax.rb ../test/open-uri/test_open-uri.rb ../test/rubygems/test_bundled_ca.rb" + - >- + nmake -l + "TESTOPTS=-v --timeout-scale=3.0 --excludes=../test/excludes/_appveyor" + TESTS=" + ../test/win32ole + ../test/ruby/test_bignum.rb + ../test/ruby/test_syntax.rb + ../test/open-uri/test_open-uri.rb + ../test/rubygems/test_bundled_ca.rb + " test-all - nmake -l test-spec MSPECOPT=-fs # not using `-j` because sometimes `mspec -j` silently dies on Windows notifications: - provider: Webhook diff --git a/.cirrus.yml b/.cirrus.yml index c8fb326c89..0cab0023c2 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -12,7 +12,7 @@ task: name: Arm64 Graviton2 / $CC skip: "changesIncludeOnly('doc/**', '**.{md,rdoc}')" arm_container: - # We use the arm64 images at http://ghcr.io/ruby/ruby-ci-image . + # We use the arm64 images at https://github.com/ruby/ruby-ci-image/pkgs/container/ruby-ci-image . image: ghcr.io/ruby/ruby-ci-image:$CC # Define the used cpu core in each matrix task. We can use total 16 cpu # cores in entire matrix. [cpu] = [total cpu: 16] / [number of tasks] @@ -22,6 +22,7 @@ numeric.rb nilclass.rb pack.rb ractor.rb +string.rb timev.rb trace_point.rb warning.rb @@ -40,7 +41,6 @@ README.ja.md COPYING COPYING.ja -CONTRIBUTING.md LEGAL diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 15abc79af6..c8d7ec5e0d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,6 +4,7 @@ # YJIT sources and tests yjit* @maximecb @xrxr @tenderlove +yjit/* @maximecb @xrxr @tenderlove doc/yjit/* @maximecb @xrxr @tenderlove bootstraptest/test_yjit* @maximecb @xrxr @tenderlove test/ruby/test_yjit* @maximecb @xrxr @tenderlove diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..b18fd29357 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: 'github-actions' + directory: '/' + schedule: + interval: 'weekly' diff --git a/.github/workflows/baseruby.yml b/.github/workflows/baseruby.yml index 24b867c7fc..7385fc4d9e 100644 --- a/.github/workflows/baseruby.yml +++ b/.github/workflows/baseruby.yml @@ -20,6 +20,7 @@ jobs: baseruby: name: BASERUBY runs-on: ubuntu-20.04 + if: ${{ !startsWith(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} strategy: matrix: ruby: @@ -28,12 +29,13 @@ jobs: # - ruby-2.4 # - ruby-2.5 # - ruby-2.6 - - ruby-2.7 +# - ruby-2.7 - ruby-3.0 + - ruby-3.1 steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v2 + - uses: actions/checkout@v3 + - uses: actions/cache@v3 with: path: .downloaded-cache key: downloaded-cache @@ -42,7 +44,7 @@ jobs: ruby-version: ${{ matrix.ruby }} bundler: none - run: echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV - - run: sudo apt-get install build-essential autoconf bison + - run: sudo apt-get install build-essential autoconf bison libyaml-dev - run: ./autogen.sh - run: ./configure --disable-install-doc - run: make common-srcs diff --git a/.github/workflows/bundled_gems.yml b/.github/workflows/bundled_gems.yml index 81ba5cd5b4..83f01d5868 100644 --- a/.github/workflows/bundled_gems.yml +++ b/.github/workflows/bundled_gems.yml @@ -1,12 +1,20 @@ name: bundled_gems on: + push: + paths: + - '.github/workflows/bundled_gems.yml' + - 'gems/bundled_gems' + pull_request: + paths: + - '.github/workflows/bundled_gems.yml' + - 'gems/bundled_gems' schedule: - cron: '45 6 * * *' jobs: update: - if: ${{ github.repository == 'ruby/ruby' }} + if: ${{ github.event_name != 'schedule' || github.repository == 'ruby/ruby' }} name: update ${{ github.workflow }} runs-on: ubuntu-latest steps: @@ -18,17 +26,64 @@ jobs: - name: Set ENV run: | echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV + echo "TODAY=$(date +%F)" >> $GITHUB_ENV - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - name: Update ${{ github.workflow }} + - uses: actions/cache@v3 + with: + path: .downloaded-cache + key: downloaded-cache-${{ github.sha }} + restore-keys: | + downloaded-cache + + - name: Download previous gems list + run: | + data=bundled_gems.json + mkdir -p .downloaded-cache + ln -s .downloaded-cache/$data . + curl -O -R -z ./$data https://stdgems.org/$data + + - name: Update bundled gems list + run: | + ruby -i~ tool/update-bundled_gems.rb gems/bundled_gems + + - name: Maintain updated gems list in NEWS run: | - ruby -i~ tool/update-bundled_gems.rb gems/${{ github.workflow }} + require 'json' + news = File.read("NEWS.md") + prev = news[/since the \*+(\d+\.\d+\.\d+)\*+/, 1] + prevs = [prev, prev.sub(/\.\d+\z/, '')] + %W[bundled].each do |type| + last = JSON.parse(File.read("#{type}_gems.json"))['gems'].filter_map do |g| + v = g['versions'].values_at(*prevs).compact.first + g = g['gem'] + g = 'RubyGems' if g == 'rubygems' + [g, v] if v + end.to_h + changed = File.foreach("gems/#{type}_gems").filter_map do |l| + next if l.start_with?("#") + g, v = l.split(" ", 3) + [g, v] unless last[g] == v + end + changed, added = changed.partition {|g, _| last[g]} + news.sub!(/^\*( +)The following #{type} gems? are updated\.\n\K(?: \1\* .*\n)*/) do + mark = "#{$1} * " + changed.map {|g, v|"#{mark}#{g} #{v}\n"}.join("") + end or next + news.sub!(/^\*( +)The following default gems are now bundled gems\.\n\K(?: \1\* .*\n)*/) do + mark = "#{$1} * " + added.map {|g, v|"#{mark}#{g} #{v}\n"}.join("") + end or next if added + File.write("NEWS.md", news) + end + shell: ruby {0} - name: Check diffs id: diff run: | - git diff --no-ext-diff --ignore-submodules --exit-code + git add -- NEWS.md + git diff --no-ext-diff --ignore-submodules --quiet -- gems/bundled_gems continue-on-error: true - name: Install libraries @@ -38,11 +93,6 @@ jobs: sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev bison autoconf ruby if: ${{ steps.diff.outcome == 'failure' }} - - uses: actions/cache@v2 - with: - path: .downloaded-cache - key: downloaded-cache - - name: Build run: | ./autogen.sh @@ -53,19 +103,31 @@ jobs: - name: Test bundled gems run: | make -s test-bundled-gems + git add -- gems/bundled_gems timeout-minutes: 30 env: RUBY_TESTOPTS: "-q --tty=no" TEST_BUNDLED_GEMS_ALLOW_FAILURES: "" if: ${{ steps.diff.outcome == 'failure' }} + - name: Show diffs + id: show + run: | + git diff --cached --color --no-ext-diff --ignore-submodules --exit-code -- + continue-on-error: true + - name: Commit run: | git pull --ff-only origin ${GITHUB_REF#refs/heads/} - git commit --message="Update ${{ github.workflow }} at $(date +%F)" gems/$file + message="Update bundled gems list at " + if [ ${{ steps.diff.outcome }} = success ]; then + git commit --message="${message}${GITHUB_SHA:0:30} [ci skip]" + else + git commit --message="${message}${TODAY}" + fi git push origin ${GITHUB_REF#refs/heads/} env: EMAIL: svn-admin@ruby-lang.org GIT_AUTHOR_NAME: git GIT_COMMITTER_NAME: git - if: ${{ steps.diff.outcome == 'failure' }} + if: ${{ github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull') && steps.show.outcome == 'failure' }} diff --git a/.github/workflows/check_dependencies.yml b/.github/workflows/check_dependencies.yml index c68e49a464..f606a86b66 100644 --- a/.github/workflows/check_dependencies.yml +++ b/.github/workflows/check_dependencies.yml @@ -22,6 +22,7 @@ jobs: os: [ubuntu-20.04] fail-fast: true runs-on: ${{ matrix.os }} + if: ${{ !startsWith(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} steps: - name: Install libraries run: | @@ -38,8 +39,8 @@ jobs: run: | git config --global advice.detachedHead 0 git config --global init.defaultBranch garbage - - uses: actions/checkout@v2 - - uses: actions/cache@v2 + - uses: actions/checkout@v3 + - uses: actions/cache@v3 with: path: .downloaded-cache key: downloaded-cache diff --git a/.github/workflows/check_misc.yml b/.github/workflows/check_misc.yml index 2a59e74066..9e35d7f432 100644 --- a/.github/workflows/check_misc.yml +++ b/.github/workflows/check_misc.yml @@ -9,7 +9,7 @@ jobs: checks: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Check if C-sources are US-ASCII run: | ! grep -r -n '[^ -~]' *.[chy] include internal win32/*.[ch] @@ -22,3 +22,76 @@ jobs: git grep -l -F -e $header -e HAVE_`echo $header | tr a-z./ A-Z__` -- . > /dev/null || echo $header done | grep -F . working-directory: include + + - uses: actions/cache@v3 + with: + path: .downloaded-cache + key: downloaded-cache-${{ github.sha }} + restore-keys: | + downloaded-cache + + - name: Download previous gems list + run: | + data=default_gems.json + mkdir -p .downloaded-cache + ln -s .downloaded-cache/$data . + curl -O -R -z ./$data https://stdgems.org/$data + + - name: Make default gems list + run: | + require 'rubygems' + $:.unshift "lib" + rgver = File.foreach("lib/rubygems.rb") do |line| + break $1 if /^\s*VERSION\s*=\s*"([^"]+)"/ =~ line + end + gems = Dir.glob("{ext,lib}/**/*.gemspec").map do |f| + spec = Gem::Specification.load(f) + "#{spec.name} #{spec.version}" + end.sort + File.open("gems/default_gems", "w") do |f| + f.puts "RubyGems #{rgver}" + f.puts gems + end + shell: ruby --disable=gems {0} + + - name: Maintain updated gems list in NEWS + run: | + require 'json' + news = File.read("NEWS.md") + prev = news[/since the \*+(\d+\.\d+\.\d+)\*+/, 1] + prevs = [prev, prev.sub(/\.\d+\z/, '')] + %W[default].each do |type| + last = JSON.parse(File.read("#{type}_gems.json"))['gems'].filter_map do |g| + v = g['versions'].values_at(*prevs).compact.first + g = g['gem'] + g = 'RubyGems' if g == 'rubygems' + [g, v] if v + end.to_h + changed = File.foreach("gems/#{type}_gems").filter_map do |l| + next if l.start_with?("#") + g, v = l.split(" ", 3) + [g, v] unless last[g] == v + end + news.sub!(/^\*( +)The following #{type} gems? are updated\.\n\K(?: \1\* .*\n)*/) do + mark = "#{$1} * " + changed.map {|g, v|"#{mark}#{g} #{v}\n"}.join("") + end or next + File.write("NEWS.md", news) + end + shell: ruby {0} + + - name: Check diffs + id: diff + run: | + git diff --color --no-ext-diff --ignore-submodules --exit-code NEWS.md + continue-on-error: true + - name: Commit + run: | + git pull --ff-only origin ${GITHUB_REF#refs/heads/} + git commit --message="Update default gems list at ${GITHUB_SHA:0:30} [ci skip]" NEWS.md + git push origin ${GITHUB_REF#refs/heads/} + env: + EMAIL: svn-admin@ruby-lang.org + GIT_AUTHOR_NAME: git + GIT_COMMITTER_NAME: git + if: ${{ github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull') && steps.diff.outcome == 'failure' }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d22da8e481..09d9135fa0 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -23,6 +23,7 @@ jobs: # CodeQL runs on ubuntu-latest and windows-latest runs-on: ubuntu-latest + if: ${{ !startsWith(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} env: enable_install_doc: no @@ -35,9 +36,9 @@ jobs: sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev bison autoconf ruby - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: .downloaded-cache key: downloaded-cache @@ -46,7 +47,7 @@ jobs: run: sudo rm /usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: config-file: ./.github/codeql/codeql-config.yml @@ -54,7 +55,7 @@ jobs: run: echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index 2c2b6598f7..261431dfb5 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -16,7 +16,7 @@ concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }} -# Github actions does not support YAML anchors. This creative use of +# GitHub actions does not support YAML anchors. This creative use of # environment variables (plus the "echo $GITHUB_ENV" hack) is to reroute that # restriction. env: @@ -25,7 +25,7 @@ env: crosshost: '' # -O1 is faster than -O3 in our tests... Majority of time are consumed trying - # to optimize binaries. Also Github Actions run on relatively modern CPUs + # to optimize binaries. Also GitHub Actions run on relatively modern CPUs # compared to, say, GCC 4 or Clang 3. We don't specify `-march=native` # because compilers tend not understand what the CPU is. optflags: '-O1' @@ -60,6 +60,8 @@ jobs: strategy: fail-fast: false matrix: + env: + - {} entry: - { key: default_cc, name: gcc-11, value: gcc-11, container: gcc-11 } - { key: default_cc, name: gcc-10, value: gcc-10, container: gcc-10 } @@ -71,10 +73,23 @@ jobs: - { key: default_cc, name: gcc-4.8, value: gcc-4.8, container: gcc-4.8 } - key: default_cc name: 'gcc-11 LTO' - value: 'gcc-11 -O2 -flto=auto -ffat-lto-objects' + value: 'gcc-11 -flto=auto -ffat-lto-objects' container: gcc-11 - shared: '--disable-shared' + configure_append: '--disable-shared optflags=-O2' # check: true + - key: default_cc + name: 'gcc-11 annocheck' + # Minimal flags to pass the check. + value: 'gcc-11 -O2 -fcf-protection -Wl,-z,now' + container: gcc-11 + env: + # FIXME: Drop skipping options + # https://bugs.ruby-lang.org/issues/18061 + # https://sourceware.org/annobin/annobin.html/Test-pie.html + # https://sourceware.org/annobin/annobin.html/Test-notes.html + TEST_ANNOCHECK_OPTS: "--skip-pie --skip-notes" + check: true + - { key: default_cc, name: clang-15, value: clang-15, container: clang-15 } - { key: default_cc, name: clang-14, value: clang-14, container: clang-14 } - { key: default_cc, name: clang-13, value: clang-13, container: clang-13 } - { key: default_cc, name: clang-12, value: clang-12, container: clang-12 } @@ -89,24 +104,30 @@ jobs: - { key: default_cc, name: clang-3.9, value: clang-3.9, container: clang-3.9 } - key: default_cc name: 'clang-14 LTO' - value: 'clang-14 -O2 -flto=auto' + value: 'clang-14 -flto=auto' container: clang-14 - shared: '--disable-shared' + configure_append: '--disable-shared optflags=-O2' # check: true - - { key: crosshost, name: aarch64-linux-gnu, value: aarch64-linux-gnu, container: crossbuild-essential-arm64 } +# - { key: crosshost, name: aarch64-linux-gnu, value: aarch64-linux-gnu, container: crossbuild-essential-arm64 } # - { key: crosshost, name: arm-linux-gnueabi, value: arm-linux-gnueabi } # - { key: crosshost, name: arm-linux-gnueabihf, value: arm-linux-gnueabihf } # - { key: crosshost, name: i686-w64-mingw32, value: i686-w64-mingw32 } # - { key: crosshost, name: powerpc-linux-gnu, value: powerpc-linux-gnu } - - { key: crosshost, name: powerpc64le-linux-gnu, value: powerpc64le-linux-gnu, container: crossbuild-essential-ppc64el } - - { key: crosshost, name: s390x-linux-gnu, value: s390x-linux-gnu, container: crossbuild-essential-s390x } - - { key: crosshost, name: x86_64-w64-mingw32, value: x86_64-w64-mingw32, container: mingw-w64 } - - - { key: append_cc, name: c99, value: '-std=c99 -Werror=pedantic -pedantic-errors' } -# - { key: append_cc, name: c11, value: '-std=c11 -Werror=pedantic -pedantic-errors' } -# - { key: append_cc, name: c17, value: '-std=c17 -Werror=pedantic -pedantic-errors' } - - { key: append_cc, name: c2x, value: '-std=c2x -Werror=pedantic -pedantic-errors' } +# - { key: crosshost, name: powerpc64le-linux-gnu, value: powerpc64le-linux-gnu, container: crossbuild-essential-ppc64el } +# - { key: crosshost, name: s390x-linux-gnu, value: s390x-linux-gnu, container: crossbuild-essential-s390x } +# - { key: crosshost, name: x86_64-w64-mingw32, value: x86_64-w64-mingw32, container: mingw-w64 } + + # -Wno-strict-prototypes is necessary with current clang-15 since + # older autoconf generate functions without prototype and -pedantic + # now implies strict-prototypes. Disabling the error but leaving the + # warning generates a lot of noise from use of ANYARGS in + # rb_define_method() and friends. + # See: https://github.com/llvm/llvm-project/commit/11da1b53d8cd3507959022cd790d5a7ad4573d94 + - { key: append_cc, name: c99, value: '-std=c99 -Werror=pedantic -pedantic-errors -Wno-strict-prototypes' } +# - { key: append_cc, name: c11, value: '-std=c11 -Werror=pedantic -pedantic-errors -Wno-strict-prototypes' } +# - { key: append_cc, name: c17, value: '-std=c17 -Werror=pedantic -pedantic-errors -Wno-strict-prototypes' } + - { key: append_cc, name: c2x, value: '-std=c2x -Werror=pedantic -pedantic-errors -Wno-strict-prototypes' } - { key: CXXFLAGS, name: c++98, value: '-std=c++98 -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' } # - { key: CXXFLAGS, name: c++11, value: '-std=c++11 -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' } # - { key: CXXFLAGS, name: c++14, value: '-std=c++14 -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' } @@ -123,6 +144,7 @@ jobs: - { key: append_configure, name: 'coroutine=pthread', value: '--with-coroutine=pthread' } - { key: append_configure, name: disable-jit-support, value: '--disable-jit-support' } - { key: append_configure, name: disable-dln, value: '--disable-dln' } + - { key: append_configure, name: enable-mkmf-verbose, value: '--enable-mkmf-verbose' } - { key: append_configure, name: disable-rubygems, value: '--disable-rubygems' } - { key: cppflags, name: OPT_THREADED_CODE=1, value: '-DOPT_THREADED_CODE=1' } @@ -141,7 +163,6 @@ jobs: # - { key: cppflags, name: ID_TABLE_DEBUG, value: '-DID_TABLE_DEBUG' } # - { key: cppflags, name: RGENGC_DEBUG=-1, value: '-DRGENGC_DEBUG=-1' } # - { key: cppflags, name: SYMBOL_DEBUG, value: '-DSYMBOL_DEBUG' } -# - { key: cppflags, name: THREAD_DEBUG=-1, value: '-DTHREAD_DEBUG=-1' } # - { key: cppflags, name: RGENGC_CHECK_MODE, value: '-DRGENGC_CHECK_MODE' } # - { key: cppflags, name: TRANSIENT_HEAP_CHECK_MODE, value: '-DTRANSIENT_HEAP_CHECK_MODE' } @@ -156,6 +177,8 @@ jobs: # - { key: cppflags, name: USE_THREAD_CACHE=0, value: '-DUSE_THREAD_CACHE=0' } # - { key: cppflags, name: USE_TRANSIENT_HEAP=0, value: '-DUSE_TRANSIENT_HEAP=0' } # - { key: cppflags, name: USE_RUBY_DEBUG_LOG=1, value: '-DUSE_RUBY_DEBUG_LOG=1' } + - { key: cppflags, name: USE_RVARGC=0, value: '-DUSE_RVARGC=0' } +# - { key: cppflags, name: USE_RVARGC=1, value: '-DUSE_RVARGC=1' } - { key: cppflags, name: DEBUG_FIND_TIME_NUMGUESS, value: '-DDEBUG_FIND_TIME_NUMGUESS' } - { key: cppflags, name: DEBUG_INTEGER_PACK, value: '-DDEBUG_INTEGER_PACK' } @@ -188,6 +211,8 @@ jobs: container: image: ghcr.io/ruby/ruby-ci-image:${{ matrix.entry.container || 'clang-14' }} options: --user root + if: ${{ !startsWith(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} + env: ${{ matrix.entry.env || matrix.env }} steps: - run: id working-directory: @@ -197,10 +222,10 @@ jobs: run: | echo "${{ matrix.entry.key }}=${{ matrix.entry.value }}" >> $GITHUB_ENV echo "GNUMAKEFLAGS=-sj$((1 + $(nproc --all)))" >> $GITHUB_ENV - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: path: src - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: src/.downloaded-cache key: downloaded-cache @@ -210,7 +235,7 @@ jobs: run: > ../src/configure -C ${default_configure} ${append_configure} ${{ matrix.entry.key == 'crosshost' && '--host="${crosshost}"' || '--with-gcc="${default_cc} ${append_cc}"' }} - ${{ matrix.entry.shared || '--enable-shared' }} + ${{ matrix.entry.configure_append || '--enable-shared' }} - run: make extract-extlibs - run: make incs - run: make @@ -222,10 +247,19 @@ jobs: if: ${{ matrix.entry.check }} - run: make test-tool if: ${{ matrix.entry.check }} + # FIXME: Skip MJIT tests failing in the annocheck case. + # https://bugs.ruby-lang.org/issues/18781 + - run: | + rm test/ruby/test_mjit.rb + rm test/ruby/test_rubyvm_mjit.rb + if: ${{ endsWith(matrix.entry.name, 'annocheck') }} + working-directory: src - run: make test-all TESTS='-- ruby -ext-' if: ${{ matrix.entry.check }} - run: make test-spec if: ${{ matrix.entry.check }} + - run: make test-annocheck + if: ${{ matrix.entry.check && endsWith(matrix.entry.name, 'annocheck') }} - uses: k0kubun/action-slack@v2.0.0 with: diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index c2db3df1ad..8e3aec6e6f 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -37,11 +37,12 @@ jobs: include: - msystem: "MINGW64" base_ruby: 2.6 - test_task: [ "check" ] # to make job names consistent + test_task: "check" # to make job names consistent - msystem: "UCRT64" base_ruby: head - test_task: [ "check" ] # to make job names consistent + test_task: "check" # to make job names consistent fail-fast: false + if: ${{ !startsWith(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} steps: - run: mkdir build working-directory: @@ -51,15 +52,15 @@ jobs: git config --global core.eol lf git config --global advice.detachedHead 0 git config --global init.defaultBranch garbage - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: path: src - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: src/.downloaded-cache key: downloaded-cache - name: Set up Ruby & MSYS2 - uses: MSP-Greg/ruby-setup-ruby@win-ucrt-1 + uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.base_ruby }} - name: set env @@ -70,6 +71,8 @@ jobs: - name: where check run: | # show where + mv /c/Windows/System32/libcrypto-1_1-x64.dll /c/Windows/System32/libcrypto-1_1-x64.dll_ + mv /c/Windows/System32/libssl-1_1-x64.dll /c/Windows/System32/libssl-1_1-x64.dll_ result=true for e in gcc.exe ragel.exe make.exe bison.exe libcrypto-1_1-x64.dll libssl-1_1-x64.dll; do echo '##['group']'$'\033[93m'$e$'\033[m' @@ -78,6 +81,18 @@ jobs: done $result + - name: version check + run: | + # show version + result=true + for e in gcc ragel make bison "openssl version"; do + case "$e" in *" "*) ;; *) e="$e --version";; esac + echo '##['group']'$'\033[93m'$e$'\033[m' + $e || result=false + echo '##['endgroup']' + done + $result + - name: autogen run: | ./autogen.sh @@ -132,7 +147,7 @@ jobs: payload: | { "ci": "GitHub Actions", - "env": "${{ github.workflow }} / ${{ matrix.test_task }}", + "env": "${{ github.workflow }} ${{ matrix.msystem }} / ${{ matrix.test_task }}", "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", "commit": "${{ github.sha }}", "branch": "${{ github.ref }}".split('/').reverse()[0] diff --git a/.github/workflows/mjit.yml b/.github/workflows/mjit.yml index 8b0011ec37..d5d9cecc56 100644 --- a/.github/workflows/mjit.yml +++ b/.github/workflows/mjit.yml @@ -20,12 +20,13 @@ jobs: strategy: matrix: test_task: [ "check" ] # to make job names consistent - jit_opts: [ "--jit", "--jit-wait" ] + jit_opts: [ "--mjit", "--mjit-wait" ] fail-fast: false runs-on: ubuntu-latest + if: ${{ !startsWith(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} env: TESTOPTS: '-q --tty=no' - RUN_OPTS: '--disable-gems ${{ matrix.jit_opts }} --jit-debug=-ggdb3' + RUN_OPTS: '--disable-gems ${{ matrix.jit_opts }} --mjit-debug=-ggdb3' GITPULLOPTIONS: --no-tags origin ${{github.ref}} steps: - run: mkdir build @@ -39,10 +40,10 @@ jobs: run: | git config --global advice.detachedHead 0 git config --global init.defaultBranch garbage - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: path: src - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: src/.downloaded-cache key: downloaded-cache diff --git a/.github/workflows/spec_guards.yml b/.github/workflows/spec_guards.yml index 79ef8a6cc0..18e2d3c1a8 100644 --- a/.github/workflows/spec_guards.yml +++ b/.github/workflows/spec_guards.yml @@ -20,16 +20,17 @@ jobs: rubyspec: name: Rubyspec runs-on: ubuntu-20.04 + if: ${{ !startsWith(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} strategy: matrix: # Specs from ruby/spec should still run on all supported Ruby versions. # This also ensures the needed ruby_version_is guards are there, see spec/README.md. ruby: - ruby-2.7 - - ruby-3.0 + - ruby-3.1 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index e63e12ac0e..b7097cca53 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -26,16 +26,18 @@ jobs: configure: ["", "cppflags=-DRUBY_DEBUG"] include: - test_task: "check" - os: ubuntu-20.04 configure: "--host=i686-$OSTYPE" + - test_task: "check" + configure: "--enable-shared --enable-load-relative" + skipped_tests: "TestGem#test_.*_from_binstubs.*" + continue-on-skipped_tests: true - test_task: "test-all TESTS=--repeat-count=2" - os: ubuntu-20.04 - configure: "" fail-fast: false env: GITPULLOPTIONS: --no-tags origin ${{github.ref}} RUBY_DEBUG: ci - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.os || 'ubuntu-20.04' }} + if: ${{ !startsWith(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} steps: - run: mkdir build working-directory: @@ -63,10 +65,10 @@ jobs: run: | git config --global advice.detachedHead 0 git config --global init.defaultBranch garbage - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: path: src - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: src/.downloaded-cache key: downloaded-cache @@ -95,20 +97,22 @@ jobs: if: ${{ matrix.test_task == 'check' }} - name: make ${{ matrix.test_task }} run: | - $SETARCH make -s ${{ matrix.test_task }} ${TESTS:+TESTS=`echo "$TESTS" | sed 's| |$/ -n!/|g;s|^|-n!/|;s|$|$$/|'`} + $SETARCH make -s ${{ matrix.test_task }} ${TESTS:+TESTS=`echo "$TESTS" | sed 's| |$$/ -n!/|g;s|^|-n!/|;s|$|$$/|'`} timeout-minutes: 40 env: RUBY_TESTOPTS: "-q --tty=no" TESTS: ${{ matrix.test_task == 'check' && matrix.skipped_tests || '' }} TEST_BUNDLED_GEMS_ALLOW_FAILURES: "" + PRECHECK_BUNDLED_GEMS: "no" - name: make skipped tests run: | - $SETARCH make -s test-all TESTS=`echo "$TESTS" | sed 's| |$/ -n/|g;s|^|-n/|;s|$|$$/|'` + $SETARCH make -s test-all TESTS=`echo "$TESTS" | sed 's| |$$/ -n/|g;s|^|-n/|;s|$|$$/|'` env: GNUMAKEFLAGS: "" RUBY_TESTOPTS: "-v --tty=no" TESTS: ${{ matrix.skipped_tests }} if: ${{ matrix.test_task == 'check' && matrix.skipped_tests != '' }} + continue-on-error: ${{ matrix.continue-on-skipped_tests || false }} - uses: k0kubun/action-slack@v2.0.0 with: payload: | diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml new file mode 100644 index 0000000000..db00db9009 --- /dev/null +++ b/.github/workflows/wasm.yml @@ -0,0 +1,106 @@ +name: WebAssembly +on: + push: + paths-ignore: + - 'doc/**' + - '**.md' + - '**.rdoc' + pull_request: + paths-ignore: + - 'doc/**' + - '**.md' + - '**.rdoc' + +concurrency: + group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} + cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }} + +jobs: + make: + strategy: + matrix: + entry: +# # wasmtime can't compile non-optimized Asyncified binary due to locals explosion +# - { name: O0-debuginfo, optflags: "-O0", debugflags: "-g", wasmoptflags: "-O1" } +# - { name: O1, optflags: "-O1", debugflags: "" , wasmoptflags: "-O1" } + - { name: O2, optflags: "-O2", debugflags: "" , wasmoptflags: "-O2" } +# - { name: O3, optflags: "-O3", debugflags: "" , wasmoptflags: "-O3" } +# # -O4 is equivalent to -O3 in clang, but it's different in wasm-opt +# - { name: O4, optflags: "-O3", debugflags: "" , wasmoptflags: "-O4" } +# - { name: Oz, optflags: "-Oz", debugflags: "" , wasmoptflags: "-Oz" } + fail-fast: false + env: + RUBY_TESTOPTS: '-q --tty=no' + GITPULLOPTIONS: --no-tags origin ${{github.ref}} + WASI_SDK_VERSION_MAJOR: 14 + WASI_SDK_VERSION_MINOR: 0 + # Use older version, which uses glibc instead of musl, to avoid https://github.com/WebAssembly/binaryen/issues/4401 + BINARYEN_VERSION: 91 + WASMTIME_VERSION: v0.33.0 + runs-on: ubuntu-20.04 + if: ${{ !startsWith(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} + steps: + - run: mkdir build + working-directory: + - name: git config + run: | + git config --global advice.detachedHead 0 + git config --global init.defaultBranch garbage + - uses: actions/checkout@v3 + with: + path: src + - name: Install libraries + run: | + set -ex + sudo apt-get update -q || : + sudo apt-get install --no-install-recommends -q -y ruby bison make autoconf git wget + + wasi_sdk_deb="wasi-sdk_${WASI_SDK_VERSION_MAJOR}.${WASI_SDK_VERSION_MINOR}_amd64.deb" + wget "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION_MAJOR}/${wasi_sdk_deb}" + sudo dpkg -i "$wasi_sdk_deb" + rm -f "$wasi_sdk_deb" + + mkdir build-sdk + pushd build-sdk + + wasmtime_url="https://github.com/bytecodealliance/wasmtime/releases/download/${WASMTIME_VERSION}/wasmtime-${WASMTIME_VERSION}-x86_64-linux.tar.xz" + wget -O - "$wasmtime_url" | tar xJf - + sudo ln -fs "$PWD/wasmtime-${WASMTIME_VERSION}-x86_64-linux/wasmtime" /usr/local/bin/wasmtime + + binaryen_tarball="binaryen-version_${BINARYEN_VERSION}-x86-linux.tar.gz" + binaryen_url="https://github.com/WebAssembly/binaryen/releases/download/version_${BINARYEN_VERSION}/${binaryen_tarball}" + wget -O - "$binaryen_url" | tar xfz - + sudo ln -fs "$PWD/binaryen-version_${BINARYEN_VERSION}/wasm-opt" /usr/local/bin/wasm-opt + working-directory: src + - name: Set ENV + run: | + echo "MAKEFLAGS=-j$((1 + $(sysctl -n hw.activecpu)))" >> $GITHUB_ENV + echo "WASI_SDK_PATH=/opt/wasi-sdk" >> $GITHUB_ENV + - run: ./autogen.sh + working-directory: src + - name: Run configure + run: | + ../src/configure \ + --host wasm32-unknown-wasi \ + --with-static-linked-ext \ + LDFLAGS=" \ + -Xlinker --stack-first \ + -Xlinker -z -Xlinker stack-size=16777216 \ + " \ + optflags="${{ matrix.entry.optflags }}" \ + debugflags="${{ matrix.entry.debugflags }}" \ + wasmoptflags="${{ matrix.entry.wasmoptflags }} ${{ matrix.entry.debugflags }}" + + - run: make ruby + - name: Run basictest + run: wasmtime run ./../build/miniruby --mapdir /::./ -- basictest/test.rb + working-directory: src + - name: Run bootstraptest (no thread) + run: | + NO_THREAD_TESTS="$(grep -L Thread -R ./bootstraptest | awk -F/ '{ print $NF }' | uniq | sed -n 's/test_\(.*\).rb/\1/p' | paste -s -d, -)" + ruby ./bootstraptest/runner.rb --ruby="$(which wasmtime) run $PWD/../build/ruby --mapdir /::./ -- " --verbose "--sets=$NO_THREAD_TESTS" + working-directory: src + +defaults: + run: + working-directory: build diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 60f9b760ec..9c9d7b9da0 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -21,18 +21,15 @@ jobs: matrix: include: - vs: 2019 - os: windows-2019 - vcvars: '"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat"' - # - vs: 2022 - # os: windows-2022 - # vcvars: '"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"' + - vs: 2022 fail-fast: false - runs-on: ${{ matrix.os }} + runs-on: windows-${{ matrix.vs < 2022 && '2019' || matrix.vs }} + if: ${{ !startsWith(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} name: VisualStudio ${{ matrix.vs }} env: GITPULLOPTIONS: --no-tags origin ${{github.ref}} - VCVARS: ${{ matrix.vcvars }} PATCH: C:\msys64\usr\bin\patch.exe + OS_VER: windows-${{ matrix.vs < 2022 && '2019' || matrix.vs }} steps: - run: md build working-directory: @@ -42,33 +39,39 @@ jobs: update: true install: >- patch - if: ${{ matrix.os != 'windows-2019' }} + if: ${{ env.OS_VER != 'windows-2019' }} - name: patch path shell: msys2 {0} run: echo PATCH=$(cygpath -wa $(command -v patch)) >> $GITHUB_ENV if: ${{ steps.setup-msys2.outcome == 'success' }} - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: C:\vcpkg\downloads - key: ${{ runner.os }}-vcpkg-download-${{ matrix.os }}-${{ github.sha }} + key: ${{ runner.os }}-vcpkg-download-${{ env.OS_VER }}-${{ github.sha }} restore-keys: | - ${{ runner.os }}-vcpkg-download-${{ matrix.os }}- + ${{ runner.os }}-vcpkg-download-${{ env.OS_VER }}- ${{ runner.os }}-vcpkg-download- + - uses: actions/cache@v3 + with: + path: C:\vcpkg\installed + key: ${{ runner.os }}-vcpkg-installed-${{ matrix.os }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-vcpkg-installed-${{ matrix.os }}- + ${{ runner.os }}-vcpkg-installed- - name: Install libraries with vcpkg run: | - vcpkg --triplet x64-windows install readline zlib - - uses: actions/cache@v2 + vcpkg --triplet x64-windows install libffi libyaml openssl readline zlib + - uses: actions/cache@v3 with: path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey - key: ${{ runner.os }}-chocolatey-${{ matrix.os }}-${{ github.sha }} + key: ${{ runner.os }}-chocolatey-${{ env.OS_VER }}-${{ github.sha }} restore-keys: | - ${{ runner.os }}-chocolatey-${{ matrix.os }}- + ${{ runner.os }}-chocolatey-${{ env.OS_VER }}- ${{ runner.os }}-chocolatey- - name: Install libraries with chocolatey run: | # Using Choco-Install for retries, but it doesn't detect failures properly # if you pass multiple package names in a single command. - Choco-Install -PackageName openssl Choco-Install -PackageName winflexbison3 shell: pwsh - name: git config @@ -77,10 +80,10 @@ jobs: git config --global core.eol lf git config --global advice.detachedHead 0 git config --global init.defaultBranch garbage - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: path: src - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: src/.downloaded-cache key: downloaded-cache @@ -88,6 +91,12 @@ jobs: # %TEMP% is inconsistent with %TMP% and test-all expects they are consistent. # https://github.com/actions/virtual-environments/issues/712#issuecomment-613004302 run: | + set VS=${{ matrix.vs }} + set VCVARS=${{ matrix.vcvars }} + if not "%VCVARS%" == "" goto :vcset + set VCVARS="C:\Program Files (x86)\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + if not exist %VCVARS% set VCVARS="C:\Program Files\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + :vcset set | C:\msys64\usr\bin\sort > old.env call %VCVARS% set TMP=%USERPROFILE%\AppData\Local\Temp @@ -96,9 +105,18 @@ jobs: set | C:\msys64\usr\bin\sort > new.env C:\msys64\usr\bin\comm -13 old.env new.env >> %GITHUB_ENV% del *.env - - name: Configure + - name: link libraries run: | - ../src/win32/configure.bat --disable-install-doc --enable-bundled-libffi --with-opt-dir=C:/vcpkg/installed/x64-windows --with-openssl-dir="C:/Program Files/OpenSSL-Win64" + for %%I in (C:\vcpkg\installed\x64-windows\bin\*.dll) do ( + if not %%~nI == readline mklink %%~nxI %%I + ) + for %%I in (libcrypto-1_1-x64 libssl-1_1-x64) do ( + ren c:\Windows\System32\%%I.dll %%I.dll_ + ) + - name: Configure + run: >- + ../src/win32/configure.bat --disable-install-doc + --with-opt-dir=C:/vcpkg/installed/x64-windows - run: nmake incs - run: nmake extract-extlibs - run: nmake diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml index 966f9fd1c7..5dac4375f2 100644 --- a/.github/workflows/yjit-ubuntu.yml +++ b/.github/workflows/yjit-ubuntu.yml @@ -16,33 +16,52 @@ concurrency: cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }} jobs: + cargo: + name: Rust cargo test + # GitHub Action's image seems to already contain a Rust 1.58.0. + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + # For now we can't run cargo test --offline because it complains about the + # capstone dependency, even though the dependency is optional + #- run: cargo test --offline + - run: RUST_BACKTRACE=1 cargo test + working-directory: yjit + # Also compile and test with all features enabled + - run: RUST_BACKTRACE=1 cargo test --all-features + working-directory: yjit + # Check that we can build in release mode too + - run: cargo build --release + working-directory: yjit make: strategy: + fail-fast: false matrix: - test_task: ["check"] # "test-bundler-parallel", - os: - - ubuntu-20.04 -# - ubuntu-18.04 - yjit_opts: [ - "--yjit", - "--yjit --yjit-call-threshold=1", - ] - configure: ["", "cppflags=-DRUBY_DEBUG"] include: + - test_task: "check-yjit-bindings" + configure: "--with-gcc=clang-12 --enable-yjit=dev" + + - test_task: "check" + configure: "--enable-yjit" # release build + + - test_task: "check" + configure: "--enable-yjit=dev" + + - test_task: "check" + configure: "--enable-yjit=dev" + yjit_opts: "--yjit-call-threshold=1" + - test_task: "test-all TESTS=--repeat-count=2" - os: ubuntu-20.04 - configure: "" - yjit_enable_env: RUBY_YJIT_ENABLE + configure: "--enable-yjit=dev" + - test_task: "test-bundled-gems" - os: ubuntu-20.04 - configure: "cppflags=-DRUBY_DEBUG" - yjit_enable_env: RUBY_YJIT_ENABLE - fail-fast: false + configure: "--enable-yjit=dev" env: GITPULLOPTIONS: --no-tags origin ${{github.ref}} RUN_OPTS: ${{ matrix.yjit_opts }} RUBY_DEBUG: ci - runs-on: ${{ matrix.os }} + runs-on: ubuntu-20.04 + if: ${{ !startsWith(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} steps: - run: mkdir build working-directory: @@ -55,10 +74,10 @@ jobs: run: | git config --global advice.detachedHead 0 git config --global init.defaultBranch garbage - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: path: src - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: src/.downloaded-cache key: downloaded-cache @@ -75,7 +94,7 @@ jobs: - name: Run configure run: ../src/configure -C --disable-install-doc ${{ matrix.configure }} - run: make incs - - run: make + - run: make -j - run: make leaked-globals if: ${{ matrix.test_task == 'check' }} - run: make prepare-gems @@ -86,12 +105,12 @@ jobs: if: ${{ matrix.test_task == 'check' }} - name: Enable YJIT through ENV run: echo "RUBY_YJIT_ENABLE=1" >> $GITHUB_ENV - if: ${{ matrix.yjit_enable_env }} - run: make -s ${{ matrix.test_task }} RUN_OPTS="$RUN_OPTS" timeout-minutes: 60 env: RUBY_TESTOPTS: "-q --tty=no" TEST_BUNDLED_GEMS_ALLOW_FAILURES: "" + PRECHECK_BUNDLED_GEMS: "no" - uses: k0kubun/action-slack@v2.0.0 with: payload: | diff --git a/.github/workflows/yjit_asm_tests.yml b/.github/workflows/yjit_asm_tests.yml deleted file mode 100644 index 58f294f137..0000000000 --- a/.github/workflows/yjit_asm_tests.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: YJIT x86 assembler tests - -on: - push: - paths-ignore: - - 'doc/**' - - '**.md' - - '**.rdoc' - pull_request: - paths-ignore: - - 'doc/**' - - '**.md' - - '**.rdoc' - -concurrency: - group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} - cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }} - -jobs: - test: - runs-on: ubuntu-latest - steps: - - name: Install dependencies - run: | - set -x - sudo apt-get update -q || : - sudo apt-get install --no-install-recommends -q -y build-essential - - name: git config - run: | - git config --global advice.detachedHead 0 - git config --global init.defaultBranch garbage - - uses: actions/checkout@v2 - with: - path: src - - name: Run ASM tests - run: ./misc/test_yjit_asm.sh - working-directory: src diff --git a/.gitignore b/.gitignore index 31bfd787a7..521f4ec807 100644 --- a/.gitignore +++ b/.gitignore @@ -127,6 +127,7 @@ lcov*.info /ruby-runner /ruby-runner.h /ruby-man.rd.gz +/rubyspec_temp /run.gdb /sizes.c /static-ruby @@ -232,3 +233,6 @@ lcov*.info /rb_mjit_header.h /mjit_config.h /include/ruby-*/*/rb_mjit_min_header-*.h + +# /wasm/ +/wasm/tests/*.wasm diff --git a/.rdoc_options b/.rdoc_options new file mode 100644 index 0000000000..760507c7a2 --- /dev/null +++ b/.rdoc_options @@ -0,0 +1,4 @@ +--- +page_dir: doc +main_page: README.md +title: Documentation for Ruby development version diff --git a/.travis.yml b/.travis.yml index eb816b815b..a34d3692af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,8 @@ language: c os: linux +if: commit_message !~ /^\[DOC\]/ + dist: focal git: @@ -62,7 +64,6 @@ env: gcc-10 g++-10 libffi-dev - libgdbm-dev libncurses-dev libncursesw5-dev libreadline-dev @@ -108,7 +109,6 @@ env: libc6:armhf libstdc++-10-dev:armhf libffi-dev:armhf - libgdbm-dev:armhf libncurses-dev:armhf libncursesw5-dev:armhf libreadline-dev:armhf @@ -134,7 +134,9 @@ matrix: # We see "Some worker was crashed." in about 40% of recent ppc64le-linux jobs # e.g. https://app.travis-ci.com/github/ruby/ruby/jobs/530959548 - name: ppc64le-linux - # - name: s390x-linux + # Tentatively disable, because often hungs up **after** all tests + # have finished successfully and saving caches. + - name: s390x-linux fast_finish: true before_script: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7363c106a2..13df6087ca 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1 @@ -Please see the [official issue tracker], [doc/contributing.rdoc] and wiki [HowToContribute]. - -[official issue tracker]: https://bugs.ruby-lang.org -[doc/contributing.rdoc]: contributing.rdoc -[HowToContribute]: https://bugs.ruby-lang.org/projects/ruby/wiki/HowToContribute +See ["Contributing to Ruby"](https://docs.ruby-lang.org/en/master/contributing_md.html), which includes setup and build instructions. @@ -1017,6 +1017,17 @@ mentioned below. {MIT License}[rdoc-label:label-MIT+License] +[lib/error_highlight] +[lib/error_highlight.rb] +[test/error_highlight] + + error_highlight is under the following license. + + >>> + Copyright (c) 2021 Yusuke Endoh + + {MIT License}[rdoc-label:label-MIT+License] + [benchmark/so_ackermann.rb] [benchmark/so_array.rb] [benchmark/so_binary_trees.rb] @@ -1,389 +1,224 @@ -# NEWS for Ruby 3.1.0 +# NEWS for Ruby 3.2.0 -This document is a list of user visible feature changes -since the **3.0.0** release, except for bug fixes. +This document is a list of user-visible feature changes +since the **3.1.0** release, except for bug fixes. Note that each entry is kept to a minimum, see links for details. ## Language changes -* The block arguments can be now be anonymous, if the block will - only be passed to another method. [[Feature #11256]] +* Anonymous rest and keyword rest arguments can now be passed as + arguments, instead of just used in method parameters. + [[Feature #18351]] ```ruby - def foo(&) - bar(&) + def foo(*) + bar(*) + end + def baz(**) + quux(**) end ``` -* Pin operator now takes an expression. [[Feature #17411]] +* A proc that accepts a single positional argument and keywords will + no longer autosplat. [[Bug #18633]] - ```ruby - Prime.each_cons(2).lazy.find_all{_1 in [n, ^(n + 2)]}.take(3).to_a - #=> [[3, 5], [5, 7], [11, 13]] - ``` + ```ruby + proc{|a, **k| a}.call([1, 2]) + # Ruby 3.1 and before + # => 1 + # Ruby 3.2 and after + # => [1, 2] + ``` -* Pin operator now supports instance, class, and global variables. - [[Feature #17724]] +* Constant assignment evaluation order for constants set on explicit + objects has been made consistent with single attribute assignment + evaluation order. With this code: ```ruby - @n = 5 - Prime.each_cons(2).lazy.find{_1 in [n, ^@n]} - #=> [3, 5] + foo::BAR = baz ``` -* One-line pattern matching is no longer experimental. - -* Multiple assignment evaluation order has been made consistent with - single assignment evaluation order. With single assignment, Ruby - uses a left-to-right evaluation order. With this code: + `foo` is now called before `baz`. Similarly, for multiple assignments + to constants, left-to-right evaluation order is used. With this + code: ```ruby - foo[0] = bar + foo1::BAR1, foo2::BAR2 = baz1, baz2 ``` - The following evaluation order is used: - - 1. `foo` - 2. `bar` - 3. `[]=` called on the result of `foo` - - In Ruby before 3.1.0, multiple assignment did not follow this - evaluation order. With this code: - - ```ruby - foo[0], bar.baz = a, b - ``` - - Versions of Ruby before 3.1.0 would evaluate in the following - order - - 1. `a` - 2. `b` - 3. `foo` - 4. `[]=` called on the result of `foo` - 5. `bar` - 6. `baz=` called on the result of `bar` + The following evaluation order is now used: - Starting in Ruby 3.1.0, evaluation order is now consistent with - single assignment, with the left hand side being evaluated before - the right hand side: + 1. `foo1` + 2. `foo2` + 3. `baz1` + 4. `baz2` - 1. `foo` - 2. `bar` - 3. `a` - 4. `b` - 5. `[]=` called on the result of `foo` - 6. `baz=` called on the result of `bar` + [[Bug #15928]] - [[Bug #4443]] +* Find pattern is no longer experimental. + [[Feature #18585]] -* Values in Hash literals and keyword arguments can be omitted. - [[Feature #14579]] +* Methods taking a rest parameter (like `*args`) and wishing to delegate keyword + arguments through `foo(*args)` must now be marked with `ruby2_keywords` + (if not already the case). In other words, all methods wishing to delegate + keyword arguments through `*args` must now be marked with `ruby2_keywords`, + with no exception. This will make it easier to transition to other ways of + delegation once a library can require Ruby 3+. Previously, the `ruby2_keywords` + flag was kept if the receiving method took `*args`, but this was a bug and an + inconsistency. A good technique to find the potentially-missing `ruby2_keywords` + is to run the test suite, for where it fails find the last method which must + receive keyword arguments, use `puts nil, caller, nil` there, and check each + method/block on the call chain which must delegate keywords is correctly marked + as `ruby2_keywords`. [[Bug #18625]] [[Bug #16466]] - For example, + ```ruby + def target(**kw) + end - * `{x:, y:}` is a syntax sugar of `{x: x, y: y}`. - * `foo(x:, y:)` is a syntax sugar of `foo(x: x, y: y)`. + # Accidentally worked without ruby2_keywords in Ruby 2.7-3.1, ruby2_keywords + # needed in 3.2+. Just like (*args, **kwargs) or (...) would be needed on + # both #foo and #bar when migrating away from ruby2_keywords. + ruby2_keywords def bar(*args) + target(*args) + end - Constant names, local variable names, and method names are allowed as - key names. Note that a reserved word is considered as a local - variable or method name even if it's a pseudo variable name such as - `self`. + ruby2_keywords def foo(*args) + bar(*args) + end -* non main-Ractors can get instance variables (ivars) of classes/modules - if ivars refer to shareable objects. - [[Feature #17592]] + foo(k: 1) + ``` ## Command line options ## Core classes updates -Outstanding ones only. - -* Array - - * Array#intersect? is added. [[Feature #15198]] - -* Class - - * Class#descendants, which returns an array of classes - directly or indirectly inheriting from the receiver, not - including the receiver or singleton classes. - [[Feature #14394]] - - ```ruby - class A; end - class B < A; end - class C < B; end - A.descendants #=> [B, C] - B.descendants #=> [C] - C.descendants #=> [] - ``` +Note: We're only listing outstanding class updates. - * Class#subclasses, which returns an array of classes - directly inheriting from the receiver, not - including singleton classes. - [[Feature #18273]] - - ```ruby - class A; end - class B < A; end - class C < B; end - class D < A; end - A.subclasses #=> [D, B] - B.subclasses #=> [C] - C.subclasses #=> [] - ``` - -* Enumerable - - * Enumerable#compact is added. [[Feature #17312]] - - * Enumerable#tally now accepts an optional hash to count. [[Feature #17744]] - - * Enumerable#each_cons and each_slice to return a receiver. [[GH-1509]] - - ```ruby - [1, 2, 3].each_cons(2){} - # 3.0 => nil - # 3.1 => [1, 2, 3] - - [1, 2, 3].each_slice(2){} - # 3.0 => nil - # 3.1 => [1, 2, 3] - ``` - -* Enumerator::Lazy - - * Enumerator::Lazy#compact is added. [[Feature #17312]] - -* File - - * File.dirname now accepts an optional argument for the level to - strip path components. [[Feature #12194]] - -* Integer - - * Integer.try_convert is added. [[Feature #15211]] +* Hash + * Hash#shift now always returns nil if the hash is + empty, instead of returning the default value or + calling the default proc. [[Bug #16908]] * Kernel - - - * Kernel#load now accepts a module as the second argument, - and will load the file using the given module as the top - level module. [[Feature #6210]] + * Kernel#binding raises RuntimeError if called from a non-Ruby frame + (such as a method defined in C). [[Bug #18487]] * MatchData - - * MatchData#match is added [[Feature #18172]] - - * MatchData#match_length is added [[Feature #18172]] + * MatchData#byteoffset has been added. [[Feature #13110]] * Module + * Module.used_refinements has been added. [[Feature #14332]] + * Module#refinements has been added. [[Feature #12737]] + * Module#const_added has been added. [[Feature #17881]] - * Module#prepend now modifies the ancestor chain if the receiver - already includes the argument. Module#prepend still does not - modify the ancestor chain if the receiver has already prepended - the argument. [[Bug #17423]] - - * Module#private, #public, #protected, and #module_function will - now return their arguments. If a single argument is given, it - is returned. If no arguments are given, nil is returned. If - multiple arguments are given, they are returned as an array. - [[Feature #12495]] - -* Process - - * Process.\_fork is added. This is a core method for fork(2). - Do not call this method directly; it is called by existing - fork methods: Kernel.#fork, Process.fork, and IO.popen("-"). - Application monitoring libraries can overwride this method to - hook fork event. [[Feature #17795]] +* Proc + * Proc#dup returns an instance of subclass. [[Bug #17545]] + * Proc#parameters now accepts lambda keyword. [[Feature #15357]] -* Struct - - * Passing only keyword arguments to Struct#initialize is warned. - You need to use a Hash literal to set a Hash to a first member. - [[Feature #16806]] +* Refinement + * Refinement#refined_class has been added. [[Feature #12737]] - * StructClass#keyword_init? is added [[Feature #18008]] +* Set + * Set is now available as a built-in class without the need for `require "set"`. [[Feature #16989]] + It is currently autoloaded via the `Set` constant or a call to `Enumerable#to_set`. * String + * String#byteindex and String#byterindex have been added. [[Feature #13110]] + * Update Unicode to Version 14.0.0 and Emoji Version 14.0. [[Feature #18037]] + (also applies to Regexp) + * String#bytesplice has been added. [[Feature #18598]] - * Update Unicode version to 13.0.0 [[Feature #17750]] - and Emoji version to 13.0 [[Feature #18029]] - -* Queue - - * Queue#initialize now accepts an Enumerable of initial values. - [[Feature #17327]] - -* Thread - - * Thread#native_thread_id is added. [[Feature #17853]] - -* Thread::Backtrace - - * Thread::Backtrace.limit, which returns the value to limit backtrace - length set by `--backtrace-limit` command line option, is added. - [[Feature #17479]] - -* $LOAD_PATH - - * $LOAD_PATH.resolve_feature_path does not raise. [[Feature #16043]] - -* Fiber Scheduler - - * Add support for `Addrinfo.getaddrinfo` using `address_resolve` hook. - [[Feature #17370]] - - * Introduce non-blocking `Timeout.timeout` using `timeout_after` hook. - [[Feature #17470]] - - * Introduce new scheduler hooks `io_read` and `io_write` along with a - low level `IO::Buffer` for zero-copy read/write. [[Feature #18020]] - - * IO hooks `io_wait`, `io_read`, `io_write`, receive the original IO object - where possible. [[Bug #18003]] - - * Make `Monitor` fiber-safe. [[Bug #17827]] - - * Replace copy coroutine with pthread implementation. [[Feature #18015]] - -* Refinement +* Struct + * A Struct class can also be initialized with keyword arguments + without `keyword_init: true` on `Struct.new` [[Feature #16806]] - * New class which represents a module created by Module#refine. - `include` and `prepend` are deprecated, and `import_methods` is added - instead. [[Bug #17429]] +* TracePoint + * TracePoint#binding now returns `nil` for `c_call`/`c_return` TracePoints. + [[Bug #18487]] + * TracePoint#enable `target_thread` keyword argument now defaults to the + current thread if `target` and `target_line` keyword arguments are not + passed. [[Bug #16889]] ## Stdlib updates -* The following default gem are updated. - * RubyGems - * Bundler - * RDoc 6.3.2 - * ReLine - * JSON 2.6.1 - * Psych 4.0.2 - * FileUtils 1.6.0 - * Fiddle 1.1.0 - * StringIO 3.0.1 - * IO::Console 0.5.9 - * IO::Wait 0.2.0 - * CSV 3.2.1 - * Etc 1.3.0 - * Date 3.2.2 - * Zlib 2.1.1 - * StringScanner 3.0.1 - * IpAddr 1.2.3 - * Logger 1.4.4 - * OStruct 0.5.0 - * Irb - * Racc 1.6.0 - * Delegate 0.2.0 - * Benchmark 0.2.0 - * CGI 0.3.1 - * Readline(C-ext) 0.1.3 - * Timeout 0.2.0 - * YAML 0.2.0 - * URI 0.11.0 - * OpenSSL - * DidYouMean - * Weakref 0.1.1 - * Tempfile 0.1.2 - * TmpDir 0.1.2 - * English 0.7.1 - * Net::Protocol 0.1.2 - * Net::Http 0.2.0 - * BigDecimal - * OptionParser 0.2.0 - * Set 1.0.2 - * Find 0.1.1 - * Rinda 0.1.1 - * Erb 2.2.3 - * NKF 0.1.1 - * Base64 0.1.1 - * OpenUri 0.2.0 - * SecureRandom 0.1.1 - * Resolv 0.2.1 - * Resolv::Replace 0.1.0 - * Time 0.2.0 - * PP 0.2.1 - * Prettyprint 0.1.1 - * Drb 2.1.0 - * Pathname 0.2.0 - * Digest 3.1.0.pre3 - * Un 0.2.0 +* The following default gems are updated. + * RubyGems 3.4.0.dev + * bigdecimal 3.1.2 + * bundler 2.4.0.dev + * cgi 0.3.2 + * etc 1.4.0 + * io-console 0.5.11 + * io-nonblock 0.1.1 + * io-wait 0.2.3 + * ipaddr 1.2.4 + * json 2.6.2 + * logger 1.5.1 + * net-http 0.2.2 + * net-protocol 0.1.3 + * ostruct 0.5.5 + * psych 5.0.0.dev + * reline 0.3.1 + * securerandom 0.2.0 + * stringio 3.0.3 + * timeout 0.3.0 * The following bundled gems are updated. - * minitest 5.14.4 - * power_assert 2.0.1 - * rake 13.0.6 - * test-unit 3.5.1 - * rbs 1.8.0 - * typeprof 0.20.4 + * net-imap 0.2.3 + * rbs 2.5.0 + * typeprof 0.21.2 + * debug 1.5.0 * The following default gems are now bundled gems. - * net-ftp - * net-imap - * net-pop - * net-smtp - * matrix - * prime ## Compatibility issues -Excluding feature bug fixes. - -* `rb_io_wait_readable`, `rb_io_wait_writable` and `rb_wait_for_single_fd` are - deprecated in favour of `rb_io_maybe_wait_readable`, - `rb_io_maybe_wait_writable` and `rb_io_maybe_wait` respectively. - `rb_thread_wait_fd` and `rb_thread_fd_writable` are deprecated. [[Bug #18003]] - -## Stdlib compatibility issues - -* `ERB#initialize` warns `safe_level` and later arguments even without -w. - [[Feature #14256]] +Note: Excluding feature bug fixes. -* `lib/debug.rb` is replaced with `debug.gem` +### Removed constants -## C API updates - -* Documented. [[GH-4815]] +The following deprecated constants are removed. -* `rb_gc_force_recycle` is deprecated and has been changed to a no-op. - [[Feature #18290]] +* `Fixnum` and `Bignum` [[Feature #12005]] +* `Random::DEFAULT` [[Feature #17351]] +* `Struct::Group` +* `Struct::Passwd` -## Implementation improvements +### Removed methods -### JIT +The following deprecated methods are removed. -* The default `--jit-max-cache` is changed from 100 to 10000. +* `Dir.exists?` [[Feature #17391]] +* `File.exists?` [[Feature #17391]] +* `Kernel#=~` [[Feature #15231]] +* `Kernel#taint`, `Kernel#untaint`, `Kernel#tainted?` + [[Feature #16131]] +* `Kernel#trust`, `Kernel#untrust`, `Kernel#untrusted?` + [[Feature #16131]] -* JIT-ed code is no longer cancelled when a TracePoint for class events - is enabled. +## Stdlib compatibility issues -* The JIT compiler no longer skips compilation of methods longer than - 1000 instructions. +* `Psych` no longer bundles libyaml sources. + Users need to install the libyaml library themselves via the package + system. [[Feature #18571]] -* `--jit-verbose` and `--jit-warning` output "JIT cancel" when JIT-ed - code is disabled because TracePoint or GC.compact is used. +## C API updates -* `RubyVM::MJIT` is renamed to `RubyVM::JIT`. [[Feature #17490]] +### Removed C APIs -### YJIT: New experimental in-process JIT compiler +The following deprecated APIs are removed. -New JIT compiler available as an experimental feature. [[Feature #18229]] +* `rb_cData` variable. +* "taintedness" and "trustedness" functions. [[Feature #16131]] -See [this blog post](https://shopify.engineering/yjit-just-in-time-compiler-cruby -) introducing the project. +## Implementation improvements -* Disabled by default, use `--yjit` command-line option to enable YJIT. +* Fixed several race conditions in `Kernel#autoload`. [[Bug #18782]] -* Performance improvements on most real-world software, up to 22% on railsbench, 39% on liquid-render. +## JIT -* Fast warm-up times. +### MJIT -* Limited to macOS & Linux on x86-64 platforms for now. +### YJIT: New experimental in-process JIT compiler ## Static analysis @@ -391,92 +226,37 @@ See [this blog post](https://shopify.engineering/yjit-just-in-time-compiler-crub ### TypeProf -* [Experimental IDE support](https://github.com/ruby/typeprof/blob/master/doc/ide.md) has been implemented. -* Many bug fixes and performance improvements since Ruby 3.0.0. - ## Debugger -* A new debugger [debug.gem](https://github.com/ruby/debug) is bundled. - debug.gem is fast debugger implementation and it provides many features - like remote debugging, colorful REPL, IDE (VSCode) integration and more. - It replaces `lib/debug.rb` standard library. - -* `rdbg` command is also installed into `bin/` directory to start and control - debugging execution. - ## error_highlight -A built-in gem, error_highlight, has been introduced. -It includes fine-grained error location in backtrace: - -``` -$ ruby test.rb -test.rb:1:in `<main>': undefined method `time' for 1:Integer (NoMethodError) - -1.time {} - ^^^^^ -Did you mean? times -``` - -This gem is enabled by default. -You can disable it by using a command-line option `--disable-error_highlight`. -See [the repository](https://github.com/ruby/error_highlight) in detail. +## IRB Autocomplete and Document Display ## Miscellaneous changes -* lib/objspace/trace.rb is added, which is a tool for tracing the object - allocation. Just by requiring this file, tracing is started *immediately*. - Just by `Kernel#p`, you can investigate where an object was created. - Note that just requiring this file brings a large performance overhead. - This is only for debugging purpose. Do not use this in production. - [[Feature #17762]] - -* Now exceptions raised in finalizers will be printed to `STDERR`, unless - `$VERBOSE` is `nil`. [[Feature #17798]] - -* `instance_eval` and `instance_exec` now only allocate a singleton class when - required, avoiding extra objects and improving performance. [[GH-5146]] - -[Bug #4443]: https://bugs.ruby-lang.org/issues/4443 -[Feature #6210]: https://bugs.ruby-lang.org/issues/6210 -[Feature #11256]: https://bugs.ruby-lang.org/issues/11256 -[Feature #12194]: https://bugs.ruby-lang.org/issues/12194 -[Feature #12495]: https://bugs.ruby-lang.org/issues/12495 -[Feature #14256]: https://bugs.ruby-lang.org/issues/14256 -[Feature #14394]: https://bugs.ruby-lang.org/issues/14394 -[Feature #14579]: https://bugs.ruby-lang.org/issues/14579 -[Feature #15198]: https://bugs.ruby-lang.org/issues/15198 -[Feature #15211]: https://bugs.ruby-lang.org/issues/15211 -[Feature #16043]: https://bugs.ruby-lang.org/issues/16043 +[Feature #12005]: https://bugs.ruby-lang.org/issues/12005 +[Feature #12737]: https://bugs.ruby-lang.org/issues/12737 +[Feature #13110]: https://bugs.ruby-lang.org/issues/13110 +[Feature #14332]: https://bugs.ruby-lang.org/issues/14332 +[Feature #15231]: https://bugs.ruby-lang.org/issues/15231 +[Feature #15357]: https://bugs.ruby-lang.org/issues/15357 +[Bug #15928]: https://bugs.ruby-lang.org/issues/15928 +[Feature #16131]: https://bugs.ruby-lang.org/issues/16131 +[Bug #16466]: https://bugs.ruby-lang.org/issues/16466 [Feature #16806]: https://bugs.ruby-lang.org/issues/16806 -[Feature #17312]: https://bugs.ruby-lang.org/issues/17312 -[Feature #17327]: https://bugs.ruby-lang.org/issues/17327 -[Feature #17370]: https://bugs.ruby-lang.org/issues/17370 -[Feature #17411]: https://bugs.ruby-lang.org/issues/17411 -[Bug #17423]: https://bugs.ruby-lang.org/issues/17423 -[Bug #17429]: https://bugs.ruby-lang.org/issues/17429 -[Feature #17470]: https://bugs.ruby-lang.org/issues/17470 -[Feature #17479]: https://bugs.ruby-lang.org/issues/17479 -[Feature #17490]: https://bugs.ruby-lang.org/issues/17490 -[Feature #17592]: https://bugs.ruby-lang.org/issues/17592 -[Feature #17724]: https://bugs.ruby-lang.org/issues/17724 -[Feature #17744]: https://bugs.ruby-lang.org/issues/17744 -[Feature #17750]: https://bugs.ruby-lang.org/issues/17750 -[Feature #17762]: https://bugs.ruby-lang.org/issues/17762 -[Feature #17795]: https://bugs.ruby-lang.org/issues/17795 -[Feature #17798]: https://bugs.ruby-lang.org/issues/17798 -[Bug #17827]: https://bugs.ruby-lang.org/issues/17827 -[Feature #17853]: https://bugs.ruby-lang.org/issues/17853 -[Bug #18003]: https://bugs.ruby-lang.org/issues/18003 -[Feature #18008]: https://bugs.ruby-lang.org/issues/18008 -[Feature #18015]: https://bugs.ruby-lang.org/issues/18015 -[Feature #18020]: https://bugs.ruby-lang.org/issues/18020 -[Feature #18029]: https://bugs.ruby-lang.org/issues/18029 -[Feature #18172]: https://bugs.ruby-lang.org/issues/18172 -[Feature #18229]: https://bugs.ruby-lang.org/issues/18229 -[Feature #18273]: https://bugs.ruby-lang.org/issues/18273 -[Feature #18290]: https://bugs.ruby-lang.org/issues/18290 -[GH-1509]: https://github.com/ruby/ruby/pull/1509 -[GH-4815]: https://github.com/ruby/ruby/pull/4815 -[GH-5146]: https://github.com/ruby/ruby/pull/5146 - +[Bug #16889]: https://bugs.ruby-lang.org/issues/16889 +[Bug #16908]: https://bugs.ruby-lang.org/issues/16908 +[Feature #16989]: https://bugs.ruby-lang.org/issues/16989 +[Feature #17351]: https://bugs.ruby-lang.org/issues/17351 +[Feature #17391]: https://bugs.ruby-lang.org/issues/17391 +[Bug #17545]: https://bugs.ruby-lang.org/issues/17545 +[Feature #17881]: https://bugs.ruby-lang.org/issues/17881 +[Feature #18037]: https://bugs.ruby-lang.org/issues/18037 +[Feature #18351]: https://bugs.ruby-lang.org/issues/18351 +[Bug #18487]: https://bugs.ruby-lang.org/issues/18487 +[Feature #18571]: https://bugs.ruby-lang.org/issues/18571 +[Feature #18585]: https://bugs.ruby-lang.org/issues/18585 +[Feature #18598]: https://bugs.ruby-lang.org/issues/18598 +[Bug #18625]: https://bugs.ruby-lang.org/issues/18625 +[Bug #18633]: https://bugs.ruby-lang.org/issues/18633 +[Bug #18782]: https://bugs.ruby-lang.org/issues/18782 diff --git a/README.ja.md b/README.ja.md index 4516c71459..bb69c09055 100644 --- a/README.ja.md +++ b/README.ja.md @@ -1,10 +1,10 @@ -[](https://travis-ci.org/ruby/ruby) -[](https://ci.appveyor.com/project/ruby/ruby/branch/master) -[](https://github.com/ruby/ruby/actions?query=workflow%3A"macOS") [](https://github.com/ruby/ruby/actions?query=workflow%3A"MinGW") [](https://github.com/ruby/ruby/actions?query=workflow%3A"MJIT") [](https://github.com/ruby/ruby/actions?query=workflow%3A"Ubuntu") [](https://github.com/ruby/ruby/actions?query=workflow%3A"Windows") +[](https://ci.appveyor.com/project/ruby/ruby/branch/master) +[](https://app.travis-ci.com/ruby/ruby) +[](https://cirrus-ci.com/github/ruby/ruby/master) # Rubyとは @@ -1,4 +1,3 @@ -[](https://github.com/ruby/ruby/actions?query=workflow%3A"macOS") [](https://github.com/ruby/ruby/actions?query=workflow%3A"MinGW") [](https://github.com/ruby/ruby/actions?query=workflow%3A"MJIT") [](https://github.com/ruby/ruby/actions?query=workflow%3A"Ubuntu") @@ -7,7 +6,7 @@ [](https://app.travis-ci.com/ruby/ruby) [](https://cirrus-ci.com/github/ruby/ruby/master) -# What's Ruby +# What is Ruby? Ruby is an interpreted object-oriented programming language often used for web development. It also offers many scripting features @@ -16,28 +15,25 @@ It is simple, straightforward, and extensible. ## Features of Ruby -* Simple Syntax -* **Normal** Object-oriented Features (e.g. class, method calls) -* **Advanced** Object-oriented Features (e.g. mix-in, singleton-method) -* Operator Overloading -* Exception Handling -* Iterators and Closures -* Garbage Collection -* Dynamic Loading of Object Files (on some architectures) -* Highly Portable (works on many Unix-like/POSIX compatible platforms as - well as Windows, macOS, etc.) cf. - https://github.com/ruby/ruby/blob/master/doc/maintainers.rdoc#label-Platform+Maintainers +* Simple Syntax +* **Normal** Object-oriented Features (e.g. class, method calls) +* **Advanced** Object-oriented Features (e.g. mix-in, singleton-method) +* Operator Overloading +* Exception Handling +* Iterators and Closures +* Garbage Collection +* Dynamic Loading of Object Files (on some architectures) +* Highly Portable (works on many Unix-like/POSIX compatible platforms as + well as Windows, macOS, etc.) cf. + https://github.com/ruby/ruby/blob/master/doc/maintainers.rdoc#label-Platform+Maintainers - -## How to get Ruby +## How to get Ruby with Git For a complete list of ways to install Ruby, including using third-party tools like rvm, see: https://www.ruby-lang.org/en/downloads/ -### Git - The mirror of the Ruby source tree can be checked out with the following command: $ git clone https://github.com/ruby/ruby.git @@ -50,22 +46,15 @@ to see the list of branches: You may also want to use https://git.ruby-lang.org/ruby.git (actual master of Ruby source) if you are a committer. -### Subversion - -Stable branches for older Ruby versions can be checked out with also the -following command: - - $ svn co https://svn.ruby-lang.org/repos/ruby/branches/ruby_2_6/ ruby - -Try the following command to see the list of branches: - - $ svn ls https://svn.ruby-lang.org/repos/ruby/branches/ - - ## Ruby home page https://www.ruby-lang.org/ +## Documentation + +- [English](https://docs.ruby-lang.org/en/master/index.html) +- [Japanese](https://docs.ruby-lang.org/ja/master/index.html) + ## Mailing list There is a mailing list to discuss Ruby. To subscribe to this list, please @@ -77,108 +66,20 @@ in the mail body (not subject) to the address [ruby-talk-request@ruby-lang.org]. [ruby-talk-request@ruby-lang.org]: mailto:ruby-talk-request@ruby-lang.org?subject=Join%20Ruby%20Mailing%20List&body=subscribe -## Requirements to build from repository - -1. GNU or BSD make -2. C99 compiler -3. autoconf 2.67 or higher -4. automake 1.15 or higher -5. bison 2.3 or higher -6. Ruby 2.2 or higher - -When building from a released version, only a C99 compiler and GNU or BSD make -is required. - -## How to compile and install - -1. If you want to use Microsoft Visual C++ to compile Ruby, read - [win32/README.win32](rdoc-ref:win32/README.win32) instead of this document. - -2. Run `./autogen.sh` to generate configure, when you build the source checked - out from the Git repository. - -3. Run `./configure`, which will generate `config.h` and `Makefile`. - - Some C compiler flags may be added by default depending on your - environment. Specify `optflags=..` and `warnflags=..` as necessary to - override them. - -4. Edit `include/ruby/defines.h` if you need. Usually this step will not be needed. - -5. Optional: Remove comment mark(`#`) before the module names from `ext/Setup`. - - This step is only necessary if you want to link modules statically. - - If you don't want to compile dynamic extensions (probably on architectures - which do not allow dynamic loading), remove comment mark from the line - "`#option nodynamic`" in `ext/Setup`. - - Usually this step will not be needed. - -6. Run `make`. - - * On Mac, set RUBY\_CODESIGN environment variable with a signing identity. - It uses the identity to sign `ruby` binary. See also codesign(1). - -7. Optionally, run '`make check`' to check whether the compiled Ruby - interpreter works well. If you see the message "`check succeeded`", your - Ruby works as it should (hopefully). - -8. Run '`make install`'. - - This command will create the following directories and install files into - them. - - * `${DESTDIR}${prefix}/bin` - * `${DESTDIR}${prefix}/include/ruby-${MAJOR}.${MINOR}.${TEENY}` - * `${DESTDIR}${prefix}/include/ruby-${MAJOR}.${MINOR}.${TEENY}/${PLATFORM}` - * `${DESTDIR}${prefix}/lib` - * `${DESTDIR}${prefix}/lib/ruby` - * `${DESTDIR}${prefix}/lib/ruby/${MAJOR}.${MINOR}.${TEENY}` - * `${DESTDIR}${prefix}/lib/ruby/${MAJOR}.${MINOR}.${TEENY}/${PLATFORM}` - * `${DESTDIR}${prefix}/lib/ruby/site_ruby` - * `${DESTDIR}${prefix}/lib/ruby/site_ruby/${MAJOR}.${MINOR}.${TEENY}` - * `${DESTDIR}${prefix}/lib/ruby/site_ruby/${MAJOR}.${MINOR}.${TEENY}/${PLATFORM}` - * `${DESTDIR}${prefix}/lib/ruby/vendor_ruby` - * `${DESTDIR}${prefix}/lib/ruby/vendor_ruby/${MAJOR}.${MINOR}.${TEENY}` - * `${DESTDIR}${prefix}/lib/ruby/vendor_ruby/${MAJOR}.${MINOR}.${TEENY}/${PLATFORM}` - * `${DESTDIR}${prefix}/lib/ruby/gems/${MAJOR}.${MINOR}.${TEENY}` - * `${DESTDIR}${prefix}/share/man/man1` - * `${DESTDIR}${prefix}/share/ri/${MAJOR}.${MINOR}.${TEENY}/system` - - - If Ruby's API version is '*x.y.z*', the `${MAJOR}` is '*x*', the - `${MINOR}` is '*y*', and the `${TEENY}` is '*z*'. - - **NOTE**: teeny of the API version may be different from one of Ruby's - program version - - You may have to be a super user to install Ruby. - -If you fail to compile Ruby, please send the detailed error report with the -error log and machine/OS type, to help others. - -Some extension libraries may not get compiled because of lack of necessary -external libraries and/or headers, then you will need to run '`make distclean-ext`' -to remove old configuration after installing them in such case. - ## Copying See the file [COPYING](rdoc-ref:COPYING). ## Feedback -Questions about the Ruby language can be asked on the [Ruby-Talk] mailing list +Questions about the Ruby language can be asked on the [Ruby-Talk](https://www.ruby-lang.org/en/community/mailing-lists) mailing list or on websites like https://stackoverflow.com. -Bugs should be reported at https://bugs.ruby-lang.org. Read [HowToReport] for more information. - -[Ruby-Talk]: https://www.ruby-lang.org/en/community/mailing-lists -[HowToReport]: https://bugs.ruby-lang.org/projects/ruby/wiki/HowToReport +Bugs should be reported at https://bugs.ruby-lang.org. Read ["Reporting Issues"](https://docs.ruby-lang.org/en/master/reporting_issues_md.html) for more information. ## Contributing -See the file [CONTRIBUTING.md](rdoc-ref:CONTRIBUTING) +See ["Contributing to Ruby"](https://docs.ruby-lang.org/en/master/contributing_md.html), which includes setup and build instructions. ## The Author diff --git a/addr2line.c b/addr2line.c index f660be9129..e102667602 100644 --- a/addr2line.c +++ b/addr2line.c @@ -2257,9 +2257,12 @@ print_line0(line_info_t *line, void *address) else if (!line->path) { kprintf("[0x%"PRIxPTR"]\n", addr); } - else if (!line->saddr || !line->sname) { + else if (!line->sname) { kprintf("%s(0x%"PRIxPTR") [0x%"PRIxPTR"]\n", line->path, addr-line->base_addr, addr); } + else if (!line->saddr) { + kprintf("%s(%s) [0x%"PRIxPTR"]\n", line->path, line->sname, addr); + } else if (line->line <= 0) { kprintf("%s(%s+0x%"PRIxPTR") [0x%"PRIxPTR"]\n", line->path, line->sname, d, addr); @@ -139,7 +139,7 @@ should_not_be_shared_and_embedded(VALUE ary) } \ } while (0) -#define ARY_CAPA(ary) (ARY_EMBED_P(ary) ? RARRAY_EMBED_LEN_MAX : \ +#define ARY_CAPA(ary) (ARY_EMBED_P(ary) ? ary_embed_capa(ary) : \ ARY_SHARED_ROOT_P(ary) ? RARRAY_LEN(ary) : ARY_HEAP_CAPA(ary)) #define ARY_SET_CAPA(ary, n) do { \ assert(!ARY_EMBED_P(ary)); \ @@ -157,7 +157,7 @@ should_not_be_shared_and_embedded(VALUE ary) assert(ARY_SHARED_ROOT_P(_value_)); \ RB_OBJ_WRITE(_ary_, &RARRAY(_ary_)->as.heap.aux.shared_root, _value_); \ } while (0) -#define RARRAY_SHARED_ROOT_FLAG FL_USER5 +#define RARRAY_SHARED_ROOT_FLAG FL_USER12 #define ARY_SHARED_ROOT_P(ary) (assert(should_be_T_ARRAY((VALUE)(ary))), \ FL_TEST_RAW((ary), RARRAY_SHARED_ROOT_FLAG)) #define ARY_SHARED_ROOT_REFCNT(ary) \ @@ -165,6 +165,7 @@ should_not_be_shared_and_embedded(VALUE ary) #define ARY_SHARED_ROOT_OCCUPIED(ary) (ARY_SHARED_ROOT_REFCNT(ary) == 1) #define ARY_SET_SHARED_ROOT_REFCNT(ary, value) do { \ assert(ARY_SHARED_ROOT_P(ary)); \ + assert((value) >= 0); \ RARRAY(ary)->as.heap.aux.capa = (value); \ } while (0) #define FL_SET_SHARED_ROOT(ary) do { \ @@ -183,6 +184,34 @@ ARY_SET(VALUE a, long i, VALUE v) } #undef RARRAY_ASET +static long +ary_embed_capa(VALUE ary) +{ +#if USE_RVARGC + size_t size = rb_gc_obj_slot_size(ary) - offsetof(struct RArray, as.ary); + assert(size % sizeof(VALUE) == 0); + return size / sizeof(VALUE); +#else + return RARRAY_EMBED_LEN_MAX; +#endif +} + +static size_t +ary_embed_size(long capa) +{ + return offsetof(struct RArray, as.ary) + (sizeof(VALUE) * capa); +} + +static bool +ary_embeddable_p(long capa) +{ +#if USE_RVARGC + return rb_gc_size_allocatable_p(ary_embed_size(capa)); +#else + return capa <= RARRAY_EMBED_LEN_MAX; +#endif +} + #if ARRAY_DEBUG #define ary_verify(ary) ary_verify_(ary, __FILE__, __LINE__) @@ -204,7 +233,7 @@ ary_verify_(VALUE ary, const char *file, int line) else if (ARY_EMBED_P(ary)) { assert(!RARRAY_TRANSIENT_P(ary)); assert(!ARY_SHARED_P(ary)); - assert(RARRAY_LEN(ary) <= RARRAY_EMBED_LEN_MAX); + assert(RARRAY_LEN(ary) <= ary_embed_capa(ary)); } else { #if 1 @@ -446,7 +475,7 @@ ary_resize_capa(VALUE ary, long capacity) assert(!OBJ_FROZEN(ary)); assert(!ARY_SHARED_P(ary)); - if (capacity > RARRAY_EMBED_LEN_MAX) { + if (capacity > ary_embed_capa(ary)) { size_t new_capa = capacity; if (ARY_EMBED_P(ary)) { long len = ARY_EMBED_LEN(ary); @@ -512,12 +541,8 @@ ary_double_capa(VALUE ary, long min) static void rb_ary_decrement_share(VALUE shared_root) { - if (shared_root) { - long num = ARY_SHARED_ROOT_REFCNT(shared_root) - 1; - if (num > 0) { - ARY_SET_SHARED_ROOT_REFCNT(shared_root, num); - } - } + long num = ARY_SHARED_ROOT_REFCNT(shared_root); + ARY_SET_SHARED_ROOT_REFCNT(shared_root, num - 1); } static void @@ -528,21 +553,26 @@ rb_ary_unshare(VALUE ary) FL_UNSET_SHARED(ary); } -static inline void -rb_ary_unshare_safe(VALUE ary) +static void +rb_ary_reset(VALUE ary) { - if (ARY_SHARED_P(ary) && !ARY_EMBED_P(ary)) { - rb_ary_unshare(ary); + if (ARY_OWNS_HEAP_P(ary)) { + ary_heap_free(ary); + } + else if (ARY_SHARED_P(ary)) { + rb_ary_unshare(ary); } + + FL_SET_EMBED(ary); + ARY_SET_EMBED_LEN(ary, 0); } static VALUE rb_ary_increment_share(VALUE shared_root) { long num = ARY_SHARED_ROOT_REFCNT(shared_root); - if (num >= 0) { - ARY_SET_SHARED_ROOT_REFCNT(shared_root, num + 1); - } + assert(num >= 0); + ARY_SET_SHARED_ROOT_REFCNT(shared_root, num + 1); return shared_root; } @@ -571,7 +601,7 @@ rb_ary_cancel_sharing(VALUE ary) ary_verify(shared_root); - if (len <= RARRAY_EMBED_LEN_MAX) { + if (len <= ary_embed_capa(ary)) { const VALUE *ptr = ARY_HEAP_PTR(ary); FL_UNSET_SHARED(ary); FL_SET_EMBED(ary); @@ -621,7 +651,7 @@ ary_ensure_room_for_push(VALUE ary, long add_len) rb_raise(rb_eIndexError, "index %ld too big", new_len); } if (ARY_SHARED_P(ary)) { - if (new_len > RARRAY_EMBED_LEN_MAX) { + if (new_len > ary_embed_capa(ary)) { VALUE shared_root = ARY_SHARED_ROOT(ary); if (ARY_SHARED_ROOT_OCCUPIED(shared_root)) { if (ARY_HEAP_PTR(ary) - RARRAY_CONST_PTR_TRANSIENT(shared_root) + new_len <= RARRAY_LEN(shared_root)) { @@ -663,6 +693,7 @@ ary_ensure_room_for_push(VALUE ary, long add_len) * array.freeze -> self * * Freezes +self+; returns +self+: + * * a = [] * a.frozen? # => false * a.freeze @@ -697,9 +728,16 @@ rb_ary_shared_with_p(VALUE ary1, VALUE ary2) } static VALUE -ary_alloc(VALUE klass) +ary_alloc_embed(VALUE klass, long capa) { - NEWOBJ_OF(ary, struct RArray, klass, T_ARRAY | RARRAY_EMBED_FLAG | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0)); + size_t size = ary_embed_size(capa); + assert(rb_gc_size_allocatable_p(size)); +#if !USE_RVARGC + assert(size <= sizeof(struct RArray)); +#endif + RVARGC_NEWOBJ_OF(ary, struct RArray, klass, + T_ARRAY | RARRAY_EMBED_FLAG | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0), + size); /* Created array is: * FL_SET_EMBED((VALUE)ary); * ARY_SET_EMBED_LEN((VALUE)ary, 0); @@ -708,10 +746,19 @@ ary_alloc(VALUE klass) } static VALUE +ary_alloc_heap(VALUE klass) +{ + RVARGC_NEWOBJ_OF(ary, struct RArray, klass, + T_ARRAY | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0), + sizeof(struct RArray)); + return (VALUE)ary; +} + +static VALUE empty_ary_alloc(VALUE klass) { RUBY_DTRACE_CREATE_HOOK(ARRAY, 0); - return ary_alloc(klass); + return ary_alloc_embed(klass, 0); } static VALUE @@ -728,10 +775,14 @@ ary_new(VALUE klass, long capa) RUBY_DTRACE_CREATE_HOOK(ARRAY, capa); - ary = ary_alloc(klass); - if (capa > RARRAY_EMBED_LEN_MAX) { + if (ary_embeddable_p(capa)) { + ary = ary_alloc_embed(klass, capa); + } + else { + ary = ary_alloc_heap(klass); + assert(!ARY_EMBED_P(ary)); + ptr = ary_heap_alloc(ary, capa); - FL_UNSET_EMBED(ary); ARY_SET_PTR(ary, ptr); ARY_SET_CAPA(ary, capa); ARY_SET_HEAP_LEN(ary, 0); @@ -749,7 +800,7 @@ rb_ary_new_capa(long capa) VALUE rb_ary_new(void) { - return rb_ary_new2(RARRAY_EMBED_LEN_MAX); + return rb_ary_new_capa(0); } VALUE @@ -792,9 +843,16 @@ rb_ary_new_from_values(long n, const VALUE *elts) } static VALUE -ec_ary_alloc(rb_execution_context_t *ec, VALUE klass) +ec_ary_alloc_embed(rb_execution_context_t *ec, VALUE klass, long capa) { - RB_EC_NEWOBJ_OF(ec, ary, struct RArray, klass, T_ARRAY | RARRAY_EMBED_FLAG | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0)); + size_t size = ary_embed_size(capa); + assert(rb_gc_size_allocatable_p(size)); +#if !USE_RVARGC + assert(size <= sizeof(struct RArray)); +#endif + RB_RVARGC_EC_NEWOBJ_OF(ec, ary, struct RArray, klass, + T_ARRAY | RARRAY_EMBED_FLAG | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0), + size); /* Created array is: * FL_SET_EMBED((VALUE)ary); * ARY_SET_EMBED_LEN((VALUE)ary, 0); @@ -803,6 +861,15 @@ ec_ary_alloc(rb_execution_context_t *ec, VALUE klass) } static VALUE +ec_ary_alloc_heap(rb_execution_context_t *ec, VALUE klass) +{ + RB_RVARGC_EC_NEWOBJ_OF(ec, ary, struct RArray, klass, + T_ARRAY | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0), + sizeof(struct RArray)); + return (VALUE)ary; +} + +static VALUE ec_ary_new(rb_execution_context_t *ec, VALUE klass, long capa) { VALUE ary,*ptr; @@ -816,11 +883,14 @@ ec_ary_new(rb_execution_context_t *ec, VALUE klass, long capa) RUBY_DTRACE_CREATE_HOOK(ARRAY, capa); - ary = ec_ary_alloc(ec, klass); + if (ary_embeddable_p(capa)) { + ary = ec_ary_alloc_embed(ec, klass, capa); + } + else { + ary = ec_ary_alloc_heap(ec, klass); + assert(!ARY_EMBED_P(ary)); - if (capa > RARRAY_EMBED_LEN_MAX) { ptr = ary_heap_alloc(ary, capa); - FL_UNSET_EMBED(ary); ARY_SET_PTR(ary, ptr); ARY_SET_CAPA(ary, capa); ARY_SET_HEAP_LEN(ary, 0); @@ -932,7 +1002,7 @@ ary_make_shared(VALUE ary) else { long capa = ARY_CAPA(ary), len = RARRAY_LEN(ary); const VALUE *ptr; - NEWOBJ_OF(shared, struct RArray, 0, T_ARRAY | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0)); + VALUE shared = ary_alloc_heap(0); VALUE vshared = (VALUE)shared; rb_ary_transient_heap_evacuate(ary, TRUE); @@ -961,8 +1031,10 @@ ary_make_substitution(VALUE ary) { long len = RARRAY_LEN(ary); - if (len <= RARRAY_EMBED_LEN_MAX) { - VALUE subst = rb_ary_new2(len); + if (ary_embeddable_p(len)) { + VALUE subst = rb_ary_new_capa(len); + assert(ARY_EMBED_P(subst)); + ary_memcpy(subst, 0, len, RARRAY_CONST_PTR_TRANSIENT(ary)); ARY_SET_EMBED_LEN(subst, len); return subst; @@ -1023,6 +1095,30 @@ rb_ary_s_try_convert(VALUE dummy, VALUE ary) return rb_check_array_type(ary); } +/* :nodoc: */ +static VALUE +rb_ary_s_new(int argc, VALUE *argv, VALUE klass) +{ + VALUE ary; + + if (klass == rb_cArray) { + long size = 0; + if (argc > 0 && FIXNUM_P(argv[0])) { + size = FIX2LONG(argv[0]); + if (size < 0) size = 0; + } + + ary = ary_new(klass, size); + + rb_obj_call_init_kw(ary, argc, argv, RB_PASS_CALLED_KEYWORDS); + } + else { + ary = rb_class_new_instance_pass_kw(argc, argv, klass); + } + + return ary; +} + /* * call-seq: * Array.new -> new_empty_array @@ -1037,6 +1133,7 @@ rb_ary_s_try_convert(VALUE dummy, VALUE ary) * * With no block and a single \Array argument +array+, * returns a new \Array formed from +array+: + * * a = Array.new([:foo, 'bar', 2]) * a.class # => Array * a # => [:foo, "bar", 2] @@ -1044,12 +1141,14 @@ rb_ary_s_try_convert(VALUE dummy, VALUE ary) * With no block and a single \Integer argument +size+, * returns a new \Array of the given size * whose elements are all +nil+: + * * a = Array.new(3) * a # => [nil, nil, nil] * * With no block and arguments +size+ and +default_value+, * returns an \Array of the given size; * each element is that same +default_value+: + * * a = Array.new(3, 'x') * a # => ['x', 'x', 'x'] * @@ -1057,6 +1156,7 @@ rb_ary_s_try_convert(VALUE dummy, VALUE ary) * returns an \Array of the given size; * the block is called with each successive integer +index+; * the element for that +index+ is the return value from the block: + * * a = Array.new(3) {|index| "Element #{index}" } * a # => ["Element 0", "Element 1", "Element 2"] * @@ -1075,12 +1175,9 @@ rb_ary_initialize(int argc, VALUE *argv, VALUE ary) rb_ary_modify(ary); if (argc == 0) { - if (ARY_OWNS_HEAP_P(ary) && ARY_HEAP_PTR(ary) != NULL) { - ary_heap_free(ary); - } - rb_ary_unshare_safe(ary); - FL_SET_EMBED(ary); - ARY_SET_EMBED_LEN(ary, 0); + rb_ary_reset(ary); + assert(ARY_EMBED_P(ary)); + assert(ARY_EMBED_LEN(ary) == 0); if (rb_block_given_p()) { rb_warning("given block not used"); } @@ -1181,15 +1278,15 @@ ary_make_partial(VALUE ary, VALUE klass, long offset, long len) assert(len >= 0); assert(offset+len <= RARRAY_LEN(ary)); - if (len <= RARRAY_EMBED_LEN_MAX) { - VALUE result = ary_alloc(klass); + if (ary_embeddable_p(len)) { + VALUE result = ary_alloc_embed(klass, len); ary_memcpy(result, 0, len, RARRAY_CONST_PTR_TRANSIENT(ary) + offset); ARY_SET_EMBED_LEN(result, len); return result; } else { - VALUE shared, result = ary_alloc(klass); - FL_UNSET_EMBED(result); + VALUE shared, result = ary_alloc_heap(klass); + assert(!ARY_EMBED_P(result)); shared = ary_make_shared(ary); ARY_SET_PTR(result, RARRAY_CONST_PTR_TRANSIENT(ary)); @@ -1229,8 +1326,9 @@ ary_make_partial_step(VALUE ary, VALUE klass, long offset, long len, long step) long i; long j = offset + ((step > 0) ? 0 : (orig_len - 1)); + VALUE result = ary_new(klass, len); - if (len <= RARRAY_EMBED_LEN_MAX) { + if (ARY_EMBED_P(result)) { VALUE *ptr = (VALUE *)ARY_EMBED_PTR(result); for (i = 0; i < len; ++i) { RB_OBJ_WRITE(result, ptr+i, values[j]); @@ -1295,13 +1393,16 @@ ary_take_first_or_last(int argc, const VALUE *argv, VALUE ary, enum ary_take_pos * array << object -> self * * Appends +object+ to +self+; returns +self+: + * * a = [:foo, 'bar', 2] * a << :baz # => [:foo, "bar", 2, :baz] * * Appends +object+ as one element, even if it is another \Array: + * * a = [:foo, 'bar', 2] * a1 = a << [3, 4] * a1 # => [:foo, "bar", 2, [3, 4]] + * */ VALUE @@ -1334,15 +1435,17 @@ rb_ary_cat(VALUE ary, const VALUE *argv, long len) * Appends trailing elements. * * Appends each argument in +objects+ to +self+; returns +self+: + * * a = [:foo, 'bar', 2] * a.push(:baz, :bat) # => [:foo, "bar", 2, :baz, :bat] * * Appends each argument as one element, even if it is another \Array: + * * a = [:foo, 'bar', 2] * a1 = a.push([:baz, :bat], [:bam, :bad]) * a1 # => [:foo, "bar", 2, [:baz, :bat], [:bam, :bad]] * - * Array#append is an alias for \Array#push. + * Array#append is an alias for Array#push. * * Related: #pop, #shift, #unshift. */ @@ -1381,6 +1484,7 @@ rb_ary_pop(VALUE ary) * * When no argument is given and +self+ is not empty, * removes and returns the last element: + * * a = [:foo, 'bar', 2] * a.pop # => 2 * a # => [:foo, "bar"] @@ -1388,12 +1492,14 @@ rb_ary_pop(VALUE ary) * Returns +nil+ if the array is empty. * * When a non-negative \Integer argument +n+ is given and is in range, + * * removes and returns the last +n+ elements in a new \Array: * a = [:foo, 'bar', 2] * a.pop(2) # => ["bar", 2] * * If +n+ is positive and out of range, * removes and returns all elements: + * * a = [:foo, 'bar', 2] * a.pop(50) # => [:foo, "bar", 2] * @@ -1422,30 +1528,14 @@ rb_ary_shift(VALUE ary) VALUE top; long len = RARRAY_LEN(ary); - rb_ary_modify_check(ary); - if (len == 0) return Qnil; - top = RARRAY_AREF(ary, 0); - if (!ARY_SHARED_P(ary)) { - if (len < ARY_DEFAULT_SIZE) { - RARRAY_PTR_USE_TRANSIENT(ary, ptr, { - MEMMOVE(ptr, ptr+1, VALUE, len-1); - }); /* WB: no new reference */ - ARY_INCREASE_LEN(ary, -1); - ary_verify(ary); - return top; - } - assert(!ARY_EMBED_P(ary)); /* ARY_EMBED_LEN_MAX < ARY_DEFAULT_SIZE */ - - ARY_SET(ary, 0, Qnil); - ary_make_shared(ary); - } - else if (ARY_SHARED_ROOT_OCCUPIED(ARY_SHARED_ROOT(ary))) { - RARRAY_PTR_USE_TRANSIENT(ary, ptr, ptr[0] = Qnil); + if (len == 0) { + rb_ary_modify_check(ary); + return Qnil; } - ARY_INCREASE_PTR(ary, 1); /* shift ptr */ - ARY_INCREASE_LEN(ary, -1); - ary_verify(ary); + top = RARRAY_AREF(ary, 0); + + rb_ary_behead(ary, 1); return top; } @@ -1458,6 +1548,7 @@ rb_ary_shift(VALUE ary) * Removes and returns leading elements. * * When no argument is given, removes and returns the first element: + * * a = [:foo, 'bar', 2] * a.shift # => :foo * a # => ['bar', 2] @@ -1466,12 +1557,14 @@ rb_ary_shift(VALUE ary) * * When positive \Integer argument +n+ is given, removes the first +n+ elements; * returns those elements in a new \Array: + * * a = [:foo, 'bar', 2] * a.shift(2) # => [:foo, 'bar'] * a # => [2] * * If +n+ is as large as or larger than <tt>self.length</tt>, * removes all elements; returns those elements in a new \Array: + * * a = [:foo, 'bar', 2] * a.shift(3) # => [:foo, 'bar', 2] * @@ -1498,48 +1591,37 @@ rb_ary_shift_m(int argc, VALUE *argv, VALUE ary) return result; } -static VALUE -behead_shared(VALUE ary, long n) -{ - assert(ARY_SHARED_P(ary)); - rb_ary_modify_check(ary); - if (ARY_SHARED_ROOT_OCCUPIED(ARY_SHARED_ROOT(ary))) { - ary_mem_clear(ary, 0, n); - } - ARY_INCREASE_PTR(ary, n); - ARY_INCREASE_LEN(ary, -n); - ary_verify(ary); - return ary; -} - -static VALUE -behead_transient(VALUE ary, long n) -{ - rb_ary_modify_check(ary); - RARRAY_PTR_USE_TRANSIENT(ary, ptr, { - MEMMOVE(ptr, ptr+n, VALUE, RARRAY_LEN(ary)-n); - }); /* WB: no new reference */ - ARY_INCREASE_LEN(ary, -n); - ary_verify(ary); - return ary; -} - MJIT_FUNC_EXPORTED VALUE rb_ary_behead(VALUE ary, long n) { if (n <= 0) { return ary; } - else if (ARY_SHARED_P(ary)) { - return behead_shared(ary, n); - } - else if (RARRAY_LEN(ary) >= ARY_DEFAULT_SIZE) { + + rb_ary_modify_check(ary); + + if (!ARY_SHARED_P(ary)) { + if (ARY_EMBED_P(ary) || RARRAY_LEN(ary) < ARY_DEFAULT_SIZE) { + RARRAY_PTR_USE_TRANSIENT(ary, ptr, { + MEMMOVE(ptr, ptr + n, VALUE, RARRAY_LEN(ary) - n); + }); /* WB: no new reference */ + ARY_INCREASE_LEN(ary, -n); + ary_verify(ary); + return ary; + } + + ary_mem_clear(ary, 0, n); ary_make_shared(ary); - return behead_shared(ary, n); } - else { - return behead_transient(ary, n); + else if (ARY_SHARED_ROOT_OCCUPIED(ARY_SHARED_ROOT(ary))) { + ary_mem_clear(ary, 0, n); } + + ARY_INCREASE_PTR(ary, n); + ARY_INCREASE_LEN(ary, -n); + ary_verify(ary); + + return ary; } static VALUE @@ -1574,7 +1656,7 @@ ary_modify_for_unshift(VALUE ary, int argc) } /* use shared array for big "queues" */ - if (new_len > ARY_DEFAULT_SIZE * 4) { + if (new_len > ARY_DEFAULT_SIZE * 4 && !ARY_EMBED_P(ary)) { ary_verify(ary); /* make a room for unshifted items */ @@ -1632,6 +1714,7 @@ ary_ensure_room_for_unshift(VALUE ary, int argc) * array.unshift(*objects) -> self * * Prepends the given +objects+ to +self+: + * * a = [:foo, 'bar', 2] * a.unshift(:bam, :bat) # => [:bam, :bat, :foo, "bar", 2] * @@ -1725,12 +1808,14 @@ static VALUE rb_ary_aref2(VALUE ary, VALUE b, VALUE e); * Returns elements from +self+; does not modify +self+. * * When a single \Integer argument +index+ is given, returns the element at offset +index+: + * * a = [:foo, 'bar', 2] * a[0] # => :foo * a[2] # => 2 * a # => [:foo, "bar", 2] * * If +index+ is negative, counts relative to the end of +self+: + * * a = [:foo, 'bar', 2] * a[-1] # => 2 * a[-2] # => "bar" @@ -1739,12 +1824,14 @@ static VALUE rb_ary_aref2(VALUE ary, VALUE b, VALUE e); * * When two \Integer arguments +start+ and +length+ are given, * returns a new \Array of size +length+ containing successive elements beginning at offset +start+: + * * a = [:foo, 'bar', 2] * a[0, 2] # => [:foo, "bar"] * a[1, 2] # => ["bar", 2] * * If <tt>start + length</tt> is greater than <tt>self.length</tt>, * returns all elements from offset +start+ to the end: + * * a = [:foo, 'bar', 2] * a[0, 4] # => [:foo, "bar", 2] * a[1, 3] # => ["bar", 2] @@ -1758,6 +1845,7 @@ static VALUE rb_ary_aref2(VALUE ary, VALUE b, VALUE e); * When a single \Range argument +range+ is given, * treats <tt>range.min</tt> as +start+ above * and <tt>range.size</tt> as +length+ above: + * * a = [:foo, 'bar', 2] * a[0..1] # => [:foo, "bar"] * a[1..2] # => ["bar", 2] @@ -1765,31 +1853,36 @@ static VALUE rb_ary_aref2(VALUE ary, VALUE b, VALUE e); * Special case: If <tt>range.start == a.size</tt>, returns a new empty \Array. * * If <tt>range.end</tt> is negative, calculates the end index from the end: + * * a = [:foo, 'bar', 2] * a[0..-1] # => [:foo, "bar", 2] * a[0..-2] # => [:foo, "bar"] * a[0..-3] # => [:foo] * * If <tt>range.start</tt> is negative, calculates the start index from the end: + * * a = [:foo, 'bar', 2] * a[-1..2] # => [2] * a[-2..2] # => ["bar", 2] * a[-3..2] # => [:foo, "bar", 2] * * If <tt>range.start</tt> is larger than the array size, returns +nil+. + * * a = [:foo, 'bar', 2] * a[4..1] # => nil * a[4..0] # => nil * a[4..-1] # => nil * * When a single Enumerator::ArithmeticSequence argument +aseq+ is given, - * returns an Array of elements corresponding to the indexes produced by + * returns an \Array of elements corresponding to the indexes produced by * the sequence. + * * a = ['--', 'data1', '--', 'data2', '--', 'data3'] * a[(1..).step(2)] # => ["data1", "data2", "data3"] * * Unlike slicing with range, if the start or the end of the arithmetic sequence * is larger than array size, throws RangeError. + * * a = ['--', 'data1', '--', 'data2', '--', 'data3'] * a[(1..11).step(2)] * # RangeError (((1..11).step(2)) out of range) @@ -1798,6 +1891,7 @@ static VALUE rb_ary_aref2(VALUE ary, VALUE b, VALUE e); * * If given a single argument, and its type is not one of the listed, tries to * convert it to Integer, and raises if it is impossible: + * * a = [:foo, 'bar', 2] * # Raises TypeError (no implicit conversion of Symbol into Integer): * a[:foo] @@ -1856,6 +1950,7 @@ rb_ary_aref1(VALUE ary, VALUE arg) * a = [:foo, 'bar', 2] * a.at(0) # => :foo * a.at(2) # => 2 + * */ VALUE @@ -1872,6 +1967,7 @@ rb_ary_at(VALUE ary, VALUE pos) * Returns elements from +self+; does not modify +self+. * * When no argument is given, returns the first element: + * * a = [:foo, 'bar', 2] * a.first # => :foo * a # => [:foo, "bar", 2] @@ -1880,14 +1976,17 @@ rb_ary_at(VALUE ary, VALUE pos) * * When non-negative \Integer argument +n+ is given, * returns the first +n+ elements in a new \Array: + * * a = [:foo, 'bar', 2] * a.first(2) # => [:foo, "bar"] * * If <tt>n >= array.size</tt>, returns all elements: + * * a = [:foo, 'bar', 2] * a.first(50) # => [:foo, "bar", 2] * * If <tt>n == 0</tt> returns an new empty \Array: + * * a = [:foo, 'bar', 2] * a.first(0) # [] * @@ -1913,6 +2012,7 @@ rb_ary_first(int argc, VALUE *argv, VALUE ary) * Returns elements from +self+; +self+ is not modified. * * When no argument is given, returns the last element: + * * a = [:foo, 'bar', 2] * a.last # => 2 * a # => [:foo, "bar", 2] @@ -1921,14 +2021,17 @@ rb_ary_first(int argc, VALUE *argv, VALUE ary) * * When non-negative \Innteger argument +n+ is given, * returns the last +n+ elements in a new \Array: + * * a = [:foo, 'bar', 2] * a.last(2) # => ["bar", 2] * * If <tt>n >= array.size</tt>, returns all elements: + * * a = [:foo, 'bar', 2] * a.last(50) # => [:foo, "bar", 2] * * If <tt>n == 0</tt>, returns an new empty \Array: + * * a = [:foo, 'bar', 2] * a.last(0) # [] * @@ -1958,10 +2061,12 @@ rb_ary_last(int argc, const VALUE *argv, VALUE ary) * * With the single \Integer argument +index+, * returns the element at offset +index+: + * * a = [:foo, 'bar', 2] * a.fetch(1) # => "bar" * * If +index+ is negative, counts from the end of the array: + * * a = [:foo, 'bar', 2] * a.fetch(-1) # => 2 * a.fetch(-2) # => "bar" @@ -1969,6 +2074,7 @@ rb_ary_last(int argc, const VALUE *argv, VALUE ary) * With arguments +index+ and +default_value+, * returns the element at offset +index+ if index is in range, * otherwise returns +default_value+: + * * a = [:foo, 'bar', 2] * a.fetch(1, nil) # => "bar" * @@ -1979,6 +2085,7 @@ rb_ary_last(int argc, const VALUE *argv, VALUE ary) * a = [:foo, 'bar', 2] * a.fetch(1) {|index| raise 'Cannot happen' } # => "bar" * a.fetch(50) {|index| "Value for #{index}" } # => "Value for 50" + * */ static VALUE @@ -2020,6 +2127,7 @@ rb_ary_fetch(int argc, VALUE *argv, VALUE ary) * When argument +object+ is given but no block, * returns the index of the first element +element+ * for which <tt>object == element</tt>: + * * a = [:foo, 'bar', 2, 'bar'] * a.index('bar') # => 1 * @@ -2028,12 +2136,14 @@ rb_ary_fetch(int argc, VALUE *argv, VALUE ary) * When both argument +object+ and a block are given, * calls the block with each successive element; * returns the index of the first element for which the block returns a truthy value: + * * a = [:foo, 'bar', 2, 'bar'] * a.index {|element| element == 'bar' } # => 1 * * Returns +nil+ if the block never returns a truthy value. * * When neither an argument nor a block is given, returns a new Enumerator: + * * a = [:foo, 'bar', 2] * e = a.index * e # => #<Enumerator: [:foo, "bar", 2]:index> @@ -2081,6 +2191,7 @@ rb_ary_index(int argc, VALUE *argv, VALUE ary) * Returns the index of the last element for which <tt>object == element</tt>. * * When argument +object+ is given but no block, returns the index of the last such element found: + * * a = [:foo, 'bar', 2, 'bar'] * a.rindex('bar') # => 3 * @@ -2088,6 +2199,7 @@ rb_ary_index(int argc, VALUE *argv, VALUE ary) * * When a block is given but no argument, calls the block with each successive element; * returns the index of the last element for which the block returns a truthy value: + * * a = [:foo, 'bar', 2, 'bar'] * a.rindex {|element| element == 'bar' } # => 3 * @@ -2251,12 +2363,18 @@ rb_ary_resize(VALUE ary, long len) else if (ARY_EMBED_P(ary)) { ARY_SET_EMBED_LEN(ary, len); } - else if (len <= RARRAY_EMBED_LEN_MAX) { - VALUE tmp[RARRAY_EMBED_LEN_MAX]; - MEMCPY(tmp, ARY_HEAP_PTR(ary), VALUE, len); - ary_discard(ary); - MEMCPY((VALUE *)ARY_EMBED_PTR(ary), tmp, VALUE, len); /* WB: no new reference */ + else if (len <= ary_embed_capa(ary)) { + const VALUE *ptr = ARY_HEAP_PTR(ary); + long ptr_capa = ARY_HEAP_SIZE(ary); + bool is_malloc_ptr = !ARY_SHARED_P(ary) && !RARRAY_TRANSIENT_P(ary); + + FL_UNSET(ary, RARRAY_TRANSIENT_FLAG); + FL_SET_EMBED(ary); + + MEMCPY((VALUE *)ARY_EMBED_PTR(ary), ptr, VALUE, len); /* WB: no new reference */ ARY_SET_EMBED_LEN(ary, len); + + if (is_malloc_ptr) ruby_sized_xfree((void *)ptr, ptr_capa); } else { if (olen > len + ARY_DEFAULT_SIZE) { @@ -2296,16 +2414,19 @@ ary_aset_by_rb_ary_splice(VALUE ary, long beg, long len, VALUE val) * When \Integer argument +index+ is given, assigns +object+ to an element in +self+. * * If +index+ is non-negative, assigns +object+ the element at offset +index+: + * * a = [:foo, 'bar', 2] * a[0] = 'foo' # => "foo" * a # => ["foo", "bar", 2] * * If +index+ is greater than <tt>self.length</tt>, extends the array: + * * a = [:foo, 'bar', 2] * a[7] = 'foo' # => "foo" * a # => [:foo, "bar", 2, nil, nil, nil, nil, "foo"] * * If +index+ is negative, counts backwards from the end of the array: + * * a = [:foo, 'bar', 2] * a[-1] = 'two' # => "two" * a # => [:foo, "bar", "two"] @@ -2313,11 +2434,13 @@ ary_aset_by_rb_ary_splice(VALUE ary, long beg, long len, VALUE val) * When \Integer arguments +start+ and +length+ are given and +object+ is not an \Array, * removes <tt>length - 1</tt> elements beginning at offset +start+, * and assigns +object+ at offset +start+: + * * a = [:foo, 'bar', 2] * a[0, 2] = 'foo' # => "foo" * a # => ["foo", 2] * * If +start+ is negative, counts backwards from the end of the array: + * * a = [:foo, 'bar', 2] * a[-2, 2] = 'foo' # => "foo" * a # => [:foo, "foo"] @@ -2325,17 +2448,20 @@ ary_aset_by_rb_ary_splice(VALUE ary, long beg, long len, VALUE val) * If +start+ is non-negative and outside the array (<tt> >= self.size</tt>), * extends the array with +nil+, assigns +object+ at offset +start+, * and ignores +length+: + * * a = [:foo, 'bar', 2] * a[6, 50] = 'foo' # => "foo" * a # => [:foo, "bar", 2, nil, nil, nil, "foo"] * * If +length+ is zero, shifts elements at and following offset +start+ * and assigns +object+ at offset +start+: + * * a = [:foo, 'bar', 2] * a[1, 0] = 'foo' # => "foo" * a # => [:foo, "foo", "bar", 2] * * If +length+ is too large for the existing array, does not extend the array: + * * a = [:foo, 'bar', 2] * a[1, 5] = 'foo' # => "foo" * a # => [:foo, "foo"] @@ -2343,29 +2469,34 @@ ary_aset_by_rb_ary_splice(VALUE ary, long beg, long len, VALUE val) * When \Range argument +range+ is given and +object+ is an \Array, * removes <tt>length - 1</tt> elements beginning at offset +start+, * and assigns +object+ at offset +start+: + * * a = [:foo, 'bar', 2] * a[0..1] = 'foo' # => "foo" * a # => ["foo", 2] * * if <tt>range.begin</tt> is negative, counts backwards from the end of the array: + * * a = [:foo, 'bar', 2] * a[-2..2] = 'foo' # => "foo" * a # => [:foo, "foo"] * * If the array length is less than <tt>range.begin</tt>, * assigns +object+ at offset <tt>range.begin</tt>, and ignores +length+: + * * a = [:foo, 'bar', 2] * a[6..50] = 'foo' # => "foo" * a # => [:foo, "bar", 2, nil, nil, nil, "foo"] * * If <tt>range.end</tt> is zero, shifts elements at and following offset +start+ * and assigns +object+ at offset +start+: + * * a = [:foo, 'bar', 2] * a[1..0] = 'foo' # => "foo" * a # => [:foo, "foo", "bar", 2] * * If <tt>range.end</tt> is negative, assigns +object+ at offset +start+, * retains <tt>range.end.abs -1</tt> elements past that, and removes those beyond: + * * a = [:foo, 'bar', 2] * a[1..-1] = 'foo' # => "foo" * a # => [:foo, "foo"] @@ -2379,9 +2510,11 @@ ary_aset_by_rb_ary_splice(VALUE ary, long beg, long len, VALUE val) * * If <tt>range.end</tt> is too large for the existing array, * replaces array elements, but does not extend the array with +nil+ values: + * * a = [:foo, 'bar', 2] * a[1..5] = 'foo' # => "foo" * a # => [:foo, "foo"] + * */ static VALUE @@ -2418,15 +2551,18 @@ rb_ary_aset(int argc, VALUE *argv, VALUE ary) * * When +index+ is non-negative, inserts all given +objects+ * before the element at offset +index+: + * * a = [:foo, 'bar', 2] * a.insert(1, :bat, :bam) # => [:foo, :bat, :bam, "bar", 2] * * Extends the array if +index+ is beyond the array (<tt>index >= self.size</tt>): + * * a = [:foo, 'bar', 2] * a.insert(5, :bat, :bam) * a # => [:foo, "bar", 2, nil, nil, :bat, :bam] * * Does nothing if no objects given: + * * a = [:foo, 'bar', 2] * a.insert(1) * a.insert(50) @@ -2435,9 +2571,11 @@ rb_ary_aset(int argc, VALUE *argv, VALUE ary) * * When +index+ is negative, inserts all given +objects+ * _after_ the element at offset <tt>index+self.size</tt>: + * * a = [:foo, 'bar', 2] * a.insert(-2, :bat, :bam) * a # => [:foo, "bar", :bat, :bam, 2] + * */ static VALUE @@ -2482,29 +2620,35 @@ ary_enum_length(VALUE ary, VALUE args, VALUE eobj) * * When a block given, passes each successive array element to the block; * returns +self+: + * * a = [:foo, 'bar', 2] * a.each {|element| puts "#{element.class} #{element}" } * * Output: + * * Symbol foo * String bar * Integer 2 * * Allows the array to be modified during iteration: + * * a = [:foo, 'bar', 2] * a.each {|element| puts element; a.clear if element.to_s.start_with?('b') } * * Output: + * * foo * bar * * When no block given, returns a new \Enumerator: * a = [:foo, 'bar', 2] + * * e = a.each * e # => #<Enumerator: [:foo, "bar", 2]:each> * a1 = e.each {|element| puts "#{element.class} #{element}" } * * Output: + * * Symbol foo * String bar * Integer 2 @@ -2533,29 +2677,35 @@ rb_ary_each(VALUE ary) * * When a block given, passes each successive array index to the block; * returns +self+: + * * a = [:foo, 'bar', 2] * a.each_index {|index| puts "#{index} #{a[index]}" } * * Output: + * * 0 foo * 1 bar * 2 2 * * Allows the array to be modified during iteration: + * * a = [:foo, 'bar', 2] * a.each_index {|index| puts index; a.clear if index > 0 } * * Output: + * * 0 * 1 * * When no block given, returns a new \Enumerator: + * * a = [:foo, 'bar', 2] * e = a.each_index * e # => #<Enumerator: [:foo, "bar", 2]:each_index> * a1 = e.each {|index| puts "#{index} #{a[index]}"} * * Output: + * * 0 foo * 1 bar * 2 2 @@ -2584,28 +2734,35 @@ rb_ary_each_index(VALUE ary) * * When a block given, passes, in reverse order, each element to the block; * returns +self+: + * * a = [:foo, 'bar', 2] * a.reverse_each {|element| puts "#{element.class} #{element}" } * * Output: + * * Integer 2 * String bar * Symbol foo * * Allows the array to be modified during iteration: + * * a = [:foo, 'bar', 2] * a.reverse_each {|element| puts element; a.clear if element.to_s.start_with?('b') } * * Output: + * * 2 * bar * * When no block given, returns a new \Enumerator: + * * a = [:foo, 'bar', 2] * e = a.reverse_each * e # => #<Enumerator: [:foo, "bar", 2]:reverse_each> * a1 = e.each {|element| puts "#{element.class} #{element}" } + * * Output: + * * Integer 2 * String bar * Symbol foo @@ -2818,22 +2975,27 @@ rb_ary_join(VALUE ary, VALUE sep) * array.join(separator = $,) -> new_string * * Returns the new \String formed by joining the array elements after conversion. - * For each element +element+ + * For each element +element+: + * * - Uses <tt>element.to_s</tt> if +element+ is not a <tt>kind_of?(Array)</tt>. * - Uses recursive <tt>element.join(separator)</tt> if +element+ is a <tt>kind_of?(Array)</tt>. * * With no argument, joins using the output field separator, <tt>$,</tt>: + * * a = [:foo, 'bar', 2] * $, # => nil * a.join # => "foobar2" * * With \string argument +separator+, joins using that separator: + * * a = [:foo, 'bar', 2] * a.join("\n") # => "foo\nbar\n2" * * Joins recursively for nested Arrays: + * * a = [:foo, [:bar, [:baz, :bat]]] * a.join # => "foobarbazbat" + * */ static VALUE rb_ary_join_m(int argc, VALUE *argv, VALUE ary) @@ -2874,6 +3036,7 @@ inspect_ary(VALUE ary, VALUE dummy, int recur) * * Returns the new \String formed by calling method <tt>#inspect</tt> * on each array element: + * * a = [:foo, 'bar', 2] * a.inspect # => "[:foo, \"bar\", 2]" * @@ -2898,10 +3061,12 @@ rb_ary_to_s(VALUE ary) * to_a -> self or new_array * * When +self+ is an instance of \Array, returns +self+: + * * a = [:foo, 'bar', 2] * a.to_a # => [:foo, "bar", 2] * * Otherwise, returns a new \Array containing the elements of +self+: + * * class MyArray < Array; end * a = MyArray.new(['foo', 'bar', 'two']) * a.instance_of?(Array) # => false @@ -2909,6 +3074,7 @@ rb_ary_to_s(VALUE ary) * a1 = a.to_a * a1 # => ["foo", "bar", "two"] * a1.class # => Array # Not MyArray + * */ static VALUE @@ -2932,16 +3098,19 @@ rb_ary_to_a(VALUE ary) * When a block is given, calls the block with each array element; * the block must return a 2-element \Array whose two elements * form a key-value pair in the returned \Hash: + * * a = ['foo', :bar, 1, [2, 3], {baz: 4}] * h = a.to_h {|item| [item, item] } * h # => {"foo"=>"foo", :bar=>:bar, 1=>1, [2, 3]=>[2, 3], {:baz=>4}=>{:baz=>4}} * * When no block is given, +self+ must be an \Array of 2-element sub-arrays, * each sub-array is formed into a key-value pair in the new \Hash: + * * [].to_h # => {} * a = [['foo', 'zero'], ['bar', 'one'], ['baz', 'two']] * h = a.to_h * h # => {"foo"=>"zero", "bar"=>"one", "baz"=>"two"} + * */ static VALUE @@ -3012,8 +3181,10 @@ rb_ary_reverse(VALUE ary) * array.reverse! -> self * * Reverses +self+ in place: + * * a = ['foo', 'bar', 'two'] * a.reverse! # => ["two", "bar", "foo"] + * */ static VALUE @@ -3026,10 +3197,12 @@ rb_ary_reverse_bang(VALUE ary) * call-seq: * array.reverse -> new_array * - * Returns a new \Array with the elements of +self+ in reverse order. + * Returns a new \Array with the elements of +self+ in reverse order: + * * a = ['foo', 'bar', 'two'] * a1 = a.reverse * a1 # => ["two", "bar", "foo"] + * */ static VALUE @@ -3097,35 +3270,42 @@ rb_ary_rotate(VALUE ary, long cnt) * Rotates +self+ in place by moving elements from one end to the other; returns +self+. * * When no argument given, rotates the first element to the last position: + * * a = [:foo, 'bar', 2, 'bar'] * a.rotate! # => ["bar", 2, "bar", :foo] * * When given a non-negative \Integer +count+, * rotates +count+ elements from the beginning to the end: + * * a = [:foo, 'bar', 2] * a.rotate!(2) * a # => [2, :foo, "bar"] * * If +count+ is large, uses <tt>count % array.size</tt> as the count: + * * a = [:foo, 'bar', 2] * a.rotate!(20) * a # => [2, :foo, "bar"] * * If +count+ is zero, returns +self+ unmodified: + * * a = [:foo, 'bar', 2] * a.rotate!(0) * a # => [:foo, "bar", 2] * * When given a negative Integer +count+, rotates in the opposite direction, * from end to beginning: + * * a = [:foo, 'bar', 2] * a.rotate!(-2) * a # => ["bar", 2, :foo] * * If +count+ is small (far from zero), uses <tt>count % array.size</tt> as the count: + * * a = [:foo, 'bar', 2] * a.rotate!(-5) * a # => ["bar", 2, :foo] + * */ static VALUE @@ -3146,36 +3326,43 @@ rb_ary_rotate_bang(int argc, VALUE *argv, VALUE ary) * * When no argument given, returns a new \Array that is like +self+, * except that the first element has been rotated to the last position: + * * a = [:foo, 'bar', 2, 'bar'] * a1 = a.rotate * a1 # => ["bar", 2, "bar", :foo] * * When given a non-negative \Integer +count+, * returns a new \Array with +count+ elements rotated from the beginning to the end: + * * a = [:foo, 'bar', 2] * a1 = a.rotate(2) * a1 # => [2, :foo, "bar"] * * If +count+ is large, uses <tt>count % array.size</tt> as the count: + * * a = [:foo, 'bar', 2] * a1 = a.rotate(20) * a1 # => [2, :foo, "bar"] * * If +count+ is zero, returns a copy of +self+, unmodified: + * * a = [:foo, 'bar', 2] * a1 = a.rotate(0) * a1 # => [:foo, "bar", 2] * * When given a negative \Integer +count+, rotates in the opposite direction, * from end to beginning: + * * a = [:foo, 'bar', 2] * a1 = a.rotate(-2) * a1 # => ["bar", 2, :foo] * * If +count+ is small (far from zero), uses <tt>count % array.size</tt> as the count: + * * a = [:foo, 'bar', 2] * a1 = a.rotate(-5) * a1 # => ["bar", 2, :foo] + * */ static VALUE @@ -3276,6 +3463,7 @@ sort_2(const void *ap, const void *bp, void *dummy) * * With no block, compares elements using operator <tt><=></tt> * (see Comparable): + * * a = 'abcde'.split('').shuffle * a # => ["e", "b", "d", "a", "c"] * a.sort! @@ -3283,11 +3471,13 @@ sort_2(const void *ap, const void *bp, void *dummy) * * With a block, calls the block with each element pair; * for each element pair +a+ and +b+, the block should return an integer: + * * - Negative when +b+ is to follow +a+. * - Zero when +a+ and +b+ are equivalent. * - Positive when +a+ is to follow +b+. * * Example: + * * a = 'abcde'.split('').shuffle * a # => ["e", "b", "d", "a", "c"] * a.sort! {|a, b| a <=> b } @@ -3297,10 +3487,12 @@ sort_2(const void *ap, const void *bp, void *dummy) * * When the block returns zero, the order for +a+ and +b+ is indeterminate, * and may be unstable: + * * a = 'abcde'.split('').shuffle * a # => ["e", "b", "d", "a", "c"] * a.sort! {|a, b| 0 } * a # => ["d", "e", "c", "a", "b"] + * */ VALUE @@ -3373,6 +3565,7 @@ rb_ary_sort_bang(VALUE ary) * * With no block, compares elements using operator <tt><=></tt> * (see Comparable): + * * a = 'abcde'.split('').shuffle * a # => ["e", "b", "d", "a", "c"] * a1 = a.sort @@ -3380,11 +3573,13 @@ rb_ary_sort_bang(VALUE ary) * * With a block, calls the block with each element pair; * for each element pair +a+ and +b+, the block should return an integer: + * * - Negative when +b+ is to follow +a+. * - Zero when +a+ and +b+ are equivalent. * - Positive when +a+ is to follow +b+. * * Example: + * * a = 'abcde'.split('').shuffle * a # => ["e", "b", "d", "a", "c"] * a1 = a.sort {|a, b| a <=> b } @@ -3394,6 +3589,7 @@ rb_ary_sort_bang(VALUE ary) * * When the block returns zero, the order for +a+ and +b+ is indeterminate, * and may be unstable: + * * a = 'abcde'.split('').shuffle * a # => ["e", "b", "d", "a", "c"] * a1 = a.sort {|a, b| 0 } @@ -3510,6 +3706,7 @@ sort_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, dummy)) * For duplicates returned by the block, the ordering is indeterminate, and may be unstable. * * This example sorts strings based on their sizes: + * * a = ['aaaa', 'bbb', 'cc', 'd'] * a.sort_by! {|element| element.size } * a # => ["d", "cc", "bbb", "aaaa"] @@ -3518,6 +3715,7 @@ sort_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, dummy)) * * a = ['aaaa', 'bbb', 'cc', 'd'] * a.sort_by! # => #<Enumerator: ["aaaa", "bbb", "cc", "d"]:sort_by!> + * */ static VALUE @@ -3540,6 +3738,7 @@ rb_ary_sort_by_bang(VALUE ary) * * Calls the block, if given, with each element of +self+; * returns a new \Array whose elements are the return values from the block: + * * a = [:foo, 'bar', 2] * a1 = a.map {|element| element.class } * a1 # => [Symbol, String, Integer] @@ -3574,10 +3773,12 @@ rb_ary_collect(VALUE ary) * * Calls the block, if given, with each element; * replaces the element with the block's return value: + * * a = [:foo, 'bar', 2] * a.map! { |element| element.class } # => [Symbol, String, Integer] * * Returns a new \Enumerator if no block given: + * * a = [:foo, 'bar', 2] * a1 = a.map! * a1 # => #<Enumerator: [:foo, "bar", 2]:map!> @@ -3660,32 +3861,39 @@ append_values_at_single(VALUE result, VALUE ary, long olen, VALUE idx) * of +self+ at the given \Integer or \Range +indexes+. * * For each positive +index+, returns the element at offset +index+: + * * a = [:foo, 'bar', 2] * a.values_at(0, 2) # => [:foo, 2] * a.values_at(0..1) # => [:foo, "bar"] * * The given +indexes+ may be in any order, and may repeat: + * * a = [:foo, 'bar', 2] * a.values_at(2, 0, 1, 0, 2) # => [2, :foo, "bar", :foo, 2] * a.values_at(1, 0..2) # => ["bar", :foo, "bar", 2] * * Assigns +nil+ for an +index+ that is too large: + * * a = [:foo, 'bar', 2] * a.values_at(0, 3, 1, 3) # => [:foo, nil, "bar", nil] * * Returns a new empty \Array if no arguments given. * * For each negative +index+, counts backward from the end of the array: + * * a = [:foo, 'bar', 2] * a.values_at(-1, -3) # => [2, :foo] * * Assigns +nil+ for an +index+ that is too small: + * * a = [:foo, 'bar', 2] * a.values_at(0, -5, 1, -6, 2) # => [:foo, nil, "bar", nil, 2] * * The given +indexes+ may have a mixture of signs: + * * a = [:foo, 'bar', 2] * a.values_at(0, -2, 1, -1) # => [:foo, "bar", "bar", 2] + * */ static VALUE @@ -3709,11 +3917,13 @@ rb_ary_values_at(int argc, VALUE *argv, VALUE ary) * Calls the block, if given, with each element of +self+; * returns a new \Array containing those elements of +self+ * for which the block returns a truthy value: + * * a = [:foo, 'bar', 2, :bam] * a1 = a.select {|element| element.to_s.start_with?('b') } * a1 # => ["bar", :bam] * * Returns a new \Enumerator if no block given: + * * a = [:foo, 'bar', 2, :bam] * a.select # => #<Enumerator: [:foo, "bar", 2, :bam]:select> * @@ -3790,12 +4000,14 @@ select_bang_ensure(VALUE a) * removes from +self+ those elements for which the block returns +false+ or +nil+. * * Returns +self+ if any elements were removed: + * * a = [:foo, 'bar', 2, :bam] * a.select! {|element| element.to_s.start_with?('b') } # => ["bar", :bam] * * Returns +nil+ if no elements were removed. * * Returns a new \Enumerator if no block given: + * * a = [:foo, 'bar', 2, :bam] * a.select! # => #<Enumerator: [:foo, "bar", 2, :bam]:select!> * @@ -3822,12 +4034,15 @@ rb_ary_select_bang(VALUE ary) * * Retains those elements for which the block returns a truthy value; * deletes all other elements; returns +self+: + * * a = [:foo, 'bar', 2, :bam] * a.keep_if {|element| element.to_s.start_with?('b') } # => ["bar", :bam] * * Returns a new \Enumerator if no block given: + * * a = [:foo, 'bar', 2, :bam] * a.keep_if # => #<Enumerator: [:foo, "bar", 2, :bam]:keep_if> + * */ static VALUE @@ -3856,11 +4071,12 @@ ary_resize_smaller(VALUE ary, long len) * array.delete(obj) -> deleted_object * array.delete(obj) {|nosuch| ... } -> deleted_object or block_return * - * Removes zero or more elements from +self+; returns +self+. + * Removes zero or more elements from +self+. * * When no block is given, * removes from +self+ each element +ele+ such that <tt>ele == obj</tt>; * returns the last deleted element: + * * s1 = 'bar'; s2 = 'bar' * a = [:foo, s1, 2, s2] * a.delete('bar') # => "bar" @@ -3873,14 +4089,17 @@ ary_resize_smaller(VALUE ary, long len) * * If any such elements are found, ignores the block * and returns the last deleted element: + * * s1 = 'bar'; s2 = 'bar' * a = [:foo, s1, 2, s2] * deleted_obj = a.delete('bar') {|obj| fail 'Cannot happen' } * a # => [:foo, 2] * * If no such elements are found, returns the block's return value: + * * a = [:foo, 'bar', 2] * a.delete(:nosuch) {|obj| "#{obj} not found" } # => "nosuch not found" + * */ VALUE @@ -3966,6 +4185,7 @@ rb_ary_delete_at(VALUE ary, long pos) * Deletes an element from +self+, per the given \Integer +index+. * * When +index+ is non-negative, deletes the element at offset +index+: + * * a = [:foo, 'bar', 2] * a.delete_at(1) # => "bar" * a # => [:foo, 2] @@ -3973,6 +4193,7 @@ rb_ary_delete_at(VALUE ary, long pos) * If index is too large, returns +nil+. * * When +index+ is negative, counts backward from the end of the array: + * * a = [:foo, 'bar', 2] * a.delete_at(-2) # => "bar" * a # => [:foo, 2] @@ -4026,11 +4247,13 @@ ary_slice_bang_by_rb_ary_splice(VALUE ary, long pos, long len) * * When the only argument is an \Integer +n+, * removes and returns the _nth_ element in +self+: + * * a = [:foo, 'bar', 2] * a.slice!(1) # => "bar" * a # => [:foo, 2] * * If +n+ is negative, counts backwards from the end of +self+: + * * a = [:foo, 'bar', 2] * a.slice!(-1) # => 2 * a # => [:foo, "bar"] @@ -4039,13 +4262,15 @@ ary_slice_bang_by_rb_ary_splice(VALUE ary, long pos, long len) * * When the only arguments are Integers +start+ and +length+, * removes +length+ elements from +self+ beginning at offset +start+; - * returns the deleted objects in a new Array: + * returns the deleted objects in a new \Array: + * * a = [:foo, 'bar', 2] * a.slice!(0, 2) # => [:foo, "bar"] * a # => [2] * * If <tt>start + length</tt> exceeds the array size, * removes and returns all elements from offset +start+ to the end: + * * a = [:foo, 'bar', 2] * a.slice!(1, 50) # => ["bar", 2] * a # => [:foo] @@ -4057,8 +4282,9 @@ ary_slice_bang_by_rb_ary_splice(VALUE ary, long pos, long len) * * When the only argument is a \Range object +range+, * treats <tt>range.min</tt> as +start+ above and <tt>range.size</tt> as +length+ above: + * * a = [:foo, 'bar', 2] - * a.slice!(1..2) # => ["bar", 2] + * a.slice!(1..2) # => ["bar", 2] * a # => [:foo] * * If <tt>range.start == a.size</tt>, returns a new empty \Array. @@ -4066,15 +4292,18 @@ ary_slice_bang_by_rb_ary_splice(VALUE ary, long pos, long len) * If <tt>range.start</tt> is larger than the array size, returns +nil+. * * If <tt>range.end</tt> is negative, counts backwards from the end of the array: + * * a = [:foo, 'bar', 2] * a.slice!(0..-2) # => [:foo, "bar"] * a # => [2] * * If <tt>range.start</tt> is negative, * calculates the start index backwards from the end of the array: + * * a = [:foo, 'bar', 2] * a.slice!(-2..2) # => ["bar", 2] * a # => [:foo] + * */ static VALUE @@ -4161,14 +4390,17 @@ ary_reject_bang(VALUE ary) * Removes each element for which the block returns a truthy value. * * Returns +self+ if any elements removed: + * * a = [:foo, 'bar', 2, 'bat'] * a.reject! {|element| element.to_s.start_with?('b') } # => [:foo, 2] * * Returns +nil+ if no elements removed. * * Returns a new \Enumerator if no block given: + * * a = [:foo, 'bar', 2] * a.reject! # => #<Enumerator: [:foo, "bar", 2]:reject!> + * */ static VALUE @@ -4186,13 +4418,16 @@ rb_ary_reject_bang(VALUE ary) * * Returns a new \Array whose elements are all those from +self+ * for which the block returns +false+ or +nil+: + * * a = [:foo, 'bar', 2, 'bat'] * a1 = a.reject {|element| element.to_s.start_with?('b') } * a1 # => [:foo, 2] * * Returns a new \Enumerator if no block given: + * * a = [:foo, 'bar', 2] * a.reject # => #<Enumerator: [:foo, "bar", 2]:reject> + * */ static VALUE @@ -4213,13 +4448,16 @@ rb_ary_reject(VALUE ary) * * Removes each element in +self+ for which the block returns a truthy value; * returns +self+: + * * a = [:foo, 'bar', 2, 'bat'] * a.delete_if {|element| element.to_s.start_with?('b') } # => [:foo, 2] * * Returns a new \Enumerator if no block given: + * * a = [:foo, 'bar', 2] * a.delete_if # => #<Enumerator: [:foo, "bar", 2]:delete_if> - */ + * +3 */ static VALUE rb_ary_delete_if(VALUE ary) @@ -4267,10 +4505,12 @@ take_items(VALUE obj, long n) * * Each nested array <tt>new_array[n]</tt> is of size <tt>other_arrays.size+1</tt>, * and contains: + * * - The _nth_ element of +self+. * - The _nth_ element of each of the +other_arrays+. * * If all +other_arrays+ and +self+ are the same size: + * * a = [:a0, :a1, :a2, :a3] * b = [:b0, :b1, :b2, :b3] * c = [:c0, :c1, :c2, :c3] @@ -4279,6 +4519,7 @@ take_items(VALUE obj, long n) * * If any array in +other_arrays+ is smaller than +self+, * fills to <tt>self.size</tt> with +nil+: + * * a = [:a0, :a1, :a2, :a3] * b = [:b0, :b1, :b2] * c = [:c0, :c1] @@ -4287,23 +4528,27 @@ take_items(VALUE obj, long n) * * If any array in +other_arrays+ is larger than +self+, * its trailing elements are ignored: + * * a = [:a0, :a1, :a2, :a3] * b = [:b0, :b1, :b2, :b3, :b4] * c = [:c0, :c1, :c2, :c3, :c4, :c5] * d = a.zip(b, c) * d # => [[:a0, :b0, :c0], [:a1, :b1, :c1], [:a2, :b2, :c2], [:a3, :b3, :c3]] * - * When a block is given, calls the block with each of the sub-arrays (formed as above); returns nil + * When a block is given, calls the block with each of the sub-arrays (formed as above); returns +nil+: + * * a = [:a0, :a1, :a2, :a3] * b = [:b0, :b1, :b2, :b3] * c = [:c0, :c1, :c2, :c3] * a.zip(b, c) {|sub_array| p sub_array} # => nil * * Output: + * * [:a0, :b0, :c0] * [:a1, :b1, :c1] * [:a2, :b2, :c2] * [:a3, :b3, :c3] + * */ static VALUE @@ -4370,8 +4615,10 @@ rb_ary_zip(int argc, VALUE *argv, VALUE ary) * * Transposes the rows and columns in an \Array of Arrays; * the nested Arrays must all be the same size: + * * a = [[:a0, :a1], [:b0, :b1], [:c0, :c1]] * a.transpose # => [[:a0, :b0, :c0], [:a1, :b1, :c1]] + * */ static VALUE @@ -4407,8 +4654,10 @@ rb_ary_transpose(VALUE ary) * array.replace(other_array) -> self * * Replaces the content of +self+ with the content of +other_array+; returns +self+: + * * a = [:foo, 'bar', 2] * a.replace(['foo', :bar, 3]) # => ["foo", :bar, 3] + * */ VALUE @@ -4418,31 +4667,35 @@ rb_ary_replace(VALUE copy, VALUE orig) orig = to_ary(orig); if (copy == orig) return copy; - if (RARRAY_LEN(orig) <= RARRAY_EMBED_LEN_MAX) { - VALUE shared_root = 0; + rb_ary_reset(copy); - if (ARY_OWNS_HEAP_P(copy)) { - ary_heap_free(copy); - } - else if (ARY_SHARED_P(copy)) { - shared_root = ARY_SHARED_ROOT(copy); - FL_UNSET_SHARED(copy); - } - FL_SET_EMBED(copy); + /* orig has enough space to embed the contents of orig. */ + if (RARRAY_LEN(orig) <= ary_embed_capa(copy)) { + assert(ARY_EMBED_P(copy)); ary_memcpy(copy, 0, RARRAY_LEN(orig), RARRAY_CONST_PTR_TRANSIENT(orig)); - if (shared_root) { - rb_ary_decrement_share(shared_root); - } - ARY_SET_LEN(copy, RARRAY_LEN(orig)); + ARY_SET_EMBED_LEN(copy, RARRAY_LEN(orig)); } +#if USE_RVARGC + /* orig is embedded but copy does not have enough space to embed the + * contents of orig. */ + else if (ARY_EMBED_P(orig)) { + long len = ARY_EMBED_LEN(orig); + VALUE *ptr = ary_heap_alloc(copy, len); + + FL_UNSET_EMBED(copy); + ARY_SET_PTR(copy, ptr); + ARY_SET_LEN(copy, len); + ARY_SET_CAPA(copy, len); + + // No allocation and exception expected that could leave `copy` in a + // bad state from the edits above. + ary_memcpy(copy, 0, len, RARRAY_CONST_PTR_TRANSIENT(orig)); + } +#endif + /* Otherwise, orig is on heap and copy does not have enough space to embed + * the contents of orig. */ else { VALUE shared_root = ary_make_shared(orig); - if (ARY_OWNS_HEAP_P(copy)) { - ary_heap_free(copy); - } - else { - rb_ary_unshare_safe(copy); - } FL_UNSET_EMBED(copy); ARY_SET_PTR(copy, ARY_HEAP_PTR(orig)); ARY_SET_LEN(copy, ARY_HEAP_LEN(orig)); @@ -4457,8 +4710,10 @@ rb_ary_replace(VALUE copy, VALUE orig) * array.clear -> self * * Removes all elements from +self+: + * * a = [:foo, 'bar', 2] * a.clear # => [] + * */ VALUE @@ -4496,6 +4751,7 @@ rb_ary_clear(VALUE ary) * Replaces specified elements in +self+ with specified objects; returns +self+. * * With argument +obj+ and no block given, replaces all elements with that one object: + * * a = ['a', 'b', 'c', 'd'] * a # => ["a", "b", "c", "d"] * a.fill(:X) # => [:X, :X, :X, :X] @@ -4505,20 +4761,24 @@ rb_ary_clear(VALUE ary) * * If +start+ is in range (<tt>0 <= start < array.size</tt>), * replaces all elements from offset +start+ through the end: + * * a = ['a', 'b', 'c', 'd'] * a.fill(:X, 2) # => ["a", "b", :X, :X] * * If +start+ is too large (<tt>start >= array.size</tt>), does nothing: + * * a = ['a', 'b', 'c', 'd'] * a.fill(:X, 4) # => ["a", "b", "c", "d"] * a = ['a', 'b', 'c', 'd'] * a.fill(:X, 5) # => ["a", "b", "c", "d"] * * If +start+ is negative, counts from the end (starting index is <tt>start + array.size</tt>): + * * a = ['a', 'b', 'c', 'd'] * a.fill(:X, -2) # => ["a", "b", :X, :X] * * If +start+ is too small (less than and far from zero), replaces all elements: + * * a = ['a', 'b', 'c', 'd'] * a.fill(:X, -6) # => [:X, :X, :X, :X] * a = ['a', 'b', 'c', 'd'] @@ -4528,20 +4788,24 @@ rb_ary_clear(VALUE ary) * replaces elements based on the given +start+ and +length+. * * If +start+ is in range, replaces +length+ elements beginning at offset +start+: + * * a = ['a', 'b', 'c', 'd'] * a.fill(:X, 1, 1) # => ["a", :X, "c", "d"] * * If +start+ is negative, counts from the end: + * * a = ['a', 'b', 'c', 'd'] * a.fill(:X, -2, 1) # => ["a", "b", :X, "d"] * * If +start+ is large (<tt>start >= array.size</tt>), extends +self+ with +nil+: + * * a = ['a', 'b', 'c', 'd'] * a.fill(:X, 5, 0) # => ["a", "b", "c", "d", nil] * a = ['a', 'b', 'c', 'd'] * a.fill(:X, 5, 2) # => ["a", "b", "c", "d", nil, :X, :X] * * If +length+ is zero or negative, replaces no elements: + * * a = ['a', 'b', 'c', 'd'] * a.fill(:X, 1, 0) # => ["a", "b", "c", "d"] * a.fill(:X, 1, -1) # => ["a", "b", "c", "d"] @@ -4551,14 +4815,17 @@ rb_ary_clear(VALUE ary) * * If the range is positive and ascending (<tt>0 < range.begin <= range.end</tt>), * replaces elements from <tt>range.begin</tt> to <tt>range.end</tt>: + * * a = ['a', 'b', 'c', 'd'] * a.fill(:X, (1..1)) # => ["a", :X, "c", "d"] * * If <tt>range.first</tt> is negative, replaces no elements: + * * a = ['a', 'b', 'c', 'd'] * a.fill(:X, (-1..1)) # => ["a", "b", "c", "d"] * * If <tt>range.last</tt> is negative, counts from the end: + * * a = ['a', 'b', 'c', 'd'] * a.fill(:X, (0..-2)) # => [:X, :X, :X, "d"] * a = ['a', 'b', 'c', 'd'] @@ -4566,6 +4833,7 @@ rb_ary_clear(VALUE ary) * * If <tt>range.last</tt> and <tt>range.last</tt> are both negative, * both count from the end of the array: + * * a = ['a', 'b', 'c', 'd'] * a.fill(:X, (-1..-1)) # => ["a", "b", "c", :X] * a = ['a', 'b', 'c', 'd'] @@ -4573,29 +4841,34 @@ rb_ary_clear(VALUE ary) * * With no arguments and a block given, calls the block with each index; * replaces the corresponding element with the block's return value: + * * a = ['a', 'b', 'c', 'd'] * a.fill { |index| "new_#{index}" } # => ["new_0", "new_1", "new_2", "new_3"] * * With argument +start+ and a block given, calls the block with each index * from offset +start+ to the end; replaces the corresponding element - * with the block's return value: + * with the block's return value. * * If start is in range (<tt>0 <= start < array.size</tt>), * replaces from offset +start+ to the end: + * * a = ['a', 'b', 'c', 'd'] * a.fill(1) { |index| "new_#{index}" } # => ["a", "new_1", "new_2", "new_3"] * * If +start+ is too large(<tt>start >= array.size</tt>), does nothing: + * * a = ['a', 'b', 'c', 'd'] * a.fill(4) { |index| fail 'Cannot happen' } # => ["a", "b", "c", "d"] * a = ['a', 'b', 'c', 'd'] * a.fill(4) { |index| fail 'Cannot happen' } # => ["a", "b", "c", "d"] * * If +start+ is negative, counts from the end: + * * a = ['a', 'b', 'c', 'd'] * a.fill(-2) { |index| "new_#{index}" } # => ["a", "b", "new_2", "new_3"] * * If start is too small (<tt>start <= -array.size</tt>, replaces all elements: + * * a = ['a', 'b', 'c', 'd'] * a.fill(-6) { |index| "new_#{index}" } # => ["new_0", "new_1", "new_2", "new_3"] * a = ['a', 'b', 'c', 'd'] @@ -4606,20 +4879,24 @@ rb_ary_clear(VALUE ary) * replaces the corresponding element with the block's return value. * * If +start+ is in range, replaces +length+ elements beginning at offset +start+: + * * a = ['a', 'b', 'c', 'd'] * a.fill(1, 1) { |index| "new_#{index}" } # => ["a", "new_1", "c", "d"] * * If start is negative, counts from the end: + * * a = ['a', 'b', 'c', 'd'] * a.fill(-2, 1) { |index| "new_#{index}" } # => ["a", "b", "new_2", "d"] * * If +start+ is large (<tt>start >= array.size</tt>), extends +self+ with +nil+: + * * a = ['a', 'b', 'c', 'd'] * a.fill(5, 0) { |index| "new_#{index}" } # => ["a", "b", "c", "d", nil] * a = ['a', 'b', 'c', 'd'] * a.fill(5, 2) { |index| "new_#{index}" } # => ["a", "b", "c", "d", nil, "new_5", "new_6"] * * If +length+ is zero or less, replaces no elements: + * * a = ['a', 'b', 'c', 'd'] * a.fill(1, 0) { |index| "new_#{index}" } # => ["a", "b", "c", "d"] * a.fill(1, -1) { |index| "new_#{index}" } # => ["a", "b", "c", "d"] @@ -4630,14 +4907,17 @@ rb_ary_clear(VALUE ary) * * If the range is positive and ascending (<tt>range 0 < range.begin <= range.end</tt>, * replaces elements from <tt>range.begin</tt> to <tt>range.end</tt>: + * * a = ['a', 'b', 'c', 'd'] * a.fill(1..1) { |index| "new_#{index}" } # => ["a", "new_1", "c", "d"] * * If +range.first+ is negative, does nothing: + * * a = ['a', 'b', 'c', 'd'] * a.fill(-1..1) { |index| fail 'Cannot happen' } # => ["a", "b", "c", "d"] * * If <tt>range.last</tt> is negative, counts from the end: + * * a = ['a', 'b', 'c', 'd'] * a.fill(0..-2) { |index| "new_#{index}" } # => ["new_0", "new_1", "new_2", "d"] * a = ['a', 'b', 'c', 'd'] @@ -4645,10 +4925,12 @@ rb_ary_clear(VALUE ary) * * If <tt>range.first</tt> and <tt>range.last</tt> are both negative, * both count from the end: + * * a = ['a', 'b', 'c', 'd'] * a.fill(-1..-1) { |index| "new_#{index}" } # => ["a", "b", "c", "new_3"] * a = ['a', 'b', 'c', 'd'] * a.fill(-2..-2) { |index| "new_#{index}" } # => ["a", "b", "new_2", "d"] + * */ static VALUE @@ -4721,6 +5003,7 @@ rb_ary_fill(int argc, VALUE *argv, VALUE ary) * * Returns a new \Array containing all elements of +array+ * followed by all elements of +other_array+: + * * a = [0, 1] + [2, 3] * a # => [0, 1, 2, 3] * @@ -4761,6 +5044,7 @@ ary_append(VALUE x, VALUE y) * array.concat(*other_arrays) -> self * * Adds to +array+ all elements from each \Array in +other_arrays+; returns +self+: + * * a = [0, 1] * a.concat([2, 3], [4, 5]) # => [0, 1, 2, 3, 4, 5] */ @@ -4799,12 +5083,15 @@ rb_ary_concat(VALUE x, VALUE y) * * When non-negative argument \Integer +n+ is given, * returns a new \Array built by concatenating the +n+ copies of +self+: + * * a = ['x', 'y'] * a * 3 # => ["x", "y", "x", "y", "x", "y"] * * When \String argument +string_separator+ is given, * equivalent to <tt>array.join(string_separator)</tt>: + * * [0, [0, 1], {foo: 0}] * ', ' # => "0, 0, 1, {:foo=>0}" + * */ static VALUE @@ -4857,6 +5144,7 @@ rb_ary_times(VALUE ary, VALUE times) * * Returns the first element in +self+ that is an \Array * whose first element <tt>==</tt> +obj+: + * * a = [{foo: 0}, [2, 4], [4, 5, 6], [4, 5]] * a.assoc(4) # => [4, 5, 6] * @@ -4886,6 +5174,7 @@ rb_ary_assoc(VALUE ary, VALUE key) * * Returns the first element in +self+ that is an \Array * whose second element <tt>==</tt> +obj+: + * * a = [{foo: 0}, [2, 4], [4, 5, 6], [4, 5]] * a.rassoc(4) # => [2, 4] * @@ -4950,6 +5239,7 @@ recursive_equal(VALUE ary1, VALUE ary2, int recur) * * Returns +true+ if both <tt>array.size == other_array.size</tt> * and for each index +i+ in +array+, <tt>array[i] == other_array[i]</tt>: + * * a0 = [:foo, 'bar', 2] * a1 = [:foo, 'bar', 2.0] * a1 == a0 # => true @@ -4995,13 +5285,14 @@ recursive_eql(VALUE ary1, VALUE ary2, int recur) * * Returns +true+ if +self+ and +other_array+ are the same size, * and if, for each index +i+ in +self+, <tt>self[i].eql? other_array[i]</tt>: + * * a0 = [:foo, 'bar', 2] * a1 = [:foo, 'bar', 2] * a1.eql?(a0) # => true * * Otherwise, returns +false+. * - * This method is different from method {Array#==}[#method-i-3D-3D], + * This method is different from method Array#==, * which compares using method <tt>Object#==</tt>. */ @@ -5022,8 +5313,10 @@ rb_ary_eql(VALUE ary1, VALUE ary2) * Returns the integer hash value for +self+. * * Two arrays with the same content will have the same hash code (and will compare using eql?): + * * [0, 1, 2].hash == [0, 1, 2].hash # => true * [0, 1, 2].hash == [0, 1, 3].hash # => false + * */ static VALUE @@ -5049,6 +5342,7 @@ rb_ary_hash(VALUE ary) * * Returns +true+ if for some index +i+ in +self+, <tt>obj == self[i]</tt>; * otherwise +false+: + * * [0, 1, 2].include?(2) # => true * [0, 1, 2].include?(3) # => false */ @@ -5111,18 +5405,27 @@ recursive_cmp(VALUE ary1, VALUE ary2, int recur) * For each index +i+ in +self+, evaluates <tt>result = self[i] <=> other_array[i]</tt>. * * Returns -1 if any result is -1: + * * [0, 1, 2] <=> [0, 1, 3] # => -1 * * Returns 1 if any result is 1: + * * [0, 1, 2] <=> [0, 1, 1] # => 1 * * When all results are zero: + * * - Returns -1 if +array+ is smaller than +other_array+: + * * [0, 1, 2] <=> [0, 1, 2, 3] # => -1 + * * - Returns 1 if +array+ is larger than +other_array+: + * * [0, 1, 2] <=> [0, 1] # => 1 + * * - Returns 0 if +array+ and +other_array+ are the same size: + * * [0, 1, 2] <=> [0, 1, 2] # => 0 + * */ VALUE @@ -5209,6 +5512,7 @@ ary_recycle_hash(VALUE hash) * that are not found in \Array +other_array+; * items are compared using <tt>eql?</tt>; * the order from +array+ is preserved: + * * [0, 1, 1, 2, 1, 1, 3, 1, 1] - [1] # => [0, 2, 3] * [0, 1, 2, 3] - [3, 0] # => [1, 2] * [0, 1, 2] - [4] # => [0, 1, 2] @@ -5252,6 +5556,7 @@ rb_ary_diff(VALUE ary1, VALUE ary2) * Returns a new \Array containing only those elements from +self+ * that are not found in any of the Arrays +other_arrays+; * items are compared using <tt>eql?</tt>; order from +self+ is preserved: + * * [0, 1, 1, 2, 1, 1, 3, 1, 1].difference([1]) # => [0, 2, 3] * [0, 1, 2, 3].difference([3, 0], [1, 3]) # => [2] * [0, 1, 2].difference([4]) # => [0, 1, 2] @@ -5304,10 +5609,12 @@ rb_ary_difference_multi(int argc, VALUE *argv, VALUE ary) * * Returns a new \Array containing each element found in both +array+ and \Array +other_array+; * duplicates are omitted; items are compared using <tt>eql?</tt>: + * * [0, 1, 2, 3] & [1, 2] # => [1, 2] * [0, 1, 0, 1] & [0, 1] # => [0, 1] * * Preserves order from +array+: + * * [0, 1, 2] & [3, 2, 1, 0] # => [0, 1, 2] * * Related: Array#intersection. @@ -5356,10 +5663,12 @@ rb_ary_and(VALUE ary1, VALUE ary2) * Returns a new \Array containing each element found both in +self+ * and in all of the given Arrays +other_arrays+; * duplicates are omitted; items are compared using <tt>eql?</tt>: + * * [0, 1, 2, 3].intersection([0, 1, 2], [0, 1, 3]) # => [0, 1] * [0, 0, 1, 1, 2, 3].intersection([0, 1, 2], [0, 1, 3]) # => [0, 1] * * Preserves order from +self+: + * * [0, 1, 2].intersection([2, 1, 0]) # => [0, 1, 2] * * Returns a copy of +self+ if no arguments given. @@ -5418,6 +5727,7 @@ rb_ary_union_hash(VALUE hash, VALUE ary2) * Returns the union of +array+ and \Array +other_array+; * duplicates are removed; order is preserved; * items are compared using <tt>eql?</tt>: + * * [0, 1] | [2, 3] # => [0, 1, 2, 3] * [0, 1, 1] | [2, 2, 3] # => [0, 1, 2, 3] * [0, 1, 2] | [3, 2, 1, 0] # => [0, 1, 2, 3] @@ -5452,6 +5762,7 @@ rb_ary_or(VALUE ary1, VALUE ary2) * * Returns a new \Array that is the union of +self+ and all given Arrays +other_arrays+; * duplicates are removed; order is preserved; items are compared using <tt>eql?</tt>: + * * [0, 1, 2, 3].union([4, 5], [6, 7]) # => [0, 1, 2, 3, 4, 5, 6, 7] * [0, 1, 1].union([2, 1], [3, 1]) # => [0, 1, 2, 3] * [0, 1, 2, 3].union([3, 2], [1, 0]) # => [0, 1, 2, 3] @@ -5496,13 +5807,14 @@ rb_ary_union_multi(int argc, VALUE *argv, VALUE ary) * ary.intersect?(other_ary) -> true or false * * Returns +true+ if the array and +other_ary+ have at least one element in - * common, otherwise returns +false+. + * common, otherwise returns +false+: * * a = [ 1, 2, 3 ] * b = [ 3, 4, 5 ] * c = [ 5, 6, 7 ] * a.intersect?(b) #=> true * a.intersect?(c) #=> false + * */ static VALUE @@ -5643,6 +5955,7 @@ ary_max_opt_string(VALUE ary, long i, VALUE vmax) * array.max(n) {|a, b| ... } -> new_array * * Returns one of the following: + * * - The maximum-valued element from +self+. * - A new \Array of maximum-valued elements selected from +self+. * @@ -5651,10 +5964,12 @@ ary_max_opt_string(VALUE ary, long i, VALUE vmax) * * With no argument and no block, returns the element in +self+ * having the maximum value per method <tt><=></tt>: + * * [0, 1, 2].max # => 2 * * With an argument \Integer +n+ and no block, returns a new \Array with at most +n+ elements, * in descending order per method <tt><=></tt>: + * * [0, 1, 2, 3].max(3) # => [3, 2, 1] * [0, 1, 2, 3].max(6) # => [3, 2, 1, 0] * @@ -5662,11 +5977,14 @@ ary_max_opt_string(VALUE ary, long i, VALUE vmax) * * With a block and no argument, calls the block <tt>self.size-1</tt> times to compare elements; * returns the element having the maximum value per the block: + * * ['0', '00', '000'].max {|a, b| a.size <=> b.size } # => "000" * * With an argument +n+ and a block, returns a new \Array with at most +n+ elements, * in descending order per the block: + * * ['0', '00', '000'].max(2) {|a, b| a.size <=> b.size } # => ["000", "00"] + * */ static VALUE rb_ary_max(int argc, VALUE *argv, VALUE ary) @@ -5806,6 +6124,7 @@ ary_min_opt_string(VALUE ary, long i, VALUE vmin) * array.min(n) { |a, b| ... } -> new_array * * Returns one of the following: + * * - The minimum-valued element from +self+. * - A new \Array of minimum-valued elements selected from +self+. * @@ -5814,10 +6133,12 @@ ary_min_opt_string(VALUE ary, long i, VALUE vmin) * * With no argument and no block, returns the element in +self+ * having the minimum value per method <tt><=></tt>: + * * [0, 1, 2].min # => 0 * * With \Integer argument +n+ and no block, returns a new \Array with at most +n+ elements, * in ascending order per method <tt><=></tt>: + * * [0, 1, 2, 3].min(3) # => [0, 1, 2] * [0, 1, 2, 3].min(6) # => [0, 1, 2, 3] * @@ -5825,11 +6146,14 @@ ary_min_opt_string(VALUE ary, long i, VALUE vmin) * * With a block and no argument, calls the block <tt>self.size-1</tt> times to compare elements; * returns the element having the minimum value per the block: + * * ['0', '00', '000'].min { |a, b| a.size <=> b.size } # => "0" * * With an argument +n+ and a block, returns a new \Array with at most +n+ elements, * in ascending order per the block: + * * ['0', '00', '000'].min(2) {|a, b| a.size <=> b.size } # => ["0", "00"] + * */ static VALUE rb_ary_min(int argc, VALUE *argv, VALUE ary) @@ -5884,13 +6208,16 @@ rb_ary_min(int argc, VALUE *argv, VALUE ary) * with an \Integer; * returns a new 2-element \Array containing the minimum and maximum values * from +self+, per method <tt><=></tt>: + * * [0, 1, 2].minmax # => [0, 2] * * When a block is given, the block must return an \Integer; * the block is called <tt>self.size-1</tt> times to compare elements; * returns a new 2-element \Array containing the minimum and maximum values * from +self+, per the block: + * * ['0', '00', '000'].minmax {|a, b| a.size <=> b.size } # => ["0", "000"] + * */ static VALUE rb_ary_minmax(VALUE ary) @@ -5920,6 +6247,7 @@ push_value(st_data_t key, st_data_t val, st_data_t ary) * to compare. * * Returns +self+ if any elements removed: + * * a = [0, 0, 1, 1, 2, 2] * a.uniq! # => [0, 1, 2] * @@ -5930,6 +6258,7 @@ push_value(st_data_t key, st_data_t val, st_data_t ary) * elements for which the block returns duplicate values. * * Returns +self+ if any elements removed: + * * a = ['a', 'aa', 'aaa', 'b', 'bb', 'bbb'] * a.uniq! {|element| element.size } # => ['a', 'aa', 'aaa'] * @@ -5975,15 +6304,18 @@ rb_ary_uniq_bang(VALUE ary) * the first occurrence always being retained. * * With no block given, identifies and omits duplicates using method <tt>eql?</tt> - * to compare. + * to compare: + * * a = [0, 0, 1, 1, 2, 2] * a.uniq # => [0, 1, 2] * * With a block given, calls the block for each element; * identifies (using method <tt>eql?</tt>) and omits duplicate values, * that is, those elements for which the block returns the same value: + * * a = ['a', 'aa', 'aaa', 'b', 'bb', 'bbb'] * a.uniq {|element| element.size } # => ["a", "aa", "aaa"] + * */ static VALUE @@ -6047,6 +6379,7 @@ rb_ary_compact_bang(VALUE ary) * array.compact -> new_array * * Returns a new \Array containing all non-+nil+ elements from +self+: + * * a = [nil, 0, nil, 1, nil, 2, nil] * a.compact # => [0, 1, 2] */ @@ -6068,19 +6401,22 @@ rb_ary_compact(VALUE ary) * Returns a count of specified elements. * * With no argument and no block, returns the count of all elements: + * * [0, 1, 2].count # => 3 * [].count # => 0 * * With argument +obj+, returns the count of elements <tt>==</tt> to +obj+: + * * [0, 1, 2, 0.0].count(0) # => 2 * [0, 1, 2].count(3) # => 0 * * With no argument and a block given, calls the block with each element; * returns the count of elements for which the block returns a truthy value: + * * [0, 1, 2, 3].count {|element| element > 1} # => 2 * * With argument +obj+ and a block given, issues a warning, ignores the block, - * and returns the count of elements <tt>==</tt> to +obj+: + * and returns the count of elements <tt>==</tt> to +obj+. */ static VALUE @@ -6214,6 +6550,7 @@ flatten(VALUE ary, int level) * returns +self+ if any changes, +nil+ otherwise. * * With non-negative \Integer argument +level+, flattens recursively through +level+ levels: + * * a = [ 0, [ 1, [2, 3], 4 ], 5 ] * a.flatten!(1) # => [0, 1, [2, 3], 4, 5] * a = [ 0, [ 1, [2, 3], 4 ], 5 ] @@ -6223,6 +6560,7 @@ flatten(VALUE ary, int level) * [0, 1, 2].flatten!(1) # => nil * * With no argument, a +nil+ argument, or with negative argument +level+, flattens all levels: + * * a = [ 0, [ 1, [2, 3], 4 ], 5 ] * a.flatten! # => [0, 1, 2, 3, 4, 5] * [0, 1, 2].flatten! # => nil @@ -6231,6 +6569,7 @@ flatten(VALUE ary, int level) * a = [ 0, [ 1, [2, 3], 4 ], 5 ] * a.flatten!(-2) # => [0, 1, 2, 3, 4, 5] * [0, 1, 2].flatten!(-1) # => nil + * */ static VALUE @@ -6265,6 +6604,7 @@ rb_ary_flatten_bang(int argc, VALUE *argv, VALUE ary) * - Each \Array is replaced by its individual elements. * * With non-negative \Integer argument +level+, flattens recursively through +level+ levels: + * * a = [ 0, [ 1, [2, 3], 4 ], 5 ] * a.flatten(0) # => [0, [1, [2, 3], 4], 5] * a = [ 0, [ 1, [2, 3], 4 ], 5 ] @@ -6275,6 +6615,7 @@ rb_ary_flatten_bang(int argc, VALUE *argv, VALUE ary) * a.flatten(3) # => [0, 1, 2, 3, 4, 5] * * With no argument, a +nil+ argument, or with negative argument +level+, flattens all levels: + * * a = [ 0, [ 1, [2, 3], 4 ], 5 ] * a.flatten # => [0, 1, 2, 3, 4, 5] * [0, 1, 2].flatten # => [0, 1, 2] @@ -6283,6 +6624,7 @@ rb_ary_flatten_bang(int argc, VALUE *argv, VALUE ary) * a = [ 0, [ 1, [2, 3], 4 ], 5 ] * a.flatten(-2) # => [0, 1, 2, 3, 4, 5] * [0, 1, 2].flatten(-1) # => [0, 1, 2] + * */ static VALUE @@ -6498,15 +6840,18 @@ rb_ary_cycle_size(VALUE self, VALUE args, VALUE eobj) * When called with positive \Integer argument +count+ and a block, * calls the block with each element, then does so again, * until it has done so +count+ times; returns +nil+: + * * output = [] * [0, 1].cycle(2) {|element| output.push(element) } # => nil * output # => [0, 1, 0, 1] * * If +count+ is zero or negative, does not call the block: + * * [0, 1].cycle(0) {|element| fail 'Cannot happen' } # => nil * [0, 1].cycle(-1) {|element| fail 'Cannot happen' } # => nil * * When a block is given, and argument is omitted or +nil+, cycles forever: + * * # Prints 0 and 1 forever. * [0, 1].cycle {|element| puts element } * [0, 1].cycle(nil) {|element| puts element } @@ -6516,6 +6861,7 @@ rb_ary_cycle_size(VALUE self, VALUE args, VALUE eobj) * [0, 1].cycle(2) # => #<Enumerator: [0, 1]:cycle(2)> * [0, 1].cycle # => # => #<Enumerator: [0, 1]:cycle> * [0, 1].cycle.first(5) # => [0, 1, 0, 1, 0] + * */ static VALUE rb_ary_cycle(int argc, VALUE *argv, VALUE ary) @@ -6674,19 +7020,26 @@ rb_ary_permutation_size(VALUE ary, VALUE args, VALUE eobj) * are given, calls the block with all +n+-tuple permutations of +self+. * * Example: + * * a = [0, 1, 2] * a.permutation(2) {|permutation| p permutation } + * * Output: + * * [0, 1] * [0, 2] * [1, 0] * [1, 2] * [2, 0] * [2, 1] + * * Another example: + * * a = [0, 1, 2] * a.permutation(3) {|permutation| p permutation } + * * Output: + * * [0, 1, 2] * [0, 2, 1] * [1, 0, 2] @@ -6695,22 +7048,29 @@ rb_ary_permutation_size(VALUE ary, VALUE args, VALUE eobj) * [2, 1, 0] * * When +n+ is zero, calls the block once with a new empty \Array: + * * a = [0, 1, 2] * a.permutation(0) {|permutation| p permutation } + * * Output: + * * [] * * When +n+ is out of range (negative or larger than <tt>self.size</tt>), * does not call the block: + * * a = [0, 1, 2] * a.permutation(-1) {|permutation| fail 'Cannot happen' } * a.permutation(4) {|permutation| fail 'Cannot happen' } * * When a block given but no argument, * behaves the same as <tt>a.permutation(a.size)</tt>: + * * a = [0, 1, 2] * a.permutation {|permutation| p permutation } + * * Output: + * * [0, 1, 2] * [0, 2, 1] * [1, 0, 2] @@ -6719,9 +7079,11 @@ rb_ary_permutation_size(VALUE ary, VALUE args, VALUE eobj) * [2, 1, 0] * * Returns a new \Enumerator if no block given: + * * a = [0, 1, 2] * a.permutation # => #<Enumerator: [0, 1, 2]:permutation> * a.permutation(2) # => #<Enumerator: [0, 1, 2]:permutation(2)> + * */ static VALUE @@ -6804,34 +7166,46 @@ rb_ary_combination_size(VALUE ary, VALUE args, VALUE eobj) * are given, calls the block with all +n+-tuple combinations of +self+. * * Example: + * * a = [0, 1, 2] * a.combination(2) {|combination| p combination } + * * Output: + * * [0, 1] * [0, 2] * [1, 2] * * Another example: + * * a = [0, 1, 2] * a.combination(3) {|combination| p combination } + * * Output: + * * [0, 1, 2] * * When +n+ is zero, calls the block once with a new empty \Array: + * * a = [0, 1, 2] * a1 = a.combination(0) {|combination| p combination } + * * Output: + * * [] * * When +n+ is out of range (negative or larger than <tt>self.size</tt>), * does not call the block: + * * a = [0, 1, 2] * a.combination(-1) {|combination| fail 'Cannot happen' } * a.combination(4) {|combination| fail 'Cannot happen' } * * Returns a new \Enumerator if no block given: + * * a = [0, 1, 2] * a.combination(2) # => #<Enumerator: [0, 1, 2]:combination(2)> + * */ static VALUE @@ -6930,16 +7304,22 @@ rb_ary_repeated_permutation_size(VALUE ary, VALUE args, VALUE eobj) * The number of permutations is <tt>self.size**n</tt>. * * +n+ = 1: + * * a = [0, 1, 2] * a.repeated_permutation(1) {|permutation| p permutation } + * * Output: + * * [0] * [1] * [2] * * +n+ = 2: + * * a.repeated_permutation(2) {|permutation| p permutation } + * * Output: + * * [0, 0] * [0, 1] * [0, 2] @@ -6953,14 +7333,17 @@ rb_ary_repeated_permutation_size(VALUE ary, VALUE args, VALUE eobj) * If +n+ is zero, calls the block once with an empty \Array. * * If +n+ is negative, does not call the block: + * * a.repeated_permutation(-1) {|permutation| fail 'Cannot happen' } * * Returns a new \Enumerator if no block given: + * * a = [0, 1, 2] * a.repeated_permutation(2) # => #<Enumerator: [0, 1, 2]:permutation(2)> * * Using Enumerators, it's convenient to show the permutations and counts * for some values of +n+: + * * e = a.repeated_permutation(0) * e.size # => 1 * e.to_a # => [[]] @@ -6970,6 +7353,7 @@ rb_ary_repeated_permutation_size(VALUE ary, VALUE args, VALUE eobj) * e = a.repeated_permutation(2) * e.size # => 9 * e.to_a # => [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]] + * */ static VALUE rb_ary_repeated_permutation(VALUE ary, VALUE num) @@ -7052,16 +7436,22 @@ rb_ary_repeated_combination_size(VALUE ary, VALUE args, VALUE eobj) * The number of combinations is <tt>(n+1)(n+2)/2</tt>. * * +n+ = 1: + * * a = [0, 1, 2] * a.repeated_combination(1) {|combination| p combination } + * * Output: + * * [0] * [1] * [2] * * +n+ = 2: + * * a.repeated_combination(2) {|combination| p combination } + * * Output: + * * [0, 0] * [0, 1] * [0, 2] @@ -7072,14 +7462,17 @@ rb_ary_repeated_combination_size(VALUE ary, VALUE args, VALUE eobj) * If +n+ is zero, calls the block once with an empty \Array. * * If +n+ is negative, does not call the block: + * * a.repeated_combination(-1) {|combination| fail 'Cannot happen' } * * Returns a new \Enumerator if no block given: + * * a = [0, 1, 2] * a.repeated_combination(2) # => #<Enumerator: [0, 1, 2]:combination(2)> * * Using Enumerators, it's convenient to show the combinations and counts * for some values of +n+: + * * e = a.repeated_combination(0) * e.size # => 1 * e.to_a # => [[]] @@ -7089,6 +7482,7 @@ rb_ary_repeated_combination_size(VALUE ary, VALUE args, VALUE eobj) * e = a.repeated_combination(2) * e.size # => 6 * e.to_a # => [[0, 0], [0, 1], [0, 2], [1, 1], [1, 2], [2, 2]] + * */ static VALUE @@ -7132,12 +7526,14 @@ rb_ary_repeated_combination(VALUE ary, VALUE num) * array.product(*other_arrays) {|combination| ... } -> self * * Computes and returns or yields all combinations of elements from all the Arrays, - * including both +self+ and +other_arrays+. + * including both +self+ and +other_arrays+: + * * - The number of combinations is the product of the sizes of all the arrays, * including both +self+ and +other_arrays+. * - The order of the returned combinations is indeterminate. * * When no block is given, returns the combinations as an \Array of Arrays: + * * a = [0, 1, 2] * a1 = [3, 4] * a2 = [5, 6] @@ -7152,11 +7548,15 @@ rb_ary_repeated_combination(VALUE ary, VALUE num) * * If no argument is given, returns an \Array of 1-element Arrays, * each containing an element of +self+: + * * a.product # => [[0], [1], [2]] * * When a block is given, yields each combination as an \Array; returns +self+: + * * a.product(a1) {|combination| p combination } + * * Output: + * * [0, 3] * [0, 4] * [1, 3] @@ -7165,14 +7565,19 @@ rb_ary_repeated_combination(VALUE ary, VALUE num) * [2, 4] * * If any argument is an empty \Array, does not call the block: + * * a.product(a1, a2, []) {|combination| fail 'Cannot happen' } * * If no argument is given, yields each element of +self+ as a 1-element \Array: + * * a.product {|combination| p combination } + * * Output: + * * [0] * [1] * [2] + * */ static VALUE @@ -7230,13 +7635,13 @@ rb_ary_product(int argc, VALUE *argv, VALUE ary) /* put it on the result array */ if (NIL_P(result)) { - FL_SET(t0, FL_USER5); + FL_SET(t0, RARRAY_SHARED_ROOT_FLAG); rb_yield(subarray); - if (! FL_TEST(t0, FL_USER5)) { + if (!FL_TEST(t0, RARRAY_SHARED_ROOT_FLAG)) { rb_raise(rb_eRuntimeError, "product reentered"); } else { - FL_UNSET(t0, FL_USER5); + FL_UNSET(t0, RARRAY_SHARED_ROOT_FLAG); } } else { @@ -7272,11 +7677,13 @@ done: * does not modify +self+. * * Examples: + * * a = [0, 1, 2, 3, 4, 5] * a.take(1) # => [0] * a.take(2) # => [0, 1] * a.take(50) # => [0, 1, 2, 3, 4, 5] * a # => [0, 1, 2, 3, 4, 5] + * */ static VALUE @@ -7299,14 +7706,17 @@ rb_ary_take(VALUE obj, VALUE n) * * With a block given, calls the block with each successive element of +self+; * stops if the block returns +false+ or +nil+; - * returns a new Array containing those elements for which the block returned a truthy value: + * returns a new \Array containing those elements for which the block returned a truthy value: + * * a = [0, 1, 2, 3, 4, 5] * a.take_while {|element| element < 3 } # => [0, 1, 2] * a.take_while {|element| true } # => [0, 1, 2, 3, 4, 5] * a # => [0, 1, 2, 3, 4, 5] * * With no block given, returns a new \Enumerator: + * * [0, 1].take_while # => #<Enumerator: [0, 1]:take_while> + * */ static VALUE @@ -7330,10 +7740,12 @@ rb_ary_take_while(VALUE ary) * does not modify +self+. * * Examples: + * * a = [0, 1, 2, 3, 4, 5] * a.drop(0) # => [0, 1, 2, 3, 4, 5] * a.drop(1) # => [1, 2, 3, 4, 5] * a.drop(2) # => [2, 3, 4, 5] + * */ static VALUE @@ -7360,12 +7772,15 @@ rb_ary_drop(VALUE ary, VALUE n) * * With a block given, calls the block with each successive element of +self+; * stops if the block returns +false+ or +nil+; - * returns a new Array _omitting_ those elements for which the block returned a truthy value: + * returns a new \Array _omitting_ those elements for which the block returned a truthy value: + * * a = [0, 1, 2, 3, 4, 5] * a.drop_while {|element| element < 3 } # => [3, 4, 5] * * With no block given, returns a new \Enumerator: + * * [0, 1].drop_while # => # => #<Enumerator: [0, 1]:drop_while> + * */ static VALUE @@ -7390,17 +7805,20 @@ rb_ary_drop_while(VALUE ary) * * With no block given and no argument, returns +true+ if +self+ has any truthy element, * +false+ otherwise: + * * [nil, 0, false].any? # => true * [nil, false].any? # => false * [].any? # => false * * With a block given and no argument, calls the block with each element in +self+; * returns +true+ if the block returns any truthy value, +false+ otherwise: + * * [0, 1, 2].any? {|element| element > 1 } # => true * [0, 1, 2].any? {|element| element > 2 } # => false * * If argument +obj+ is given, returns +true+ if +obj+.<tt>===</tt> any element, * +false+ otherwise: + * * ['food', 'drink'].any?(/foo/) # => true * ['food', 'drink'].any?(/bar/) # => false * [].any?(/foo/) # => false @@ -7448,16 +7866,19 @@ rb_ary_any_p(int argc, VALUE *argv, VALUE ary) * * With no block given and no argument, returns +true+ if +self+ contains only truthy elements, * +false+ otherwise: + * * [0, 1, :foo].all? # => true * [0, nil, 2].all? # => false * [].all? # => true * * With a block given and no argument, calls the block with each element in +self+; * returns +true+ if the block returns only truthy values, +false+ otherwise: + * * [0, 1, 2].all? { |element| element < 3 } # => true * [0, 1, 2].all? { |element| element < 2 } # => false * * If argument +obj+ is given, returns +true+ if <tt>obj.===</tt> every element, +false+ otherwise: + * * ['food', 'fool', 'foot'].all?(/foo/) # => true * ['food', 'drink'].all?(/bar/) # => false * [].all?(/foo/) # => true @@ -7505,16 +7926,19 @@ rb_ary_all_p(int argc, VALUE *argv, VALUE ary) * * With no block given and no argument, returns +true+ if +self+ has no truthy elements, * +false+ otherwise: + * * [nil, false].none? # => true * [nil, 0, false].none? # => false * [].none? # => true * * With a block given and no argument, calls the block with each element in +self+; * returns +true+ if the block returns no truthy value, +false+ otherwise: + * * [0, 1, 2].none? {|element| element > 3 } # => true * [0, 1, 2].none? {|element| element > 1 } # => false * * If argument +obj+ is given, returns +true+ if <tt>obj.===</tt> no element, +false+ otherwise: + * * ['food', 'drink'].none?(/bar/) # => true * ['food', 'drink'].none?(/foo/) # => false * [].none?(/foo/) # => true @@ -7562,6 +7986,7 @@ rb_ary_none_p(int argc, VALUE *argv, VALUE ary) * * With no block given and no argument, returns +true+ if +self+ has exactly one truthy element, * +false+ otherwise: + * * [nil, 0].one? # => true * [0, 0].one? # => false * [nil, nil].one? # => false @@ -7569,12 +7994,14 @@ rb_ary_none_p(int argc, VALUE *argv, VALUE ary) * * With a block given and no argument, calls the block with each element in +self+; * returns +true+ if the block a truthy value for exactly one element, +false+ otherwise: + * * [0, 1, 2].one? {|element| element > 0 } # => false * [0, 1, 2].one? {|element| element > 1 } # => true * [0, 1, 2].one? {|element| element > 2 } # => false * * If argument +obj+ is given, returns +true+ if <tt>obj.===</tt> exactly one element, * +false+ otherwise: + * * [0, 1, 2].one?(0) # => true * [0, 0, 1].one?(0) # => false * [1, 1, 2].one?(0) # => false @@ -7633,11 +8060,13 @@ rb_ary_one_p(int argc, VALUE *argv, VALUE ary) * See {Dig Methods}[rdoc-ref:dig_methods.rdoc]. * * Examples: + * * a = [:foo, [:bar, :baz, [:bat, :bam]]] * a.dig(1) # => [:bar, :baz, [:bat, :bam]] * a.dig(1, 2) # => [:bat, :bam] * a.dig(1, 2, 0) # => :bat * a.dig(1, 2, 3) # => nil + * */ static VALUE @@ -7670,31 +8099,38 @@ finish_exact_sum(long n, VALUE r, VALUE v, int z) * array.sum(init = 0) {|element| ... } -> object * * When no block is given, returns the object equivalent to: + * * sum = init * array.each {|element| sum += element } * sum + * * For example, <tt>[e1, e2, e3].sum</tt> returns <tt>init + e1 + e2 + e3</tt>. * * Examples: + * * a = [0, 1, 2, 3] * a.sum # => 6 * a.sum(100) # => 106 * * The elements need not be numeric, but must be <tt>+</tt>-compatible * with each other and with +init+: + * * a = ['abc', 'def', 'ghi'] * a.sum('jkl') # => "jklabcdefghi" * * When a block is given, it is called with each element * and the block's return value (instead of the element itself) is used as the addend: + * * a = ['zero', 1, :two] * s = a.sum('Coerced and concatenated: ') {|element| element.to_s } * s # => "Coerced and concatenated: zero1two" * * Notes: + * * - Array#join and Array#flatten may be faster than Array#sum * for an \Array of Strings or an \Array of Arrays. * - Array#sum method may not respect method redefinition of "+" methods such as Integer#+. + * */ static VALUE @@ -7814,82 +8250,128 @@ rb_ary_deconstruct(VALUE ary) } /* - * An \Array is an ordered, integer-indexed collection of objects, - * called _elements_. Any object may be an \Array element. + * An \Array is an ordered, integer-indexed collection of objects, called _elements_. + * Any object (even another array) may be an array element, + * and an array can contain objects of different types. * * == \Array Indexes * * \Array indexing starts at 0, as in C or Java. * * A positive index is an offset from the first element: + * * - Index 0 indicates the first element. * - Index 1 indicates the second element. * - ... * * A negative index is an offset, backwards, from the end of the array: + * * - Index -1 indicates the last element. * - Index -2 indicates the next-to-last element. * - ... * - * A non-negative index is <i>in range</i> if it is smaller than + * A non-negative index is <i>in range</i> if and only if it is smaller than * the size of the array. For a 3-element array: + * * - Indexes 0 through 2 are in range. * - Index 3 is out of range. * - * A negative index is <i>in range</i> if its absolute value is + * A negative index is <i>in range</i> if and only if its absolute value is * not larger than the size of the array. For a 3-element array: + * * - Indexes -1 through -3 are in range. * - Index -4 is out of range. * - * == Creating Arrays - * - * You can create an \Array object explicitly with: - * - * - An {array literal}[doc/syntax/literals_rdoc.html#label-Array+Literals]. - * - * You can convert certain objects to Arrays with: - * - * - \Method {Array}[Kernel.html#method-i-Array]. - * - * An \Array can contain different types of objects. For - * example, the array below contains an Integer, a String and a Float: - * - * ary = [1, "two", 3.0] #=> [1, "two", 3.0] - * - * An array can also be created by calling Array.new with zero, one - * (the initial size of the Array) or two arguments (the initial size and a - * default object). - * - * ary = Array.new #=> [] - * Array.new(3) #=> [nil, nil, nil] - * Array.new(3, true) #=> [true, true, true] - * - * Note that the second argument populates the array with references to the - * same object. Therefore, it is only recommended in cases when you need to - * instantiate arrays with natively immutable objects such as Symbols, - * numbers, true or false. + * Although the effective index into an array is always an integer, + * some methods (both within and outside of class \Array) + * accept one or more non-integer arguments that are + * {integer-convertible objects}[rdoc-ref:implicit_conversion.rdoc@Integer-Convertible+Objects]. * - * To create an array with separate objects a block can be passed instead. - * This method is safe to use with mutable objects such as hashes, strings or - * other arrays: * - * Array.new(4) {Hash.new} #=> [{}, {}, {}, {}] - * Array.new(4) {|i| i.to_s } #=> ["0", "1", "2", "3"] - * - * This is also a quick way to build up multi-dimensional arrays: - * - * empty_table = Array.new(3) {Array.new(3)} - * #=> [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]] + * == Creating Arrays * - * An array can also be created by using the Array() method, provided by - * Kernel, which tries to call #to_ary, then #to_a on its argument. + * You can create an \Array object explicitly with: * - * Array({:a => "a", :b => "b"}) #=> [[:a, "a"], [:b, "b"]] + * - An {array literal}[rdoc-ref:literals.rdoc@Array+Literals]: + * + * [1, 'one', :one, [2, 'two', :two]] + * + * - A {%w or %W: string-array Literal}[rdoc-ref:literals.rdoc@25w+and+-25W-3A+String-Array+Literals]: + * + * %w[foo bar baz] # => ["foo", "bar", "baz"] + * %w[1 % *] # => ["1", "%", "*"] + * + * - A {%i pr %I: symbol-array Literal}[rdoc-ref:literals.rdoc@25i+and+-25I-3A+Symbol-Array+Literals]: + * + * %i[foo bar baz] # => [:foo, :bar, :baz] + * %i[1 % *] # => [:"1", :%, :*] + * + * - \Method Kernel#Array: + * + * Array(["a", "b"]) # => ["a", "b"] + * Array(1..5) # => [1, 2, 3, 4, 5] + * Array(key: :value) # => [[:key, :value]] + * Array(nil) # => [] + * Array(1) # => [1] + * Array({:a => "a", :b => "b"}) # => [[:a, "a"], [:b, "b"]] + * + * - \Method Array.new: + * + * Array.new # => [] + * Array.new(3) # => [nil, nil, nil] |