diff --git a/NEWS.md b/NEWS.md index f3e7f1ae0e3a6e..89f45deb99cf7b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -198,7 +198,7 @@ The following default gems are updated. * io-console 0.8.1 * io-nonblock 0.3.2 * io-wait 0.4.0.dev -* json 2.17.0 +* json 2.17.1 * net-http 0.8.0 * openssl 4.0.0.pre * optparse 0.8.0 diff --git a/doc/extension.rdoc b/doc/extension.rdoc index ba59d107ab4c84..6cf4c4926c93fc 100644 --- a/doc/extension.rdoc +++ b/doc/extension.rdoc @@ -2047,7 +2047,7 @@ the *_kw functions introduced in Ruby 2.7. #define rb_proc_call_with_block_kw(p, c, v, b, kw) rb_proc_call_with_block(p, c, v, b) #define rb_method_call_kw(c, v, m, kw) rb_method_call(c, v, m) #define rb_method_call_with_block_kw(c, v, m, b, kw) rb_method_call_with_block(c, v, m, b) - #define rb_eval_cmd_kwd(c, a, kw) rb_eval_cmd(c, a, 0) + #define rb_eval_cmd_kw(c, a, kw) rb_eval_cmd(c, a, 0) #endif == Appendix C. Functions available for use in extconf.rb diff --git a/ext/json/lib/json/version.rb b/ext/json/lib/json/version.rb index b7de7c27e21446..4ed61c43c8b187 100644 --- a/ext/json/lib/json/version.rb +++ b/ext/json/lib/json/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module JSON - VERSION = '2.17.0' + VERSION = '2.17.1' end diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index 5b7cd835cd7d1e..c84c7ed660a06d 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -651,7 +651,9 @@ static inline const char *json_next_backslash(const char *pe, const char *string positions->size--; const char *next_position = positions->positions[0]; positions->positions++; - return next_position; + if (next_position >= pe) { + return next_position; + } } if (positions->has_more) { diff --git a/include/ruby/internal/intern/vm.h b/include/ruby/internal/intern/vm.h index 29e0c7f534acb1..779f77fe91fcb5 100644 --- a/include/ruby/internal/intern/vm.h +++ b/include/ruby/internal/intern/vm.h @@ -95,6 +95,7 @@ VALUE rb_check_funcall(VALUE recv, ID mid, int argc, const VALUE *argv); */ VALUE rb_check_funcall_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat); +#ifdef RBIMPL_ATTR_DEPRECATED_INTERNAL /** * This API is practically a variant of rb_proc_call_kw() now. Historically * when there still was a concept called `$SAFE`, this was an API for that. @@ -109,7 +110,9 @@ VALUE rb_check_funcall_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int k * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. * @return What the command evaluates to. */ +RBIMPL_ATTR_DEPRECATED_INTERNAL(4.0) VALUE rb_eval_cmd_kw(VALUE cmd, VALUE arg, int kw_splat); +#endif /** * Identical to rb_funcallv(), except it takes Ruby's array instead of C's. diff --git a/internal/vm.h b/internal/vm.h index e5ed47afae0e29..7fae590d198fdd 100644 --- a/internal/vm.h +++ b/internal/vm.h @@ -78,6 +78,7 @@ void rb_check_stack_overflow(void); VALUE rb_block_call2(VALUE obj, ID mid, int argc, const VALUE *argv, rb_block_call_func_t bl_proc, VALUE data2, long flags); struct vm_ifunc *rb_current_ifunc(void); VALUE rb_gccct_clear_table(VALUE); +VALUE rb_eval_cmd_call_kw(VALUE cmd, int argc, const VALUE *argv, int kw_splat); #if USE_YJIT || USE_ZJIT /* vm_exec.c */ diff --git a/ractor.rb b/ractor.rb index 7c00e148a120aa..3c0c6744e6148e 100644 --- a/ractor.rb +++ b/ractor.rb @@ -472,6 +472,7 @@ def self.[]=(sym, val) # }.map(&:value).uniq.size #=> 1 and f() is called only once # def self.store_if_absent(sym) + Primitive.attr! :use_block Primitive.ractor_local_value_store_if_absent(sym) end diff --git a/signal.c b/signal.c index 5971e12ee9b00f..12af9058a25439 100644 --- a/signal.c +++ b/signal.c @@ -1066,7 +1066,7 @@ signal_exec(VALUE cmd, int sig) EC_PUSH_TAG(ec); if ((state = EC_EXEC_TAG()) == TAG_NONE) { VALUE signum = INT2NUM(sig); - rb_eval_cmd_kw(cmd, rb_ary_new3(1, signum), RB_NO_KEYWORDS); + rb_eval_cmd_call_kw(cmd, 1, &signum, RB_NO_KEYWORDS); } EC_POP_TAG(); ec = GET_EC(); diff --git a/spec/ruby/optional/capi/ext/kernel_spec.c b/spec/ruby/optional/capi/ext/kernel_spec.c index ff71a7e5892219..a8fed21b5900b6 100644 --- a/spec/ruby/optional/capi/ext/kernel_spec.c +++ b/spec/ruby/optional/capi/ext/kernel_spec.c @@ -117,9 +117,11 @@ VALUE kernel_spec_rb_eval_string(VALUE self, VALUE str) { return rb_eval_string(RSTRING_PTR(str)); } +#ifndef RUBY_VERSION_IS_4_0 VALUE kernel_spec_rb_eval_cmd_kw(VALUE self, VALUE cmd, VALUE args, VALUE kw_splat) { return rb_eval_cmd_kw(cmd, args, NUM2INT(kw_splat)); } +#endif VALUE kernel_spec_rb_raise(VALUE self, VALUE hash) { rb_hash_aset(hash, ID2SYM(rb_intern("stage")), ID2SYM(rb_intern("before"))); @@ -403,7 +405,9 @@ void Init_kernel_spec(void) { rb_define_method(cls, "rb_category_warn_deprecated_with_integer_extra_value", kernel_spec_rb_category_warn_deprecated_with_integer_extra_value, 1); rb_define_method(cls, "rb_ensure", kernel_spec_rb_ensure, 4); rb_define_method(cls, "rb_eval_string", kernel_spec_rb_eval_string, 1); +#ifndef RUBY_VERSION_IS_4_0 rb_define_method(cls, "rb_eval_cmd_kw", kernel_spec_rb_eval_cmd_kw, 3); +#endif rb_define_method(cls, "rb_raise", kernel_spec_rb_raise, 1); rb_define_method(cls, "rb_throw", kernel_spec_rb_throw, 1); rb_define_method(cls, "rb_throw_obj", kernel_spec_rb_throw_obj, 2); diff --git a/spec/ruby/optional/capi/kernel_spec.rb b/spec/ruby/optional/capi/kernel_spec.rb index d915a72c22af75..6633ee50c1f8ac 100644 --- a/spec/ruby/optional/capi/kernel_spec.rb +++ b/spec/ruby/optional/capi/kernel_spec.rb @@ -635,22 +635,24 @@ def proc_caller end end - describe "rb_eval_cmd_kw" do - it "evaluates a string of ruby code" do - @s.rb_eval_cmd_kw("1+1", [], 0).should == 2 - end + ruby_version_is ""..."4.0" do + describe "rb_eval_cmd_kw" do + it "evaluates a string of ruby code" do + @s.rb_eval_cmd_kw("1+1", [], 0).should == 2 + end - it "calls a proc with the supplied arguments" do - @s.rb_eval_cmd_kw(-> *x { x.map { |i| i + 1 } }, [1, 3, 7], 0).should == [2, 4, 8] - end + it "calls a proc with the supplied arguments" do + @s.rb_eval_cmd_kw(-> *x { x.map { |i| i + 1 } }, [1, 3, 7], 0).should == [2, 4, 8] + end - it "calls a proc with keyword arguments if kw_splat is non zero" do - a_proc = -> *x, **y { - res = x.map { |i| i + 1 } - y.each { |k, v| res << k; res << v } - res - } - @s.rb_eval_cmd_kw(a_proc, [1, 3, 7, {a: 1, b: 2, c: 3}], 1).should == [2, 4, 8, :a, 1, :b, 2, :c, 3] + it "calls a proc with keyword arguments if kw_splat is non zero" do + a_proc = -> *x, **y { + res = x.map { |i| i + 1 } + y.each { |k, v| res << k; res << v } + res + } + @s.rb_eval_cmd_kw(a_proc, [1, 3, 7, {a: 1, b: 2, c: 3}], 1).should == [2, 4, 8, :a, 1, :b, 2, :c, 3] + end end end diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb index 3e662bda324ba5..257e4f1736a2d0 100644 --- a/test/json/json_parser_test.rb +++ b/test/json/json_parser_test.rb @@ -325,6 +325,13 @@ def test_invalid_unicode_escape assert_raise(JSON::ParserError) { parse('"\u111___"') } end + def test_unicode_followed_by_newline + # Ref: https://github.com/ruby/json/issues/912 + assert_equal "🌌\n".bytes, JSON.parse('"\ud83c\udf0c\n"').bytes + assert_equal "🌌\n", JSON.parse('"\ud83c\udf0c\n"') + assert_predicate JSON.parse('"\ud83c\udf0c\n"'), :valid_encoding? + end + def test_invalid_surogates assert_raise(JSON::ParserError) { parse('"\\uD800"') } assert_raise(JSON::ParserError) { parse('"\\uD800_________________"') } diff --git a/variable.c b/variable.c index faeffec660a9f2..47b521856676dd 100644 --- a/variable.c +++ b/variable.c @@ -862,7 +862,7 @@ rb_define_virtual_variable( static void rb_trace_eval(VALUE cmd, VALUE val) { - rb_eval_cmd_kw(cmd, rb_ary_new3(1, val), RB_NO_KEYWORDS); + rb_eval_cmd_call_kw(cmd, 1, &val, RB_NO_KEYWORDS); } VALUE diff --git a/vm_eval.c b/vm_eval.c index 281d63a1b8268a..12bdabc3302f68 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -2147,6 +2147,17 @@ rb_eval_string_wrap(const char *str, int *pstate) VALUE rb_eval_cmd_kw(VALUE cmd, VALUE arg, int kw_splat) +{ + Check_Type(arg, T_ARRAY); + int argc = RARRAY_LENINT(arg); + const VALUE *argv = RARRAY_CONST_PTR(arg); + VALUE val = rb_eval_cmd_call_kw(cmd, argc, argv, kw_splat); + RB_GC_GUARD(arg); + return val; +} + +VALUE +rb_eval_cmd_call_kw(VALUE cmd, int argc, const VALUE *argv, int kw_splat) { enum ruby_tag_type state; volatile VALUE val = Qnil; /* OK */ @@ -2155,8 +2166,7 @@ rb_eval_cmd_kw(VALUE cmd, VALUE arg, int kw_splat) EC_PUSH_TAG(ec); if ((state = EC_EXEC_TAG()) == TAG_NONE) { if (!RB_TYPE_P(cmd, T_STRING)) { - val = rb_funcallv_kw(cmd, idCall, RARRAY_LENINT(arg), - RARRAY_CONST_PTR(arg), kw_splat); + val = rb_funcallv_kw(cmd, idCall, argc, argv, kw_splat); } else { val = eval_string_with_cref(rb_vm_top_self(), cmd, NULL, 0, 0);