diff --git a/NEWS.md b/NEWS.md index 6f20061a27882c..df71bf98e898df 100644 --- a/NEWS.md +++ b/NEWS.md @@ -26,6 +26,11 @@ Note: We're only listing outstanding class updates. directory without hardcoding absolute paths or manipulating `$LOAD_PATH`. [[Feature #15330]] +* MatchData + + * `MatchData#integer_at` is added. It converts the matched substring to + integer and return the result. [[Feature #21932]] + * Method * `Method#source_location`, `Proc#source_location`, and @@ -130,6 +135,7 @@ A lot of work has gone into making Ractors more stable, performant, and usable. [Feature #15330]: https://bugs.ruby-lang.org/issues/15330 [Feature #21390]: https://bugs.ruby-lang.org/issues/21390 [Feature #21785]: https://bugs.ruby-lang.org/issues/21785 +[Feature #21932]: https://bugs.ruby-lang.org/issues/21932 [test-unit-3.7.4]: https://github.com/test-unit/test-unit/releases/tag/3.7.4 [test-unit-3.7.5]: https://github.com/test-unit/test-unit/releases/tag/3.7.5 [rss-0.3.2]: https://github.com/ruby/rss/releases/tag/0.3.2 diff --git a/ext/json/generator/extconf.rb b/ext/json/generator/extconf.rb index ee1bbeaba76346..205a887e78f3bd 100644 --- a/ext/json/generator/extconf.rb +++ b/ext/json/generator/extconf.rb @@ -5,6 +5,8 @@ File.write('Makefile', dummy_makefile("").join) else append_cflags("-std=c99") + have_const("RUBY_TYPED_EMBEDDABLE", "ruby.h") # RUBY_VERSION >= 3.3 + $defs << "-DJSON_GENERATOR" $defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0" diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c index a6302691d9fd6b..b52bc975124aa9 100644 --- a/ext/json/generator/generator.c +++ b/ext/json/generator/generator.c @@ -722,27 +722,20 @@ static void State_compact(void *ptr) state->as_json = rb_gc_location(state->as_json); } -static void State_free(void *ptr) -{ - JSON_Generator_State *state = ptr; - ruby_xfree(state); -} - static size_t State_memsize(const void *ptr) { return sizeof(JSON_Generator_State); } static const rb_data_type_t JSON_Generator_State_type = { - "JSON/Generator/State", - { + .wrap_struct_name = "JSON/Generator/State", + .function = { .dmark = State_mark, - .dfree = State_free, + .dfree = RUBY_DEFAULT_FREE, .dsize = State_memsize, .dcompact = State_compact, }, - 0, 0, - RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE, + .flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_EMBEDDABLE, }; static void state_init(JSON_Generator_State *state) diff --git a/ext/json/json.h b/ext/json/json.h index 087a2eae66e317..8737989a6c9aa6 100644 --- a/ext/json/json.h +++ b/ext/json/json.h @@ -54,6 +54,17 @@ typedef unsigned char _Bool; # define RUBY_TYPED_FROZEN_SHAREABLE 0 #endif +#ifdef RUBY_TYPED_EMBEDDABLE +# define HAVE_RUBY_TYPED_EMBEDDABLE 1 +#else +# ifdef HAVE_CONST_RUBY_TYPED_EMBEDDABLE +# define RUBY_TYPED_EMBEDDABLE RUBY_TYPED_EMBEDDABLE +# define HAVE_RUBY_TYPED_EMBEDDABLE 1 +# else +# define RUBY_TYPED_EMBEDDABLE 0 +# endif +#endif + #ifndef NORETURN #if defined(__has_attribute) && __has_attribute(noreturn) #define NORETURN(x) __attribute__((noreturn)) x diff --git a/ext/json/parser/extconf.rb b/ext/json/parser/extconf.rb index 2440e66d8bdb39..f12fc2dddce02a 100644 --- a/ext/json/parser/extconf.rb +++ b/ext/json/parser/extconf.rb @@ -7,6 +7,10 @@ have_func("rb_hash_new_capa", "ruby.h") # RUBY_VERSION >= 3.2 have_func("rb_hash_bulk_insert", "ruby.h") # Missing on TruffleRuby +if RUBY_ENGINE == "ruby" + have_const("RUBY_TYPED_EMBEDDABLE", "ruby.h") # RUBY_VERSION >= 3.3 +end + append_cflags("-std=c99") if enable_config('parser-use-simd', default=!ENV["JSON_DISABLE_SIMD"]) diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index 1a8e2aaf485b62..b05f3f5273d71d 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -241,17 +241,27 @@ static void rvalue_stack_mark(void *ptr) { rvalue_stack *stack = (rvalue_stack *)ptr; long index; - for (index = 0; index < stack->head; index++) { - rb_gc_mark(stack->ptr[index]); + if (stack && stack->ptr) { + for (index = 0; index < stack->head; index++) { + rb_gc_mark(stack->ptr[index]); + } } } +static void rvalue_stack_free_buffer(rvalue_stack *stack) +{ + ruby_xfree(stack->ptr); + stack->ptr = NULL; +} + static void rvalue_stack_free(void *ptr) { rvalue_stack *stack = (rvalue_stack *)ptr; if (stack) { - ruby_xfree(stack->ptr); + rvalue_stack_free_buffer(stack); +#ifndef HAVE_RUBY_TYPED_EMBEDDABLE ruby_xfree(stack); +#endif } } @@ -262,14 +272,13 @@ static size_t rvalue_stack_memsize(const void *ptr) } static const rb_data_type_t JSON_Parser_rvalue_stack_type = { - "JSON::Ext::Parser/rvalue_stack", - { + .wrap_struct_name = "JSON::Ext::Parser/rvalue_stack", + .function = { .dmark = rvalue_stack_mark, .dfree = rvalue_stack_free, .dsize = rvalue_stack_memsize, }, - 0, 0, - RUBY_TYPED_FREE_IMMEDIATELY, + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE, }; static rvalue_stack *rvalue_stack_spill(rvalue_stack *old_stack, VALUE *handle, rvalue_stack **stack_ref) @@ -291,8 +300,12 @@ static void rvalue_stack_eagerly_release(VALUE handle) if (handle) { rvalue_stack *stack; TypedData_Get_Struct(handle, rvalue_stack, &JSON_Parser_rvalue_stack_type, stack); - RTYPEDDATA_DATA(handle) = NULL; +#ifdef HAVE_RUBY_TYPED_EMBEDDABLE + rvalue_stack_free_buffer(stack); +#else rvalue_stack_free(stack); + RTYPEDDATA_DATA(handle) = NULL; +#endif } } @@ -343,7 +356,7 @@ typedef struct JSON_ParserStruct { } JSON_ParserConfig; typedef struct JSON_ParserStateStruct { - VALUE stack_handle; + VALUE *stack_handle; const char *start; const char *cursor; const char *end; @@ -944,7 +957,7 @@ static inline VALUE json_push_value(JSON_ParserState *state, JSON_ParserConfig * if (RB_UNLIKELY(config->on_load_proc)) { value = rb_proc_call_with_block(config->on_load_proc, 1, &value, Qnil); } - rvalue_stack_push(state->stack, value, &state->stack_handle, &state->stack); + rvalue_stack_push(state->stack, value, state->stack_handle, &state->stack); return value; } @@ -1554,11 +1567,13 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource) const char *start; RSTRING_GETMEM(Vsource, start, len); + VALUE stack_handle = 0; JSON_ParserState _state = { .start = start, .cursor = start, .end = start + len, .stack = &stack, + .stack_handle = &stack_handle, }; JSON_ParserState *state = &_state; @@ -1566,8 +1581,8 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource) // This may be skipped in case of exception, but // it won't cause a leak. - rvalue_stack_eagerly_release(state->stack_handle); - + rvalue_stack_eagerly_release(stack_handle); + RB_GC_GUARD(stack_handle); json_ensure_eof(state); return result; @@ -1605,26 +1620,19 @@ static void JSON_ParserConfig_mark(void *ptr) rb_gc_mark(config->decimal_class); } -static void JSON_ParserConfig_free(void *ptr) -{ - JSON_ParserConfig *config = ptr; - ruby_xfree(config); -} - static size_t JSON_ParserConfig_memsize(const void *ptr) { return sizeof(JSON_ParserConfig); } static const rb_data_type_t JSON_ParserConfig_type = { - "JSON::Ext::Parser/ParserConfig", - { + .wrap_struct_name = "JSON::Ext::Parser/ParserConfig", + .function = { JSON_ParserConfig_mark, - JSON_ParserConfig_free, + RUBY_DEFAULT_FREE, JSON_ParserConfig_memsize, }, - 0, 0, - RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE, + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_EMBEDDABLE, }; static VALUE cJSON_parser_s_allocate(VALUE klass) diff --git a/lib/bundled_gems.rb b/lib/bundled_gems.rb index b64991af0ca050..a287c49a34d22a 100644 --- a/lib/bundled_gems.rb +++ b/lib/bundled_gems.rb @@ -137,8 +137,8 @@ def self.warning?(name, specs: nil) # Don't warn if the feature is found outside the standard library # (e.g., benchmark-ips's lib dir is on $LOAD_PATH but not in specs) resolved = $LOAD_PATH.resolve_feature_path(feature) rescue nil - if resolved - return unless resolved[1].start_with?(LIBDIR) || resolved[1].start_with?(ARCHDIR) + if resolved && !resolved[1].start_with?(LIBDIR, ARCHDIR) + return end end diff --git a/marshal.c b/marshal.c index e2a0ce6dd10210..199923df4df7f9 100644 --- a/marshal.c +++ b/marshal.c @@ -1879,6 +1879,9 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ } v = (VALUE)link; if (!st_lookup(arg->partial_objects, (st_data_t)v, &link)) { + if (arg->freeze && RB_TYPE_P(v, T_STRING)) { + v = rb_str_to_interned_str(v); + } v = r_post_proc(v, arg); } break; diff --git a/test/ruby/test_marshal.rb b/test/ruby/test_marshal.rb index eb669948017d98..05d3ceb60a5387 100644 --- a/test/ruby/test_marshal.rb +++ b/test/ruby/test_marshal.rb @@ -1002,5 +1002,19 @@ def test_modules_and_classes_are_not_frozen refute_predicate Object, :frozen? refute_predicate Kernel, :frozen? end + + def test_linked_strings_are_frozen + str = "test" + str.instance_variable_set(:@self, str) + source = [str, str] + + objects = Marshal.load(encode(source), freeze: true) + assert_predicate objects[0], :frozen? + assert_predicate objects[1], :frozen? + assert_same objects[0], objects[1] + assert_same objects[0], objects[0].instance_variable_get(:@self) + assert_same objects[1], objects[1].instance_variable_get(:@self) + assert_same objects[0].instance_variable_get(:@self), objects[1].instance_variable_get(:@self) + end end end