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.yml6
-rw-r--r--.github/zizmor.yml1
-rw-r--r--NEWS.md19
-rw-r--r--array.c2
-rw-r--r--class.c10
-rw-r--r--depend1
-rw-r--r--encoding.c14
-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.c2
-rw-r--r--ext/json/parser/parser.c14
-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.c119
-rw-r--r--hash.c8
-rw-r--r--imemo.c18
-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/gc.h2
-rw-r--r--internal/hash.h2
-rw-r--r--misc/lldb_rb/utils.py37
-rw-r--r--pathname_builtin.rb148
-rw-r--r--ractor.c4
-rw-r--r--shape.c78
-rw-r--r--shape.h54
-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--string.c2
-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.rb12
-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_ractor.rb53
-rw-r--r--test/ruby/test_shapes.rb31
-rw-r--r--test/rubygems/test_gem_commands_cert_command.rb14
-rw-r--r--tool/bundler/test_gems.rb1
-rw-r--r--tool/bundler/test_gems.rb.lock1
-rw-r--r--variable.c22
-rw-r--r--vm.c7
-rw-r--r--vm_exec.h4
-rw-r--r--vm_insnhelper.c26
-rw-r--r--win32/win32.c2
-rw-r--r--yjit/src/cruby_bindings.inc.rs2
-rw-r--r--zjit/src/backend/lir.rs73
-rw-r--r--zjit/src/codegen.rs53
-rw-r--r--zjit/src/cruby_bindings.inc.rs10
-rw-r--r--zjit/src/hir.rs5
-rw-r--r--zjit/src/hir_type/mod.rs3
-rw-r--r--zjit/src/state.rs28
76 files changed, 982 insertions, 825 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 50c7bc190a..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
@@ -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/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 327742f544..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.6
- * 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]
+* 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
@@ -209,6 +223,7 @@ A lot of work has gone into making Ractors more stable, performant, and usable.
[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/array.c b/array.c
index 08d8d17c90..7f06e3c9c7 100644
--- a/array.c
+++ b/array.c
@@ -30,6 +30,7 @@
#include "ruby/thread.h"
#include "ruby/util.h"
#include "ruby/ractor.h"
+#include "shape.h"
#include "vm_core.h"
#include "builtin.h"
@@ -909,6 +910,7 @@ init_fake_ary_flags(void)
struct RArray fake_ary = {0};
fake_ary.basic.flags = T_ARRAY;
VALUE ary = (VALUE)&fake_ary;
+ RBASIC_SET_SHAPE_ID(ary, ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_OTHER);
rb_ary_freeze(ary);
return fake_ary.basic.flags;
}
diff --git a/class.c b/class.c
index 69194ccab2..02078cc9bc 100644
--- a/class.c
+++ b/class.c
@@ -585,7 +585,15 @@ class_alloc0(enum ruby_value_type type, VALUE klass, bool boxable)
VALUE flags = type | FL_SHAREABLE;
if (boxable) flags |= RCLASS_BOXABLE;
- NEWOBJ_OF(obj, struct RClass, klass, flags, alloc_size);
+ shape_id_t shape_id = ROOT_SHAPE_ID;
+ if (boxable) {
+ shape_id |= SHAPE_ID_LAYOUT_OTHER;
+ }
+ else {
+ shape_id |= SHAPE_ID_LAYOUT_RCLASS;
+ }
+
+ struct RClass *obj = (struct RClass *)rb_newobj(GET_EC(), klass, flags, shape_id, true, alloc_size);
obj->object_id = 0;
diff --git a/depend b/depend
index e61b685670..a2e8312298 100644
--- a/depend
+++ b/depend
@@ -80,6 +80,7 @@ array.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
array.$(OBJEXT): $(top_srcdir)/internal/serial.h
array.$(OBJEXT): $(top_srcdir)/internal/set_table.h
array.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
+array.$(OBJEXT): $(top_srcdir)/internal/struct.h
array.$(OBJEXT): $(top_srcdir)/internal/variable.h
array.$(OBJEXT): $(top_srcdir)/internal/vm.h
array.$(OBJEXT): $(top_srcdir)/internal/warnings.h
diff --git a/encoding.c b/encoding.c
index 8bb393b471..c17f118eef 100644
--- a/encoding.c
+++ b/encoding.c
@@ -98,6 +98,7 @@ static rb_encoding *global_enc_ascii,
*global_enc_us_ascii;
static int filesystem_encindex = ENCINDEX_ASCII_8BIT;
+static rb_atomic_t locale_alias_registered;
#define GLOBAL_ENC_TABLE_LOCKING(tbl) \
for (struct enc_table *tbl = &global_enc_table, **locking = &tbl; \
@@ -126,7 +127,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
@@ -1568,15 +1569,16 @@ rb_locale_encindex(void)
if (idx < 0) idx = ENCINDEX_UTF_8;
- GLOBAL_ENC_TABLE_LOCKING(enc_table) {
- if (enc_registered(enc_table, "locale") < 0) {
+ if (!RUBY_ATOMIC_LOAD(locale_alias_registered)) {
+ GLOBAL_ENC_TABLE_LOCKING(enc_table) {
+ if (enc_registered(enc_table, "locale") < 0) {
# if defined _WIN32
- void Init_w32_codepage(void);
- Init_w32_codepage();
+ void Init_w32_codepage(void);
+ Init_w32_codepage();
# endif
- GLOBAL_ENC_TABLE_LOCKING(enc_table) {
enc_alias_internal(enc_table, "locale", idx);
}
+ RUBY_ATOMIC_SET(locale_alias_registered, 1);
}
}
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 9260712c9a..110b5f6b32 100644
--- a/ext/json/generator/generator.c
+++ b/ext/json/generator/generator.c
@@ -1581,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.
diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c
index 363f109d3a..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;
}
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..6357577e63 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
@@ -1055,7 +1056,15 @@ rb_newobj(rb_execution_context_t *ec, VALUE klass, VALUE flags, shape_id_t shape
VALUE
rb_ec_newobj_of(rb_execution_context_t *ec, VALUE klass, VALUE flags, size_t size)
{
- return rb_newobj(ec, klass, flags, ROOT_SHAPE_ID, true, size);
+ VALUE type = flags & T_MASK;
+ RUBY_ASSERT(type != T_OBJECT);
+ RUBY_ASSERT(type != T_DATA);
+ RUBY_ASSERT(type != T_CLASS);
+ RUBY_ASSERT(type != T_MODULE);
+ RUBY_ASSERT(type != T_ICLASS);
+ (void)type;
+
+ return rb_newobj(ec, klass, flags, ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_OTHER, true, size);
}
VALUE
@@ -1067,13 +1076,13 @@ rb_newobj_of_with_shape(VALUE klass, VALUE flags, shape_id_t shape_id, size_t si
VALUE
rb_newobj_of(VALUE klass, VALUE flags, size_t size)
{
- return rb_newobj(GET_EC(), klass, flags, ROOT_SHAPE_ID, true, size);
+ return rb_newobj(GET_EC(), klass, flags, ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_OTHER, true, size);
}
static
VALUE class_allocate_complex_instance(VALUE klass, uint32_t capacity)
{
- shape_id_t initial_shape_id = rb_shape_root(rb_gc_heap_id_for_size(sizeof(struct RObject)));
+ shape_id_t initial_shape_id = rb_shape_id_with_robject_layout(rb_shape_root(rb_gc_heap_id_for_size(sizeof(struct RObject))));
VALUE obj = rb_newobj_of_with_shape(klass, T_OBJECT, initial_shape_id, sizeof(struct RObject));
rb_obj_init_complex(obj, rb_st_init_numtable_with_size(capacity));
return obj;
@@ -1098,7 +1107,7 @@ rb_class_allocate_instance(VALUE klass)
// There might be a NEWOBJ tracepoint callback, and it may set fields.
// So the shape must be passed to `NEWOBJ_OF`.
- obj = rb_newobj_of_with_shape(klass, T_OBJECT, rb_shape_root(rb_gc_heap_id_for_size(size)), size);
+ obj = rb_newobj_of_with_shape(klass, T_OBJECT, rb_shape_id_with_robject_layout(rb_shape_root(rb_gc_heap_id_for_size(size))), size);
#if RUBY_DEBUG
VALUE *ptr = ROBJECT_FIELDS(obj);
@@ -1138,33 +1147,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 +1157,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 | SHAPE_ID_LAYOUT_RDATA, wb_protected, size);
rb_gc_register_pinning_obj(obj);
@@ -1237,18 +1219,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 +1238,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 +1260,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 +1273,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 +1395,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 +1457,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 +3479,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 +3492,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);
}
}
@@ -4233,7 +4197,11 @@ vm_weak_table_gen_fields_foreach(st_data_t key, st_data_t value, st_data_t data)
break;
case ST_DELETE:
- RBASIC_SET_SHAPE_ID((VALUE)key, ROOT_SHAPE_ID);
+ // When we're removing an object from the weak ref table, we need to
+ // set the shape on it so that the GC finalizer won't try to remove
+ // it again. A "root shape" indicates to the GC that this object
+ // has no fields on it, hence it won't be in the gen fields table.
+ RBASIC_SET_SHAPE_ID((VALUE)key, ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_OTHER);
return ST_DELETE;
case ST_REPLACE: {
@@ -4460,13 +4428,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 +4441,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/imemo.c b/imemo.c
index 3448a8dcd3..796e078c89 100644
--- a/imemo.c
+++ b/imemo.c
@@ -141,7 +141,11 @@ rb_imemo_fields_new(VALUE owner, shape_id_t shape_id, bool shareable)
size_t embedded_size = offsetof(struct rb_fields, as.embed) + capa * sizeof(VALUE);
RUBY_ASSERT(rb_gc_size_allocatable_p(embedded_size));
VALUE fields = rb_imemo_new(imemo_fields, owner, embedded_size, shareable);
- RBASIC_SET_SHAPE_ID(fields, shape_id);
+ // imemo fields objects should always have "RObject" layout. The
+ // layout in the shape describes the layout of the thing on which it is set.
+ // Imemo fields have the same layout as robject, therefore the layout
+ // should reflect that fact.
+ RBASIC_SET_SHAPE_ID(fields, rb_shape_id_with_robject_layout(shape_id));
RUBY_ASSERT(IMEMO_TYPE_P(fields, imemo_fields));
return fields;
}
@@ -152,7 +156,11 @@ rb_imemo_fields_new_complex(VALUE owner, shape_id_t shape_id, size_t capa, bool
VALUE fields = rb_imemo_new(imemo_fields, owner, sizeof(struct rb_fields), shareable);
IMEMO_OBJ_FIELDS(fields)->as.complex.table = st_init_numtable_with_size(capa);
FL_SET_RAW(fields, OBJ_FIELD_HEAP);
- RBASIC_SET_SHAPE_ID(fields, shape_id);
+ // imemo fields objects should always have "RObject" layout. The
+ // layout in the shape describes the layout of the thing on which it is set.
+ // Imemo fields have the same layout as robject, therefore the layout
+ // should reflect that fact.
+ RBASIC_SET_SHAPE_ID(fields, rb_shape_id_with_robject_layout(shape_id));
return fields;
}
@@ -177,7 +185,11 @@ rb_imemo_fields_new_complex_tbl(VALUE owner, shape_id_t shape_id, st_table *tbl,
VALUE fields = rb_imemo_new(imemo_fields, owner, sizeof(struct rb_fields), shareable);
IMEMO_OBJ_FIELDS(fields)->as.complex.table = tbl;
FL_SET_RAW(fields, OBJ_FIELD_HEAP);
- RBASIC_SET_SHAPE_ID(fields, shape_id);
+ // imemo fields objects should always have "RObject" layout. The
+ // layout in the shape describes the layout of the thing on which it is set.
+ // Imemo fields have the same layout as robject, therefore the layout
+ // should reflect that fact.
+ RBASIC_SET_SHAPE_ID(fields, rb_shape_id_with_robject_layout(shape_id));
st_foreach(tbl, imemo_fields_trigger_wb_i, (st_data_t)fields);
return fields;
}
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/gc.h b/internal/gc.h
index 41675810c7..b78808370c 100644
--- a/internal/gc.h
+++ b/internal/gc.h
@@ -124,7 +124,7 @@ struct rb_objspace; /* in vm_core.h */
T *(var) = (T *)rb_ec_newobj_of((ec), (c), (f), s)
#define NEWOBJ_OF(var, T, c, f, s) EC_NEWOBJ_OF(var, T, c, f, s, GET_EC())
#define UNPROTECTED_NEWOBJ_OF(var, T, c, f, s) \
- T *(var) = (T *)rb_newobj((GET_EC()), (c), (f), 0 /* ROOT_SHAPE_ID */, false, s)
+ T *(var) = (T *)rb_newobj((GET_EC()), (c), (f), ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_OTHER, false, s)
#ifndef RB_GC_OBJECT_METADATA_ENTRY_DEFINED
# define RB_GC_OBJECT_METADATA_ENTRY_DEFINED
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/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..f94c06cc73 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;
@@ -2015,7 +2015,7 @@ move_enter(VALUE obj, struct obj_traverse_replace_data *data)
else {
VALUE type = RB_BUILTIN_TYPE(obj);
size_t slot_size = rb_gc_obj_slot_size(obj);
- VALUE moved = rb_newobj(GET_EC(), 0, type, 0, wb_protected_types[type], slot_size);
+ VALUE moved = rb_newobj(GET_EC(), 0, type, RBASIC_SHAPE_ID(obj), wb_protected_types[type], slot_size);
MEMZERO(((struct RBasic *)moved) + 1, char, slot_size - sizeof(struct RBasic));
data->replacement = (VALUE)moved;
return traverse_cont;
diff --git a/shape.c b/shape.c
index 24f1394f6c..7a02b23073 100644
--- a/shape.c
+++ b/shape.c
@@ -409,10 +409,14 @@ rb_obj_shape_id(VALUE obj)
if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) {
VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
+ shape_id_t base = ROOT_SHAPE_ID;
if (fields_obj) {
- return RBASIC_SHAPE_ID(fields_obj);
+ // Remove the layout from the fields object. We want to
+ // combine the shape of the fields object with the layout of the
+ // class / module object.
+ base = RBASIC_SHAPE_ID(fields_obj) & ~SHAPE_ID_LAYOUT_MASK;
}
- return ROOT_SHAPE_ID;
+ return rb_shape_layout(RBASIC_SHAPE_ID(obj)) | base;
}
return RBASIC_SHAPE_ID(obj);
}
@@ -697,7 +701,7 @@ rb_shape_transition_object_id(shape_id_t original_shape_id)
bool dont_care;
rb_shape_t *shape = get_next_shape_internal(RSHAPE(original_shape_id), id_object_id, SHAPE_OBJ_ID, &dont_care, true);
if (!shape) {
- return ROOT_COMPLEX_WITH_OBJ_ID | RSHAPE_FLAGS(original_shape_id);
+ return rb_shape_layout(original_shape_id) | ROOT_COMPLEX_WITH_OBJ_ID | RSHAPE_FLAGS(original_shape_id);
}
RUBY_ASSERT(shape);
@@ -1225,17 +1229,55 @@ rb_shape_foreach_field(shape_id_t initial_shape_id, rb_shape_foreach_transition_
}
#if RUBY_DEBUG
+/*
+ * Get the layout of this object. The "layout" indicates what strategy
+ * we should use for fetching instance variables from `obj`. It's based
+ * on the C struct layout for each particular object.
+ *
+ * TODO: make Struct have a similar layout to RDATA
+ */
+static shape_id_t
+rb_shape_expected_layout(VALUE obj)
+{
+ switch (BUILTIN_TYPE(obj)) {
+ case T_OBJECT:
+ return SHAPE_ID_LAYOUT_ROBJECT;
+ case T_CLASS:
+ case T_MODULE:
+ if (FL_TEST_RAW(obj, RCLASS_BOXABLE)) {
+ return SHAPE_ID_LAYOUT_OTHER;
+ }
+ return SHAPE_ID_LAYOUT_RCLASS;
+ case T_DATA:
+ return SHAPE_ID_LAYOUT_RDATA;
+ case T_IMEMO:
+ if (IMEMO_TYPE_P(obj, imemo_fields)) {
+ return SHAPE_ID_LAYOUT_ROBJECT;
+ }
+ return SHAPE_ID_LAYOUT_OTHER;
+ default:
+ return SHAPE_ID_LAYOUT_OTHER;
+ }
+}
+
bool
rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id)
{
- if (shape_id == ROOT_SHAPE_ID) {
- return true;
- }
-
if (shape_id == INVALID_SHAPE_ID) {
rb_bug("Can't set INVALID_SHAPE_ID on an object");
}
+ shape_id_t actual_layout = rb_shape_layout(rb_obj_shape_id(obj));
+ shape_id_t expected_layout = rb_shape_expected_layout(obj);
+ if (actual_layout != expected_layout) {
+ rb_bug("shape_id layout mismatch: expected=%x actual=%x shape_id=%u obj=%s",
+ expected_layout, actual_layout, shape_id, rb_obj_info(obj));
+ }
+
+ if (shape_id == ROOT_SHAPE_ID) {
+ return true;
+ }
+
rb_shape_t *shape = RSHAPE(shape_id);
bool has_object_id = false;
@@ -1249,13 +1291,11 @@ rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id)
if (rb_shape_has_object_id(shape_id)) {
if (!has_object_id) {
- rb_p(obj);
rb_bug("shape_id claim having obj_id but doesn't shape_id=%u, obj=%s", shape_id, rb_obj_info(obj));
}
}
else {
if (has_object_id) {
- rb_p(obj);
rb_bug("shape_id claim not having obj_id but it does shape_id=%u, obj=%s", shape_id, rb_obj_info(obj));
}
}
@@ -1330,6 +1370,25 @@ shape_has_object_id_p(VALUE self)
}
static VALUE
+shape_layout(VALUE self)
+{
+ shape_id_t shape_id = NUM2UINT(rb_struct_getmember(self, rb_intern("id")));
+
+ switch (rb_shape_layout(shape_id)) {
+ case SHAPE_ID_LAYOUT_ROBJECT:
+ return ID2SYM(rb_intern("robject"));
+ case SHAPE_ID_LAYOUT_RCLASS:
+ return ID2SYM(rb_intern("rclass"));
+ case SHAPE_ID_LAYOUT_RDATA:
+ return ID2SYM(rb_intern("rdata"));
+ case SHAPE_ID_LAYOUT_OTHER:
+ return ID2SYM(rb_intern("other"));
+ default:
+ rb_bug("unknown shape layout: %u", rb_shape_layout(shape_id));
+ }
+}
+
+static VALUE
parse_key(ID key)
{
if (is_instance_id(key)) {
@@ -1628,6 +1687,7 @@ Init_shape(void)
rb_define_method(rb_cShape, "complex?", shape_complex, 0);
rb_define_method(rb_cShape, "shape_frozen?", shape_frozen, 0);
rb_define_method(rb_cShape, "has_object_id?", shape_has_object_id_p, 0);
+ rb_define_method(rb_cShape, "layout", shape_layout, 0);
rb_define_const(rb_cShape, "SHAPE_ROOT", INT2NUM(SHAPE_ROOT));
rb_define_const(rb_cShape, "SHAPE_IVAR", INT2NUM(SHAPE_IVAR));
diff --git a/shape.h b/shape.h
index 61fadca5ba..a319449988 100644
--- a/shape.h
+++ b/shape.h
@@ -27,12 +27,14 @@ STATIC_ASSERT(shape_id_num_bits, SHAPE_ID_NUM_BITS == sizeof(shape_id_t) * CHAR_
// 19-22 SHAPE_ID_HEAP_INDEX_MASK
// index in rb_shape_tree.capacities. Allow to access slot size.
// Currently always 0 except for T_OBJECT.
-// 23 SHAPE_ID_FL_FROZEN
+// 23 SHAPE_ID_FL_COMPLEX
+// The object is backed by a `st_table`.
+// 24 SHAPE_ID_FL_FROZEN
// Whether the object is frozen or not.
-// 24 SHAPE_ID_FL_HAS_OBJECT_ID
+// 25 SHAPE_ID_FL_HAS_OBJECT_ID
// Whether the object has an `SHAPE_OBJ_ID` transition.
-// 25 SHAPE_ID_FL_COMPLEX
-// The object is backed by a `st_table`.
+// 26-27 SHAPE_ID_LAYOUT_MASK
+// The object's physical field layout.
enum shape_id_fl_type {
#define RBIMPL_SHAPE_ID_FL(n) (1<<(SHAPE_ID_FL_USHIFT+n))
@@ -43,8 +45,26 @@ enum shape_id_fl_type {
SHAPE_ID_FL_FROZEN = RBIMPL_SHAPE_ID_FL(1),
SHAPE_ID_FL_HAS_OBJECT_ID = RBIMPL_SHAPE_ID_FL(2),
+ // Means IVs are found at an offset from the object's addr, or in a
+ // malloc allocated side table
+ SHAPE_ID_LAYOUT_ROBJECT = 0,
+
+ // Means this object is a class/module that is NOT RCLASS_BOXABLE, and IV's
+ // are found in the fields_obj found on the rclass struct
+ SHAPE_ID_LAYOUT_RCLASS = RBIMPL_SHAPE_ID_FL(3),
+
+ // Means this object is an RData or RTypedData and IVs are found in the
+ // fields_obj found on the RData/RTypedData struct
+ SHAPE_ID_LAYOUT_RDATA = RBIMPL_SHAPE_ID_FL(4),
+
+ // Means this is a complicated object: boxable classes, structs, objects
+ // that store IVs on the geniv table
+ SHAPE_ID_LAYOUT_OTHER = SHAPE_ID_LAYOUT_RCLASS | SHAPE_ID_LAYOUT_RDATA,
+
+ SHAPE_ID_LAYOUT_MASK = SHAPE_ID_LAYOUT_OTHER,
+
SHAPE_ID_FL_NON_CANONICAL_MASK = SHAPE_ID_FL_FROZEN | SHAPE_ID_FL_HAS_OBJECT_ID,
- SHAPE_ID_FLAGS_MASK = SHAPE_ID_HEAP_INDEX_MASK | SHAPE_ID_FL_NON_CANONICAL_MASK | SHAPE_ID_FL_COMPLEX,
+ SHAPE_ID_FLAGS_MASK = SHAPE_ID_HEAP_INDEX_MASK | SHAPE_ID_FL_NON_CANONICAL_MASK | SHAPE_ID_FL_COMPLEX | SHAPE_ID_LAYOUT_MASK,
#undef RBIMPL_SHAPE_ID_FL
};
@@ -55,12 +75,13 @@ enum shape_id_mask {
SHAPE_ID_HAS_IVAR_MASK = SHAPE_ID_FL_COMPLEX | (SHAPE_ID_OFFSET_MASK - 1),
};
-// The interpreter doesn't care about frozen status, slot size or object id when reading ivars.
+// The interpreter doesn't care about frozen status, slot size, or object id, and
+// has its own checks for physical field layout when reading ivars.
// So we normalize shape_id by clearing these bits to improve cache hits.
// JITs however might care about some of it.
-#define SHAPE_ID_READ_ONLY_MASK (~(SHAPE_ID_FL_FROZEN | SHAPE_ID_HEAP_INDEX_MASK | SHAPE_ID_FL_HAS_OBJECT_ID))
+#define SHAPE_ID_READ_ONLY_MASK (~(SHAPE_ID_FL_FROZEN | SHAPE_ID_HEAP_INDEX_MASK | SHAPE_ID_FL_HAS_OBJECT_ID | SHAPE_ID_LAYOUT_MASK))
// For write it's the same idea, but here we do care about frozen status.
-#define SHAPE_ID_WRITE_MASK (~(SHAPE_ID_HEAP_INDEX_MASK | SHAPE_ID_FL_HAS_OBJECT_ID))
+#define SHAPE_ID_WRITE_MASK (~(SHAPE_ID_HEAP_INDEX_MASK | SHAPE_ID_FL_HAS_OBJECT_ID | SHAPE_ID_LAYOUT_MASK))
typedef uint32_t redblack_id_t;
@@ -153,11 +174,18 @@ RBASIC_SET_SHAPE_ID_NO_CHECKS(VALUE obj, shape_id_t shape_id)
#endif
}
+static inline shape_id_t
+rb_shape_layout(shape_id_t shape_id)
+{
+ return shape_id & SHAPE_ID_LAYOUT_MASK;
+}
+
static inline void
RBASIC_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id)
{
RUBY_ASSERT(!RB_SPECIAL_CONST_P(obj));
RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO) || IMEMO_TYPE_P(obj, imemo_fields));
+ RUBY_ASSERT(!IMEMO_TYPE_P(obj, imemo_fields) || rb_shape_layout(shape_id) == SHAPE_ID_LAYOUT_ROBJECT);
RBASIC_SET_SHAPE_ID_NO_CHECKS(obj, shape_id);
@@ -232,6 +260,12 @@ rb_shape_canonical_p(shape_id_t shape_id)
return !(shape_id & SHAPE_ID_FL_NON_CANONICAL_MASK);
}
+static inline shape_id_t
+rb_shape_id_with_robject_layout(shape_id_t shape_id)
+{
+ return (shape_id & ~SHAPE_ID_LAYOUT_MASK) | SHAPE_ID_LAYOUT_ROBJECT;
+}
+
static inline uint8_t
rb_shape_heap_index(shape_id_t shape_id)
{
@@ -450,10 +484,10 @@ rb_shape_transition_frozen(shape_id_t shape_id)
static inline shape_id_t
rb_shape_transition_complex(shape_id_t shape_id)
{
- shape_id_t next_shape_id = ROOT_COMPLEX_SHAPE_ID;
+ shape_id_t next_shape_id = rb_shape_layout(shape_id) | ROOT_COMPLEX_SHAPE_ID;
if (rb_shape_has_object_id(shape_id)) {
- next_shape_id = ROOT_COMPLEX_WITH_OBJ_ID;
+ next_shape_id = rb_shape_layout(shape_id) | ROOT_COMPLEX_WITH_OBJ_ID;
}
uint8_t heap_index = rb_shape_heap_index(shape_id);
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/string.c b/string.c
index 6865b0d8e6..61d4f16679 100644
--- a/string.c
+++ b/string.c
@@ -630,7 +630,7 @@ static VALUE
setup_fake_str(struct RString *fake_str, const char *name, long len, int encidx)
{
fake_str->basic.flags = T_STRING|RSTRING_NOEMBED|STR_NOFREE|STR_FAKESTR;
- RBASIC_SET_SHAPE_ID((VALUE)fake_str, ROOT_SHAPE_ID);
+ RBASIC_SET_SHAPE_ID((VALUE)fake_str, ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_OTHER);
if (!name) {
RUBY_ASSERT_ALWAYS(len == 0);
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 1969e79b31..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)
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_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/ruby/test_shapes.rb b/test/ruby/test_shapes.rb
index ef5dbd9fb1..bace69658a 100644
--- a/test/ruby/test_shapes.rb
+++ b/test/ruby/test_shapes.rb
@@ -1067,6 +1067,37 @@ class TestShapes < Test::Unit::TestCase
assert_nil shape.parent
end
+ def test_shape_layout
+ assert_equal :robject, RubyVM::Shape.of(TestObject.new).layout
+
+ if ENV["RUBY_BOX"]
+ assert_equal :other, RubyVM::Shape.of(Kernel).layout
+ assert_equal :other, RubyVM::Shape.of(String).layout
+ else
+ assert_equal :rclass, RubyVM::Shape.of(Kernel).layout
+ assert_equal :rclass, RubyVM::Shape.of(String).layout
+ end
+
+ assert_equal :rclass, RubyVM::Shape.of(Class.new).layout
+ assert_equal :rclass, RubyVM::Shape.of(Module.new).layout
+
+ klass = Class.new
+ assert_equal :rclass, RubyVM::Shape.of(klass).layout
+ klass.instance_variable_set(:@a, 123)
+ assert_equal :rclass, RubyVM::Shape.of(klass).layout
+
+ assert_equal :rdata, RubyVM::Shape.of(Thread.current).layout
+ assert_equal :rdata, RubyVM::Shape.of(lambda {}).layout
+
+ assert_equal :other, RubyVM::Shape.of(Struct.new(:x).new(1)).layout
+ assert_equal :other, RubyVM::Shape.of([]).layout
+ assert_equal :other, RubyVM::Shape.of("hello").layout
+ assert_equal :other, RubyVM::Shape.of(/foo/).layout
+ assert_equal :other, RubyVM::Shape.of(2..3).layout
+ assert_equal :other, RubyVM::Shape.of(2**67).layout
+ assert_equal :other, RubyVM::Shape.of(:"aaroniscool#{123}").layout
+ end
+
def test_str_has_root_shape
assert_shape_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of(""))
end
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/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/variable.c b/variable.c
index 857d870413..687fa03631 100644
--- a/variable.c
+++ b/variable.c
@@ -1343,7 +1343,7 @@ rb_free_generic_ivar(VALUE obj)
}
}
}
- RBASIC_SET_SHAPE_ID(obj, ROOT_SHAPE_ID);
+ RBASIC_SET_SHAPE_ID(obj, rb_shape_layout(RBASIC_SHAPE_ID(obj)) | ROOT_SHAPE_ID);
}
}
@@ -1395,7 +1395,7 @@ rb_obj_set_fields(VALUE obj, VALUE fields_obj, ID field_name, VALUE original_fie
}
}
- RBASIC_SET_SHAPE_ID(obj, RBASIC_SHAPE_ID(fields_obj));
+ RBASIC_SET_SHAPE_ID(obj, rb_shape_layout(RBASIC_SHAPE_ID(obj)) | RBASIC_SHAPE_ID(fields_obj));
}
void
@@ -1710,6 +1710,7 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
size_t trailing_fields = new_fields_count - removed_index;
MEMMOVE(&fields[removed_index], &fields[removed_index + 1], VALUE, trailing_fields);
+ RUBY_ASSERT(rb_shape_layout(next_shape_id) == SHAPE_ID_LAYOUT_ROBJECT);
RBASIC_SET_SHAPE_ID(fields_obj, next_shape_id);
if (FL_TEST_RAW(fields_obj, OBJ_FIELD_HEAP)) {
@@ -1732,7 +1733,7 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
}
}
- RBASIC_SET_SHAPE_ID(obj, next_shape_id);
+ RBASIC_SET_SHAPE_ID(obj, rb_shape_layout(RBASIC_SHAPE_ID(obj)) | next_shape_id);
if (fields_obj != original_fields_obj) {
switch (type) {
case T_OBJECT:
@@ -1843,7 +1844,7 @@ imemo_fields_set(VALUE owner, VALUE fields_obj, shape_id_t target_shape_id, ID f
RUBY_ASSERT(field_name);
st_insert(table, (st_data_t)field_name, (st_data_t)val);
RB_OBJ_WRITTEN(fields_obj, Qundef, val);
- RBASIC_SET_SHAPE_ID(fields_obj, target_shape_id);
+ RBASIC_SET_SHAPE_ID(fields_obj, rb_shape_id_with_robject_layout(target_shape_id));
}
else {
attr_index_t index = RSHAPE_INDEX(target_shape_id);
@@ -1855,7 +1856,7 @@ imemo_fields_set(VALUE owner, VALUE fields_obj, shape_id_t target_shape_id, ID f
RB_OBJ_WRITE(fields_obj, &table[index], val);
if (index >= RSHAPE_LEN(current_shape_id)) {
- RBASIC_SET_SHAPE_ID(fields_obj, target_shape_id);
+ RBASIC_SET_SHAPE_ID(fields_obj, rb_shape_id_with_robject_layout(target_shape_id));
}
}
@@ -2010,11 +2011,12 @@ rb_obj_freeze_inline(VALUE x)
RB_FL_UNSET_RAW(x, FL_USER2 | FL_USER3); // STR_CHILLED
}
+ // rb_obj_freeze_inline(String)
shape_id_t shape_id = rb_obj_shape_transition_frozen(x);
switch (BUILTIN_TYPE(x)) {
case T_CLASS:
case T_MODULE:
- RBASIC_SET_SHAPE_ID(RCLASS_WRITABLE_ENSURE_FIELDS_OBJ(x), shape_id);
+ rb_obj_freeze_inline(RCLASS_WRITABLE_ENSURE_FIELDS_OBJ(x));
// FIXME: How to do multi-shape?
RBASIC_SET_SHAPE_ID(x, shape_id);
break;
@@ -2303,7 +2305,7 @@ rb_copy_generic_ivar(VALUE dest, VALUE obj)
}
if (!RSHAPE_LEN(dest_shape_id)) {
- RBASIC_SET_SHAPE_ID(dest, dest_shape_id);
+ RBASIC_SET_SHAPE_ID(dest, rb_shape_layout(RBASIC_SHAPE_ID(dest)) | dest_shape_id);
return;
}
@@ -4636,6 +4638,7 @@ class_fields_ivar_set(VALUE klass, VALUE fields_obj, ID id, VALUE val, bool conc
}
if (new_ivar) {
+ RUBY_ASSERT(rb_shape_layout(next_shape_id) == SHAPE_ID_LAYOUT_ROBJECT);
RBASIC_SET_SHAPE_ID(fields_obj, next_shape_id);
}
}
@@ -4658,6 +4661,7 @@ complex:
RB_OBJ_WRITTEN(fields_obj, Qundef, val);
if (fields_obj != original_fields_obj) {
+ RUBY_ASSERT(rb_shape_layout(next_shape_id) == SHAPE_ID_LAYOUT_ROBJECT);
RBASIC_SET_SHAPE_ID(fields_obj, next_shape_id);
}
}
@@ -4684,7 +4688,7 @@ class_ivar_set(VALUE obj, ID id, VALUE val, bool *new_ivar)
// TODO: What should we set as the T_CLASS shape_id?
// In most case we can replicate the single `fields_obj` shape
// but in namespaced case? Perhaps INVALID_SHAPE_ID?
- RBASIC_SET_SHAPE_ID(obj, RBASIC_SHAPE_ID(new_fields_obj));
+ RBASIC_SET_SHAPE_ID(obj, rb_shape_layout(RBASIC_SHAPE_ID(obj)) | RBASIC_SHAPE_ID(new_fields_obj));
return index;
}
@@ -4709,7 +4713,7 @@ rb_fields_tbl_copy(VALUE dst, VALUE src)
VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(src);
if (fields_obj) {
RCLASS_WRITABLE_SET_FIELDS_OBJ(dst, rb_imemo_fields_clone(fields_obj));
- RBASIC_SET_SHAPE_ID(dst, RBASIC_SHAPE_ID(src));
+ RBASIC_SET_SHAPE_ID(dst, rb_shape_layout(RBASIC_SHAPE_ID(dst)) | RBASIC_SHAPE_ID(src));
}
}
diff --git a/vm.c b/vm.c
index de8628454e..1d2a3eb66a 100644
--- a/vm.c
+++ b/vm.c
@@ -574,13 +574,6 @@ jit_exec(rb_execution_context_t *ec)
rb_jit_func_t func = zjit_compile(ec);
if (func) {
VALUE result = ((rb_zjit_func_t)zjit_entry)(ec, ec->cfp, func);
- // Materialize any remaining lightweight ZJIT frames on side exit.
- // This is done here (once per JIT entry) instead of in each side exit
- // to reduce generated code size.
- if (UNDEF_P(result)) {
- ec->cfp->jit_return = 0; // exit code already cleared most fields except jit_return
- rb_zjit_materialize_frames(ec, ec->cfp);
- }
return result;
}
}
diff --git a/vm_exec.h b/vm_exec.h
index d7c7752110..ecf8df3c7d 100644
--- a/vm_exec.h
+++ b/vm_exec.h
@@ -189,10 +189,6 @@ default: \
rb_jit_func_t func = zjit_compile(ec); \
if (func) { \
val = zjit_entry(ec, ec->cfp, func); \
- if (UNDEF_P(val)) { \
- ec->cfp->jit_return = 0; \
- rb_zjit_materialize_frames(ec, ec->cfp); \
- } \
} \
} \
} \
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 75d5023566..f515662bf0 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -1412,7 +1412,8 @@ vm_setivar_class(VALUE obj, VALUE val, rb_setivar_cache cache)
RB_OBJ_WRITE(fields_obj, &rb_imemo_fields_ptr(fields_obj)[cache.index], val);
if (shape_id != dest_shape_id) {
- RBASIC_SET_SHAPE_ID(obj, dest_shape_id);
+ // The dest_shape_id comes from the fields_obj
+ RBASIC_SET_SHAPE_ID(obj, SHAPE_ID_LAYOUT_RCLASS | (dest_shape_id & ~SHAPE_ID_LAYOUT_MASK));
RBASIC_SET_SHAPE_ID(fields_obj, dest_shape_id);
}
@@ -1437,7 +1438,9 @@ vm_setivar_default(VALUE obj, ID id, VALUE val, rb_setivar_cache cache)
if (shape_id != dest_shape_id) {
RBASIC_SET_SHAPE_ID(obj, dest_shape_id);
- RBASIC_SET_SHAPE_ID(fields_obj, dest_shape_id);
+ // The dest_shape_id comes from the owner, but fields_obj must always
+ // have layout RObject, so give the fields_object the right layout.
+ RBASIC_SET_SHAPE_ID(fields_obj, rb_shape_id_with_robject_layout(dest_shape_id));
}
RB_DEBUG_COUNTER_INC(ivar_set_ic_hit);
@@ -6080,8 +6083,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/backend/lir.rs b/zjit/src/backend/lir.rs
index 25fa0cc151..a417df300a 100644
--- a/zjit/src/backend/lir.rs
+++ b/zjit/src/backend/lir.rs
@@ -4,7 +4,7 @@ use std::mem::take;
use std::rc::Rc;
use crate::bitset::BitSet;
use crate::codegen::{local_size_and_idx_to_ep_offset, perf_symbol_range_start, perf_symbol_range_end};
-use crate::cruby::{IseqPtr, Qundef, RUBY_OFFSET_CFP_ISEQ, RUBY_OFFSET_CFP_JIT_RETURN, RUBY_OFFSET_CFP_PC, RUBY_OFFSET_CFP_SP, SIZEOF_VALUE_I32, vm_stack_canary};
+use crate::cruby::{IseqPtr, RUBY_OFFSET_CFP_ISEQ, RUBY_OFFSET_CFP_JIT_RETURN, RUBY_OFFSET_CFP_PC, RUBY_OFFSET_CFP_SP, SIZEOF_VALUE_I32, vm_stack_canary};
use crate::hir::{Invariant, SideExitReason};
use crate::hir;
use crate::options::{TraceExits, PerfMap, get_option};
@@ -13,7 +13,7 @@ use crate::payload::IseqVersionRef;
use crate::stats::{exit_counter_ptr, exit_counter_ptr_for_opcode, side_exit_counter, CompileError};
use crate::virtualmem::CodePtr;
use crate::asm::{CodeBlock, Label};
-use crate::state::rb_zjit_record_exit_stack;
+use crate::state::{ZJITState, rb_zjit_record_exit_stack};
/// LIR Block ID. Unique ID for each block, and also defined in LIR so
/// we can differentiate it from HIR block ids.
@@ -1562,23 +1562,6 @@ impl Assembler
iter
}
- /// Return an operand for a basic block argument at a given index.
- /// To simplify the implementation, we allocate a fixed register or a stack slot
- /// for each basic block argument.
- pub fn param_opnd(idx: usize) -> Opnd {
- use crate::backend::current::ALLOC_REGS;
- use crate::cruby::SIZEOF_VALUE_I32;
-
- if idx < ALLOC_REGS.len() {
- Opnd::Reg(ALLOC_REGS[idx])
- } else {
- // With FrameSetup, the address that NATIVE_BASE_PTR points to stores an old value in the register.
- // To avoid clobbering it, we need to start from the next slot, and we also reserve one space for
- // JITFrame, hence `+ 2` for the index.
- Opnd::mem(64, NATIVE_BASE_PTR, (idx - ALLOC_REGS.len() + 2) as i32 * -SIZEOF_VALUE_I32)
- }
- }
-
pub fn linearize_instructions(&self) -> Vec<Insn> {
// Emit instructions with labels, expanding branch parameters
let mut insns = Vec::with_capacity(ASSEMBLER_INSNS_CAPACITY);
@@ -2040,11 +2023,25 @@ impl Assembler
if self.basic_blocks[block_id.0].is_dummy() { continue; }
let params = self.basic_blocks[block_id.0].parameters.clone();
+ // JIT-to-JIT entries that would need more argument registers should
+ // be unreachable because can_direct_send() refuses to call them.
+ // Keep compiling the function body, but make the unsupported entry
+ // abort if control ever reaches it. TODO: Remove this (Shopify/ruby#916)
+ if params.len() > C_ARG_OPNDS.len() {
+ let insert_pos = self.basic_blocks[block_id.0].insns.iter()
+ .position(|insn| matches!(insn, Insn::FrameSetup { .. }))
+ .or_else(|| self.basic_blocks[block_id.0].insns.iter().position(|insn| matches!(insn, Insn::Label(_))).map(|idx| idx + 1))
+ .unwrap_or(0);
+ self.basic_blocks[block_id.0].insns.insert(insert_pos, Insn::Abort);
+ self.basic_blocks[block_id.0].insn_ids.insert(insert_pos, None);
+ continue;
+ }
+
// Rewrite VRegs to physical registers before sequentialization
// so the parcopy algorithm can detect physical register conflicts.
let reg_copies: Vec<parcopy::RegisterCopy<Opnd>> = params.iter().enumerate()
.map(|(i, param)| parcopy::RegisterCopy::<Opnd> {
- source: Assembler::param_opnd(i),
+ source: C_ARG_OPNDS[i],
destination: Self::rewritten_opnd(*param, assignments),
})
.filter(|copy| copy.source != copy.destination)
@@ -2380,7 +2377,7 @@ impl Assembler
asm_comment!(asm, "save cfp->iseq");
asm.store(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_ISEQ), VALUE::from(*iseq).into());
- // cfp->block_code and cfp->jit_return are cleared by the caller jit_exec() or JIT_EXEC()
+ // cfp->block_code and cfp->jit_return are cleared by the materialize_exit trampoline
if !stack.is_empty() {
asm_comment!(asm, "write stack slots: {}", join_opnds(&stack, ", "));
@@ -2400,8 +2397,7 @@ impl Assembler
/// Tear down the JIT frame and return to the interpreter.
fn compile_exit_return(asm: &mut Assembler) {
asm_comment!(asm, "exit to the interpreter");
- asm.frame_teardown(&[]); // matching the setup in gen_entry_point()
- asm.cret(Opnd::UImm(Qundef.as_u64()));
+ asm.jmp(Target::CodePtr(ZJITState::get_materialize_exit_trampoline()));
}
fn compile_exit_recompile(asm: &mut Assembler, exit: &SideExit) {
@@ -2434,7 +2430,7 @@ impl Assembler
compile_exit_save_state(asm, exit);
if trace_reason.is_some() || exit.recompile.is_some() {
// Clear cfp->jit_return to prepare for a C call. Normally, cfp->jit_return
- // is cleared by the caller jit_exec() or JIT_EXEC(), but if we're about to
+ // is cleared by the materialize_exit trampoline, but if we're about to
// make a C call, we need to clear any stale JITFrame.
asm_comment!(asm, "clear cfp->jit_return");
asm.store(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_JIT_RETURN), 0.into());
@@ -4217,8 +4213,8 @@ mod tests {
let (assignments, _) = asm.linear_scan(intervals.clone(), 5, &preferred_registers);
// Entry block b1 has parameters [v0, v1].
- // With 5 registers: v0 -> Reg(0) = regs[0], arrival = param_opnd(0) = regs[0] -> self-move, filtered
- // v1 -> Reg(1) = regs[1], arrival = param_opnd(1) = regs[1] -> self-move, filtered
+ // With 5 registers: v0 -> Reg(0) = regs[0], arrival = C_ARG_OPNDS[0] = regs[0] -> self-move, filtered
+ // v1 -> Reg(1) = regs[1], arrival = C_ARG_OPNDS[1] = regs[1] -> self-move, filtered
// Before resolve_ssa, b1 has: [Label, Jmp] = 2 insns
assert_eq!(asm.basic_blocks[b1.0].insns.len(), 2);
@@ -4243,6 +4239,31 @@ mod tests {
}
}
+ #[test]
+ fn test_resolve_ssa_entry_params_too_many_abort() {
+ let mut asm = Assembler::new();
+ let block = asm.new_block(hir::BlockId(0), true, 0);
+ asm.set_current_block(block);
+ let label = asm.new_label("bb0");
+ asm.write_label(label);
+
+ for _ in 0..=C_ARG_OPNDS.len() {
+ let param = asm.new_vreg(64);
+ asm.basic_blocks[block.0].add_parameter(param);
+ }
+ asm.basic_blocks[block.0].push_insn(Insn::CRet(Opnd::UImm(0)));
+
+ let live_in = asm.analyze_liveness();
+ asm.number_instructions(0);
+ let intervals = asm.build_intervals(live_in);
+ let preferred_registers = asm.preferred_register_assignments(&intervals);
+ let (assignments, _) = asm.linear_scan(intervals.clone(), 5, &preferred_registers);
+
+ asm.resolve_ssa(&intervals, &assignments);
+
+ assert!(matches!(asm.basic_blocks[block.0].insns[1], Insn::Abort));
+ }
+
fn build_critical_edge() -> (Assembler, Opnd, Opnd, Opnd, Opnd, Opnd, BlockId, BlockId, BlockId) {
let mut asm = Assembler::new();
diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs
index 7c893a72fe..d5d381acfa 100644
--- a/zjit/src/codegen.rs
+++ b/zjit/src/codegen.rs
@@ -2551,25 +2551,6 @@ fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, guard
asm.cmp(klass, Opnd::Value(expected_class));
asm.jne(jit, side_exit);
- } else if guard_type.is_subtype(types::TData) {
- let side = side_exit(jit, state, GuardType(guard_type));
-
- // Check special constant
- asm.test(val, Opnd::UImm(RUBY_IMMEDIATE_MASK as u64));
- asm.jnz(jit, side.clone());
-
- // Check false
- asm.cmp(val, Qfalse.into());
- asm.je(jit, side.clone());
-
- // Check the T_DATA builtin type.
- let val = asm.load_mem(val);
- let flags = asm.load(Opnd::mem(VALUE_BITS, val, RUBY_OFFSET_RBASIC_FLAGS));
- let mask = RUBY_T_MASK.to_usize();
- let expected = RUBY_T_DATA.to_usize();
- let masked = asm.and(flags, mask.into());
- asm.cmp(masked, expected.into());
- asm.jne(jit, side);
} else if let Some(builtin_type) = guard_type.builtin_type_equivalent() {
let side = side_exit(jit, state, GuardType(guard_type));
@@ -3098,7 +3079,7 @@ c_callable! {
// If we side-exit from function_stub_hit (before JIT code runs), we need to set them here.
fn prepare_for_exit(iseq: IseqPtr, cfp: CfpPtr, sp: *mut VALUE, argc: u16, num_opts_filled: u16, compile_error: &CompileError) {
unsafe {
- // Caller frames are materialized by jit_exec() after the entry trampoline returns.
+ // Caller frames are materialized by the materialize_exit trampoline before unwinding native frames.
// The current frame's pc and iseq are already set by function_stub_hit before this point.
// Set SP which gen_push_frame() doesn't set
@@ -3186,7 +3167,7 @@ c_callable! {
unsafe { Rc::increment_strong_count(iseq_call_ptr as *const IseqCall); }
prepare_for_exit(iseq, cfp, sp, argc, num_opts_filled, compile_error);
- return ZJITState::get_exit_trampoline_with_counter().raw_ptr(cb);
+ return ZJITState::get_materialize_exit_trampoline_with_counter().raw_ptr(cb);
}
// Otherwise, attempt to compile the ISEQ. We have to mark_all_executable() beyond this point.
@@ -3201,7 +3182,7 @@ c_callable! {
unsafe { Rc::increment_strong_count(iseq_call_ptr as *const IseqCall); }
prepare_for_exit(iseq, cfp, sp, argc, num_opts_filled, &compile_error);
- ZJITState::get_exit_trampoline_with_counter()
+ ZJITState::get_materialize_exit_trampoline_with_counter()
});
cb.mark_all_executable();
code_ptr.raw_ptr(cb)
@@ -3332,14 +3313,34 @@ pub fn gen_exit_trampoline(cb: &mut CodeBlock) -> Result<CodePtr, CompileError>
})
}
-/// Generate a trampoline that increments exit_compilation_failure and jumps to exit_trampoline.
-pub fn gen_exit_trampoline_with_counter(cb: &mut CodeBlock, exit_trampoline: CodePtr) -> Result<CodePtr, CompileError> {
+/// Generate a trampoline that materializes ZJIT frames before unwinding native frames.
+pub fn gen_materialize_exit_trampoline(cb: &mut CodeBlock, exit_trampoline: CodePtr) -> Result<CodePtr, CompileError> {
+ unsafe extern "C" {
+ fn rb_zjit_materialize_frames(ec: EcPtr, cfp: CfpPtr);
+ }
+
let mut asm = Assembler::new();
- asm.new_block_without_id("exit_trampoline_with_counter");
+ asm.new_block_without_id("materialize_exit_trampoline");
+
+ asm_comment!(asm, "materialize ZJIT frames");
+ asm.store(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_JIT_RETURN), 0.into());
+ asm_ccall!(asm, rb_zjit_materialize_frames, EC, CFP);
+ asm.jmp(Target::CodePtr(exit_trampoline));
+
+ asm.compile(cb).map(|(code_ptr, gc_offsets)| {
+ assert_eq!(gc_offsets.len(), 0);
+ code_ptr
+ })
+}
+
+/// Generate a trampoline that increments exit_compilation_failure and jumps to materialize_exit_trampoline.
+pub fn gen_materialize_exit_trampoline_with_counter(cb: &mut CodeBlock, materialize_exit_trampoline: CodePtr) -> Result<CodePtr, CompileError> {
+ let mut asm = Assembler::new();
+ asm.new_block_without_id("materialize_exit_trampoline_with_counter");
asm_comment!(asm, "function stub exit trampoline");
gen_incr_counter(&mut asm, exit_compile_error);
- asm.jmp(Target::CodePtr(exit_trampoline));
+ asm.jmp(Target::CodePtr(materialize_exit_trampoline));
asm.compile(cb).map(|(code_ptr, gc_offsets)| {
assert_eq!(gc_offsets.len(), 0);
diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs
index dad29087be..08c502b0d8 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;
@@ -1492,8 +1491,13 @@ pub const SHAPE_ID_HEAP_INDEX_MASK: shape_id_fl_type = 7864320;
pub const SHAPE_ID_FL_COMPLEX: shape_id_fl_type = 8388608;
pub const SHAPE_ID_FL_FROZEN: shape_id_fl_type = 16777216;
pub const SHAPE_ID_FL_HAS_OBJECT_ID: shape_id_fl_type = 33554432;
+pub const SHAPE_ID_LAYOUT_ROBJECT: shape_id_fl_type = 0;
+pub const SHAPE_ID_LAYOUT_RCLASS: shape_id_fl_type = 67108864;
+pub const SHAPE_ID_LAYOUT_RDATA: shape_id_fl_type = 134217728;
+pub const SHAPE_ID_LAYOUT_OTHER: shape_id_fl_type = 201326592;
+pub const SHAPE_ID_LAYOUT_MASK: shape_id_fl_type = 201326592;
pub const SHAPE_ID_FL_NON_CANONICAL_MASK: shape_id_fl_type = 50331648;
-pub const SHAPE_ID_FLAGS_MASK: shape_id_fl_type = 66584576;
+pub const SHAPE_ID_FLAGS_MASK: shape_id_fl_type = 267911168;
pub type shape_id_fl_type = u32;
pub const CONST_DEPRECATED: rb_const_flag_t = 256;
pub const CONST_VISIBILITY_MASK: rb_const_flag_t = 255;
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}")?;
}
diff --git a/zjit/src/hir_type/mod.rs b/zjit/src/hir_type/mod.rs
index 6aa10dea55..d7327975ce 100644
--- a/zjit/src/hir_type/mod.rs
+++ b/zjit/src/hir_type/mod.rs
@@ -444,8 +444,9 @@ impl Type {
Some(cruby::RUBY_T_STRING)
} else if self.bit_equal(types::Hash) {
Some(cruby::RUBY_T_HASH)
+ } else if self.bit_equal(types::TData) {
+ Some(cruby::RUBY_T_DATA)
} else {
- // T_DATA uses a specialized guard, so not here.
None
}
}
diff --git a/zjit/src/state.rs b/zjit/src/state.rs
index 2dbc2140e1..da09d09314 100644
--- a/zjit/src/state.rs
+++ b/zjit/src/state.rs
@@ -1,6 +1,6 @@
//! Runtime state of ZJIT.
-use crate::codegen::{gen_entry_trampoline, gen_exit_trampoline, gen_exit_trampoline_with_counter, gen_function_stub_hit_trampoline};
+use crate::codegen::{gen_entry_trampoline, gen_exit_trampoline, gen_function_stub_hit_trampoline, gen_materialize_exit_trampoline, gen_materialize_exit_trampoline_with_counter};
use crate::cruby::{self, rb_bug_panic_hook, rb_vm_insn_count, src_loc, EcPtr, Qnil, Qtrue, rb_profile_frames, rb_profile_frame_full_label, rb_profile_frame_absolute_path, rb_profile_frame_path, VALUE, VM_INSTRUCTION_SIZE, with_vm_lock, rust_str_to_id, rb_funcallv, rb_const_get, rb_cRubyVM};
use crate::cruby_methods;
use cruby::{ID, rb_callable_method_entry, get_def_method_serial, rb_gc_register_mark_object, ruby_str_to_rust_string_result};
@@ -51,8 +51,11 @@ pub struct ZJITState {
/// Trampoline to side-exit without restoring PC or the stack
exit_trampoline: CodePtr,
- /// Trampoline to side-exit and increment exit_compilation_failure
- exit_trampoline_with_counter: CodePtr,
+ /// Trampoline to materialize JIT frames before side-exiting
+ materialize_exit_trampoline: CodePtr,
+
+ /// Trampoline to materialize JIT frames and increment exit_compilation_failure
+ materialize_exit_trampoline_with_counter: CodePtr,
/// Trampoline to call function_stub_hit
function_stub_hit_trampoline: CodePtr,
@@ -126,6 +129,7 @@ impl ZJITState {
let entry_trampoline = gen_entry_trampoline(&mut cb).unwrap().raw_ptr(&cb);
let exit_trampoline = gen_exit_trampoline(&mut cb).unwrap();
+ let materialize_exit_trampoline = gen_materialize_exit_trampoline(&mut cb, exit_trampoline).unwrap();
let function_stub_hit_trampoline = gen_function_stub_hit_trampoline(&mut cb).unwrap();
let perfetto_tracer = if get_option!(trace_side_exits).is_some() || get_option!(trace_compiles) || get_option!(trace_invalidation) {
@@ -144,8 +148,9 @@ impl ZJITState {
assert_compiles: false,
method_annotations,
exit_trampoline,
+ materialize_exit_trampoline,
+ materialize_exit_trampoline_with_counter: materialize_exit_trampoline,
function_stub_hit_trampoline,
- exit_trampoline_with_counter: exit_trampoline,
full_frame_cfunc_counter_pointers: HashMap::new(),
not_annotated_frame_cfunc_counter_pointers: HashMap::new(),
ccall_counter_pointers: HashMap::new(),
@@ -160,8 +165,8 @@ impl ZJITState {
// on the counter, so ZJIT_STATE needs to be initialized first.
if get_option!(stats) {
let cb = ZJITState::get_code_block();
- let code_ptr = gen_exit_trampoline_with_counter(cb, exit_trampoline).unwrap();
- ZJITState::get_instance().exit_trampoline_with_counter = code_ptr;
+ let code_ptr = gen_materialize_exit_trampoline_with_counter(cb, materialize_exit_trampoline).unwrap();
+ ZJITState::get_instance().materialize_exit_trampoline_with_counter = code_ptr;
}
entry_trampoline
@@ -288,9 +293,14 @@ impl ZJITState {
ZJITState::get_instance().exit_trampoline
}
- /// Return a code pointer to the exit trampoline for function stubs
- pub fn get_exit_trampoline_with_counter() -> CodePtr {
- ZJITState::get_instance().exit_trampoline_with_counter
+ /// Return a code pointer to the materialize_exit trampoline
+ pub fn get_materialize_exit_trampoline() -> CodePtr {
+ ZJITState::get_instance().materialize_exit_trampoline
+ }
+
+ /// Return a code pointer to the materialize_exit trampoline for function stubs
+ pub fn get_materialize_exit_trampoline_with_counter() -> CodePtr {
+ ZJITState::get_instance().materialize_exit_trampoline_with_counter
}
/// Return a code pointer to the function stub hit trampoline