Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions ext/json/generator/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
15 changes: 4 additions & 11 deletions ext/json/generator/generator.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
11 changes: 11 additions & 0 deletions ext/json/json.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions ext/json/parser/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"])
Expand Down
54 changes: 31 additions & 23 deletions ext/json/parser/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand All @@ -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)
Expand All @@ -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
}
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -1554,20 +1567,22 @@ 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;

VALUE result = json_parse_any(state, config);

// 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;
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions lib/bundled_gems.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 3 additions & 0 deletions marshal.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
14 changes: 14 additions & 0 deletions test/ruby/test_marshal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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