Skip to content

Commit 3fdb483

Browse files
committed
Merge remote-tracking branch 'origin/ruby_4_0' into v4.0.7-pshopify3
* origin/ruby_4_0: (50 commits) [Backport #22415] Restore ec->errinfo after handling interrupts Remove discard qualifiers warning with C23 strchr Reset the fork lock in the child without unlocking it Join getaddrinfo and getnameinfo threads before fork Defer unloading box-local extension DLLs on Windows [Backport #22426] ZJIT: Snapshot FrameState with reordered args before direct send [Bug #22416] Respect timezone abbreviation encodings in strftime Fix buffer overflow in Array#values_at [Bug #22284] Test curry with an arity that responds to to_int [Bug #22284] Let NUM2INT handle Fixnum arity of curry [Bug #22284] Fix curry with a non-Fixnum arity on LLP64 [Bug #22388] Fix use-after-free in `Set#&` [Bug #22388] Fix use-after-free in `Enumerator::Lazy#flat_map` [Bug #22388] Fix use-after-free in `Enumerable#sum` [Bug #22388] Fix use-after-free in some `Enumerable` methods [Bug #22384] Fix memory leak on compile error in a later branch of an alternation io: preserve errno across `rb_io_wait()` call. (ruby#19121) Fix IO#gets ignoring the limit argument Use Kernel without test/unit in the classext GC stress test Attach a box classext to its class before filling it in ...
2 parents 92f8ccf + ac12199 commit 3fdb483

80 files changed

Lines changed: 1705 additions & 232 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/macos.yml‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ jobs:
4747
os: macos-15
4848
- test_task: check
4949
os: macos-15-intel
50-
- test_task: check
51-
os: macos-14
5250
fail-fast: false
5351

5452
env:
@@ -194,7 +192,7 @@ jobs:
194192
result:
195193
if: ${{ always() }}
196194
name: ${{ github.workflow }} result
197-
runs-on: macos-latest
195+
runs-on: ubuntu-latest
198196
needs: [make]
199197
steps:
200198
- run: exit 1

‎.github/workflows/tarball-macos.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
# default-gem stub.
3939
matrix:
4040
test_task: ${{ inputs.full-matrix && fromJSON('["check", "test-bundled-gems", "test-bundler-parallel"]') || fromJSON('["check"]') }}
41-
os: ${{ inputs.full-matrix && fromJSON('["macos-26", "macos-15", "macos-14"]') || fromJSON('["macos-26"]') }}
41+
os: ${{ inputs.full-matrix && fromJSON('["macos-26", "macos-15"]') || fromJSON('["macos-26"]') }}
4242
include: ${{ inputs.full-matrix && fromJSON('[{"os":"macos-15-intel", "test_task":"check", "prefix":"/tmp/ruby-prefix"}]') || fromJSON('[]') }}
4343
fail-fast: false
4444
runs-on: ${{ matrix.os }}

‎.github/workflows/yjit-macos.yml‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
cargo:
3131
name: cargo test
3232

33-
runs-on: macos-14
33+
runs-on: macos-15
3434

3535
if: >-
3636
${{!(false
@@ -72,7 +72,7 @@ jobs:
7272
RUN_OPTS: ${{ matrix.yjit_opts }}
7373
SPECOPTS: ${{ matrix.specopts }}
7474

75-
runs-on: macos-14
75+
runs-on: macos-15
7676

7777
if: >-
7878
${{!(false
@@ -131,7 +131,7 @@ jobs:
131131
id: launchable
132132
uses: ./.github/actions/launchable/setup
133133
with:
134-
os: macos-14
134+
os: macos-15
135135
test-opts: ${{ matrix.configure }}
136136
launchable-token: ${{ secrets.LAUNCHABLE_TOKEN }}
137137
builddir: build
@@ -185,7 +185,7 @@ jobs:
185185
result:
186186
if: ${{ always() }}
187187
name: ${{ github.workflow }} result
188-
runs-on: macos-14
188+
runs-on: ubuntu-latest
189189
needs: [make]
190190
steps:
191191
- name: ${{ github.workflow }} jobs have failed

‎.github/workflows/zjit-macos.yml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ jobs:
5757
TESTOPTS: ${{ matrix.testopts }}
5858
ZJIT_RB_BUG: 1
5959

60-
runs-on: macos-14
60+
runs-on: macos-15
6161

6262
if: >-
6363
${{!(false
@@ -144,7 +144,7 @@ jobs:
144144
result:
145145
if: ${{ always() }}
146146
name: ${{ github.workflow }} result
147-
runs-on: macos-14
147+
runs-on: ubuntu-latest
148148
needs: [make]
149149
steps:
150150
- run: exit 1
@@ -161,7 +161,7 @@ jobs:
161161
bench_opts: '--warmup=1 --bench=1'
162162
configure: '--enable-zjit=dev_nodebug' # --enable-zjit=dev is too slow
163163

164-
runs-on: macos-14
164+
runs-on: macos-15
165165

166166
if: >-
167167
${{!(false

‎array.c‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3834,6 +3834,9 @@ append_values_at_single(VALUE result, VALUE ary, long olen, VALUE idx)
38343834
/* check if idx is Range */
38353835
else if (rb_range_beg_len(idx, &beg, &len, olen, 1)) {
38363836
if (len > 0) {
3837+
// rb_range_beg_len may run arbitrary code that modifies ary, so we
3838+
// need to re-calculate olen
3839+
const long olen = RARRAY_LEN(ary);
38373840
const VALUE *const src = RARRAY_CONST_PTR(ary);
38383841
const long end = beg + len;
38393842
const long prevlen = RARRAY_LEN(result);
@@ -4715,7 +4718,7 @@ rb_ary_zip(int argc, VALUE *argv, VALUE ary)
47154718
else {
47164719
result = rb_ary_new_capa(len);
47174720

4718-
for (i=0; i<len; i++) {
4721+
for (i=0; i<RARRAY_LEN(ary); i++) {
47194722
VALUE tmp = rb_ary_new_capa(argc+1);
47204723

47214724
rb_ary_push(tmp, RARRAY_AREF(ary, i));
@@ -6629,7 +6632,7 @@ static VALUE
66296632
flatten(VALUE ary, int level)
66306633
{
66316634
long i;
6632-
VALUE stack, result, tmp = 0, elt;
6635+
VALUE stack, result, tmp = Qnil, elt;
66336636
VALUE memo = Qfalse;
66346637

66356638
for (i = 0; i < RARRAY_LEN(ary); i++) {
@@ -6639,9 +6642,14 @@ flatten(VALUE ary, int level)
66396642
break;
66406643
}
66416644
}
6642-
if (i == RARRAY_LEN(ary)) {
6645+
if (NIL_P(tmp)) {
66436646
return ary;
66446647
}
6648+
if (i > RARRAY_LEN(ary)) {
6649+
/* ary was shrunk while converting an element with #to_ary, so
6650+
the scanned elements may no longer exist in ary */
6651+
i = RARRAY_LEN(ary);
6652+
}
66456653

66466654
result = ary_new(0, RARRAY_LEN(ary));
66476655
ary_memcpy(result, 0, i, RARRAY_CONST_PTR(ary));

‎box.c‎

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ bool ruby_box_crashed = false; // extern, changed only in vm.c
6161

6262
VALUE rb_resolve_feature_path(VALUE klass, VALUE fname);
6363
static VALUE rb_box_inspect(VALUE obj);
64-
static void cleanup_all_local_extensions(VALUE libmap);
6564

6665
void
6766
rb_box_set_gem_flags(rb_box_gem_flags_t *flags)
@@ -273,6 +272,12 @@ free_classext_for_box(st_data_t key, st_data_t _value, st_data_t box_arg)
273272
VALUE obj = (VALUE)key;
274273
const rb_box_t *box = (const rb_box_t *)box_arg;
275274

275+
if (RB_TYPE_P(obj, T_NONE)) {
276+
/* RUBY_FREE_AT_EXIT can free a class before the box that refers to
277+
* it, and freeing a class already freed its classext for this box. */
278+
return ST_CONTINUE;
279+
}
280+
276281
if (RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE)) {
277282
ext = rb_class_unlink_classext(obj, box);
278283
rb_class_classext_free(obj, ext, false);
@@ -296,8 +301,6 @@ box_entry_free(void *ptr)
296301
st_foreach(box->classext_cow_classes, free_classext_for_box, (st_data_t)box);
297302
}
298303

299-
cleanup_all_local_extensions(box->ruby_dln_libmap);
300-
301304
free_box_st_tables(ptr);
302305
xfree((void *)box);
303306
}
@@ -798,24 +801,48 @@ rb_box_cleanup_local_extension(VALUE cleanup)
798801
(void)p;
799802
}
800803

801-
static int
802-
cleanup_local_extension_i(VALUE key, VALUE value, VALUE arg)
803-
{
804804
#if defined(_WIN32)
805-
HMODULE h = (HMODULE)NUM2PTR(value);
806-
WCHAR module_path[MAXPATHLEN];
807-
DWORD len = GetModuleFileNameW(h, module_path, numberof(module_path));
805+
struct box_local_ext_list {
806+
struct box_local_ext_list *next;
807+
HMODULE handle;
808+
};
809+
static struct box_local_ext_list *box_local_exts;
810+
#endif
808811

809-
FreeLibrary(h);
810-
if (len > 0 && len < numberof(module_path)) DeleteFileW(module_path);
812+
void
813+
rb_box_defer_unload_local_extension(void *handle)
814+
{
815+
#if defined(_WIN32)
816+
/* A box-local copy of an extension DLL must stay loaded as long as
817+
* objects created by it can be finalized; unloading is deferred to
818+
* rb_box_unload_local_extensions after the objspace is destructed.
819+
* The loaded copy cannot be deleted on Windows, so the file is also
820+
* removed there instead of in box_ext_cleanup_free. */
821+
struct box_local_ext_list *ext = malloc(sizeof(struct box_local_ext_list));
822+
if (!ext) return;
823+
ext->handle = (HMODULE)handle;
824+
ext->next = box_local_exts;
825+
box_local_exts = ext;
811826
#endif
812-
return ST_DELETE;
813827
}
814828

815-
static void
816-
cleanup_all_local_extensions(VALUE libmap)
829+
void
830+
rb_box_unload_local_extensions(void)
817831
{
818-
rb_hash_foreach(libmap, cleanup_local_extension_i, 0);
832+
#if defined(_WIN32)
833+
struct box_local_ext_list *ext = box_local_exts;
834+
box_local_exts = NULL;
835+
while (ext) {
836+
struct box_local_ext_list *next = ext->next;
837+
WCHAR module_path[MAXPATHLEN];
838+
DWORD len = GetModuleFileNameW(ext->handle, module_path, numberof(module_path));
839+
840+
FreeLibrary(ext->handle);
841+
if (len > 0 && len < numberof(module_path)) DeleteFileW(module_path);
842+
free(ext);
843+
ext = next;
844+
}
845+
#endif
819846
}
820847

821848
VALUE

‎class.c‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,9 @@ class_duplicate_iclass_classext(VALUE iclass, rb_classext_t *mod_ext, const rb_b
299299

300300
RCLASSEXT_BOX(ext) = box;
301301

302+
VM_ASSERT(FL_TEST_RAW(iclass, RCLASS_BOXABLE));
303+
first_set = RCLASS_SET_BOX_CLASSEXT(iclass, box, ext);
304+
302305
RCLASSEXT_SUPER(ext) = RCLASSEXT_SUPER(src);
303306

304307
// See also: rb_include_class_new()
@@ -324,16 +327,13 @@ class_duplicate_iclass_classext(VALUE iclass, rb_classext_t *mod_ext, const rb_b
324327

325328
RCLASSEXT_SET_INCLUDER(ext, iclass, RCLASSEXT_INCLUDER(src));
326329

327-
VM_ASSERT(FL_TEST_RAW(iclass, RCLASS_BOXABLE));
328-
329-
first_set = RCLASS_SET_BOX_CLASSEXT(iclass, box, ext);
330330
if (first_set) {
331331
RCLASS_SET_PRIME_CLASSEXT_WRITABLE(iclass, false);
332332
}
333333
}
334334

335335
rb_classext_t *
336-
rb_class_duplicate_classext(rb_classext_t *orig, VALUE klass, const rb_box_t *box)
336+
rb_class_duplicate_classext(rb_classext_t *orig, VALUE klass, const rb_box_t *box, int *first_set)
337337
{
338338
VM_ASSERT(RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_MODULE) || RB_TYPE_P(klass, T_ICLASS));
339339

@@ -342,6 +342,10 @@ rb_class_duplicate_classext(rb_classext_t *orig, VALUE klass, const rb_box_t *bo
342342

343343
RCLASSEXT_BOX(ext) = box;
344344

345+
/* Everything made below is reachable only through this classext, so put
346+
* it where the GC can find it before allocating any of it. */
347+
*first_set = RCLASS_SET_BOX_CLASSEXT(klass, box, ext);
348+
345349
RCLASSEXT_SUPER(ext) = RCLASSEXT_SUPER(orig);
346350

347351
RCLASSEXT_M_TBL(ext) = duplicate_classext_m_tbl(RCLASSEXT_M_TBL(orig), klass, dup_iclass);
@@ -1922,6 +1926,10 @@ ensure_origin(VALUE klass)
19221926
{
19231927
VALUE origin = RCLASS_ORIGIN(klass);
19241928
if (origin == klass) {
1929+
/* Create the box-local classext before reading m_tbl, so that the
1930+
* origin shares the m_tbl with the box-local iclasses of klass,
1931+
* as rb_prepend_module relies on that identity. */
1932+
rb_class_ensure_writable(klass);
19251933
origin = class_alloc(T_ICLASS, klass);
19261934
RCLASS_SET_M_TBL(origin, RCLASS_M_TBL(klass));
19271935
rb_class_set_super(origin, RCLASS_SUPER(klass));

‎common.mk‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ HTMLOUT = $(EXTOUT)/html
7575
CAPIOUT = doc/capi
7676
INSTALL_DOC_OPTS = --rdoc-output="$(RDOCOUT)" --html-output="$(HTMLOUT)"
7777
RDOC_GEN_OPTS = --no-force-update \
78+
--exclude '^lib/rubygems/core_ext/kernel_require\.rb$$' \
7879
$(empty)
7980

8081
INITOBJS = dmyext.$(OBJEXT) dmyenc.$(OBJEXT)

‎enum.c‎

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1940,11 +1940,11 @@ DEFINE_ENUMFUNCS(one)
19401940
}
19411941

19421942
struct nmin_data {
1943+
VALUE buf;
1944+
VALUE limit;
19431945
long n;
19441946
long bufmax;
19451947
long curlen;
1946-
VALUE buf;
1947-
VALUE limit;
19481948
int (*cmpfunc)(const void *, const void *, void *);
19491949
int rev: 1; /* max if 1 */
19501950
int by: 1; /* min_by if 1 */
@@ -2065,13 +2065,10 @@ nmin_filter(struct nmin_data *data)
20652065
}
20662066

20672067
static VALUE
2068-
nmin_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _data))
2068+
nmin_i_ary(VALUE i, struct nmin_data *data, int argc)
20692069
{
2070-
struct nmin_data *data = (struct nmin_data *)_data;
20712070
VALUE cmpv;
20722071

2073-
ENUM_WANT_SVALUE();
2074-
20752072
if (data->by)
20762073
cmpv = enum_yield(argc, i);
20772074
else
@@ -2098,6 +2095,14 @@ nmin_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _data))
20982095
return Qnil;
20992096
}
21002097

2098+
static VALUE
2099+
nmin_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, data))
2100+
{
2101+
ENUM_WANT_SVALUE();
2102+
2103+
return nmin_i_ary(i, MEMO_FOR(struct nmin_data, data), argc);
2104+
}
2105+
21012106
VALUE
21022107
rb_nmin_run(VALUE obj, VALUE num, int by, int rev, int ary)
21032108
{
@@ -2120,17 +2125,24 @@ rb_nmin_run(VALUE obj, VALUE num, int by, int rev, int ary)
21202125
nmin_cmp;
21212126
data.rev = rev;
21222127
data.by = by;
2128+
2129+
VALUE memo;
2130+
struct nmin_data *m = &data;
2131+
if (!ary || data.cmpfunc == nmin_block_cmp) {
2132+
*(m = NEW_PARTIAL_MEMO_FOR(struct nmin_data, memo, n)) = data;
2133+
}
21232134
if (ary) {
21242135
long i;
21252136
for (i = 0; i < RARRAY_LEN(obj); i++) {
2126-
VALUE args[1];
2127-
args[0] = RARRAY_AREF(obj, i);
2128-
nmin_i(obj, (VALUE)&data, 1, args, Qundef);
2137+
nmin_i_ary(RARRAY_AREF(obj, i), m, 1);
21292138
}
21302139
}
21312140
else {
2132-
rb_block_call(obj, id_each, 0, 0, nmin_i, (VALUE)&data);
2141+
rb_block_call(obj, id_each, 0, 0, nmin_i, memo);
21332142
}
2143+
if (m != &data) data = *m;
2144+
RB_GC_GUARD(memo);
2145+
21342146
nmin_filter(&data);
21352147
result = data.buf;
21362148
if (by) {
@@ -4744,7 +4756,7 @@ static VALUE
47444756
enum_sum_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
47454757
{
47464758
ENUM_WANT_SVALUE();
4747-
sum_iter(i, (struct enum_sum_memo *) args);
4759+
sum_iter(i, MEMO_FOR(struct enum_sum_memo, args));
47484760
return Qnil;
47494761
}
47504762

@@ -4846,10 +4858,17 @@ enum_sum(int argc, VALUE* argv, VALUE obj)
48464858
}
48474859

48484860
if (RB_TYPE_P(obj, T_HASH) &&
4849-
rb_method_basic_definition_p(CLASS_OF(obj), id_each))
4861+
rb_method_basic_definition_p(CLASS_OF(obj), id_each)) {
48504862
hash_sum(obj, &memo);
4851-
else
4852-
rb_block_call(obj, id_each, 0, 0, enum_sum_i, (VALUE)&memo);
4863+
}
4864+
else {
4865+
VALUE arg;
4866+
struct enum_sum_memo *m = NEW_PARTIAL_MEMO_FOR(struct enum_sum_memo, arg, n);
4867+
*m = memo;
4868+
rb_block_call(obj, id_each, 0, 0, enum_sum_i, arg);
4869+
memo = *m;
4870+
RB_GC_GUARD(arg);
4871+
}
48534872

48544873
if (memo.float_value) {
48554874
return DBL2NUM(memo.f + memo.c);

0 commit comments

Comments
 (0)