diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index 8c3d16fbc9c120..5c639ad48bdf38 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -207,7 +207,7 @@ jobs: - name: test timeout-minutes: 30 - run: make test + run: make test test-tool env: GNUMAKEFLAGS: '' RUBY_TESTOPTS: '-v --tty=no' diff --git a/NEWS.md b/NEWS.md index e37e4ebf5b435a..02fef64f4cb5e5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -47,7 +47,7 @@ releases. ### The following bundled gems are updated. * minitest 6.0.1 -* test-unit 3.7.6 +* test-unit 3.7.7 * rss 0.3.2 * net-imap 0.6.2 * typeprof 0.31.1 diff --git a/encoding.c b/encoding.c index 749cbd586d00be..8bb393b471ed54 100644 --- a/encoding.c +++ b/encoding.c @@ -125,8 +125,9 @@ static const rb_data_type_t encoding_data_type = { 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED }; -#define is_data_encoding(obj) (RTYPEDDATA_P(obj) && RTYPEDDATA_TYPE(obj) == &encoding_data_type) -#define is_obj_encoding(obj) (RB_TYPE_P((obj), T_DATA) && is_data_encoding(obj)) +#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_obj_encoding(obj) (rbimpl_obj_typeddata_p(obj) && is_encoding_type(obj)) int rb_data_is_encoding(VALUE obj) @@ -1345,7 +1346,7 @@ enc_inspect(VALUE self) { rb_encoding *enc; - if (!is_data_encoding(self)) { + if (!is_obj_encoding(self)) { /* do not resolve autoload */ not_encoding(self); } if (!(enc = RTYPEDDATA_GET_DATA(self)) || rb_enc_from_index(rb_enc_to_index(enc)) != enc) { diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index 8f9729ef28a7fb..0ac16918f876b9 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -477,23 +477,24 @@ static const signed char digit_values[256] = { -1, -1, -1, -1, -1, -1, -1 }; -static uint32_t unescape_unicode(JSON_ParserState *state, const unsigned char *p) +static uint32_t unescape_unicode(JSON_ParserState *state, const char *sp, const char *spe) { - signed char b; - uint32_t result = 0; - b = digit_values[p[0]]; - if (b < 0) raise_parse_error_at("incomplete unicode character escape sequence at %s", state, (char *)p - 2); - result = (result << 4) | (unsigned char)b; - b = digit_values[p[1]]; - if (b < 0) raise_parse_error_at("incomplete unicode character escape sequence at %s", state, (char *)p - 2); - result = (result << 4) | (unsigned char)b; - b = digit_values[p[2]]; - if (b < 0) raise_parse_error_at("incomplete unicode character escape sequence at %s", state, (char *)p - 2); - result = (result << 4) | (unsigned char)b; - b = digit_values[p[3]]; - if (b < 0) raise_parse_error_at("incomplete unicode character escape sequence at %s", state, (char *)p - 2); - result = (result << 4) | (unsigned char)b; - return result; + if (RB_UNLIKELY(sp > spe - 4)) { + raise_parse_error_at("incomplete unicode character escape sequence at %s", state, sp - 2); + } + + const unsigned char *p = (const unsigned char *)sp; + + const signed char b0 = digit_values[p[0]]; + const signed char b1 = digit_values[p[1]]; + const signed char b2 = digit_values[p[2]]; + const signed char b3 = digit_values[p[3]]; + + if (RB_UNLIKELY((signed char)(b0 | b1 | b2 | b3) < 0)) { + raise_parse_error_at("incomplete unicode character escape sequence at %s", state, sp - 2); + } + + return ((uint32_t)b0 << 12) | ((uint32_t)b1 << 8) | ((uint32_t)b2 << 4) | (uint32_t)b3; } #define GET_PARSER_CONFIG \ @@ -643,7 +644,7 @@ static inline VALUE json_string_fastpath(JSON_ParserState *state, JSON_ParserCon typedef struct _json_unescape_positions { long size; const char **positions; - bool has_more; + unsigned long additional_backslashes; } JSON_UnescapePositions; static inline const char *json_next_backslash(const char *pe, const char *stringEnd, JSON_UnescapePositions *positions) @@ -657,7 +658,8 @@ static inline const char *json_next_backslash(const char *pe, const char *string } } - if (positions->has_more) { + if (positions->additional_backslashes) { + positions->additional_backslashes--; return memchr(pe, '\\', stringEnd - pe); } @@ -707,50 +709,43 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser case 'f': APPEND_CHAR('\f'); break; - case 'u': - if (pe > stringEnd - 5) { - raise_parse_error_at("incomplete unicode character escape sequence at %s", state, p); - } else { - uint32_t ch = unescape_unicode(state, (unsigned char *) ++pe); - pe += 3; - /* To handle values above U+FFFF, we take a sequence of - * \uXXXX escapes in the U+D800..U+DBFF then - * U+DC00..U+DFFF ranges, take the low 10 bits from each - * to make a 20-bit number, then add 0x10000 to get the - * final codepoint. - * - * See Unicode 15: 3.8 "Surrogates", 5.3 "Handling - * Surrogate Pairs in UTF-16", and 23.6 "Surrogates - * Area". - */ - if ((ch & 0xFC00) == 0xD800) { - pe++; - if (pe > stringEnd - 6) { - raise_parse_error_at("incomplete surrogate pair at %s", state, p); - } - if (pe[0] == '\\' && pe[1] == 'u') { - uint32_t sur = unescape_unicode(state, (unsigned char *) pe + 2); - - if ((sur & 0xFC00) != 0xDC00) { - raise_parse_error_at("invalid surrogate pair at %s", state, p); - } - - ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16) - | (sur & 0x3FF)); - pe += 5; - } else { - raise_parse_error_at("incomplete surrogate pair at %s", state, p); - break; + case 'u': { + uint32_t ch = unescape_unicode(state, ++pe, stringEnd); + pe += 3; + /* To handle values above U+FFFF, we take a sequence of + * \uXXXX escapes in the U+D800..U+DBFF then + * U+DC00..U+DFFF ranges, take the low 10 bits from each + * to make a 20-bit number, then add 0x10000 to get the + * final codepoint. + * + * See Unicode 15: 3.8 "Surrogates", 5.3 "Handling + * Surrogate Pairs in UTF-16", and 23.6 "Surrogates + * Area". + */ + if ((ch & 0xFC00) == 0xD800) { + pe++; + if (RB_LIKELY((pe <= stringEnd - 6) && memcmp(pe, "\\u", 2) == 0)) { + uint32_t sur = unescape_unicode(state, pe + 2, stringEnd); + + if (RB_UNLIKELY((sur & 0xFC00) != 0xDC00)) { + raise_parse_error_at("invalid surrogate pair at %s", state, p); } - } - char buf[4]; - int unescape_len = convert_UTF32_to_UTF8(buf, ch); - MEMCPY(buffer, buf, char, unescape_len); - buffer += unescape_len; - p = ++pe; + ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16) | (sur & 0x3FF)); + pe += 5; + } else { + raise_parse_error_at("incomplete surrogate pair at %s", state, p); + break; + } } + + char buf[4]; + int unescape_len = convert_UTF32_to_UTF8(buf, ch); + MEMCPY(buffer, buf, char, unescape_len); + buffer += unescape_len; + p = ++pe; break; + } default: if ((unsigned char)*pe < 0x20) { if (!config->allow_control_characters) { @@ -992,7 +987,7 @@ static VALUE json_parse_escaped_string(JSON_ParserState *state, JSON_ParserConfi JSON_UnescapePositions positions = { .size = 0, .positions = backslashes, - .has_more = false, + .additional_backslashes = 0, }; do { @@ -1007,7 +1002,7 @@ static VALUE json_parse_escaped_string(JSON_ParserState *state, JSON_ParserConfi backslashes[positions.size] = state->cursor; positions.size++; } else { - positions.has_more = true; + positions.additional_backslashes++; } state->cursor++; break; diff --git a/gc.c b/gc.c index 8a18a129786f33..f77ee417c52dea 100644 --- a/gc.c +++ b/gc.c @@ -1096,6 +1096,10 @@ rb_data_object_zalloc(VALUE klass, size_t size, RUBY_DATA_FUNC dmark, RUBY_DATA_ 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)) + static VALUE typed_data_alloc(VALUE klass, VALUE typed_flag, void *datap, const rb_data_type_t *type, size_t size) { @@ -1117,7 +1121,7 @@ typed_data_alloc(VALUE klass, VALUE typed_flag, void *datap, const rb_data_type_ VALUE rb_data_typed_object_wrap(VALUE klass, void *datap, const rb_data_type_t *type) { - if (UNLIKELY(type->flags & RUBY_TYPED_EMBEDDABLE)) { + if (UNLIKELY(RB_DATA_TYPE_EMBEDDABLE_P(type))) { rb_raise(rb_eTypeError, "Cannot wrap an embeddable TypedData"); } @@ -1127,7 +1131,7 @@ rb_data_typed_object_wrap(VALUE klass, void *datap, const rb_data_type_t *type) VALUE rb_data_typed_object_zalloc(VALUE klass, size_t size, const rb_data_type_t *type) { - if (type->flags & RUBY_TYPED_EMBEDDABLE) { + if (RB_DATA_TYPE_EMBEDDABLE_P(type)) { if (!(type->flags & RUBY_TYPED_FREE_IMMEDIATELY)) { rb_raise(rb_eTypeError, "Embeddable TypedData must be freed immediately"); } @@ -1153,7 +1157,7 @@ rb_objspace_data_type_memsize(VALUE obj) const rb_data_type_t *type = RTYPEDDATA_TYPE(obj); const void *ptr = RTYPEDDATA_GET_DATA(obj); - if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !RTYPEDDATA_EMBEDDED_P(obj)) { + if (RTYPEDDATA_EMBEDDABLE_P(obj) && !RTYPEDDATA_EMBEDDED_P(obj)) { #ifdef HAVE_MALLOC_USABLE_SIZE size += malloc_usable_size((void *)ptr); #endif @@ -1285,7 +1289,7 @@ rb_data_free(void *objspace, VALUE obj) } else if (free_immediately) { (*dfree)(data); - if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !RTYPEDDATA_EMBEDDED_P(obj)) { + if (RTYPEDDATA_EMBEDDABLE_P(obj) && !RTYPEDDATA_EMBEDDED_P(obj)) { xfree(data); } diff --git a/gc/mmtk/mmtk.c b/gc/mmtk/mmtk.c index d69a5cda2df2ae..b532d3a774cca1 100644 --- a/gc/mmtk/mmtk.c +++ b/gc/mmtk/mmtk.c @@ -549,7 +549,7 @@ rb_gc_impl_init(void) rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_OVERHEAD")), INT2NUM(0)); rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVARGC_MAX_ALLOCATE_SIZE")), LONG2FIX(MMTK_MAX_OBJ_SIZE)); // Pretend we have 5 size pools - rb_hash_aset(gc_constants, ID2SYM(rb_intern("SIZE_POOL_COUNT")), LONG2FIX(5)); + rb_hash_aset(gc_constants, ID2SYM(rb_intern("SIZE_POOL_COUNT")), LONG2FIX(MMTK_HEAP_COUNT)); // TODO: correctly set RVALUE_OLD_AGE when we have generational GC support rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_OLD_AGE")), INT2FIX(0)); OBJ_FREEZE(gc_constants); diff --git a/gc/mmtk/src/abi.rs b/gc/mmtk/src/abi.rs index 1d7dc62a8753b4..1e03dbf2f98809 100644 --- a/gc/mmtk/src/abi.rs +++ b/gc/mmtk/src/abi.rs @@ -1,8 +1,12 @@ use crate::api::RubyMutator; -use crate::{extra_assert, Ruby}; +use crate::extra_assert; +use crate::Ruby; use libc::c_int; use mmtk::scheduler::GCWorker; -use mmtk::util::{Address, ObjectReference, VMMutatorThread, VMWorkerThread}; +use mmtk::util::Address; +use mmtk::util::ObjectReference; +use mmtk::util::VMMutatorThread; +use mmtk::util::VMWorkerThread; // For the C binding pub const OBJREF_OFFSET: usize = 8; diff --git a/gc/mmtk/src/collection.rs b/gc/mmtk/src/collection.rs index 0b1221204c1468..83d046aef43092 100644 --- a/gc/mmtk/src/collection.rs +++ b/gc/mmtk/src/collection.rs @@ -2,12 +2,17 @@ use crate::abi::GCThreadTLS; use crate::api::RubyMutator; use crate::heap::RubyHeapTrigger; -use crate::{mmtk, upcalls, Ruby}; +use crate::mmtk; +use crate::upcalls; +use crate::Ruby; use mmtk::memory_manager; use mmtk::scheduler::*; use mmtk::util::heap::GCTriggerPolicy; -use mmtk::util::{VMMutatorThread, VMThread, VMWorkerThread}; -use mmtk::vm::{Collection, GCThreadContext}; +use mmtk::util::VMMutatorThread; +use mmtk::util::VMThread; +use mmtk::util::VMWorkerThread; +use mmtk::vm::Collection; +use mmtk::vm::GCThreadContext; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; use std::thread; diff --git a/gc/mmtk/src/heap/ruby_heap_trigger.rs b/gc/mmtk/src/heap/ruby_heap_trigger.rs index 9215e2ebb0ec04..fe1130043d55f7 100644 --- a/gc/mmtk/src/heap/ruby_heap_trigger.rs +++ b/gc/mmtk/src/heap/ruby_heap_trigger.rs @@ -1,4 +1,5 @@ -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; use mmtk::util::heap::GCTriggerPolicy; use mmtk::util::heap::SpaceStats; diff --git a/gc/mmtk/src/lib.rs b/gc/mmtk/src/lib.rs index 0da6121883747e..0ee8f6752e5778 100644 --- a/gc/mmtk/src/lib.rs +++ b/gc/mmtk/src/lib.rs @@ -14,8 +14,11 @@ use std::sync::Mutex; use std::thread::ThreadId; use abi::RubyUpcalls; -use binding::{RubyBinding, RubyBindingFast, RubyConfiguration}; -use mmtk::vm::slot::{SimpleSlot, UnimplementedMemorySlice}; +use binding::RubyBinding; +use binding::RubyBindingFast; +use binding::RubyConfiguration; +use mmtk::vm::slot::SimpleSlot; +use mmtk::vm::slot::UnimplementedMemorySlice; use mmtk::vm::VMBinding; use mmtk::MMTK; use once_cell::sync::OnceCell; diff --git a/gc/mmtk/src/object_model.rs b/gc/mmtk/src/object_model.rs index dd27f83612cb9a..d673ca11a0a31d 100644 --- a/gc/mmtk/src/object_model.rs +++ b/gc/mmtk/src/object_model.rs @@ -1,10 +1,15 @@ use std::ptr::copy_nonoverlapping; -use crate::abi::{RubyObjectAccess, MIN_OBJ_ALIGN, OBJREF_OFFSET}; -use crate::{abi, Ruby}; +use crate::abi; +use crate::abi::RubyObjectAccess; +use crate::abi::MIN_OBJ_ALIGN; +use crate::abi::OBJREF_OFFSET; +use crate::Ruby; use mmtk::util::constants::BITS_IN_BYTE; -use mmtk::util::copy::{CopySemantics, GCWorkerCopyContext}; -use mmtk::util::{Address, ObjectReference}; +use mmtk::util::copy::CopySemantics; +use mmtk::util::copy::GCWorkerCopyContext; +use mmtk::util::Address; +use mmtk::util::ObjectReference; use mmtk::vm::*; pub struct VMObjectModel {} diff --git a/gc/mmtk/src/pinning_registry.rs b/gc/mmtk/src/pinning_registry.rs index 7f67bbeef5e4a3..71cf73eaf913b4 100644 --- a/gc/mmtk/src/pinning_registry.rs +++ b/gc/mmtk/src/pinning_registry.rs @@ -1,13 +1,16 @@ use std::sync::Mutex; -use mmtk::{ - memory_manager, - scheduler::{GCWork, GCWorker, WorkBucketStage}, - util::{ObjectReference, VMWorkerThread}, - MMTK, -}; - -use crate::{abi::GCThreadTLS, upcalls, Ruby}; +use mmtk::memory_manager; +use mmtk::scheduler::GCWork; +use mmtk::scheduler::GCWorker; +use mmtk::scheduler::WorkBucketStage; +use mmtk::util::ObjectReference; +use mmtk::util::VMWorkerThread; +use mmtk::MMTK; + +use crate::abi::GCThreadTLS; +use crate::upcalls; +use crate::Ruby; pub struct PinningRegistry { pinning_objs: Mutex>, diff --git a/gc/mmtk/src/scanning.rs b/gc/mmtk/src/scanning.rs index eca4769e2f9d15..be834bdd0bde95 100644 --- a/gc/mmtk/src/scanning.rs +++ b/gc/mmtk/src/scanning.rs @@ -1,10 +1,18 @@ use crate::abi::GCThreadTLS; +use crate::upcalls; use crate::utils::ChunkedVecCollector; -use crate::{upcalls, Ruby, RubySlot}; -use mmtk::scheduler::{GCWork, GCWorker, WorkBucketStage}; -use mmtk::util::{ObjectReference, VMWorkerThread}; -use mmtk::vm::{ObjectTracer, RootsWorkFactory, Scanning, SlotVisitor}; +use crate::Ruby; +use crate::RubySlot; +use mmtk::scheduler::GCWork; +use mmtk::scheduler::GCWorker; +use mmtk::scheduler::WorkBucketStage; +use mmtk::util::ObjectReference; +use mmtk::util::VMWorkerThread; +use mmtk::vm::ObjectTracer; +use mmtk::vm::RootsWorkFactory; +use mmtk::vm::Scanning; +use mmtk::vm::SlotVisitor; use mmtk::Mutator; pub struct VMScanning {} diff --git a/gc/mmtk/src/utils.rs b/gc/mmtk/src/utils.rs index 71a7ae8dd25f60..d1979eaf58da4e 100644 --- a/gc/mmtk/src/utils.rs +++ b/gc/mmtk/src/utils.rs @@ -1,7 +1,10 @@ -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; use atomic_refcell::AtomicRefCell; -use mmtk::scheduler::{GCWork, GCWorker, WorkBucketStage}; +use mmtk::scheduler::GCWork; +use mmtk::scheduler::GCWorker; +use mmtk::scheduler::WorkBucketStage; use crate::Ruby; use sysinfo::System; diff --git a/gc/mmtk/src/weak_proc.rs b/gc/mmtk/src/weak_proc.rs index 3184c4f6d183f2..c8ad944633bd3a 100644 --- a/gc/mmtk/src/weak_proc.rs +++ b/gc/mmtk/src/weak_proc.rs @@ -1,12 +1,14 @@ use std::sync::Mutex; -use mmtk::{ - scheduler::{GCWork, GCWorker, WorkBucketStage}, - util::ObjectReference, - vm::ObjectTracerContext, -}; - -use crate::{abi::GCThreadTLS, upcalls, Ruby}; +use mmtk::scheduler::GCWork; +use mmtk::scheduler::GCWorker; +use mmtk::scheduler::WorkBucketStage; +use mmtk::util::ObjectReference; +use mmtk::vm::ObjectTracerContext; + +use crate::abi::GCThreadTLS; +use crate::upcalls; +use crate::Ruby; pub struct WeakProcessor { /// Objects that needs `obj_free` called when dying. diff --git a/gems/bundled_gems b/gems/bundled_gems index f4afd8e9c27ac9..fcf63ab603ac6b 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -9,7 +9,7 @@ minitest 6.0.1 https://github.com/minitest/minitest power_assert 3.0.1 https://github.com/ruby/power_assert rake 13.3.1 https://github.com/ruby/rake -test-unit 3.7.6 https://github.com/test-unit/test-unit +test-unit 3.7.7 https://github.com/test-unit/test-unit rexml 3.4.4 https://github.com/ruby/rexml rss 0.3.2 https://github.com/ruby/rss net-ftp 0.3.9 https://github.com/ruby/net-ftp diff --git a/include/ruby/internal/core/rtypeddata.h b/include/ruby/internal/core/rtypeddata.h index 1dd7397f7d335c..aadccc238ba1fc 100644 --- a/include/ruby/internal/core/rtypeddata.h +++ b/include/ruby/internal/core/rtypeddata.h @@ -109,14 +109,17 @@ /** @cond INTERNAL_MACRO */ #define RTYPEDDATA_P RTYPEDDATA_P #define RTYPEDDATA_TYPE RTYPEDDATA_TYPE +#define TYPED_DATA_EMBEDDED ((VALUE)1) +#define TYPED_DATA_PTR_MASK (~(TYPED_DATA_EMBEDDED)) +/** @endcond */ + +/** + * Macros to see if each corresponding flag is defined. + */ #define RUBY_TYPED_FREE_IMMEDIATELY RUBY_TYPED_FREE_IMMEDIATELY #define RUBY_TYPED_FROZEN_SHAREABLE RUBY_TYPED_FROZEN_SHAREABLE #define RUBY_TYPED_WB_PROTECTED RUBY_TYPED_WB_PROTECTED #define RUBY_TYPED_PROMOTED1 RUBY_TYPED_PROMOTED1 -/** @endcond */ - -#define TYPED_DATA_EMBEDDED ((VALUE)1) -#define TYPED_DATA_PTR_MASK (~(TYPED_DATA_EMBEDDED)) /** * @private @@ -482,6 +485,17 @@ RBIMPL_ATTR_NONNULL(()) void rb_unexpected_typeddata(const rb_data_type_t *actual, const rb_data_type_t *expected); RBIMPL_SYMBOL_EXPORT_END() +#if RUBY_DEBUG +# define RBIMPL_TYPEDDATA_PRECONDITION(obj, unreachable) \ + while (RB_UNLIKELY(!RB_TYPE_P(obj, RUBY_T_DATA))) { \ + rb_unexpected_object_type(obj, "Data"); \ + unreachable; \ + } +#else +# define RBIMPL_TYPEDDATA_PRECONDITION(obj, unreachable) \ + RBIMPL_ASSERT_NOTHING +#endif + /** * Converts sval, a pointer to your struct, into a Ruby object. * @@ -510,7 +524,7 @@ RBIMPL_SYMBOL_EXPORT_END() */ #define TypedData_Make_Struct0(result, klass, type, size, data_type, sval) \ VALUE result = rb_data_typed_object_zalloc(klass, size, data_type); \ - (sval) = RBIMPL_CAST((type *)RTYPEDDATA_GET_DATA(result)); \ + (sval) = RBIMPL_CAST((type *)rbimpl_typeddata_get_data(result)); \ RBIMPL_CAST(/*suppress unused variable warnings*/(void)(sval)) /** @@ -547,33 +561,36 @@ RBIMPL_SYMBOL_EXPORT_END() sizeof(type)) #endif +static inline bool +rbimpl_typeddata_embedded_p(VALUE obj) +{ + return (RTYPEDDATA(obj)->type) & TYPED_DATA_EMBEDDED; +} + +RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY() static inline bool RTYPEDDATA_EMBEDDED_P(VALUE obj) { -#if RUBY_DEBUG - if (RB_UNLIKELY(!RB_TYPE_P(obj, RUBY_T_DATA))) { - Check_Type(obj, RUBY_T_DATA); - RBIMPL_UNREACHABLE_RETURN(false); - } -#endif + RBIMPL_TYPEDDATA_PRECONDITION(obj, RBIMPL_UNREACHABLE_RETURN(false)); - return (RTYPEDDATA(obj)->type) & TYPED_DATA_EMBEDDED; + return rbimpl_typeddata_embedded_p(obj); +} + +static inline void * +rbimpl_typeddata_get_data(VALUE obj) +{ + /* We reuse the data pointer in embedded TypedData. */ + return rbimpl_typeddata_embedded_p(obj) ? + RBIMPL_CAST((void *)&RTYPEDDATA_DATA(obj)) : + RTYPEDDATA_DATA(obj); } static inline void * RTYPEDDATA_GET_DATA(VALUE obj) { -#if RUBY_DEBUG - if (RB_UNLIKELY(!RB_TYPE_P(obj, RUBY_T_DATA))) { - Check_Type(obj, RUBY_T_DATA); - RBIMPL_UNREACHABLE_RETURN(NULL); - } -#endif + RBIMPL_TYPEDDATA_PRECONDITION(obj, RBIMPL_UNREACHABLE_RETURN(NULL)); - /* We reuse the data pointer in embedded TypedData. */ - return RTYPEDDATA_EMBEDDED_P(obj) ? - RBIMPL_CAST((void *)&(RTYPEDDATA(obj)->data)) : - RTYPEDDATA(obj)->data; + return rbimpl_typeddata_get_data(obj); } RBIMPL_ATTR_PURE() @@ -629,12 +646,7 @@ RBIMPL_ATTR_ARTIFICIAL() static inline bool RTYPEDDATA_P(VALUE obj) { -#if RUBY_DEBUG - if (RB_UNLIKELY(! RB_TYPE_P(obj, RUBY_T_DATA))) { - Check_Type(obj, RUBY_T_DATA); - RBIMPL_UNREACHABLE_RETURN(false); - } -#endif + RBIMPL_TYPEDDATA_PRECONDITION(obj, RBIMPL_UNREACHABLE_RETURN(false)); return rbimpl_rtypeddata_p(obj); } @@ -652,12 +664,7 @@ RBIMPL_ATTR_RETURNS_NONNULL() static inline const rb_data_type_t * RTYPEDDATA_TYPE(VALUE obj) { -#if RUBY_DEBUG - if (RB_UNLIKELY(! RTYPEDDATA_P(obj))) { - rb_unexpected_type(obj, RUBY_T_DATA); - RBIMPL_UNREACHABLE_RETURN(NULL); - } -#endif + RBIMPL_TYPEDDATA_PRECONDITION(obj, RBIMPL_UNREACHABLE_RETURN(NULL)); VALUE type = RTYPEDDATA(obj)->type & TYPED_DATA_PTR_MASK; const rb_data_type_t *ptr = RBIMPL_CAST((const rb_data_type_t *)type); diff --git a/prism/templates/lib/prism/node.rb.erb b/prism/templates/lib/prism/node.rb.erb index c97c029d3bf410..066a0cea1b508f 100644 --- a/prism/templates/lib/prism/node.rb.erb +++ b/prism/templates/lib/prism/node.rb.erb @@ -353,7 +353,7 @@ module Prism <%- when Prism::Template::OptionalNodeField -%> yield <%= field.name %> if <%= field.name %> <%- when Prism::Template::NodeListField -%> - <%= field.name %>.each {|node| yield node } + <%= field.name %>.each { |node| yield node } <%- end -%> <%- end -%> end diff --git a/string.c b/string.c index b6c6d3f6268caf..1f2a14f681e6d2 100644 --- a/string.c +++ b/string.c @@ -1960,8 +1960,8 @@ str_duplicate_setup_heap(VALUE klass, VALUE str, VALUE dup) RUBY_ASSERT(RB_OBJ_FROZEN_RAW(root)); RSTRING(dup)->as.heap.ptr = RSTRING_PTR(str); - FL_SET(root, STR_SHARED_ROOT); - RB_OBJ_WRITE(dup, &RSTRING(dup)->as.heap.aux.shared, root); + FL_SET_RAW(dup, RSTRING_NOEMBED); + STR_SET_SHARED(dup, root); flags |= RSTRING_NOEMBED | STR_SHARED; STR_SET_LEN(dup, RSTRING_LEN(str)); diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb index ec9391909d779d..ac53ba9f0c872f 100644 --- a/test/json/json_parser_test.rb +++ b/test/json/json_parser_test.rb @@ -350,6 +350,7 @@ def test_invalid_surogates assert_raise(JSON::ParserError) { parse('"\\uD800"') } assert_raise(JSON::ParserError) { parse('"\\uD800_________________"') } assert_raise(JSON::ParserError) { parse('"\\uD800\\u0041"') } + assert_raise(JSON::ParserError) { parse('"\\uD800\\u004') } end def test_parse_big_integers diff --git a/test/ruby/test_box.rb b/test/ruby/test_box.rb index 763f6148cb5483..bb98a2efbe57ca 100644 --- a/test/ruby/test_box.rb +++ b/test/ruby/test_box.rb @@ -28,15 +28,15 @@ def test_box_availability_in_default assert_separately(['RUBY_BOX'=>nil], __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true) begin; assert_nil ENV['RUBY_BOX'] - assert !Ruby::Box.enabled? + assert_not_predicate Ruby::Box, :enabled? end; end def test_box_availability_when_enabled assert_separately([ENV_ENABLE_BOX], __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true) begin; - assert '1', ENV['RUBY_BOX'] - assert Ruby::Box.enabled? + assert_equal '1', ENV['RUBY_BOX'] + assert_predicate Ruby::Box, :enabled? end; end @@ -44,7 +44,7 @@ def test_current_box_in_main assert_separately([ENV_ENABLE_BOX], __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true) begin; assert_equal Ruby::Box.main, Ruby::Box.current - assert Ruby::Box.main.main? + assert_predicate Ruby::Box.main, :main? end; end @@ -152,7 +152,7 @@ def test_raising_errors_in_require setup_box assert_raise(RuntimeError, "Yay!") { @box.require(File.join(__dir__, 'box', 'raise')) } - assert Ruby::Box.current.inspect.include?("main") + assert_include Ruby::Box.current.inspect, "main" end def test_autoload_in_box @@ -514,7 +514,7 @@ def test_global_variables assert_equal nil, $, # used only in box - assert !global_variables.include?(:$used_only_in_box) + assert_not_include? global_variables, :$used_only_in_box @box::UniqueGvar.write(123) assert_equal 123, @box::UniqueGvar.read assert_nil $used_only_in_box @@ -535,7 +535,7 @@ def test_global_variables def test_load_path_and_loaded_features setup_box - assert $LOAD_PATH.respond_to?(:resolve_feature_path) + assert_respond_to $LOAD_PATH, :resolve_feature_path @box.require_relative('box/load_path') @@ -545,13 +545,13 @@ def test_load_path_and_loaded_features box_dir = File.join(__dir__, 'box') # TODO: $LOADED_FEATURES in method calls should refer the current box in addition to the loading box. - # assert @box::LoadPathCheck.current_loaded_features.include?(File.join(box_dir, 'blank1.rb')) - # assert !@box::LoadPathCheck.current_loaded_features.include?(File.join(box_dir, 'blank2.rb')) - # assert @box::LoadPathCheck.require_blank2 - # assert @box::LoadPathCheck.current_loaded_features.include?(File.join(box_dir, 'blank2.rb')) + # assert_include @box::LoadPathCheck.current_loaded_features, File.join(box_dir, 'blank1.rb') + # assert_not_include @box::LoadPathCheck.current_loaded_features, File.join(box_dir, 'blank2.rb') + # assert_predicate @box::LoadPathCheck, :require_blank2 + # assert_include(@box::LoadPathCheck.current_loaded_features, File.join(box_dir, 'blank2.rb')) - assert !$LOADED_FEATURES.include?(File.join(box_dir, 'blank1.rb')) - assert !$LOADED_FEATURES.include?(File.join(box_dir, 'blank2.rb')) + assert_not_include $LOADED_FEATURES, File.join(box_dir, 'blank1.rb') + assert_not_include $LOADED_FEATURES, File.join(box_dir, 'blank2.rb') end def test_eval_basic @@ -690,23 +690,23 @@ def test_root_and_main_methods begin; pend unless Ruby::Box.respond_to?(:root) and Ruby::Box.respond_to?(:main) # for RUBY_DEBUG > 0 - assert Ruby::Box.root.respond_to?(:root?) - assert Ruby::Box.main.respond_to?(:main?) + assert_respond_to Ruby::Box.root, :root? + assert_respond_to Ruby::Box.main, :main? - assert Ruby::Box.root.root? - assert Ruby::Box.main.main? + assert_predicate Ruby::Box.root, :root? + assert_predicate Ruby::Box.main, :main? assert_equal Ruby::Box.main, Ruby::Box.current $a = 1 $LOADED_FEATURES.push("/tmp/foobar") assert_equal 2, Ruby::Box.root.eval('$a = 2; $a') - assert !Ruby::Box.root.eval('$LOADED_FEATURES.push("/tmp/barbaz"); $LOADED_FEATURES.include?("/tmp/foobar")') - assert "FooClass", Ruby::Box.root.eval('class FooClass; end; Object.const_get(:FooClass).to_s') + assert_not_include Ruby::Box.root.eval('$LOADED_FEATURES.push("/tmp/barbaz"); $LOADED_FEATURES'), "/tmp/foobar" + assert_equal "FooClass", Ruby::Box.root.eval('class FooClass; end; Object.const_get(:FooClass).to_s') assert_equal 1, $a - assert !$LOADED_FEATURES.include?("/tmp/barbaz") - assert !Object.const_defined?(:FooClass) + assert_not_include $LOADED_FEATURES, "/tmp/barbaz" + assert_not_operator Object, :const_defined?, :FooClass end; end diff --git a/test/ruby/test_encoding.rb b/test/ruby/test_encoding.rb index 5c1eb50bb13786..0cd5bf49dc19f7 100644 --- a/test/ruby/test_encoding.rb +++ b/test/ruby/test_encoding.rb @@ -154,7 +154,7 @@ def test_ractor_lazy_load_encoding_concurrently r, _obj = Ractor.select(*rs) rs.delete(r) end - assert rs.empty? + assert_empty rs end; end @@ -173,7 +173,7 @@ def test_ractor_set_default_external_string r, _obj = Ractor.select(*rs) rs.delete(r) end - assert rs.empty? + assert_empty rs end; end end diff --git a/test/ruby/test_env.rb b/test/ruby/test_env.rb index 2727620c198522..d17e300bceb2f1 100644 --- a/test/ruby/test_env.rb +++ b/test/ruby/test_env.rb @@ -1379,23 +1379,24 @@ def test_frozen_in_ractor Ractor.new port = Ractor::Port.new do |port| ENV["#{PATH_ENV}"] = "/" ENV.each do |k, v| - port.send [k.frozen?] - port.send [v.frozen?] + port.send [k] + port.send [v] end ENV.each_key do |k| - port.send [k.frozen?] + port.send [k] end ENV.each_value do |v| - port.send [v.frozen?] + port.send [v] end ENV.each_key do |k| - port.send [ENV[k].frozen?, "[\#{k.dump}]"] - port.send [ENV.fetch(k).frozen?, "fetch(\#{k.dump})"] + port.send [ENV[k], "[\#{k.dump}]"] + port.send [ENV.fetch(k), "fetch(\#{k.dump})"] end port.send "finished" end while((params=port.receive) != "finished") - assert(*params) + value, *params = params + assert_predicate(value, :frozen?, *params) end end; end diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb index 17ff5a2e82996b..31e5aa9f6b9505 100644 --- a/test/ruby/test_exception.rb +++ b/test/ruby/test_exception.rb @@ -992,7 +992,7 @@ def test_output_string_encoding assert_equal 1, outs.size assert_equal 0, errs.size err = outs.first.force_encoding('utf-8') - assert err.valid_encoding?, 'must be valid encoding' + assert_predicate err, :valid_encoding? assert_match %r/\u3042/, err end end diff --git a/test/ruby/test_gc_compact.rb b/test/ruby/test_gc_compact.rb index a03535171c42e8..bc7692d644fba9 100644 --- a/test/ruby/test_gc_compact.rb +++ b/test/ruby/test_gc_compact.rb @@ -30,7 +30,7 @@ class AutoCompact < Test::Unit::TestCase def test_enable_autocompact before = GC.auto_compact GC.auto_compact = true - assert GC.auto_compact + assert_predicate GC, :auto_compact ensure GC.auto_compact = before end @@ -151,12 +151,12 @@ def test_ast_compacts def walk_ast ast children = ast.children.grep(RubyVM::AbstractSyntaxTree::Node) children.each do |child| - assert child.type + assert_predicate child, :type walk_ast child end end ast = RubyVM::AbstractSyntaxTree.parse_file #{__FILE__.dump} - assert GC.compact + assert_predicate GC, :compact walk_ast ast end; end diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb index e996fc39b88eb2..706ce16c42a547 100644 --- a/test/ruby/test_io_buffer.rb +++ b/test/ruby/test_io_buffer.rb @@ -46,22 +46,22 @@ def test_default_size def test_new_internal buffer = IO::Buffer.new(1024, IO::Buffer::INTERNAL) assert_equal 1024, buffer.size - refute buffer.external? - assert buffer.internal? - refute buffer.mapped? + refute_predicate buffer, :external? + assert_predicate buffer, :internal? + refute_predicate buffer, :mapped? end def test_new_mapped buffer = IO::Buffer.new(1024, IO::Buffer::MAPPED) assert_equal 1024, buffer.size - refute buffer.external? - refute buffer.internal? - assert buffer.mapped? + refute_predicate buffer, :external? + refute_predicate buffer, :internal? + assert_predicate buffer, :mapped? end def test_new_readonly buffer = IO::Buffer.new(128, IO::Buffer::INTERNAL|IO::Buffer::READONLY) - assert buffer.readonly? + assert_predicate buffer, :readonly? assert_raise IO::Buffer::AccessError do buffer.set_string("") @@ -141,19 +141,19 @@ def test_file_mapped_invalid def test_string_mapped string = "Hello World" buffer = IO::Buffer.for(string) - assert buffer.readonly? + assert_predicate buffer, :readonly? end def test_string_mapped_frozen string = "Hello World".freeze buffer = IO::Buffer.for(string) - assert buffer.readonly? + assert_predicate buffer, :readonly? end def test_string_mapped_mutable string = "Hello World" IO::Buffer.for(string) do |buffer| - refute buffer.readonly? + refute_predicate buffer, :readonly? buffer.set_value(:U8, 0, "h".ord) @@ -715,8 +715,8 @@ def test_private buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::PRIVATE) begin - assert buffer.private? - refute buffer.readonly? + assert_predicate buffer, :private? + refute_predicate buffer, :readonly? buffer.set_string("J") diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb index 30a7c5d9bc1c22..3db60dec8f3b43 100644 --- a/test/ruby/test_module.rb +++ b/test/ruby/test_module.rb @@ -9,18 +9,18 @@ def _wrap_assertion yield end - def assert_method_defined?(klass, mid, message="") + def assert_method_defined?(klass, (mid, *args), message="") message = build_message(message, "#{klass}\##{mid} expected to be defined.") _wrap_assertion do - klass.method_defined?(mid) or + klass.method_defined?(mid, *args) or raise Test::Unit::AssertionFailedError, message, caller(3) end end - def assert_method_not_defined?(klass, mid, message="") + def assert_method_not_defined?(klass, (mid, *args), message="") message = build_message(message, "#{klass}\##{mid} expected to not be defined.") _wrap_assertion do - klass.method_defined?(mid) and + klass.method_defined?(mid, *args) and raise Test::Unit::AssertionFailedError, message, caller(3) end end @@ -813,40 +813,40 @@ def test_instance_methods def test_method_defined? [User, Class.new{include User}, Class.new{prepend User}].each do |klass| [[], [true]].each do |args| - assert !klass.method_defined?(:wombat, *args) - assert klass.method_defined?(:mixin, *args) - assert klass.method_defined?(:user, *args) - assert klass.method_defined?(:user2, *args) - assert !klass.method_defined?(:user3, *args) + assert_method_not_defined?(klass, [:wombat, *args]) + assert_method_defined?(klass, [:mixin, *args]) + assert_method_defined?(klass, [:user, *args]) + assert_method_defined?(klass, [:user2, *args]) + assert_method_not_defined?(klass, [:user3, *args]) - assert !klass.method_defined?("wombat", *args) - assert klass.method_defined?("mixin", *args) - assert klass.method_defined?("user", *args) - assert klass.method_defined?("user2", *args) - assert !klass.method_defined?("user3", *args) + assert_method_not_defined?(klass, ["wombat", *args]) + assert_method_defined?(klass, ["mixin", *args]) + assert_method_defined?(klass, ["user", *args]) + assert_method_defined?(klass, ["user2", *args]) + assert_method_not_defined?(klass, ["user3", *args]) end end end def test_method_defined_without_include_super - assert User.method_defined?(:user, false) - assert !User.method_defined?(:mixin, false) - assert Mixin.method_defined?(:mixin, false) + assert_method_defined?(User, [:user, false]) + assert_method_not_defined?(User, [:mixin, false]) + assert_method_defined?(Mixin, [:mixin, false]) User.const_set(:FOO, c = Class.new) c.prepend(User) - assert !c.method_defined?(:user, false) + assert_method_not_defined?(c, [:user, false]) c.define_method(:user){} - assert c.method_defined?(:user, false) + assert_method_defined?(c, [:user, false]) - assert !c.method_defined?(:mixin, false) + assert_method_not_defined?(c, [:mixin, false]) c.define_method(:mixin){} - assert c.method_defined?(:mixin, false) + assert_method_defined?(c, [:mixin, false]) - assert !c.method_defined?(:userx, false) + assert_method_not_defined?(c, [:userx, false]) c.define_method(:userx){} - assert c.method_defined?(:userx, false) + assert_method_defined?(c, [:userx, false]) # cleanup User.class_eval do diff --git a/test/ruby/test_nomethod_error.rb b/test/ruby/test_nomethod_error.rb index aa2a88b2d8f3fb..6abd20cc81bae5 100644 --- a/test/ruby/test_nomethod_error.rb +++ b/test/ruby/test_nomethod_error.rb @@ -78,7 +78,7 @@ def test_new_receiver assert_equal :foo, error.name assert_equal [1, 2], error.args assert_equal receiver, error.receiver - assert error.private_call?, "private_call? was false." + assert_predicate error, :private_call? end def test_message_encoding diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb index 9fa4dad41e2a86..def41d60175eb5 100644 --- a/test/ruby/test_parse.rb +++ b/test/ruby/test_parse.rb @@ -1588,7 +1588,7 @@ def test_shareable_constant_value_simple assert_ractor_shareable(a) assert_not_ractor_shareable(obj) assert_equal obj, a - assert !obj.equal?(a) + assert_not_same obj, a bug_20339 = '[ruby-core:117186] [Bug #20339]' bug_20341 = '[ruby-core:117197] [Bug #20341]' diff --git a/test/ruby/test_signal.rb b/test/ruby/test_signal.rb index a2bdf02b88522f..661ba031413ba8 100644 --- a/test/ruby/test_signal.rb +++ b/test/ruby/test_signal.rb @@ -320,15 +320,15 @@ def test_self_stop # The parent should be notified about the stop _, status = Process.waitpid2(child_pid, Process::WUNTRACED) - assert status.stopped? + assert_predicate status, :stopped? # It can be continued Process.kill(:CONT, child_pid) # And the child then runs to completion _, status = Process.waitpid2(child_pid) - assert status.exited? - assert status.success? + assert_predicate status, :exited? + assert_predicate status, :success? end def test_sigwait_fd_unused diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb index 3796e12f083feb..8edebfb5c219da 100644 --- a/test/ruby/test_thread.rb +++ b/test/ruby/test_thread.rb @@ -1480,7 +1480,7 @@ def test_thread_native_thread_id_across_fork_on_linux end def test_thread_interrupt_for_killed_thread - pend "hang-up" if RUBY_PLATFORM.include?("mingw") + pend "hang-up" if /mswin|mingw/ =~ RUBY_PLATFORM opts = { timeout: 5, timeout_error: nil } diff --git a/test/ruby/test_thread_queue.rb b/test/ruby/test_thread_queue.rb index 9485528977e599..9a41be8b1ac1a3 100644 --- a/test/ruby/test_thread_queue.rb +++ b/test/ruby/test_thread_queue.rb @@ -379,7 +379,7 @@ def test_close assert_equal false, q.closed? q << :something assert_equal q, q.close - assert q.closed? + assert_predicate q, :closed? assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing} assert_equal q.pop, :something assert_nil q.pop @@ -433,7 +433,7 @@ def test_sized_queue_one_closed_interrupt assert_equal 1, q.size assert_equal :one, q.pop - assert q.empty?, "queue not empty" + assert_empty q end # make sure that shutdown state is handled properly by empty? for the non-blocking case @@ -567,7 +567,7 @@ def test_blocked_pushers_empty assert_equal 0, q.size assert_equal 3, ary.size - ary.each{|e| assert [0,1,2,3,4,5].include?(e)} + ary.each{|e| assert_include [0,1,2,3,4,5], e} assert_nil q.pop prod_threads.each{|t| diff --git a/test/ruby/test_transcode.rb b/test/ruby/test_transcode.rb index 15e290728beadc..99b5ee8d43da94 100644 --- a/test/ruby/test_transcode.rb +++ b/test/ruby/test_transcode.rb @@ -2338,7 +2338,7 @@ def test_ractor_lazy_load_encoding r, _obj = Ractor.select(*rs) rs.delete(r) end - assert rs.empty? + assert_empty rs end; end @@ -2357,7 +2357,7 @@ def test_ractor_lazy_load_encoding_random r, _obj = Ractor.select(*rs) rs.delete(r) end - assert rs.empty? + assert_empty rs end; end @@ -2380,7 +2380,7 @@ def test_ractor_asciicompat_encoding_exists r, _obj = Ractor.select(*rs) rs.delete(r) end - assert rs.empty? + assert_empty rs end; end @@ -2403,7 +2403,7 @@ def test_ractor_asciicompat_encoding_doesnt_exist r, _obj = Ractor.select(*rs) rs.delete(r) end - assert rs.empty? + assert_empty rs end; end diff --git a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock index 6302e9ee37b60a..a5051607c15957 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock +++ b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock @@ -152,18 +152,18 @@ dependencies = [ [[package]] name = "rb-sys" -version = "0.9.117" +version = "0.9.123" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f900d1ce4629a2ebffaf5de74bd8f9c1188d4c5ed406df02f97e22f77a006f44" +checksum = "45fb1a185af97ee456f1c9e56dbe6e2e662bec4fdeaf83c4c28e0e6adfb18816" dependencies = [ "rb-sys-build", ] [[package]] name = "rb-sys-build" -version = "0.9.117" +version = "0.9.123" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1e9c857028f631056bcd6d88cec390c751e343ce2223ddb26d23eb4a151d59" +checksum = "a58ebd02d7a6033e6a5f6f8d150c1e9f16506039092b84a73e6bedce6d3adf41" dependencies = [ "bindgen", "lazy_static", diff --git a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml index e26fa352a81156..42ac3497a66b14 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml +++ b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -rb-sys = "0.9.117" +rb-sys = "0.9.123" diff --git a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock index 4bbe8932a2b8a4..efc85ffd5fcbf0 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock +++ b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock @@ -145,18 +145,18 @@ dependencies = [ [[package]] name = "rb-sys" -version = "0.9.117" +version = "0.9.123" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f900d1ce4629a2ebffaf5de74bd8f9c1188d4c5ed406df02f97e22f77a006f44" +checksum = "45fb1a185af97ee456f1c9e56dbe6e2e662bec4fdeaf83c4c28e0e6adfb18816" dependencies = [ "rb-sys-build", ] [[package]] name = "rb-sys-build" -version = "0.9.117" +version = "0.9.123" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1e9c857028f631056bcd6d88cec390c751e343ce2223ddb26d23eb4a151d59" +checksum = "a58ebd02d7a6033e6a5f6f8d150c1e9f16506039092b84a73e6bedce6d3adf41" dependencies = [ "bindgen", "lazy_static", diff --git a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml index 55d5c5bde7f645..6972bb51750cdf 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml +++ b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -rb-sys = "0.9.117" +rb-sys = "0.9.123" diff --git a/thread_sync.c b/thread_sync.c index a93888fad02ae6..72341762399a8f 100644 --- a/thread_sync.c +++ b/thread_sync.c @@ -691,47 +691,57 @@ rb_mutex_allow_trap(VALUE self, int val) /* Queue */ -#define queue_waitq(q) UNALIGNED_MEMBER_PTR(q, waitq) -#define queue_list(q) UNALIGNED_MEMBER_PTR(q, que) -RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_BEGIN() struct rb_queue { struct ccan_list_head waitq; rb_serial_t fork_gen; - const VALUE que; + long capa; + long len; + long offset; + VALUE *buffer; int num_waiting; -} RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_END(); +}; + +#define szqueue_waitq(sq) &sq->q.waitq +#define szqueue_pushq(sq) &sq->pushq -#define szqueue_waitq(sq) UNALIGNED_MEMBER_PTR(sq, q.waitq) -#define szqueue_list(sq) UNALIGNED_MEMBER_PTR(sq, q.que) -#define szqueue_pushq(sq) UNALIGNED_MEMBER_PTR(sq, pushq) -RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_BEGIN() struct rb_szqueue { struct rb_queue q; int num_waiting_push; struct ccan_list_head pushq; long max; -} RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_END(); +}; static void queue_mark_and_move(void *ptr) { struct rb_queue *q = ptr; - /* no need to mark threads in waitq, they are on stack */ - rb_gc_mark_and_move((VALUE *)UNALIGNED_MEMBER_PTR(q, que)); + for (long index = 0; index < q->len; index++) { + rb_gc_mark_and_move(&q->buffer[((q->offset + index) % q->capa)]); + } +} + +static void +queue_free(void *ptr) +{ + struct rb_queue *q = ptr; + if (q->buffer) { + ruby_sized_xfree(q->buffer, q->capa * sizeof(VALUE)); + } } static size_t queue_memsize(const void *ptr) { - return sizeof(struct rb_queue); + const struct rb_queue *q = ptr; + return sizeof(struct rb_queue) + (q->capa * sizeof(VALUE)); } static const rb_data_type_t queue_data_type = { .wrap_struct_name = "Thread::Queue", .function = { .dmark = queue_mark_and_move, - .dfree = RUBY_TYPED_DEFAULT_FREE, + .dfree = queue_free, .dsize = queue_memsize, .dcompact = queue_mark_and_move, }, @@ -745,7 +755,7 @@ queue_alloc(VALUE klass) struct rb_queue *q; obj = TypedData_Make_Struct(klass, struct rb_queue, &queue_data_type, q); - ccan_list_head_init(queue_waitq(q)); + ccan_list_head_init(&q->waitq); return obj; } @@ -759,13 +769,13 @@ queue_fork_check(struct rb_queue *q) } /* forked children can't reach into parent thread stacks */ q->fork_gen = fork_gen; - ccan_list_head_init(queue_waitq(q)); + ccan_list_head_init(&q->waitq); q->num_waiting = 0; return 1; } -static struct rb_queue * -queue_ptr(VALUE obj) +static inline struct rb_queue * +raw_queue_ptr(VALUE obj) { struct rb_queue *q; @@ -775,6 +785,22 @@ queue_ptr(VALUE obj) return q; } +static inline void +check_queue(VALUE obj, struct rb_queue *q) +{ + if (RB_UNLIKELY(q->buffer == NULL)) { + rb_raise(rb_eTypeError, "%+"PRIsVALUE" not initialized", obj); + } +} + +static inline struct rb_queue * +queue_ptr(VALUE obj) +{ + struct rb_queue *q = raw_queue_ptr(obj); + check_queue(obj, q); + return q; +} + #define QUEUE_CLOSED FL_USER5 static rb_hrtime_t @@ -801,17 +827,25 @@ szqueue_mark_and_move(void *ptr) queue_mark_and_move(&sq->q); } +static void +szqueue_free(void *ptr) +{ + struct rb_szqueue *sq = ptr; + queue_free(&sq->q); +} + static size_t szqueue_memsize(const void *ptr) { - return sizeof(struct rb_szqueue); + const struct rb_szqueue *sq = ptr; + return sizeof(struct rb_szqueue) + (sq->q.capa * sizeof(VALUE)); } static const rb_data_type_t szqueue_data_type = { .wrap_struct_name = "Thread::SizedQueue", .function = { .dmark = szqueue_mark_and_move, - .dfree = RUBY_TYPED_DEFAULT_FREE, + .dfree = szqueue_free, .dsize = szqueue_memsize, .dcompact = szqueue_mark_and_move, }, @@ -830,8 +864,8 @@ szqueue_alloc(VALUE klass) return obj; } -static struct rb_szqueue * -szqueue_ptr(VALUE obj) +static inline struct rb_szqueue * +raw_szqueue_ptr(VALUE obj) { struct rb_szqueue *sq; @@ -844,25 +878,12 @@ szqueue_ptr(VALUE obj) return sq; } -static VALUE -ary_buf_new(void) -{ - return rb_ary_hidden_new(1); -} - -static inline VALUE -check_array(VALUE obj, VALUE ary) -{ - if (RB_LIKELY(ary)) { - return ary; - } - rb_raise(rb_eTypeError, "%+"PRIsVALUE" not initialized", obj); -} - -static long -queue_length(VALUE self, struct rb_queue *q) +static inline struct rb_szqueue * +szqueue_ptr(VALUE obj) { - return RARRAY_LEN(check_array(self, q->que)); + struct rb_szqueue *sq = raw_szqueue_ptr(obj); + check_queue(obj, &sq->q); + return sq; } static int @@ -889,10 +910,63 @@ raise_closed_queue_error(VALUE self) static VALUE queue_closed_result(VALUE self, struct rb_queue *q) { - RUBY_ASSERT(queue_length(self, q) == 0); + RUBY_ASSERT(q->len == 0); return Qnil; } +#define QUEUE_INITIAL_CAPA 8 + +static inline void +ring_buffer_init(struct rb_queue *q, long initial_capa) +{ + q->buffer = ALLOC_N(VALUE, initial_capa); + q->capa = initial_capa; +} + +static inline void +ring_buffer_expand(struct rb_queue *q) +{ + RUBY_ASSERT(q->capa > 0); + VALUE *new_buffer = ALLOC_N(VALUE, q->capa * 2); + MEMCPY(new_buffer, q->buffer + q->offset, VALUE, q->capa - q->offset); + MEMCPY(new_buffer + (q->capa - q->offset), q->buffer, VALUE, q->offset); + VALUE *old_buffer = q->buffer; + q->buffer = new_buffer; + q->offset = 0; + ruby_sized_xfree(old_buffer, q->capa * sizeof(VALUE)); + q->capa *= 2; +} + +static void +ring_buffer_push(VALUE self, struct rb_queue *q, VALUE obj) +{ + if (RB_UNLIKELY(q->len >= q->capa)) { + ring_buffer_expand(q); + } + RUBY_ASSERT(q->capa > q->len); + long index = (q->offset + q->len) % q->capa; + q->len++; + RB_OBJ_WRITE(self, &q->buffer[index], obj); +} + +static VALUE +ring_buffer_shift(struct rb_queue *q) +{ + if (!q->len) { + return Qnil; + } + + VALUE obj = q->buffer[q->offset]; + q->len--; + if (q->len == 0) { + q->offset = 0; + } + else { + q->offset = (q->offset + 1) % q->capa; + } + return obj; +} + /* * Document-class: Thread::Queue * @@ -957,14 +1031,27 @@ static VALUE rb_queue_initialize(int argc, VALUE *argv, VALUE self) { VALUE initial; - struct rb_queue *q = queue_ptr(self); + struct rb_queue *q = raw_queue_ptr(self); if ((argc = rb_scan_args(argc, argv, "01", &initial)) == 1) { initial = rb_to_array(initial); } - RB_OBJ_WRITE(self, queue_list(q), ary_buf_new()); - ccan_list_head_init(queue_waitq(q)); + ccan_list_head_init(&q->waitq); if (argc == 1) { - rb_ary_concat(q->que, initial); + long len = RARRAY_LEN(initial); + long initial_capa = QUEUE_INITIAL_CAPA; + while (initial_capa < len) { + initial_capa *= 2; + } + ring_buffer_init(q, initial_capa); + + const VALUE *initial_ptr = RARRAY_CONST_PTR(initial); + + for (long index = 0; index < len; index++) { + ring_buffer_push(self, q, initial_ptr[index]); + } + } + else { + ring_buffer_init(q, QUEUE_INITIAL_CAPA); } return self; } @@ -972,11 +1059,12 @@ rb_queue_initialize(int argc, VALUE *argv, VALUE self) static VALUE queue_do_push(VALUE self, struct rb_queue *q, VALUE obj) { + check_queue(self, q); if (queue_closed_p(self)) { raise_closed_queue_error(self); } - rb_ary_push(check_array(self, q->que), obj); - wakeup_one(queue_waitq(q)); + ring_buffer_push(self, q, obj); + wakeup_one(&q->waitq); return self; } @@ -1021,7 +1109,7 @@ rb_queue_close(VALUE self) if (!queue_closed_p(self)) { FL_SET(self, QUEUE_CLOSED); - wakeup_all(queue_waitq(q)); + wakeup_all(&q->waitq); } return self; @@ -1097,8 +1185,7 @@ szqueue_sleep_done(VALUE p) static inline VALUE queue_do_pop(rb_execution_context_t *ec, VALUE self, struct rb_queue *q, VALUE non_block, VALUE timeout) { - check_array(self, q->que); - if (RARRAY_LEN(q->que) == 0) { + if (q->len == 0) { if (RTEST(non_block)) { rb_raise(rb_eThreadError, "queue empty"); } @@ -1109,12 +1196,12 @@ queue_do_pop(rb_execution_context_t *ec, VALUE self, struct rb_queue *q, VALUE n } rb_hrtime_t end = queue_timeout2hrtime(timeout); - while (RARRAY_LEN(q->que) == 0) { + while (q->len == 0) { if (queue_closed_p(self)) { return queue_closed_result(self, q); } else { - RUBY_ASSERT(RARRAY_LEN(q->que) == 0); + RUBY_ASSERT(q->len == 0); RUBY_ASSERT(queue_closed_p(self) == 0); struct queue_waiter queue_waiter = { @@ -1122,7 +1209,7 @@ queue_do_pop(rb_execution_context_t *ec, VALUE self, struct rb_queue *q, VALUE n .as = {.q = q} }; - struct ccan_list_head *waitq = queue_waitq(q); + struct ccan_list_head *waitq = &q->waitq; ccan_list_add_tail(waitq, &queue_waiter.w.node); queue_waiter.as.q->num_waiting++; @@ -1139,7 +1226,7 @@ queue_do_pop(rb_execution_context_t *ec, VALUE self, struct rb_queue *q, VALUE n } } - return rb_ary_shift(q->que); + return ring_buffer_shift(q); } static VALUE @@ -1158,7 +1245,14 @@ rb_queue_pop(rb_execution_context_t *ec, VALUE self, VALUE non_block, VALUE time static VALUE rb_queue_empty_p(VALUE self) { - return RBOOL(queue_length(self, queue_ptr(self)) == 0); + return RBOOL(queue_ptr(self)->len == 0); +} + +static void +queue_clear(struct rb_queue *q) +{ + q->len = 0; + q->offset = 0; } /* @@ -1170,9 +1264,7 @@ rb_queue_empty_p(VALUE self) static VALUE rb_queue_clear(VALUE self) { - struct rb_queue *q = queue_ptr(self); - - rb_ary_clear(check_array(self, q->que)); + queue_clear(queue_ptr(self)); return self; } @@ -1188,7 +1280,7 @@ rb_queue_clear(VALUE self) static VALUE rb_queue_length(VALUE self) { - return LONG2NUM(queue_length(self, queue_ptr(self))); + return LONG2NUM(queue_ptr(self)->len); } NORETURN(static VALUE rb_queue_freeze(VALUE self)); @@ -1241,14 +1333,13 @@ static VALUE rb_szqueue_initialize(VALUE self, VALUE vmax) { long max; - struct rb_szqueue *sq = szqueue_ptr(self); + struct rb_szqueue *sq = raw_szqueue_ptr(self); max = NUM2LONG(vmax); if (max <= 0) { rb_raise(rb_eArgError, "queue size must be positive"); } - - RB_OBJ_WRITE(self, szqueue_list(sq), ary_buf_new()); + ring_buffer_init(&sq->q, QUEUE_INITIAL_CAPA); ccan_list_head_init(szqueue_waitq(sq)); ccan_list_head_init(szqueue_pushq(sq)); sq->max = max; @@ -1323,7 +1414,7 @@ rb_szqueue_push(rb_execution_context_t *ec, VALUE self, VALUE object, VALUE non_ { struct rb_szqueue *sq = szqueue_ptr(self); - if (queue_length(self, &sq->q) >= sq->max) { + if (sq->q.len >= sq->max) { if (RTEST(non_block)) { rb_raise(rb_eThreadError, "queue full"); } @@ -1334,7 +1425,7 @@ rb_szqueue_push(rb_execution_context_t *ec, VALUE self, VALUE object, VALUE non_ } rb_hrtime_t end = queue_timeout2hrtime(timeout); - while (queue_length(self, &sq->q) >= sq->max) { + while (sq->q.len >= sq->max) { if (queue_closed_p(self)) { raise_closed_queue_error(self); } @@ -1370,7 +1461,7 @@ rb_szqueue_pop(rb_execution_context_t *ec, VALUE self, VALUE non_block, VALUE ti struct rb_szqueue *sq = szqueue_ptr(self); VALUE retval = queue_do_pop(ec, self, &sq->q, non_block, timeout); - if (queue_length(self, &sq->q) < sq->max) { + if (sq->q.len < sq->max) { wakeup_one(szqueue_pushq(sq)); } @@ -1387,8 +1478,7 @@ static VALUE rb_szqueue_clear(VALUE self) { struct rb_szqueue *sq = szqueue_ptr(self); - - rb_ary_clear(check_array(self, sq->q.que)); + queue_clear(&sq->q); wakeup_all(szqueue_pushq(sq)); return self; } diff --git a/tool/test/test_sync_default_gems.rb b/tool/test/test_sync_default_gems.rb index f9ab0aae4def92..252687f3f35a58 100755 --- a/tool/test/test_sync_default_gems.rb +++ b/tool/test/test_sync_default_gems.rb @@ -324,7 +324,7 @@ def test_squash_merge # We don't know which exact version fixed it, but we know git 2.52.0 works. stdout, status = Open3.capture2('git', '--version', err: File::NULL) omit 'git version check failed' unless status.success? - git_version = stdout.rstrip.delete_prefix('git version ') + git_version = stdout[/\Agit version \K\S+/] omit "git #{git_version} is too old" if Gem::Version.new(git_version) < Gem::Version.new('2.44.0') # 2---. <- branch diff --git a/tool/test/testunit/test_assertion.rb b/tool/test/testunit/test_assertion.rb index b0c2267b31ade3..7b1f28a8698b15 100644 --- a/tool/test/testunit/test_assertion.rb +++ b/tool/test/testunit/test_assertion.rb @@ -8,6 +8,8 @@ def test_wrong_assertion end def test_timeout_separately + pend "hang-up" if /mswin|mingw/ =~ RUBY_PLATFORM + assert_raise(Timeout::Error) do assert_separately([], <<~"end;", timeout: 0.1) sleep diff --git a/vm_method.c b/vm_method.c index 26dbe4cae8b416..dcf35527f7b956 100644 --- a/vm_method.c +++ b/vm_method.c @@ -1119,6 +1119,10 @@ rb_method_entry_alloc(ID called_id, VALUE owner, VALUE defined_class, rb_method_ VM_ASSERT_TYPE2(defined_class, T_CLASS, T_ICLASS); } rb_method_entry_t *me = SHAREABLE_IMEMO_NEW(rb_method_entry_t, imemo_ment, defined_class); + + // mark_and_move_method_entry pins itself when it is in the overloaded_cme table + rb_gc_register_pinning_obj((VALUE)me); + *((rb_method_definition_t **)&me->def) = def; me->called_id = called_id; me->owner = owner;