summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gdbinit19
-rw-r--r--.github/workflows/check_sast.yml5
-rw-r--r--.github/workflows/macos.yml18
-rw-r--r--.github/workflows/modgc.yml2
-rw-r--r--.github/workflows/tarball-macos.yml10
-rw-r--r--.github/workflows/tarball-non-development.yml3
-rw-r--r--.github/workflows/tarball-test.yml14
-rw-r--r--.github/workflows/tarball-ubuntu.yml8
-rw-r--r--.github/workflows/tarball-windows.yml8
-rw-r--r--.github/workflows/yjit-macos.yml6
-rw-r--r--.github/workflows/zjit-macos.yml8
-rw-r--r--.github/workflows/zjit-ubuntu.yml2
-rw-r--r--.github/zizmor.yml1
-rw-r--r--NEWS.md20
-rw-r--r--encoding.c2
-rw-r--r--error.c7
-rw-r--r--ext/date/date_core.c4
-rw-r--r--ext/date/date_strptime.c6
-rw-r--r--ext/json/generator/generator.c19
-rw-r--r--ext/json/lib/json/version.rb2
-rw-r--r--ext/json/parser/parser.c58
-rw-r--r--ext/objspace/objspace_dump.c2
-rw-r--r--ext/openssl/lib/openssl/digest.rb24
-rw-r--r--ext/openssl/ossl_kdf.c8
-rw-r--r--ext/openssl/ossl_pkcs12.c11
-rw-r--r--ext/openssl/ossl_pkcs7.c2
-rw-r--r--ext/openssl/ossl_ts.c41
-rw-r--r--file.c94
-rw-r--r--gc.c97
-rw-r--r--hash.c8
-rw-r--r--include/ruby/internal/abi.h2
-rw-r--r--include/ruby/internal/core/rdata.h287
-rw-r--r--include/ruby/internal/core/rtypeddata.h53
-rw-r--r--include/ruby/internal/fl_type.h4
-rw-r--r--include/ruby/internal/value_type.h22
-rw-r--r--include/ruby/random.h1
-rw-r--r--insns.def19
-rw-r--r--internal/hash.h2
-rw-r--r--lib/rubygems/yaml_serializer.rb60
-rw-r--r--marshal.c51
-rw-r--r--misc/lldb_rb/utils.py37
-rw-r--r--pathname_builtin.rb148
-rw-r--r--ractor.c2
-rw-r--r--spec/bundler/spec_helper.rb10
-rw-r--r--spec/bundler/support/artifice/helpers/compact_index.rb10
-rw-r--r--spec/bundler/support/shards.rb (renamed from spec/bundler/support/windows_tag_group.rb)16
-rw-r--r--spec/ruby/optional/capi/ext/rubyspec.h4
-rw-r--r--spec/ruby/optional/capi/ext/typed_data_spec.c9
-rw-r--r--spec/ruby/optional/capi/typed_data_spec.rb18
-rw-r--r--test/date/test_date_strptime.rb15
-rwxr-xr-xtest/json/json_generator_test.rb2
-rw-r--r--test/json/json_parser_test.rb28
-rw-r--r--test/json/json_ryu_fallback_test.rb8
-rw-r--r--test/openssl/test_digest.rb16
-rw-r--r--test/openssl/test_kdf.rb135
-rw-r--r--test/ruby/test_io.rb4
-rw-r--r--test/ruby/test_marshal.rb35
-rw-r--r--test/ruby/test_ractor.rb53
-rw-r--r--test/rubygems/test_gem_commands_cert_command.rb14
-rw-r--r--test/rubygems/test_gem_safe_yaml.rb73
-rw-r--r--tool/bundler/test_gems.rb1
-rw-r--r--tool/bundler/test_gems.rb.lock1
-rw-r--r--vm_insnhelper.c19
-rw-r--r--win32/win32.c2
-rw-r--r--yjit/src/cruby_bindings.inc.rs2
-rw-r--r--zjit/src/cruby_bindings.inc.rs3
-rw-r--r--zjit/src/hir.rs5
67 files changed, 915 insertions, 765 deletions
diff --git a/.gdbinit b/.gdbinit
index 0d585beef9..4457f6f12b 100644
--- a/.gdbinit
+++ b/.gdbinit
@@ -185,19 +185,14 @@ define rp
print (struct RBasic *)($arg0)
else
if ($flags & RUBY_T_MASK) == RUBY_T_DATA
- if ($flags & RUBY_TYPED_FL_IS_TYPED_DATA)
- set $data = (struct RTypedData *)($arg0)
- set $type = (const rb_data_type_t *)($data->type & ~1)
- printf "%sT_DATA%s(%s): ", $color_type, $color_end, $type->wrap_struct_name
- print *$type
- if ($data->type & 1)
- print (void *)&$data->data
- else
- print $data
- end
+ set $data = (struct RTypedData *)($arg0)
+ set $type = (const rb_data_type_t *)($data->type & ~1)
+ printf "%sT_DATA%s(%s): ", $color_type, $color_end, $type->wrap_struct_name
+ print *$type
+ if ($data->type & 1)
+ print (void *)&$data->data
else
- printf "%sT_DATA%s: ", $color_type, $color_end
- print *(struct RData *)($arg0)
+ print $data
end
else
if ($flags & RUBY_T_MASK) == RUBY_T_MATCH
diff --git a/.github/workflows/check_sast.yml b/.github/workflows/check_sast.yml
index 091f98ef34..6fd1be6542 100644
--- a/.github/workflows/check_sast.yml
+++ b/.github/workflows/check_sast.yml
@@ -92,7 +92,7 @@ jobs:
output: sarif-results
- name: filter-sarif
- uses: advanced-security/filter-sarif@2da736ff05ef065cb2894ac6892e47b5eac2c3c0 # v1.1.0.1.1
+ uses: advanced-security/filter-sarif@2da736ff05ef065cb2894ac6892e47b5eac2c3c0 # v1.1
with:
patterns: |
+**/*.rb
@@ -100,6 +100,7 @@ jobs:
-lib/uri/rfc3986_parser.rb:rb/overly-large-range
-lib/bundler/vendor/uri/lib/uri/mailto.rb:rb/overly-large-range
-lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb:rb/overly-large-range
+ -spec/ruby/core/regexp/timeout_spec.rb:rb/redos
-test/ruby/test_io.rb:rb/non-constant-kernel-open
-test/open-uri/test_open-uri.rb:rb/non-constant-kernel-open
-test/open-uri/test_ssl.rb:rb/non-constant-kernel-open
@@ -115,7 +116,7 @@ jobs:
continue-on-error: true
- name: filter-sarif
- uses: advanced-security/filter-sarif@2da736ff05ef065cb2894ac6892e47b5eac2c3c0 # v1.1.0.1.1
+ uses: advanced-security/filter-sarif@2da736ff05ef065cb2894ac6892e47b5eac2c3c0 # v1.1
with:
patterns: |
+**/*.c
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index d355e4f4ab..501d35698b 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -26,25 +26,29 @@ jobs:
matrix:
include:
- test_task: check
- os: macos-14
+ os: macos-26
- test_task: check
- os: macos-14
+ os: macos-15
configure_args: '--with-gcc=gcc-14'
- test_task: check
- os: macos-14
+ os: macos-26
configure_args: '--with-jemalloc --with-opt-dir=$(brew --prefix jemalloc)'
- test_task: check
- os: macos-14
+ os: macos-26
configure_args: '--with-gmp'
- test_task: test-all
test_opts: --repeat-count=2
- os: macos-14
+ os: macos-26
- test_task: test-bundler-parallel
- os: macos-14
+ os: macos-26
- test_task: test-bundled-gems
- os: macos-14
+ os: macos-26
- test_task: check
os: macos-15
+ - test_task: check
+ os: macos-15-intel
+ - test_task: check
+ os: macos-14
fail-fast: false
env:
diff --git a/.github/workflows/modgc.yml b/.github/workflows/modgc.yml
index f6aa445ad0..bd3c6ab575 100644
--- a/.github/workflows/modgc.yml
+++ b/.github/workflows/modgc.yml
@@ -28,7 +28,7 @@ jobs:
- name: default
- name: mmtk
mmtk_build: release
- os: [macos-latest, ubuntu-latest]
+ os: [macos-26, ubuntu-latest]
include:
- test_task: check
fail-fast: false
diff --git a/.github/workflows/tarball-macos.yml b/.github/workflows/tarball-macos.yml
index 72c5a78c14..9bec94d528 100644
--- a/.github/workflows/tarball-macos.yml
+++ b/.github/workflows/tarball-macos.yml
@@ -12,13 +12,21 @@ on:
required: false
type: boolean
default: false
+ secrets:
+ SIMPLER_ALERTS_URL:
+ required: false
+ SNAPSHOT_SLACK_WEBHOOK_URL:
+ required: false
+
+permissions:
+ contents: read
jobs:
macos:
strategy:
matrix:
test_task: [check, test-bundled-gems, test-bundler-parallel]
- os: [macos-14, macos-15]
+ os: [macos-26, macos-15, macos-14]
include:
- os: macos-15-intel
test_task: check
diff --git a/.github/workflows/tarball-non-development.yml b/.github/workflows/tarball-non-development.yml
index 154d204a2c..db6230b301 100644
--- a/.github/workflows/tarball-non-development.yml
+++ b/.github/workflows/tarball-non-development.yml
@@ -3,6 +3,9 @@ name: tarball-non-development (reusable)
on:
workflow_call: {}
+permissions:
+ contents: read
+
jobs:
non_development:
strategy:
diff --git a/.github/workflows/tarball-test.yml b/.github/workflows/tarball-test.yml
index e99c4515c8..52c4d31fc8 100644
--- a/.github/workflows/tarball-test.yml
+++ b/.github/workflows/tarball-test.yml
@@ -50,6 +50,7 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 1 # actions/checkout fetches all heads/tags unless > 0
+ persist-credentials: false
# tool/make-snapshot derives the branch name from HEAD and looks up
# the upstream during ChangeLog generation. Detached checkouts
# (pull_request, merge_group) lack a local branch with tracking, so
@@ -73,7 +74,9 @@ jobs:
with:
archname: snapshot-${{ needs.tarball.outputs.branch }}
notify-release-channel: ${{ github.event_name == 'workflow_dispatch' && inputs.notify-release-channel || false }}
- secrets: inherit
+ secrets:
+ SIMPLER_ALERTS_URL: ${{ secrets.SIMPLER_ALERTS_URL }}
+ SNAPSHOT_SLACK_WEBHOOK_URL: ${{ secrets.SNAPSHOT_SLACK_WEBHOOK_URL }}
macos:
needs: tarball
@@ -82,7 +85,9 @@ jobs:
with:
archname: snapshot-${{ needs.tarball.outputs.branch }}
notify-release-channel: ${{ github.event_name == 'workflow_dispatch' && inputs.notify-release-channel || false }}
- secrets: inherit
+ secrets:
+ SIMPLER_ALERTS_URL: ${{ secrets.SIMPLER_ALERTS_URL }}
+ SNAPSHOT_SLACK_WEBHOOK_URL: ${{ secrets.SNAPSHOT_SLACK_WEBHOOK_URL }}
windows:
needs: tarball
@@ -91,9 +96,10 @@ jobs:
with:
archname: snapshot-${{ needs.tarball.outputs.branch }}
notify-release-channel: ${{ github.event_name == 'workflow_dispatch' && inputs.notify-release-channel || false }}
- secrets: inherit
+ secrets:
+ SIMPLER_ALERTS_URL: ${{ secrets.SIMPLER_ALERTS_URL }}
+ SNAPSHOT_SLACK_WEBHOOK_URL: ${{ secrets.SNAPSHOT_SLACK_WEBHOOK_URL }}
non_development:
needs: tarball
uses: ./.github/workflows/tarball-non-development.yml
- secrets: inherit
diff --git a/.github/workflows/tarball-ubuntu.yml b/.github/workflows/tarball-ubuntu.yml
index f0e773b526..03f2f946b5 100644
--- a/.github/workflows/tarball-ubuntu.yml
+++ b/.github/workflows/tarball-ubuntu.yml
@@ -12,6 +12,14 @@ on:
required: false
type: boolean
default: false
+ secrets:
+ SIMPLER_ALERTS_URL:
+ required: false
+ SNAPSHOT_SLACK_WEBHOOK_URL:
+ required: false
+
+permissions:
+ contents: read
jobs:
ubuntu:
diff --git a/.github/workflows/tarball-windows.yml b/.github/workflows/tarball-windows.yml
index 1cd4ef454d..1ce95de6fc 100644
--- a/.github/workflows/tarball-windows.yml
+++ b/.github/workflows/tarball-windows.yml
@@ -12,6 +12,14 @@ on:
required: false
type: boolean
default: false
+ secrets:
+ SIMPLER_ALERTS_URL:
+ required: false
+ SNAPSHOT_SLACK_WEBHOOK_URL:
+ required: false
+
+permissions:
+ contents: read
jobs:
windows:
diff --git a/.github/workflows/yjit-macos.yml b/.github/workflows/yjit-macos.yml
index 6e0827c8fe..b52c6355ad 100644
--- a/.github/workflows/yjit-macos.yml
+++ b/.github/workflows/yjit-macos.yml
@@ -30,7 +30,7 @@ jobs:
cargo:
name: cargo test
- runs-on: macos-14
+ runs-on: macos-26
if: >-
${{!(false
@@ -74,7 +74,7 @@ jobs:
RUN_OPTS: ${{ matrix.yjit_opts }}
SPECOPTS: ${{ matrix.specopts }}
- runs-on: macos-14
+ runs-on: macos-26
if: >-
${{!(false
@@ -134,7 +134,7 @@ jobs:
id: launchable
uses: ./.github/actions/launchable/setup
with:
- os: macos-14
+ os: macos-26
test-opts: ${{ matrix.configure }}
launchable-token: ${{ secrets.LAUNCHABLE_TOKEN }}
builddir: build
diff --git a/.github/workflows/zjit-macos.yml b/.github/workflows/zjit-macos.yml
index 3011a04cac..f9282d64e8 100644
--- a/.github/workflows/zjit-macos.yml
+++ b/.github/workflows/zjit-macos.yml
@@ -58,7 +58,7 @@ jobs:
RUST_BACKTRACE: 1
ZJIT_RB_BUG: 1
- runs-on: macos-14
+ runs-on: macos-26
if: >-
${{!(false
@@ -93,7 +93,7 @@ jobs:
rustup install ${{ matrix.rust_version }} --profile minimal
rustup default ${{ matrix.rust_version }}
- - uses: taiki-e/install-action@8f531eaecd1898bc3da7d104ad91bee98d1b97bd # v2.79.9
+ - uses: taiki-e/install-action@13608cbb45b01feb47ef444ab1a42dc41ad56f1a # v2.79.11
with:
tool: nextest@0.9
if: ${{ matrix.test_task == 'zjit-check' }}
@@ -118,7 +118,7 @@ jobs:
id: launchable
uses: ./.github/actions/launchable/setup
with:
- os: macos-14
+ os: macos-26
test-opts: ${{ matrix.configure }}
launchable-token: ${{ secrets.LAUNCHABLE_TOKEN }}
builddir: build
@@ -181,7 +181,7 @@ jobs:
bench_opts: '--warmup=1 --bench=1 --excludes=shipit'
configure: '--enable-zjit=dev_nodebug' # --enable-zjit=dev is too slow
- runs-on: macos-14
+ runs-on: macos-26
if: >-
${{!(false
diff --git a/.github/workflows/zjit-ubuntu.yml b/.github/workflows/zjit-ubuntu.yml
index d91dfcbeeb..1e7bec3859 100644
--- a/.github/workflows/zjit-ubuntu.yml
+++ b/.github/workflows/zjit-ubuntu.yml
@@ -119,7 +119,7 @@ jobs:
ruby-version: '3.1'
bundler: none
- - uses: taiki-e/install-action@8f531eaecd1898bc3da7d104ad91bee98d1b97bd # v2.79.9
+ - uses: taiki-e/install-action@13608cbb45b01feb47ef444ab1a42dc41ad56f1a # v2.79.11
with:
tool: nextest@0.9
if: ${{ matrix.test_task == 'zjit-check' }}
diff --git a/.github/zizmor.yml b/.github/zizmor.yml
index 53112a2815..65b67fb6c8 100644
--- a/.github/zizmor.yml
+++ b/.github/zizmor.yml
@@ -13,6 +13,7 @@ rules:
misfeature:
ignore:
- mingw.yml
+ - tarball-windows.yml
- windows.yml
unpinned-images:
ignore:
diff --git a/NEWS.md b/NEWS.md
index 9273e0adff..038f1a63c6 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -82,8 +82,8 @@ releases.
* 6.0.1 to [v6.0.1.1][erb-v6.0.1.1], [v6.0.2][erb-v6.0.2], [v6.0.3][erb-v6.0.3], [v6.0.4][erb-v6.0.4]
* ipaddr 1.2.9
* 1.2.8 to [v1.2.9][ipaddr-v1.2.9]
-* json 2.19.5
- * 2.18.0 to [v2.18.1][json-v2.18.1], [v2.19.0][json-v2.19.0], [v2.19.1][json-v2.19.1], [v2.19.2][json-v2.19.2], [v2.19.3][json-v2.19.3], [v2.19.4][json-v2.19.4], [v2.19.5][json-v2.19.5]
+* json 2.19.7
+ * 2.18.0 to [v2.18.1][json-v2.18.1], [v2.19.0][json-v2.19.0], [v2.19.1][json-v2.19.1], [v2.19.2][json-v2.19.2], [v2.19.3][json-v2.19.3], [v2.19.4][json-v2.19.4], [v2.19.5][json-v2.19.5], [v2.19.6][json-v2.19.6], [v2.19.7][json-v2.19.7]
* openssl 4.0.2
* 4.0.0 to [v4.0.1][openssl-v4.0.1], [v4.0.2][openssl-v4.0.2]
* prism 1.9.0
@@ -161,6 +161,19 @@ Ruby 4.0 bundled RubyGems and Bundler version 4. see the following links for det
[[Feature #21861]]
+### Removed APIs
+
+The following APIs, which have been deprecated for many years, are removed.
+[[Feature #21768]]
+
+* old postponed job functions,
+* untyped data object type/functions,
+* old APIs to allocate a data object,
+* taintedness/trustedness enums/macros,
+* `rb_gc_force_recycle` function,
+* `rb_iterate` function,
+* and some functions and constants for internal use.
+
## Implementation improvements
### Ractor
@@ -172,6 +185,7 @@ A lot of work has gone into making Ractors more stable, performant, and usable.
[Feature #8948]: https://bugs.ruby-lang.org/issues/8948
[Feature #15330]: https://bugs.ruby-lang.org/issues/15330
[Feature #21390]: https://bugs.ruby-lang.org/issues/21390
+[Feature #21768]: https://bugs.ruby-lang.org/issues/21768
[Feature #21785]: https://bugs.ruby-lang.org/issues/21785
[Feature #21796]: https://bugs.ruby-lang.org/issues/21796
[Feature #21853]: https://bugs.ruby-lang.org/issues/21853
@@ -208,6 +222,8 @@ A lot of work has gone into making Ractors more stable, performant, and usable.
[json-v2.19.3]: https://github.com/ruby/json/releases/tag/v2.19.3
[json-v2.19.4]: https://github.com/ruby/json/releases/tag/v2.19.4
[json-v2.19.5]: https://github.com/ruby/json/releases/tag/v2.19.5
+[json-v2.19.6]: https://github.com/ruby/json/releases/tag/v2.19.6
+[json-v2.19.7]: https://github.com/ruby/json/releases/tag/v2.19.7
[openssl-v4.0.1]: https://github.com/ruby/openssl/releases/tag/v4.0.1
[openssl-v4.0.2]: https://github.com/ruby/openssl/releases/tag/v4.0.2
[prism-v1.8.0]: https://github.com/ruby/prism/releases/tag/v1.8.0
diff --git a/encoding.c b/encoding.c
index 8bb393b471..73fad8f1a6 100644
--- a/encoding.c
+++ b/encoding.c
@@ -126,7 +126,7 @@ static const rb_data_type_t encoding_data_type = {
};
#define is_encoding_type(obj) (RTYPEDDATA_TYPE(obj) == &encoding_data_type)
-#define is_data_encoding(obj) (rbimpl_rtypeddata_p(obj) && is_encoding_type(obj))
+#define is_data_encoding(obj) is_encoding_type(obj)
#define is_obj_encoding(obj) (rbimpl_obj_typeddata_p(obj) && is_encoding_type(obj))
int
diff --git a/error.c b/error.c
index ed7394dbf5..7a08fd2b9e 100644
--- a/error.c
+++ b/error.c
@@ -1364,8 +1364,7 @@ rb_check_type(VALUE x, int t)
rb_bug(UNDEF_LEAKED);
}
- xt = TYPE(x);
- if (xt != t || (xt == T_DATA && rbimpl_rtypeddata_p(x))) {
+ if (t == T_DATA) {
/*
* Typed data is not simple `T_DATA`, but in a sense an
* extension of `struct RVALUE`, which are incompatible with
@@ -1374,6 +1373,10 @@ rb_check_type(VALUE x, int t)
* So it is not enough to just check `T_DATA`, it must be
* identified by its `type` using `Check_TypedStruct` instead.
*/
+ rb_unexpected_object_type(x, builtin_types[t]);
+ }
+ xt = TYPE(x);
+ if (xt != t) {
unexpected_type(x, xt, t);
}
}
diff --git a/ext/date/date_core.c b/ext/date/date_core.c
index 6295264a3d..72d697c8ea 100644
--- a/ext/date/date_core.c
+++ b/ext/date/date_core.c
@@ -27,7 +27,7 @@ static VALUE eDateError;
static VALUE half_days_in_day, day_in_nanoseconds;
static double positive_inf, negative_inf;
-// used by deconstruct_keys
+/* used by deconstruct_keys */
static VALUE sym_year, sym_month, sym_day, sym_yday, sym_wday;
static VALUE sym_hour, sym_min, sym_sec, sym_sec_fraction, sym_zone;
@@ -4528,6 +4528,7 @@ date_s__strptime_internal(int argc, VALUE *argv, VALUE klass,
rb_scan_args(argc, argv, "11", &vstr, &vfmt);
StringValue(vstr);
+ if (argc > 1) StringValue(vfmt);
if (!rb_enc_str_asciicompat_p(vstr))
rb_raise(rb_eArgError,
"string should have ASCII compatible encoding");
@@ -4538,7 +4539,6 @@ date_s__strptime_internal(int argc, VALUE *argv, VALUE klass,
flen = strlen(default_fmt);
}
else {
- StringValue(vfmt);
if (!rb_enc_str_asciicompat_p(vfmt))
rb_raise(rb_eArgError,
"format should have ASCII compatible encoding");
diff --git a/ext/date/date_strptime.c b/ext/date/date_strptime.c
index f1c8201de8..1dde5fa3ec 100644
--- a/ext/date/date_strptime.c
+++ b/ext/date/date_strptime.c
@@ -661,6 +661,9 @@ date__strptime(const char *str, size_t slen,
si = date__strptime_internal(str, slen, fmt, flen, hash);
+ if (fail_p())
+ return Qnil;
+
if (slen > si) {
VALUE s;
@@ -668,9 +671,6 @@ date__strptime(const char *str, size_t slen,
set_hash("leftover", s);
}
- if (fail_p())
- return Qnil;
-
cent = del_hash("_cent");
if (!NIL_P(cent)) {
VALUE year;
diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c
index 882d47a611..110b5f6b32 100644
--- a/ext/json/generator/generator.c
+++ b/ext/json/generator/generator.c
@@ -1367,12 +1367,14 @@ static VALUE cState_init_copy(VALUE obj, VALUE orig)
if (!objState) rb_raise(rb_eArgError, "unallocated JSON::State");
MEMCPY(objState, origState, JSON_Generator_State, 1);
- objState->indent = origState->indent;
- objState->space = origState->space;
- objState->space_before = origState->space_before;
- objState->object_nl = origState->object_nl;
- objState->array_nl = origState->array_nl;
- objState->as_json = origState->as_json;
+
+ RB_OBJ_WRITTEN(obj, Qundef, objState->indent);
+ RB_OBJ_WRITTEN(obj, Qundef, objState->space);
+ RB_OBJ_WRITTEN(obj, Qundef, objState->space_before);
+ RB_OBJ_WRITTEN(obj, Qundef, objState->object_nl);
+ RB_OBJ_WRITTEN(obj, Qundef, objState->array_nl);
+ RB_OBJ_WRITTEN(obj, Qundef, objState->as_json);
+
return obj;
}
@@ -1579,7 +1581,7 @@ static VALUE cState_max_nesting(VALUE self)
static long long_config(VALUE num)
{
- return RTEST(num) ? FIX2LONG(num) : 0;
+ return RTEST(num) ? NUM2LONG(num) : 0;
}
// depth must never be negative; reject early with a clear error.
@@ -1590,6 +1592,9 @@ static long depth_config(VALUE num)
if (RB_UNLIKELY(d < 0)) {
rb_raise(rb_eArgError, "depth must be >= 0 (got %ld)", d);
}
+ if (RB_UNLIKELY(d > INT_MAX)) {
+ rb_raise(rb_eArgError, "depth is too large (got %ld)", d);
+ }
return d;
}
diff --git a/ext/json/lib/json/version.rb b/ext/json/lib/json/version.rb
index ebb55656d7..a69590ff9c 100644
--- a/ext/json/lib/json/version.rb
+++ b/ext/json/lib/json/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module JSON
- VERSION = '2.19.5'
+ VERSION = '2.19.7'
end
diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c
index a740bd42ed..503bed1fd4 100644
--- a/ext/json/parser/parser.c
+++ b/ext/json/parser/parser.c
@@ -385,6 +385,13 @@ static inline char peek(JSON_ParserState *state)
static void cursor_position(JSON_ParserState *state, long *line_out, long *column_out)
{
+ JSON_ASSERT(state->cursor <= state->end);
+
+ // Redundant but helpful for hardening
+ if (RB_UNLIKELY(state->cursor > state->end)) {
+ state->cursor = state->end;
+ }
+
const char *cursor = state->cursor;
long column = 0;
long line = 1;
@@ -1022,6 +1029,13 @@ ALWAYS_INLINE(static) bool string_scan(JSON_ParserState *state)
}
state->cursor++;
}
+
+ // If the string ended with an unterminated escape sequence, we might
+ // have gone past the end.
+ if (RB_UNLIKELY(state->cursor > state->end)) {
+ state->cursor = state->end;
+ }
+
return false;
}
@@ -1202,7 +1216,11 @@ static inline VALUE json_parse_number(JSON_ParserState *state, JSON_ParserConfig
raise_parse_error_at("invalid number: %s", state, start);
}
- exponent = negative_exponent ? -abs_exponent : abs_exponent;
+ if (RB_UNLIKELY(exponent_digits >= 20 || abs_exponent > (uint64_t)INT64_MAX)) {
+ exponent = negative_exponent ? INT64_MIN : INT64_MAX;
+ } else {
+ exponent = negative_exponent ? -(int64_t)abs_exponent : (int64_t)abs_exponent;
+ }
}
if (integer) {
@@ -1457,18 +1475,21 @@ static void json_ensure_eof(JSON_ParserState *state)
static VALUE convert_encoding(VALUE source)
{
- int encindex = RB_ENCODING_GET(source);
+ StringValue(source);
+ int encindex = RB_ENCODING_GET(source);
- if (RB_LIKELY(encindex == utf8_encindex)) {
- return source;
- }
+ if (RB_LIKELY(encindex == utf8_encindex)) {
+ return source;
+ }
- if (encindex == binary_encindex) {
- // For historical reason, we silently reinterpret binary strings as UTF-8
- return rb_enc_associate_index(rb_str_dup(source), utf8_encindex);
- }
+ if (encindex == binary_encindex) {
+ // For historical reason, we silently reinterpret binary strings as UTF-8
+ return rb_enc_associate_index(rb_str_dup(source), utf8_encindex);
+ }
- return rb_funcall(source, i_encode, 1, Encoding_UTF_8);
+ source = rb_funcall(source, i_encode, 1, Encoding_UTF_8);
+ StringValue(source);
+ return source;
}
struct parser_config_init_args {
@@ -1583,10 +1604,16 @@ static VALUE cParserConfig_initialize(VALUE self, VALUE opts)
return self;
}
-static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource)
+static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src)
{
- Vsource = convert_encoding(StringValue(Vsource));
- StringValue(Vsource);
+ VALUE Vsource = convert_encoding(src);
+
+ // Ensure the string isn't mutated under us.
+ // The classic API to use is `rb_str_locktmp`, but then we'd
+ // need to use `rb_protect` to make sure we always unlock.
+ if (Vsource == src) {
+ Vsource = rb_str_new_frozen(Vsource);
+ }
VALUE rvalue_stack_buffer[RVALUE_STACK_INITIAL_CAPA];
rvalue_stack stack = {
@@ -1597,6 +1624,7 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource)
long len;
const char *start;
+
RSTRING_GETMEM(Vsource, start, len);
VALUE stack_handle = 0;
@@ -1615,6 +1643,7 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource)
// it won't cause a leak.
rvalue_stack_eagerly_release(stack_handle);
RB_GC_GUARD(stack_handle);
+ RB_GC_GUARD(Vsource);
json_ensure_eof(state);
return result;
@@ -1635,9 +1664,6 @@ static VALUE cParserConfig_parse(VALUE self, VALUE Vsource)
static VALUE cParser_m_parse(VALUE klass, VALUE Vsource, VALUE opts)
{
- Vsource = convert_encoding(StringValue(Vsource));
- StringValue(Vsource);
-
JSON_ParserConfig _config = {0};
JSON_ParserConfig *config = &_config;
parser_config_init(config, opts, false);
diff --git a/ext/objspace/objspace_dump.c b/ext/objspace/objspace_dump.c
index b644c489b8..68ada01af3 100644
--- a/ext/objspace/objspace_dump.c
+++ b/ext/objspace/objspace_dump.c
@@ -573,7 +573,7 @@ dump_object(VALUE obj, struct dump_config *dc)
break;
case T_DATA:
- if (RTYPEDDATA_P(obj)) {
+ {
const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
dump_append(dc, ", \"struct\":\"");
dump_append(dc, type->wrap_struct_name);
diff --git a/ext/openssl/lib/openssl/digest.rb b/ext/openssl/lib/openssl/digest.rb
index 46ddfd6021..4e6dea8d03 100644
--- a/ext/openssl/lib/openssl/digest.rb
+++ b/ext/openssl/lib/openssl/digest.rb
@@ -27,17 +27,21 @@ module OpenSSL
end
%w(MD4 MD5 RIPEMD160 SHA1 SHA224 SHA256 SHA384 SHA512).each do |name|
- klass = Class.new(self) {
- define_method(:initialize, ->(data = nil) {super(name, data)})
- }
-
- singleton = (class << klass; self; end)
-
- singleton.class_eval{
- define_method(:digest) {|data| new.digest(data)}
- define_method(:hexdigest) {|data| new.hexdigest(data)}
- }
+ klass = Class.new(self)
+ klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def initialize(data = nil)
+ super("#{name}", data)
+ end
+ RUBY
+ klass.singleton_class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def digest(data)
+ new.digest(data)
+ end
+ def hexdigest(data)
+ new.hexdigest(data)
+ end
+ RUBY
const_set(name.tr('-', '_'), klass)
end
diff --git a/ext/openssl/ossl_kdf.c b/ext/openssl/ossl_kdf.c
index f70b7f6cf9..99a3589b39 100644
--- a/ext/openssl/ossl_kdf.c
+++ b/ext/openssl/ossl_kdf.c
@@ -39,6 +39,10 @@ pbkdf2_hmac_nogvl(void *args_)
* For more information about PBKDF2, see RFC 2898 Section 5.2
* (https://www.rfc-editor.org/rfc/rfc2898#section-5.2).
*
+ * *NOTE*: This method cannot be interrupted by Timeout.timeout from the
+ * "timeout" library. Do not take parameters from untrusted sources without
+ * enforcing reasonable limits.
+ *
* === Parameters
* pass :: The password.
* salt :: The salt. Salts prevent attacks based on dictionaries of common
@@ -143,6 +147,10 @@ scrypt_nogvl(void *args_)
*
* See RFC 7914 (https://www.rfc-editor.org/rfc/rfc7914) for more information.
*
+ * *NOTE*: This method cannot be interrupted by Timeout.timeout from the
+ * "timeout" library. Do not take parameters from untrusted sources without
+ * enforcing reasonable limits.
+ *
* === Parameters
* pass :: Passphrase.
* salt :: Salt.
diff --git a/ext/openssl/ossl_pkcs12.c b/ext/openssl/ossl_pkcs12.c
index 32b82a881c..a47c81354c 100644
--- a/ext/openssl/ossl_pkcs12.c
+++ b/ext/openssl/ossl_pkcs12.c
@@ -191,6 +191,7 @@ ossl_x509_sk2ary_i(VALUE arg)
static VALUE
ossl_pkcs12_initialize(int argc, VALUE *argv, VALUE self)
{
+ PKCS12 *p12, *p12_orig = DATA_PTR(self);
BIO *in;
VALUE arg, pass, pkey, cert, ca;
char *passphrase;
@@ -198,17 +199,19 @@ ossl_pkcs12_initialize(int argc, VALUE *argv, VALUE self)
X509 *x509;
STACK_OF(X509) *x509s = NULL;
int st = 0;
- PKCS12 *pkcs = DATA_PTR(self);
if(rb_scan_args(argc, argv, "02", &arg, &pass) == 0) return self;
passphrase = NIL_P(pass) ? NULL : StringValueCStr(pass);
in = ossl_obj2bio(&arg);
- d2i_PKCS12_bio(in, &pkcs);
- DATA_PTR(self) = pkcs;
+ p12 = d2i_PKCS12_bio(in, NULL);
BIO_free(in);
+ if (!p12)
+ ossl_raise(ePKCS12Error, "d2i_PKCS12_bio");
+ PKCS12_free(p12_orig);
+ RTYPEDDATA_DATA(self) = p12;
pkey = cert = ca = Qnil;
- if(!PKCS12_parse(pkcs, passphrase, &key, &x509, &x509s))
+ if (!PKCS12_parse(p12, passphrase, &key, &x509, &x509s))
ossl_raise(ePKCS12Error, "PKCS12_parse");
if (key) {
pkey = rb_protect(ossl_pkey_wrap_i, (VALUE)key, &st);
diff --git a/ext/openssl/ossl_pkcs7.c b/ext/openssl/ossl_pkcs7.c
index 3cf5820c36..44e8cb305b 100644
--- a/ext/openssl/ossl_pkcs7.c
+++ b/ext/openssl/ossl_pkcs7.c
@@ -453,7 +453,7 @@ ossl_pkcs7_sym2typeid(VALUE sym)
if(i == numberof(p7_type_tab))
ossl_raise(ePKCS7Error, "unknown type \"%"PRIsVALUE"\"", sym);
if(strlen(p7_type_tab[i].name) != l) continue;
- if(strcmp(p7_type_tab[i].name, s) == 0){
+ if(memcmp(p7_type_tab[i].name, s, l) == 0){
ret = p7_type_tab[i].nid;
break;
}
diff --git a/ext/openssl/ossl_ts.c b/ext/openssl/ossl_ts.c
index 393e08acff..317f7786aa 100644
--- a/ext/openssl/ossl_ts.c
+++ b/ext/openssl/ossl_ts.c
@@ -168,7 +168,7 @@ ossl_ts_req_alloc(VALUE klass)
static VALUE
ossl_ts_req_initialize(int argc, VALUE *argv, VALUE self)
{
- TS_REQ *ts_req = DATA_PTR(self);
+ TS_REQ *req, *req_orig = DATA_PTR(self);
BIO *in;
VALUE arg;
@@ -178,13 +178,14 @@ ossl_ts_req_initialize(int argc, VALUE *argv, VALUE self)
arg = ossl_to_der_if_possible(arg);
in = ossl_obj2bio(&arg);
- ts_req = d2i_TS_REQ_bio(in, &ts_req);
+ req = d2i_TS_REQ_bio(in, NULL);
BIO_free(in);
- if (!ts_req) {
- DATA_PTR(self) = NULL;
- ossl_raise(eTimestampError, "Error when decoding the timestamp request");
+ if (!req) {
+ ossl_raise(eTimestampError,
+ "Error when decoding the timestamp request");
}
- DATA_PTR(self) = ts_req;
+ TS_REQ_free(req_orig);
+ RTYPEDDATA_DATA(self) = req;
return self;
}
@@ -522,18 +523,19 @@ ossl_ts_resp_alloc(VALUE klass)
static VALUE
ossl_ts_resp_initialize(VALUE self, VALUE der)
{
- TS_RESP *ts_resp = DATA_PTR(self);
+ TS_RESP *resp, *resp_orig = DATA_PTR(self);
BIO *in;
der = ossl_to_der_if_possible(der);
- in = ossl_obj2bio(&der);
- ts_resp = d2i_TS_RESP_bio(in, &ts_resp);
+ in = ossl_obj2bio(&der);
+ resp = d2i_TS_RESP_bio(in, NULL);
BIO_free(in);
- if (!ts_resp) {
- DATA_PTR(self) = NULL;
- ossl_raise(eTimestampError, "Error when decoding the timestamp response");
+ if (!resp) {
+ ossl_raise(eTimestampError,
+ "Error when decoding the timestamp response");
}
- DATA_PTR(self) = ts_resp;
+ TS_RESP_free(resp_orig);
+ RTYPEDDATA_DATA(self) = resp;
return self;
}
@@ -876,18 +878,19 @@ ossl_ts_token_info_alloc(VALUE klass)
static VALUE
ossl_ts_token_info_initialize(VALUE self, VALUE der)
{
- TS_TST_INFO *info = DATA_PTR(self);
+ TS_TST_INFO *info, *info_orig = DATA_PTR(self);
BIO *in;
der = ossl_to_der_if_possible(der);
- in = ossl_obj2bio(&der);
- info = d2i_TS_TST_INFO_bio(in, &info);
+ in = ossl_obj2bio(&der);
+ info = d2i_TS_TST_INFO_bio(in, NULL);
BIO_free(in);
if (!info) {
- DATA_PTR(self) = NULL;
- ossl_raise(eTimestampError, "Error when decoding the timestamp token info");
+ ossl_raise(eTimestampError,
+ "Error when decoding the timestamp token info");
}
- DATA_PTR(self) = info;
+ TS_TST_INFO_free(info_orig);
+ RTYPEDDATA_DATA(self) = info;
return self;
}
diff --git a/file.c b/file.c
index 17a38637c6..c4a531d783 100644
--- a/file.c
+++ b/file.c
@@ -4479,31 +4479,43 @@ rb_file_s_expand_path(int argc, const VALUE *argv)
}
/*
+ * :markup: markdown
+ *
* call-seq:
- * File.expand_path(file_name [, dir_string] ) -> abs_file_name
+ * File.expand_path(path, dirpath = '.') -> absolute_path
*
- * Converts a pathname to an absolute pathname. Relative paths are
- * referenced from the current working directory of the process unless
- * +dir_string+ is given, in which case it will be used as the
- * starting point. The given pathname may start with a
- * ``<code>~</code>'', which expands to the process owner's home
- * directory (the environment variable +HOME+ must be set
- * correctly). ``<code>~</code><i>user</i>'' expands to the named
- * user's home directory.
+ * Returns the string absolute path for the given `path`.
*
- * File.expand_path("~oracle/bin") #=> "/home/oracle/bin"
+ * Evaluates a relative path with respect to the directory given by `dirpath`:
*
- * A simple example of using +dir_string+ is as follows.
- * File.expand_path("ruby", "/usr/bin") #=> "/usr/bin/ruby"
+ * ```ruby
+ * Dir.chdir('/snap')
+ * # Default dirpath.
+ * File.expand_path('README') # => "/snap/README"
+ * File.expand_path('bin') # => "/snap/bin"
+ * File.expand_path('bin/../var') # => "/snap/var" # Cleaned.
+ * # Other dirpath.
+ * File.expand_path('../zip', '/usr/bin/ruby') # => "/usr/bin/zip"
+ * Dir.chdir('/usr/bin')
+ * File.expand_path('../../snap', __FILE__) # => "/usr/snap"
+ * ```
*
- * A more complex example which also resolves parent directory is as follows.
- * Suppose we are in bin/mygem and want the absolute path of lib/mygem.rb.
+ * Evaluates an absolute path without respect to `dirpath`:
*
- * File.expand_path("../../lib/mygem.rb", __FILE__)
- * #=> ".../path/to/project/lib/mygem.rb"
+ * ```ruby
+ * File.expand_path('/snap') # => "/snap"
+ * File.expand_path('/snap', 'nosuch') # => "/snap"
+ * File.expand_path('/snap/../snap') # => "/snap" # Cleaned.
+ * ```
+ *
+ * More examples:
+ *
+ * ```
+ * Dir.chdir('/usr/bin')
+ * File.expand_path('../../snap', __FILE__) # => "/usr/snap"
+ * File.expand_path('../../snap') # => "/snap"
+ * ```
*
- * So first it resolves the parent of __FILE__, that is bin/, then go to the
- * parent, the root of the project and appends +lib/mygem.rb+.
*/
static VALUE
@@ -5318,28 +5330,42 @@ ruby_enc_find_extname(const char *name, long *len, rb_encoding *enc)
}
/*
+ * :markup: markdown
+ *
* call-seq:
- * File.extname(path) -> string
+ * File.extname(path) -> extension
+ *
+ * Returns the filename extension --
+ * usually the portion of the string `path`
+ * beginning from the last period:
+ *
+ * ```ruby
+ * File.extname('t.rb') # => ".rb"
+ * File.extname('foo.bar.t.rb') # => ".rb"
+ * File.extname('foo/bar/t.rb') # => ".rb"
+ * File.extname('nosuch.txt') # => ".txt" # Path need not exist.
+ * ```
+ *
+ * Returns the entire string when there is no period:
*
- * Returns the extension (the portion of file name in +path+
- * starting from the last period).
+ * ```ruby
+ * Pathname('foo').extname # => ""
+ * ```
*
- * If +path+ is a dotfile, or starts with a period, then the starting
- * dot is not dealt with the start of the extension.
+ * Returns an empty string when the only period is the first character:
*
- * An empty string will also be returned when the period is the last character
- * in +path+.
+ * ```ruby
+ * File.extname('.irbrc') # => ""
+ * ```
*
- * On Windows, trailing dots are truncated.
+ * Returns an empty string or `'.'` when `path` ends with a period:
*
- * File.extname("test.rb") #=> ".rb"
- * File.extname("a/b/d/test.rb") #=> ".rb"
- * File.extname(".a/b/d/test.rb") #=> ".rb"
- * File.extname("foo.") #=> "" on Windows
- * File.extname("foo.") #=> "." on non-Windows
- * File.extname("test") #=> ""
- * File.extname(".profile") #=> ""
- * File.extname(".profile.sh") #=> ".sh"
+ * ```
+ * File.extname('foo.') # => "" # On Windows.
+ * File.extname('foo.') # => "." # Elsewhere.
+ * File.extname('foo....') # => "" # On Windows.
+ * File.extname('foo....') # => "." # Elsewhere.
+ * ```
*
*/
diff --git a/gc.c b/gc.c
index 30502b9640..9eb7232940 100644
--- a/gc.c
+++ b/gc.c
@@ -337,8 +337,10 @@ rb_gc_shutdown_call_finalizer_p(VALUE obj)
{
switch (BUILTIN_TYPE(obj)) {
case T_DATA:
- if (!ruby_free_at_exit_p() && !DATA_PTR(obj)) return false;
- if (!ruby_free_at_exit_p() && !RTYPEDDATA_P(obj) && !RDATA(obj)->dfree) return false;
+ if (!ruby_free_at_exit_p()) {
+ if (!RDATA(obj)->type) return false;
+ if (!rbimpl_typeddata_embedded_p(obj) && !RTYPEDDATA(obj)->data) return false;
+ }
if (rb_obj_is_thread(obj)) return false;
if (rb_obj_is_mutex(obj)) return false;
if (rb_obj_is_fiber(obj)) return false;
@@ -374,7 +376,6 @@ void rb_vm_update_references(void *ptr);
#define rb_setjmp(env) RUBY_SETJMP(env)
#define rb_jmp_buf rb_jmpbuf_t
-#undef rb_data_object_wrap
#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
#define MAP_ANONYMOUS MAP_ANON
@@ -1138,33 +1139,6 @@ rb_data_object_check(VALUE klass)
}
}
-VALUE
-rb_data_object_wrap(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree)
-{
- RUBY_ASSERT_ALWAYS(dfree != (RUBY_DATA_FUNC)1);
- if (klass) rb_data_object_check(klass);
- VALUE obj = rb_newobj(GET_EC(), klass, T_DATA, ROOT_SHAPE_ID, !dmark, sizeof(struct RData));
-
- rb_gc_register_pinning_obj(obj);
-
- struct RData *data = (struct RData *)obj;
- data->fields_obj = 0;
- data->_reserved = 0;
- data->data = datap;
- data->dmark = dmark;
- data->dfree = dfree;
-
- return obj;
-}
-
-VALUE
-rb_data_object_zalloc(VALUE klass, size_t size, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree)
-{
- VALUE obj = rb_data_object_wrap(klass, 0, dmark, dfree);
- DATA_PTR(obj) = xcalloc(1, size);
- return obj;
-}
-
#define RTYPEDDATA_EMBEDDED_P rbimpl_typeddata_embedded_p
#define RB_DATA_TYPE_EMBEDDABLE_P(type) ((type)->flags & RUBY_TYPED_EMBEDDABLE)
#define RTYPEDDATA_EMBEDDABLE_P(obj) RB_DATA_TYPE_EMBEDDABLE_P(RTYPEDDATA_TYPE(obj))
@@ -1175,7 +1149,7 @@ typed_data_alloc(VALUE klass, VALUE typed_flag, void *datap, const rb_data_type_
RBIMPL_NONNULL_ARG(type);
if (klass) rb_data_object_check(klass);
bool wb_protected = (type->flags & RUBY_FL_WB_PROTECTED) || !type->function.dmark;
- VALUE obj = rb_newobj(GET_EC(), klass, T_DATA | RUBY_TYPED_FL_IS_TYPED_DATA, ROOT_SHAPE_ID, wb_protected, size);
+ VALUE obj = rb_newobj(GET_EC(), klass, T_DATA, ROOT_SHAPE_ID, wb_protected, size);
rb_gc_register_pinning_obj(obj);
@@ -1237,18 +1211,16 @@ static size_t
rb_objspace_data_type_memsize(VALUE obj)
{
size_t size = 0;
- if (RTYPEDDATA_P(obj)) {
- const void *ptr = RTYPEDDATA_GET_DATA(obj);
+ const void *ptr = RTYPEDDATA_GET_DATA(obj);
- if (ptr) {
- if (RTYPEDDATA_EMBEDDABLE_P(obj) && !RTYPEDDATA_EMBEDDED_P(obj)) {
- size += ruby_xmalloc_usable_size((void *)ptr);
- }
+ if (ptr) {
+ if (RTYPEDDATA_EMBEDDABLE_P(obj) && !RTYPEDDATA_EMBEDDED_P(obj)) {
+ size += ruby_xmalloc_usable_size((void *)ptr);
+ }
- const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
- if (type->function.dsize) {
- size += type->function.dsize(ptr);
- }
+ const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
+ if (type->function.dsize) {
+ size += type->function.dsize(ptr);
}
}
@@ -1258,12 +1230,7 @@ rb_objspace_data_type_memsize(VALUE obj)
const char *
rb_objspace_data_type_name(VALUE obj)
{
- if (RTYPEDDATA_P(obj)) {
- return RTYPEDDATA_TYPE(obj)->wrap_struct_name;
- }
- else {
- return 0;
- }
+ return RTYPEDDATA_TYPE(obj)->wrap_struct_name;
}
void
@@ -1285,7 +1252,7 @@ rb_gc_handle_weak_references(VALUE obj)
{
switch (BUILTIN_TYPE(obj)) {
case T_DATA:
- if (RTYPEDDATA_P(obj)) {
+ {
const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
if (type->function.handle_weak_references) {
@@ -1298,9 +1265,6 @@ rb_gc_handle_weak_references(VALUE obj)
);
}
}
- else {
- rb_bug("rb_gc_handle_weak_references: unknown T_DATA");
- }
break;
case T_IMEMO: {
@@ -1423,7 +1387,7 @@ rb_gc_obj_needs_cleanup_p(VALUE obj)
return false;
case T_DATA:
- if (flags & RUBY_TYPED_FL_IS_TYPED_DATA) {
+ {
uintptr_t type = (uintptr_t)RTYPEDDATA(obj)->type;
if (type & TYPED_DATA_EMBEDDED) {
RUBY_DATA_FUNC dfree = ((const rb_data_type_t *)(type & TYPED_DATA_PTR_MASK))->function.dfree;
@@ -1485,22 +1449,17 @@ make_io_zombie(void *objspace, VALUE obj)
static bool
rb_data_free(void *objspace, VALUE obj)
{
- void *data = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj);
+ void *data = RTYPEDDATA_GET_DATA(obj);
if (data) {
int free_immediately = false;
void (*dfree)(void *);
- if (RTYPEDDATA_P(obj)) {
- free_immediately = (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_FREE_IMMEDIATELY) != 0;
- dfree = RTYPEDDATA_TYPE(obj)->function.dfree;
- }
- else {
- dfree = RDATA(obj)->dfree;
- }
+ free_immediately = (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_FREE_IMMEDIATELY) != 0;
+ dfree = RTYPEDDATA_TYPE(obj)->function.dfree;
if (dfree) {
if (dfree == RUBY_DEFAULT_FREE) {
- if (!RTYPEDDATA_P(obj) || !RTYPEDDATA_EMBEDDED_P(obj)) {
+ if (!RTYPEDDATA_EMBEDDED_P(obj)) {
xfree(data);
RB_DEBUG_COUNTER_INC(obj_data_xfree);
}
@@ -3512,13 +3471,12 @@ rb_gc_mark_children(void *objspace, VALUE obj)
break;
case T_DATA: {
- bool typed_data = RTYPEDDATA_P(obj);
- void *const ptr = typed_data ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj);
+ void *const ptr = RTYPEDDATA_GET_DATA(obj);
gc_mark_internal(RTYPEDDATA(obj)->fields_obj);
if (ptr) {
- if (typed_data && gc_declarative_marking_p(RTYPEDDATA_TYPE(obj))) {
+ if (gc_declarative_marking_p(RTYPEDDATA_TYPE(obj))) {
size_t *offset_list = TYPED_DATA_REFS_OFFSET_LIST(obj);
for (size_t offset = *offset_list; offset != RUBY_REF_END; offset = *offset_list++) {
@@ -3526,9 +3484,7 @@ rb_gc_mark_children(void *objspace, VALUE obj)
}
}
else {
- RUBY_DATA_FUNC mark_func = typed_data ?
- RTYPEDDATA_TYPE(obj)->function.dmark :
- RDATA(obj)->dmark;
+ RUBY_DATA_FUNC mark_func = RTYPEDDATA_TYPE(obj)->function.dmark;
if (mark_func) (*mark_func)(ptr);
}
}
@@ -4460,13 +4416,12 @@ rb_gc_update_object_references(void *objspace, VALUE obj)
case T_DATA:
/* Call the compaction callback, if it exists */
{
- bool typed_data = RTYPEDDATA_P(obj);
- void *const ptr = typed_data ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj);
+ void *const ptr = RTYPEDDATA_GET_DATA(obj);
UPDATE_IF_MOVED(objspace, RTYPEDDATA(obj)->fields_obj);
if (ptr) {
- if (typed_data && gc_declarative_marking_p(RTYPEDDATA_TYPE(obj))) {
+ if (gc_declarative_marking_p(RTYPEDDATA_TYPE(obj))) {
size_t *offset_list = TYPED_DATA_REFS_OFFSET_LIST(obj);
for (size_t offset = *offset_list; offset != RUBY_REF_END; offset = *offset_list++) {
@@ -4474,7 +4429,7 @@ rb_gc_update_object_references(void *objspace, VALUE obj)
*ref = gc_location_internal(objspace, *ref);
}
}
- else if (typed_data) {
+ else {
RUBY_DATA_FUNC compact_func = RTYPEDDATA_TYPE(obj)->function.dcompact;
if (compact_func) (*compact_func)(ptr);
}
diff --git a/hash.c b/hash.c
index bac1b4abb5..9ba8c3d4fe 100644
--- a/hash.c
+++ b/hash.c
@@ -528,7 +528,7 @@ hash_st_table_init(VALUE hash, const struct st_hash_type *type, st_index_t size)
RHASH_SET_ST_FLAG(hash);
}
-void
+static void
rb_hash_st_table_set(VALUE hash, st_table *st)
{
HASH_ASSERT(st != NULL);
@@ -730,7 +730,7 @@ ar_force_convert_table(VALUE hash, const char *file, int line)
st_init_existing_table_with_size(new_tab, &objhash, size);
ar_each_key(ar, bound, ar_each_key_insert, NULL, new_tab, hashes);
hash_ar_free_and_clear_table(hash);
- RHASH_ST_TABLE_SET(hash, new_tab);
+ rb_hash_st_table_set(hash, new_tab);
return RHASH_ST_TABLE(hash);
}
}
@@ -1986,7 +1986,7 @@ rb_hash_rehash(VALUE hash)
rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tmp);
hash_st_free(hash);
- RHASH_ST_TABLE_SET(hash, tbl);
+ rb_hash_st_table_set(hash, tbl);
RHASH_ST_CLEAR(tmp);
}
hash_verify(hash);
@@ -4669,7 +4669,7 @@ rb_hash_compare_by_id(VALUE hash)
// We know for sure `identtable` is an st table,
// so we can skip `ar_force_convert_table` here.
- RHASH_ST_TABLE_SET(hash, identtable);
+ rb_hash_st_table_set(hash, identtable);
RHASH_ST_CLEAR(tmp);
}
diff --git a/include/ruby/internal/abi.h b/include/ruby/internal/abi.h
index 0c99d93bf9..7ceb8c40b7 100644
--- a/include/ruby/internal/abi.h
+++ b/include/ruby/internal/abi.h
@@ -24,7 +24,7 @@
* In released versions of Ruby, this number is not defined since teeny
* versions of Ruby should guarantee ABI compatibility.
*/
-#define RUBY_ABI_VERSION 2
+#define RUBY_ABI_VERSION 3
/* Windows does not support weak symbols so ruby_abi_version will not exist
* in the shared library. */
diff --git a/include/ruby/internal/core/rdata.h b/include/ruby/internal/core/rdata.h
index 5ebeb2473e..d0a4dc7c83 100644
--- a/include/ruby/internal/core/rdata.h
+++ b/include/ruby/internal/core/rdata.h
@@ -42,31 +42,9 @@
#endif
#define RBIMPL_DATA_FUNC(f) RBIMPL_CAST((void (*)(void *))(f))
-#define RBIMPL_ATTRSET_UNTYPED_DATA_FUNC() \
- RBIMPL_ATTR_WARNING(("untyped Data is unsafe; use TypedData instead")) \
- RBIMPL_ATTR_DEPRECATED(("by TypedData"))
-
-#define RBIMPL_MACRO_SELECT(x, y) x ## y
-#define RUBY_MACRO_SELECT(x, y) RBIMPL_MACRO_SELECT(x, y)
/** @endcond */
/**
- * Convenient casting macro.
- *
- * @param obj An object, which is in fact an ::RData.
- * @return The passed object casted to ::RData.
- */
-#define RDATA(obj) RBIMPL_CAST((struct RData *)(obj))
-
-/**
- * Convenient getter macro.
- *
- * @param obj An object, which is in fact an ::RData.
- * @return The passed object's ::RData::data field.
- */
-#define DATA_PTR(obj) RDATA(obj)->data
-
-/**
* This is a value you can set to ::RData::dfree. Setting this means the data
* was allocated using ::ruby_xmalloc() (or variants), and shall be freed using
* ::ruby_xfree().
@@ -85,19 +63,6 @@
#define RUBY_NEVER_FREE RBIMPL_DATA_FUNC(0)
/**
- * @private
- *
- * @deprecated This macro once was a thing in the old days, but makes no sense
- * any longer today. Exists here for backwards compatibility
- * only. You can safely forget about it.
- */
-#define RUBY_UNTYPED_DATA_FUNC(f) f RBIMPL_ATTRSET_UNTYPED_DATA_FUNC()
-
-/*
-#define RUBY_DATA_FUNC(func) ((void (*)(void*))(func))
-*/
-
-/**
* This is the type of callbacks registered to ::RData. The argument is the
* `data` field.
*/
@@ -106,264 +71,16 @@ typedef void (*RUBY_DATA_FUNC)(void*);
/**
* @deprecated
*
- * Old "untyped" user data. It has roughly the same usage as struct
- * ::RTypedData, but lacked several features such as support for compaction GC.
- * Use of this struct is not recommended any longer. If it is dead necessary,
- * please inform the core devs about your usage.
- *
- * @internal
- *
- * @shyouhei tried to add RBIMPL_ATTR_DEPRECATED for this type but that yielded
- * too many warnings in the core. Maybe we want to retry later... Just add
- * deprecated document for now.
- *
- * RData shares its initial fields with struct ::RTypedData so the VM can handle
- * per-object fields without checking whether a T_DATA object is typed or legacy.
+ * DO NOT USE: Obsolete "untyped" user data, that is remained only for
+ * the backward compatibility for some wrapper generator gems.
*/
struct RData {
-
- /** Basic part, including flags and class. */
struct RBasic basic;
-
- /** @internal Direct reference to the slots that hold instance variables, if any. */
VALUE fields_obj;
-
- /** @internal Padding where ::RTypedData stores its type, so both structs place data at the same offset. */
VALUE _reserved;
-
- /** Pointer to the actual C level struct that you want to wrap. */
void *data;
-
- /**
- * This function is called when the object is experiencing GC marks. If it
- * contains references to other Ruby objects, you need to mark them also.
- * Otherwise GC will smash your data.
- *
- * @see rb_gc_mark()
- * @warning This is called during GC runs. Object allocations are
- * impossible at that moment (that is why GC runs).
- */
RUBY_DATA_FUNC dmark;
-
- /**
- * This function is called when the object is no longer used. You need to
- * do whatever necessary to avoid memory leaks.
- *
- * @warning This is called during GC runs. Object allocations are
- * impossible at that moment (that is why GC runs).
- */
RUBY_DATA_FUNC dfree;
};
-RBIMPL_SYMBOL_EXPORT_BEGIN()
-
-/**
- * This is the primitive way to wrap an existing C struct into ::RData.
- *
- * @param[in] klass Ruby level class of the returning object.
- * @param[in] datap Pointer to the target C struct.
- * @param[in] dmark Mark function.
- * @param[in] dfree Free function.
- * @exception rb_eTypeError `klass` is not a class.
- * @exception rb_eNoMemError Out of memory.
- * @return An allocated object that wraps `datap`.
- */
-VALUE rb_data_object_wrap(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree);
-
-/**
- * Identical to rb_data_object_wrap(), except it allocates a new data region
- * internally instead of taking an existing one. The allocation is done using
- * ruby_calloc(). Hence it makes no sense to pass anything other than
- * ::RUBY_DEFAULT_FREE to the last argument.
- *
- * @param[in] klass Ruby level class of the returning object.
- * @param[in] size Requested size of memory to allocate.
- * @param[in] dmark Mark function.
- * @param[in] dfree Free function.
- * @exception rb_eTypeError `klass` is not a class.
- * @exception rb_eNoMemError Out of memory.
- * @return An allocated object that wraps a new `size` byte region.
- */
-VALUE rb_data_object_zalloc(VALUE klass, size_t size, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree);
-
-RBIMPL_SYMBOL_EXPORT_END()
-
-/**
- * Converts sval, a pointer to your struct, into a Ruby object.
- *
- * @param klass A ruby level class.
- * @param mark Mark function.
- * @param free Free function.
- * @param sval A pointer to your struct.
- * @exception rb_eTypeError `klass` is not a class.
- * @exception rb_eNoMemError Out of memory.
- * @return A created Ruby object.
- */
-#define Data_Wrap_Struct(klass, mark, free, sval) \
- rb_data_object_wrap( \
- (klass), \
- (sval), \
- RBIMPL_DATA_FUNC(mark), \
- RBIMPL_DATA_FUNC(free))
-
-/**
- * @private
- *
- * This is an implementation detail of #Data_Make_Struct. People don't use it
- * directly.
- *
- * @param result Variable name of created Ruby object.
- * @param klass Ruby level class of the object.
- * @param type Type name of the C struct.
- * @param size Size of the C struct.
- * @param mark Mark function.
- * @param free Free function.
- * @param sval Variable name of created C struct.
- */
-#define Data_Make_Struct0(result, klass, type, size, mark, free, sval) \
- VALUE result = rb_data_object_zalloc( \
- (klass), \
- (size), \
- RBIMPL_DATA_FUNC(mark), \
- RBIMPL_DATA_FUNC(free)); \
- (sval) = RBIMPL_CAST((type *)DATA_PTR(result)); \
- RBIMPL_CAST(/*suppress unused variable warnings*/(void)(sval))
-
-/**
- * Identical to #Data_Wrap_Struct, except it allocates a new data region
- * internally instead of taking an existing one. The allocation is done using
- * ruby_calloc(). Hence it makes no sense to pass anything other than
- * ::RUBY_DEFAULT_FREE to the `free` argument.
- *
- * @param klass Ruby level class of the returning object.
- * @param type Type name of the C struct.
- * @param mark Mark function.
- * @param free Free function.
- * @param sval Variable name of created C struct.
- * @exception rb_eTypeError `klass` is not a class.
- * @exception rb_eNoMemError Out of memory.
- * @return A created Ruby object.
- */
-#ifdef HAVE_STMT_AND_DECL_IN_EXPR
-#define Data_Make_Struct(klass, type, mark, free, sval) \
- RB_GNUC_EXTENSION({ \
- Data_Make_Struct0( \
- data_struct_obj, \
- klass, \
- type, \
- sizeof(type), \
- mark, \
- free, \
- sval); \
- data_struct_obj; \
- })
-#else
-#define Data_Make_Struct(klass, type, mark, free, sval) \
- rb_data_object_make( \
- (klass), \
- RBIMPL_DATA_FUNC(mark), \
- RBIMPL_DATA_FUNC(free), \
- RBIMPL_CAST((void **)&(sval)), \
- sizeof(type))
-#endif
-
-/**
- * Obtains a C struct from inside of a wrapper Ruby object.
- *
- * @param obj An instance of ::RData.
- * @param type Type name of the C struct.
- * @param sval Variable name of obtained C struct.
- * @return Unwrapped C struct that `obj` holds.
- */
-#define Data_Get_Struct(obj, type, sval) \
- ((sval) = RBIMPL_CAST((type*)rb_data_object_get(obj)))
-
-RBIMPL_ATTRSET_UNTYPED_DATA_FUNC()
-/**
- * @private
- *
- * This is an implementation detail of rb_data_object_wrap(). People don't use
- * it directly.
- *
- * @param[in] klass Ruby level class of the returning object.
- * @param[in] ptr Pointer to the target C struct.
- * @param[in] mark Mark function.
- * @param[in] free Free function.
- * @exception rb_eTypeError `klass` is not a class.
- * @exception rb_eNoMemError Out of memory.
- * @return An allocated object that wraps `datap`.
- */
-static inline VALUE
-rb_data_object_wrap_warning(VALUE klass, void *ptr, RUBY_DATA_FUNC mark, RUBY_DATA_FUNC free)
-{
- return rb_data_object_wrap(klass, ptr, mark, free);
-}
-
-/**
- * @private
- *
- * This is an implementation detail of #Data_Get_Struct. People don't use it
- * directly.
- *
- * @param[in] obj An instance of ::RData.
- * @return Unwrapped C struct that `obj` holds.
- */
-static inline void *
-rb_data_object_get(VALUE obj)
-{
- Check_Type(obj, RUBY_T_DATA);
- return DATA_PTR(obj);
-}
-
-RBIMPL_ATTRSET_UNTYPED_DATA_FUNC()
-/**
- * @private
- *
- * This is an implementation detail of #Data_Get_Struct. People don't use it
- * directly.
- *
- * @param[in] obj An instance of ::RData.
- * @return Unwrapped C struct that `obj` holds.
- */
-static inline void *
-rb_data_object_get_warning(VALUE obj)
-{
- return rb_data_object_get(obj);
-}
-
-/**
- * This is an implementation detail of #Data_Make_Struct. People don't use it
- * directly.
- *
- * @param[in] klass Ruby level class of the returning object.
- * @param[in] mark_func Mark function.
- * @param[in] free_func Free function.
- * @param[in] datap Variable of created C struct.
- * @param[in] size Requested size of allocation.
- * @exception rb_eTypeError `klass` is not a class.
- * @exception rb_eNoMemError Out of memory.
- * @return A created Ruby object.
- * @post `*datap` holds the created C struct.
- */
-static inline VALUE
-rb_data_object_make(VALUE klass, RUBY_DATA_FUNC mark_func, RUBY_DATA_FUNC free_func, void **datap, size_t size)
-{
- Data_Make_Struct0(result, klass, void, size, mark_func, free_func, *datap);
- return result;
-}
-
-/** @cond INTERNAL_MACRO */
-#define rb_data_object_wrap_0 rb_data_object_wrap
-#define rb_data_object_wrap_1 rb_data_object_wrap_warning
-#define rb_data_object_wrap_2 rb_data_object_wrap_ /* Used here vvvv */
-#define rb_data_object_wrap RUBY_MACRO_SELECT(rb_data_object_wrap_2, RUBY_UNTYPED_DATA_WARNING)
-#define rb_data_object_get_0 rb_data_object_get
-#define rb_data_object_get_1 rb_data_object_get_warning
-#define rb_data_object_get_2 rb_data_object_get_ /* Used here vvvv */
-#define rb_data_object_get RUBY_MACRO_SELECT(rb_data_object_get_2, RUBY_UNTYPED_DATA_WARNING)
-#define rb_data_object_make_0 rb_data_object_make
-#define rb_data_object_make_1 rb_data_object_make_warning
-#define rb_data_object_make_2 rb_data_object_make_ /* Used here vvvv */
-#define rb_data_object_make RUBY_MACRO_SELECT(rb_data_object_make_2, RUBY_UNTYPED_DATA_WARNING)
-/** @endcond */
#endif /* RBIMPL_RDATA_H */
diff --git a/include/ruby/internal/core/rtypeddata.h b/include/ruby/internal/core/rtypeddata.h
index 0b189c7ef8..eb8313f180 100644
--- a/include/ruby/internal/core/rtypeddata.h
+++ b/include/ruby/internal/core/rtypeddata.h
@@ -206,11 +206,6 @@ rbimpl_typeddata_flags {
RUBY_TYPED_WB_PROTECTED = RUBY_FL_WB_PROTECTED, /* THIS FLAG DEPENDS ON Ruby version */
/**
- * This flag is used to distinguish RTypedData from deprecated RData objects.
- */
- RUBY_TYPED_FL_IS_TYPED_DATA = RUBY_FL_USERPRIV0,
-
- /**
* This flag determines whether marking and compaction should be carried out
* using the dmark/dcompact callback functions or whether we should mark
* declaratively using a list of references defined inside the data struct we're wrapping
@@ -409,6 +404,22 @@ RBIMPL_STATIC_ASSERT(fields_obj_in_rdata, offsetof(struct RData, fields_obj) ==
RBIMPL_STATIC_ASSERT(data_in_rtypeddata, offsetof(struct RData, data) == offsetof(struct RTypedData, data));
#endif
+/**
+ * Convenient casting macro for backward compatibility.
+ *
+ * @param obj An object, which is in fact an ::RData.
+ * @return The passed object casted to ::RData.
+ */
+#define RDATA(obj) RTYPEDDATA(obj)
+
+/**
+ * Convenient casting macro for backward compatibility.
+ *
+ * @param obj An object, which is in fact an ::RData.
+ * @return The passed object's ::RTypedData::data field.
+ */
+#define DATA_PTR(obj) RTYPEDDATA_DATA(obj)
+
RBIMPL_SYMBOL_EXPORT_BEGIN()
RBIMPL_ATTR_NONNULL((3))
/**
@@ -621,27 +632,7 @@ RBIMPL_ATTR_ARTIFICIAL()
/**
* @private
*
- * This is an implementation detail of Check_Type(). People don't use it
- * directly.
- *
- * @param[in] obj Object in question
- * @retval true `obj` is an instance of ::RTypedData.
- * @retval false `obj` is an instance of ::RData.
- * @pre `obj` must be a Ruby object of ::RUBY_T_DATA.
- */
-static inline bool
-rbimpl_rtypeddata_p(VALUE obj)
-{
- return FL_TEST_RAW(obj, RUBY_TYPED_FL_IS_TYPED_DATA);
-}
-
-RBIMPL_ATTR_PURE()
-RBIMPL_ATTR_ARTIFICIAL()
-/**
- * @private
- *
- * Identical to rbimpl_rtypeddata_p(), except it is allowed to call on non-data
- * objects.
+ * Checks whether the passed object is ::RTypedData.
*
* This is an implementation detail of inline functions defined in this file.
* People don't use it directly.
@@ -653,17 +644,16 @@ RBIMPL_ATTR_ARTIFICIAL()
static inline bool
rbimpl_obj_typeddata_p(VALUE obj)
{
- return RB_TYPE_P(obj, RUBY_T_DATA) && rbimpl_rtypeddata_p(obj);
+ return RB_TYPE_P(obj, RUBY_T_DATA);
}
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
RBIMPL_ATTR_ARTIFICIAL()
/**
- * Checks whether the passed object is ::RTypedData or ::RData.
+ * Checks whether the passed object is ::RTypedData.
*
* @param[in] obj Object in question
- * @retval true `obj` is an instance of ::RTypedData.
- * @retval false `obj` is an instance of ::RData.
+ * @retval true
* @pre `obj` must be a Ruby object of ::RUBY_T_DATA.
*/
static inline bool
@@ -671,7 +661,7 @@ RTYPEDDATA_P(VALUE obj)
{
RBIMPL_TYPEDDATA_PRECONDITION(obj, RBIMPL_UNREACHABLE_RETURN(false));
- return rbimpl_rtypeddata_p(obj);
+ return true;
}
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
@@ -752,7 +742,6 @@ static inline VALUE
rbimpl_check_external_typeddata(VALUE obj)
{
RBIMPL_TYPEDDATA_PRECONDITION(obj, RBIMPL_UNREACHABLE_RETURN(false));
- RUBY_ASSERT(rbimpl_obj_typeddata_p(obj));
RUBY_ASSERT(!rbimpl_typeddata_embedded_p(obj));
return obj;
}
diff --git a/include/ruby/internal/fl_type.h b/include/ruby/internal/fl_type.h
index e3878d9ed7..f7ec742422 100644
--- a/include/ruby/internal/fl_type.h
+++ b/include/ruby/internal/fl_type.h
@@ -205,11 +205,11 @@ ruby_fl_type {
RUBY_FL_PROMOTED = (1<<5),
/**
- * This flag meaning is type dependent, currently only used by T_DATA.
+ * This flag is no longer in use
*
* @internal
*/
- RUBY_FL_USERPRIV0 = (1<<6),
+ RUBY_FL_UNUSED6 = (1<<6),
/**
* This flag has something to do with finalisers. A ruby object can have
diff --git a/include/ruby/internal/value_type.h b/include/ruby/internal/value_type.h
index b47d8afb97..77cb38bc7d 100644
--- a/include/ruby/internal/value_type.h
+++ b/include/ruby/internal/value_type.h
@@ -410,14 +410,6 @@ RB_TYPE_P(VALUE obj, enum ruby_value_type t)
#endif
/** @endcond */
-RBIMPL_ATTR_PURE()
-RBIMPL_ATTR_ARTIFICIAL()
-/**
- * @private
- * Defined in ruby/internal/core/rtypeddata.h
- */
-static inline bool rbimpl_rtypeddata_p(VALUE obj);
-
RBIMPL_ATTR_ARTIFICIAL()
/**
* Identical to RB_TYPE_P(), except it raises exceptions on predication
@@ -432,19 +424,11 @@ RBIMPL_ATTR_ARTIFICIAL()
static inline void
Check_Type(VALUE v, enum ruby_value_type t)
{
+ /* Typed data is not simple `T_DATA`, see `rb_check_type` */
+ RUBY_ASSERT_ALWAYS(t != RUBY_T_DATA);
if (RB_UNLIKELY(! RB_TYPE_P(v, t))) {
- goto unexpected_type;
+ rb_unexpected_type(v, RBIMPL_CAST((int)t));
}
- else if (t == RUBY_T_DATA && rbimpl_rtypeddata_p(v)) {
- /* Typed data is not simple `T_DATA`, see `rb_check_type` */
- goto unexpected_type;
- }
- else {
- return;
- }
-
- unexpected_type:
- rb_unexpected_type(v, RBIMPL_CAST((int)t));
}
#endif /* RBIMPL_VALUE_TYPE_H */
diff --git a/include/ruby/random.h b/include/ruby/random.h
index 740be6bdad..3b63a23334 100644
--- a/include/ruby/random.h
+++ b/include/ruby/random.h
@@ -333,7 +333,6 @@ static inline const rb_random_interface_t *
rb_rand_if(VALUE obj)
{
RBIMPL_ASSERT_OR_ASSUME(RB_TYPE_P(obj, T_DATA));
- RBIMPL_ASSERT_OR_ASSUME(RTYPEDDATA_P(obj));
RUBY_ASSERT(rb_typeddata_is_kind_of(obj, &rb_random_data_type));
const struct rb_data_type_struct *t = RTYPEDDATA_TYPE(obj);
const void *ret = t->data;
diff --git a/insns.def b/insns.def
index 26a44299db..3ad378081a 100644
--- a/insns.def
+++ b/insns.def
@@ -1099,10 +1099,25 @@ invokesuper
// attr rb_snum_t comptime_sp_inc = sp_inc_of_sendish(ci);
// attr bool zjit_profile = true;
{
- VALUE bh = vm_caller_setup_arg_block(ec, GET_CFP(), cd->ci, blockiseq, true);
- val = vm_sendish(ec, GET_CFP(), cd, bh, mexp_search_super);
+ struct rb_callinfo adjusted_ci = VM_CI_ON_STACK(vm_ci_mid(cd->ci),
+ vm_ci_flag(cd->ci),
+ vm_ci_argc(cd->ci),
+ vm_ci_kwarg(cd->ci));
+ const struct rb_callcache *original_cc = rbimpl_atomic_ptr_load((void **)&cd->cc, RBIMPL_ATOMIC_ACQUIRE);
+ struct rb_call_data adjusted_cd = {
+ .ci = &adjusted_ci,
+ .cc = original_cc,
+ };
+
+ VALUE bh = vm_caller_setup_arg_block(ec, GET_CFP(), adjusted_cd.ci, blockiseq, true);
+ val = vm_sendish(ec, GET_CFP(), &adjusted_cd, bh, mexp_search_super);
JIT_EXEC(ec, val);
+ if (original_cc != adjusted_cd.cc && vm_cc_markable(adjusted_cd.cc)) {
+ rbimpl_atomic_ptr_store((volatile void **)&cd->cc, (void *)adjusted_cd.cc, RBIMPL_ATOMIC_RELEASE);
+ RB_OBJ_WRITTEN(GET_ISEQ(), Qundef, adjusted_cd.cc);
+ }
+
if (UNDEF_P(val)) {
RESTORE_REGS();
NEXT_INSN();
diff --git a/internal/hash.h b/internal/hash.h
index baf5af9abd..0386a5009c 100644
--- a/internal/hash.h
+++ b/internal/hash.h
@@ -70,7 +70,6 @@ struct RHash {
#endif
/* hash.c */
-void rb_hash_st_table_set(VALUE hash, st_table *st);
VALUE rb_hash_default_value(VALUE hash, VALUE key);
VALUE rb_hash_set_default(VALUE hash, VALUE ifnone);
VALUE rb_hash_set_default_proc(VALUE hash, VALUE proc);
@@ -197,7 +196,6 @@ RHASH_AR_TABLE_SIZE_RAW(VALUE h)
((unsigned int)((RBASIC(h)->flags >> RHASH_AR_TABLE_BOUND_SHIFT) & \
(RHASH_AR_TABLE_BOUND_MASK >> RHASH_AR_TABLE_BOUND_SHIFT)))
-#define RHASH_ST_TABLE_SET(h, s) rb_hash_st_table_set(h, s)
#define RHASH_TYPE(hash) (RHASH_AR_TABLE_P(hash) ? &objhash : RHASH_ST_TABLE(hash)->type)
static inline unsigned int
diff --git a/lib/rubygems/yaml_serializer.rb b/lib/rubygems/yaml_serializer.rb
index a1d5c94fc5..b2547b136b 100644
--- a/lib/rubygems/yaml_serializer.rb
+++ b/lib/rubygems/yaml_serializer.rb
@@ -72,9 +72,7 @@ module Gem
def parse_node(base_indent)
@depth += 1
- if @depth > MAX_NESTING_DEPTH
- raise Psych::SyntaxError, "exceeded maximum nesting depth (#{MAX_NESTING_DEPTH})"
- end
+ raise_max_nesting! if @depth > MAX_NESTING_DEPTH
skip_blank_and_comments
return nil if @lines.empty?
@@ -285,7 +283,9 @@ module Gem
Scalar.new(value: result)
end
- def coerce(val)
+ def coerce(val, depth = 0)
+ raise_max_nesting! if depth > MAX_NESTING_DEPTH
+
val = val.sub(/^! /, "") if val.start_with?("! ")
if val =~ /^"(.*)"$/
@@ -311,7 +311,7 @@ module Gem
elsif val =~ /^\[(.*)\]$/
inner = $1.strip
return Sequence.new if inner.empty?
- items = inner.split(/\s*,\s*/).reject(&:empty?).map {|e| Scalar.new(value: coerce(e)) }
+ items = inner.split(/\s*,\s*/).reject(&:empty?).map {|e| Scalar.new(value: coerce(e, depth + 1)) }
Sequence.new(items: items)
elsif /\A\d{4}-\d{2}-\d{2}([ T]\d{2}:\d{2}:\d{2})?/.match?(val)
begin
@@ -392,6 +392,15 @@ module Gem
node
end
+ def raise_max_nesting!
+ message = "exceeded maximum nesting depth (#{MAX_NESTING_DEPTH})"
+ if defined?(Psych::VERSION)
+ raise Psych::SyntaxError.new(nil, 0, 0, 0, message, nil)
+ else
+ raise Psych::SyntaxError, message
+ end
+ end
+
def skip_blank_and_comments
while @lines.any?
line = @lines[0]
@@ -454,9 +463,7 @@ module Gem
result = build_node(node)
- if result.is_a?(Hash) &&
- (result[:tag] == "!ruby/object:Gem::Specification" ||
- result["tag"] == "!ruby/object:Gem::Specification")
+ if result.is_a?(Hash) && result[:tag] == "!ruby/object:Gem::Specification"
build_specification(result)
else
result
@@ -482,7 +489,11 @@ module Gem
if @alias_count > MAX_ALIAS_RESOLUTIONS
raise Psych::BadAlias, "exceeded maximum alias resolutions (#{MAX_ALIAS_RESOLUTIONS})"
end
- @anchor_values.fetch(node.name, nil)
+ unless @anchor_values.key?(node.name)
+ klass = defined?(Psych::AnchorNotDefined) ? Psych::AnchorNotDefined : Psych::BadAlias
+ raise klass, "An alias referenced an unknown anchor: #{node.name}"
+ end
+ @anchor_values.fetch(node.name)
end
def store_anchor(name, value)
@@ -491,7 +502,6 @@ module Gem
end
def build_mapping(node)
- check_anchor!(node)
validate_tag!(node.tag) if node.tag
result = case node.tag
@@ -592,9 +602,14 @@ module Gem
d.instance_variable_set(:@requirement, hash["requirement"] || hash["version_requirements"])
- type = hash["type"]
- type = type ? type.to_s.sub(/^:/, "").to_sym : :runtime
- validate_symbol!(type)
+ raw_type = hash["type"]
+ if raw_type
+ name = raw_type.to_s.sub(/^:/, "")
+ validate_symbol!(name)
+ type = name.to_sym
+ else
+ type = :runtime
+ end
d.instance_variable_set(:@type, type)
d.instance_variable_set(:@prerelease, ["true", true].include?(hash["prerelease"]))
@@ -634,19 +649,14 @@ module Gem
end
end
- def validate_symbol!(sym)
- if @permitted_symbols.any? && !@permitted_symbols.include?(sym.to_s)
- if defined?(Psych::VERSION)
- raise Psych::DisallowedClass.new("load", sym.inspect)
- else
- raise Psych::DisallowedClass, "Tried to load unspecified class: #{sym.inspect}"
- end
- end
- end
+ def validate_symbol!(name)
+ return if @permitted_symbols.empty? || @permitted_symbols.include?(name)
- def check_anchor!(node)
- if node.anchor
- raise Psych::AliasesNotEnabled unless @aliases
+ label = ":#{name}"
+ if defined?(Psych::VERSION)
+ raise Psych::DisallowedClass.new("load", label)
+ else
+ raise Psych::DisallowedClass, "Tried to load unspecified class: #{label}"
end
end
diff --git a/marshal.c b/marshal.c
index 18cacd410f..e6ee3b47b0 100644
--- a/marshal.c
+++ b/marshal.c
@@ -1242,6 +1242,7 @@ rb_marshal_dump_limited(VALUE obj, VALUE port, int limit)
struct load_arg {
VALUE src;
char *buf;
+ long bufsize;
long buflen;
long readable;
long offset;
@@ -1327,15 +1328,23 @@ static unsigned char
r_byte1_buffered(struct load_arg *arg)
{
if (arg->buflen == 0) {
- long readable = arg->readable < BUFSIZ ? arg->readable : BUFSIZ;
+ long readable = arg->readable < arg->bufsize ? arg->readable : arg->bufsize;
+ long read_len;
VALUE str, n = LONG2NUM(readable);
str = load_funcall(arg, arg->src, s_read, 1, &n);
if (NIL_P(str)) too_short();
StringValue(str);
- memcpy(arg->buf, RSTRING_PTR(str), RSTRING_LEN(str));
+ read_len = RSTRING_LEN(str);
+ if (UNLIKELY(read_len < readable)) too_short();
+ if (UNLIKELY(read_len > arg->bufsize)) {
+ arg->buf = ruby_sized_realloc_n(arg->buf, read_len, 1, arg->bufsize);
+ arg->bufsize = read_len;
+ }
+ memcpy(arg->buf, RSTRING_PTR(str), read_len);
arg->offset = 0;
- arg->buflen = RSTRING_LEN(str);
+ arg->buflen = read_len;
+ RB_GC_GUARD(str);
}
arg->buflen--;
return arg->buf[arg->offset++];
@@ -1422,6 +1431,18 @@ ruby_marshal_read_long(const char **buf, long len)
return x;
}
+static long
+r_keep_readable(struct load_arg *arg, long len, size_t size)
+{
+ if (UNLIKELY(len < 0)) {
+ rb_raise(rb_eArgError, "negative length");
+ }
+ if (UNLIKELY((unsigned long)len > SIZE_MAX / size || arg->readable >= LONG_MAX - len)) {
+ rb_raise(rb_eArgError, "marshaled data too big");
+ }
+ return len;
+}
+
static VALUE
r_bytes1(long len, struct load_arg *arg)
{
@@ -1451,7 +1472,7 @@ r_bytes1_buffered(long len, struct load_arg *arg)
long tmp_len, read_len, need_len = len - buflen;
VALUE tmp, n;
- readable = readable < BUFSIZ ? readable : BUFSIZ;
+ readable = readable < arg->bufsize ? readable : arg->bufsize;
read_len = need_len > readable ? need_len : readable;
n = LONG2NUM(read_len);
tmp = load_funcall(arg, arg->src, s_read, 1, &n);
@@ -2005,7 +2026,10 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE klass, VALUE ex
int sign;
sign = r_byte(arg);
- len = r_long(arg);
+ if (sign != '+' && sign != '-') {
+ rb_raise(rb_eArgError, "invalid Bignum sign");
+ }
+ len = r_keep_readable(arg, r_long(arg), 2);
if (SIZEOF_VALUE >= 8 && len <= 4) {
// Representable within uintptr, likely FIXNUM
@@ -2083,7 +2107,7 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE klass, VALUE ex
case TYPE_ARRAY:
{
- long len = r_long(arg);
+ long len = r_keep_readable(arg, r_long(arg), 1);
v = rb_ary_new2(len);
v = r_entry(v, arg);
@@ -2101,7 +2125,7 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE klass, VALUE ex
case TYPE_HASH_DEF:
type_hash:
{
- long len = r_long(arg);
+ long len = r_keep_readable(arg, r_long(arg), 2);
v = hash_new_with_size(len);
v = r_entry(v, arg);
@@ -2127,7 +2151,7 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE klass, VALUE ex
VALUE slot;
st_index_t idx = r_prepare(arg);
VALUE klass = path2class(r_unique(arg));
- long len = r_long(arg);
+ long len = r_keep_readable(arg, r_long(arg), 2);
v = rb_obj_alloc(klass);
if (!RB_TYPE_P(v, T_STRUCT)) {
@@ -2344,8 +2368,9 @@ r_object(struct load_arg *arg)
static void
clear_load_arg(struct load_arg *arg)
{
- ruby_xfree_sized(arg->buf, BUFSIZ);
+ ruby_xfree_sized(arg->buf, arg->bufsize);
arg->buf = NULL;
+ arg->bufsize = 0;
arg->buflen = 0;
arg->offset = 0;
arg->readable = 0;
@@ -2391,10 +2416,14 @@ rb_marshal_load_with_proc(VALUE port, VALUE proc, bool freeze)
arg->readable = 0;
arg->freeze = freeze;
- if (NIL_P(v))
+ if (NIL_P(v)) {
+ arg->bufsize = BUFSIZ;
arg->buf = xmalloc(BUFSIZ);
- else
+ }
+ else {
+ arg->bufsize = 0;
arg->buf = 0;
+ }
major = r_byte(arg);
minor = r_byte(arg);
diff --git a/misc/lldb_rb/utils.py b/misc/lldb_rb/utils.py
index e0cb609b1a..a2bcedc328 100644
--- a/misc/lldb_rb/utils.py
+++ b/misc/lldb_rb/utils.py
@@ -236,29 +236,20 @@ class RbInspector(LLDBInterface):
elif rval.is_type("RUBY_T_DATA"):
tRTypedData = self.target.FindFirstType("struct RTypedData").GetPointerType()
val = val.Cast(tRTypedData)
- is_typed_data = self.ruby_globals.get("RUBY_TYPED_FL_IS_TYPED_DATA", None)
- if is_typed_data:
- typed = rval.flags & is_typed_data
- else:
- typed = val.GetValueForExpressionPath("->typed_flag").GetValueAsUnsigned() == 1
-
- if typed:
- type = val.GetValueForExpressionPath("->type").GetValueAsUnsigned()
- embed = (type & 1)
- if embed:
- flaginfo += "[EMBED] "
- type = self.frame.EvaluateExpression("(rb_data_type_t *)%0#x" % (type & ~1))
- print("T_DATA: %s%s" %
- (flaginfo, type.GetValueForExpressionPath("->wrap_struct_name")),
- file=self.result)
- print("%s", type.Dereference(), file=self.result)
- ptr = val.GetValueForExpressionPath("->data")
- if embed:
- ptr = ptr.AddressOf()
- self._append_expression("(void *)%0#x" % ptr.GetValueAsUnsigned())
- else:
- print("T_DATA:", file=self.result)
- self._append_expression("*(struct RData *) %0#x" % val.GetValueAsUnsigned())
+
+ type = val.GetValueForExpressionPath("->type").GetValueAsUnsigned()
+ embed = (type & 1)
+ if embed:
+ flaginfo += "[EMBED] "
+ type = self.frame.EvaluateExpression("(rb_data_type_t *)%0#x" % (type & ~1))
+ print("T_DATA: %s%s" %
+ (flaginfo, type.GetValueForExpressionPath("->wrap_struct_name")),
+ file=self.result)
+ print("%s", type.Dereference(), file=self.result)
+ ptr = val.GetValueForExpressionPath("->data")
+ if embed:
+ ptr = ptr.AddressOf()
+ self._append_expression("(void *)%0#x" % ptr.GetValueAsUnsigned())
elif rval.is_type("RUBY_T_IMEMO"):
imemo_type = ((rval.flags >> self.ruby_globals["RUBY_FL_USHIFT"])
diff --git a/pathname_builtin.rb b/pathname_builtin.rb
index ac436d4946..11ade220f0 100644
--- a/pathname_builtin.rb
+++ b/pathname_builtin.rb
@@ -557,19 +557,28 @@ class Pathname
!absolute?
end
+ # :markup: markdown
+ #
+ # call-seq:
+ # each_filename {|component| ... } -> nil
+ # each_filename -> new_enumerator
#
- # Iterates over each component of the path.
+ # With a block given, yields each component of the string path:
#
- # Pathname.new("/usr/bin/ruby").each_filename {|filename| ... }
- # # yields "usr", "bin", and "ruby".
+ # ```ruby
+ # Pathname('/foo/bar/baz').each_filename {|filename| p filename }
+ # => nil
+ # ```
#
- # Returns an Enumerator if no block was given.
+ # Output:
#
- # enum = Pathname.new("/usr/bin/ruby").each_filename
- # # ... do stuff ...
- # enum.each { |e| ... }
- # # yields "usr", "bin", and "ruby".
+ # ```text
+ # "foo"
+ # "bar"
+ # "baz"
+ # ```
#
+ # With no block given, returns a new Enumerator.
def each_filename # :yield: filename
return to_enum(__method__) unless block_given?
_, names = split_names(@path)
@@ -901,12 +910,34 @@ class Pathname
end
class Pathname # * File *
+
+ # :markup: markdown
+ #
+ # call-seq:
+ # each_line(sep = $/, **opts) {|line| ... } → nil
+ # each_line(limit, **opts) {|line| ... } → nil
+ # each_line(sep, limit, **opts) {|line| ... } → nil
+ # each_line(...) → new_enumerator
+ #
+ # With a block given, calls the block with each line
+ # from the file represented by `self`;
+ # returns `nil`:
#
- # #each_line iterates over the line in the file. It yields a String object
- # for each line.
+ # ```ruby
+ # lines = []
+ # Pathname('COPYING').each_line {|line| lines << line }
+ # lines.take(3)
+ # # =>
+ # # ["{日本語}[rdoc-ref:COPYING.ja]\n",
+ # # "\n",
+ # # "Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.\n"]
+ # ```
#
- # This method has existed since 1.8.1.
+ # The lines are read using IO.foreach,
+ # all arguments and options are passed to that method;
+ # see details at IO.foreach.
#
+ # With no block given, returns a new Enumerator.
def each_line(...) # :yield: line
File.foreach(@path, ...)
end
@@ -1391,10 +1422,81 @@ class Pathname # * File *
# See <tt>File.dirname</tt>. Returns all but the last component of the path.
def dirname() self.class.new(File.dirname(@path)) end
- # See <tt>File.extname</tt>. Returns the file's extension.
+ # :markup: markdown
+ #
+ # call-seq:
+ # extname -> extension
+ #
+ # Returns the filename extension of `self` --
+ # usually the portion of the string path beginning from the last period:
+ #
+ # ```ruby
+ # Pathname('t.rb').extname # => ".rb"
+ # Pathname('foo.bar.t.rb').extname # => ".rb"
+ # Pathname('foo/bar/t.rb').extname # => ".rb"
+ # Pathname('nosuch.txt').extname # => ".txt" # Path need not exist.
+ # ```
+ #
+ # Returns the entire string when there is no period:
+ #
+ # ```ruby
+ # Pathname('foo').extname # => ""
+ # ```
+ #
+ # Returns an empty string when the only period is the first character:
+ #
+ # ```ruby
+ # Pathname('.irbrc').extname # => ""
+ # ```
+ #
+ # Returns an empty string or `'.'` when `path` ends with a period:
+ #
+ # ```ruby
+ # Pathname('foo.').extname # => "" # On Windows.
+ # Pathname('foo.').extname # => "." # Elsewhere.
+ # Pathname('foo....').extname # => "" # On Windows.
+ # Pathname('foo....').extname # => "." # Elsewhere.
+ # ```
+ #
def extname() File.extname(@path) end
- # See <tt>File.expand_path</tt>.
+ # :markup: markdown
+ #
+ # call-seq:
+ # expand_path(dirpath = '.') -> new_pathname
+ #
+ # Returns a new pathname containing the absolute path for `self`.
+ #
+ # Evaluates a relative path with respect to the directory given by `dirpath`:
+ #
+ # ```ruby
+ # Dir.chdir('/snap')
+ # # Default dirpath.
+ # Pathname('README').expand_path # => #<Pathname:/snap/README>
+ # Pathname('bin').expand_path # => #<Pathname:/snap/bin>
+ # Pathname('bin/../var').expand_path # => #<Pathname:/snap/var> # Cleaned.
+ # # Other dirpath.
+ # Pathname('../zip').expand_path('/usr/bin/ruby') # => #<Pathname:/usr/bin/zip>
+ # Dir.chdir('/usr/bin')
+ # Pathname('../../snap').expand_path(__FILE__) # => #<Pathname:/usr/snap>
+ # ```
+ #
+ # Evaluates an absolute path without respect to `dirpath`:
+ #
+ # ```ruby
+ # Pathname('/snap').expand_path # => #<Pathname:/snap>
+ # Pathname('/snap').expand_path.expand_path('nosuch') # => #<Pathname:/snap>
+ # Pathname('/snap/../snap').expand_path # => #<Pathname:/snap> # Cleaned.
+ # ```
+ #
+ # More examples:
+ #
+ # ```
+ # Dir.chdir('/usr/bin')
+ # Pathname('../../snap').expand_path(__FILE__) # => #<Pathname:/usr/snap>
+ # Pathname('../../snap').expand_path # => #<Pathname:/snap>
+ # ```
+ #
def expand_path(...) self.class.new(File.expand_path(@path, ...)) end
# See <tt>File.split</tt>. Returns the #dirname and the #basename in an
@@ -1694,8 +1796,24 @@ class Pathname # * Dir *
alias pwd getwd
end
- # Return the entries (files and subdirectories) in the directory, each as a
- # Pathname object.
+ # :markup: markdown
+ #
+ # call-seq:
+ # entries -> array_of_pathnames
+ #
+ # Returns an array of pathnames,
+ # one for each entry in the directory represented by `self`:
+ #
+ # ```ruby
+ # Pathname('.').entries.take(5)
+ # # =>
+ # # [#<Pathname:.>,
+ # # #<Pathname:..>,
+ # # #<Pathname:gc.rb>,
+ # # #<Pathname:yjit.rb>,
+ # # #<Pathname:iseq.h>]
+ # ```
+ #
def entries() Dir.entries(@path).map {|f| self.class.new(f) } end
# :markup: markdown
diff --git a/ractor.c b/ractor.c
index 8fe4b15044..d611ca97c2 100644
--- a/ractor.c
+++ b/ractor.c
@@ -1451,7 +1451,7 @@ allow_frozen_shareable_p(VALUE obj)
if (!RB_TYPE_P(obj, T_DATA)) {
return true;
}
- else if (RTYPEDDATA_P(obj)) {
+ else {
const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
if (type->flags & RUBY_TYPED_FROZEN_SHAREABLE) {
return true;
diff --git a/spec/bundler/spec_helper.rb b/spec/bundler/spec_helper.rb
index 877deca663..27ddc6a771 100644
--- a/spec/bundler/spec_helper.rb
+++ b/spec/bundler/spec_helper.rb
@@ -38,7 +38,7 @@ require_relative "support/indexes"
require_relative "support/matchers"
require_relative "support/permissions"
require_relative "support/platforms"
-require_relative "support/windows_tag_group"
+require_relative "support/shards"
begin
raise LoadError if File.exist?(File.expand_path("../../lib/bundler/bundler.gemspec", __dir__))
@@ -88,7 +88,7 @@ RSpec.configure do |config|
config.include Spec::Path
config.include Spec::Platforms
config.include Spec::Permissions
- config.include Spec::WindowsTagGroup
+ config.include Spec::Shards
# Enable flags like --only-failures and --next-failure
config.example_status_persistence_file_path = ".rspec_status"
@@ -175,7 +175,7 @@ RSpec.configure do |config|
reset!
end
- Spec::WindowsTagGroup::EXAMPLE_MAPPINGS.each do |tag, file_paths|
+ Spec::Shards::EXAMPLE_MAPPINGS.each do |tag, file_paths|
file_pattern = Regexp.union(file_paths.map {|path| Regexp.new(Regexp.escape(path) + "$") })
config.define_derived_metadata(file_path: file_pattern) do |metadata|
@@ -185,8 +185,8 @@ RSpec.configure do |config|
config.before(:context) do |example|
metadata = example.class.metadata
- if metadata[:type] != :aruba && !metadata[:realworld] && metadata.keys.none? {|k| Spec::WindowsTagGroup::EXAMPLE_MAPPINGS.keys.include?(k) }
- warn "#{metadata[:file_path]} is not assigned to any Windows runner group. see spec/support/windows_tag_group.rb for details."
+ if metadata[:type] != :aruba && !metadata[:realworld] && metadata.keys.none? {|k| Spec::Shards::EXAMPLE_MAPPINGS.keys.include?(k) }
+ warn "#{metadata[:file_path]} is not assigned to any shard. see spec/support/shards.rb for details."
end
end unless Spec::Path.ruby_core?
end
diff --git a/spec/bundler/support/artifice/helpers/compact_index.rb b/spec/bundler/support/artifice/helpers/compact_index.rb
index e61fe921ec..e684aa8628 100644
--- a/spec/bundler/support/artifice/helpers/compact_index.rb
+++ b/spec/bundler/support/artifice/helpers/compact_index.rb
@@ -2,7 +2,7 @@
require_relative "endpoint"
-$LOAD_PATH.unshift Dir[Spec::Path.scoped_base_system_gem_path.join("gems/compact_index*/lib")].first.to_s
+$LOAD_PATH.unshift Spec::Path.tmp_root.join("compact_index/lib").to_s
require "compact_index"
require "digest"
@@ -90,13 +90,17 @@ class CompactIndexAPI < Endpoint
rescue StandardError
checksum = nil
end
- CompactIndex::GemVersion.new(spec.version.version, spec.platform.to_s, checksum, nil,
- deps, spec.required_ruby_version.to_s, spec.required_rubygems_version.to_s)
+ build_gem_version(spec, deps, checksum)
end
CompactIndex::Gem.new(name, gem_versions)
end
end
end
+
+ def build_gem_version(spec, deps, checksum)
+ CompactIndex::GemVersion.new(spec.version.version, spec.platform.to_s, checksum, nil,
+ deps, spec.required_ruby_version.to_s, spec.required_rubygems_version.to_s)
+ end
end
get "/names" do
diff --git a/spec/bundler/support/windows_tag_group.rb b/spec/bundler/support/shards.rb
index fb9c081149..580997eb72 100644
--- a/spec/bundler/support/windows_tag_group.rb
+++ b/spec/bundler/support/shards.rb
@@ -1,14 +1,14 @@
# frozen_string_literal: true
-# This group classifies test files into 4 groups by running `bin/rspec --profile 10000`
+# This classifies test files into 4 shards by running `bin/rspec --profile 10000`
# to ensure balanced execution times. When adding new test files, it is recommended to
-# re-aggregate and adjust the groups to keep them balanced.
-# For now, please add new files to group 'windows_d'.
+# re-aggregate and adjust the shards to keep them balanced.
+# For now, please add new files to shard 'shard_d'.
module Spec
- module WindowsTagGroup
+ module Shards
EXAMPLE_MAPPINGS = {
- windows_a: [
+ shard_a: [
"spec/runtime/setup_spec.rb",
"spec/commands/install_spec.rb",
"spec/commands/add_spec.rb",
@@ -53,7 +53,7 @@ module Spec
"spec/bundler/plugin/source_list_spec.rb",
"spec/bundler/source/path_spec.rb",
],
- windows_b: [
+ shard_b: [
"spec/install/gemfile/git_spec.rb",
"spec/install/gems/standalone_spec.rb",
"spec/commands/lock_spec.rb",
@@ -97,7 +97,7 @@ module Spec
"spec/bundler/index_spec.rb",
"spec/other/cli_man_pages_spec.rb",
],
- windows_c: [
+ shard_c: [
"spec/commands/newgem_spec.rb",
"spec/commands/exec_spec.rb",
"spec/commands/clean_spec.rb",
@@ -142,7 +142,7 @@ module Spec
"spec/bundler/cli_common_spec.rb",
"spec/bundler/ci_detector_spec.rb",
],
- windows_d: [
+ shard_d: [
"spec/commands/outdated_spec.rb",
"spec/commands/update_spec.rb",
"spec/lock/lockfile_spec.rb",
diff --git a/spec/ruby/optional/capi/ext/rubyspec.h b/spec/ruby/optional/capi/ext/rubyspec.h
index 7107bead90..5a92645785 100644
--- a/spec/ruby/optional/capi/ext/rubyspec.h
+++ b/spec/ruby/optional/capi/ext/rubyspec.h
@@ -35,6 +35,10 @@
(RUBY_API_VERSION_MAJOR == (major) && RUBY_API_VERSION_MINOR < (minor)))
#define RUBY_VERSION_SINCE(major,minor) (!RUBY_VERSION_BEFORE(major, minor))
+#if RUBY_VERSION_SINCE(4, 1)
+#define RUBY_VERSION_IS_4_1
+#endif
+
#if RUBY_VERSION_SINCE(4, 0)
#define RUBY_VERSION_IS_4_0
#endif
diff --git a/spec/ruby/optional/capi/ext/typed_data_spec.c b/spec/ruby/optional/capi/ext/typed_data_spec.c
index 221f1c8ac4..c6fcfa3bc8 100644
--- a/spec/ruby/optional/capi/ext/typed_data_spec.c
+++ b/spec/ruby/optional/capi/ext/typed_data_spec.c
@@ -106,6 +106,7 @@ VALUE sws_typed_wrap_struct(VALUE self, VALUE val) {
return TypedData_Wrap_Struct(rb_cObject, &sample_typed_wrapped_struct_data_type, bar);
}
+#ifndef RUBY_VERSION_IS_4_1
#undef RUBY_UNTYPED_DATA_WARNING
#define RUBY_UNTYPED_DATA_WARNING 0
VALUE sws_untyped_wrap_struct(VALUE self, VALUE val) {
@@ -113,6 +114,7 @@ VALUE sws_untyped_wrap_struct(VALUE self, VALUE val) {
*data = FIX2INT(val);
return Data_Wrap_Struct(rb_cObject, NULL, free, data);
}
+#endif
VALUE sws_typed_get_struct(VALUE self, VALUE obj) {
struct sample_typed_wrapped_struct* bar;
@@ -173,9 +175,11 @@ VALUE sws_typed_rb_check_typeddata_different_type(VALUE self, VALUE obj) {
return rb_check_typeddata(obj, &sample_typed_wrapped_struct_other_data_type) == DATA_PTR(obj) ? Qtrue : Qfalse;
}
+#ifndef RUBY_VERSION_IS_4_1
VALUE sws_typed_RTYPEDDATA_P(VALUE self, VALUE obj) {
return RTYPEDDATA_P(obj) ? Qtrue : Qfalse;
}
+#endif
void Init_typed_data_spec(void) {
VALUE cls = rb_define_class("CApiAllocTypedSpecs", rb_cObject);
@@ -183,7 +187,9 @@ void Init_typed_data_spec(void) {
rb_define_method(cls, "typed_wrapped_data", sdaf_typed_get_struct, 0);
cls = rb_define_class("CApiWrappedTypedStructSpecs", rb_cObject);
rb_define_method(cls, "typed_wrap_struct", sws_typed_wrap_struct, 1);
+#ifndef RUBY_VERSION_IS_4_1
rb_define_method(cls, "untyped_wrap_struct", sws_untyped_wrap_struct, 1);
+#endif
rb_define_method(cls, "typed_get_struct", sws_typed_get_struct, 1);
rb_define_method(cls, "typed_get_struct_other", sws_typed_get_struct_different_type, 1);
rb_define_method(cls, "typed_get_struct_parent", sws_typed_get_struct_parent_type, 1);
@@ -194,10 +200,11 @@ void Init_typed_data_spec(void) {
rb_define_method(cls, "rb_check_typeddata_same_type", sws_typed_rb_check_typeddata_same_type, 1);
rb_define_method(cls, "rb_check_typeddata_same_type_parent", sws_typed_rb_check_typeddata_same_type_parent, 1);
rb_define_method(cls, "rb_check_typeddata_different_type", sws_typed_rb_check_typeddata_different_type, 1);
+#ifndef RUBY_VERSION_IS_4_1
rb_define_method(cls, "RTYPEDDATA_P", sws_typed_RTYPEDDATA_P, 1);
+#endif
}
#ifdef __cplusplus
}
#endif
-
diff --git a/spec/ruby/optional/capi/typed_data_spec.rb b/spec/ruby/optional/capi/typed_data_spec.rb
index 8eaf7751ba..376cfe417f 100644
--- a/spec/ruby/optional/capi/typed_data_spec.rb
+++ b/spec/ruby/optional/capi/typed_data_spec.rb
@@ -86,15 +86,17 @@ describe "CApiWrappedTypedStruct" do
end
end
- describe "RTYPEDDATA_P" do
- it "returns true for a typed data" do
- a = @s.typed_wrap_struct(1024)
- @s.RTYPEDDATA_P(a).should == true
- end
+ ruby_version_is ""..."4.1" do
+ describe "RTYPEDDATA_P" do
+ it "returns true for a typed data" do
+ a = @s.typed_wrap_struct(1024)
+ @s.RTYPEDDATA_P(a).should == true
+ end
- it "returns false for an untyped data object" do
- a = @s.untyped_wrap_struct(1024)
- @s.RTYPEDDATA_P(a).should == false
+ it "returns false for an untyped data object" do
+ a = @s.untyped_wrap_struct(1024)
+ @s.RTYPEDDATA_P(a).should == false
+ end
end
end
end
diff --git a/test/date/test_date_strptime.rb b/test/date/test_date_strptime.rb
index 4efe1a47d0..6aa7db292d 100644
--- a/test/date/test_date_strptime.rb
+++ b/test/date/test_date_strptime.rb
@@ -517,7 +517,20 @@ class TestDateStrptime < Test::Unit::TestCase
d = DateTime.strptime('9000 +0200', '%Q %z')
assert_equal([1970, 1, 1, 2, 0, 9], [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
assert_equal(Rational(2, 24), d.offset)
-
end
+ def test_format_modified
+ str = " " * 100
+ fmt = Struct.new(:str) {
+ def to_str
+ str << "2026-06-01" << " "*100
+ " %F "
+ end
+ }.new(str)
+ d = Date._strptime(str, fmt)
+ assert_not_nil(d)
+ assert_equal(2026, d[:year])
+ assert_equal(6, d[:mon])
+ assert_equal(1, d[:mday])
+ end
end
diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb
index 87d9cd7f7d..753ee0fbdf 100755
--- a/test/json/json_generator_test.rb
+++ b/test/json/json_generator_test.rb
@@ -601,6 +601,8 @@ class JSONGeneratorTest < Test::Unit::TestCase
assert_equal too_deep, ok
ok = generate too_deep_ary, :max_nesting => 0
assert_equal too_deep, ok
+
+ assert_raise(TypeError) { generate too_deep_ary, max_nesting: "garbage" }
end
def test_backslash
diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb
index 67d86a0b35..292ca1a670 100644
--- a/test/json/json_parser_test.rb
+++ b/test/json/json_parser_test.rb
@@ -545,22 +545,26 @@ class JSONParserTest < Test::Unit::TestCase
end
def test_backslash
+ assert_raise(JSON::ParserError) do
+ JSON.parse('"\\')
+ end
+
data = [ '\\.(?i:gif|jpe?g|png)$' ]
json = '["\\\\.(?i:gif|jpe?g|png)$"]'
assert_equal data, parse(json)
- #
+
data = [ '\\"' ]
json = '["\\\\\""]'
assert_equal data, parse(json)
- #
+
json = '["/"]'
data = [ '/' ]
assert_equal data, parse(json)
- #
+
json = '["\""]'
data = ['"']
assert_equal data, parse(json)
- #
+
json = '["\\/"]'
data = ["/"]
assert_equal data, parse(json)
@@ -878,6 +882,22 @@ class JSONParserTest < Test::Unit::TestCase
end
end
+ def test_mutating_source_string_during_parsing
+ expected = ([1] * 100) + [2.3] + ([1] * 100)
+ source = JSON.generate(expected)
+ expected.delete_at(100)
+
+ fake_decimal_class = Class.new
+ fake_decimal_class.define_method(:initialize) do |number|
+ source.tr!('1', '0')
+ number.to_f
+ end
+
+ actual = JSON.parse(source, decimal_class: fake_decimal_class)
+ actual.delete_at(100)
+ assert_equal expected, actual
+ end
+
private
def assert_equal_float(expected, actual, delta = 1e-2)
diff --git a/test/json/json_ryu_fallback_test.rb b/test/json/json_ryu_fallback_test.rb
index 152de7e360..a61b3e668d 100644
--- a/test/json/json_ryu_fallback_test.rb
+++ b/test/json/json_ryu_fallback_test.rb
@@ -179,5 +179,13 @@ class JSONRyuFallbackTest < Test::Unit::TestCase
assert_equal(-0.0, JSON.parse("-99999999999999999e-4294967296"))
assert_equal(-Float::INFINITY, JSON.parse("-1e4294967295"))
assert_equal(-Float::INFINITY, JSON.parse("-1e4294967297"))
+
+ assert_equal(Float::INFINITY, JSON.parse("1e9223372036854775808"))
+ assert_equal(Float::INFINITY, JSON.parse("1e9999999999999999999"))
+ assert_equal(Float::INFINITY, JSON.parse("1e18446744073709551616"))
+ assert_equal(Float::INFINITY, JSON.parse("1e10000000000000000000"))
+ assert_equal(Float::INFINITY, JSON.parse("1e184467440737095516160"))
+ assert_equal 0.0, JSON.parse("1e-18446744073709551615")
+ assert_equal 0.0, JSON.parse("1e-9223372036854775809")
end
end
diff --git a/test/openssl/test_digest.rb b/test/openssl/test_digest.rb
index 91ed247414..bc1f680df5 100644
--- a/test/openssl/test_digest.rb
+++ b/test/openssl/test_digest.rb
@@ -155,6 +155,22 @@ class OpenSSL::TestDigest < OpenSSL::TestCase
assert_include digests, "sha256"
assert_include digests, "sha512"
end
+
+ if respond_to?(:ractor) && defined?(Ractor.shareable_proc)
+ ractor
+
+ def test_ractor
+ assert_nothing_raised do
+ Ractor.new {
+ [
+ OpenSSL::Digest::SHA256.new(""),
+ OpenSSL::Digest::SHA256.hexdigest(""),
+ OpenSSL::Digest::SHA256.digest(""),
+ ]
+ }.value
+ end
+ end
+ end
end
end
diff --git a/test/openssl/test_kdf.rb b/test/openssl/test_kdf.rb
index 6a12a25aa8..708d1883af 100644
--- a/test/openssl/test_kdf.rb
+++ b/test/openssl/test_kdf.rb
@@ -5,64 +5,31 @@ if defined?(OpenSSL)
class OpenSSL::TestKDF < OpenSSL::TestCase
def test_pkcs5_pbkdf2_hmac_compatibility
- expected = OpenSSL::KDF.pbkdf2_hmac("password", salt: "salt", iterations: 1, length: 20, hash: "sha1")
- assert_equal(expected, OpenSSL::PKCS5.pbkdf2_hmac("password", "salt", 1, 20, "sha1"))
- assert_equal(expected, OpenSSL::PKCS5.pbkdf2_hmac_sha1("password", "salt", 1, 20))
+ # PBKDF2 salt >= 16 bytes (128 bits) and iterations >= 1000 are required in
+ # FIPS.
+ # SP 800-132.
+ # https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-132.pdf
+ # * 5.1 The Salt (S)
+ # * 5.2 The Iteration Count (C)
+ # https://github.com/openssl/openssl/blob/71943544885ff364a10bcc5ffc62d0e651c9a021/providers/implementations/kdfs/pbkdf2.c#L235-L240
+ # https://github.com/openssl/openssl/blob/71943544885ff364a10bcc5ffc62d0e651c9a021/providers/implementations/kdfs/pbkdf2.c#L247-L252
+ # Use the same parameters with test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_25.
+ expected = OpenSSL::KDF.pbkdf2_hmac("passwordPASSWORDpassword",
+ salt: "saltSALTsaltSALTsaltSALTsaltSALTsalt",
+ iterations: 4096,
+ length: 25,
+ hash: "sha1")
+ assert_equal(expected, OpenSSL::PKCS5.pbkdf2_hmac("passwordPASSWORDpassword",
+ "saltSALTsaltSALTsaltSALTsaltSALTsalt",
+ 4096,
+ 25,
+ "sha1"))
+ assert_equal(expected, OpenSSL::PKCS5.pbkdf2_hmac_sha1("passwordPASSWORDpassword",
+ "saltSALTsaltSALTsaltSALTsaltSALTsalt",
+ 4096,
+ 25))
end
- def test_pbkdf2_hmac_sha1_rfc6070_c_1_len_20
- p ="password"
- s = "salt"
- c = 1
- dk_len = 20
- raw = %w{ 0c 60 c8 0f 96 1f 0e 71
- f3 a9 b5 24 af 60 12 06
- 2f e0 37 a6 }
- expected = [raw.join('')].pack('H*')
- value = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha1")
- assert_equal(expected, value)
- end
-
- def test_pbkdf2_hmac_sha1_rfc6070_c_2_len_20
- p ="password"
- s = "salt"
- c = 2
- dk_len = 20
- raw = %w{ ea 6c 01 4d c7 2d 6f 8c
- cd 1e d9 2a ce 1d 41 f0
- d8 de 89 57 }
- expected = [raw.join('')].pack('H*')
- value = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha1")
- assert_equal(expected, value)
- end
-
- def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_20
- p ="password"
- s = "salt"
- c = 4096
- dk_len = 20
- raw = %w{ 4b 00 79 01 b7 65 48 9a
- be ad 49 d9 26 f7 21 d0
- 65 a4 29 c1 }
- expected = [raw.join('')].pack('H*')
- value = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha1")
- assert_equal(expected, value)
- end
-
-# takes too long!
-# def test_pbkdf2_hmac_sha1_rfc6070_c_16777216_len_20
-# p ="password"
-# s = "salt"
-# c = 16777216
-# dk_len = 20
-# raw = %w{ ee fe 3d 61 cd 4d a4 e4
-# e9 94 5b 3d 6b a2 15 8c
-# 26 34 e9 84 }
-# expected = [raw.join('')].pack('H*')
-# value = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha1")
-# assert_equal(expected, value)
-# end
-
def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_25
p ="passwordPASSWORDpassword"
s = "saltSALTsaltSALTsaltSALTsaltSALTsalt"
@@ -78,18 +45,6 @@ class OpenSSL::TestKDF < OpenSSL::TestCase
assert_equal(expected, value)
end
- def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_16
- p ="pass\0word"
- s = "sa\0lt"
- c = 4096
- dk_len = 16
- raw = %w{ 56 fa 6a a7 55 48 09 9d
- cc 37 d7 f0 34 25 e0 c3 }
- expected = [raw.join('')].pack('H*')
- value = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha1")
- assert_equal(expected, value)
- end
-
def test_pbkdf2_hmac_sha256_c_20000_len_32
#unfortunately no official test vectors available yet for SHA-2
p ="password"
@@ -103,6 +58,11 @@ class OpenSSL::TestKDF < OpenSSL::TestCase
def test_scrypt_rfc7914_first
pend "scrypt is not implemented" unless OpenSSL::KDF.respond_to?(:scrypt) # OpenSSL >= 1.1.0
+ # scrypt is not available in FIPS.
+ # EVP_KDF_fetch(ctx, OSSL_KDF_NAME_SCRYPT, propq) returns NULL in FIPS.
+ # https://github.com/openssl/openssl/blob/71943544885ff364a10bcc5ffc62d0e651c9a021/crypto/evp/pbe_scrypt.c#L67-L71
+ omit_on_fips
+
pass = ""
salt = ""
n = 16
@@ -118,6 +78,9 @@ class OpenSSL::TestKDF < OpenSSL::TestCase
def test_scrypt_rfc7914_second
pend "scrypt is not implemented" unless OpenSSL::KDF.respond_to?(:scrypt) # OpenSSL >= 1.1.0
+ # scrypt is not available in FIPS.
+ omit_on_fips
+
pass = "password"
salt = "NaCl"
n = 1024
@@ -131,6 +94,7 @@ class OpenSSL::TestKDF < OpenSSL::TestCase
assert_equal(expected, OpenSSL::KDF.scrypt(pass, salt: salt, N: n, r: r, p: p, length: dklen))
end
+ # https://www.rfc-editor.org/rfc/rfc5869#appendix-A.1
def test_hkdf_rfc5869_test_case_1
hash = "sha256"
ikm = B("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b")
@@ -144,6 +108,7 @@ class OpenSSL::TestKDF < OpenSSL::TestCase
assert_equal(okm, OpenSSL::KDF.hkdf(ikm, salt: salt, info: info, length: l, hash: hash))
end
+ # https://www.rfc-editor.org/rfc/rfc5869#appendix-A.3
def test_hkdf_rfc5869_test_case_3
hash = "sha256"
ikm = B("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b")
@@ -157,16 +122,32 @@ class OpenSSL::TestKDF < OpenSSL::TestCase
assert_equal(okm, OpenSSL::KDF.hkdf(ikm, salt: salt, info: info, length: l, hash: hash))
end
- def test_hkdf_rfc5869_test_case_4
+ # https://www.rfc-editor.org/rfc/rfc5869#appendix-A.5
+ def test_hkdf_rfc5869_test_case_5
hash = "sha1"
- ikm = B("0b0b0b0b0b0b0b0b0b0b0b")
- salt = B("000102030405060708090a0b0c")
- info = B("f0f1f2f3f4f5f6f7f8f9")
- l = 42
-
- okm = B("085a01ea1b10f36933068b56efa5ad81" \
- "a4f14b822f5b091568a9cdd4f155fda2" \
- "c22e422478d305f3f896")
+ ikm = B("000102030405060708090a0b0c0d0e0f" \
+ "101112131415161718191a1b1c1d1e1f" \
+ "202122232425262728292a2b2c2d2e2f" \
+ "303132333435363738393a3b3c3d3e3f" \
+ "404142434445464748494a4b4c4d4e4f")
+ salt = B("606162636465666768696a6b6c6d6e6f" \
+ "707172737475767778797a7b7c7d7e7f" \
+ "808182838485868788898a8b8c8d8e8f" \
+ "909192939495969798999a9b9c9d9e9f" \
+ "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf")
+ info = B("b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" \
+ "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" \
+ "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" \
+ "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" \
+ "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff")
+ l = 82
+
+ okm = B("0bd770a74d1160f7c9f12cd5912a06eb" \
+ "ff6adcae899d92191fe4305673ba2ffe" \
+ "8fa3f1a4e5ad79f3f334b3b202b2173c" \
+ "486ea37ce3d397ed034c7f9dfeb15c5e" \
+ "927336d0441f4c4300e2cff0d0900b52" \
+ "d3b4")
assert_equal(okm, OpenSSL::KDF.hkdf(ikm, salt: salt, info: info, length: l, hash: hash))
end
diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb
index 1adf47ac51..a78527d40e 100644
--- a/test/ruby/test_io.rb
+++ b/test/ruby/test_io.rb
@@ -1393,10 +1393,6 @@ class TestIO < Test::Unit::TestCase
args = ['-e', '$>.write($<.read)'] if args.empty?
ruby = EnvUtil.rubybin
opts = {}
- if defined?(Process::RLIMIT_NPROC)
- lim = Process.getrlimit(Process::RLIMIT_NPROC)[1]
- opts[:rlimit_nproc] = [lim, 2048].min
- end
f = IO.popen([ruby] + args, 'r+', opts)
pid = f.pid
yield(f)
diff --git a/test/ruby/test_marshal.rb b/test/ruby/test_marshal.rb
index 1e4de74503..48a67e1dc5 100644
--- a/test/ruby/test_marshal.rb
+++ b/test/ruby/test_marshal.rb
@@ -933,6 +933,41 @@ class TestMarshal < Test::Unit::TestCase
end
end
+ def test_load_overread
+ input = Struct.new(:bytes, :used) do
+ def initialize
+ super("\x04\x08[\x07".bytes, false)
+ end
+
+ def getbyte
+ bytes.shift
+ end
+
+ def read(_len, _outbuf = nil)
+ return nil if used
+ self.used = true
+ "0" * (1024 * 128)
+ end
+ end.new
+
+ assert_equal([nil, nil], Marshal.load(input))
+ end
+
+ def test_bignum_len_overflow
+ assert_raise(ArgumentError) do
+ Marshal.load("\x04\x08l+\x04\x00\x00\x00\x40")
+ end
+ assert_raise(ArgumentError) do
+ Marshal.load("\x04\x08l+\xfc\x00\x00\x00\x80")
+ end
+ end
+
+ def test_bignum_invalid_sign
+ assert_raise(ArgumentError) do
+ Marshal.load("\x04\bl?")
+ end
+ end
+
class TestMarshalFreezeProc < Test::Unit::TestCase
include MarshalTestLib
diff --git a/test/ruby/test_ractor.rb b/test/ruby/test_ractor.rb
index fabb414e14..611b3b7715 100644
--- a/test/ruby/test_ractor.rb
+++ b/test/ruby/test_ractor.rb
@@ -42,6 +42,59 @@ class TestRactor < Test::Unit::TestCase
=end
end
+ def test_shareable_proc_define_method_super_method_missing
+ assert_ractor(<<~'RUBY', timeout: 30)
+ iterations = 1_000_000
+
+ class SuperFromShareableProcMethodMissingBase
+ def method_missing(mid, *) = mid
+ end
+
+ class SuperFromShareableProcMethodMissingChild < SuperFromShareableProcMethodMissingBase
+ BODY = Ractor.shareable_proc { super() }
+ define_method(:foo, &BODY)
+ define_method(:bar, &BODY)
+ end
+
+ [:foo, :bar].map do |mid|
+ Ractor.new(mid, iterations) do |mid, iterations|
+ obj = SuperFromShareableProcMethodMissingChild.new
+ iterations.times do
+ got = obj.__send__(mid)
+ raise "#{mid} returned #{got.inspect}" unless got == mid
+ end
+ end
+ end.each(&:value)
+ RUBY
+ end
+
+ def test_shareable_proc_define_method_super_method_entry
+ assert_ractor(<<~'RUBY', timeout: 30)
+ iterations = 1_000_000
+
+ class SuperFromShareableProcBase
+ def foo = :foo
+ def bar = :bar
+ end
+
+ class SuperFromShareableProcChild < SuperFromShareableProcBase
+ BODY = Ractor.shareable_proc { super() }
+ define_method(:foo, &BODY)
+ define_method(:bar, &BODY)
+ end
+
+ [:foo, :bar].map do |mid|
+ Ractor.new(mid, iterations) do |mid, iterations|
+ obj = SuperFromShareableProcChild.new
+ iterations.times do
+ got = obj.__send__(mid)
+ raise "#{mid} returned #{got.inspect}" unless got == mid
+ end
+ end
+ end.each(&:value)
+ RUBY
+ end
+
def test_shareability_error_uses_inspect
x = (+"").instance_exec { method(:to_s) }
def x.to_s
diff --git a/test/rubygems/test_gem_commands_cert_command.rb b/test/rubygems/test_gem_commands_cert_command.rb
index 39fda73eba..ed1a1c8627 100644
--- a/test/rubygems/test_gem_commands_cert_command.rb
+++ b/test/rubygems/test_gem_commands_cert_command.rb
@@ -31,14 +31,6 @@ class TestGemCommandsCertCommand < Gem::TestCase
@cmd = Gem::Commands::CertCommand.new
@trust_dir = Gem::Security.trust_dir
-
- @cleanup = []
- end
-
- def teardown
- FileUtils.rm_f(@cleanup)
-
- super
end
def test_certificates_matching
@@ -661,8 +653,7 @@ ERROR: --private-key not specified and ~/.gem/gem-private_key.pem does not exis
assert_equal "/CN=nobody/DC=example", EXPIRED_PUBLIC_CERT.issuer.to_s
- tmp_expired_cert_file = File.join(Dir.tmpdir, File.basename(EXPIRED_PUBLIC_CERT_FILE))
- @cleanup << tmp_expired_cert_file
+ tmp_expired_cert_file = File.join(@tempdir, File.basename(EXPIRED_PUBLIC_CERT_FILE))
File.write(tmp_expired_cert_file, File.read(EXPIRED_PUBLIC_CERT_FILE))
@cmd.handle_options %W[
@@ -694,8 +685,7 @@ ERROR: --private-key not specified and ~/.gem/gem-private_key.pem does not exis
assert_equal "/CN=nobody/DC=example", EXPIRED_PUBLIC_CERT.issuer.to_s
- tmp_expired_cert_file = File.join(Dir.tmpdir, File.basename(EXPIRED_PUBLIC_CERT_FILE))
- @cleanup << tmp_expired_cert_file
+ tmp_expired_cert_file = File.join(@tempdir, File.basename(EXPIRED_PUBLIC_CERT_FILE))
File.write(tmp_expired_cert_file, File.read(EXPIRED_PUBLIC_CERT_FILE))
@cmd.handle_options %W[
diff --git a/test/rubygems/test_gem_safe_yaml.rb b/test/rubygems/test_gem_safe_yaml.rb
index d6fef1d7de..8d0ac63c41 100644
--- a/test/rubygems/test_gem_safe_yaml.rb
+++ b/test/rubygems/test_gem_safe_yaml.rb
@@ -70,6 +70,19 @@ class TestGemSafeYAML < Gem::TestCase
assert_match(/unspecified class/, exception.message)
end
+ def test_plain_tag_key_does_not_construct_specification
+ yaml = <<~YAML
+ tag: "!ruby/object:Gem::Specification"
+ name: pwned
+ arbitrary_ivar: hello
+ YAML
+
+ result = Gem::SafeYAML.safe_load(yaml)
+ assert_kind_of Hash, result
+ assert_equal "!ruby/object:Gem::Specification", result["tag"]
+ assert_equal "pwned", result["name"]
+ end
+
def test_disallowed_symbol_rejected
yaml = <<~YAML
--- !ruby/object:Gem::Dependency
@@ -94,6 +107,66 @@ class TestGemSafeYAML < Gem::TestCase
assert_match(/unspecified class/, exception.message)
end
+ def test_disallowed_symbol_not_interned
+ unique = "rejected_symbol_#{rand(1 << 30)}"
+ yaml = <<~YAML
+ --- !ruby/object:Gem::Dependency
+ name: test
+ requirement: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: 0
+ type: :#{unique}
+ prerelease: false
+ version_requirements: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: 0
+ YAML
+
+ assert_raise(Psych::DisallowedClass) do
+ Gem::YAMLSerializer.load(yaml,
+ permitted_classes: Gem::SafeYAML::PERMITTED_CLASSES,
+ permitted_symbols: Gem::SafeYAML::PERMITTED_SYMBOLS)
+ end
+ refute_includes Symbol.all_symbols.map(&:to_s), unique
+ end
+
+ def test_inline_array_nesting_capped
+ depth = Gem::YAMLSerializer::Parser::MAX_NESTING_DEPTH + 1
+ yaml = "x: " + ("[" * depth) + "a" + ("]" * depth) + "\n"
+
+ expected = [Psych::SyntaxError]
+ # JRuby's JVM stack overflows before the Ruby-level nesting cap fires.
+ expected << ::Java::JavaLang::StackOverflowError if RUBY_ENGINE == "jruby"
+
+ assert_raise(*expected) do
+ Gem::YAMLSerializer.load(yaml, permitted_classes: [])
+ end
+ end
+
+ def test_unknown_alias_raises
+ yaml = <<~YAML
+ foo: 1
+ bar: *missing
+ YAML
+
+ expected_error = defined?(Psych::AnchorNotDefined) ? Psych::AnchorNotDefined : Psych::BadAlias
+ assert_raise(expected_error) { Gem::SafeYAML.safe_load(yaml) }
+ end
+
+ def test_unused_anchor_with_aliases_disabled_is_allowed
+ aliases_enabled = Gem::SafeYAML.aliases_enabled?
+ Gem::SafeYAML.aliases_enabled = false
+
+ result = Gem::SafeYAML.safe_load("foo: &unused 1\nbar: 2\n")
+ assert_equal({ "foo" => 1, "bar" => 2 }, result)
+ ensure
+ Gem::SafeYAML.aliases_enabled = aliases_enabled
+ end
+
def test_yaml_serializer_aliases_disabled
aliases_enabled = Gem::SafeYAML.aliases_enabled?
Gem::SafeYAML.aliases_enabled = false
diff --git a/tool/bundler/test_gems.rb b/tool/bundler/test_gems.rb
index 384ff85d1f..71230c32b7 100644
--- a/tool/bundler/test_gems.rb
+++ b/tool/bundler/test_gems.rb
@@ -4,7 +4,6 @@ source "https://rubygems.org"
gem "rack", "~> 3.1"
gem "rack-test", "~> 2.1"
-gem "compact_index", "~> 0.15.0"
gem "sinatra", "~> 4.1"
gem "rake", "~> 13.1"
gem "builder", "~> 3.2"
diff --git a/tool/bundler/test_gems.rb.lock b/tool/bundler/test_gems.rb.lock
index a8352f3070..0b9ac34162 100644
--- a/tool/bundler/test_gems.rb.lock
+++ b/tool/bundler/test_gems.rb.lock
@@ -57,7 +57,6 @@ PLATFORMS
DEPENDENCIES
builder (~> 3.2)
- compact_index (~> 0.15.0)
concurrent-ruby
etc
fiddle
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 75d5023566..ac0d81092d 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -6080,8 +6080,23 @@ rb_vm_invokesuper(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, CALL_
{
stack_check(ec);
- VALUE bh = vm_caller_setup_arg_block(ec, GET_CFP(), cd->ci, blockiseq, true);
- VALUE val = vm_sendish(ec, GET_CFP(), cd, bh, mexp_search_super);
+ struct rb_callinfo adjusted_ci = VM_CI_ON_STACK(vm_ci_mid(cd->ci),
+ vm_ci_flag(cd->ci),
+ vm_ci_argc(cd->ci),
+ vm_ci_kwarg(cd->ci));
+ const struct rb_callcache *original_cc = rbimpl_atomic_ptr_load((void **)&cd->cc, RBIMPL_ATOMIC_ACQUIRE);
+ struct rb_call_data adjusted_cd = {
+ .ci = &adjusted_ci,
+ .cc = original_cc,
+ };
+
+ VALUE bh = vm_caller_setup_arg_block(ec, GET_CFP(), adjusted_cd.ci, blockiseq, true);
+ VALUE val = vm_sendish(ec, GET_CFP(), &adjusted_cd, bh, mexp_search_super);
+
+ if (original_cc != adjusted_cd.cc && vm_cc_markable(adjusted_cd.cc)) {
+ rbimpl_atomic_ptr_store((volatile void **)&cd->cc, (void *)adjusted_cd.cc, RBIMPL_ATOMIC_RELEASE);
+ RB_OBJ_WRITTEN(CFP_ISEQ(GET_CFP()), Qundef, adjusted_cd.cc);
+ }
VM_EXEC(ec, val);
return val;
diff --git a/win32/win32.c b/win32/win32.c
index f25139ca8b..e3a3df71f6 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -6603,7 +6603,7 @@ rb_w32_pipe(int fds[2])
memcpy(name, prefix, width_of_prefix);
snprintf(name + width_of_prefix, width_of_ids, "%.*"PRI_PIDT_PREFIX"x-%.*lx",
- width_of_pid, rb_w32_getpid(), width_of_serial, InterlockedIncrement(&serial)-1);
+ width_of_pid, rb_w32_getpid(), width_of_serial, (unsigned long)(InterlockedIncrement(&serial)-1));
sec.nLength = sizeof(sec);
sec.lpSecurityDescriptor = NULL;
diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs
index ae372711d7..272c10fde9 100644
--- a/yjit/src/cruby_bindings.inc.rs
+++ b/yjit/src/cruby_bindings.inc.rs
@@ -223,7 +223,7 @@ pub const RUBY_FL_USHIFT: ruby_fl_ushift = 12;
pub type ruby_fl_ushift = u32;
pub const RUBY_FL_WB_PROTECTED: ruby_fl_type = 32;
pub const RUBY_FL_PROMOTED: ruby_fl_type = 32;
-pub const RUBY_FL_USERPRIV0: ruby_fl_type = 64;
+pub const RUBY_FL_UNUSED6: ruby_fl_type = 64;
pub const RUBY_FL_FINALIZE: ruby_fl_type = 128;
pub const RUBY_FL_EXIVAR: ruby_fl_type = 0;
pub const RUBY_FL_SHAREABLE: ruby_fl_type = 256;
diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs
index dad29087be..bd832acc96 100644
--- a/zjit/src/cruby_bindings.inc.rs
+++ b/zjit/src/cruby_bindings.inc.rs
@@ -286,7 +286,7 @@ pub const RUBY_FL_USHIFT: ruby_fl_ushift = 12;
pub type ruby_fl_ushift = u32;
pub const RUBY_FL_WB_PROTECTED: ruby_fl_type = 32;
pub const RUBY_FL_PROMOTED: ruby_fl_type = 32;
-pub const RUBY_FL_USERPRIV0: ruby_fl_type = 64;
+pub const RUBY_FL_UNUSED6: ruby_fl_type = 64;
pub const RUBY_FL_FINALIZE: ruby_fl_type = 128;
pub const RUBY_FL_EXIVAR: ruby_fl_type = 0;
pub const RUBY_FL_SHAREABLE: ruby_fl_type = 256;
@@ -347,7 +347,6 @@ pub const RUBY_TYPED_FREE_IMMEDIATELY: rbimpl_typeddata_flags = 1;
pub const RUBY_TYPED_EMBEDDABLE: rbimpl_typeddata_flags = 2;
pub const RUBY_TYPED_FROZEN_SHAREABLE: rbimpl_typeddata_flags = 256;
pub const RUBY_TYPED_WB_PROTECTED: rbimpl_typeddata_flags = 32;
-pub const RUBY_TYPED_FL_IS_TYPED_DATA: rbimpl_typeddata_flags = 64;
pub const RUBY_TYPED_DECL_MARKING: rbimpl_typeddata_flags = 16384;
pub type rbimpl_typeddata_flags = u32;
pub type rb_event_flag_t = u32;
diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs
index 86078f4ac8..6911129ad3 100644
--- a/zjit/src/hir.rs
+++ b/zjit/src/hir.rs
@@ -1984,9 +1984,10 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
Insn::FixnumAref { recv, index } => write!(f, "FixnumAref {recv}, {index}"),
Insn::Jump(target) => { write!(f, "Jump {target}") }
Insn::CondBranch { val, if_true, if_false } => { write!(f, "CondBranch {val}, {if_true}, {if_false}") },
- Insn::SendDirect { recv, cd, iseq, args, block, .. } => {
+ Insn::SendDirect { recv, cme, iseq, args, block, .. } => {
let blockiseq = block.map(|bh| match bh { BlockHandler::BlockIseq(iseq) => iseq, BlockHandler::BlockArg => unreachable!() });
- write!(f, "SendDirect {recv}, {:p}, :{} ({:?})", self.ptr_map.map_ptr(&blockiseq), ruby_call_method_name(*cd), self.ptr_map.map_ptr(iseq))?;
+ let method_name = unsafe { (**cme).called_id };
+ write!(f, "SendDirect {recv}, {:p}, :{} ({:?})", self.ptr_map.map_ptr(&blockiseq), method_name, self.ptr_map.map_ptr(iseq))?;
for arg in args {
write!(f, ", {arg}")?;
}