From 730440714b31c276407f562e492e0b96d7219084 Mon Sep 17 00:00:00 2001 From: Josh Kline Date: Sat, 28 Dec 2013 14:58:30 -0800 Subject: [PATCH 0001/1010] Spelling and grammar fixes for comments. --- lib/json/pure/generator.rb | 2 +- lib/json/pure/parser.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/json/pure/generator.rb b/lib/json/pure/generator.rb index 9056a5d7f..fc93e3f34 100644 --- a/lib/json/pure/generator.rb +++ b/lib/json/pure/generator.rb @@ -468,7 +468,7 @@ def to_json(state = nil, *args) end end - # Module that holds the extinding methods if, the String module is + # Module that holds the extending methods if, the String module is # included. module Extend # Raw Strings are JSON Objects (the raw bytes are stored in an diff --git a/lib/json/pure/parser.rb b/lib/json/pure/parser.rb index a41d1eeb1..937f56f02 100644 --- a/lib/json/pure/parser.rb +++ b/lib/json/pure/parser.rb @@ -64,7 +64,7 @@ class Parser < StringScanner # (keys) in a JSON object. Otherwise strings are returned, which is also # the default. # * *create_additions*: If set to true, the Parser creates - # additions when if a matching class and create_id was found. This + # additions when a matching class and create_id are found. This # option defaults to false. # * *object_class*: Defaults to Hash # * *array_class*: Defaults to Array From f68bbd67181718e20bc367a62407967e72efcb46 Mon Sep 17 00:00:00 2001 From: Vipul A M Date: Wed, 9 Apr 2014 11:35:33 +0530 Subject: [PATCH 0002/1010] Perf. improvements to Hash#to_json in pure implementation generator: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use `.each` instead of `for` for iterating over elements. - Initialize `delim`, `result` vars with initial states instead of appending `state.object_nl` later. - Use faster string interpolation where possible on `Hash#json_transform` instead of appending strings repeatedly. Benchmark: ```ruby hash ={:a => {:a => {:a => {:a => {:a => 1}}}}} require 'benchmark/ips' Benchmark.ips do |x| x.report("to_json ") { hash.to_json} end ``` Before: Calculating ------------------------------------- to_json 4990 i/100ms ------------------------------------------------- to_json 52970.8 (±5.9%) i/s - 264470 in 5.010558s After: Calculating ------------------------------------- to_json 5253 i/100ms ------------------------------------------------- to_json 55998.6 (±5.9%) i/s - 283662 in 5.083350s --- lib/json/pure/generator.rb | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/json/pure/generator.rb b/lib/json/pure/generator.rb index 9056a5d7f..f2ad51467 100644 --- a/lib/json/pure/generator.rb +++ b/lib/json/pure/generator.rb @@ -248,7 +248,7 @@ def configure(opts) else raise TypeError, "can't convert #{opts.class} into Hash" end - for key, value in opts + opts.each do |key, value| instance_variable_set "@#{key}", value end @indent = opts[:indent] if opts.key?(:indent) @@ -277,7 +277,7 @@ def configure(opts) # passed to the configure method. def to_h result = {} - for iv in instance_variables + instance_variables.each do |iv| iv = iv.to_s[1..-1] result[iv.to_sym] = self[iv] end @@ -349,21 +349,15 @@ def json_shift(state) end def json_transform(state) - delim = ',' - delim << state.object_nl - result = '{' - result << state.object_nl + delim = ",#{state.object_nl}" + result = "{#{state.object_nl}" depth = state.depth += 1 first = true indent = !state.object_nl.empty? - each { |key,value| + each { |key, value| result << delim unless first result << state.indent * depth if indent - result << key.to_s.to_json(state) - result << state.space_before - result << ':' - result << state.space - result << value.to_json(state) + result = "#{result}#{key.to_s.to_json(state)}#{state.space_before}:#{state.space}#{value.to_json(state)}" first = false } depth = state.depth -= 1 From 2f3f44c180e1fc095006fb6b7749aa395a39d934 Mon Sep 17 00:00:00 2001 From: zverok Date: Thu, 8 Mar 2018 20:32:00 +0200 Subject: [PATCH 0003/1010] Add :nodoc: for GeneratorMethods --- ext/json/ext/generator/generator.c | 70 ++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 2bf807456..8fd00cea6 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -324,6 +324,76 @@ static char *fstrndup(const char *ptr, unsigned long len) { * */ +/* Explanation of the following: that's the only way to not pollute + * standard library's docs with GeneratorMethods:: which + * are uninformative and take a large place in a list of classes + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::Array + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::Bignum + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::FalseClass + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::Fixnum + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::Float + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::Hash + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::Integer + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::NilClass + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::Object + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::String + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::String::Extend + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::TrueClass + * :nodoc: + */ + /* * call-seq: to_json(state = nil) * From 4123df7d561c60ad50456bfb203ceb02d66b78f2 Mon Sep 17 00:00:00 2001 From: kares Date: Wed, 28 Nov 2018 12:45:01 +0100 Subject: [PATCH 0004/1010] [build] no need to clean on rake test_pure (similar to test_ext) --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index c6c195f9f..532aacb50 100644 --- a/Rakefile +++ b/Rakefile @@ -176,7 +176,7 @@ task :check_env do end desc "Testing library (pure ruby)" -task :test_pure => [ :clean, :check_env, :do_test_pure ] +task :test_pure => [ :check_env, :do_test_pure ] UndocumentedTestTask.new do |t| t.name = 'do_test_pure' From 645f36ed8a72bd31920efec0abacc0a24d3abe78 Mon Sep 17 00:00:00 2001 From: kares Date: Wed, 28 Nov 2018 12:57:13 +0100 Subject: [PATCH 0005/1010] [build] let's build with Java 1.6 JRuby 1.7 was Java6+ compatible (9.2 is Java8+) --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 532aacb50..56edecc24 100644 --- a/Rakefile +++ b/Rakefile @@ -238,7 +238,7 @@ if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby' classpath = (Dir['java/lib/*.jar'] << 'java/src' << JRUBY_JAR) * ':' obj = src.sub(/\.java\Z/, '.class') file obj => src do - sh 'javac', '-classpath', classpath, '-source', '1.5', '-target', '1.5', src + sh 'javac', '-classpath', classpath, '-source', '1.6', '-target', '1.6', src end JAVA_CLASSES << obj end From 362a7c3eae107773fe7413549de167d532e0e7ae Mon Sep 17 00:00:00 2001 From: kares Date: Wed, 28 Nov 2018 14:46:56 +0100 Subject: [PATCH 0006/1010] [build] make test_xxx work standalone + avoid bundle exec --- Rakefile | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Rakefile b/Rakefile index 56edecc24..e60cfc119 100644 --- a/Rakefile +++ b/Rakefile @@ -176,7 +176,8 @@ task :check_env do end desc "Testing library (pure ruby)" -task :test_pure => [ :check_env, :do_test_pure ] +task :test_pure => [ :set_env_pure, :check_env, :do_test_pure ] +task(:set_env_pure) { ENV['JSON'] = 'pure' } UndocumentedTestTask.new do |t| t.name = 'do_test_pure' @@ -187,10 +188,7 @@ UndocumentedTestTask.new do |t| end desc "Testing library (pure ruby and extension)" -task :test do - sh "env JSON=pure #{BUNDLE} exec rake test_pure" or exit 1 - sh "env JSON=ext #{BUNDLE} exec rake test_ext" or exit 1 -end +task :test => [ :test_pure, :test_ext ] namespace :gems do desc 'Install all development gems' @@ -257,7 +255,8 @@ if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby' end desc "Testing library (jruby)" - task :test_ext => [ :check_env, :create_jar, :do_test_ext ] + task :test_ext => [ :set_env_ext, :create_jar, :check_env, :do_test_ext ] + task(:set_env_ext) { ENV['JSON'] = 'ext' } UndocumentedTestTask.new do |t| t.name = 'do_test_ext' From 373b633f386af2874ee1f6aa01fb91cfe3b6e8a7 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Sun, 25 Aug 2019 17:21:02 -0700 Subject: [PATCH 0007/1010] Remove invalid JSON.generate description from JSON module rdoc This text used to be true in older versions of json, but has not been true for a number of years (since json version 2 I think). --- lib/json.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/json.rb b/lib/json.rb index b5a691241..947ac630a 100644 --- a/lib/json.rb +++ b/lib/json.rb @@ -44,14 +44,6 @@ # require 'json' # puts {:hello => "goodbye"}.to_json => "{\"hello\":\"goodbye\"}" # -# JSON.generate only allows objects or arrays to be converted -# to JSON syntax. to_json, however, accepts many Ruby classes -# even though it acts only as a method for serialization: -# -# require 'json' -# -# 1.to_json => "1" -# module JSON require 'json/version' From 4f471bf590a6193681ef10fc15da33eed91fc33a Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 14 Oct 2019 19:13:20 +0900 Subject: [PATCH 0008/1010] Fixed unexpected illegal/malformed utf-8 error flori/json@c34d01ff6a18dac04a90b2e0f820cdb1d5c7e1b2 does not consider US-ASCII compatible but non-UTF-8 encodings, and causes an error in RDoc tests. --- ext/json/ext/generator/generator.c | 11 ++++++++++- tests/json_generator_test.rb | 6 ++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index cc8c3a7c3..862f76649 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -846,11 +846,20 @@ static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_St fbuffer_append_char(buffer, ']'); } +#ifdef HAVE_RUBY_ENCODING_H +static int enc_utf8_compatible_p(rb_encoding *enc) +{ + if (enc == rb_usascii_encoding()) return 1; + if (enc == rb_utf8_encoding()) return 1; + return 0; +} +#endif + static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) { fbuffer_append_char(buffer, '"'); #ifdef HAVE_RUBY_ENCODING_H - if (!rb_enc_str_asciicompat_p(obj)) { + if (!enc_utf8_compatible_p(rb_enc_get(obj))) { obj = rb_str_encode(obj, CEncoding_UTF_8, 0, Qnil); } #endif diff --git a/tests/json_generator_test.rb b/tests/json_generator_test.rb index 86be398f4..d7f9ebeb6 100644 --- a/tests/json_generator_test.rb +++ b/tests/json_generator_test.rb @@ -374,4 +374,10 @@ def to_s; self; end assert_equal '["foo"]', JSON.generate([s.new('foo')]) end end + + if defined?(Encoding) + def test_nonutf8_encoding + assert_equal("\"5\u{b0}\"", "5\xb0".force_encoding("iso-8859-1").to_json) + end + end end From bd1c2748cbcb5f75c8a045ba3ca12ac1b710ebdd Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 17 Oct 2019 13:30:09 -0700 Subject: [PATCH 0009/1010] Look up constant instead of caching in a global The global can go bad if the compactor runs, so we need to look up the constant instead of caching it in a global. --- ext/json/ext/generator/generator.c | 9 +++----- tests/json_generator_test.rb | 37 ++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index cc8c3a7c3..3ff06ad67 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -15,7 +15,7 @@ static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject, #endif mFloat, mString, mString_Extend, mTrueClass, mFalseClass, mNilClass, eGeneratorError, - eNestingError, CRegexp_MULTILINE, CJSON_SAFE_STATE_PROTOTYPE, + eNestingError, CRegexp_MULTILINE, i_SAFE_STATE_PROTOTYPE; static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before, @@ -1073,10 +1073,8 @@ static VALUE cState_from_state_s(VALUE self, VALUE opts) } else if (rb_obj_is_kind_of(opts, rb_cHash)) { return rb_funcall(self, i_new, 1, opts); } else { - if (NIL_P(CJSON_SAFE_STATE_PROTOTYPE)) { - CJSON_SAFE_STATE_PROTOTYPE = rb_const_get(mJSON, i_SAFE_STATE_PROTOTYPE); - } - return rb_funcall(CJSON_SAFE_STATE_PROTOTYPE, i_dup, 0); + VALUE prototype = rb_const_get(mJSON, i_SAFE_STATE_PROTOTYPE); + return rb_funcall(prototype, i_dup, 0); } } @@ -1488,5 +1486,4 @@ void Init_generator(void) i_encode = rb_intern("encode"); #endif i_SAFE_STATE_PROTOTYPE = rb_intern("SAFE_STATE_PROTOTYPE"); - CJSON_SAFE_STATE_PROTOTYPE = Qnil; } diff --git a/tests/json_generator_test.rb b/tests/json_generator_test.rb index 86be398f4..a85bd11e8 100644 --- a/tests/json_generator_test.rb +++ b/tests/json_generator_test.rb @@ -40,6 +40,43 @@ def setup EOT end + def silence + v = $VERBOSE + $VERBOSE = nil + yield + ensure + $VERBOSE = v + end + + def test_remove_const_segv + stress = GC.stress + const = JSON::SAFE_STATE_PROTOTYPE.dup + + bignum_too_long_to_embed_as_string = 1234567890123456789012345 + expect = bignum_too_long_to_embed_as_string.to_s + GC.stress = true + + 10.times do |i| + tmp = bignum_too_long_to_embed_as_string.to_json + raise "'\#{expect}' is expected, but '\#{tmp}'" unless tmp == expect + end + + silence do + JSON.const_set :SAFE_STATE_PROTOTYPE, nil + end + + 10.times do |i| + assert_raise TypeError do + bignum_too_long_to_embed_as_string.to_json + end + end + ensure + GC.stress = stress + silence do + JSON.const_set :SAFE_STATE_PROTOTYPE, const + end + end + def test_generate json = generate(@hash) assert_equal(parse(@json2), parse(json)) From 226f0f6940ddb1a8d1e60412a4ba58af27d92b62 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 17 Oct 2019 13:35:26 -0700 Subject: [PATCH 0010/1010] Remove unused constant. This constant isn't used, so lets remove it. --- ext/json/ext/generator/generator.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 3ff06ad67..d14e4714c 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -15,7 +15,7 @@ static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject, #endif mFloat, mString, mString_Extend, mTrueClass, mFalseClass, mNilClass, eGeneratorError, - eNestingError, CRegexp_MULTILINE, + eNestingError, i_SAFE_STATE_PROTOTYPE; static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before, @@ -1455,7 +1455,6 @@ void Init_generator(void) mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass"); rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1); - CRegexp_MULTILINE = rb_const_get(rb_cRegexp, rb_intern("MULTILINE")); i_to_s = rb_intern("to_s"); i_to_json = rb_intern("to_json"); i_new = rb_intern("new"); From 9253b3f0c9768596067efbc90a8b37fb9bbfd859 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Sat, 1 Jun 2019 15:07:35 +0300 Subject: [PATCH 0011/1010] Ignore warnings about ambiguous first argument with the negative integer. --- tests/json_parser_test.rb | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/json_parser_test.rb b/tests/json_parser_test.rb index 5f454eb12..469f93c1b 100644 --- a/tests/json_parser_test.rb +++ b/tests/json_parser_test.rb @@ -91,22 +91,22 @@ def test_parse_numbers assert_raise(JSON::ParserError) { parse('+23') } assert_raise(JSON::ParserError) { parse('.23') } assert_raise(JSON::ParserError) { parse('023') } - assert_equal 23, parse('23') - assert_equal -23, parse('-23') - assert_equal_float 3.141, parse('3.141') - assert_equal_float -3.141, parse('-3.141') - assert_equal_float 3.141, parse('3141e-3') - assert_equal_float 3.141, parse('3141.1e-3') - assert_equal_float 3.141, parse('3141E-3') - assert_equal_float 3.141, parse('3141.0E-3') - assert_equal_float -3.141, parse('-3141.0e-3') - assert_equal_float -3.141, parse('-3141e-3') + assert_equal(23, parse('23')) + assert_equal(-23, parse('-23')) + assert_equal_float(3.141, parse('3.141')) + assert_equal_float(-3.141, parse('-3.141')) + assert_equal_float(3.141, parse('3141e-3')) + assert_equal_float(3.141, parse('3141.1e-3')) + assert_equal_float(3.141, parse('3141E-3')) + assert_equal_float(3.141, parse('3141.0E-3')) + assert_equal_float(-3.141, parse('-3141.0e-3')) + assert_equal_float(-3.141, parse('-3141e-3')) assert_raise(ParserError) { parse('NaN') } assert parse('NaN', :allow_nan => true).nan? assert_raise(ParserError) { parse('Infinity') } - assert_equal 1.0/0, parse('Infinity', :allow_nan => true) + assert_equal(1.0/0, parse('Infinity', :allow_nan => true)) assert_raise(ParserError) { parse('-Infinity') } - assert_equal -1.0/0, parse('-Infinity', :allow_nan => true) + assert_equal(-1.0/0, parse('-Infinity', :allow_nan => true)) end def test_parse_bigdecimals From bbc88248581fb3c7063edd9fae00af5f7d1f18d9 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Wed, 25 Sep 2019 11:18:49 -0700 Subject: [PATCH 0012/1010] Make rb_scan_args handle keywords more similar to Ruby methods (#2460) Cfuncs that use rb_scan_args with the : entry suffer similar keyword argument separation issues that Ruby methods suffer if the cfuncs accept optional or variable arguments. This makes the following changes to : handling. * Treats as **kw, prompting keyword argument separation warnings if called with a positional hash. * Do not look for an option hash if empty keywords are provided. For backwards compatibility, treat an empty keyword splat as a empty mandatory positional hash argument, but emit a a warning, as this behavior will be removed in Ruby 3. The argument number check needs to be moved lower so it can correctly handle an empty positional argument being added. * If the last argument is nil and it is necessary to treat it as an option hash in order to make sure all arguments are processed, continue to treat the last argument as the option hash. Emit a warning in this case, as this behavior will be removed in Ruby 3. * If splitting the keyword hash into two hashes, issue a warning, as we will not be splitting hashes in Ruby 3. * If the keyword argument is required to fill a mandatory positional argument, continue to do so, but emit a warning as this behavior will be going away in Ruby 3. * If keyword arguments are provided and the last argument is not a hash, that indicates something wrong. This can happen if a cfunc is calling rb_scan_args multiple times, and providing arguments that were not passed to it from Ruby. Callers need to switch to the new rb_scan_args_kw function, which allows passing of whether keywords were provided. This commit fixes all warnings caused by the changes above. It switches some function calls to *_kw versions with appropriate kw_splat flags. If delegating arguments, RB_PASS_CALLED_KEYWORDS is used. If creating new arguments, RB_PASS_KEYWORDS is used if the last argument is a hash to be treated as keywords. In open_key_args in io.c, use rb_scan_args_kw. In this case, the arguments provided come from another C function, not Ruby. The last argument may or may not be a hash, so we can't set keyword argument mode. However, if it is a hash, we don't want to warn when treating it as keywords. In Ruby files, make sure to appropriately use keyword splats or literal keywords when calling Cfuncs that now issue keyword argument separation warnings through rb_scan_args. Also, make sure not to pass nil in place of an option hash. Work around Kernel#warn warnings due to problems in the Rubygems override of the method. There is an open pull request to fix these issues in Rubygems, but part of the Rubygems tests for their override fail on ruby-head due to rb_scan_args not recognizing empty keyword splats, which this commit fixes. Implementation wise, adding rb_scan_args_kw is kind of a pain, because rb_scan_args takes a variable number of arguments. In order to not duplicate all the code, the function internals need to be split into two functions taking a va_list, and to avoid passing in a ton of arguments, a single struct argument is used to handle the variables previously local to the function. --- ext/json/ext/parser/parser.c | 2 +- lib/json/common.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 63b895eb4..79ca067cb 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -1833,7 +1833,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } else { json->max_nesting = 100; json->allow_nan = 0; - json->create_additions = 1; + json->create_additions = 0; json->create_id = rb_funcall(mJSON, i_create_id, 0); json->object_class = Qnil; json->array_class = Qnil; diff --git a/lib/json/common.rb b/lib/json/common.rb index 7cc852916..3be9fd8dc 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -153,7 +153,7 @@ class MissingUnicodeSupport < JSONError; end # * *object_class*: Defaults to Hash # * *array_class*: Defaults to Array def parse(source, opts = {}) - Parser.new(source, opts).parse + Parser.new(source, **(opts||{})).parse end # Parse the JSON document _source_ into a Ruby data structure and return it. @@ -176,7 +176,7 @@ def parse!(source, opts = {}) :max_nesting => false, :allow_nan => true }.merge(opts) - Parser.new(source, opts).parse + Parser.new(source, **(opts||{})).parse end # Generate a JSON document from the Ruby data structure _obj_ and return From fd3873c0a32b4127e3178bf65bf5b114d3ab8de3 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Sat, 1 Jun 2019 14:44:24 +0300 Subject: [PATCH 0013/1010] Ignore warnings about ambiguous first argument of regexp with assert match. --- tests/json_common_interface_test.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/json_common_interface_test.rb b/tests/json_common_interface_test.rb index de88c6e89..53f335ed3 100644 --- a/tests/json_common_interface_test.rb +++ b/tests/json_common_interface_test.rb @@ -27,15 +27,15 @@ def test_index end def test_parser - assert_match /::Parser\z/, JSON.parser.name + assert_match(/::Parser\z/, JSON.parser.name) end def test_generator - assert_match /::Generator\z/, JSON.generator.name + assert_match(/::Generator\z/, JSON.generator.name) end def test_state - assert_match /::Generator::State\z/, JSON.state.name + assert_match(/::Generator::State\z/, JSON.state.name) end def test_create_id From dcb35811cf240f771a0587ee20ec54b10594ff52 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 14 Oct 2019 19:13:20 +0900 Subject: [PATCH 0014/1010] [flori/json] Fixed unexpected illegal/malformed utf-8 error flori/json@c34d01ff6a18dac04a90b2e0f820cdb1d5c7e1b2 does not consider US-ASCII compatible but non-UTF-8 encodings, and causes an error in RDoc tests. https://github.com/flori/json/commit/4f471bf590 --- ext/json/ext/generator/generator.c | 11 ++++++++++- tests/json_generator_test.rb | 6 ++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index d14e4714c..f481346ad 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -846,11 +846,20 @@ static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_St fbuffer_append_char(buffer, ']'); } +#ifdef HAVE_RUBY_ENCODING_H +static int enc_utf8_compatible_p(rb_encoding *enc) +{ + if (enc == rb_usascii_encoding()) return 1; + if (enc == rb_utf8_encoding()) return 1; + return 0; +} +#endif + static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) { fbuffer_append_char(buffer, '"'); #ifdef HAVE_RUBY_ENCODING_H - if (!rb_enc_str_asciicompat_p(obj)) { + if (!enc_utf8_compatible_p(rb_enc_get(obj))) { obj = rb_str_encode(obj, CEncoding_UTF_8, 0, Qnil); } #endif diff --git a/tests/json_generator_test.rb b/tests/json_generator_test.rb index a85bd11e8..3d9ab7a36 100644 --- a/tests/json_generator_test.rb +++ b/tests/json_generator_test.rb @@ -411,4 +411,10 @@ def to_s; self; end assert_equal '["foo"]', JSON.generate([s.new('foo')]) end end + + if defined?(Encoding) + def test_nonutf8_encoding + assert_equal("\"5\u{b0}\"", "5\xb0".force_encoding("iso-8859-1").to_json) + end + end end From 04b2fcf7397bc0b684b00bd3c92184650432cbba Mon Sep 17 00:00:00 2001 From: mrkn Date: Sun, 2 Dec 2018 05:21:57 +0000 Subject: [PATCH 0015/1010] Fix JSON::Parser against bigdecimal updates git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66127 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- tests/json_parser_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/json_parser_test.rb b/tests/json_parser_test.rb index 469f93c1b..9946dd93e 100644 --- a/tests/json_parser_test.rb +++ b/tests/json_parser_test.rb @@ -110,8 +110,8 @@ def test_parse_numbers end def test_parse_bigdecimals - assert_equal(BigDecimal, JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"].class) - assert_equal(BigDecimal.new("0.901234567890123456789E1"),JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"] ) + assert_equal(BigDecimal, JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"].class) + assert_equal(BigDecimal("0.901234567890123456789E1"),JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"] ) end if Array.method_defined?(:permutation) From 5fab969606888063191fce3c71b48e939edb3ff7 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 10 Jul 2019 17:15:18 +0900 Subject: [PATCH 0016/1010] Removed binary line --- json.gemspec | Bin 5439 -> 5300 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/json.gemspec b/json.gemspec index 6e7873fdea50150805c653c8bc10bf705b6ac7dc..d8a0dc5e92448e43b70257b974a7c498a3aaa5d8 100644 GIT binary patch delta 32 ocmdn5wMBD+)W*p&j4VdFMuwX=Gp=IXoX_mf&uFxHzfc1c0JUNYW&i*H delta 112 zcmdm@xnFC7l!~%KaY<>Cl|oi=ex8Dno{^q`LQ!c_r9w_-5*JKv@_%OW$$Oc(7#Svi zWRw$ONUbQ*2dUQwGLrN2()5awHcHAevY6`{8gI5@T*b)l Date: Wed, 10 Jul 2019 17:16:01 +0900 Subject: [PATCH 0017/1010] Drop fossil rubygems support --- json.gemspec | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/json.gemspec b/json.gemspec index d8a0dc5e9..f1d1d1c96 100644 --- a/json.gemspec +++ b/json.gemspec @@ -21,18 +21,6 @@ Gem::Specification.new do |s| s.summary = "JSON Implementation for Ruby".freeze s.test_files = ["./tests/test_helper.rb".freeze] - if s.respond_to? :specification_version then - s.specification_version = 4 - - if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - s.add_development_dependency(%q.freeze, [">= 0"]) - s.add_development_dependency(%q.freeze, ["~> 2.0"]) - else - s.add_dependency(%q.freeze, [">= 0"]) - s.add_dependency(%q.freeze, ["~> 2.0"]) - end - else - s.add_dependency(%q.freeze, [">= 0"]) - s.add_dependency(%q.freeze, ["~> 2.0"]) - end + s.add_development_dependency(%q.freeze, [">= 0"]) + s.add_development_dependency(%q.freeze, ["~> 2.0"]) end From c2978a97b714c2bfca6fb38cc5da13e70a09234f Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 12 Jul 2019 07:25:06 +0900 Subject: [PATCH 0018/1010] Removed useless `freeze`s from gemspec files --- json.gemspec | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/json.gemspec b/json.gemspec index f1d1d1c96..1cb236d2b 100644 --- a/json.gemspec +++ b/json.gemspec @@ -1,26 +1,26 @@ # -*- encoding: utf-8 -*- Gem::Specification.new do |s| - s.name = "json".freeze + s.name = "json" s.version = "2.2.0" - s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= - s.require_paths = ["lib".freeze] - s.authors = ["Florian Frank".freeze] + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib"] + s.authors = ["Florian Frank"] s.date = "2019-02-21" - s.description = "This is a JSON implementation as a Ruby extension in C.".freeze - s.email = "flori@ping.de".freeze - s.extensions = ["ext/json/ext/generator/extconf.rb".freeze, "ext/json/ext/parser/extconf.rb".freeze, "ext/json/extconf.rb".freeze] - s.extra_rdoc_files = ["README.md".freeze] - s.files = ["./tests/test_helper.rb".freeze, ".gitignore".freeze, ".travis.yml".freeze, "CHANGES.md".freeze, "Gemfile".freeze, "README-json-jruby.md".freeze, "README.md".freeze, "Rakefile".freeze, "VERSION".freeze, "diagrams/.keep".freeze, "ext/json/ext/fbuffer/fbuffer.h".freeze, "ext/json/ext/generator/depend".freeze, "ext/json/ext/generator/extconf.rb".freeze, "ext/json/ext/generator/generator.c".freeze, "ext/json/ext/generator/generator.h".freeze, "ext/json/ext/parser/depend".freeze, "ext/json/ext/parser/extconf.rb".freeze, "ext/json/ext/parser/parser.c".freeze, "ext/json/ext/parser/parser.h".freeze, "ext/json/ext/parser/parser.rl".freeze, "ext/json/extconf.rb".freeze, "install.rb".freeze, "java/src/json/ext/ByteListTranscoder.java".freeze, "java/src/json/ext/Generator.java".freeze, "java/src/json/ext/GeneratorMethods.java".freeze, "java/src/json/ext/GeneratorService.java".freeze, "java/src/json/ext/GeneratorState.java".freeze, "java/src/json/ext/OptionsReader.java".freeze, "java/src/json/ext/Parser.java".freeze, "java/src/json/ext/Parser.rl".freeze, "java/src/json/ext/ParserService.java".freeze, "java/src/json/ext/RuntimeInfo.java".freeze, "java/src/json/ext/StringDecoder.java".freeze, "java/src/json/ext/StringEncoder.java".freeze, "java/src/json/ext/Utils.java".freeze, "json-java.gemspec".freeze, "json.gemspec".freeze, "json_pure.gemspec".freeze, "lib/json.rb".freeze, "lib/json/add/bigdecimal.rb".freeze, "lib/json/add/complex.rb".freeze, "lib/json/add/core.rb".freeze, "lib/json/add/date.rb".freeze, "lib/json/add/date_time.rb".freeze, "lib/json/add/exception.rb".freeze, "lib/json/add/ostruct.rb".freeze, "lib/json/add/range.rb".freeze, "lib/json/add/rational.rb".freeze, "lib/json/add/regexp.rb".freeze, "lib/json/add/set.rb".freeze, "lib/json/add/struct.rb".freeze, "lib/json/add/symbol.rb".freeze, "lib/json/add/time.rb".freeze, "lib/json/common.rb".freeze, "lib/json/ext.rb".freeze, "lib/json/ext/.keep".freeze, "lib/json/generic_object.rb".freeze, "lib/json/pure.rb".freeze, "lib/json/pure/generator.rb".freeze, "lib/json/pure/parser.rb".freeze, "lib/json/version.rb".freeze, "references/rfc7159.txt".freeze, "tests/fixtures/fail10.json".freeze, "tests/fixtures/fail11.json".freeze, "tests/fixtures/fail12.json".freeze, "tests/fixtures/fail13.json".freeze, "tests/fixtures/fail14.json".freeze, "tests/fixtures/fail18.json".freeze, "tests/fixtures/fail19.json".freeze, "tests/fixtures/fail2.json".freeze, "tests/fixtures/fail20.json".freeze, "tests/fixtures/fail21.json".freeze, "tests/fixtures/fail22.json".freeze, "tests/fixtures/fail23.json".freeze, "tests/fixtures/fail24.json".freeze, "tests/fixtures/fail25.json".freeze, "tests/fixtures/fail27.json".freeze, "tests/fixtures/fail28.json".freeze, "tests/fixtures/fail3.json".freeze, "tests/fixtures/fail4.json".freeze, "tests/fixtures/fail5.json".freeze, "tests/fixtures/fail6.json".freeze, "tests/fixtures/fail7.json".freeze, "tests/fixtures/fail8.json".freeze, "tests/fixtures/fail9.json".freeze, "tests/fixtures/obsolete_fail1.json".freeze, "tests/fixtures/pass1.json".freeze, "tests/fixtures/pass15.json".freeze, "tests/fixtures/pass16.json".freeze, "tests/fixtures/pass17.json".freeze, "tests/fixtures/pass2.json".freeze, "tests/fixtures/pass26.json".freeze, "tests/fixtures/pass3.json".freeze, "tests/json_addition_test.rb".freeze, "tests/json_common_interface_test.rb".freeze, "tests/json_encoding_test.rb".freeze, "tests/json_ext_parser_test.rb".freeze, "tests/json_fixtures_test.rb".freeze, "tests/json_generator_test.rb".freeze, "tests/json_generic_object_test.rb".freeze, "tests/json_parser_test.rb".freeze, "tests/json_string_matching_test.rb".freeze, "tests/test_helper.rb".freeze, "tools/diff.sh".freeze, "tools/fuzz.rb".freeze, "tools/server.rb".freeze] - s.homepage = "http://flori.github.com/json".freeze - s.licenses = ["Ruby".freeze] - s.rdoc_options = ["--title".freeze, "JSON implemention for Ruby".freeze, "--main".freeze, "README.md".freeze] - s.required_ruby_version = Gem::Requirement.new(">= 1.9".freeze) - s.rubygems_version = "3.0.2".freeze - s.summary = "JSON Implementation for Ruby".freeze - s.test_files = ["./tests/test_helper.rb".freeze] + s.description = "This is a JSON implementation as a Ruby extension in C." + s.email = "flori@ping.de" + s.extensions = ["ext/json/ext/generator/extconf.rb", "ext/json/ext/parser/extconf.rb", "ext/json/extconf.rb"] + s.extra_rdoc_files = ["README.md"] + s.files = ["./tests/test_helper.rb", ".gitignore", ".travis.yml", "CHANGES.md", "Gemfile", "README-json-jruby.md", "README.md", "Rakefile", "VERSION", "diagrams/.keep", "ext/json/ext/fbuffer/fbuffer.h", "ext/json/ext/generator/depend", "ext/json/ext/generator/extconf.rb", "ext/json/ext/generator/generator.c", "ext/json/ext/generator/generator.h", "ext/json/ext/parser/depend", "ext/json/ext/parser/extconf.rb", "ext/json/ext/parser/parser.c", "ext/json/ext/parser/parser.h", "ext/json/ext/parser/parser.rl", "ext/json/extconf.rb", "install.rb", "java/src/json/ext/ByteListTranscoder.java", "java/src/json/ext/Generator.java", "java/src/json/ext/GeneratorMethods.java", "java/src/json/ext/GeneratorService.java", "java/src/json/ext/GeneratorState.java", "java/src/json/ext/OptionsReader.java", "java/src/json/ext/Parser.java", "java/src/json/ext/Parser.rl", "java/src/json/ext/ParserService.java", "java/src/json/ext/RuntimeInfo.java", "java/src/json/ext/StringDecoder.java", "java/src/json/ext/StringEncoder.java", "java/src/json/ext/Utils.java", "json-java.gemspec", "json.gemspec", "json_pure.gemspec", "lib/json.rb", "lib/json/add/bigdecimal.rb", "lib/json/add/complex.rb", "lib/json/add/core.rb", "lib/json/add/date.rb", "lib/json/add/date_time.rb", "lib/json/add/exception.rb", "lib/json/add/ostruct.rb", "lib/json/add/range.rb", "lib/json/add/rational.rb", "lib/json/add/regexp.rb", "lib/json/add/set.rb", "lib/json/add/struct.rb", "lib/json/add/symbol.rb", "lib/json/add/time.rb", "lib/json/common.rb", "lib/json/ext.rb", "lib/json/ext/.keep", "lib/json/generic_object.rb", "lib/json/pure.rb", "lib/json/pure/generator.rb", "lib/json/pure/parser.rb", "lib/json/version.rb", "references/rfc7159.txt", "tests/fixtures/fail10.json", "tests/fixtures/fail11.json", "tests/fixtures/fail12.json", "tests/fixtures/fail13.json", "tests/fixtures/fail14.json", "tests/fixtures/fail18.json", "tests/fixtures/fail19.json", "tests/fixtures/fail2.json", "tests/fixtures/fail20.json", "tests/fixtures/fail21.json", "tests/fixtures/fail22.json", "tests/fixtures/fail23.json", "tests/fixtures/fail24.json", "tests/fixtures/fail25.json", "tests/fixtures/fail27.json", "tests/fixtures/fail28.json", "tests/fixtures/fail3.json", "tests/fixtures/fail4.json", "tests/fixtures/fail5.json", "tests/fixtures/fail6.json", "tests/fixtures/fail7.json", "tests/fixtures/fail8.json", "tests/fixtures/fail9.json", "tests/fixtures/obsolete_fail1.json", "tests/fixtures/pass1.json", "tests/fixtures/pass15.json", "tests/fixtures/pass16.json", "tests/fixtures/pass17.json", "tests/fixtures/pass2.json", "tests/fixtures/pass26.json", "tests/fixtures/pass3.json", "tests/json_addition_test.rb", "tests/json_common_interface_test.rb", "tests/json_encoding_test.rb", "tests/json_ext_parser_test.rb", "tests/json_fixtures_test.rb", "tests/json_generator_test.rb", "tests/json_generic_object_test.rb", "tests/json_parser_test.rb", "tests/json_string_matching_test.rb", "tests/test_helper.rb", "tools/diff.sh", "tools/fuzz.rb", "tools/server.rb"] + s.homepage = "http://flori.github.com/json" + s.licenses = ["Ruby"] + s.rdoc_options = ["--title", "JSON implemention for Ruby", "--main", "README.md"] + s.required_ruby_version = Gem::Requirement.new(">= 1.9") + s.rubygems_version = "3.0.2" + s.summary = "JSON Implementation for Ruby" + s.test_files = ["./tests/test_helper.rb"] - s.add_development_dependency(%q.freeze, [">= 0"]) - s.add_development_dependency(%q.freeze, ["~> 2.0"]) + s.add_development_dependency("rake", [">= 0"]) + s.add_development_dependency("test-unit", ["~> 2.0"]) end From e1f3bce7edc8e8335167dcf38dcedc7687d478c0 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 9 Jul 2019 08:31:27 +0900 Subject: [PATCH 0019/1010] Suppress uninitialized instance variable warnings --- ext/json/ext/generator/generator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index f481346ad..871f258d3 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -696,7 +696,7 @@ static VALUE cState_aref(VALUE self, VALUE name) if (RTEST(rb_funcall(self, i_respond_to_p, 1, name))) { return rb_funcall(self, i_send, 1, name); } else { - return rb_ivar_get(self, rb_intern_str(rb_str_concat(rb_str_new2("@"), name))); + return rb_attr_get(self, rb_intern_str(rb_str_concat(rb_str_new2("@"), name))); } } From bc311ad5f0bca668fdac369fc2b2f1bfc001631c Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Sat, 5 Oct 2019 06:30:27 +0900 Subject: [PATCH 0020/1010] ext/json/parser/parser.rl: Update the source code of parser.c There have been some direct changes in parser.c which is automatically generated from parser.rl. This updates parser.rl to sync the changes: * 91793b8967e0531bd1159a8ff0cc7e50739c7620 * 79ead821dd4880725c9c6bb9645b3fad71715c5b * 80b5a0ff2a7709367178f29d4ebe1c54122b1c27 --- ext/json/ext/parser/parser.rl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl index e7738d656..c1d6d3962 100644 --- a/ext/json/ext/parser/parser.rl +++ b/ext/json/ext/parser/parser.rl @@ -728,7 +728,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } else { json->max_nesting = 100; json->allow_nan = 0; - json->create_additions = 1; + json->create_additions = 0; json->create_id = rb_funcall(mJSON, i_create_id, 0); json->object_class = Qnil; json->array_class = Qnil; @@ -849,14 +849,21 @@ void Init_parser(void) cParser = rb_define_class_under(mExt, "Parser", rb_cObject); eParserError = rb_path2class("JSON::ParserError"); eNestingError = rb_path2class("JSON::NestingError"); + rb_gc_register_mark_object(eParserError); + rb_gc_register_mark_object(eNestingError); rb_define_alloc_func(cParser, cJSON_parser_s_allocate); rb_define_method(cParser, "initialize", cParser_initialize, -1); rb_define_method(cParser, "parse", cParser_parse, 0); rb_define_method(cParser, "source", cParser_source, 0); CNaN = rb_const_get(mJSON, rb_intern("NaN")); + rb_gc_register_mark_object(CNaN); + CInfinity = rb_const_get(mJSON, rb_intern("Infinity")); + rb_gc_register_mark_object(CInfinity); + CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity")); + rb_gc_register_mark_object(CMinusInfinity); i_json_creatable_p = rb_intern("json_creatable?"); i_json_create = rb_intern("json_create"); From 59b29a5f991eaa4e1a66d4ba90e647460f778031 Mon Sep 17 00:00:00 2001 From: tenderlove Date: Sat, 20 Apr 2019 01:19:47 +0000 Subject: [PATCH 0021/1010] Add `GC.compact` again. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🙏 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@67620 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/json/ext/generator/generator.c | 2 ++ ext/json/ext/parser/parser.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 871f258d3..881435e3d 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -1399,6 +1399,8 @@ void Init_generator(void) eGeneratorError = rb_path2class("JSON::GeneratorError"); eNestingError = rb_path2class("JSON::NestingError"); + rb_gc_register_mark_object(eGeneratorError); + rb_gc_register_mark_object(eNestingError); cState = rb_define_class_under(mGenerator, "State", rb_cObject); rb_define_alloc_func(cState, cState_s_allocate); diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 79ca067cb..d91802236 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -2089,6 +2089,8 @@ void Init_parser(void) cParser = rb_define_class_under(mExt, "Parser", rb_cObject); eParserError = rb_path2class("JSON::ParserError"); eNestingError = rb_path2class("JSON::NestingError"); + rb_gc_register_mark_object(eParserError); + rb_gc_register_mark_object(eNestingError); rb_define_alloc_func(cParser, cJSON_parser_s_allocate); rb_define_method(cParser, "initialize", cParser_initialize, -1); rb_define_method(cParser, "parse", cParser_parse, 0); From 75f9a47856493b8cdd455bd013a87919b6193e55 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Sat, 5 Oct 2019 07:00:57 +0900 Subject: [PATCH 0022/1010] ext/json/parser/parser.rl: Use "signed" char to contain negative values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit char is not always signed. In fact, it is unsigned in arm. https://rubyci.org/logs/rubyci.s3.amazonaws.com/scw-9d6766/ruby-master/log/20191004T181708Z.log.html.gz ``` compiling parser.c parser.rl: In function ‘unescape_unicode’: parser.rl:50:5: warning: comparison is always false due to limited range of data type [-Wtype-limits] if (b < 0) return UNI_REPLACEMENT_CHAR; ^ ``` --- ext/json/ext/parser/parser.c | 4 ++-- ext/json/ext/parser/parser.rl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index d91802236..a8ac947d5 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -27,7 +27,7 @@ enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...) /* unicode */ -static const char digit_values[256] = { +static const signed char digit_values[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, @@ -46,7 +46,7 @@ static const char digit_values[256] = { static UTF32 unescape_unicode(const unsigned char *p) { - char b; + signed char b; UTF32 result = 0; b = digit_values[p[0]]; if (b < 0) return UNI_REPLACEMENT_CHAR; diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl index c1d6d3962..6b38bb283 100644 --- a/ext/json/ext/parser/parser.rl +++ b/ext/json/ext/parser/parser.rl @@ -25,7 +25,7 @@ enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...) /* unicode */ -static const char digit_values[256] = { +static const signed char digit_values[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, @@ -44,7 +44,7 @@ static const char digit_values[256] = { static UTF32 unescape_unicode(const unsigned char *p) { - char b; + signed char b; UTF32 result = 0; b = digit_values[p[0]]; if (b < 0) return UNI_REPLACEMENT_CHAR; From b2f06973cddf37cf86c68c82391976358dc05e46 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Sat, 5 Oct 2019 07:00:07 +0900 Subject: [PATCH 0023/1010] ext/json/parser/prereq.mk: Add a "automatically generated" header to parser.c. --- ext/json/ext/parser/parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index a8ac947d5..1fae9ca23 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -1,4 +1,4 @@ - +/* This file is automatically generated from parser.rl by using ragel */ #line 1 "parser.rl" #include "../fbuffer/fbuffer.h" #include "parser.h" From 8ac832ad1c790f2119449af1754f6a80c4d917e7 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 13 May 2019 14:27:54 -0700 Subject: [PATCH 0024/1010] Add NaN / Infinity / MinusInfinity to mark list This prevents the constants from moving. --- ext/json/ext/parser/parser.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 1fae9ca23..0f98cf982 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -2097,8 +2097,13 @@ void Init_parser(void) rb_define_method(cParser, "source", cParser_source, 0); CNaN = rb_const_get(mJSON, rb_intern("NaN")); + rb_gc_register_mark_object(CNaN); + CInfinity = rb_const_get(mJSON, rb_intern("Infinity")); + rb_gc_register_mark_object(CInfinity); + CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity")); + rb_gc_register_mark_object(CMinusInfinity); i_json_creatable_p = rb_intern("json_creatable?"); i_json_create = rb_intern("json_create"); From 619a85865db3714b989cbc8bc097af631cad9a9f Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 11 Nov 2019 15:56:59 +0900 Subject: [PATCH 0025/1010] Removed duplicate file "./tests/test_helper.rb" and "tests/test_helper.rb" in `s.files` are same. --- json.gemspec | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 2 deletions(-) diff --git a/json.gemspec b/json.gemspec index 1cb236d2b..51dd8f02b 100644 --- a/json.gemspec +++ b/json.gemspec @@ -12,14 +12,121 @@ Gem::Specification.new do |s| s.email = "flori@ping.de" s.extensions = ["ext/json/ext/generator/extconf.rb", "ext/json/ext/parser/extconf.rb", "ext/json/extconf.rb"] s.extra_rdoc_files = ["README.md"] - s.files = ["./tests/test_helper.rb", ".gitignore", ".travis.yml", "CHANGES.md", "Gemfile", "README-json-jruby.md", "README.md", "Rakefile", "VERSION", "diagrams/.keep", "ext/json/ext/fbuffer/fbuffer.h", "ext/json/ext/generator/depend", "ext/json/ext/generator/extconf.rb", "ext/json/ext/generator/generator.c", "ext/json/ext/generator/generator.h", "ext/json/ext/parser/depend", "ext/json/ext/parser/extconf.rb", "ext/json/ext/parser/parser.c", "ext/json/ext/parser/parser.h", "ext/json/ext/parser/parser.rl", "ext/json/extconf.rb", "install.rb", "java/src/json/ext/ByteListTranscoder.java", "java/src/json/ext/Generator.java", "java/src/json/ext/GeneratorMethods.java", "java/src/json/ext/GeneratorService.java", "java/src/json/ext/GeneratorState.java", "java/src/json/ext/OptionsReader.java", "java/src/json/ext/Parser.java", "java/src/json/ext/Parser.rl", "java/src/json/ext/ParserService.java", "java/src/json/ext/RuntimeInfo.java", "java/src/json/ext/StringDecoder.java", "java/src/json/ext/StringEncoder.java", "java/src/json/ext/Utils.java", "json-java.gemspec", "json.gemspec", "json_pure.gemspec", "lib/json.rb", "lib/json/add/bigdecimal.rb", "lib/json/add/complex.rb", "lib/json/add/core.rb", "lib/json/add/date.rb", "lib/json/add/date_time.rb", "lib/json/add/exception.rb", "lib/json/add/ostruct.rb", "lib/json/add/range.rb", "lib/json/add/rational.rb", "lib/json/add/regexp.rb", "lib/json/add/set.rb", "lib/json/add/struct.rb", "lib/json/add/symbol.rb", "lib/json/add/time.rb", "lib/json/common.rb", "lib/json/ext.rb", "lib/json/ext/.keep", "lib/json/generic_object.rb", "lib/json/pure.rb", "lib/json/pure/generator.rb", "lib/json/pure/parser.rb", "lib/json/version.rb", "references/rfc7159.txt", "tests/fixtures/fail10.json", "tests/fixtures/fail11.json", "tests/fixtures/fail12.json", "tests/fixtures/fail13.json", "tests/fixtures/fail14.json", "tests/fixtures/fail18.json", "tests/fixtures/fail19.json", "tests/fixtures/fail2.json", "tests/fixtures/fail20.json", "tests/fixtures/fail21.json", "tests/fixtures/fail22.json", "tests/fixtures/fail23.json", "tests/fixtures/fail24.json", "tests/fixtures/fail25.json", "tests/fixtures/fail27.json", "tests/fixtures/fail28.json", "tests/fixtures/fail3.json", "tests/fixtures/fail4.json", "tests/fixtures/fail5.json", "tests/fixtures/fail6.json", "tests/fixtures/fail7.json", "tests/fixtures/fail8.json", "tests/fixtures/fail9.json", "tests/fixtures/obsolete_fail1.json", "tests/fixtures/pass1.json", "tests/fixtures/pass15.json", "tests/fixtures/pass16.json", "tests/fixtures/pass17.json", "tests/fixtures/pass2.json", "tests/fixtures/pass26.json", "tests/fixtures/pass3.json", "tests/json_addition_test.rb", "tests/json_common_interface_test.rb", "tests/json_encoding_test.rb", "tests/json_ext_parser_test.rb", "tests/json_fixtures_test.rb", "tests/json_generator_test.rb", "tests/json_generic_object_test.rb", "tests/json_parser_test.rb", "tests/json_string_matching_test.rb", "tests/test_helper.rb", "tools/diff.sh", "tools/fuzz.rb", "tools/server.rb"] + s.files = [ + ".gitignore", + ".travis.yml", + "CHANGES.md", + "Gemfile", + "README-json-jruby.md", + "README.md", + "Rakefile", + "VERSION", + "diagrams/.keep", + "ext/json/ext/fbuffer/fbuffer.h", + "ext/json/ext/generator/depend", + "ext/json/ext/generator/extconf.rb", + "ext/json/ext/generator/generator.c", + "ext/json/ext/generator/generator.h", + "ext/json/ext/parser/depend", + "ext/json/ext/parser/extconf.rb", + "ext/json/ext/parser/parser.c", + "ext/json/ext/parser/parser.h", + "ext/json/ext/parser/parser.rl", + "ext/json/extconf.rb", + "install.rb", + "java/src/json/ext/ByteListTranscoder.java", + "java/src/json/ext/Generator.java", + "java/src/json/ext/GeneratorMethods.java", + "java/src/json/ext/GeneratorService.java", + "java/src/json/ext/GeneratorState.java", + "java/src/json/ext/OptionsReader.java", + "java/src/json/ext/Parser.java", + "java/src/json/ext/Parser.rl", + "java/src/json/ext/ParserService.java", + "java/src/json/ext/RuntimeInfo.java", + "java/src/json/ext/StringDecoder.java", + "java/src/json/ext/StringEncoder.java", + "java/src/json/ext/Utils.java", + "json-java.gemspec", + "json.gemspec", + "json_pure.gemspec", + "lib/json.rb", + "lib/json/add/bigdecimal.rb", + "lib/json/add/complex.rb", + "lib/json/add/core.rb", + "lib/json/add/date.rb", + "lib/json/add/date_time.rb", + "lib/json/add/exception.rb", + "lib/json/add/ostruct.rb", + "lib/json/add/range.rb", + "lib/json/add/rational.rb", + "lib/json/add/regexp.rb", + "lib/json/add/set.rb", + "lib/json/add/struct.rb", + "lib/json/add/symbol.rb", + "lib/json/add/time.rb", + "lib/json/common.rb", + "lib/json/ext.rb", + "lib/json/ext/.keep", + "lib/json/generic_object.rb", + "lib/json/pure.rb", + "lib/json/pure/generator.rb", + "lib/json/pure/parser.rb", + "lib/json/version.rb", + "references/rfc7159.txt", + "tests/fixtures/fail10.json", + "tests/fixtures/fail11.json", + "tests/fixtures/fail12.json", + "tests/fixtures/fail13.json", + "tests/fixtures/fail14.json", + "tests/fixtures/fail18.json", + "tests/fixtures/fail19.json", + "tests/fixtures/fail2.json", + "tests/fixtures/fail20.json", + "tests/fixtures/fail21.json", + "tests/fixtures/fail22.json", + "tests/fixtures/fail23.json", + "tests/fixtures/fail24.json", + "tests/fixtures/fail25.json", + "tests/fixtures/fail27.json", + "tests/fixtures/fail28.json", + "tests/fixtures/fail3.json", + "tests/fixtures/fail4.json", + "tests/fixtures/fail5.json", + "tests/fixtures/fail6.json", + "tests/fixtures/fail7.json", + "tests/fixtures/fail8.json", + "tests/fixtures/fail9.json", + "tests/fixtures/obsolete_fail1.json", + "tests/fixtures/pass1.json", + "tests/fixtures/pass15.json", + "tests/fixtures/pass16.json", + "tests/fixtures/pass17.json", + "tests/fixtures/pass2.json", + "tests/fixtures/pass26.json", + "tests/fixtures/pass3.json", + "tests/json_addition_test.rb", + "tests/json_common_interface_test.rb", + "tests/json_encoding_test.rb", + "tests/json_ext_parser_test.rb", + "tests/json_fixtures_test.rb", + "tests/json_generator_test.rb", + "tests/json_generic_object_test.rb", + "tests/json_parser_test.rb", + "tests/json_string_matching_test.rb", + "tests/test_helper.rb", + "tests/test_helper.rb", + "tools/diff.sh", + "tools/fuzz.rb", + "tools/server.rb", + ] s.homepage = "http://flori.github.com/json" s.licenses = ["Ruby"] s.rdoc_options = ["--title", "JSON implemention for Ruby", "--main", "README.md"] s.required_ruby_version = Gem::Requirement.new(">= 1.9") s.rubygems_version = "3.0.2" s.summary = "JSON Implementation for Ruby" - s.test_files = ["./tests/test_helper.rb"] + s.test_files = ["tests/test_helper.rb"] s.add_development_dependency("rake", [">= 0"]) s.add_development_dependency("test-unit", ["~> 2.0"]) From 19da336333e63dc9dde7baea47b179e162b7568e Mon Sep 17 00:00:00 2001 From: Ryan Davis Date: Fri, 22 Nov 2019 14:53:04 -0800 Subject: [PATCH 0026/1010] Minor cleanup for ruby 2.7 warnings and failures. --- .travis.yml | 1 + lib/json/common.rb | 5 +++-- lib/json/pure/parser.rb | 10 +++++++++- tests/json_parser_test.rb | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1ac58d828..2962e9d31 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,7 @@ rvm: - 2.4 - 2.5 - 2.6 + - 2.7.0-preview2 - ruby-head - jruby matrix: diff --git a/lib/json/common.rb b/lib/json/common.rb index 7cc852916..05da9d0f9 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -153,7 +153,8 @@ class MissingUnicodeSupport < JSONError; end # * *object_class*: Defaults to Hash # * *array_class*: Defaults to Array def parse(source, opts = {}) - Parser.new(source, opts).parse + opts ||= {} + Parser.new(source, **opts).parse end # Parse the JSON document _source_ into a Ruby data structure and return it. @@ -176,7 +177,7 @@ def parse!(source, opts = {}) :max_nesting => false, :allow_nan => true }.merge(opts) - Parser.new(source, opts).parse + Parser.new(source, **opts).parse end # Generate a JSON document from the Ruby data structure _obj_ and return diff --git a/lib/json/pure/parser.rb b/lib/json/pure/parser.rb index 3a6343bc9..534029649 100644 --- a/lib/json/pure/parser.rb +++ b/lib/json/pure/parser.rb @@ -197,7 +197,15 @@ def parse_string def parse_value case when scan(FLOAT) - @decimal_class && @decimal_class.new(self[1]) || Float(self[1]) + if @decimal_class then + if @decimal_class == BigDecimal then + BigDecimal(self[1]) + else + @decimal_class.new(self[1]) || Float(self[1]) + end + else + Float(self[1]) + end when scan(INTEGER) Integer(self[1]) when scan(TRUE) diff --git a/tests/json_parser_test.rb b/tests/json_parser_test.rb index 5f454eb12..452e177c7 100644 --- a/tests/json_parser_test.rb +++ b/tests/json_parser_test.rb @@ -111,7 +111,7 @@ def test_parse_numbers def test_parse_bigdecimals assert_equal(BigDecimal, JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"].class) - assert_equal(BigDecimal.new("0.901234567890123456789E1"),JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"] ) + assert_equal(BigDecimal("0.901234567890123456789E1"), JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"] ) end if Array.method_defined?(:permutation) From 45be8b4d5a545c61ca274d20b2faf0d929d0604e Mon Sep 17 00:00:00 2001 From: Ryan Davis Date: Wed, 27 Nov 2019 14:55:16 -0800 Subject: [PATCH 0027/1010] Fix syntax warnings from tests. Signed-off-by: Ryan Davis --- tests/json_common_interface_test.rb | 6 +++--- tests/json_parser_test.rb | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/json_common_interface_test.rb b/tests/json_common_interface_test.rb index de88c6e89..53f335ed3 100644 --- a/tests/json_common_interface_test.rb +++ b/tests/json_common_interface_test.rb @@ -27,15 +27,15 @@ def test_index end def test_parser - assert_match /::Parser\z/, JSON.parser.name + assert_match(/::Parser\z/, JSON.parser.name) end def test_generator - assert_match /::Generator\z/, JSON.generator.name + assert_match(/::Generator\z/, JSON.generator.name) end def test_state - assert_match /::Generator::State\z/, JSON.state.name + assert_match(/::Generator::State\z/, JSON.state.name) end def test_create_id diff --git a/tests/json_parser_test.rb b/tests/json_parser_test.rb index 452e177c7..96e71c50a 100644 --- a/tests/json_parser_test.rb +++ b/tests/json_parser_test.rb @@ -92,21 +92,21 @@ def test_parse_numbers assert_raise(JSON::ParserError) { parse('.23') } assert_raise(JSON::ParserError) { parse('023') } assert_equal 23, parse('23') - assert_equal -23, parse('-23') + assert_equal(-23, parse('-23')) assert_equal_float 3.141, parse('3.141') - assert_equal_float -3.141, parse('-3.141') + assert_equal_float(-3.141, parse('-3.141')) assert_equal_float 3.141, parse('3141e-3') assert_equal_float 3.141, parse('3141.1e-3') assert_equal_float 3.141, parse('3141E-3') assert_equal_float 3.141, parse('3141.0E-3') - assert_equal_float -3.141, parse('-3141.0e-3') - assert_equal_float -3.141, parse('-3141e-3') + assert_equal_float(-3.141, parse('-3141.0e-3')) + assert_equal_float(-3.141, parse('-3141e-3')) assert_raise(ParserError) { parse('NaN') } assert parse('NaN', :allow_nan => true).nan? assert_raise(ParserError) { parse('Infinity') } assert_equal 1.0/0, parse('Infinity', :allow_nan => true) assert_raise(ParserError) { parse('-Infinity') } - assert_equal -1.0/0, parse('-Infinity', :allow_nan => true) + assert_equal(-1.0/0, parse('-Infinity', :allow_nan => true)) end def test_parse_bigdecimals From de14dea34074558dc671d7edc71513f0216ce21d Mon Sep 17 00:00:00 2001 From: Ryan Davis Date: Wed, 27 Nov 2019 14:55:28 -0800 Subject: [PATCH 0028/1010] Fix warning from trying to access an uninitialized ivar. Signed-off-by: Ryan Davis --- lib/json/pure/generator.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/json/pure/generator.rb b/lib/json/pure/generator.rb index ccb6fe422..a1143f8b0 100644 --- a/lib/json/pure/generator.rb +++ b/lib/json/pure/generator.rb @@ -250,7 +250,8 @@ def [](name) if respond_to?(name) __send__(name) else - instance_variable_get("@#{name}") + instance_variable_get("@#{name}") if + instance_variables.include?("@#{name}".to_sym) # avoid warning end end From 927c1672c6aaafb45d11e7ac8cad8f7c3a8fb0b6 Mon Sep 17 00:00:00 2001 From: Ryan Davis Date: Wed, 27 Nov 2019 14:58:41 -0800 Subject: [PATCH 0029/1010] Bump to test-unit 3 to get warnings cleaned up. Immaculate test output! Signed-off-by: Ryan Davis --- Rakefile | 4 ++-- json-java.gemspec | 6 +++--- json.gemspec | Bin 5439 -> 5298 bytes json_pure.gemspec | 13 +++++-------- 4 files changed, 10 insertions(+), 13 deletions(-) mode change 100644 => 100755 json-java.gemspec diff --git a/Rakefile b/Rakefile index ce390952d..41f822cdc 100644 --- a/Rakefile +++ b/Rakefile @@ -89,7 +89,7 @@ if defined?(Gem) and defined?(Gem::PackageTask) s.require_path = 'lib' s.add_development_dependency 'rake' - s.add_development_dependency 'test-unit', '~> 2.0' + s.add_development_dependency 'test-unit', '~> 3.0' s.extra_rdoc_files << 'README.md' s.rdoc_options << @@ -127,7 +127,7 @@ if defined?(Gem) and defined?(Gem::PackageTask) s.require_path = 'lib' s.add_development_dependency 'rake' - s.add_development_dependency 'test-unit', '~> 2.0' + s.add_development_dependency 'test-unit', '~> 3.0' s.extra_rdoc_files << 'README.md' s.rdoc_options << diff --git a/json-java.gemspec b/json-java.gemspec old mode 100644 new mode 100755 index f3fa0c913..c99ed89d4 --- a/json-java.gemspec +++ b/json-java.gemspec @@ -19,14 +19,14 @@ spec = Gem::Specification.new do |s| if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, ["~> 2.0"]) + s.add_development_dependency(%q, ["~> 3.0"]) else s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 2.0"]) + s.add_dependency(%q, ["~> 3.0"]) end else s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 2.0"]) + s.add_dependency(%q, ["~> 3.0"]) end end diff --git a/json.gemspec b/json.gemspec index 6e7873fdea50150805c653c8bc10bf705b6ac7dc..4e183b723907fff2e2c52d290aea41e4abbf1405 100644 GIT binary patch delta 110 zcmdn5wMlcsI!0DQLtP{D&6^oH1=$Vt4D@S^keZh=Sx;C@CNU)?zNj>> zBr`WPJ|(pPD4d#?T&Yl!k($Rf`INB3$aD8*_{J|>bO2w`%84dDXn G-~s^G>LUXH delta 177 zcmdm_xnFC;I!0Clb6rE@&6^oH1(^)=CLb0$=*^`71es|H?y0#}R$-|{#hLkedU>hk z8V~{3pkPmbKTQQYTTB(|hI&SN2I`s$B^jxCKyxPV7Iv7tS6FGXw1|WdP(~?M6Kr~F iPH`$o+EK)0a= Gem::Version.new('1.2.0') then - s.add_development_dependency(%q.freeze, [">= 0"]) - s.add_development_dependency(%q.freeze, ["~> 2.0"]) - else - s.add_dependency(%q.freeze, [">= 0"]) - s.add_dependency(%q.freeze, ["~> 2.0"]) - end + if s.respond_to? :add_runtime_dependency then + s.add_development_dependency(%q.freeze, [">= 0"]) + s.add_development_dependency(%q.freeze, ["~> 3.0"]) else s.add_dependency(%q.freeze, [">= 0"]) - s.add_dependency(%q.freeze, ["~> 2.0"]) + s.add_dependency(%q.freeze, ["~> 3.0"]) end end From 5a28298e4f4cbaaadc3fda8a25b4567becb685c0 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 29 Nov 2019 11:19:09 +0900 Subject: [PATCH 0030/1010] Skip useless test `JSONGeneratorTest#test_remove_const_seg` is meaningful only for the extension library version, but nonsense for pure ruby version. --- tests/json_generator_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/json_generator_test.rb b/tests/json_generator_test.rb index 3d9ab7a36..10cbcd178 100644 --- a/tests/json_generator_test.rb +++ b/tests/json_generator_test.rb @@ -75,7 +75,7 @@ def test_remove_const_segv silence do JSON.const_set :SAFE_STATE_PROTOTYPE, const end - end + end if JSON.const_defined?("Ext") def test_generate json = generate(@hash) From 87379e694a103e0db18c1c8f652b0112d8239ebf Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 29 Nov 2019 16:42:18 +0900 Subject: [PATCH 0031/1010] relax test-unit version for old ruby --- Rakefile | 4 ++-- ext/json/ext/parser/parser.c | 2 +- json-java.gemspec | 6 +++--- json_pure.gemspec | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Rakefile b/Rakefile index 41f822cdc..ce390952d 100644 --- a/Rakefile +++ b/Rakefile @@ -89,7 +89,7 @@ if defined?(Gem) and defined?(Gem::PackageTask) s.require_path = 'lib' s.add_development_dependency 'rake' - s.add_development_dependency 'test-unit', '~> 3.0' + s.add_development_dependency 'test-unit', '~> 2.0' s.extra_rdoc_files << 'README.md' s.rdoc_options << @@ -127,7 +127,7 @@ if defined?(Gem) and defined?(Gem::PackageTask) s.require_path = 'lib' s.add_development_dependency 'rake' - s.add_development_dependency 'test-unit', '~> 3.0' + s.add_development_dependency 'test-unit', '~> 2.0' s.extra_rdoc_files << 'README.md' s.rdoc_options << diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 0f98cf982..bc4ac46e0 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -1,4 +1,4 @@ -/* This file is automatically generated from parser.rl by using ragel */ + #line 1 "parser.rl" #include "../fbuffer/fbuffer.h" #include "parser.h" diff --git a/json-java.gemspec b/json-java.gemspec index c99ed89d4..f3fa0c913 100755 --- a/json-java.gemspec +++ b/json-java.gemspec @@ -19,14 +19,14 @@ spec = Gem::Specification.new do |s| if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, ["~> 3.0"]) + s.add_development_dependency(%q, ["~> 2.0"]) else s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 3.0"]) + s.add_dependency(%q, ["~> 2.0"]) end else s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 3.0"]) + s.add_dependency(%q, ["~> 2.0"]) end end diff --git a/json_pure.gemspec b/json_pure.gemspec index 6ee2f4daa..c0f1c51ae 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -27,9 +27,9 @@ Gem::Specification.new do |s| if s.respond_to? :add_runtime_dependency then s.add_development_dependency(%q.freeze, [">= 0"]) - s.add_development_dependency(%q.freeze, ["~> 3.0"]) + s.add_development_dependency(%q.freeze, ["~> 2.0"]) else s.add_dependency(%q.freeze, [">= 0"]) - s.add_dependency(%q.freeze, ["~> 3.0"]) + s.add_dependency(%q.freeze, ["~> 2.0"]) end end From 38f68d1e05174fa466dcabbc924d2098f0d8b824 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Mon, 9 Dec 2019 17:29:55 -0600 Subject: [PATCH 0032/1010] Bump versions for 2.3.0. --- VERSION | 2 +- json.gemspec | Bin 4425 -> 5439 bytes json_pure.gemspec | 19 +++++++++++-------- lib/json/version.rb | 2 +- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/VERSION b/VERSION index ccbccc3dc..276cbf9e2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.2.0 +2.3.0 diff --git a/json.gemspec b/json.gemspec index 51dd8f02b5bc3c5005b190379f3733507553e37b..c3469186713c1c1826510f9d73dc811fadf56c9a 100644 GIT binary patch literal 5439 zcmb7IZExE)5Z-713c|(EW~e0FeNC`*Yg0GN&?bZ3^~(?lv~;qWNTNzonH?1S?K@JI zEX9_@1c|Ni9^M`APItU}LY!Y50%^cQmM%|7t|I5HUK>0iQss*iawB<4ChiM&L`1&W zkeDqh8myJ4>7IUALJEQ^F7$r^Pa{_>erg~xDkQX$w_65-b4X53<{1PmVgXf*r)~=W zl8}=h^2dM>>83ORa!Q6;_Rx(4z&#kk9SDhdO*1MF2WDBo_nZj`eSLx@B+}mjjkH+- zNFY^O*0bTeQ!*+!9}yOzPysUIY3M8d8#!s^cv=_eXH=~uYV*$!%~tZeHV~St6&D5Z zQ_KaUDfuL5dfSABRGFhrM&n~=JaIkX>*&*dT`@`Umy$o{SC@n(Sq%C# z#(aco!c2P){Q;>l5lhLLTgoylA)zeR%p$GM?->S!8$uIOZZGt1c=wS1%gxWpuz}r+ zgcjg7CQ52yIpid$FG3#p5sRTvWj1~P@ypar!X|bHa6JW5N%QHiAkIKwfR<=f&|Nac zmMIt2k@X^qx?|GaBykOT_Q(6n^Xa@M4WSrFT3QYE`QmJPIiJ>63N0NSx6X|Y#wLrs zLm=B3y@dySzfEWJi>u2T-;mLzpo#R{TY#)aXjna3 zL~rOF^`r=PN%zA>!DlAbSIis|lQ;&o2G#7kzvsM6)Q^CE0nApRe8i49h&vX*V|HM+ zvcO-Nq$g)Utxo%wDLVaXY;g@2?c1(ES)Xl|r-~(Tkw&~bUmu?%c z|E*ZO??01>FkoC=6|v;j0|#j>%YK#%I6%W9XF{_XwdJZu!_Zr>Wr&qMp$|g40SdUT zU+WGw0jB8=vaXsvWM4a`0|V;-Or&*SxKtt!R0jfI%Vn1oyf9$}?=*&hC9Ja!84|iQ z=v7f}k_GSbMu(x$Q3*y#qmEXd#01w)*{f95Hk0vL;Pb@|wA$7L;9;?vw7ZP9Xkb8P zR<7RyDmAt@#O8hhSU>T$4AK)(@Miq#*i~y)dufV0M#R<%wUAzfH>&Z-t>06zE*=7n zZ9o$n&Kz8_=-@$VTspjhQAkW|~-uvoX`e#!Qn}HWqke1F|u=jovnD+i3mT zLTQ_W*_^8{j`%|I7!>&Co$666qf+*Tiox_*!GQYAU_gC#(4fA+G#DFcEkU63jE_|o z8yS4iX{MRJio|13MdtQdsz5|EfNpd-Am~A@6?VM3$M43K>9HGC-TWj(Qt#d$2fkVTOz>wQ779_9D<51eA~Xf&)iHA>Ozu6P0&U78WV zN~!F`^GtWHo7GjmaM6wC##NRe)5yno?@nde7-N@NQVX7@bLPb@+RP>#M={mtT$@7# zJ!6QtFcXB56_zo_!GS?Ff5C;l_H`?-K7Fy7yo}xBQp}M#(1Eed5C>B3>k?*AGEWjJ zHhO|#^uK5hK^~sK>@i-n9THsYY0~~INZ*^QQB`$5wSpAkdR%oFa7tckeukqNLVYWS zS7rcJEEIkI5y5HH9^&cPow%c?N2C&@Md8cuhj0fm&-C~QpLPF^ukYsB&)k~P RT)NJE2;VCeZ933@{{z+*W4QnT literal 4425 zcma)AZExE)5dNNDL1XWXz*CVizB(hd&cioGc5-qF<=NSyf|TVfrJ4yIarl>H zf_zusPY6*FrzMbc65J`l11os|sSs#{aTG_>;KYh0e65*;ERpqcyMj_BJD@c)cYqSO zHb$NWZ_mjzcurW3W+bRe@GQ~d7jibJ>U`f)Qc=BD=*E|zV9VgvuF+brg>=%7g^-ML z@=;QL->hd;+i>FP>@=LlVLWp|WuQ{YD#L&Y?DLu_f*(r$SlnC_R#pWV$1O#KnuR%r zNnoRaTQ(Rc?<1!RO3DhOB{vTIS|RCC2F+T#!MTY0Cx+RHc~}7lNwtvXS1NcO$z||@ z1P7psN(EzxcBA71_ez?`Or%N93UF5E7wlq@dEY(d(`%hU@Mx?ESoIZD#M;v()~kvfJlWY`I{jHDHg|}HPd3o))GVd z5CAqQ*kdyU5K7BB)kDkpI;{qkV{^g&HXO8s6>O>@)d~i!j{BsxWwt-gc=CZXYp`t50i>|e<&L$H(Et=VDT`wa{ zpS{7jsj$4GLmX1MdD_il z&a;Ez`c2TIhEUDa@$=Et~bvRXb?pa-y`fe}{dh>DX z{&~Q+A<5tY3Q?IE9iPo;ho^e`ERZ-@Vw7LppAEOp&#?{0P*`(@9T|81_m-G{IB*|o GJoz7avW!&# diff --git a/json_pure.gemspec b/json_pure.gemspec index c0f1c51ae..068deeb62 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -1,14 +1,14 @@ # -*- encoding: utf-8 -*- -# stub: json_pure 2.2.0 ruby lib +# stub: json_pure 2.3.0 ruby lib Gem::Specification.new do |s| s.name = "json_pure".freeze - s.version = "2.2.0" + s.version = "2.3.0" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Florian Frank".freeze] - s.date = "2019-07-13" + s.date = "2019-12-09" s.description = "This is a JSON implementation in pure Ruby.".freeze s.email = "flori@ping.de".freeze s.extra_rdoc_files = ["README.md".freeze] @@ -17,17 +17,20 @@ Gem::Specification.new do |s| s.licenses = ["Ruby".freeze] s.rdoc_options = ["--title".freeze, "JSON implemention for ruby".freeze, "--main".freeze, "README.md".freeze] s.required_ruby_version = Gem::Requirement.new(">= 1.9".freeze) - s.rubygems_version = "3.0.3".freeze + s.rubygems_version = "3.0.6".freeze s.summary = "JSON Implementation for Ruby".freeze s.test_files = ["./tests/test_helper.rb".freeze] if s.respond_to? :specification_version then s.specification_version = 4 - end - if s.respond_to? :add_runtime_dependency then - s.add_development_dependency(%q.freeze, [">= 0"]) - s.add_development_dependency(%q.freeze, ["~> 2.0"]) + if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then + s.add_development_dependency(%q.freeze, [">= 0"]) + s.add_development_dependency(%q.freeze, ["~> 2.0"]) + else + s.add_dependency(%q.freeze, [">= 0"]) + s.add_dependency(%q.freeze, ["~> 2.0"]) + end else s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, ["~> 2.0"]) diff --git a/lib/json/version.rb b/lib/json/version.rb index 115eb5bb9..9d781df87 100644 --- a/lib/json/version.rb +++ b/lib/json/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module JSON # JSON version - VERSION = '2.2.0' + VERSION = '2.3.0' VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc: VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc: VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: From 49317c1328fc08e6bd146164e7ef16c7cde84e0f Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Tue, 10 Dec 2019 11:44:32 +0100 Subject: [PATCH 0033/1010] Ignore log files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 69b608db7..e8b1a5226 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ Gemfile.lock */**/Makefile */**/*.o .byebug_history +*.log From c194360ac2677fec94b05d9507e9e54871940335 Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Tue, 10 Dec 2019 11:53:29 +0100 Subject: [PATCH 0034/1010] Update travis config --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2962e9d31..f2a846264 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ language: ruby # Specify which ruby versions you wish to run your tests on, each version will be used rvm: - - 1.9.3 - 2.0.0 - 2.1 - 2.2 @@ -12,12 +11,11 @@ rvm: - 2.4 - 2.5 - 2.6 - - 2.7.0-preview2 + - 2.7.0-preview3 - ruby-head - jruby matrix: allow_failures: - - rvm: 1.9.3 - rvm: ruby-head - rvm: jruby script: "bundle exec rake" From acabfebf14361090af1771ecc3c22b8dcb740421 Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Tue, 10 Dec 2019 12:01:39 +0100 Subject: [PATCH 0035/1010] Make tests green on jruby --- json.gemspec | Bin 5439 -> 5439 bytes json_pure.gemspec | 2 +- tests/json_generator_test.rb | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/json.gemspec b/json.gemspec index c3469186713c1c1826510f9d73dc811fadf56c9a..a8c45392da34e9208cf55cdd297af120147c2c3b 100644 GIT binary patch delta 15 Wcmdn5wO?z)Mn)z>gUy>6dxQZox&@v9 delta 15 Wcmdn5wO?z)Mn)zB%gvh^dxQZo=>?_$ diff --git a/json_pure.gemspec b/json_pure.gemspec index 068deeb62..37c5fed0e 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -8,7 +8,7 @@ Gem::Specification.new do |s| s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Florian Frank".freeze] - s.date = "2019-12-09" + s.date = "2019-12-10" s.description = "This is a JSON implementation in pure Ruby.".freeze s.email = "flori@ping.de".freeze s.extra_rdoc_files = ["README.md".freeze] diff --git a/tests/json_generator_test.rb b/tests/json_generator_test.rb index 10cbcd178..ee19fa5e6 100644 --- a/tests/json_generator_test.rb +++ b/tests/json_generator_test.rb @@ -49,6 +49,7 @@ def silence end def test_remove_const_segv + return if RUBY_ENGINE == 'jruby' stress = GC.stress const = JSON::SAFE_STATE_PROTOTYPE.dup From 579ae85676f8a41c62ce9d2230db969ead9707bb Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Tue, 10 Dec 2019 12:36:28 +0100 Subject: [PATCH 0036/1010] Add some more recent jruby --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index f2a846264..a9ae42c78 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,9 +14,11 @@ rvm: - 2.7.0-preview3 - ruby-head - jruby + - jruby-9.2.7.0 matrix: allow_failures: - rvm: ruby-head - rvm: jruby + - rvm: jruby-9.2.7.0 script: "bundle exec rake" sudo: false From 92cf5c451a6ec0f3a00e291eb909e57cf38fbea4 Mon Sep 17 00:00:00 2001 From: "NARUSE, Yui" Date: Thu, 12 Dec 2019 02:24:15 +0900 Subject: [PATCH 0037/1010] v2.3.0 --- json.gemspec | Bin 5439 -> 5439 bytes json_pure.gemspec | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/json.gemspec b/json.gemspec index a8c45392da34e9208cf55cdd297af120147c2c3b..63693d573417067c3f4fc041f4616505fb172e36 100644 GIT binary patch delta 21 dcmdn5wO?z)CPqfX&6^py1sRPu?-y!d0svQu2PFUi delta 21 dcmdn5wO?z)CPqes&6^py1sTmY?-y!d0svQz2PXgk diff --git a/json_pure.gemspec b/json_pure.gemspec index 37c5fed0e..08413d677 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -8,7 +8,7 @@ Gem::Specification.new do |s| s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Florian Frank".freeze] - s.date = "2019-12-10" + s.date = "2019-12-11" s.description = "This is a JSON implementation in pure Ruby.".freeze s.email = "flori@ping.de".freeze s.extra_rdoc_files = ["README.md".freeze] @@ -17,7 +17,7 @@ Gem::Specification.new do |s| s.licenses = ["Ruby".freeze] s.rdoc_options = ["--title".freeze, "JSON implemention for ruby".freeze, "--main".freeze, "README.md".freeze] s.required_ruby_version = Gem::Requirement.new(">= 1.9".freeze) - s.rubygems_version = "3.0.6".freeze + s.rubygems_version = "3.0.3".freeze s.summary = "JSON Implementation for Ruby".freeze s.test_files = ["./tests/test_helper.rb".freeze] From ae6bf43dc1d7baa428df0ac95cdf17eba0c432b7 Mon Sep 17 00:00:00 2001 From: "NARUSE, Yui" Date: Sun, 22 Dec 2019 04:48:21 +0900 Subject: [PATCH 0038/1010] Add procedure of releasing a gem --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 5923cfb82..af1ed68ea 100644 --- a/README.md +++ b/README.md @@ -390,6 +390,19 @@ Here are the median comparisons for completeness' sake: secs/call ``` +## Development +### Release + +``` +rbenv shell 2.6.5 +rake build +gem push pkg/json-2.3.0.gem + +rbenv shell jruby-9.2.9.0 +rake build +gem push pkg/json-2.3.0-java.gem +``` + ## Author Florian Frank From 78ec5e2bd32b2c2b8db50919e35c29d9f9cdecb2 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Mon, 23 Dec 2019 08:49:50 +0900 Subject: [PATCH 0039/1010] keyword argument is provided after Ruby 2.0+ --- Rakefile | 2 +- json.gemspec | Bin 5439 -> 5439 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index ce390952d..c75f5bbca 100644 --- a/Rakefile +++ b/Rakefile @@ -100,7 +100,7 @@ if defined?(Gem) and defined?(Gem::PackageTask) s.email = "flori@ping.de" s.homepage = "http://flori.github.com/#{PKG_NAME}" s.license = 'Ruby' - s.required_ruby_version = '>= 1.9' + s.required_ruby_version = '>= 2.0' end desc 'Creates a json_pure.gemspec file' diff --git a/json.gemspec b/json.gemspec index 63693d573417067c3f4fc041f4616505fb172e36..7af20558d8c8aec7bb3dd18726f6885ea873481b 100644 GIT binary patch delta 16 Ycmdn5wO?z)R3T;~J%i0NgqAY_05#VI!vFvP delta 16 Ycmdn5wO?z)R3T Date: Sun, 29 Dec 2019 18:58:27 +0800 Subject: [PATCH 0040/1010] Restore json.gemspec from 619a85865db3714b989cbc8bc097af631cad9a9f --- json.gemspec | Bin 5439 -> 4425 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/json.gemspec b/json.gemspec index 7af20558d8c8aec7bb3dd18726f6885ea873481b..c29431a09d66c0387ac201062fe20c40690b112a 100644 GIT binary patch literal 4425 zcma)AZExE)5dNNDL1XWXz%!9EzC0t1F2XlPc64+J)!Es+hKv;~qnZgGarl?y zf_zusj|fo_rxlQM65J`l11os|sSs#{N%S&04vwr?!qM@GY6=E zYh&bD@b;V>2hRyB(2N9C3!bN1{6fw~Rh{ozN^7cD3f=e;lxz{)+BMqfm5@&Qu@sU~ zPCiP?@7wj9Y8y^+d~zBdCt-5pg33Wvs;5o#7@ z7$$+W25#A4oV<^mE~qFgjh4bV@N12vM>(`>-3I3(?vD+#v3Xbm2T8S%=2s?o5y?gH zf&_b@no0#zh;F0f1NTar%3Ne=!AfveXBY22e7cCLyd|YyK?(^8q9xO8$%TaAMJqui z4@^ays_aDXe}8v0a0)jnp@pH`3T^S^8C5l4(GP0P}rUgy~zeW0 zQ~EQ$n+gjP$jE>?@}6$E>|TyTai0JyL9Pyk&p|%03=W24De!@BY&lUgp#Jdwvahwr zdJEbn;VDfHa#%34hHF;A6)%Lx@LWqQVjtj;L^X`aUmw#>2TRdn}|?jbT^O8$CGL59U~Y|XToskPLQ zJ_dkw2KLyD0ff@B$@JJVzD~b!{aHubV&ny!3|Qjbid#h=md*Xh`krFy%~5 zm72`H1xyBP5t9*H#AL_@F?qx+z-weEwWbuL*u(IOjt{07?9>)l8!W_?&y;ZuvY;7E zK)cCs3bNKXZkfqr0@PKl2}pnY^)ZqKH&~Y1UR)G~d4MYOz9O4ckB`c5~TER8Z5Uokdq#*Jp8Tr$sY+t(rxI z>9aQ&H#L@cGv%tA-f)&`2RA#{33tu3gK!vXrp*jSg8jkO%(aD(c4{@NVTeO2H&5GH z%z3sqd>=ByC-@2AIkspV;-60b0e&Ee5*LMPs*1{unT)M-SNl_S@1E6VsqY8VU@#xI z?#lzd4QUP!P>R~j==f|#J3Q5!XMx1Q5~KX$J~rGsKc_YrLt(=ic4XZ3-&s_7GgMOSv}D_Dzk%PzkNr_ zlBL*^m>{tg-p9M+-RX{Z&xrGrLm&-!$kOG6b1c$B2~VakXy-9GImegVgOUaiRYUcpAB4@k0ZVQ6Zs~yxlSwTtG6J%rgjB!~&`qPu&#$ zB_Su@<@W(0(oJasz+gUxYmHBNjuU%53`n!{@1+giY)Y;Cc$AlIGK2L7aiW04>p|pu1#> zEmJP4BkM&Jb*o5`zw&f=3K~%TozU^2l6^-L& zh~CnB>PZpolJ4&t1)rEyUodk>OyU^S8dS6E{+^36Q9l9t88BOg@(DZUAnsWJPuYRl z$^w6FlAfFawL0xzrs(vmvBfo9v~RltWqr0;o+_5WWg78*4dzN<%KZSAZYzm}zV!&c;k*8#9ex+gRX@4ammeHhSBrZKL(i z7E0S3%;sEual{vr$DqJB?^KUs8I`gxR1Bui3I^0?1_SD|g9h~lroq@qYY76KXMC)( z*vR06PBYE)RU{sRDl)gvQUxNS0d%9w0YMLHt+3gI0&tI9<;)#q^(cRMc;pm=N26iIsZokvcf}LP=+cY; zR!U_P&okY*ZdOT5B) zG6Se$q3H9E2u`E+5YI<0+UfZbsRU_J`11Q9+(XPWJ^sOG-M{1OyLs{Bw_gO#65eg+ zWiN3kWu{y=M@EbO-P%@8o-$Qu@!vZf4jvD;Odyte5T}1iU7jw#zQaal8luQb@>As= M!uLu=n-28f|1f)FrvLx| From 1920653013a26394a5102b6203205222dee2566d Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Sun, 29 Dec 2019 18:59:55 +0800 Subject: [PATCH 0041/1010] Gem::Specification#date is set automatically by RubyGems.org. --- json.gemspec | 1 - 1 file changed, 1 deletion(-) diff --git a/json.gemspec b/json.gemspec index c29431a09..1a23eb6de 100644 --- a/json.gemspec +++ b/json.gemspec @@ -7,7 +7,6 @@ Gem::Specification.new do |s| s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.require_paths = ["lib"] s.authors = ["Florian Frank"] - s.date = "2019-02-21" s.description = "This is a JSON implementation as a Ruby extension in C." s.email = "flori@ping.de" s.extensions = ["ext/json/ext/generator/extconf.rb", "ext/json/ext/parser/extconf.rb", "ext/json/extconf.rb"] From cc2b5d18ac016eead0722531bcd343054500e223 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Sun, 29 Dec 2019 19:05:54 +0800 Subject: [PATCH 0042/1010] Removed gemspec_ext from gemspec task because it always add control char to gemspec. --- Rakefile | 41 +---------------------------------------- json.gemspec | 2 +- 2 files changed, 2 insertions(+), 41 deletions(-) diff --git a/Rakefile b/Rakefile index c75f5bbca..12938c7ad 100644 --- a/Rakefile +++ b/Rakefile @@ -115,47 +115,8 @@ if defined?(Gem) and defined?(Gem::PackageTask) pkg.package_files = PKG_FILES end - spec_ext = Gem::Specification.new do |s| - s.name = 'json' - s.version = PKG_VERSION - s.summary = PKG_TITLE - s.description = "This is a JSON implementation as a Ruby extension in C." - - s.files = PKG_FILES - - s.extensions = FileList['ext/**/extconf.rb'] - - s.require_path = 'lib' - s.add_development_dependency 'rake' - s.add_development_dependency 'test-unit', '~> 2.0' - - s.extra_rdoc_files << 'README.md' - s.rdoc_options << - '--title' << 'JSON implemention for Ruby' << '--main' << 'README.md' - s.test_files.concat Dir['./tests/test_*.rb'] - - s.author = "Florian Frank" - s.email = "flori@ping.de" - s.homepage = "http://flori.github.com/#{PKG_NAME}" - s.license = 'Ruby' - s.required_ruby_version = '>= 1.9' - end - - desc 'Creates a json.gemspec file' - task :gemspec_ext => :version do - File.open('json.gemspec', 'w') do |gemspec| - gemspec.write spec_ext.to_ruby - end - end - - Gem::PackageTask.new(spec_ext) do |pkg| - pkg.need_tar = true - pkg.package_files = PKG_FILES - end - - desc 'Create all gemspec files' - task :gemspec => [ :gemspec_pure, :gemspec_ext ] + task :gemspec => [ :gemspec_pure ] end desc m = "Writing version information for #{PKG_VERSION}" diff --git a/json.gemspec b/json.gemspec index 1a23eb6de..0156355a8 100644 --- a/json.gemspec +++ b/json.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |s| s.name = "json" - s.version = "2.3.0" + s.version = File.read('VERSION').chomp s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.require_paths = ["lib"] From e1a816db83b6ff000064b7afb71ad243b934968f Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Sun, 29 Dec 2019 19:08:25 +0800 Subject: [PATCH 0043/1010] Added instruction to Relase section --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index af1ed68ea..682db2f25 100644 --- a/README.md +++ b/README.md @@ -391,8 +391,11 @@ Here are the median comparisons for completeness' sake: ``` ## Development + ### Release +Update the json.gemspec and json-java.gemspec. + ``` rbenv shell 2.6.5 rake build From 4ede0a7d1957bab4df69c81ef0808c83f0930931 Mon Sep 17 00:00:00 2001 From: zverok Date: Thu, 8 Mar 2018 17:32:36 +0200 Subject: [PATCH 0044/1010] Enchance generic JSON and #generate docs --- lib/json.rb | 50 +++++++++++++++++++++++++++++++++++++++++----- lib/json/common.rb | 29 +++++++++++++++------------ 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/lib/json.rb b/lib/json.rb index 947ac630a..ec9b571be 100644 --- a/lib/json.rb +++ b/lib/json.rb @@ -9,8 +9,11 @@ # JSON is completely language agnostic, making it the ideal interchange format. # # Built on two universally available structures: -# 1. A collection of name/value pairs. Often referred to as an _object_, hash table, record, struct, keyed list, or associative array. -# 2. An ordered list of values. More commonly called an _array_, vector, sequence or list. +# +# 1. A collection of name/value pairs. Often referred to as an _object_, hash table, +# record, struct, keyed list, or associative array. +# 2. An ordered list of values. More commonly called an _array_, vector, sequence or +# list. # # To read more about JSON visit: http://json.org # @@ -22,7 +25,7 @@ # require 'json' # # my_hash = JSON.parse('{"hello": "goodbye"}') -# puts my_hash["hello"] => "goodbye" +# puts my_hash["hello"] # => "goodbye" # # Notice the extra quotes '' around the hash notation. Ruby expects # the argument to be a string and can't convert objects like a hash or array. @@ -37,13 +40,50 @@ # require 'json' # # my_hash = {:hello => "goodbye"} -# puts JSON.generate(my_hash) => "{\"hello\":\"goodbye\"}" +# puts JSON.generate(my_hash) # => "{\"hello\":\"goodbye\"}" # # Or an alternative way: # # require 'json' -# puts {:hello => "goodbye"}.to_json => "{\"hello\":\"goodbye\"}" +# puts {:hello => "goodbye"}.to_json # => "{\"hello\":\"goodbye\"}" +# +# JSON.generate only allows objects or arrays to be converted +# to JSON syntax. to_json, however, accepts many Ruby classes +# even though it acts only as a method for serialization: +# +# require 'json' # +# 1.to_json => "1" +# +# The {#generate}[rdoc-ref:JSON#generate] method accepts a variety of options +# to set the formatting of string output and defining what input is accepteable. +# There are also shortcut methods pretty_generate (with a set of options to +# generate human-readable multiline JSON) and fast_generate (with a set of +# options to generate JSON faster at the price of disabling some checks). +# +# == Extended rendering and loading of Ruby objects +# +# JSON library provides optional _additions_ allowing to serialize and +# deserialize Ruby classes without loosing their type. +# +# # without additions +# require "json" +# json = JSON.generate({range: 1..3, regex: /test/}) +# # => '{"range":"1..3","regex":"(?-mix:test)"}' +# JSON.parse(json) +# # => {"range"=>"1..3", "regex"=>"(?-mix:test)"} +# +# # with additions +# require "json/add/range" +# require "json/add/regexp" +# json = JSON.generate({range: 1..3, regex: /test/}) +# # => '{"range":{"json_class":"Range","a":[1,3,false]},"regex":{"json_class":"Regexp","o":0,"s":"test"}}' +# JSON.parse(json) +# # => {"range"=>{"json_class"=>"Range", "a"=>[1, 3, false]}, "regex"=>{"json_class"=>"Regexp", "o"=>0, "s"=>"test"}} +# JSON.load(json) +# # => {"range"=>1..3, "regex"=>/test/} +# +# See JSON.load for details. module JSON require 'json/version' diff --git a/lib/json/common.rb b/lib/json/common.rb index 3be9fd8dc..5ba8f1d29 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -180,27 +180,30 @@ def parse!(source, opts = {}) end # Generate a JSON document from the Ruby data structure _obj_ and return - # it. _state_ is * a JSON::State object, - # * or a Hash like object (responding to to_hash), - # * an object convertible into a hash by a to_h method, - # that is used as or to configure a State object. + # it. _opts_ is + # * a Hash like object (responding to +to_hash+), + # * or an object convertible into a hash by a +to_h+ method, + # * or a JSON::State object. # - # It defaults to a state object, that creates the shortest possible JSON text - # in one line, checks for circular data structures and doesn't allow NaN, + # If hash-alike or hash-convertible object is provided, it is internally + # converted into a State object. + # + # The default options are set to create the shortest possible JSON text + # in one line, check for circular data structures and do not allow NaN, # Infinity, and -Infinity. # - # A _state_ hash can have the following keys: - # * *indent*: a string used to indent levels (default: ''), - # * *space*: a string that is put after, a : or , delimiter (default: ''), - # * *space_before*: a string that is put before a : pair delimiter (default: ''), - # * *object_nl*: a string that is put at the end of a JSON object (default: ''), - # * *array_nl*: a string that is put at the end of a JSON array (default: ''), + # An _opts_ hash can have the following keys: + # * *indent*: a string used to indent levels (default: ''), + # * *space*: a string that is put after a : pair delimiter (default: ''), + # * *space_before*: a string that is put before a : pair delimiter (default: ''), + # * *object_nl*: a string that is put at the end of a JSON object (default: ''), + # * *array_nl*: a string that is put at the end of a JSON array (default: ''), # * *allow_nan*: true if NaN, Infinity, and -Infinity should be # generated, otherwise an exception is thrown if these values are # encountered. This options defaults to false. # * *max_nesting*: The maximum depth of nesting allowed in the data # structures from which JSON is to be generated. Disable depth checking - # with :max_nesting => false, it defaults to 100. + # with max_nesting: false, it defaults to 100. # # See also the fast_generate for the fastest creation method with the least # amount of sanity checks, and the pretty_generate method for some From 3845491d92c27cf55a8962f8c23429eaafda036f Mon Sep 17 00:00:00 2001 From: zverok Date: Sun, 29 Dec 2019 22:55:23 +0200 Subject: [PATCH 0045/1010] Fix examples syntax --- lib/json.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/json.rb b/lib/json.rb index ec9b571be..151d9c2f2 100644 --- a/lib/json.rb +++ b/lib/json.rb @@ -45,7 +45,7 @@ # Or an alternative way: # # require 'json' -# puts {:hello => "goodbye"}.to_json # => "{\"hello\":\"goodbye\"}" +# puts({:hello => "goodbye"}.to_json) # => "{\"hello\":\"goodbye\"}" # # JSON.generate only allows objects or arrays to be converted # to JSON syntax. to_json, however, accepts many Ruby classes @@ -53,7 +53,7 @@ # # require 'json' # -# 1.to_json => "1" +# 1.to_json # => "1" # # The {#generate}[rdoc-ref:JSON#generate] method accepts a variety of options # to set the formatting of string output and defining what input is accepteable. From 9f430a7bba152dc2562b4aa4b44f56df248ae484 Mon Sep 17 00:00:00 2001 From: noraj Date: Fri, 3 Jan 2020 16:10:42 +0100 Subject: [PATCH 0046/1010] add metadata --- json.gemspec | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/json.gemspec b/json.gemspec index 0156355a8..443c32460 100644 --- a/json.gemspec +++ b/json.gemspec @@ -120,6 +120,14 @@ Gem::Specification.new do |s| "tools/server.rb", ] s.homepage = "http://flori.github.com/json" + s.metadata = { + 'bug_tracker_uri' => 'https://github.com/flori/json/issues', + 'changelog_uri' => 'https://github.com/flori/json/blob/master/CHANGES.md', + 'documentation_uri' => 'http://flori.github.io/json/doc/index.html', + 'homepage_uri' => 'http://flori.github.io/json/', + 'source_code_uri' => 'https://github.com/flori/json', + 'wiki_uri' => 'https://github.com/flori/json/wiki' + } s.licenses = ["Ruby"] s.rdoc_options = ["--title", "JSON implemention for Ruby", "--main", "README.md"] s.required_ruby_version = Gem::Requirement.new(">= 2.0") From bfa3ff603597dc8025aebe008b55c54a3726a001 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 16 Jan 2020 05:15:52 +0100 Subject: [PATCH 0047/1010] Test with TruffleRuby in CI --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index a9ae42c78..9d36665d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,7 @@ rvm: - ruby-head - jruby - jruby-9.2.7.0 + - truffleruby matrix: allow_failures: - rvm: ruby-head From a4d495c047b5212e22ad48aba18a8e6a50759325 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 19 Mar 2020 01:46:37 +0100 Subject: [PATCH 0048/1010] Add truffleruby in allow_failures as requested --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 9d36665d0..920a4ac4d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,5 +21,6 @@ matrix: - rvm: ruby-head - rvm: jruby - rvm: jruby-9.2.7.0 + - rvm: truffleruby script: "bundle exec rake" sudo: false From e7e3732130df477a43c86ac59e915a2651928249 Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Wed, 6 May 2020 07:59:54 -0500 Subject: [PATCH 0049/1010] Rdoc enhancements --- lib/json.rb | 441 +++++++++++++++++++++++++++++++++++++------- lib/json/common.rb | 445 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 732 insertions(+), 154 deletions(-) diff --git a/lib/json.rb b/lib/json.rb index 151d9c2f2..a96f4bc61 100644 --- a/lib/json.rb +++ b/lib/json.rb @@ -2,88 +2,405 @@ require 'json/common' ## -# = JavaScript Object Notation (JSON) +# = JavaScript \Object Notation (\JSON) # -# JSON is a lightweight data-interchange format. It is easy for us -# humans to read and write. Plus, equally simple for machines to generate or parse. -# JSON is completely language agnostic, making it the ideal interchange format. +# \JSON is a lightweight data-interchange format. # -# Built on two universally available structures: +# A \JSON value is one of the following: +# - Double-quoted text: "foo". +# - Number: +1+, +1.0+, +2.0e2+. +# - Boolean: +true+, +false+. +# - Null: +null+. +# - \Array: an ordered list of values, enclosed by square brackets: +# ["foo", 1, 1.0, 2.0e2, true, false, null] # -# 1. A collection of name/value pairs. Often referred to as an _object_, hash table, -# record, struct, keyed list, or associative array. -# 2. An ordered list of values. More commonly called an _array_, vector, sequence or -# list. +# - \Object: a collection of name/value pairs, enclosed by curly braces; +# each name is double-quoted text; +# the values may be any \JSON values: +# {"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null} # -# To read more about JSON visit: http://json.org +# A \JSON array or object may contain nested arrays, objects, and scalars +# to any depth: +# {"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]} +# [{"foo": 0, "bar": 1}, ["baz", 2]] # -# == Parsing JSON -# -# To parse a JSON string received by another application or generated within -# your existing application: +# == Using \Module \JSON # +# To make module \JSON available in your code, begin with: # require 'json' # -# my_hash = JSON.parse('{"hello": "goodbye"}') -# puts my_hash["hello"] # => "goodbye" +# All examples here assume that this has been done. # -# Notice the extra quotes '' around the hash notation. Ruby expects -# the argument to be a string and can't convert objects like a hash or array. +# === Parsing \JSON # -# Ruby converts your string into a hash +# You can parse a \String containing \JSON data using +# either of two methods: +# - JSON.parse(source, opts) +# - JSON.parse!(source, opts) # -# == Generating JSON +# where +# - +source+ is a Ruby object. +# - +opts+ is a \Hash object containing options +# that control both input allowed and output formatting. # -# Creating a JSON string for communication or serialization is -# just as simple. +# The difference between the two methods +# is that JSON.parse! omits some checks +# and may not be safe for some +source+ data; +# use it only for data from trusted sources. +# Use the safer method JSON.parse for less trusted sources. # -# require 'json' +# ==== Parsing \JSON Arrays # -# my_hash = {:hello => "goodbye"} -# puts JSON.generate(my_hash) # => "{\"hello\":\"goodbye\"}" +# When +source+ is a \JSON array, JSON.parse by default returns a Ruby \Array: +# json = '["foo", 1, 1.0, 2.0e2, true, false, null]' +# ruby = JSON.parse(json) +# ruby # => ["foo", 1, 1.0, 200.0, true, false, nil] +# ruby.class # => Array # -# Or an alternative way: +# The \JSON array may contain nested arrays, objects, and scalars +# to any depth: +# json = '[{"foo": 0, "bar": 1}, ["baz", 2]]' +# JSON.parse(json) # => [{"foo"=>0, "bar"=>1}, ["baz", 2]] # -# require 'json' -# puts({:hello => "goodbye"}.to_json) # => "{\"hello\":\"goodbye\"}" +# ==== Parsing \JSON \Objects +# +# When the source is a \JSON object, JSON.parse by default returns a Ruby \Hash: +# json = '{"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}' +# ruby = JSON.parse(json) +# ruby # => {"a"=>"foo", "b"=>1, "c"=>1.0, "d"=>200.0, "e"=>true, "f"=>false, "g"=>nil} +# ruby.class # => Hash +# +# The \JSON object may contain nested arrays, objects, and scalars +# to any depth: +# json = '{"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]}' +# JSON.parse(json) # => {"foo"=>{"bar"=>1, "baz"=>2}, "bat"=>[0, 1, 2]} +# +# ==== Parsing \JSON Scalars +# +# When the source is a \JSON scalar (not an array or object), +# JSON.parse returns a Ruby scalar. +# +# \String: +# ruby = JSON.parse('"foo"') +# ruby # => "foo" +# ruby.class # => String +# \Integer: +# ruby = JSON.parse('1') +# ruby # => 1 +# ruby.class # => Integer +# \Float: +# ruby = JSON.parse('1.0') +# ruby # => 1.0 +# ruby.class # => Float +# ruby = JSON.parse('2.0e2') +# ruby # => 200 +# ruby.class # => Float +# Boolean: +# ruby = JSON.parse('true') +# ruby # => true +# ruby.class # => TrueClass +# ruby = JSON.parse('false') +# ruby # => false +# ruby.class # => FalseClass +# Null: +# ruby = JSON.parse('null') +# ruby # => nil +# ruby.class # => NilClass +# +# === Generating \JSON +# +# To generate a Ruby \String containing \JSON data, +# use method JSON.generate(source, opts), where +# - +source+ is a Ruby object. +# - +opts+ is a \Hash object containing options +# that control both input allowed and output formatting. +# +# ==== Generating \JSON from Arrays +# +# When the source is a Ruby \Array, JSON.generate returns +# a \String containing a \JSON array: +# ruby = [0, 's', :foo] +# json = JSON.generate(ruby) +# json # => "[0,\"s\",\"foo\"]" +# +# The Ruby \Array array may contain nested arrays, hashes, and scalars +# to any depth: +# ruby = [0, [1, 2], {foo: 3, bar: 4}] +# json = JSON.generate(ruby) +# json # => "[0,[1,2],{\"foo\":3,\"bar\":4}]" +# +# ==== Generating \JSON from Hashes +# +# When the source is a Ruby \Hash, JSON.generate returns +# a \String containing a \JSON object: +# ruby = {foo: 0, bar: 's', baz: :bat} +# json = JSON.generate(ruby) +# json # => "{\"foo\":0,\"bar\":\"s\",\"baz\":\"bat\"}" +# +# The Ruby \Hash array may contain nested arrays, hashes, and scalars +# to any depth: +# ruby = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad} +# json = JSON.generate(ruby) +# json # => "{\"foo\":[0,1],\"bar\":{\"baz\":2,\"bat\":3},\"bam\":\"bad\"}" +# +# ==== Generating \JSON from Other Objects +# +# When the source is neither an \Array nor a \Hash, +# the generated \JSON data depends on the class of the source. +# +# When the source is a Ruby \Integer or \Float, JSON.generate returns +# a \String containing a \JSON number: +# JSON.generate(Integer(0)) # => "0"" +# JSON.generate(Float(1.0)) # => "1.0" +# +# When the source is a Ruby \String, JSON.generate returns +# a \String containing a \JSON string (with double-quotes): +# JSON.generate('A string') # => "\"A string\"" +# +# When the source is +true+, +false+ or +nil+, JSON.generate returns +# a \String containing the corresponding \JSON token: +# JSON.generate(true) # => "true" +# JSON.generate(false) # => "false" +# JSON.generate(nil) # => "null" +# +# When the source is none of the above, JSON.generate returns +# a \String containing a \JSON string representation of the source: +# JSON.generate(:foo) # => "\"foo\"" +# JSON.generate(Complex(0, 0)) # => "\"0+0i\"" +# JSON.generate(Dir.new('.')) # => "\"#\"" +# +# == \JSON Additions +# +# When you "round trip" a non-\String object from Ruby to \JSON and back, +# you have a new \String, instead of the object you began with: +# ruby0 = Range.new(0, 2) +# json = JSON.generate(ruby0) +# json # => "\"0..2\"" +# ruby1 = JSON.parse(json) +# ruby1 # => "0..2" +# ruby1.class # => String +# +# You can use \JSON _additions_ to preserve the original object. +# The addition is an extension of a ruby class, so that: +# - \JSON.generate stores more information in the \JSON string. +# - \JSON.parse, called with option +create_additions+, +# uses that information to create a proper Ruby object. # -# JSON.generate only allows objects or arrays to be converted -# to JSON syntax. to_json, however, accepts many Ruby classes -# even though it acts only as a method for serialization: +# This example shows a \Range being generated into \JSON +# and parsed back into Ruby, both without and with +# the addition for \Range: +# ruby = Range.new(0, 2) +# # This passage does not use the addition for Range. +# json0 = JSON.generate(ruby) +# ruby0 = JSON.parse(json0) +# # This passage uses the addition for Range. +# require 'json/add/range' +# json1 = JSON.generate(ruby) +# ruby1 = JSON.parse(json1, create_additions: true) +# # Make a nice display. +# display = <require 'json/add/bigdecimal' +# - Complex: require 'json/add/complex' +# - Date: require 'json/add/date' +# - DateTime: require 'json/add/date_time' +# - Exception: require 'json/add/exception' +# - OpenStruct: require 'json/add/ostruct' +# - Range: require 'json/add/range' +# - Rational: require 'json/add/rational' +# - Regexp: require 'json/add/regexp' +# - Set: require 'json/add/set' +# - Struct: require 'json/add/struct' +# - Symbol: require 'json/add/symbol' +# - Time: require 'json/add/time' +# +# To reduce punctuation clutter, the examples below +# show the generated \JSON via +puts+, rather than the usual +inspect+, +# +# \BigDecimal: +# require 'json/add/bigdecimal' +# ruby0 = BigDecimal(0) # 0.0 +# json = JSON.generate(ruby0) # {"json_class":"BigDecimal","b":"27:0.0"} +# ruby1 = JSON.parse(json, create_additions: true) # 0.0 +# ruby1.class # => BigDecimal +# +# \Complex: +# require 'json/add/complex' +# ruby0 = Complex(1+0i) # 1+0i +# json = JSON.generate(ruby0) # {"json_class":"Complex","r":1,"i":0} +# ruby1 = JSON.parse(json, create_additions: true) # 1+0i +# ruby1.class # Complex +# +# \Date: +# require 'json/add/date' +# ruby0 = Date.today # 2020-05-02 +# json = JSON.generate(ruby0) # {"json_class":"Date","y":2020,"m":5,"d":2,"sg":2299161.0} +# ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02 +# ruby1.class # Date +# +# \DateTime: +# require 'json/add/date_time' +# ruby0 = DateTime.now # 2020-05-02T10:38:13-05:00 +# json = JSON.generate(ruby0) # {"json_class":"DateTime","y":2020,"m":5,"d":2,"H":10,"M":38,"S":13,"of":"-5/24","sg":2299161.0} +# ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02T10:38:13-05:00 +# ruby1.class # DateTime +# +# \Exception (and its subclasses including \RuntimeError): +# require 'json/add/exception' +# ruby0 = Exception.new('A message') # A message +# json = JSON.generate(ruby0) # {"json_class":"Exception","m":"A message","b":null} +# ruby1 = JSON.parse(json, create_additions: true) # A message +# ruby1.class # Exception +# ruby0 = RuntimeError.new('Another message') # Another message +# json = JSON.generate(ruby0) # {"json_class":"RuntimeError","m":"Another message","b":null} +# ruby1 = JSON.parse(json, create_additions: true) # Another message +# ruby1.class # RuntimeError +# +# \OpenStruct: +# require 'json/add/ostruct' +# ruby0 = OpenStruct.new(name: 'Matz', language: 'Ruby') # # +# json = JSON.generate(ruby0) # {"json_class":"OpenStruct","t":{"name":"Matz","language":"Ruby"}} +# ruby1 = JSON.parse(json, create_additions: true) # # +# ruby1.class # OpenStruct +# +# \Range: +# require 'json/add/range' +# ruby0 = Range.new(0, 2) # 0..2 +# json = JSON.generate(ruby0) # {"json_class":"Range","a":[0,2,false]} +# ruby1 = JSON.parse(json, create_additions: true) # 0..2 +# ruby1.class # Range +# +# \Rational: +# require 'json/add/rational' +# ruby0 = Rational(1, 3) # 1/3 +# json = JSON.generate(ruby0) # {"json_class":"Rational","n":1,"d":3} +# ruby1 = JSON.parse(json, create_additions: true) # 1/3 +# ruby1.class # Rational +# +# \Regexp: +# require 'json/add/regexp' +# ruby0 = Regexp.new('foo') # (?-mix:foo) +# json = JSON.generate(ruby0) # {"json_class":"Regexp","o":0,"s":"foo"} +# ruby1 = JSON.parse(json, create_additions: true) # (?-mix:foo) +# ruby1.class # Regexp +# +# \Set: +# require 'json/add/set' +# ruby0 = Set.new([0, 1, 2]) # # +# json = JSON.generate(ruby0) # {"json_class":"Set","a":[0,1,2]} +# ruby1 = JSON.parse(json, create_additions: true) # # +# ruby1.class # Set +# +# \Struct: +# require 'json/add/struct' +# Customer = Struct.new(:name, :address) # Customer +# ruby0 = Customer.new("Dave", "123 Main") # # +# json = JSON.generate(ruby0) # {"json_class":"Customer","v":["Dave","123 Main"]} +# ruby1 = JSON.parse(json, create_additions: true) # # +# ruby1.class # Customer + # +# \Symbol: +# require 'json/add/symbol' +# ruby0 = :foo # foo +# json = JSON.generate(ruby0) # {"json_class":"Symbol","s":"foo"} +# ruby1 = JSON.parse(json, create_additions: true) # foo +# ruby1.class # Symbol +# +# \Time: +# require 'json/add/time' +# ruby0 = Time.now # 2020-05-02 11:28:26 -0500 +# json = JSON.generate(ruby0) # {"json_class":"Time","s":1588436906,"n":840560000} +# ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02 11:28:26 -0500 +# ruby1.class # Time +# +# +# === Custom \JSON Additions +# +# In addition to the \JSON additions provided, +# you can craft \JSON additions of your own, +# either for Ruby built-in classes or for user-defined classes. +# +# Here's a user-defined class +Foo+: +# class Foo +# attr_accessor :bar, :baz +# def initialize(bar, baz) +# self.bar = bar +# self.baz = baz +# end +# end +# +# Here's the \JSON addition for it: +# # Extend class Foo with JSON addition. +# class Foo +# # Serialize Foo object with its class name and arguments +# def to_json(*args) +# { +# JSON.create_id => self.class.name, +# 'a' => [ bar, baz ] +# }.to_json(*args) +# end +# # Deserialize JSON string by constructing new Foo object with arguments. +# def self.json_create(object) +# new(*object['a']) +# end +# end +# +# Demonstration: # require 'json' +# # This Foo object has no custom addition. +# foo0 = Foo.new(0, 1) +# json0 = JSON.generate(foo0) +# obj0 = JSON.parse(json0) +# # Lood the custom addition. +# require_relative 'foo_addition' +# # This foo has the custom addition. +# foo1 = Foo.new(0, 1) +# json1 = JSON.generate(foo1) +# obj1 = JSON.parse(json1, create_additions: true) +# # Make a nice display. +# display = <" (String) +# With custom addition: {"json_class":"Foo","a":[0,1]} (String) +# Parsed JSON: +# Without custom addition: "#" (String) +# With custom addition: # (Foo) # -# 1.to_json # => "1" -# -# The {#generate}[rdoc-ref:JSON#generate] method accepts a variety of options -# to set the formatting of string output and defining what input is accepteable. -# There are also shortcut methods pretty_generate (with a set of options to -# generate human-readable multiline JSON) and fast_generate (with a set of -# options to generate JSON faster at the price of disabling some checks). -# -# == Extended rendering and loading of Ruby objects -# -# JSON library provides optional _additions_ allowing to serialize and -# deserialize Ruby classes without loosing their type. -# -# # without additions -# require "json" -# json = JSON.generate({range: 1..3, regex: /test/}) -# # => '{"range":"1..3","regex":"(?-mix:test)"}' -# JSON.parse(json) -# # => {"range"=>"1..3", "regex"=>"(?-mix:test)"} -# -# # with additions -# require "json/add/range" -# require "json/add/regexp" -# json = JSON.generate({range: 1..3, regex: /test/}) -# # => '{"range":{"json_class":"Range","a":[1,3,false]},"regex":{"json_class":"Regexp","o":0,"s":"test"}}' -# JSON.parse(json) -# # => {"range"=>{"json_class"=>"Range", "a"=>[1, 3, false]}, "regex"=>{"json_class"=>"Regexp", "o"=>0, "s"=>"test"}} -# JSON.load(json) -# # => {"range"=>1..3, "regex"=>/test/} -# -# See JSON.load for details. module JSON require 'json/version' diff --git a/lib/json/common.rb b/lib/json/common.rb index 5ba8f1d29..0e69050b1 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -4,12 +4,15 @@ module JSON class << self - # If _object_ is string-like, parse the string and return the parsed - # result as a Ruby data structure. Otherwise generate a JSON text from the - # Ruby data structure object and return it. + # If +object+ is a + # {String-convertible object}[doc/implicit_conversion_rdoc.html#label-String-Convertible+Objects] + # (implementing +to_str+), calls JSON.parse with +object+ and +opts+: + # json = '[0, 1, null]' + # JSON[json]# => [0, 1, nil] # - # The _opts_ argument is passed through to generate/parse respectively. - # See generate and parse for their documentation. + # Otherwise, calls JSON.generate with +object+ and +opts+: + # ruby = [0, 1, nil] + # JSON[ruby] # => "[0,1,null]" def [](object, opts = {}) if object.respond_to? :to_str JSON.parse(object.to_str, opts) @@ -19,7 +22,8 @@ def [](object, opts = {}) end # Returns the JSON parser class that is used by JSON. This is either - # JSON::Ext::Parser or JSON::Pure::Parser. + # JSON::Ext::Parser or JSON::Pure::Parser: + # JSON.parser # => JSON::Ext::Parser attr_reader :parser # Set the JSON parser class _parser_ to be used by JSON. @@ -84,15 +88,18 @@ def generator=(generator) # :nodoc: end # Returns the JSON generator module that is used by JSON. This is - # either JSON::Ext::Generator or JSON::Pure::Generator. + # either JSON::Ext::Generator or JSON::Pure::Generator: + # JSON.generator # => JSON::Ext::Generator attr_reader :generator - # Returns the JSON generator state class that is used by JSON. This is - # either JSON::Ext::Generator::State or JSON::Pure::Generator::State. + # Sets or Returns the JSON generator state class that is used by JSON. This is + # either JSON::Ext::Generator::State or JSON::Pure::Generator::State: + # JSON.state # => JSON::Ext::Generator::State attr_accessor :state - # This is create identifier, which is used to decide if the _json_create_ - # hook of a class should be called. It defaults to 'json_class'. + # Sets or returns create identifier, which is used to decide if the _json_create_ + # hook of a class should be called; initial value is +json_class+: + # JSON.create_id # => "json_class" attr_accessor :create_id end self.create_id = 'json_class' @@ -126,7 +133,7 @@ class CircularDatastructure < NestingError; end # This exception is raised if a generator or unparser error occurs. class GeneratorError < JSONError; end # For backwards compatibility - UnparserError = GeneratorError + UnparserError = GeneratorError # :nodoc: # This exception is raised if the required unicode support is missing on the # system. Usually this means that the iconv library is not installed. @@ -134,43 +141,158 @@ class MissingUnicodeSupport < JSONError; end module_function - # Parse the JSON document _source_ into a Ruby data structure and return it. - # - # _opts_ can have the following - # keys: - # * *max_nesting*: The maximum depth of nesting allowed in the parsed data - # structures. Disable depth checking with :max_nesting => false. It - # defaults to 100. - # * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in - # defiance of RFC 7159 to be parsed by the Parser. This option defaults - # to false. - # * *symbolize_names*: If set to true, returns symbols for the names - # (keys) in a JSON object. Otherwise strings are returned. Strings are - # the default. - # * *create_additions*: If set to false, the Parser doesn't create - # additions even if a matching class and create_id was found. This option - # defaults to false. - # * *object_class*: Defaults to Hash - # * *array_class*: Defaults to Array + # Argument +source+ contains the \String to be parsed. It must be a + # {String-convertible object}[doc/implicit_conversion_rdoc.html#label-String-Convertible+Objects] + # (implementing +to_str+), and must contain valid \JSON data. + # + # Argument +opts+, if given, contains options for the parsing, and must be a + # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects] + # (implementing +to_hash+). + # + # Returns the Ruby objects created by parsing the given +source+. + # + # --- + # + # When +source+ is a \JSON array, returns a Ruby \Array: + # source = '["foo", 1.0, true, false, null]' + # ruby = JSON.parse(source) + # ruby # => ["foo", 1.0, true, false, nil] + # ruby.class # => Array + # + # When +source+ is a \JSON object, returns a Ruby \Hash: + # source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}' + # ruby = JSON.parse(source) + # ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil} + # ruby.class # => Hash + # + # For examples of parsing for all \JSON data types, see + # {Parsing \JSON}[#module-JSON-label-Parsing+JSON]. + # + # ====== Input Options + # + # Option +max_nesting+ (\Integer) specifies the maximum nesting depth allowed; + # defaults to +100+; specify +false+ to disable depth checking. + # + # With the default, +false+: + # source = '[0, [1, [2, [3]]]]' + # ruby = JSON.parse(source) + # ruby # => [0, [1, [2, [3]]]] + # Too deep: + # # Raises JSON::NestingError (nesting of 2 is too deep): + # JSON.parse(source, {max_nesting: 1}) + # Bad value: + # # Raises TypeError (wrong argument type Symbol (expected Fixnum)): + # JSON.parse(source, {max_nesting: :foo}) + # + # --- + # + # Option +allow_nan+ (boolean) specifies whether to allow + # NaN, Infinity, and MinusInfinity in +source+; + # defaults to +false+. + # + # With the default, +false+: + # # Raises JSON::ParserError (225: unexpected token at '[NaN]'): + # JSON.parse('[NaN]') + # # Raises JSON::ParserError (232: unexpected token at '[Infinity]'): + # JSON.parse('[Infinity]') + # # Raises JSON::ParserError (248: unexpected token at '[-Infinity]'): + # JSON.parse('[-Infinity]') + # Allow: + # source = '[NaN, Infinity, -Infinity]' + # ruby = JSON.parse(source, {allow_nan: true}) + # ruby # => [NaN, Infinity, -Infinity] + # With a truthy value: + # ruby = JSON.parse(source, {allow_nan: :foo}) + # ruby # => [NaN, Infinity, -Infinity] + # + # ====== Output Options + # + # Option +symbolize_names+ (boolean) specifies whether returned \Hash keys + # should be Symbols; + # defaults to +false+ (use Strings). + # + # With the default, +false+: + # source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}' + # ruby = JSON.parse(source) + # ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil} + # Use Symbols: + # ruby = JSON.parse(source, {symbolize_names: true}) + # ruby # => {:a=>"foo", :b=>1.0, :c=>true, :d=>false, :e=>nil} + # + # --- + # + # Option +object_class+ (\Class) specifies the Ruby class to be used + # for each \JSON object; + # defaults to \Hash. + # + # With the default, \Hash: + # source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}' + # ruby = JSON.parse(source) + # ruby.class # => Hash + # Use class \OpenStruct: + # ruby = JSON.parse(source, {object_class: OpenStruct}) + # ruby # => # + # Try class \Object: + # # Raises NoMethodError (undefined method `[]=' for #): + # JSON.parse(source, {object_class: Object}) + # Bad value: + # # Raises TypeError (wrong argument type Symbol (expected Class)): + # JSON.parse(source, {object_class: :foo}) + # + # --- + # + # Option +array_class+ (\Class) specifies the Ruby class to be used + # for each \JSON array; + # defaults to \Array. + # + # With the default, \Array: + # source = '["foo", 1.0, true, false, null]' + # ruby = JSON.parse(source) + # ruby.class # => Array + # Use class \Set: + # ruby = JSON.parse(source, {array_class: Set}) + # ruby # => # + # Try class \Object: + # # Raises NoMethodError (undefined method `<<' for #): + # JSON.parse(source, {array_class: Object}) + # Bad value: + # # Raises TypeError (wrong argument type Symbol (expected Class)): + # JSON.parse(source, {array_class: :foo}) + # + # --- + # + # Option +create_additions+ (boolean) specifies whether to use \JSON additions in parsing. + # See {\JSON Additions}[#module-JSON-label-JSON+Additions]. + # + # ====== Exceptions + # + # Raises an exception if +source+ is not \String-convertible: + # + # # Raises TypeError (no implicit conversion of Symbol into String): + # JSON.parse(:foo) + # + # Raises an exception if +opts+ is not \Hash-convertible: + # + # # Raises TypeError (no implicit conversion of Symbol into Hash): + # JSON.parse(['foo'], :foo) + # + # Raises an exception if +source+ is not valid JSON: + # + # # Raises JSON::ParserError (783: unexpected token at ''): + # JSON.parse('') + # def parse(source, opts = {}) Parser.new(source, **(opts||{})).parse end - # Parse the JSON document _source_ into a Ruby data structure and return it. - # The bang version of the parse method defaults to the more dangerous values - # for the _opts_ hash, so be sure only to parse trusted _source_ documents. - # - # _opts_ can have the following keys: - # * *max_nesting*: The maximum depth of nesting allowed in the parsed data - # structures. Enable depth checking with :max_nesting => anInteger. The - # parse! methods defaults to not doing max depth checking: This can be - # dangerous if someone wants to fill up your stack. - # * *allow_nan*: If set to true, allow NaN, Infinity, and -Infinity in - # defiance of RFC 7159 to be parsed by the Parser. This option defaults - # to true. - # * *create_additions*: If set to false, the Parser doesn't create - # additions even if a matching class and create_id was found. This option - # defaults to false. + # Calls + # JSON.parse(source, opts) + # with +source+ and possibly modified +opts+. + # + # Differences from JSON.parse: + # - Option +max_nesting+, if not provided, defaults to +false+, + # which disables checking for nesting depth. + # - Option +allow_nan+, if not provided, defaults to +true+. def parse!(source, opts = {}) opts = { :max_nesting => false, @@ -179,35 +301,148 @@ def parse!(source, opts = {}) Parser.new(source, **(opts||{})).parse end - # Generate a JSON document from the Ruby data structure _obj_ and return - # it. _opts_ is - # * a Hash like object (responding to +to_hash+), - # * or an object convertible into a hash by a +to_h+ method, - # * or a JSON::State object. - # - # If hash-alike or hash-convertible object is provided, it is internally - # converted into a State object. - # - # The default options are set to create the shortest possible JSON text - # in one line, check for circular data structures and do not allow NaN, - # Infinity, and -Infinity. - # - # An _opts_ hash can have the following keys: - # * *indent*: a string used to indent levels (default: ''), - # * *space*: a string that is put after a : pair delimiter (default: ''), - # * *space_before*: a string that is put before a : pair delimiter (default: ''), - # * *object_nl*: a string that is put at the end of a JSON object (default: ''), - # * *array_nl*: a string that is put at the end of a JSON array (default: ''), - # * *allow_nan*: true if NaN, Infinity, and -Infinity should be - # generated, otherwise an exception is thrown if these values are - # encountered. This options defaults to false. - # * *max_nesting*: The maximum depth of nesting allowed in the data - # structures from which JSON is to be generated. Disable depth checking - # with max_nesting: false, it defaults to 100. - # - # See also the fast_generate for the fastest creation method with the least - # amount of sanity checks, and the pretty_generate method for some - # defaults for pretty output. + # Argument +obj+ is the Ruby object to be converted to \JSON. + # + # Argument +opts+, if given, contains options for the generation, and must be a + # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects] + # (implementing +to_hash+). + # + # Returns a \String containing the generated \JSON data. + # + # See also JSON.fast_generate, JSON.pretty_generate. + # + # --- + # + # When +obj+ is an + # {Array-convertible object}[doc/implicit_conversion_rdoc.html#label-Array-Convertible+Objects] + # (implementing +to_ary+), returns a \String containing a \JSON array: + # obj = ["foo", 1.0, true, false, nil] + # json = JSON.generate(obj) + # json # => "[\"foo\",1.0,true,false,null]" + # json.class # => String + # + # When +obj+ is a + # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects], + # return a \String containing a \JSON object: + # obj = {foo: 0, bar: 's', baz: :bat} + # json = JSON.generate(obj) + # json # => "{\"foo\":0,\"bar\":\"s\",\"baz\":\"bat\"}" + # + # For examples of generating from other Ruby objects, see + # {Generating \JSON from Other Objects}[#module-JSON-label-Generating+JSON+from+Other+Objects]. + # + # ====== Input Options + # + # Option +allow_nan+ (boolean) specifies whether + # +NaN+, +Infinity+, and -Infinity may be generated; + # defaults to +false+. + # + # With the default, +false+: + # # Raises JSON::GeneratorError (920: NaN not allowed in JSON): + # JSON.generate(JSON::NaN) + # # Raises JSON::GeneratorError (917: Infinity not allowed in JSON): + # JSON.generate(JSON::Infinity) + # # Raises JSON::GeneratorError (917: -Infinity not allowed in JSON): + # JSON.generate(JSON::MinusInfinity) + # + # Allow: + # ruby = [JSON::NaN, JSON::Infinity, JSON::MinusInfinity] + # JSON.generate(ruby, allow_nan: true) # => "[NaN,Infinity,-Infinity]" + # + # --- + # + # Option +max_nesting+ (\Integer) specifies the maximum nesting depth + # in +obj+; defaults to +100+. + # + # With the default, +100+: + # obj = [[[[[[0]]]]]] + # JSON.generate(obj) # => "[[[[[[0]]]]]]" + # + # Too deep: + # # Raises JSON::NestingError (nesting of 2 is too deep): + # JSON.generate(obj, max_nesting: 2) + # + # Bad Value: + # # Raises TypeError (can't convert Symbol into Hash): + # JSON.generate(obj, :foo) + # + # ====== Output Options + # + # The default formatting options generate the most compact + # \JSON data, all on one line and with no whitespace. + # + # You can use these formatting options to generate + # \JSON data in a more open format, using whitespace. + # See also JSON.pretty_generate. + # + # - Option +array_nl+ (\String) specifies a string (usually a newline) + # to be inserted after each \JSON array; defaults to the empty \String, ''. + # - Option +object_nl+ (\String) specifies a string (usually a newline) + # to be inserted after each \JSON object; defaults to the empty \String, ''. + # - Option +indent+ (\String) specifies the string (usually spaces) to be + # used for indentation; defaults to the empty \String, ''; + # defaults to the empty \String, ''; + # has no effect unless options +array_nl+ or +object_nl+ specify newlines. + # - Option +space+ (\String) specifies a string (usually a space) to be + # inserted after the colon in each \JSON object's pair; + # defaults to the empty \String, ''. + # - Option +space_before+ (\String) specifies a string (usually a space) to be + # inserted before the colon in each \JSON object's pair; + # defaults to the empty \String, ''. + # + # In this example, +obj+ is used first to generate the shortest + # \JSON data (no whitespace), then again with all formatting options + # specified: + # + # obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}} + # json = JSON.generate(obj) + # puts 'Compact:', json + # opts = { + # array_nl: "\n", + # object_nl: "\n", + # indent+: ' ', + # space_before: ' ', + # space: ' ' + # } + # puts 'Open:', JSON.generate(obj, opts) + # + # Output: + # Compact: + # {"foo":["bar","baz"],"bat":{"bam":0,"bad":1}} + # Open: + # { + # "foo" : [ + # "bar", + # "baz" + # ], + # "bat" : { + # "bam" : 0, + # "bad" : 1 + # } + # } + # + # --- + # + # Raises an exception if any formatting option is not a \String. + # + # ====== Exceptions + # + # Raises an exception if +obj+ is not a valid Ruby object: + # # Raises NameError (uninitialized constant Foo): + # JSON.generate(Foo) + # # Raises NameError (undefined local variable or method `foo' for main:Object): + # JSON.generate(foo) + # + # Raises an exception if +obj+ contains circular references: + # a = []; b = []; a.push(b); b.push(a) + # # Raises JSON::NestingError (nesting of 100 is too deep): + # JSON.generate(a) + # + # Raises an exception if +opts is not a + # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects] + # (implementing +to_hash+): + # # Raises TypeError (can't convert Symbol into Hash): + # JSON.generate('x', :foo) def generate(obj, opts = nil) if State === opts state, opts = opts, nil @@ -234,11 +469,16 @@ def generate(obj, opts = nil) module_function :unparse # :startdoc: - # Generate a JSON document from the Ruby data structure _obj_ and return it. - # This method disables the checks for circles in Ruby objects. + # Arguments +obj+ and +opts+ here are the same as + # arguments +obj+ and +opts+ in JSON.generate. + # + # By default, generates \JSON data without checking + # for circular references in +obj+ (option +max_nesting+ set to +false+, disabled). # - # *WARNING*: Be careful not to pass any Ruby data structures with circles as - # _obj_ argument because this will cause JSON to go into an infinite loop. + # Raises an exception if +obj+ contains circular references: + # a = []; b = []; a.push(b); b.push(a) + # # Raises SystemStackError (stack level too deep): + # JSON.fast_generate(a) def fast_generate(obj, opts = nil) if State === opts state, opts = opts, nil @@ -264,12 +504,33 @@ def fast_generate(obj, opts = nil) module_function :fast_unparse # :startdoc: - # Generate a JSON document from the Ruby data structure _obj_ and return it. - # The returned document is a prettier form of the document returned by - # #unparse. + # Arguments +obj+ and +opts+ here are the same as + # arguments +obj+ and +opts+ in JSON.generate. + # + # Default options are: + # { + # indent: ' ', # Two spaces + # space: ' ', # One space + # array_nl: "\n", # Newline + # object_nl: "\n" # Newline + # } + # + # Example: + # obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}} + # json = JSON.pretty_generate(obj) + # puts json + # Output: + # { + # "foo": [ + # "bar", + # "baz" + # ], + # "bat": { + # "bam": 0, + # "bad": 1 + # } + # } # - # The _opts_ argument can be used to configure the generator. See the - # generate method for a more detailed explanation. def pretty_generate(obj, opts = nil) if State === opts state, opts = opts, nil @@ -296,10 +557,10 @@ def pretty_generate(obj, opts = nil) # :startdoc: class << self - # The global default options for the JSON.load method: - # :max_nesting: false - # :allow_nan: true - # :allow_blank: true + # Sets or returns default options for the JSON.load method. + # Initially: + # opts = JSON.load_default_options + # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true} attr_accessor :load_default_options end self.load_default_options = { @@ -358,10 +619,10 @@ def recurse_proc(result, &proc) module_function :restore class << self - # The global default options for the JSON.dump method: - # :max_nesting: false - # :allow_nan: true - # :allow_blank: true + # Sets or returns the default options for the JSON.dump method. + # Initially: + # opts = JSON.dump_default_options + # opts # => {:max_nesting=>false, :allow_nan=>true} attr_accessor :dump_default_options end self.dump_default_options = { @@ -405,7 +666,7 @@ def dump(obj, anIO = nil, limit = nil) raise ArgumentError, "exceed depth limit" end - # Encodes string using Ruby's _String.encode_ + # Encodes string using String.encode. def self.iconv(to, from, string) string.encode(to, from) end From 90678ed484131b6efa032f67d7ed06c6fa2106a9 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Thu, 25 Jun 2020 01:31:45 -0400 Subject: [PATCH 0050/1010] Add comment that gemspec is generated automatically --- Rakefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 12938c7ad..fed7f8e31 100644 --- a/Rakefile +++ b/Rakefile @@ -106,7 +106,10 @@ if defined?(Gem) and defined?(Gem::PackageTask) desc 'Creates a json_pure.gemspec file' task :gemspec_pure => :version do File.open('json_pure.gemspec', 'w') do |gemspec| - gemspec.write spec_pure.to_ruby + src = spec_pure.to_ruby + src.gsub!(/# stub:.*/, "# This gemspec is generated automatically using `rake gemspec`.\n" \ + "# Do not modify directly.") + gemspec.write src end end From 3d638ddab70b5f8f42dba82d87ed96d16f6df39e Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Thu, 25 Jun 2020 01:33:58 -0400 Subject: [PATCH 0051/1010] Update gemspec. This fixes required_ruby_version --- json_pure.gemspec | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/json_pure.gemspec b/json_pure.gemspec index 08413d677..57f8663ba 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -1,5 +1,6 @@ # -*- encoding: utf-8 -*- -# stub: json_pure 2.3.0 ruby lib +# This gemspec is generated automatically using `rake gemspec`. +# Do not modify directly. Gem::Specification.new do |s| s.name = "json_pure".freeze @@ -8,7 +9,7 @@ Gem::Specification.new do |s| s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Florian Frank".freeze] - s.date = "2019-12-11" + s.date = "2020-06-25" s.description = "This is a JSON implementation in pure Ruby.".freeze s.email = "flori@ping.de".freeze s.extra_rdoc_files = ["README.md".freeze] @@ -16,21 +17,18 @@ Gem::Specification.new do |s| s.homepage = "http://flori.github.com/json".freeze s.licenses = ["Ruby".freeze] s.rdoc_options = ["--title".freeze, "JSON implemention for ruby".freeze, "--main".freeze, "README.md".freeze] - s.required_ruby_version = Gem::Requirement.new(">= 1.9".freeze) - s.rubygems_version = "3.0.3".freeze + s.required_ruby_version = Gem::Requirement.new(">= 2.0".freeze) + s.rubygems_version = "3.1.2".freeze s.summary = "JSON Implementation for Ruby".freeze s.test_files = ["./tests/test_helper.rb".freeze] if s.respond_to? :specification_version then s.specification_version = 4 + end - if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - s.add_development_dependency(%q.freeze, [">= 0"]) - s.add_development_dependency(%q.freeze, ["~> 2.0"]) - else - s.add_dependency(%q.freeze, [">= 0"]) - s.add_dependency(%q.freeze, ["~> 2.0"]) - end + if s.respond_to? :add_runtime_dependency then + s.add_development_dependency(%q.freeze, [">= 0"]) + s.add_development_dependency(%q.freeze, ["~> 2.0"]) else s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, ["~> 2.0"]) From 4a2e95cb78eee659501a6bd76c3cd9d35dcad711 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Thu, 25 Jun 2020 01:35:48 -0400 Subject: [PATCH 0052/1010] Accept test-unit v3.x too --- Rakefile | 2 +- json-java.gemspec | 6 +++--- json.gemspec | 2 +- json_pure.gemspec | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Rakefile b/Rakefile index fed7f8e31..a851d63a2 100644 --- a/Rakefile +++ b/Rakefile @@ -89,7 +89,7 @@ if defined?(Gem) and defined?(Gem::PackageTask) s.require_path = 'lib' s.add_development_dependency 'rake' - s.add_development_dependency 'test-unit', '~> 2.0' + s.add_development_dependency 'test-unit', '>= 2.0', '< 4.0' s.extra_rdoc_files << 'README.md' s.rdoc_options << diff --git a/json-java.gemspec b/json-java.gemspec index f3fa0c913..1bcd639d1 100755 --- a/json-java.gemspec +++ b/json-java.gemspec @@ -19,14 +19,14 @@ spec = Gem::Specification.new do |s| if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, ["~> 2.0"]) + s.add_development_dependency(%q, [">= 2.0", "< 4.0"]) else s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 2.0"]) + s.add_dependency(%q, [">= 2.0", "< 4.0"]) end else s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 2.0"]) + s.add_dependency(%q, [">= 2.0", "< 4.0"]) end end diff --git a/json.gemspec b/json.gemspec index 443c32460..a101aba9d 100644 --- a/json.gemspec +++ b/json.gemspec @@ -136,5 +136,5 @@ Gem::Specification.new do |s| s.test_files = ["tests/test_helper.rb"] s.add_development_dependency("rake", [">= 0"]) - s.add_development_dependency("test-unit", ["~> 2.0"]) + s.add_development_dependency("test-unit", [">= 2.0", "< 4.0"]) end diff --git a/json_pure.gemspec b/json_pure.gemspec index 57f8663ba..7b4114aff 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -28,9 +28,9 @@ Gem::Specification.new do |s| if s.respond_to? :add_runtime_dependency then s.add_development_dependency(%q.freeze, [">= 0"]) - s.add_development_dependency(%q.freeze, ["~> 2.0"]) + s.add_development_dependency(%q.freeze, [">= 2.0", "< 4.0"]) else s.add_dependency(%q.freeze, [">= 0"]) - s.add_dependency(%q.freeze, ["~> 2.0"]) + s.add_dependency(%q.freeze, [">= 2.0", "< 4.0"]) end end From ec1eaf2092f8b6c3d900458fbc776062abf8501f Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Thu, 25 Jun 2020 02:04:33 -0400 Subject: [PATCH 0053/1010] Fix rake task description --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index a851d63a2..a2ed9013a 100644 --- a/Rakefile +++ b/Rakefile @@ -118,7 +118,7 @@ if defined?(Gem) and defined?(Gem::PackageTask) pkg.package_files = PKG_FILES end - desc 'Create all gemspec files' + desc 'Alias for gemspec_pure' task :gemspec => [ :gemspec_pure ] end From d5c6566b4127b80641fc932e03cf24455c5d883e Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Thu, 25 Jun 2020 02:42:44 -0400 Subject: [PATCH 0054/1010] Fix test that wasn't testing anything --- tests/json_fixtures_test.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/json_fixtures_test.rb b/tests/json_fixtures_test.rb index 01954fe70..b2a1ec8b8 100644 --- a/tests/json_fixtures_test.rb +++ b/tests/json_fixtures_test.rb @@ -3,7 +3,7 @@ class JSONFixturesTest < Test::Unit::TestCase def setup - fixtures = File.join(File.dirname(__FILE__), 'fixtures/{fail,pass}.json') + fixtures = File.join(File.dirname(__FILE__), 'fixtures/{fail,pass}*.json') passed, failed = Dir[fixtures].partition { |f| f['pass'] } @passed = passed.inject([]) { |a, f| a << [ f, File.read(f) ] }.sort @failed = failed.inject([]) { |a, f| a << [ f, File.read(f) ] }.sort @@ -29,4 +29,9 @@ def test_failing end end end + + def test_sanity + assert(@passed.size > 5) + assert(@failed.size > 20) + end end From 0baa541081e0ceaf2f8af283d40462654557ea16 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Sat, 5 Oct 2019 07:00:07 +0900 Subject: [PATCH 0055/1010] ext/json/parser/prereq.mk: Add a "automatically generated" header to parser.c. ruby/ruby@5717e55e9a7790c938afa694a9bf558c0e54bb83 ruby/ruby@70e3fda2eb45c841e5fb4574273d20f8df5455e5 --- Rakefile | 1 + ext/json/ext/parser/parser.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 7e307dcb5..2b688187b 100644 --- a/Rakefile +++ b/Rakefile @@ -331,6 +331,7 @@ else end src = File.read("parser.c").gsub(/[ \t]+$/, '') src.gsub!(/^static const int (JSON_.*=.*);$/, 'enum {\1};') + src[0, 0] = "/* This file is automatically generated from parser.rl by using ragel */" File.open("parser.c", "w") {|f| f.print src} end end diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index bc4ac46e0..0f98cf982 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -1,4 +1,4 @@ - +/* This file is automatically generated from parser.rl by using ragel */ #line 1 "parser.rl" #include "../fbuffer/fbuffer.h" #include "parser.h" From 14fadad26a6e3a6ce5fa4e0286b2ce4336052177 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Thu, 14 May 2020 00:44:35 +0900 Subject: [PATCH 0056/1010] ext/json/parser/prereq.mk: remove type-limit warning if char is unsigned MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ragel generates a code `0 <= (*p)` where `*p` is char. As char is unsigned by default on arm and RISC-V, it is warned by gcc: ``` compiling parser.c parser.c: In function ‘JSON_parse_string’: parser.c:1566:2: warning: comparison is always true due to limited range of data type [-Wtype-limits] if ( 0 <= (*p) && (*p) <= 31 ) ^ parser.c:1596:2: warning: comparison is always true due to limited range of data type [-Wtype-limits] if ( 0 <= (*p) && (*p) <= 31 ) ^ ``` This change removes the warning by substituting the condition with `0 <= (signed char)(*p)`. ruby/ruby@8bd27c547c3260ce72dc5edbab248bb858c84cf2 --- Rakefile | 1 + ext/json/ext/parser/parser.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Rakefile b/Rakefile index 2b688187b..0945a67c8 100644 --- a/Rakefile +++ b/Rakefile @@ -331,6 +331,7 @@ else end src = File.read("parser.c").gsub(/[ \t]+$/, '') src.gsub!(/^static const int (JSON_.*=.*);$/, 'enum {\1};') + src.gsub!(/0 <= \(\*p\) && \(\*p\) <= 31/, "0 <= (signed char)(*p) && (*p) <= 31") src[0, 0] = "/* This file is automatically generated from parser.rl by using ragel */" File.open("parser.c", "w") {|f| f.print src} end diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 0f98cf982..e4a305663 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -1563,7 +1563,7 @@ case 2: case 34: goto tr2; case 92: goto st3; } - if ( 0 <= (*p) && (*p) <= 31 ) + if ( 0 <= (signed char)(*p) && (*p) <= 31 ) goto st0; goto st2; tr2: @@ -1593,7 +1593,7 @@ case 8: case 3: if ( (*p) == 117 ) goto st4; - if ( 0 <= (*p) && (*p) <= 31 ) + if ( 0 <= (signed char)(*p) && (*p) <= 31 ) goto st0; goto st2; st4: From 9c29ce3ad51e0cce80302e63a438c2927c8d99b5 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 25 Jun 2020 16:41:08 +0900 Subject: [PATCH 0057/1010] Removed nonsense `rubygems_version` in input gemspec files As it is ignored and set at building packages automatically, it is just nonsense to set in gemspec file for input. ruby/ruby@9a78e24f7d269c9688a0fa50c82751b5ec8d512a --- json.gemspec | 1 - 1 file changed, 1 deletion(-) diff --git a/json.gemspec b/json.gemspec index a101aba9d..0c72e8242 100644 --- a/json.gemspec +++ b/json.gemspec @@ -131,7 +131,6 @@ Gem::Specification.new do |s| s.licenses = ["Ruby"] s.rdoc_options = ["--title", "JSON implemention for Ruby", "--main", "README.md"] s.required_ruby_version = Gem::Requirement.new(">= 2.0") - s.rubygems_version = "3.0.2" s.summary = "JSON Implementation for Ruby" s.test_files = ["tests/test_helper.rb"] From 7bee2c7c13ba545bda24db6259718a8b7255270a Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Thu, 25 Jun 2020 12:09:28 -0500 Subject: [PATCH 0058/1010] RDoc enhancements --- lib/json.rb | 4 ++-- lib/json/common.rb | 27 +-------------------------- 2 files changed, 3 insertions(+), 28 deletions(-) diff --git a/lib/json.rb b/lib/json.rb index a96f4bc61..8fc7e8300 100644 --- a/lib/json.rb +++ b/lib/json.rb @@ -150,8 +150,8 @@ # # When the source is a Ruby \Integer or \Float, JSON.generate returns # a \String containing a \JSON number: -# JSON.generate(Integer(0)) # => "0"" -# JSON.generate(Float(1.0)) # => "1.0" +# JSON.generate(42) # => "42" +# JSON.generate(0.42) # => "0.42" # # When the source is a Ruby \String, JSON.generate returns # a \String containing a \JSON string (with double-quotes): diff --git a/lib/json/common.rb b/lib/json/common.rb index 0e69050b1..db50b3386 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -201,9 +201,6 @@ class MissingUnicodeSupport < JSONError; end # source = '[NaN, Infinity, -Infinity]' # ruby = JSON.parse(source, {allow_nan: true}) # ruby # => [NaN, Infinity, -Infinity] - # With a truthy value: - # ruby = JSON.parse(source, {allow_nan: :foo}) - # ruby # => [NaN, Infinity, -Infinity] # # ====== Output Options # @@ -232,12 +229,6 @@ class MissingUnicodeSupport < JSONError; end # Use class \OpenStruct: # ruby = JSON.parse(source, {object_class: OpenStruct}) # ruby # => # - # Try class \Object: - # # Raises NoMethodError (undefined method `[]=' for #): - # JSON.parse(source, {object_class: Object}) - # Bad value: - # # Raises TypeError (wrong argument type Symbol (expected Class)): - # JSON.parse(source, {object_class: :foo}) # # --- # @@ -319,7 +310,6 @@ def parse!(source, opts = {}) # obj = ["foo", 1.0, true, false, nil] # json = JSON.generate(obj) # json # => "[\"foo\",1.0,true,false,null]" - # json.class # => String # # When +obj+ is a # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects], @@ -346,7 +336,7 @@ def parse!(source, opts = {}) # JSON.generate(JSON::MinusInfinity) # # Allow: - # ruby = [JSON::NaN, JSON::Infinity, JSON::MinusInfinity] + # ruby = [Float::NaN, Float::Infinity, Float::MinusInfinity] # JSON.generate(ruby, allow_nan: true) # => "[NaN,Infinity,-Infinity]" # # --- @@ -362,10 +352,6 @@ def parse!(source, opts = {}) # # Raises JSON::NestingError (nesting of 2 is too deep): # JSON.generate(obj, max_nesting: 2) # - # Bad Value: - # # Raises TypeError (can't convert Symbol into Hash): - # JSON.generate(obj, :foo) - # # ====== Output Options # # The default formatting options generate the most compact @@ -427,22 +413,11 @@ def parse!(source, opts = {}) # # ====== Exceptions # - # Raises an exception if +obj+ is not a valid Ruby object: - # # Raises NameError (uninitialized constant Foo): - # JSON.generate(Foo) - # # Raises NameError (undefined local variable or method `foo' for main:Object): - # JSON.generate(foo) - # # Raises an exception if +obj+ contains circular references: # a = []; b = []; a.push(b); b.push(a) # # Raises JSON::NestingError (nesting of 100 is too deep): # JSON.generate(a) # - # Raises an exception if +opts is not a - # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects] - # (implementing +to_hash+): - # # Raises TypeError (can't convert Symbol into Hash): - # JSON.generate('x', :foo) def generate(obj, opts = nil) if State === opts state, opts = opts, nil From 470d909c0d1ecf0460bd4ac036510ac61251143b Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Thu, 25 Jun 2020 13:58:45 -0500 Subject: [PATCH 0059/1010] RDoc enhancements --- lib/json.rb | 32 ++++++++++++++++---------------- lib/json/common.rb | 12 ++++++------ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/json.rb b/lib/json.rb index 8fc7e8300..3eb59f84b 100644 --- a/lib/json.rb +++ b/lib/json.rb @@ -82,7 +82,7 @@ # # \String: # ruby = JSON.parse('"foo"') -# ruby # => "foo" +# ruby # => 'foo' # ruby.class # => String # \Integer: # ruby = JSON.parse('1') @@ -121,13 +121,13 @@ # a \String containing a \JSON array: # ruby = [0, 's', :foo] # json = JSON.generate(ruby) -# json # => "[0,\"s\",\"foo\"]" +# json # => '[0,"s","foo"]' # # The Ruby \Array array may contain nested arrays, hashes, and scalars # to any depth: # ruby = [0, [1, 2], {foo: 3, bar: 4}] # json = JSON.generate(ruby) -# json # => "[0,[1,2],{\"foo\":3,\"bar\":4}]" +# json # => '[0,[1,2],{"foo":3,"bar":4}]' # # ==== Generating \JSON from Hashes # @@ -135,13 +135,13 @@ # a \String containing a \JSON object: # ruby = {foo: 0, bar: 's', baz: :bat} # json = JSON.generate(ruby) -# json # => "{\"foo\":0,\"bar\":\"s\",\"baz\":\"bat\"}" +# json # => '{"foo":0,"bar":"s","baz":"bat"}' # # The Ruby \Hash array may contain nested arrays, hashes, and scalars # to any depth: # ruby = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad} # json = JSON.generate(ruby) -# json # => "{\"foo\":[0,1],\"bar\":{\"baz\":2,\"bat\":3},\"bam\":\"bad\"}" +# json # => '{"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}' # # ==== Generating \JSON from Other Objects # @@ -150,24 +150,24 @@ # # When the source is a Ruby \Integer or \Float, JSON.generate returns # a \String containing a \JSON number: -# JSON.generate(42) # => "42" -# JSON.generate(0.42) # => "0.42" +# JSON.generate(42) # => '42' +# JSON.generate(0.42) # => '0.42' # # When the source is a Ruby \String, JSON.generate returns # a \String containing a \JSON string (with double-quotes): -# JSON.generate('A string') # => "\"A string\"" +# JSON.generate('A string') # => '"A string"' # # When the source is +true+, +false+ or +nil+, JSON.generate returns # a \String containing the corresponding \JSON token: -# JSON.generate(true) # => "true" -# JSON.generate(false) # => "false" -# JSON.generate(nil) # => "null" +# JSON.generate(true) # => 'true' +# JSON.generate(false) # => 'false' +# JSON.generate(nil) # => 'null' # # When the source is none of the above, JSON.generate returns # a \String containing a \JSON string representation of the source: -# JSON.generate(:foo) # => "\"foo\"" -# JSON.generate(Complex(0, 0)) # => "\"0+0i\"" -# JSON.generate(Dir.new('.')) # => "\"#\"" +# JSON.generate(:foo) # => '"foo"' +# JSON.generate(Complex(0, 0)) # => '"0+0i"' +# JSON.generate(Dir.new('.')) # => '"#"' # # == \JSON Additions # @@ -175,9 +175,9 @@ # you have a new \String, instead of the object you began with: # ruby0 = Range.new(0, 2) # json = JSON.generate(ruby0) -# json # => "\"0..2\"" +# json # => '0..2"' # ruby1 = JSON.parse(json) -# ruby1 # => "0..2" +# ruby1 # => '0..2' # ruby1.class # => String # # You can use \JSON _additions_ to preserve the original object. diff --git a/lib/json/common.rb b/lib/json/common.rb index db50b3386..a5cb70ba8 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -12,7 +12,7 @@ class << self # # Otherwise, calls JSON.generate with +object+ and +opts+: # ruby = [0, 1, nil] - # JSON[ruby] # => "[0,1,null]" + # JSON[ruby] # => '[0,1,null]' def [](object, opts = {}) if object.respond_to? :to_str JSON.parse(object.to_str, opts) @@ -99,7 +99,7 @@ def generator=(generator) # :nodoc: # Sets or returns create identifier, which is used to decide if the _json_create_ # hook of a class should be called; initial value is +json_class+: - # JSON.create_id # => "json_class" + # JSON.create_id # => 'json_class' attr_accessor :create_id end self.create_id = 'json_class' @@ -309,14 +309,14 @@ def parse!(source, opts = {}) # (implementing +to_ary+), returns a \String containing a \JSON array: # obj = ["foo", 1.0, true, false, nil] # json = JSON.generate(obj) - # json # => "[\"foo\",1.0,true,false,null]" + # json # => '["foo",1.0,true,false,null]' # # When +obj+ is a # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects], # return a \String containing a \JSON object: # obj = {foo: 0, bar: 's', baz: :bat} # json = JSON.generate(obj) - # json # => "{\"foo\":0,\"bar\":\"s\",\"baz\":\"bat\"}" + # json # => '{"foo":0,"bar":"s","baz":"bat"}' # # For examples of generating from other Ruby objects, see # {Generating \JSON from Other Objects}[#module-JSON-label-Generating+JSON+from+Other+Objects]. @@ -337,7 +337,7 @@ def parse!(source, opts = {}) # # Allow: # ruby = [Float::NaN, Float::Infinity, Float::MinusInfinity] - # JSON.generate(ruby, allow_nan: true) # => "[NaN,Infinity,-Infinity]" + # JSON.generate(ruby, allow_nan: true) # => '[NaN,Infinity,-Infinity]' # # --- # @@ -346,7 +346,7 @@ def parse!(source, opts = {}) # # With the default, +100+: # obj = [[[[[[0]]]]]] - # JSON.generate(obj) # => "[[[[[[0]]]]]]" + # JSON.generate(obj) # => '[[[[[[0]]]]]]' # # Too deep: # # Raises JSON::NestingError (nesting of 2 is too deep): From 70ae672cfe34eee5ffa1ed84ffced4bf2400b9b2 Mon Sep 17 00:00:00 2001 From: kares Date: Wed, 28 Nov 2018 12:26:29 +0100 Subject: [PATCH 0060/1010] [CI] setup supported JRubies to test against --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 920a4ac4d..a28752333 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,14 +13,14 @@ rvm: - 2.6 - 2.7.0-preview3 - ruby-head - - jruby - - jruby-9.2.7.0 + - jruby-9.1 # Ruby 2.3 + - jruby-9.2 # Ruby 2.5 - truffleruby matrix: allow_failures: - rvm: ruby-head - - rvm: jruby - - rvm: jruby-9.2.7.0 + - rvm: jruby-9.1 + - rvm: jruby-9.2 - rvm: truffleruby script: "bundle exec rake" sudo: false From ada48f023606edf4abcb49fec4b60601971dcf62 Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Thu, 25 Jun 2020 16:49:39 -0500 Subject: [PATCH 0061/1010] RDoc enhancements --- lib/json/common.rb | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/lib/json/common.rb b/lib/json/common.rb index a5cb70ba8..c17c0ec33 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -243,12 +243,6 @@ class MissingUnicodeSupport < JSONError; end # Use class \Set: # ruby = JSON.parse(source, {array_class: Set}) # ruby # => # - # Try class \Object: - # # Raises NoMethodError (undefined method `<<' for #): - # JSON.parse(source, {array_class: Object}) - # Bad value: - # # Raises TypeError (wrong argument type Symbol (expected Class)): - # JSON.parse(source, {array_class: :foo}) # # --- # @@ -257,16 +251,6 @@ class MissingUnicodeSupport < JSONError; end # # ====== Exceptions # - # Raises an exception if +source+ is not \String-convertible: - # - # # Raises TypeError (no implicit conversion of Symbol into String): - # JSON.parse(:foo) - # - # Raises an exception if +opts+ is not \Hash-convertible: - # - # # Raises TypeError (no implicit conversion of Symbol into Hash): - # JSON.parse(['foo'], :foo) - # # Raises an exception if +source+ is not valid JSON: # # # Raises JSON::ParserError (783: unexpected token at ''): @@ -277,7 +261,7 @@ def parse(source, opts = {}) end # Calls - # JSON.parse(source, opts) + # parse(source, opts) # with +source+ and possibly modified +opts+. # # Differences from JSON.parse: From 18292c0c1d735673561dfbebfdac2be5e2928223 Mon Sep 17 00:00:00 2001 From: Watson Date: Tue, 27 Feb 2018 22:40:58 +0900 Subject: [PATCH 0062/1010] Use frozen string for hash key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When use non-frozen string for hash key with `rb_hash_aset()`, it will duplicate and freeze it internally. To avoid duplicate and freeze, this patch will give a frozen string in `rb_hash_aset()`. ``` Warming up -------------------------------------- json 14.000 i/100ms Calculating ------------------------------------- json 148.844 (± 1.3%) i/s - 756.000 in 5.079969s ``` ``` Warming up -------------------------------------- json 16.000 i/100ms Calculating ------------------------------------- json 165.608 (± 1.8%) i/s - 832.000 in 5.025367s ``` ``` require 'json' require 'securerandom' require 'benchmark/ips' obj = [] 1000.times do |i| obj << { "id": i, "uuid": SecureRandom.uuid, "created_at": Time.now } end json = obj.to_json Benchmark.ips do |x| x.report "json" do |iter| count = 0 while count < iter JSON.parse(json) count += 1 end end end ``` --- ext/json/ext/parser/parser.c | 135 +++++++++++++++++----------------- ext/json/ext/parser/parser.rl | 1 + 2 files changed, 69 insertions(+), 67 deletions(-) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index bc4ac46e0..7675cd230 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -112,7 +112,7 @@ enum {JSON_object_error = 0}; enum {JSON_object_en_main = 1}; -#line 167 "parser.rl" +#line 168 "parser.rl" static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) @@ -133,7 +133,7 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu cs = JSON_object_start; } -#line 182 "parser.rl" +#line 183 "parser.rl" #line 139 "parser.c" { @@ -163,7 +163,7 @@ case 2: goto st2; goto st0; tr2: -#line 149 "parser.rl" +#line 150 "parser.rl" { char *np; json->parsing_name = 1; @@ -251,6 +251,7 @@ case 8: p--; {p++; cs = 9; goto _out;} } else { if (NIL_P(json->object_class)) { + OBJ_FREEZE(last_name); rb_hash_aset(*result, last_name, v); } else { rb_funcall(*result, i_aset, 2, last_name, v); @@ -263,7 +264,7 @@ case 8: if ( ++p == pe ) goto _test_eof9; case 9: -#line 267 "parser.c" +#line 268 "parser.c" switch( (*p) ) { case 13: goto st9; case 32: goto st9; @@ -352,14 +353,14 @@ case 18: goto st9; goto st18; tr4: -#line 157 "parser.rl" +#line 158 "parser.rl" { p--; {p++; cs = 27; goto _out;} } goto st27; st27: if ( ++p == pe ) goto _test_eof27; case 27: -#line 363 "parser.c" +#line 364 "parser.c" goto st0; st19: if ( ++p == pe ) @@ -457,7 +458,7 @@ case 26: _out: {} } -#line 183 "parser.rl" +#line 184 "parser.rl" if (cs >= JSON_object_first_final) { if (json->create_additions) { @@ -482,7 +483,7 @@ case 26: -#line 486 "parser.c" +#line 487 "parser.c" enum {JSON_value_start = 1}; enum {JSON_value_first_final = 29}; enum {JSON_value_error = 0}; @@ -490,7 +491,7 @@ enum {JSON_value_error = 0}; enum {JSON_value_en_main = 1}; -#line 283 "parser.rl" +#line 284 "parser.rl" static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) @@ -498,14 +499,14 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul int cs = EVIL; -#line 502 "parser.c" +#line 503 "parser.c" { cs = JSON_value_start; } -#line 290 "parser.rl" +#line 291 "parser.rl" -#line 509 "parser.c" +#line 510 "parser.c" { if ( p == pe ) goto _test_eof; @@ -539,14 +540,14 @@ case 1: cs = 0; goto _out; tr2: -#line 235 "parser.rl" +#line 236 "parser.rl" { char *np = JSON_parse_string(json, p, pe, result); if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;} } goto st29; tr3: -#line 240 "parser.rl" +#line 241 "parser.rl" { char *np; if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) { @@ -566,7 +567,7 @@ cs = 0; } goto st29; tr7: -#line 258 "parser.rl" +#line 259 "parser.rl" { char *np; np = JSON_parse_array(json, p, pe, result, current_nesting + 1); @@ -574,7 +575,7 @@ cs = 0; } goto st29; tr11: -#line 264 "parser.rl" +#line 265 "parser.rl" { char *np; np = JSON_parse_object(json, p, pe, result, current_nesting + 1); @@ -582,7 +583,7 @@ cs = 0; } goto st29; tr25: -#line 228 "parser.rl" +#line 229 "parser.rl" { if (json->allow_nan) { *result = CInfinity; @@ -592,7 +593,7 @@ cs = 0; } goto st29; tr27: -#line 221 "parser.rl" +#line 222 "parser.rl" { if (json->allow_nan) { *result = CNaN; @@ -602,19 +603,19 @@ cs = 0; } goto st29; tr31: -#line 215 "parser.rl" +#line 216 "parser.rl" { *result = Qfalse; } goto st29; tr34: -#line 212 "parser.rl" +#line 213 "parser.rl" { *result = Qnil; } goto st29; tr37: -#line 218 "parser.rl" +#line 219 "parser.rl" { *result = Qtrue; } @@ -623,9 +624,9 @@ cs = 0; if ( ++p == pe ) goto _test_eof29; case 29: -#line 270 "parser.rl" +#line 271 "parser.rl" { p--; {p++; cs = 29; goto _out;} } -#line 629 "parser.c" +#line 630 "parser.c" switch( (*p) ) { case 13: goto st29; case 32: goto st29; @@ -866,7 +867,7 @@ case 28: _out: {} } -#line 291 "parser.rl" +#line 292 "parser.rl" if (cs >= JSON_value_first_final) { return p; @@ -876,7 +877,7 @@ case 28: } -#line 880 "parser.c" +#line 881 "parser.c" enum {JSON_integer_start = 1}; enum {JSON_integer_first_final = 3}; enum {JSON_integer_error = 0}; @@ -884,7 +885,7 @@ enum {JSON_integer_error = 0}; enum {JSON_integer_en_main = 1}; -#line 307 "parser.rl" +#line 308 "parser.rl" static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result) @@ -892,15 +893,15 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res int cs = EVIL; -#line 896 "parser.c" +#line 897 "parser.c" { cs = JSON_integer_start; } -#line 314 "parser.rl" +#line 315 "parser.rl" json->memo = p; -#line 904 "parser.c" +#line 905 "parser.c" { if ( p == pe ) goto _test_eof; @@ -934,14 +935,14 @@ case 3: goto st0; goto tr4; tr4: -#line 304 "parser.rl" +#line 305 "parser.rl" { p--; {p++; cs = 4; goto _out;} } goto st4; st4: if ( ++p == pe ) goto _test_eof4; case 4: -#line 945 "parser.c" +#line 946 "parser.c" goto st0; st5: if ( ++p == pe ) @@ -960,7 +961,7 @@ case 5: _out: {} } -#line 316 "parser.rl" +#line 317 "parser.rl" if (cs >= JSON_integer_first_final) { long len = p - json->memo; @@ -975,7 +976,7 @@ case 5: } -#line 979 "parser.c" +#line 980 "parser.c" enum {JSON_float_start = 1}; enum {JSON_float_first_final = 8}; enum {JSON_float_error = 0}; @@ -983,7 +984,7 @@ enum {JSON_float_error = 0}; enum {JSON_float_en_main = 1}; -#line 341 "parser.rl" +#line 342 "parser.rl" static int is_bigdecimal_class(VALUE obj) @@ -1004,15 +1005,15 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul int cs = EVIL; -#line 1008 "parser.c" +#line 1009 "parser.c" { cs = JSON_float_start; } -#line 361 "parser.rl" +#line 362 "parser.rl" json->memo = p; -#line 1016 "parser.c" +#line 1017 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1070,14 +1071,14 @@ case 8: goto st0; goto tr9; tr9: -#line 335 "parser.rl" +#line 336 "parser.rl" { p--; {p++; cs = 9; goto _out;} } goto st9; st9: if ( ++p == pe ) goto _test_eof9; case 9: -#line 1081 "parser.c" +#line 1082 "parser.c" goto st0; st5: if ( ++p == pe ) @@ -1138,7 +1139,7 @@ case 7: _out: {} } -#line 363 "parser.rl" +#line 364 "parser.rl" if (cs >= JSON_float_first_final) { long len = p - json->memo; @@ -1164,7 +1165,7 @@ case 7: -#line 1168 "parser.c" +#line 1169 "parser.c" enum {JSON_array_start = 1}; enum {JSON_array_first_final = 17}; enum {JSON_array_error = 0}; @@ -1172,7 +1173,7 @@ enum {JSON_array_error = 0}; enum {JSON_array_en_main = 1}; -#line 416 "parser.rl" +#line 417 "parser.rl" static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) @@ -1186,14 +1187,14 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul *result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class); -#line 1190 "parser.c" +#line 1191 "parser.c" { cs = JSON_array_start; } -#line 429 "parser.rl" +#line 430 "parser.rl" -#line 1197 "parser.c" +#line 1198 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1232,7 +1233,7 @@ case 2: goto st2; goto st0; tr2: -#line 393 "parser.rl" +#line 394 "parser.rl" { VALUE v = Qnil; char *np = JSON_parse_value(json, p, pe, &v, current_nesting); @@ -1252,7 +1253,7 @@ case 2: if ( ++p == pe ) goto _test_eof3; case 3: -#line 1256 "parser.c" +#line 1257 "parser.c" switch( (*p) ) { case 13: goto st3; case 32: goto st3; @@ -1352,14 +1353,14 @@ case 12: goto st3; goto st12; tr4: -#line 408 "parser.rl" +#line 409 "parser.rl" { p--; {p++; cs = 17; goto _out;} } goto st17; st17: if ( ++p == pe ) goto _test_eof17; case 17: -#line 1363 "parser.c" +#line 1364 "parser.c" goto st0; st13: if ( ++p == pe ) @@ -1415,7 +1416,7 @@ case 16: _out: {} } -#line 430 "parser.rl" +#line 431 "parser.rl" if(cs >= JSON_array_first_final) { return p + 1; @@ -1504,7 +1505,7 @@ static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd) } -#line 1508 "parser.c" +#line 1509 "parser.c" enum {JSON_string_start = 1}; enum {JSON_string_first_final = 8}; enum {JSON_string_error = 0}; @@ -1512,7 +1513,7 @@ enum {JSON_string_error = 0}; enum {JSON_string_en_main = 1}; -#line 537 "parser.rl" +#line 538 "parser.rl" static int @@ -1534,15 +1535,15 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu *result = rb_str_buf_new(0); -#line 1538 "parser.c" +#line 1539 "parser.c" { cs = JSON_string_start; } -#line 558 "parser.rl" +#line 559 "parser.rl" json->memo = p; -#line 1546 "parser.c" +#line 1547 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1567,7 +1568,7 @@ case 2: goto st0; goto st2; tr2: -#line 523 "parser.rl" +#line 524 "parser.rl" { *result = json_string_unescape(*result, json->memo + 1, p); if (NIL_P(*result)) { @@ -1578,14 +1579,14 @@ case 2: {p = (( p + 1))-1;} } } -#line 534 "parser.rl" +#line 535 "parser.rl" { p--; {p++; cs = 8; goto _out;} } goto st8; st8: if ( ++p == pe ) goto _test_eof8; case 8: -#line 1589 "parser.c" +#line 1590 "parser.c" goto st0; st3: if ( ++p == pe ) @@ -1661,7 +1662,7 @@ case 7: _out: {} } -#line 560 "parser.rl" +#line 561 "parser.rl" if (json->create_additions && RTEST(match_string = json->match_string)) { VALUE klass; @@ -1848,7 +1849,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } -#line 1852 "parser.c" +#line 1853 "parser.c" enum {JSON_start = 1}; enum {JSON_first_final = 10}; enum {JSON_error = 0}; @@ -1856,7 +1857,7 @@ enum {JSON_error = 0}; enum {JSON_en_main = 1}; -#line 760 "parser.rl" +#line 761 "parser.rl" /* @@ -1873,16 +1874,16 @@ static VALUE cParser_parse(VALUE self) GET_PARSER; -#line 1877 "parser.c" +#line 1878 "parser.c" { cs = JSON_start; } -#line 776 "parser.rl" +#line 777 "parser.rl" p = json->source; pe = p + json->len; -#line 1886 "parser.c" +#line 1887 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1916,7 +1917,7 @@ case 1: cs = 0; goto _out; tr2: -#line 752 "parser.rl" +#line 753 "parser.rl" { char *np = JSON_parse_value(json, p, pe, &result, 0); if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;} @@ -1926,7 +1927,7 @@ cs = 0; if ( ++p == pe ) goto _test_eof10; case 10: -#line 1930 "parser.c" +#line 1931 "parser.c" switch( (*p) ) { case 13: goto st10; case 32: goto st10; @@ -2015,7 +2016,7 @@ case 9: _out: {} } -#line 779 "parser.rl" +#line 780 "parser.rl" if (cs >= JSON_first_final && p == pe) { return result; diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl index 6b38bb283..00a35bded 100644 --- a/ext/json/ext/parser/parser.rl +++ b/ext/json/ext/parser/parser.rl @@ -138,6 +138,7 @@ static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, fhold; fbreak; } else { if (NIL_P(json->object_class)) { + OBJ_FREEZE(last_name); rb_hash_aset(*result, last_name, v); } else { rb_funcall(*result, i_aset, 2, last_name, v); From ee5b6a74e9097c31542b10d9a32e3d3bdfc4822b Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Sun, 28 Jun 2020 13:55:17 -0500 Subject: [PATCH 0063/1010] Added :call-seq: to RDOc for some methods --- lib/json/common.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/json/common.rb b/lib/json/common.rb index c17c0ec33..991d7604f 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -141,6 +141,9 @@ class MissingUnicodeSupport < JSONError; end module_function + # :call-seq: + # JSON.parse(source, opts) -> object + # # Argument +source+ contains the \String to be parsed. It must be a # {String-convertible object}[doc/implicit_conversion_rdoc.html#label-String-Convertible+Objects] # (implementing +to_str+), and must contain valid \JSON data. @@ -260,6 +263,9 @@ def parse(source, opts = {}) Parser.new(source, **(opts||{})).parse end + # :call-seq: + # JSON.parse!(source, opts) -> object + # # Calls # parse(source, opts) # with +source+ and possibly modified +opts+. @@ -276,6 +282,9 @@ def parse!(source, opts = {}) Parser.new(source, **(opts||{})).parse end + # :call-seq: + # JSON.generate(obj, opts = nil) -> new_string + # # Argument +obj+ is the Ruby object to be converted to \JSON. # # Argument +opts+, if given, contains options for the generation, and must be a @@ -463,6 +472,9 @@ def fast_generate(obj, opts = nil) module_function :fast_unparse # :startdoc: + # :call-seq: + # JSON.pretty_generate(obj, opts = nil) -> new_string + # # Arguments +obj+ and +opts+ here are the same as # arguments +obj+ and +opts+ in JSON.generate. # From 26c1769969122493bab659a13b46dfd76fd4b010 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Tue, 30 Jun 2020 00:28:23 -0400 Subject: [PATCH 0064/1010] Typo fix --- ext/json/ext/generator/generator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index ae60c4f46..749efaf72 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -1096,7 +1096,7 @@ static VALUE cState_generate(VALUE self, VALUE obj) * generated, otherwise an exception is thrown, if these values are * encountered. This options defaults to false. * * *ascii_only*: true if only ASCII characters should be generated. This - * ontions defaults to false. + * option defaults to false. * * *buffer_initial_length*: sets the initial length of the generator's * internal buffer. */ From 1a6f004bd354bc9736af76c36e52102e6fd8a223 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Tue, 30 Jun 2020 00:27:36 -0400 Subject: [PATCH 0065/1010] Update Changes for 2.3.0 [Fixes #397] --- CHANGES.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 92a192529..89c026b18 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,13 @@ # Changes +## 2019-12-11 (2.3.0) + * Fix default of `create_additions` to always be false [CVE-2020-10663] + * Fix passing args all #to_json in json/add/*. + * Fix encoding issues + * Fix issues of keyword vs positional parameter + * Fix JSON::Parser against bigdecimal updates + * Bug fixes to JRuby port + ## 2019-02-21 (2.2.0) * Adds support for 2.6 BigDecimal and ruby standard library Set datetype. From 5b4f1baef055d2a7be58cdfe7329b1b2f5b748c9 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Tue, 30 Jun 2020 01:10:49 -0400 Subject: [PATCH 0066/1010] Fix pure parser with unclosed arrays / objects [Fix #314] --- lib/json/pure/parser.rb | 8 ++++++-- tests/fixtures/fail29.json | 1 + tests/fixtures/fail30.json | 1 + tests/fixtures/fail31.json | 1 + tests/fixtures/fail32.json | 1 + 5 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/fail29.json create mode 100644 tests/fixtures/fail30.json create mode 100644 tests/fixtures/fail31.json create mode 100644 tests/fixtures/fail32.json diff --git a/lib/json/pure/parser.rb b/lib/json/pure/parser.rb index 1c8f59f80..2bf93f099 100644 --- a/lib/json/pure/parser.rb +++ b/lib/json/pure/parser.rb @@ -242,8 +242,10 @@ def parse_array @max_nesting.nonzero? && @current_nesting > @max_nesting result = @array_class.new delim = false - until eos? + loop do case + when eos? + raise ParserError, "unexpected end of string while parsing array" when !UNPARSED.equal?(value = parse_value) delim = false result << value @@ -274,8 +276,10 @@ def parse_object @max_nesting.nonzero? && @current_nesting > @max_nesting result = @object_class.new delim = false - until eos? + loop do case + when eos? + raise ParserError, "unexpected end of string while parsing object" when !UNPARSED.equal?(string = parse_string) skip(IGNORE) unless scan(PAIR_DELIMITER) diff --git a/tests/fixtures/fail29.json b/tests/fixtures/fail29.json new file mode 100644 index 000000000..98232c64f --- /dev/null +++ b/tests/fixtures/fail29.json @@ -0,0 +1 @@ +{ diff --git a/tests/fixtures/fail30.json b/tests/fixtures/fail30.json new file mode 100644 index 000000000..558ed37d9 --- /dev/null +++ b/tests/fixtures/fail30.json @@ -0,0 +1 @@ +[ diff --git a/tests/fixtures/fail31.json b/tests/fixtures/fail31.json new file mode 100644 index 000000000..70773e47f --- /dev/null +++ b/tests/fixtures/fail31.json @@ -0,0 +1 @@ +[1, 2, 3, diff --git a/tests/fixtures/fail32.json b/tests/fixtures/fail32.json new file mode 100644 index 000000000..b18d550ca --- /dev/null +++ b/tests/fixtures/fail32.json @@ -0,0 +1 @@ +{"foo": "bar" From 765adb3ed3231b579108ad3d5c8dc7f841d62ab8 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Tue, 30 Jun 2020 00:59:09 -0400 Subject: [PATCH 0067/1010] Fix alignment --- lib/json/pure/parser.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/json/pure/parser.rb b/lib/json/pure/parser.rb index 1c8f59f80..5db244c7a 100644 --- a/lib/json/pure/parser.rb +++ b/lib/json/pure/parser.rb @@ -49,7 +49,7 @@ class Parser < StringScanner )+ )mx - UNPARSED = Object.new.freeze + UNPARSED = Object.new.freeze # Creates a new JSON::Pure::Parser instance for the string _source_. # From 9e2a1fb7d8193dfae67ed70a6f531dd4e4b5f0d5 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Tue, 30 Jun 2020 02:29:19 -0400 Subject: [PATCH 0068/1010] Make changes more precise [#424] --- CHANGES.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 89c026b18..efd747719 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,10 @@ # Changes ## 2019-12-11 (2.3.0) - * Fix default of `create_additions` to always be false [CVE-2020-10663] + * Fix default of `create_additions` to always be `false` for `JSON(user_input)` + and `JSON.parse(user_input, nil)`. + Note that `JSON.load` remains with default `true` and is meant for internal + serialization of trusted data. [CVE-2020-10663] * Fix passing args all #to_json in json/add/*. * Fix encoding issues * Fix issues of keyword vs positional parameter From 4c11a40c1f0de83b5c39877f40143fe8a5e512cd Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 30 Jun 2020 20:19:48 +0900 Subject: [PATCH 0069/1010] Removed task for json_pure.gemspec --- Rakefile | 44 -------------------------------------------- json_pure.gemspec | 2 -- 2 files changed, 46 deletions(-) diff --git a/Rakefile b/Rakefile index 0945a67c8..8e9fd7cac 100644 --- a/Rakefile +++ b/Rakefile @@ -78,50 +78,6 @@ task :install_ext => [ :compile, :install_pure, :install_ext_really ] desc "Installing library (extension)" task :install => :install_ext -if defined?(Gem) and defined?(Gem::PackageTask) - spec_pure = Gem::Specification.new do |s| - s.name = 'json_pure' - s.version = PKG_VERSION - s.summary = PKG_TITLE - s.description = "This is a JSON implementation in pure Ruby." - - s.files = PKG_FILES - - s.require_path = 'lib' - s.add_development_dependency 'rake' - s.add_development_dependency 'test-unit', '>= 2.0', '< 4.0' - - s.extra_rdoc_files << 'README.md' - s.rdoc_options << - '--title' << 'JSON implemention for ruby' << '--main' << 'README.md' - s.test_files.concat Dir['./tests/test_*.rb'] - - s.author = "Florian Frank" - s.email = "flori@ping.de" - s.homepage = "http://flori.github.com/#{PKG_NAME}" - s.license = 'Ruby' - s.required_ruby_version = '>= 2.0' - end - - desc 'Creates a json_pure.gemspec file' - task :gemspec_pure => :version do - File.open('json_pure.gemspec', 'w') do |gemspec| - src = spec_pure.to_ruby - src.gsub!(/# stub:.*/, "# This gemspec is generated automatically using `rake gemspec`.\n" \ - "# Do not modify directly.") - gemspec.write src - end - end - - Gem::PackageTask.new(spec_pure) do |pkg| - pkg.need_tar = true - pkg.package_files = PKG_FILES - end - - desc 'Alias for gemspec_pure' - task :gemspec => [ :gemspec_pure ] -end - desc m = "Writing version information for #{PKG_VERSION}" task :version do puts m diff --git a/json_pure.gemspec b/json_pure.gemspec index 7b4114aff..ecd18314a 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -1,6 +1,4 @@ # -*- encoding: utf-8 -*- -# This gemspec is generated automatically using `rake gemspec`. -# Do not modify directly. Gem::Specification.new do |s| s.name = "json_pure".freeze From dc90bcf240fe8059157e6e37cd52944e64523266 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 30 Jun 2020 20:21:16 +0900 Subject: [PATCH 0070/1010] Removed explicitly date field in gemspec, it will assign by rubygems.org --- json_pure.gemspec | 1 - 1 file changed, 1 deletion(-) diff --git a/json_pure.gemspec b/json_pure.gemspec index ecd18314a..8203a7fcf 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -7,7 +7,6 @@ Gem::Specification.new do |s| s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Florian Frank".freeze] - s.date = "2020-06-25" s.description = "This is a JSON implementation in pure Ruby.".freeze s.email = "flori@ping.de".freeze s.extra_rdoc_files = ["README.md".freeze] From 9fd637107f1267726acee108e88e21dc773f137e Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 30 Jun 2020 20:23:49 +0900 Subject: [PATCH 0071/1010] Use VERSION file instead of hard-coded value --- json_pure.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json_pure.gemspec b/json_pure.gemspec index 8203a7fcf..007acd367 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |s| s.name = "json_pure".freeze - s.version = "2.3.0" + s.version = File.read("VERSION").chomp s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] From cee8020dc8a3c33936149667ada3f017d396ade7 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 30 Jun 2020 20:35:07 +0900 Subject: [PATCH 0072/1010] Removed gemspec task from default task on Rakefile --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 8e9fd7cac..5b422926e 100644 --- a/Rakefile +++ b/Rakefile @@ -331,4 +331,4 @@ else end desc "Compile in the the source directory" -task :default => [ :clean, :gemspec, :test ] +task :default => [ :clean, :test ] From 0be363c99bb33da6b2b1b0149269b4a2e91120f7 Mon Sep 17 00:00:00 2001 From: Keith Bennett Date: Tue, 30 Jun 2020 14:07:23 -0400 Subject: [PATCH 0073/1010] Add `load_file` and `load_file!` methods, with tests. Fixes issue #386. --- lib/json/common.rb | 10 ++++++ tests/json_common_interface_test.rb | 56 +++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/lib/json/common.rb b/lib/json/common.rb index 991d7604f..bd339971c 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -282,6 +282,16 @@ def parse!(source, opts = {}) Parser.new(source, **(opts||{})).parse end + # Parses the content of a file (see parse method documentation for more information). + def load_file(filespec, opts = {}) + parse(File.read(filespec), opts) + end + + # Parses the content of a file (see parse! method documentation for more information). + def load_file!(filespec, opts = {}) + parse!(File.read(filespec), opts) + end + # :call-seq: # JSON.generate(obj, opts = nil) -> new_string # diff --git a/tests/json_common_interface_test.rb b/tests/json_common_interface_test.rb index 53f335ed3..4fdc2b118 100644 --- a/tests/json_common_interface_test.rb +++ b/tests/json_common_interface_test.rb @@ -123,4 +123,60 @@ def test_JSON assert_equal @json, JSON(@hash) assert_equal @hash, JSON(@json) end + + def test_load_file + test_load_shared(:load_file) + end + + def test_load_file! + test_load_shared(:load_file!) + end + + def test_load_file_with_option + test_load_file_with_option_shared(:load_file) + end + + def test_load_file_with_option! + test_load_file_with_option_shared(:load_file!) + end + + private + + def test_load_shared(method_name) + temp_file_containing(@json) do |filespec| + assert_equal JSON.public_send(method_name, filespec), @hash + end + end + + def test_load_file_with_option_shared(method_name) + temp_file_containing(@json) do |filespec| + parsed_object = JSON.public_send(method_name, filespec, symbolize_names: true) + key_classes = parsed_object.keys.map(&:class) + assert_true key_classes.include?(Symbol) && (! key_classes.include?(String)) + end + end + + # Copied and slightly modified from https://github.com/keithrbennett/trick_bag + # (https://github.com/keithrbennett/trick_bag/blob/master/lib/trick_bag/io/temp_files.rb). + # + # For the easy creation and deletion of a temp file populated with text, + # wrapped around the code block you provide. + # + # @param text the text to write to the temporary file + # @param file_prefix optional prefix for the temporary file's name + # @yield filespec of the temporary file + def temp_file_containing(text, file_prefix = '') + raise "This method must be called with a code block." unless block_given? + + filespec = nil + begin + Tempfile.open(file_prefix) do |file| + file << text + filespec = file.path + end + yield(filespec) + ensure + File.delete(filespec) if filespec && File.exist?(filespec) + end + end end From 20d7be605a62198ffec4cf74080553c128d404e2 Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Tue, 30 Jun 2020 14:50:06 -0500 Subject: [PATCH 0074/1010] Move options from #generate and #parse to common area --- lib/json.rb | 171 +++++++++++++++++++++++++++++++++++++++++ lib/json/common.rb | 187 +++------------------------------------------ 2 files changed, 181 insertions(+), 177 deletions(-) diff --git a/lib/json.rb b/lib/json.rb index 3eb59f84b..6bb82245b 100644 --- a/lib/json.rb +++ b/lib/json.rb @@ -107,6 +107,89 @@ # ruby # => nil # ruby.class # => NilClass # +# ==== Parsing Options +# +# ====== Input Options +# +# Option +max_nesting+ (\Integer) specifies the maximum nesting depth allowed; +# defaults to +100+; specify +false+ to disable depth checking. +# +# With the default, +false+: +# source = '[0, [1, [2, [3]]]]' +# ruby = JSON.parse(source) +# ruby # => [0, [1, [2, [3]]]] +# Too deep: +# # Raises JSON::NestingError (nesting of 2 is too deep): +# JSON.parse(source, {max_nesting: 1}) +# Bad value: +# # Raises TypeError (wrong argument type Symbol (expected Fixnum)): +# JSON.parse(source, {max_nesting: :foo}) +# +# --- +# +# Option +allow_nan+ (boolean) specifies whether to allow +# NaN, Infinity, and MinusInfinity in +source+; +# defaults to +false+. +# +# With the default, +false+: +# # Raises JSON::ParserError (225: unexpected token at '[NaN]'): +# JSON.parse('[NaN]') +# # Raises JSON::ParserError (232: unexpected token at '[Infinity]'): +# JSON.parse('[Infinity]') +# # Raises JSON::ParserError (248: unexpected token at '[-Infinity]'): +# JSON.parse('[-Infinity]') +# Allow: +# source = '[NaN, Infinity, -Infinity]' +# ruby = JSON.parse(source, {allow_nan: true}) +# ruby # => [NaN, Infinity, -Infinity] +# +# ====== Output Options +# +# Option +symbolize_names+ (boolean) specifies whether returned \Hash keys +# should be Symbols; +# defaults to +false+ (use Strings). +# +# With the default, +false+: +# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}' +# ruby = JSON.parse(source) +# ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil} +# Use Symbols: +# ruby = JSON.parse(source, {symbolize_names: true}) +# ruby # => {:a=>"foo", :b=>1.0, :c=>true, :d=>false, :e=>nil} +# +# --- +# +# Option +object_class+ (\Class) specifies the Ruby class to be used +# for each \JSON object; +# defaults to \Hash. +# +# With the default, \Hash: +# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}' +# ruby = JSON.parse(source) +# ruby.class # => Hash +# Use class \OpenStruct: +# ruby = JSON.parse(source, {object_class: OpenStruct}) +# ruby # => # +# +# --- +# +# Option +array_class+ (\Class) specifies the Ruby class to be used +# for each \JSON array; +# defaults to \Array. +# +# With the default, \Array: +# source = '["foo", 1.0, true, false, null]' +# ruby = JSON.parse(source) +# ruby.class # => Array +# Use class \Set: +# ruby = JSON.parse(source, {array_class: Set}) +# ruby # => # +# +# --- +# +# Option +create_additions+ (boolean) specifies whether to use \JSON additions in parsing. +# See {\JSON Additions}[#module-JSON-label-JSON+Additions]. +# # === Generating \JSON # # To generate a Ruby \String containing \JSON data, @@ -169,6 +252,94 @@ # JSON.generate(Complex(0, 0)) # => '"0+0i"' # JSON.generate(Dir.new('.')) # => '"#"' # +# ==== Generating Options +# +# ====== Input Options +# +# Option +allow_nan+ (boolean) specifies whether +# +NaN+, +Infinity+, and -Infinity may be generated; +# defaults to +false+. +# +# With the default, +false+: +# # Raises JSON::GeneratorError (920: NaN not allowed in JSON): +# JSON.generate(JSON::NaN) +# # Raises JSON::GeneratorError (917: Infinity not allowed in JSON): +# JSON.generate(JSON::Infinity) +# # Raises JSON::GeneratorError (917: -Infinity not allowed in JSON): +# JSON.generate(JSON::MinusInfinity) +# +# Allow: +# ruby = [Float::NaN, Float::Infinity, Float::MinusInfinity] +# JSON.generate(ruby, allow_nan: true) # => '[NaN,Infinity,-Infinity]' +# +# --- +# +# Option +max_nesting+ (\Integer) specifies the maximum nesting depth +# in +obj+; defaults to +100+. +# +# With the default, +100+: +# obj = [[[[[[0]]]]]] +# JSON.generate(obj) # => '[[[[[[0]]]]]]' +# +# Too deep: +# # Raises JSON::NestingError (nesting of 2 is too deep): +# JSON.generate(obj, max_nesting: 2) +# +# ====== Output Options +# +# The default formatting options generate the most compact +# \JSON data, all on one line and with no whitespace. +# +# You can use these formatting options to generate +# \JSON data in a more open format, using whitespace. +# See also JSON.pretty_generate. +# +# - Option +array_nl+ (\String) specifies a string (usually a newline) +# to be inserted after each \JSON array; defaults to the empty \String, ''. +# - Option +object_nl+ (\String) specifies a string (usually a newline) +# to be inserted after each \JSON object; defaults to the empty \String, ''. +# - Option +indent+ (\String) specifies the string (usually spaces) to be +# used for indentation; defaults to the empty \String, ''; +# defaults to the empty \String, ''; +# has no effect unless options +array_nl+ or +object_nl+ specify newlines. +# - Option +space+ (\String) specifies a string (usually a space) to be +# inserted after the colon in each \JSON object's pair; +# defaults to the empty \String, ''. +# - Option +space_before+ (\String) specifies a string (usually a space) to be +# inserted before the colon in each \JSON object's pair; +# defaults to the empty \String, ''. +# +# In this example, +obj+ is used first to generate the shortest +# \JSON data (no whitespace), then again with all formatting options +# specified: +# +# obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}} +# json = JSON.generate(obj) +# puts 'Compact:', json +# opts = { +# array_nl: "\n", +# object_nl: "\n", +# indent+: ' ', +# space_before: ' ', +# space: ' ' +# } +# puts 'Open:', JSON.generate(obj, opts) +# +# Output: +# Compact: +# {"foo":["bar","baz"],"bat":{"bam":0,"bad":1}} +# Open: +# { +# "foo" : [ +# "bar", +# "baz" +# ], +# "bat" : { +# "bam" : 0, +# "bad" : 1 +# } +# } +# # == \JSON Additions # # When you "round trip" a non-\String object from Ruby to \JSON and back, diff --git a/lib/json/common.rb b/lib/json/common.rb index 991d7604f..6bec15b44 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -144,15 +144,15 @@ class MissingUnicodeSupport < JSONError; end # :call-seq: # JSON.parse(source, opts) -> object # + # Returns the Ruby objects created by parsing the given +source+. + # # Argument +source+ contains the \String to be parsed. It must be a # {String-convertible object}[doc/implicit_conversion_rdoc.html#label-String-Convertible+Objects] # (implementing +to_str+), and must contain valid \JSON data. # # Argument +opts+, if given, contains options for the parsing, and must be a - # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects] - # (implementing +to_hash+). - # - # Returns the Ruby objects created by parsing the given +source+. + # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash+Convertible+Objects]. + # See {Parsing Options}[#module-JSON-label-Parsing+Options]. # # --- # @@ -171,87 +171,6 @@ class MissingUnicodeSupport < JSONError; end # For examples of parsing for all \JSON data types, see # {Parsing \JSON}[#module-JSON-label-Parsing+JSON]. # - # ====== Input Options - # - # Option +max_nesting+ (\Integer) specifies the maximum nesting depth allowed; - # defaults to +100+; specify +false+ to disable depth checking. - # - # With the default, +false+: - # source = '[0, [1, [2, [3]]]]' - # ruby = JSON.parse(source) - # ruby # => [0, [1, [2, [3]]]] - # Too deep: - # # Raises JSON::NestingError (nesting of 2 is too deep): - # JSON.parse(source, {max_nesting: 1}) - # Bad value: - # # Raises TypeError (wrong argument type Symbol (expected Fixnum)): - # JSON.parse(source, {max_nesting: :foo}) - # - # --- - # - # Option +allow_nan+ (boolean) specifies whether to allow - # NaN, Infinity, and MinusInfinity in +source+; - # defaults to +false+. - # - # With the default, +false+: - # # Raises JSON::ParserError (225: unexpected token at '[NaN]'): - # JSON.parse('[NaN]') - # # Raises JSON::ParserError (232: unexpected token at '[Infinity]'): - # JSON.parse('[Infinity]') - # # Raises JSON::ParserError (248: unexpected token at '[-Infinity]'): - # JSON.parse('[-Infinity]') - # Allow: - # source = '[NaN, Infinity, -Infinity]' - # ruby = JSON.parse(source, {allow_nan: true}) - # ruby # => [NaN, Infinity, -Infinity] - # - # ====== Output Options - # - # Option +symbolize_names+ (boolean) specifies whether returned \Hash keys - # should be Symbols; - # defaults to +false+ (use Strings). - # - # With the default, +false+: - # source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}' - # ruby = JSON.parse(source) - # ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil} - # Use Symbols: - # ruby = JSON.parse(source, {symbolize_names: true}) - # ruby # => {:a=>"foo", :b=>1.0, :c=>true, :d=>false, :e=>nil} - # - # --- - # - # Option +object_class+ (\Class) specifies the Ruby class to be used - # for each \JSON object; - # defaults to \Hash. - # - # With the default, \Hash: - # source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}' - # ruby = JSON.parse(source) - # ruby.class # => Hash - # Use class \OpenStruct: - # ruby = JSON.parse(source, {object_class: OpenStruct}) - # ruby # => # - # - # --- - # - # Option +array_class+ (\Class) specifies the Ruby class to be used - # for each \JSON array; - # defaults to \Array. - # - # With the default, \Array: - # source = '["foo", 1.0, true, false, null]' - # ruby = JSON.parse(source) - # ruby.class # => Array - # Use class \Set: - # ruby = JSON.parse(source, {array_class: Set}) - # ruby # => # - # - # --- - # - # Option +create_additions+ (boolean) specifies whether to use \JSON additions in parsing. - # See {\JSON Additions}[#module-JSON-label-JSON+Additions]. - # # ====== Exceptions # # Raises an exception if +source+ is not valid JSON: @@ -285,16 +204,16 @@ def parse!(source, opts = {}) # :call-seq: # JSON.generate(obj, opts = nil) -> new_string # - # Argument +obj+ is the Ruby object to be converted to \JSON. - # - # Argument +opts+, if given, contains options for the generation, and must be a - # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects] - # (implementing +to_hash+). - # # Returns a \String containing the generated \JSON data. # # See also JSON.fast_generate, JSON.pretty_generate. # + # Argument +obj+ is the Ruby object to be converted to \JSON. + # + # Argument +opts+, if given, contains options for the generation, and must be a + # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects]. + # See {Generating Options}[#module-JSON-label-Generating+Options]. + # # --- # # When +obj+ is an @@ -314,92 +233,6 @@ def parse!(source, opts = {}) # For examples of generating from other Ruby objects, see # {Generating \JSON from Other Objects}[#module-JSON-label-Generating+JSON+from+Other+Objects]. # - # ====== Input Options - # - # Option +allow_nan+ (boolean) specifies whether - # +NaN+, +Infinity+, and -Infinity may be generated; - # defaults to +false+. - # - # With the default, +false+: - # # Raises JSON::GeneratorError (920: NaN not allowed in JSON): - # JSON.generate(JSON::NaN) - # # Raises JSON::GeneratorError (917: Infinity not allowed in JSON): - # JSON.generate(JSON::Infinity) - # # Raises JSON::GeneratorError (917: -Infinity not allowed in JSON): - # JSON.generate(JSON::MinusInfinity) - # - # Allow: - # ruby = [Float::NaN, Float::Infinity, Float::MinusInfinity] - # JSON.generate(ruby, allow_nan: true) # => '[NaN,Infinity,-Infinity]' - # - # --- - # - # Option +max_nesting+ (\Integer) specifies the maximum nesting depth - # in +obj+; defaults to +100+. - # - # With the default, +100+: - # obj = [[[[[[0]]]]]] - # JSON.generate(obj) # => '[[[[[[0]]]]]]' - # - # Too deep: - # # Raises JSON::NestingError (nesting of 2 is too deep): - # JSON.generate(obj, max_nesting: 2) - # - # ====== Output Options - # - # The default formatting options generate the most compact - # \JSON data, all on one line and with no whitespace. - # - # You can use these formatting options to generate - # \JSON data in a more open format, using whitespace. - # See also JSON.pretty_generate. - # - # - Option +array_nl+ (\String) specifies a string (usually a newline) - # to be inserted after each \JSON array; defaults to the empty \String, ''. - # - Option +object_nl+ (\String) specifies a string (usually a newline) - # to be inserted after each \JSON object; defaults to the empty \String, ''. - # - Option +indent+ (\String) specifies the string (usually spaces) to be - # used for indentation; defaults to the empty \String, ''; - # defaults to the empty \String, ''; - # has no effect unless options +array_nl+ or +object_nl+ specify newlines. - # - Option +space+ (\String) specifies a string (usually a space) to be - # inserted after the colon in each \JSON object's pair; - # defaults to the empty \String, ''. - # - Option +space_before+ (\String) specifies a string (usually a space) to be - # inserted before the colon in each \JSON object's pair; - # defaults to the empty \String, ''. - # - # In this example, +obj+ is used first to generate the shortest - # \JSON data (no whitespace), then again with all formatting options - # specified: - # - # obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}} - # json = JSON.generate(obj) - # puts 'Compact:', json - # opts = { - # array_nl: "\n", - # object_nl: "\n", - # indent+: ' ', - # space_before: ' ', - # space: ' ' - # } - # puts 'Open:', JSON.generate(obj, opts) - # - # Output: - # Compact: - # {"foo":["bar","baz"],"bat":{"bam":0,"bad":1}} - # Open: - # { - # "foo" : [ - # "bar", - # "baz" - # ], - # "bat" : { - # "bam" : 0, - # "bad" : 1 - # } - # } - # # --- # # Raises an exception if any formatting option is not a \String. From 0a76a1f509d78b948da59df51b2253f63b05ef2a Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 30 Jun 2020 20:55:51 +0900 Subject: [PATCH 0075/1010] Bump version to 2.3.1 --- CHANGES.md | 22 ++++++++++++++++++++++ VERSION | 2 +- lib/json/version.rb | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index efd747719..0d39f21ec 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,27 @@ # Changes +## 2020-06-30 (2.3.1) + +* Spelling and grammar fixes for comments. Pull request #191 by Josh + Kline. +* Enhance generic JSON and #generate docs. Pull request #347 by Victor + Shepelev. +* Add :nodoc: for GeneratorMethods. Pull request #349 by Victor Shepelev. +* Baseline changes to help (JRuby) development. Pull request #371 by Karol + Bucek. +* Add metadata for rubygems.org. Pull request #379 by Alexandre ZANNI. +* Remove invalid JSON.generate description from JSON module rdoc. Pull + request #384 by Jeremy Evans. +* Test with TruffleRuby in CI. Pull request #402 by Benoit Daloze. +* Rdoc enhancements. Pull request #413 by Burdette Lamar. +* Fixtures/ are not being tested... Pull request #416 by Marc-André + Lafortune. +* Use frozen string for hash key. Pull request #420 by Marc-André + Lafortune. +* Added :call-seq: to RDoc for some methods. Pull request #422 by Burdette + Lamar. +* Small typo fix. Pull request #423 by Marc-André Lafortune. + ## 2019-12-11 (2.3.0) * Fix default of `create_additions` to always be `false` for `JSON(user_input)` and `JSON.parse(user_input, nil)`. diff --git a/VERSION b/VERSION index 276cbf9e2..2bf1c1ccf 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.3.0 +2.3.1 diff --git a/lib/json/version.rb b/lib/json/version.rb index 9d781df87..e4dd3e336 100644 --- a/lib/json/version.rb +++ b/lib/json/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module JSON # JSON version - VERSION = '2.3.0' + VERSION = '2.3.1' VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc: VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc: VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: From 607678068648e2922c3728b4e5e954ff5bb4a9ef Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Fri, 3 Jul 2020 11:28:16 +0900 Subject: [PATCH 0076/1010] Do not add a relative path to $LOAD_PATH ... because it conflicts with test/ruby/test_m17n.rb. An exception `incompatible character encodings: UTF-8 and UTF-16BE` occurs when: * a non-existence relative path is added to $LOAD_PATH, * ASCII-incompatible encoding is set to default_external, and * some file is loaded. ``` $LOAD_PATH << "no_existing_dir" Encoding.default_external = Encoding::UTF_16BE load "dummy.rb" #=> incompatible character encodings: UTF-8 and UTF-16BE ``` This issue can be actually observed by a combination of out-of-place build and the following command: make test-all TESTS="json ruby/m17n -n test_object_inspect_external" http://ci.rvm.jp/logfiles/brlog.trunk-test-random.20200322-221411 ASCII-incompatible default external encoding assumes that the cwd is the encoding, and it is attempted to beconcatenated with a non-existence relative LOAD_PATH UTF-8 string, which causes the exception. This changeset avoids a relative path. --- tests/test_helper.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_helper.rb b/tests/test_helper.rb index c5ec0fca7..446c020e8 100644 --- a/tests/test_helper.rb +++ b/tests/test_helper.rb @@ -1,12 +1,12 @@ case ENV['JSON'] when 'pure' - $:.unshift 'lib' + $:.unshift File.join(__dir__, '../lib') require 'json/pure' when 'ext' - $:.unshift 'ext', 'lib' + $:.unshift File.join(__dir__, '../ext'), File.join(__dir__, '../lib') require 'json/ext' else - $:.unshift 'ext', 'lib' + $:.unshift File.join(__dir__, '../ext'), File.join(__dir__, '../lib') require 'json' end From 438d85e0407902cf76c497662be5381ea7f9d4c5 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Fri, 3 Jul 2020 11:48:46 +0900 Subject: [PATCH 0077/1010] test/json/json_fixtures_test.rb: Prevent an "out of range" warning with `make test-all RUBYOPT=-w` ``` /home/mame/work/ruby/.ext/common/json/common.rb:263: warning: Float 23456789012E666 out of range ``` --- tests/json_fixtures_test.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/json_fixtures_test.rb b/tests/json_fixtures_test.rb index b2a1ec8b8..845abb486 100644 --- a/tests/json_fixtures_test.rb +++ b/tests/json_fixtures_test.rb @@ -10,6 +10,7 @@ def setup end def test_passing + verbose_bak, $VERBOSE = $VERBOSE, nil for name, source in @passed begin assert JSON.parse(source), @@ -19,6 +20,8 @@ def test_passing raise e end end + ensure + $VERBOSE = verbose_bak end def test_failing From 33e64ef255948923c26ef4f6eb9c2909f30e7b46 Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Fri, 3 Jul 2020 14:11:26 -0500 Subject: [PATCH 0078/1010] Enhance RDoc for JSON.parse --- lib/json/common.rb | 49 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/lib/json/common.rb b/lib/json/common.rb index 132e9c870..5ef3ac248 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -4,13 +4,16 @@ module JSON class << self + # :call-seq: + # JSON[object] -> new_array or new_string + # # If +object+ is a - # {String-convertible object}[doc/implicit_conversion_rdoc.html#label-String-Convertible+Objects] - # (implementing +to_str+), calls JSON.parse with +object+ and +opts+: + # {String-convertible object}[doc/implicit_conversion_rdoc.html#label-String-Convertible+Objects], + # calls JSON.parse with +object+ and +opts+ (see method #parse): # json = '[0, 1, null]' # JSON[json]# => [0, 1, nil] # - # Otherwise, calls JSON.generate with +object+ and +opts+: + # Otherwise, calls JSON.generate with +object+ and +opts+ (see method #generate): # ruby = [0, 1, nil] # JSON[ruby] # => '[0,1,null]' def [](object, opts = {}) @@ -171,10 +174,24 @@ class MissingUnicodeSupport < JSONError; end # For examples of parsing for all \JSON data types, see # {Parsing \JSON}[#module-JSON-label-Parsing+JSON]. # - # ====== Exceptions + # Parses nested JSON objects: + # source = <<-EOT + # { + # "name": "Dave", + # "age" :40, + # "hats": [ + # "Cattleman's", + # "Panama", + # "Tophat" + # ] + # } + # EOT + # ruby = JSON.parse(source) + # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]} # - # Raises an exception if +source+ is not valid JSON: + # --- # + # Raises an exception if +source+ is not valid JSON: # # Raises JSON::ParserError (783: unexpected token at ''): # JSON.parse('') # @@ -201,12 +218,24 @@ def parse!(source, opts = {}) Parser.new(source, **(opts||{})).parse end - # Parses the content of a file (see parse method documentation for more information). + # :call-seq: + # CSV.load_file(path, opts={}) -> object + # + # Calls: + # parse(File.read(path), opts) + # + # See method #parse. def load_file(filespec, opts = {}) parse(File.read(filespec), opts) end - # Parses the content of a file (see parse! method documentation for more information). + # :call-seq: + # CSV.load_file!(path, opts = {}) + # + # Calls: + # CSV.parse!(File.read(path, opts)) + # + # See method #parse! def load_file!(filespec, opts = {}) parse!(File.read(filespec), opts) end @@ -247,8 +276,6 @@ def load_file!(filespec, opts = {}) # # Raises an exception if any formatting option is not a \String. # - # ====== Exceptions - # # Raises an exception if +obj+ contains circular references: # a = []; b = []; a.push(b); b.push(a) # # Raises JSON::NestingError (nesting of 100 is too deep): @@ -280,6 +307,9 @@ def generate(obj, opts = nil) module_function :unparse # :startdoc: + # :call-seq: + # JSON.fast_generate(obj, opts) -> new_string + # # Arguments +obj+ and +opts+ here are the same as # arguments +obj+ and +opts+ in JSON.generate. # @@ -398,6 +428,7 @@ class << self # # This method is part of the implementation of the load/dump interface of # Marshal and YAML. + # def load(source, proc = nil, options = {}) opts = load_default_options.merge options if source.respond_to? :to_str From 8a18cf6a14fc1fae54b3d581434a255c9d98ceea Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Thu, 30 Jan 2020 12:48:05 +0100 Subject: [PATCH 0079/1010] Add an option to escape forward slash character Squashed commit of the following: commit 26d181059989279a79c433cedcd893b4f52e42ee Author: Francois Chagnon Date: Tue Sep 15 21:17:34 2015 +0000 add config options for escape_slash commit fa282334051b16df91ca097dd7304b46f3bc7719 Author: Francois Chagnon Date: Mon Feb 9 21:09:33 2015 +0000 add forward slash to escape character --- ext/json/ext/generator/generator.c | 53 ++++++++++++++++++++++++--- ext/json/ext/generator/generator.h | 7 +++- java/src/json/ext/Generator.java | 2 +- java/src/json/ext/GeneratorState.java | 30 +++++++++++++++ java/src/json/ext/StringEncoder.java | 10 ++++- lib/json/common.rb | 3 +- lib/json/pure/generator.rb | 30 ++++++++++++--- tests/json_generator_test.rb | 7 ++++ tests/json_parser_test.rb | 4 ++ 9 files changed, 129 insertions(+), 17 deletions(-) mode change 100644 => 100755 tests/json_generator_test.rb diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 749efaf72..80d1ca7bf 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -22,7 +22,7 @@ static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before, i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only, i_pack, i_unpack, i_create_id, i_extend, i_key_p, i_aref, i_send, i_respond_to_p, i_match, i_keys, i_depth, - i_buffer_initial_length, i_dup; + i_buffer_initial_length, i_dup, i_escape_slash; /* * Copyright 2001-2004 Unicode, Inc. @@ -130,7 +130,7 @@ static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16 /* Converts string to a JSON string in FBuffer buffer, where all but the ASCII * and control characters are JSON escaped. */ -static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string) +static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string, char escape_slash) { const UTF8 *source = (UTF8 *) RSTRING_PTR(string); const UTF8 *sourceEnd = source + RSTRING_LEN(string); @@ -180,6 +180,11 @@ static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string) case '"': fbuffer_append(buffer, "\\\"", 2); break; + case '/': + if(escape_slash) { + fbuffer_append(buffer, "\\/", 2); + break; + } default: fbuffer_append_char(buffer, (char)ch); break; @@ -229,7 +234,7 @@ static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string) * characters required by the JSON standard are JSON escaped. The remaining * characters (should be UTF8) are just passed through and appended to the * result. */ -static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string) +static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string, char escape_slash) { const char *ptr = RSTRING_PTR(string), *p; unsigned long len = RSTRING_LEN(string), start = 0, end = 0; @@ -280,6 +285,12 @@ static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string) escape = "\\\""; escape_len = 2; break; + case '/': + if(escape_slash) { + escape = "\\/"; + escape_len = 2; + break; + } default: { unsigned short clen = 1; @@ -716,6 +727,8 @@ static VALUE cState_configure(VALUE self, VALUE opts) state->allow_nan = RTEST(tmp); tmp = rb_hash_aref(opts, ID2SYM(i_ascii_only)); state->ascii_only = RTEST(tmp); + tmp = rb_hash_aref(opts, ID2SYM(i_escape_slash)); + state->escape_slash = RTEST(tmp); return self; } @@ -750,6 +763,7 @@ static VALUE cState_to_h(VALUE self) rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse); rb_hash_aset(result, ID2SYM(i_ascii_only), state->ascii_only ? Qtrue : Qfalse); rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting)); + rb_hash_aset(result, ID2SYM(i_escape_slash), state->escape_slash ? Qtrue : Qfalse); rb_hash_aset(result, ID2SYM(i_depth), LONG2FIX(state->depth)); rb_hash_aset(result, ID2SYM(i_buffer_initial_length), LONG2FIX(state->buffer_initial_length)); return result; @@ -934,9 +948,9 @@ static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_S } #endif if (state->ascii_only) { - convert_UTF8_to_JSON_ASCII(buffer, obj); + convert_UTF8_to_JSON_ASCII(buffer, obj, state->escape_slash); } else { - convert_UTF8_to_JSON(buffer, obj); + convert_UTF8_to_JSON(buffer, obj, state->escape_slash); } fbuffer_append_char(buffer, '"'); } @@ -1377,6 +1391,31 @@ static VALUE cState_max_nesting_set(VALUE self, VALUE depth) return state->max_nesting = FIX2LONG(depth); } +/* + * call-seq: escape_slash + * + * If this boolean is true, the forward slashes will be escaped in + * the json output. + */ +static VALUE cState_escape_slash(VALUE self) +{ + GET_STATE(self); + return state->escape_slash ? Qtrue : Qfalse; +} + +/* + * call-seq: escape_slash=(depth) + * + * This sets whether or not the forward slashes will be escaped in + * the json output. + */ +static VALUE cState_escape_slash_set(VALUE self, VALUE enable) +{ + GET_STATE(self); + state->escape_slash = RTEST(enable); + return Qnil; +} + /* * call-seq: allow_nan? * @@ -1489,6 +1528,9 @@ void Init_generator(void) rb_define_method(cState, "array_nl=", cState_array_nl_set, 1); rb_define_method(cState, "max_nesting", cState_max_nesting, 0); rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1); + rb_define_method(cState, "escape_slash", cState_escape_slash, 0); + rb_define_method(cState, "escape_slash?", cState_escape_slash, 0); + rb_define_method(cState, "escape_slash=", cState_escape_slash_set, 1); rb_define_method(cState, "check_circular?", cState_check_circular_p, 0); rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0); rb_define_method(cState, "ascii_only?", cState_ascii_only_p, 0); @@ -1545,6 +1587,7 @@ void Init_generator(void) i_object_nl = rb_intern("object_nl"); i_array_nl = rb_intern("array_nl"); i_max_nesting = rb_intern("max_nesting"); + i_escape_slash = rb_intern("escape_slash"); i_allow_nan = rb_intern("allow_nan"); i_ascii_only = rb_intern("ascii_only"); i_depth = rb_intern("depth"); diff --git a/ext/json/ext/generator/generator.h b/ext/json/ext/generator/generator.h index c367a6209..3ebd62255 100644 --- a/ext/json/ext/generator/generator.h +++ b/ext/json/ext/generator/generator.h @@ -49,8 +49,8 @@ static const UTF32 halfMask = 0x3FFUL; static unsigned char isLegalUTF8(const UTF8 *source, unsigned long length); static void unicode_escape(char *buf, UTF16 character); static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16 character); -static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string); -static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string); +static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string, char escape_slash); +static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string, char escape_slash); static char *fstrndup(const char *ptr, unsigned long len); /* ruby api and some helpers */ @@ -72,6 +72,7 @@ typedef struct JSON_Generator_StateStruct { long max_nesting; char allow_nan; char ascii_only; + char escape_slash; long depth; long buffer_initial_length; } JSON_Generator_State; @@ -150,6 +151,8 @@ static VALUE cState_allow_nan_p(VALUE self); static VALUE cState_ascii_only_p(VALUE self); static VALUE cState_depth(VALUE self); static VALUE cState_depth_set(VALUE self, VALUE depth); +static VALUE cState_escape_slash(VALUE self); +static VALUE cState_escape_slash_set(VALUE self, VALUE depth); static FBuffer *cState_prepare_buffer(VALUE self); #ifndef ZALLOC #define ZALLOC(type) ((type *)ruby_zalloc(sizeof(type))) diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index 24bf04974..cd23c73bf 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -158,7 +158,7 @@ public RuntimeInfo getInfo() { public StringEncoder getStringEncoder() { if (stringEncoder == null) { - stringEncoder = new StringEncoder(context, getState().asciiOnly()); + stringEncoder = new StringEncoder(context, getState().asciiOnly(), getState().escapeSlash()); } return stringEncoder; } diff --git a/java/src/json/ext/GeneratorState.java b/java/src/json/ext/GeneratorState.java index 44a931132..7fec09d4f 100644 --- a/java/src/json/ext/GeneratorState.java +++ b/java/src/json/ext/GeneratorState.java @@ -82,6 +82,12 @@ public class GeneratorState extends RubyObject { */ private boolean quirksMode = DEFAULT_QUIRKS_MODE; static final boolean DEFAULT_QUIRKS_MODE = false; + /** + * If set to true the forward slash will be escaped in + * json output. + */ + private boolean escapeSlash = DEFAULT_ESCAPE_SLASH; + static final boolean DEFAULT_ESCAPE_SLASH = false; /** * The initial buffer length of this state. (This isn't really used on all * non-C implementations.) @@ -171,6 +177,9 @@ static GeneratorState fromState(ThreadContext context, RuntimeInfo info, * -Infinity should be generated, otherwise an exception is * thrown if these values are encountered. * This options defaults to false. + *
:escape_slash + *
set to true if the forward slashes should be escaped + * in the json output (default: false) */ @JRubyMethod(optional=1, visibility=Visibility.PRIVATE) public IRubyObject initialize(ThreadContext context, IRubyObject[] args) { @@ -194,6 +203,7 @@ public IRubyObject initialize_copy(ThreadContext context, IRubyObject vOrig) { this.allowNaN = orig.allowNaN; this.asciiOnly = orig.asciiOnly; this.quirksMode = orig.quirksMode; + this.escapeSlash = orig.escapeSlash; this.bufferInitialLength = orig.bufferInitialLength; this.depth = orig.depth; return this; @@ -346,6 +356,24 @@ public IRubyObject max_nesting_set(IRubyObject max_nesting) { return max_nesting; } + /** + * Returns true if forward slashes are escaped in the json output. + */ + public boolean escapeSlash() { + return escapeSlash; + } + + @JRubyMethod(name="escape_slash") + public RubyBoolean escape_slash_get(ThreadContext context) { + return context.getRuntime().newBoolean(escapeSlash); + } + + @JRubyMethod(name="escape_slash=") + public IRubyObject escape_slash_set(IRubyObject escape_slash) { + escapeSlash = escape_slash.isTrue(); + return escape_slash.getRuntime().newBoolean(escapeSlash); + } + public boolean allowNaN() { return allowNaN; } @@ -430,6 +458,7 @@ public IRubyObject configure(ThreadContext context, IRubyObject vOpts) { maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING); allowNaN = opts.getBool("allow_nan", DEFAULT_ALLOW_NAN); asciiOnly = opts.getBool("ascii_only", DEFAULT_ASCII_ONLY); + escapeSlash = opts.getBool("escape_slash", DEFAULT_ESCAPE_SLASH); bufferInitialLength = opts.getInt("buffer_initial_length", DEFAULT_BUFFER_INITIAL_LENGTH); depth = opts.getInt("depth", 0); @@ -457,6 +486,7 @@ public RubyHash to_h(ThreadContext context) { result.op_aset(context, runtime.newSymbol("allow_nan"), allow_nan_p(context)); result.op_aset(context, runtime.newSymbol("ascii_only"), ascii_only_p(context)); result.op_aset(context, runtime.newSymbol("max_nesting"), max_nesting_get(context)); + result.op_aset(context, runtime.newSymbol("escape_slash"), escape_slash_get(context)); result.op_aset(context, runtime.newSymbol("depth"), depth_get(context)); result.op_aset(context, runtime.newSymbol("buffer_initial_length"), buffer_initial_length_get(context)); for (String name: getInstanceVariableNameList()) { diff --git a/java/src/json/ext/StringEncoder.java b/java/src/json/ext/StringEncoder.java index 9d40dd383..26678ede3 100644 --- a/java/src/json/ext/StringEncoder.java +++ b/java/src/json/ext/StringEncoder.java @@ -15,7 +15,7 @@ * and throws a GeneratorError if any problem is found. */ final class StringEncoder extends ByteListTranscoder { - private final boolean asciiOnly; + private final boolean asciiOnly, escapeSlash; // Escaped characters will reuse this array, to avoid new allocations // or appending them byte-by-byte @@ -37,9 +37,10 @@ final class StringEncoder extends ByteListTranscoder { new byte[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - StringEncoder(ThreadContext context, boolean asciiOnly) { + StringEncoder(ThreadContext context, boolean asciiOnly, boolean escapeSlash) { super(context); this.asciiOnly = asciiOnly; + this.escapeSlash = escapeSlash; } void encode(ByteList src, ByteList out) { @@ -73,6 +74,11 @@ private void handleChar(int c) { case '\b': escapeChar('b'); break; + case '/': + if(escapeSlash) { + escapeChar((char)c); + break; + } default: if (c >= 0x20 && c <= 0x7f || (c >= 0x80 && !asciiOnly)) { diff --git a/lib/json/common.rb b/lib/json/common.rb index 132e9c870..ed4e04762 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -436,12 +436,13 @@ class << self # Sets or returns the default options for the JSON.dump method. # Initially: # opts = JSON.dump_default_options - # opts # => {:max_nesting=>false, :allow_nan=>true} + # opts # => {:max_nesting=>false, :allow_nan=>true, :escape_slash=>false} attr_accessor :dump_default_options end self.dump_default_options = { :max_nesting => false, :allow_nan => true, + :escape_slash => false, } # Dumps _obj_ as a JSON string, i.e. calls generate on the object and returns diff --git a/lib/json/pure/generator.rb b/lib/json/pure/generator.rb index 471af635a..ef1c2e884 100644 --- a/lib/json/pure/generator.rb +++ b/lib/json/pure/generator.rb @@ -37,20 +37,26 @@ module JSON '\\' => '\\\\', } # :nodoc: + ESCAPE_SLASH_MAP = MAP.merge( + '/' => '\\/', + ) + # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with # UTF16 big endian characters as \u????, and return it. - def utf8_to_json(string) # :nodoc: + def utf8_to_json(string, escape_slash = false) # :nodoc: string = string.dup string.force_encoding(::Encoding::ASCII_8BIT) - string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] } + map = escape_slash ? ESCAPE_SLASH_MAP : MAP + string.gsub!(/[\/"\\\x0-\x1f]/) { map[$&] || $& } string.force_encoding(::Encoding::UTF_8) string end - def utf8_to_json_ascii(string) # :nodoc: + def utf8_to_json_ascii(string, escape_slash = false) # :nodoc: string = string.dup string.force_encoding(::Encoding::ASCII_8BIT) - string.gsub!(/["\\\x0-\x1f]/n) { MAP[$&] } + map = escape_slash ? ESCAPE_SLASH_MAP : MAP + string.gsub!(/[\/"\\\x0-\x1f]/n) { map[$&] || $& } string.gsub!(/( (?: [\xc2-\xdf][\x80-\xbf] | @@ -109,6 +115,7 @@ def self.from_state(opts) # * *space_before*: a string that is put before a : pair delimiter (default: ''), # * *object_nl*: a string that is put at the end of a JSON object (default: ''), # * *array_nl*: a string that is put at the end of a JSON array (default: ''), + # * *escape_slash*: true if forward slash (/) should be escaped (default: false) # * *check_circular*: is deprecated now, use the :max_nesting option instead, # * *max_nesting*: sets the maximum level of data structure nesting in # the generated JSON, max_nesting = 0 if no maximum should be checked. @@ -123,6 +130,7 @@ def initialize(opts = {}) @array_nl = '' @allow_nan = false @ascii_only = false + @escape_slash = false @buffer_initial_length = 1024 configure opts end @@ -148,6 +156,10 @@ def initialize(opts = {}) # the generated JSON, max_nesting = 0 if no maximum is checked. attr_accessor :max_nesting + # If this attribute is set to true, forward slashes will be escaped in + # all json strings. + attr_accessor :escape_slash + # :stopdoc: attr_reader :buffer_initial_length @@ -187,6 +199,11 @@ def ascii_only? @ascii_only end + # Returns true, if forward slashes are escaped. Otherwise returns false. + def escape_slash? + @escape_slash + end + # Configure this State instance with the Hash _opts_, and return # itself. def configure(opts) @@ -209,6 +226,7 @@ def configure(opts) @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only) @depth = opts[:depth] || 0 @buffer_initial_length ||= opts[:buffer_initial_length] + @escape_slash = !!opts[:escape_slash] if opts.key?(:escape_slash) if !opts.key?(:max_nesting) # defaults to 100 @max_nesting = 100 @@ -399,9 +417,9 @@ def to_json(state = nil, *args) string = encode(::Encoding::UTF_8) end if state.ascii_only? - '"' << JSON.utf8_to_json_ascii(string) << '"' + '"' << JSON.utf8_to_json_ascii(string, state.escape_slash) << '"' else - '"' << JSON.utf8_to_json(string) << '"' + '"' << JSON.utf8_to_json(string, state.escape_slash) << '"' end end diff --git a/tests/json_generator_test.rb b/tests/json_generator_test.rb old mode 100644 new mode 100755 index ee19fa5e6..13d3b5ab9 --- a/tests/json_generator_test.rb +++ b/tests/json_generator_test.rb @@ -174,6 +174,7 @@ def test_pretty_state :ascii_only => false, :buffer_initial_length => 1024, :depth => 0, + :escape_slash => false, :indent => " ", :max_nesting => 100, :object_nl => "\n", @@ -190,6 +191,7 @@ def test_safe_state :ascii_only => false, :buffer_initial_length => 1024, :depth => 0, + :escape_slash => false, :indent => "", :max_nesting => 100, :object_nl => "", @@ -206,6 +208,7 @@ def test_fast_state :ascii_only => false, :buffer_initial_length => 1024, :depth => 0, + :escape_slash => false, :indent => "", :max_nesting => 0, :object_nl => "", @@ -394,6 +397,10 @@ def test_backslash json = '["/"]' assert_equal json, generate(data) # + data = [ '/' ] + json = '["\/"]' + assert_equal json, generate(data, :escape_slash => true) + # data = ['"'] json = '["\""]' assert_equal json, generate(data) diff --git a/tests/json_parser_test.rb b/tests/json_parser_test.rb index 9946dd93e..514441efc 100644 --- a/tests/json_parser_test.rb +++ b/tests/json_parser_test.rb @@ -293,6 +293,10 @@ def test_backslash json = '["\\\'"]' data = ["'"] assert_equal data, parse(json) + + json = '["\/"]' + data = [ '/' ] + assert_equal data, parse(json) end class SubArray < Array From e4eead665c14f2a355bf0f07a4297e74228f1a0d Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Sat, 4 Jul 2020 09:21:12 -0500 Subject: [PATCH 0080/1010] RDoc example for JSON.load --- lib/json.rb | 2 +- lib/json/common.rb | 100 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 88 insertions(+), 14 deletions(-) diff --git a/lib/json.rb b/lib/json.rb index 6bb82245b..aeb9774ee 100644 --- a/lib/json.rb +++ b/lib/json.rb @@ -319,7 +319,7 @@ # opts = { # array_nl: "\n", # object_nl: "\n", -# indent+: ' ', +# indent: ' ', # space_before: ' ', # space: ' ' # } diff --git a/lib/json/common.rb b/lib/json/common.rb index 5ef3ac248..f5b45ab12 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -414,21 +414,95 @@ class << self :create_additions => true, } - # Load a ruby data structure from a JSON _source_ and return it. A source can - # either be a string-like object, an IO-like object, or an object responding - # to the read method. If _proc_ was given, it will be called with any nested - # Ruby object as an argument recursively in depth first order. To modify the - # default options pass in the optional _options_ argument as well. - # - # BEWARE: This method is meant to serialise data from trusted user input, - # like from your own database server or clients under your control, it could - # be dangerous to allow untrusted users to pass JSON sources into it. The - # default options for the parser can be changed via the load_default_options - # method. + # :call-seq: + # JSON.load(source, proc = nil, options = {}) -> object # - # This method is part of the implementation of the load/dump interface of - # Marshal and YAML. + # Returns the Ruby objects created by parsing the given +source+. # + # - Argument +source+ must be, or be convertible to, a \String: + # - If +source+ responds to instance method +to_str+, + # source.to_str becomes the source. + # - If +source+ responds to instance method +to_io+, + # source.to_io.read becomes the source. + # - If +source+ responds to instance method +read+, + # source.read becomes the source. + # - If both of the following are true, source becomes the \String 'null': + # - Option +allow_blank+ specifies a truthy value. + # - The source, as defined above, is +nil+ or the empty \String ''. + # - Otherwise, +source+ remains the source. + # - Argument +proc+, if given, must be a \Proc that accepts one argument. + # It will be called recursively with each result (depth-first order). + # See details below. + # BEWARE: This method is meant to serialise data from trusted user input, + # like from your own database server or clients under your control, it could + # be dangerous to allow untrusted users to pass JSON sources into it. + # - Argument +opts+, if given, contains options for the parsing, and must be a + # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash+Convertible+Objects]. + # See {Parsing Options}[#module-JSON-label-Parsing+Options]. + # The default options can be changed via method JSON.load_default_options=. + # + # Examples in this section assume prior execution of: + # source = <<-EOT + # { + # "name": "Dave", + # "age" :40, + # "hats": [ + # "Cattleman's", + # "Panama", + # "Tophat" + # ] + # } + # EOT + # + # --- + # + # When +proc+ is not given, modifies +source+ as above and returns the result of + # parse(source, opts); see #parse. + # + # Load a \String: + # ruby = JSON.load(source) + # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]} + # + # Load an \IO object: + # require 'stringio' + # object = JSON.load(StringIO.new(source)) + # object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]} + # + # Load a \File object: + # path = 't.json' + # File.write(path, source) + # File.open(path) do |file| + # JSON.load(file) + # end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]} + # + # --- + # + # When +proc+ is given: + # - Modifies +source+ as above. + # - Gets the +result+ from calling parse(source, opts). + # - Recursively calls proc(result). + # - Returns the final result. + # + # Example: + # def mung(obj) + # case obj + # when String + # obj.upcase + # when Integer + # obj * 100 + # else + # obj + # end + # end + # new_obj = JSON.load(source, proc {|obj| + # case obj + # when Hash + # obj.each {|k, v| obj[k] = mung(v) } + # when Array + # obj.map! {|v| mung(v) } + # end + # }) + # new_obj # => {"name"=>"DAVE", "age"=>4000, "hats"=>["CATTLEMAN'S", "PANAMA", "TOPHAT"]} def load(source, proc = nil, options = {}) opts = load_default_options.merge options if source.respond_to? :to_str From 0662debc5272186a357c16ed5164907cd97d7040 Mon Sep 17 00:00:00 2001 From: Karol Bucek Date: Mon, 6 Jul 2020 13:32:09 +0200 Subject: [PATCH 0081/1010] unify json-java gemspec with the baseline --- json-java.gemspec | 40 +++++++++++++++++++++------------------- json.gemspec | 12 ++++++------ 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/json-java.gemspec b/json-java.gemspec index 1bcd639d1..78cf9c9b6 100755 --- a/json-java.gemspec +++ b/json-java.gemspec @@ -1,33 +1,35 @@ -#!/usr/bin/env jruby -require "rubygems" +# -*- encoding: utf-8 -*- spec = Gem::Specification.new do |s| s.name = "json" s.version = File.read("VERSION").chomp - s.summary = "JSON implementation for JRuby" + + s.summary = "JSON Implementation for Ruby" s.description = "A JSON implementation as a JRuby extension." + s.licenses = ["Ruby"] s.author = "Daniel Luz" s.email = "dev+ruby@mernen.com" - s.homepage = "http://flori.github.com/json" - s.platform = 'java' s.licenses = ["Ruby"] + s.platform = 'java' + s.files = Dir["{docs,lib,tests}/**/*"] - if s.respond_to? :specification_version then - s.specification_version = 4 - - if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 2.0", "< 4.0"]) - else - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 2.0", "< 4.0"]) - end - else - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 2.0", "< 4.0"]) - end + s.homepage = "http://flori.github.com/json" + s.metadata = { + 'bug_tracker_uri' => 'https://github.com/flori/json/issues', + 'changelog_uri' => 'https://github.com/flori/json/blob/master/CHANGES.md', + 'documentation_uri' => 'http://flori.github.io/json/doc/index.html', + 'homepage_uri' => 'http://flori.github.io/json/', + 'source_code_uri' => 'https://github.com/flori/json', + 'wiki_uri' => 'https://github.com/flori/json/wiki' + } + + s.required_ruby_version = Gem::Requirement.new(">= 2.0") + s.test_files = ["tests/test_helper.rb"] + + s.add_development_dependency("rake", [">= 0"]) + s.add_development_dependency("test-unit", [">= 2.0", "< 4.0"]) end if $0 == __FILE__ diff --git a/json.gemspec b/json.gemspec index 0c72e8242..8870f887d 100644 --- a/json.gemspec +++ b/json.gemspec @@ -4,13 +4,15 @@ Gem::Specification.new do |s| s.name = "json" s.version = File.read('VERSION').chomp - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= - s.require_paths = ["lib"] - s.authors = ["Florian Frank"] + s.summary = "JSON Implementation for Ruby" s.description = "This is a JSON implementation as a Ruby extension in C." + s.licenses = ["Ruby"] + s.authors = ["Florian Frank"] s.email = "flori@ping.de" + s.extensions = ["ext/json/ext/generator/extconf.rb", "ext/json/ext/parser/extconf.rb", "ext/json/extconf.rb"] s.extra_rdoc_files = ["README.md"] + s.rdoc_options = ["--title", "JSON implemention for Ruby", "--main", "README.md"] s.files = [ ".gitignore", ".travis.yml", @@ -128,10 +130,8 @@ Gem::Specification.new do |s| 'source_code_uri' => 'https://github.com/flori/json', 'wiki_uri' => 'https://github.com/flori/json/wiki' } - s.licenses = ["Ruby"] - s.rdoc_options = ["--title", "JSON implemention for Ruby", "--main", "README.md"] + s.required_ruby_version = Gem::Requirement.new(">= 2.0") - s.summary = "JSON Implementation for Ruby" s.test_files = ["tests/test_helper.rb"] s.add_development_dependency("rake", [">= 0"]) From 0f587b0e39797a43f362b43b00d71ee08157c383 Mon Sep 17 00:00:00 2001 From: Karol Bucek Date: Mon, 6 Jul 2020 13:42:55 +0200 Subject: [PATCH 0082/1010] [refactor] drop old JRuby 1.7 compat code --- java/src/json/ext/Generator.java | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index 24bf04974..b5dd73bb8 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -10,13 +10,10 @@ import org.jruby.RubyBasicObject; import org.jruby.RubyBignum; import org.jruby.RubyBoolean; -import org.jruby.RubyClass; import org.jruby.RubyFixnum; import org.jruby.RubyFloat; import org.jruby.RubyHash; -import org.jruby.RubyNumeric; import org.jruby.RubyString; -import org.jruby.runtime.ClassIndex; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.util.ByteList; @@ -32,8 +29,7 @@ private Generator() { static RubyString generateJson(ThreadContext context, T object, Handler handler, IRubyObject[] args) { - Session session = new Session(context, args.length > 0 ? args[0] - : null); + Session session = new Session(context, args.length > 0 ? args[0] : null); return session.infect(handler.generateNew(session, object)); } @@ -43,7 +39,7 @@ private Generator() { */ static RubyString generateJson(ThreadContext context, T object, IRubyObject[] args) { - Handler handler = getHandlerFor(context.getRuntime(), object); + Handler handler = getHandlerFor(context.runtime, object); return generateJson(context, object, handler, args); } @@ -55,33 +51,18 @@ private Generator() { generateJson(ThreadContext context, T object, GeneratorState config) { Session session = new Session(context, config); - Handler handler = getHandlerFor(context.getRuntime(), object); + Handler handler = getHandlerFor(context.runtime, object); return handler.generateNew(session, object); } - // NOTE: drop this once Ruby 1.9.3 support is gone! - private static final int FIXNUM = 1; - private static final int BIGNUM = 2; - private static final int ARRAY = 3; - private static final int STRING = 4; - private static final int NIL = 5; - private static final int TRUE = 6; - private static final int FALSE = 7; - private static final int HASH = 10; - private static final int FLOAT = 11; - // hard-coded due JRuby 1.7 compatibility - // https://github.com/jruby/jruby/blob/1.7.27/core/src/main/java/org/jruby/runtime/ClassIndex.java - /** * Returns the best serialization handler for the given object. */ // Java's generics can't handle this satisfactorily, so I'll just leave // the best I could get and ignore the warnings @SuppressWarnings("unchecked") - private static - Handler getHandlerFor(Ruby runtime, T object) { - switch (((RubyBasicObject) object).getNativeTypeIndex()) { - // can not use getNativeClassIndex due 1.7 compatibility + private static Handler getHandlerFor(Ruby runtime, T object) { + switch (((RubyBasicObject) object).getNativeClassIndex()) { case NIL : return (Handler) NIL_HANDLER; case TRUE : return (Handler) TRUE_HANDLER; case FALSE : return (Handler) FALSE_HANDLER; From 1e31f59231d7d31dc58a1e07f228897ddd4b4d7b Mon Sep 17 00:00:00 2001 From: Karol Bucek Date: Mon, 6 Jul 2020 20:09:50 +0200 Subject: [PATCH 0083/1010] [test] properly 'skip' test on JRuby an early return still caused ensure to execute, setting JSON constant to `nil` for later tests! --- .travis.yml | 2 -- tests/json_generator_test.rb | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index a28752333..78b19dd86 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,6 @@ rvm: matrix: allow_failures: - rvm: ruby-head - - rvm: jruby-9.1 - - rvm: jruby-9.2 - rvm: truffleruby script: "bundle exec rake" sudo: false diff --git a/tests/json_generator_test.rb b/tests/json_generator_test.rb index ee19fa5e6..028f82f9b 100644 --- a/tests/json_generator_test.rb +++ b/tests/json_generator_test.rb @@ -49,7 +49,6 @@ def silence end def test_remove_const_segv - return if RUBY_ENGINE == 'jruby' stress = GC.stress const = JSON::SAFE_STATE_PROTOTYPE.dup @@ -76,7 +75,7 @@ def test_remove_const_segv silence do JSON.const_set :SAFE_STATE_PROTOTYPE, const end - end if JSON.const_defined?("Ext") + end if JSON.const_defined?("Ext") && RUBY_ENGINE != 'jruby' def test_generate json = generate(@hash) From a55c91934e5146cfafda42d59863fcbe5ede85d7 Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Tue, 28 Jul 2020 13:57:16 -0500 Subject: [PATCH 0084/1010] RDoc for JSON.load with proc --- lib/json/common.rb | 76 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 18 deletions(-) diff --git a/lib/json/common.rb b/lib/json/common.rb index f5b45ab12..6ed00b3cc 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -441,7 +441,12 @@ class << self # See {Parsing Options}[#module-JSON-label-Parsing+Options]. # The default options can be changed via method JSON.load_default_options=. # - # Examples in this section assume prior execution of: + # --- + # + # When no +proc+ is given, modifies +source+ as above and returns the result of + # parse(source, opts); see #parse. + # + # Source for following examples: # source = <<-EOT # { # "name": "Dave", @@ -454,11 +459,6 @@ class << self # } # EOT # - # --- - # - # When +proc+ is not given, modifies +source+ as above and returns the result of - # parse(source, opts); see #parse. - # # Load a \String: # ruby = JSON.load(source) # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]} @@ -484,25 +484,65 @@ class << self # - Returns the final result. # # Example: - # def mung(obj) - # case obj - # when String - # obj.upcase - # when Integer - # obj * 100 - # else - # obj + # require 'json' + # + # # Some classes for the example. + # class Base + # def initialize(attributes) + # @attributes = attributes # end # end - # new_obj = JSON.load(source, proc {|obj| + # class User < Base; end + # class Account < Base; end + # class Admin < Base; end + # # The JSON source. + # json = <<-EOF + # { + # "users": [ + # {"type": "User", "username": "jane", "email": "jane@example.com"}, + # {"type": "User", "username": "john", "email": "john@example.com"} + # ], + # "accounts": [ + # {"account": {"type": "Account", "paid": true, "account_id": "1234"}}, + # {"account": {"type": "Account", "paid": false, "account_id": "1235"}} + # ], + # "admins": {"type": "Admin", "password": "0wn3d"} + # } + # EOF + # # Deserializer method. + # def deserialize_obj(obj, safe_types = %w(User Account Admin)) + # type = obj.is_a?(Hash) && obj["type"] + # safe_types.include?(type) ? Object.const_get(type).new(obj) : obj + # end + # # Call to JSON.load + # ruby = JSON.load(json, proc {|obj| # case obj # when Hash - # obj.each {|k, v| obj[k] = mung(v) } + # obj.each {|k, v| obj[k] = deserialize_obj v } # when Array - # obj.map! {|v| mung(v) } + # obj.map! {|v| deserialize_obj v } # end # }) - # new_obj # => {"name"=>"DAVE", "age"=>4000, "hats"=>["CATTLEMAN'S", "PANAMA", "TOPHAT"]} + # pp ruby + # Output: + # {"users"=> + # [#"User", "username"=>"jane", "email"=>"jane@example.com"}>, + # #"User", "username"=>"john", "email"=>"john@example.com"}>], + # "accounts"=> + # [{"account"=> + # #"Account", "paid"=>true, "account_id"=>"1234"}>}, + # {"account"=> + # #"Account", "paid"=>false, "account_id"=>"1235"}>}], + # "admins"=> + # #"Admin", "password"=>"0wn3d"}>} + # def load(source, proc = nil, options = {}) opts = load_default_options.merge options if source.respond_to? :to_str From f8c0fe24083dd6cc8e4b1fc61b06681b35024193 Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Wed, 29 Jul 2020 14:46:13 -0500 Subject: [PATCH 0085/1010] Nodoc for recurse_proc --- lib/json/common.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/json/common.rb b/lib/json/common.rb index 6ed00b3cc..fc6b7a839 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -561,7 +561,7 @@ def load(source, proc = nil, options = {}) end # Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_ - def recurse_proc(result, &proc) + def recurse_proc(result, &proc) # :nodoc: case result when Array result.each { |x| recurse_proc x, &proc } From e953f8a460517b5d9b63d5ecf2eb805d3e6ec970 Mon Sep 17 00:00:00 2001 From: Julien Feltesse Date: Fri, 14 Aug 2020 12:45:09 +0900 Subject: [PATCH 0086/1010] bundle the LICENSE file in the gem --- json-java.gemspec | 3 +-- json.gemspec | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/json-java.gemspec b/json-java.gemspec index 78cf9c9b6..a34103bff 100755 --- a/json-java.gemspec +++ b/json-java.gemspec @@ -9,11 +9,10 @@ spec = Gem::Specification.new do |s| s.licenses = ["Ruby"] s.author = "Daniel Luz" s.email = "dev+ruby@mernen.com" - s.licenses = ["Ruby"] s.platform = 'java' - s.files = Dir["{docs,lib,tests}/**/*"] + s.files = Dir["{docs,lib,tests}/**/*", "LICENSE"] s.homepage = "http://flori.github.com/json" s.metadata = { diff --git a/json.gemspec b/json.gemspec index 8870f887d..04d543fd2 100644 --- a/json.gemspec +++ b/json.gemspec @@ -18,6 +18,7 @@ Gem::Specification.new do |s| ".travis.yml", "CHANGES.md", "Gemfile", + "LICENSE", "README-json-jruby.md", "README.md", "Rakefile", From 03f1699ec4127bf4d8282dbe90c683a5716413a3 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Fri, 14 Aug 2020 00:03:12 -0500 Subject: [PATCH 0087/1010] Enhanced RDoc for JSON.dump (#443) * Enhanced RDoc for JSON.dump --- lib/json/common.rb | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/lib/json/common.rb b/lib/json/common.rb index f6230a7c2..2f101e7ed 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -590,21 +590,33 @@ class << self :escape_slash => false, } - # Dumps _obj_ as a JSON string, i.e. calls generate on the object and returns - # the result. + # :call-seq: + # JSON.dump(obj, io = nil, limit = nil) + # + # Dumps +obj+ as a \JSON string, i.e. calls generate on the object and returns the result. + # + # The default options can be changed via method JSON.dump_default_options. # - # If anIO (an IO-like object or an object that responds to the write method) - # was given, the resulting JSON is written to it. + # - Argument +io+, if given, should respond to method +write+; + # the \JSON \String is written to +io+, and +io+ is returned. + # If +io+ is not given, the \JSON \String is returned. + # - Argument +limit+, if given, is passed to JSON.generate as option +max_nesting+. # - # If the number of nested arrays or objects exceeds _limit_, an ArgumentError - # exception is raised. This argument is similar (but not exactly the - # same!) to the _limit_ argument in Marshal.dump. + # --- # - # The default options for the generator can be changed via the - # dump_default_options method. + # When argument +io+ is not given, returns the \JSON \String generated from +obj+: + # obj = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad} + # json = JSON.dump(obj) + # json # => "{\"foo\":[0,1],\"bar\":{\"baz\":2,\"bat\":3},\"bam\":\"bad\"}" # - # This method is part of the implementation of the load/dump interface of - # Marshal and YAML. + # When argument +io+ is given, writes the \JSON \String to +io+ and returns +io+: + # path = 't.json' + # File.open(path, 'w') do |file| + # JSON.dump(obj, file) + # end # => # + # puts File.read(path) + # Output: + # {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"} def dump(obj, anIO = nil, limit = nil) if anIO and limit.nil? anIO = anIO.to_io if anIO.respond_to?(:to_io) From 6dfa885134ddbc1bed4fc2c67ee94a16a98b8e0d Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Thu, 20 Aug 2020 11:37:42 -0500 Subject: [PATCH 0088/1010] Partial compliance with doc/method_documentation.rdoc --- lib/json/common.rb | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/lib/json/common.rb b/lib/json/common.rb index 2f101e7ed..2e4aa2a43 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -7,8 +7,7 @@ class << self # :call-seq: # JSON[object] -> new_array or new_string # - # If +object+ is a - # {String-convertible object}[doc/implicit_conversion_rdoc.html#label-String-Convertible+Objects], + # If +object+ is a \String, # calls JSON.parse with +object+ and +opts+ (see method #parse): # json = '[0, 1, null]' # JSON[json]# => [0, 1, nil] @@ -149,12 +148,9 @@ class MissingUnicodeSupport < JSONError; end # # Returns the Ruby objects created by parsing the given +source+. # - # Argument +source+ contains the \String to be parsed. It must be a - # {String-convertible object}[doc/implicit_conversion_rdoc.html#label-String-Convertible+Objects] - # (implementing +to_str+), and must contain valid \JSON data. + # Argument +source+ contains the \String to be parsed. # - # Argument +opts+, if given, contains options for the parsing, and must be a - # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash+Convertible+Objects]. + # Argument +opts+, if given, contains a \Hash of options for the parsing. # See {Parsing Options}[#module-JSON-label-Parsing+Options]. # # --- @@ -249,22 +245,17 @@ def load_file!(filespec, opts = {}) # # Argument +obj+ is the Ruby object to be converted to \JSON. # - # Argument +opts+, if given, contains options for the generation, and must be a - # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects]. + # Argument +opts+, if given, contains a \Hash of options for the generation. # See {Generating Options}[#module-JSON-label-Generating+Options]. # # --- # - # When +obj+ is an - # {Array-convertible object}[doc/implicit_conversion_rdoc.html#label-Array-Convertible+Objects] - # (implementing +to_ary+), returns a \String containing a \JSON array: + # When +obj+ is an \Array, returns a \String containing a \JSON array: # obj = ["foo", 1.0, true, false, nil] # json = JSON.generate(obj) # json # => '["foo",1.0,true,false,null]' # - # When +obj+ is a - # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects], - # return a \String containing a \JSON object: + # When +obj+ is a \Hash, returns a \String containing a \JSON object: # obj = {foo: 0, bar: 's', baz: :bat} # json = JSON.generate(obj) # json # => '{"foo":0,"bar":"s","baz":"bat"}' @@ -436,8 +427,7 @@ class << self # BEWARE: This method is meant to serialise data from trusted user input, # like from your own database server or clients under your control, it could # be dangerous to allow untrusted users to pass JSON sources into it. - # - Argument +opts+, if given, contains options for the parsing, and must be a - # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash+Convertible+Objects]. + # - Argument +opts+, if given, contains a \Hash of options for the parsing. # See {Parsing Options}[#module-JSON-label-Parsing+Options]. # The default options can be changed via method JSON.load_default_options=. # From cb61a00ba8da3df36524979822a5d145f104ea35 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 9 Sep 2020 16:48:07 +0200 Subject: [PATCH 0089/1010] Fix JSON.load_file doc --- lib/json/common.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/json/common.rb b/lib/json/common.rb index 2e4aa2a43..111d70c33 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -215,7 +215,7 @@ def parse!(source, opts = {}) end # :call-seq: - # CSV.load_file(path, opts={}) -> object + # JSON.load_file(path, opts={}) -> object # # Calls: # parse(File.read(path), opts) @@ -226,10 +226,10 @@ def load_file(filespec, opts = {}) end # :call-seq: - # CSV.load_file!(path, opts = {}) + # JSON.load_file!(path, opts = {}) # # Calls: - # CSV.parse!(File.read(path, opts)) + # JSON.parse!(File.read(path, opts)) # # See method #parse! def load_file!(filespec, opts = {}) From 9bf8aa21b36de3bd90bed85e6c85412426b03d9d Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 9 Sep 2020 15:24:22 +0200 Subject: [PATCH 0090/1010] Implement a freeze: parser option If set to true all parsed objects will be immediately frozen, and strings will be deduplicated if the Ruby implementation allows it. --- ext/json/ext/parser/extconf.rb | 25 ++++++ ext/json/ext/parser/parser.c | 115 +++++++++++++++--------- ext/json/ext/parser/parser.h | 1 + ext/json/ext/parser/parser.rl | 29 +++++- ext/json/extconf.rb | 1 + java/src/json/ext/Parser.java | 158 ++++++++++++++++++--------------- java/src/json/ext/Parser.rl | 16 +++- lib/json/pure/parser.rb | 14 +++ tests/json_parser_test.rb | 21 +++++ 9 files changed, 260 insertions(+), 120 deletions(-) diff --git a/ext/json/ext/parser/extconf.rb b/ext/json/ext/parser/extconf.rb index f7360d46b..f832b56a6 100644 --- a/ext/json/ext/parser/extconf.rb +++ b/ext/json/ext/parser/extconf.rb @@ -3,4 +3,29 @@ have_func("rb_enc_raise", "ruby.h") +# checking if String#-@ (str_uminus) dedupes... ' +begin + a = -(%w(t e s t).join) + b = -(%w(t e s t).join) + if a.equal?(b) + $CFLAGS << ' -DSTR_UMINUS_DEDUPE=1 ' + else + $CFLAGS << ' -DSTR_UMINUS_DEDUPE=0 ' + end +rescue NoMethodError + $CFLAGS << ' -DSTR_UMINUS_DEDUPE=0 ' +end + +# checking if String#-@ (str_uminus) directly interns frozen strings... ' +begin + s = rand.to_s.freeze + if (-s).equal?(s) && (-s.dup).equal?(s) + $CFLAGS << ' -DSTR_UMINUS_DEDUPE_FROZEN=1 ' + else + $CFLAGS << ' -DSTR_UMINUS_DEDUPE_FROZEN=0 ' + end +rescue NoMethodError + $CFLAGS << ' -DSTR_UMINUS_DEDUPE_FROZEN=0 ' +end + create_makefile 'json/ext/parser' diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 6f0e4ace8..aaef53aac 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -97,7 +97,7 @@ static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_object_class, i_array_class, i_decimal_class, i_key_p, i_deep_const_get, i_match, i_match_string, i_aset, i_aref, - i_leftshift, i_new, i_BigDecimal; + i_leftshift, i_new, i_BigDecimal, i_freeze, i_uminus; #line 126 "parser.rl" @@ -869,6 +869,10 @@ case 28: #line 292 "parser.rl" + if (json->freeze) { + OBJ_FREEZE(*result); + } + if (cs >= JSON_value_first_final) { return p; } else { @@ -877,7 +881,7 @@ case 28: } -#line 881 "parser.c" +#line 885 "parser.c" enum {JSON_integer_start = 1}; enum {JSON_integer_first_final = 3}; enum {JSON_integer_error = 0}; @@ -885,7 +889,7 @@ enum {JSON_integer_error = 0}; enum {JSON_integer_en_main = 1}; -#line 308 "parser.rl" +#line 312 "parser.rl" static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result) @@ -893,15 +897,15 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res int cs = EVIL; -#line 897 "parser.c" +#line 901 "parser.c" { cs = JSON_integer_start; } -#line 315 "parser.rl" +#line 319 "parser.rl" json->memo = p; -#line 905 "parser.c" +#line 909 "parser.c" { if ( p == pe ) goto _test_eof; @@ -935,14 +939,14 @@ case 3: goto st0; goto tr4; tr4: -#line 305 "parser.rl" +#line 309 "parser.rl" { p--; {p++; cs = 4; goto _out;} } goto st4; st4: if ( ++p == pe ) goto _test_eof4; case 4: -#line 946 "parser.c" +#line 950 "parser.c" goto st0; st5: if ( ++p == pe ) @@ -961,7 +965,7 @@ case 5: _out: {} } -#line 317 "parser.rl" +#line 321 "parser.rl" if (cs >= JSON_integer_first_final) { long len = p - json->memo; @@ -976,7 +980,7 @@ case 5: } -#line 980 "parser.c" +#line 984 "parser.c" enum {JSON_float_start = 1}; enum {JSON_float_first_final = 8}; enum {JSON_float_error = 0}; @@ -984,7 +988,7 @@ enum {JSON_float_error = 0}; enum {JSON_float_en_main = 1}; -#line 342 "parser.rl" +#line 346 "parser.rl" static int is_bigdecimal_class(VALUE obj) @@ -1005,15 +1009,15 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul int cs = EVIL; -#line 1009 "parser.c" +#line 1013 "parser.c" { cs = JSON_float_start; } -#line 362 "parser.rl" +#line 366 "parser.rl" json->memo = p; -#line 1017 "parser.c" +#line 1021 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1071,14 +1075,14 @@ case 8: goto st0; goto tr9; tr9: -#line 336 "parser.rl" +#line 340 "parser.rl" { p--; {p++; cs = 9; goto _out;} } goto st9; st9: if ( ++p == pe ) goto _test_eof9; case 9: -#line 1082 "parser.c" +#line 1086 "parser.c" goto st0; st5: if ( ++p == pe ) @@ -1139,7 +1143,7 @@ case 7: _out: {} } -#line 364 "parser.rl" +#line 368 "parser.rl" if (cs >= JSON_float_first_final) { long len = p - json->memo; @@ -1165,7 +1169,7 @@ case 7: -#line 1169 "parser.c" +#line 1173 "parser.c" enum {JSON_array_start = 1}; enum {JSON_array_first_final = 17}; enum {JSON_array_error = 0}; @@ -1173,7 +1177,7 @@ enum {JSON_array_error = 0}; enum {JSON_array_en_main = 1}; -#line 417 "parser.rl" +#line 421 "parser.rl" static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) @@ -1187,14 +1191,14 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul *result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class); -#line 1191 "parser.c" +#line 1195 "parser.c" { cs = JSON_array_start; } -#line 430 "parser.rl" +#line 434 "parser.rl" -#line 1198 "parser.c" +#line 1202 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1233,7 +1237,7 @@ case 2: goto st2; goto st0; tr2: -#line 394 "parser.rl" +#line 398 "parser.rl" { VALUE v = Qnil; char *np = JSON_parse_value(json, p, pe, &v, current_nesting); @@ -1253,7 +1257,7 @@ case 2: if ( ++p == pe ) goto _test_eof3; case 3: -#line 1257 "parser.c" +#line 1261 "parser.c" switch( (*p) ) { case 13: goto st3; case 32: goto st3; @@ -1353,14 +1357,14 @@ case 12: goto st3; goto st12; tr4: -#line 409 "parser.rl" +#line 413 "parser.rl" { p--; {p++; cs = 17; goto _out;} } goto st17; st17: if ( ++p == pe ) goto _test_eof17; case 17: -#line 1364 "parser.c" +#line 1368 "parser.c" goto st0; st13: if ( ++p == pe ) @@ -1416,7 +1420,7 @@ case 16: _out: {} } -#line 431 "parser.rl" +#line 435 "parser.rl" if(cs >= JSON_array_first_final) { return p + 1; @@ -1505,7 +1509,7 @@ static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd) } -#line 1509 "parser.c" +#line 1513 "parser.c" enum {JSON_string_start = 1}; enum {JSON_string_first_final = 8}; enum {JSON_string_error = 0}; @@ -1513,7 +1517,7 @@ enum {JSON_string_error = 0}; enum {JSON_string_en_main = 1}; -#line 538 "parser.rl" +#line 542 "parser.rl" static int @@ -1535,15 +1539,15 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu *result = rb_str_buf_new(0); -#line 1539 "parser.c" +#line 1543 "parser.c" { cs = JSON_string_start; } -#line 559 "parser.rl" +#line 563 "parser.rl" json->memo = p; -#line 1547 "parser.c" +#line 1551 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1568,7 +1572,7 @@ case 2: goto st0; goto st2; tr2: -#line 524 "parser.rl" +#line 528 "parser.rl" { *result = json_string_unescape(*result, json->memo + 1, p); if (NIL_P(*result)) { @@ -1579,14 +1583,14 @@ case 2: {p = (( p + 1))-1;} } } -#line 535 "parser.rl" +#line 539 "parser.rl" { p--; {p++; cs = 8; goto _out;} } goto st8; st8: if ( ++p == pe ) goto _test_eof8; case 8: -#line 1590 "parser.c" +#line 1594 "parser.c" goto st0; st3: if ( ++p == pe ) @@ -1662,7 +1666,7 @@ case 7: _out: {} } -#line 561 "parser.rl" +#line 565 "parser.rl" if (json->create_additions && RTEST(match_string = json->match_string)) { VALUE klass; @@ -1678,7 +1682,22 @@ case 7: if (json->symbolize_names && json->parsing_name) { *result = rb_str_intern(*result); } else if (RB_TYPE_P(*result, T_STRING)) { +# if STR_UMINUS_DEDUPE_FROZEN + if (json->freeze) { + // Starting from MRI 2.8 it is preferable to freeze the string + // before deduplication so that it can be interned directly + // otherwise it would be duplicated first which is wasteful. + *result = rb_funcall(rb_str_freeze(*result), i_uminus, 0); + } +# elif STR_UMINUS_DEDUPE + if (json->freeze) { + // MRI 2.5 and older do not deduplicate strings that are already + // frozen. + *result = rb_funcall(*result, i_uminus, 0); + } +# else rb_str_resize(*result, RSTRING_LEN(*result)); +# endif } if (cs >= JSON_string_first_final) { return p + 1; @@ -1786,6 +1805,12 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } else { json->symbolize_names = 0; } + tmp = ID2SYM(i_freeze); + if (option_given_p(opts, tmp)) { + json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; + } else { + json->freeze = 0; + } tmp = ID2SYM(i_create_additions); if (option_given_p(opts, tmp)) { json->create_additions = RTEST(rb_hash_aref(opts, tmp)); @@ -1849,7 +1874,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } -#line 1853 "parser.c" +#line 1878 "parser.c" enum {JSON_start = 1}; enum {JSON_first_final = 10}; enum {JSON_error = 0}; @@ -1857,7 +1882,7 @@ enum {JSON_error = 0}; enum {JSON_en_main = 1}; -#line 761 "parser.rl" +#line 786 "parser.rl" /* @@ -1874,16 +1899,16 @@ static VALUE cParser_parse(VALUE self) GET_PARSER; -#line 1878 "parser.c" +#line 1903 "parser.c" { cs = JSON_start; } -#line 777 "parser.rl" +#line 802 "parser.rl" p = json->source; pe = p + json->len; -#line 1887 "parser.c" +#line 1912 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1917,7 +1942,7 @@ case 1: cs = 0; goto _out; tr2: -#line 753 "parser.rl" +#line 778 "parser.rl" { char *np = JSON_parse_value(json, p, pe, &result, 0); if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;} @@ -1927,7 +1952,7 @@ cs = 0; if ( ++p == pe ) goto _test_eof10; case 10: -#line 1931 "parser.c" +#line 1956 "parser.c" switch( (*p) ) { case 13: goto st10; case 32: goto st10; @@ -2016,7 +2041,7 @@ case 9: _out: {} } -#line 780 "parser.rl" +#line 805 "parser.rl" if (cs >= JSON_first_final && p == pe) { return result; @@ -2126,6 +2151,8 @@ void Init_parser(void) i_leftshift = rb_intern("<<"); i_new = rb_intern("new"); i_BigDecimal = rb_intern("BigDecimal"); + i_freeze = rb_intern("freeze"); + i_uminus = rb_intern("-@"); } /* diff --git a/ext/json/ext/parser/parser.h b/ext/json/ext/parser/parser.h index e6cf77902..e3eb920cc 100644 --- a/ext/json/ext/parser/parser.h +++ b/ext/json/ext/parser/parser.h @@ -37,6 +37,7 @@ typedef struct JSON_ParserStruct { int allow_nan; int parsing_name; int symbolize_names; + int freeze; VALUE object_class; VALUE array_class; VALUE decimal_class; diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl index 00a35bded..462905209 100644 --- a/ext/json/ext/parser/parser.rl +++ b/ext/json/ext/parser/parser.rl @@ -95,7 +95,7 @@ static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_object_class, i_array_class, i_decimal_class, i_key_p, i_deep_const_get, i_match, i_match_string, i_aset, i_aref, - i_leftshift, i_new, i_BigDecimal; + i_leftshift, i_new, i_BigDecimal, i_freeze, i_uminus; %%{ machine JSON_common; @@ -290,6 +290,10 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul %% write init; %% write exec; + if (json->freeze) { + OBJ_FREEZE(*result); + } + if (cs >= JSON_value_first_final) { return p; } else { @@ -573,7 +577,22 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu if (json->symbolize_names && json->parsing_name) { *result = rb_str_intern(*result); } else if (RB_TYPE_P(*result, T_STRING)) { +# if STR_UMINUS_DEDUPE_FROZEN + if (json->freeze) { + // Starting from MRI 2.8 it is preferable to freeze the string + // before deduplication so that it can be interned directly + // otherwise it would be duplicated first which is wasteful. + *result = rb_funcall(rb_str_freeze(*result), i_uminus, 0); + } +# elif STR_UMINUS_DEDUPE + if (json->freeze) { + // MRI 2.5 and older do not deduplicate strings that are already + // frozen. + *result = rb_funcall(*result, i_uminus, 0); + } +# else rb_str_resize(*result, RSTRING_LEN(*result)); +# endif } if (cs >= JSON_string_first_final) { return p + 1; @@ -681,6 +700,12 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } else { json->symbolize_names = 0; } + tmp = ID2SYM(i_freeze); + if (option_given_p(opts, tmp)) { + json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; + } else { + json->freeze = 0; + } tmp = ID2SYM(i_create_additions); if (option_given_p(opts, tmp)) { json->create_additions = RTEST(rb_hash_aref(opts, tmp)); @@ -886,6 +911,8 @@ void Init_parser(void) i_leftshift = rb_intern("<<"); i_new = rb_intern("new"); i_BigDecimal = rb_intern("BigDecimal"); + i_freeze = rb_intern("freeze"); + i_uminus = rb_intern("-@"); } /* diff --git a/ext/json/extconf.rb b/ext/json/extconf.rb index 7595d58a9..8a99b6a5c 100644 --- a/ext/json/extconf.rb +++ b/ext/json/extconf.rb @@ -1,2 +1,3 @@ require 'mkmf' + create_makefile('json') diff --git a/java/src/json/ext/Parser.java b/java/src/json/ext/Parser.java index 647afca89..ef2839322 100644 --- a/java/src/json/ext/Parser.java +++ b/java/src/json/ext/Parser.java @@ -52,6 +52,7 @@ public class Parser extends RubyObject { private int maxNesting; private boolean allowNaN; private boolean symbolizeNames; + private boolean freeze; private RubyClass objectClass; private RubyClass arrayClass; private RubyClass decimalClass; @@ -160,6 +161,7 @@ public IRubyObject initialize(ThreadContext context, IRubyObject[] args) { this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING); this.allowNaN = opts.getBool("allow_nan", false); this.symbolizeNames = opts.getBool("symbolize_names", false); + this.freeze = opts.getBool("freeze", false); this.createId = opts.getString("create_id", getCreateId(context)); this.createAdditions = opts.getBool("create_additions", false); this.objectClass = opts.getClass("object_class", runtime.getHash()); @@ -313,11 +315,11 @@ private Ruby getRuntime() { } -// line 339 "Parser.rl" +// line 341 "Parser.rl" -// line 321 "Parser.java" +// line 323 "Parser.java" private static byte[] init__JSON_value_actions_0() { return new byte [] { @@ -431,7 +433,7 @@ private static byte[] init__JSON_value_from_state_actions_0() static final int JSON_value_en_main = 1; -// line 445 "Parser.rl" +// line 447 "Parser.rl" void parseValue(ParserResult res, int p, int pe) { @@ -439,14 +441,14 @@ void parseValue(ParserResult res, int p, int pe) { IRubyObject result = null; -// line 443 "Parser.java" +// line 445 "Parser.java" { cs = JSON_value_start; } -// line 452 "Parser.rl" +// line 454 "Parser.rl" -// line 450 "Parser.java" +// line 452 "Parser.java" { int _klen; int _trans = 0; @@ -472,13 +474,13 @@ void parseValue(ParserResult res, int p, int pe) { while ( _nacts-- > 0 ) { switch ( _JSON_value_actions[_acts++] ) { case 9: -// line 430 "Parser.rl" +// line 432 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 482 "Parser.java" +// line 484 "Parser.java" } } @@ -541,25 +543,25 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) switch ( _JSON_value_actions[_acts++] ) { case 0: -// line 347 "Parser.rl" +// line 349 "Parser.rl" { result = getRuntime().getNil(); } break; case 1: -// line 350 "Parser.rl" +// line 352 "Parser.rl" { result = getRuntime().getFalse(); } break; case 2: -// line 353 "Parser.rl" +// line 355 "Parser.rl" { result = getRuntime().getTrue(); } break; case 3: -// line 356 "Parser.rl" +// line 358 "Parser.rl" { if (parser.allowNaN) { result = getConstant(CONST_NAN); @@ -569,7 +571,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } break; case 4: -// line 363 "Parser.rl" +// line 365 "Parser.rl" { if (parser.allowNaN) { result = getConstant(CONST_INFINITY); @@ -579,7 +581,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } break; case 5: -// line 370 "Parser.rl" +// line 372 "Parser.rl" { if (pe > p + 8 && absSubSequence(p, p + 9).equals(JSON_MINUS_INFINITY)) { @@ -608,7 +610,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } break; case 6: -// line 396 "Parser.rl" +// line 398 "Parser.rl" { parseString(res, p, pe); if (res.result == null) { @@ -621,7 +623,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } break; case 7: -// line 406 "Parser.rl" +// line 408 "Parser.rl" { currentNesting++; parseArray(res, p, pe); @@ -636,7 +638,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } break; case 8: -// line 418 "Parser.rl" +// line 420 "Parser.rl" { currentNesting++; parseObject(res, p, pe); @@ -650,7 +652,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } } break; -// line 654 "Parser.java" +// line 656 "Parser.java" } } } @@ -670,9 +672,12 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) break; } } -// line 453 "Parser.rl" +// line 455 "Parser.rl" if (cs >= JSON_value_first_final && result != null) { + if (parser.freeze) { + result.setFrozen(true); + } res.update(result, p); } else { res.update(null, p); @@ -680,7 +685,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } -// line 684 "Parser.java" +// line 689 "Parser.java" private static byte[] init__JSON_integer_actions_0() { return new byte [] { @@ -779,7 +784,7 @@ private static byte[] init__JSON_integer_trans_actions_0() static final int JSON_integer_en_main = 1; -// line 472 "Parser.rl" +// line 477 "Parser.rl" void parseInteger(ParserResult res, int p, int pe) { @@ -797,15 +802,15 @@ int parseIntegerInternal(int p, int pe) { int cs = EVIL; -// line 801 "Parser.java" +// line 806 "Parser.java" { cs = JSON_integer_start; } -// line 489 "Parser.rl" +// line 494 "Parser.rl" int memo = p; -// line 809 "Parser.java" +// line 814 "Parser.java" { int _klen; int _trans = 0; @@ -886,13 +891,13 @@ else if ( data[p] > _JSON_integer_trans_keys[_mid+1] ) switch ( _JSON_integer_actions[_acts++] ) { case 0: -// line 466 "Parser.rl" +// line 471 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 896 "Parser.java" +// line 901 "Parser.java" } } } @@ -912,7 +917,7 @@ else if ( data[p] > _JSON_integer_trans_keys[_mid+1] ) break; } } -// line 491 "Parser.rl" +// line 496 "Parser.rl" if (cs < JSON_integer_first_final) { return -1; @@ -934,7 +939,7 @@ RubyInteger bytesToInum(Ruby runtime, ByteList num) { } -// line 938 "Parser.java" +// line 943 "Parser.java" private static byte[] init__JSON_float_actions_0() { return new byte [] { @@ -1036,7 +1041,7 @@ private static byte[] init__JSON_float_trans_actions_0() static final int JSON_float_en_main = 1; -// line 526 "Parser.rl" +// line 531 "Parser.rl" void parseFloat(ParserResult res, int p, int pe) { @@ -1056,15 +1061,15 @@ int parseFloatInternal(int p, int pe) { int cs = EVIL; -// line 1060 "Parser.java" +// line 1065 "Parser.java" { cs = JSON_float_start; } -// line 545 "Parser.rl" +// line 550 "Parser.rl" int memo = p; -// line 1068 "Parser.java" +// line 1073 "Parser.java" { int _klen; int _trans = 0; @@ -1145,13 +1150,13 @@ else if ( data[p] > _JSON_float_trans_keys[_mid+1] ) switch ( _JSON_float_actions[_acts++] ) { case 0: -// line 517 "Parser.rl" +// line 522 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 1155 "Parser.java" +// line 1160 "Parser.java" } } } @@ -1171,7 +1176,7 @@ else if ( data[p] > _JSON_float_trans_keys[_mid+1] ) break; } } -// line 547 "Parser.rl" +// line 552 "Parser.rl" if (cs < JSON_float_first_final) { return -1; @@ -1194,7 +1199,7 @@ IRubyObject createCustomDecimal(int p, int new_p) { } -// line 1198 "Parser.java" +// line 1203 "Parser.java" private static byte[] init__JSON_string_actions_0() { return new byte [] { @@ -1296,7 +1301,7 @@ private static byte[] init__JSON_string_trans_actions_0() static final int JSON_string_en_main = 1; -// line 599 "Parser.rl" +// line 604 "Parser.rl" void parseString(ParserResult res, int p, int pe) { @@ -1304,15 +1309,15 @@ void parseString(ParserResult res, int p, int pe) { IRubyObject result = null; -// line 1308 "Parser.java" +// line 1313 "Parser.java" { cs = JSON_string_start; } -// line 606 "Parser.rl" +// line 611 "Parser.rl" int memo = p; -// line 1316 "Parser.java" +// line 1321 "Parser.java" { int _klen; int _trans = 0; @@ -1393,7 +1398,7 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] ) switch ( _JSON_string_actions[_acts++] ) { case 0: -// line 574 "Parser.rl" +// line 579 "Parser.rl" { int offset = byteList.begin(); ByteList decoded = decoder.decode(byteList, memo + 1 - offset, @@ -1408,13 +1413,13 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] ) } break; case 1: -// line 587 "Parser.rl" +// line 592 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 1418 "Parser.java" +// line 1423 "Parser.java" } } } @@ -1434,7 +1439,7 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] ) break; } } -// line 608 "Parser.rl" +// line 613 "Parser.rl" if (parser.createAdditions) { RubyHash matchString = parser.match_string; @@ -1463,16 +1468,23 @@ public void visit(IRubyObject pattern, IRubyObject klass) { if (cs >= JSON_string_first_final && result != null) { if (result instanceof RubyString) { - ((RubyString)result).force_encoding(context, info.utf8.get()); + RubyString string = (RubyString)result; + string.force_encoding(context, info.utf8.get()); + if (parser.freeze) { + string.setFrozen(true); + string = getRuntime().freezeAndDedupString(string); + } + res.update(string, p + 1); + } else { + res.update(result, p + 1); } - res.update(result, p + 1); } else { res.update(null, p + 1); } } -// line 1476 "Parser.java" +// line 1488 "Parser.java" private static byte[] init__JSON_array_actions_0() { return new byte [] { @@ -1585,7 +1597,7 @@ private static byte[] init__JSON_array_trans_actions_0() static final int JSON_array_en_main = 1; -// line 681 "Parser.rl" +// line 693 "Parser.rl" void parseArray(ParserResult res, int p, int pe) { @@ -1605,14 +1617,14 @@ void parseArray(ParserResult res, int p, int pe) { } -// line 1609 "Parser.java" +// line 1621 "Parser.java" { cs = JSON_array_start; } -// line 700 "Parser.rl" +// line 712 "Parser.rl" -// line 1616 "Parser.java" +// line 1628 "Parser.java" { int _klen; int _trans = 0; @@ -1693,7 +1705,7 @@ else if ( data[p] > _JSON_array_trans_keys[_mid+1] ) switch ( _JSON_array_actions[_acts++] ) { case 0: -// line 650 "Parser.rl" +// line 662 "Parser.rl" { parseValue(res, p, pe); if (res.result == null) { @@ -1710,13 +1722,13 @@ else if ( data[p] > _JSON_array_trans_keys[_mid+1] ) } break; case 1: -// line 665 "Parser.rl" +// line 677 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 1720 "Parser.java" +// line 1732 "Parser.java" } } } @@ -1736,7 +1748,7 @@ else if ( data[p] > _JSON_array_trans_keys[_mid+1] ) break; } } -// line 701 "Parser.rl" +// line 713 "Parser.rl" if (cs >= JSON_array_first_final) { res.update(result, p + 1); @@ -1746,7 +1758,7 @@ else if ( data[p] > _JSON_array_trans_keys[_mid+1] ) } -// line 1750 "Parser.java" +// line 1762 "Parser.java" private static byte[] init__JSON_object_actions_0() { return new byte [] { @@ -1869,7 +1881,7 @@ private static byte[] init__JSON_object_trans_actions_0() static final int JSON_object_en_main = 1; -// line 760 "Parser.rl" +// line 772 "Parser.rl" void parseObject(ParserResult res, int p, int pe) { @@ -1894,14 +1906,14 @@ void parseObject(ParserResult res, int p, int pe) { } -// line 1898 "Parser.java" +// line 1910 "Parser.java" { cs = JSON_object_start; } -// line 784 "Parser.rl" +// line 796 "Parser.rl" -// line 1905 "Parser.java" +// line 1917 "Parser.java" { int _klen; int _trans = 0; @@ -1982,7 +1994,7 @@ else if ( data[p] > _JSON_object_trans_keys[_mid+1] ) switch ( _JSON_object_actions[_acts++] ) { case 0: -// line 715 "Parser.rl" +// line 727 "Parser.rl" { parseValue(res, p, pe); if (res.result == null) { @@ -1999,7 +2011,7 @@ else if ( data[p] > _JSON_object_trans_keys[_mid+1] ) } break; case 1: -// line 730 "Parser.rl" +// line 742 "Parser.rl" { parseString(res, p, pe); if (res.result == null) { @@ -2019,13 +2031,13 @@ else if ( data[p] > _JSON_object_trans_keys[_mid+1] ) } break; case 2: -// line 748 "Parser.rl" +// line 760 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 2029 "Parser.java" +// line 2041 "Parser.java" } } } @@ -2045,7 +2057,7 @@ else if ( data[p] > _JSON_object_trans_keys[_mid+1] ) break; } } -// line 785 "Parser.rl" +// line 797 "Parser.rl" if (cs < JSON_object_first_final) { res.update(null, p + 1); @@ -2078,7 +2090,7 @@ else if ( data[p] > _JSON_object_trans_keys[_mid+1] ) } -// line 2082 "Parser.java" +// line 2094 "Parser.java" private static byte[] init__JSON_actions_0() { return new byte [] { @@ -2181,7 +2193,7 @@ private static byte[] init__JSON_trans_actions_0() static final int JSON_en_main = 1; -// line 836 "Parser.rl" +// line 848 "Parser.rl" public IRubyObject parseImplemetation() { @@ -2191,16 +2203,16 @@ public IRubyObject parseImplemetation() { ParserResult res = new ParserResult(); -// line 2195 "Parser.java" +// line 2207 "Parser.java" { cs = JSON_start; } -// line 845 "Parser.rl" +// line 857 "Parser.rl" p = byteList.begin(); pe = p + byteList.length(); -// line 2204 "Parser.java" +// line 2216 "Parser.java" { int _klen; int _trans = 0; @@ -2281,7 +2293,7 @@ else if ( data[p] > _JSON_trans_keys[_mid+1] ) switch ( _JSON_actions[_acts++] ) { case 0: -// line 822 "Parser.rl" +// line 834 "Parser.rl" { parseValue(res, p, pe); if (res.result == null) { @@ -2293,7 +2305,7 @@ else if ( data[p] > _JSON_trans_keys[_mid+1] ) } } break; -// line 2297 "Parser.java" +// line 2309 "Parser.java" } } } @@ -2313,7 +2325,7 @@ else if ( data[p] > _JSON_trans_keys[_mid+1] ) break; } } -// line 848 "Parser.rl" +// line 860 "Parser.rl" if (cs >= JSON_first_final && p == pe) { return result; diff --git a/java/src/json/ext/Parser.rl b/java/src/json/ext/Parser.rl index c0c72adc1..f670bad1e 100644 --- a/java/src/json/ext/Parser.rl +++ b/java/src/json/ext/Parser.rl @@ -50,6 +50,7 @@ public class Parser extends RubyObject { private int maxNesting; private boolean allowNaN; private boolean symbolizeNames; + private boolean freeze; private RubyClass objectClass; private RubyClass arrayClass; private RubyClass decimalClass; @@ -158,6 +159,7 @@ public class Parser extends RubyObject { this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING); this.allowNaN = opts.getBool("allow_nan", false); this.symbolizeNames = opts.getBool("symbolize_names", false); + this.freeze = opts.getBool("freeze", false); this.createId = opts.getString("create_id", getCreateId(context)); this.createAdditions = opts.getBool("create_additions", false); this.objectClass = opts.getClass("object_class", runtime.getHash()); @@ -452,6 +454,9 @@ public class Parser extends RubyObject { %% write exec; if (cs >= JSON_value_first_final && result != null) { + if (parser.freeze) { + result.setFrozen(true); + } res.update(result, p); } else { res.update(null, p); @@ -633,9 +638,16 @@ public class Parser extends RubyObject { if (cs >= JSON_string_first_final && result != null) { if (result instanceof RubyString) { - ((RubyString)result).force_encoding(context, info.utf8.get()); + RubyString string = (RubyString)result; + string.force_encoding(context, info.utf8.get()); + if (parser.freeze) { + string.setFrozen(true); + string = getRuntime().freezeAndDedupString(string); + } + res.update(string, p + 1); + } else { + res.update(result, p + 1); } - res.update(result, p + 1); } else { res.update(null, p + 1); } diff --git a/lib/json/pure/parser.rb b/lib/json/pure/parser.rb index 6f37e0cb8..d795a3b92 100644 --- a/lib/json/pure/parser.rb +++ b/lib/json/pure/parser.rb @@ -61,6 +61,8 @@ class Parser < StringScanner # * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in # defiance of RFC 7159 to be parsed by the Parser. This option defaults # to false. + # * *freeze*: If set to true, all parsed objects will be frozen. Parsed + # string will be deduplicated if possible. # * *symbolize_names*: If set to true, returns symbols for the names # (keys) in a JSON object. Otherwise strings are returned, which is # also the default. It's not possible to use this option in @@ -86,6 +88,7 @@ def initialize(source, opts = {}) end @allow_nan = !!opts[:allow_nan] @symbolize_names = !!opts[:symbolize_names] + @freeze = !!opts[:freeze] if opts.key?(:create_additions) @create_additions = !!opts[:create_additions] else @@ -120,6 +123,7 @@ def parse obj = parse_value UNPARSED.equal?(obj) and raise ParserError, "source is not valid JSON!" + obj.freeze if @freeze end while !eos? && skip(IGNORE) do end eos? or raise ParserError, "source is not valid JSON!" @@ -161,6 +165,7 @@ def convert_encoding(source) EMPTY_8BIT_STRING.force_encoding Encoding::ASCII_8BIT end + STR_UMINUS = ''.respond_to?(:-@) def parse_string if scan(STRING) return '' if self[1].empty? @@ -180,6 +185,15 @@ def parse_string if string.respond_to?(:force_encoding) string.force_encoding(::Encoding::UTF_8) end + + if @freeze + if STR_UMINUS + string = -string + else + string.freeze + end + end + if @create_additions and @match_string for (regexp, klass) in @match_string klass.json_creatable? or next diff --git a/tests/json_parser_test.rb b/tests/json_parser_test.rb index 514441efc..e29f3f126 100644 --- a/tests/json_parser_test.rb +++ b/tests/json_parser_test.rb @@ -218,6 +218,17 @@ def test_symbolize_names end end + def test_freeze + assert_predicate parse('{}', :freeze => true), :frozen? + assert_predicate parse('[]', :freeze => true), :frozen? + assert_predicate parse('"foo"', :freeze => true), :frozen? + + if string_deduplication_available? + assert_same -'foo', parse('"foo"', :freeze => true) + assert_same -'foo', parse('{"foo": 1}', :freeze => true).keys.first + end + end + def test_parse_comments json = < Date: Wed, 7 Oct 2020 03:13:48 +0100 Subject: [PATCH 0091/1010] Fix an issue with generate_pretty and empty objects in the Ruby and Java implementations --- java/src/json/ext/Generator.java | 10 +++++----- lib/json/pure/generator.rb | 6 ++++-- tests/json_generator_test.rb | 5 +++++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index 914d00e79..fc1764039 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -334,13 +334,13 @@ void generate(final Session session, RubyHash object, buffer.append((byte)'{'); buffer.append(objectNl); - object.visitAll(new RubyHash.Visitor() { - private boolean firstPair = true; + final boolean[] firstPair = new boolean[]{true}; + object.visitAll(new RubyHash.Visitor() { @Override public void visit(IRubyObject key, IRubyObject value) { - if (firstPair) { - firstPair = false; + if (firstPair[0]) { + firstPair[0] = false; } else { buffer.append((byte)','); buffer.append(objectNl); @@ -360,7 +360,7 @@ public void visit(IRubyObject key, IRubyObject value) { } }); state.decreaseDepth(); - if (objectNl.length() != 0) { + if (!firstPair[0] && objectNl.length() != 0) { buffer.append(objectNl); buffer.append(Utils.repeat(state.getIndent(), state.getDepth())); } diff --git a/lib/json/pure/generator.rb b/lib/json/pure/generator.rb index ef1c2e884..2257ee346 100644 --- a/lib/json/pure/generator.rb +++ b/lib/json/pure/generator.rb @@ -332,8 +332,10 @@ def json_transform(state) first = false } depth = state.depth -= 1 - result << state.object_nl - result << state.indent * depth if indent + unless first + result << state.object_nl + result << state.indent * depth if indent + end result << '}' result end diff --git a/tests/json_generator_test.rb b/tests/json_generator_test.rb index 77b539dc3..2ecdc9729 100755 --- a/tests/json_generator_test.rb +++ b/tests/json_generator_test.rb @@ -92,6 +92,11 @@ def test_generate end def test_generate_pretty + json = pretty_generate({}) + assert_equal(<<'EOT'.chomp, json) +{ +} +EOT json = pretty_generate(@hash) # hashes aren't (insertion) ordered on every ruby implementation # assert_equal(@json3, json) From 17a931ab65b5a8bfe4e801a5faafbd1d9d5d5ede Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 10 Nov 2020 12:14:14 +0900 Subject: [PATCH 0092/1010] Complex and Rational is embedded classes now --- lib/json/add/complex.rb | 1 - lib/json/add/rational.rb | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/json/add/complex.rb b/lib/json/add/complex.rb index 4d977e758..e63e29fd2 100644 --- a/lib/json/add/complex.rb +++ b/lib/json/add/complex.rb @@ -2,7 +2,6 @@ unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED require 'json' end -defined?(::Complex) or require 'complex' class Complex diff --git a/lib/json/add/rational.rb b/lib/json/add/rational.rb index 6be403458..f77622604 100644 --- a/lib/json/add/rational.rb +++ b/lib/json/add/rational.rb @@ -2,7 +2,6 @@ unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED require 'json' end -defined?(::Rational) or require 'rational' class Rational # Deserializes JSON string by converting numerator value n, From ca6c13549e444939233e0bf48d06eea51c136bd8 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 20 Oct 2020 23:52:03 +0900 Subject: [PATCH 0093/1010] strip trailing spaces [ci skip] --- tests/json_parser_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/json_parser_test.rb b/tests/json_parser_test.rb index e29f3f126..c7f1a5f3b 100644 --- a/tests/json_parser_test.rb +++ b/tests/json_parser_test.rb @@ -222,7 +222,7 @@ def test_freeze assert_predicate parse('{}', :freeze => true), :frozen? assert_predicate parse('[]', :freeze => true), :frozen? assert_predicate parse('"foo"', :freeze => true), :frozen? - + if string_deduplication_available? assert_same -'foo', parse('"foo"', :freeze => true) assert_same -'foo', parse('{"foo": 1}', :freeze => true).keys.first From c03f39ab4c8541de88408dfc2f272487cb2f31f5 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Wed, 21 Oct 2020 23:36:49 +0900 Subject: [PATCH 0094/1010] test/json/json_parser_test.rb: suppress warnings http://rubyci.s3.amazonaws.com/ubuntu/ruby-master/log/20201021T123003Z.log.html.gz ``` /home/chkbuild/chkbuild/tmp/build/20201021T123003Z/ruby/test/json/json_parser_test.rb:227: warning: ambiguous first argument; put parentheses or a space even after `-' operator /home/chkbuild/chkbuild/tmp/build/20201021T123003Z/ruby/test/json/json_parser_test.rb:228: warning: ambiguous first argument; put parentheses or a space even after `-' operator ``` --- tests/json_parser_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/json_parser_test.rb b/tests/json_parser_test.rb index c7f1a5f3b..dce693e54 100644 --- a/tests/json_parser_test.rb +++ b/tests/json_parser_test.rb @@ -224,8 +224,8 @@ def test_freeze assert_predicate parse('"foo"', :freeze => true), :frozen? if string_deduplication_available? - assert_same -'foo', parse('"foo"', :freeze => true) - assert_same -'foo', parse('{"foo": 1}', :freeze => true).keys.first + assert_same(-'foo', parse('"foo"', :freeze => true)) + assert_same(-'foo', parse('{"foo": 1}', :freeze => true).keys.first) end end From 6a97ad2795d1aa2d5c36b980d40d5b92a52061e1 Mon Sep 17 00:00:00 2001 From: Kazuhiro NISHIYAMA Date: Mon, 28 Sep 2020 23:06:50 +0900 Subject: [PATCH 0095/1010] Remove unnecessary executable bit [ci skip] --- tests/json_generator_test.rb | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 tests/json_generator_test.rb diff --git a/tests/json_generator_test.rb b/tests/json_generator_test.rb old mode 100755 new mode 100644 From 1172b6e2185e95caf577df8aa213d6740569d274 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 25 Sep 2020 15:40:56 +0900 Subject: [PATCH 0096/1010] assert_true is not provided by test-unit --- tests/json_common_interface_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/json_common_interface_test.rb b/tests/json_common_interface_test.rb index 4fdc2b118..6939d4b69 100644 --- a/tests/json_common_interface_test.rb +++ b/tests/json_common_interface_test.rb @@ -152,7 +152,7 @@ def test_load_file_with_option_shared(method_name) temp_file_containing(@json) do |filespec| parsed_object = JSON.public_send(method_name, filespec, symbolize_names: true) key_classes = parsed_object.keys.map(&:class) - assert_true key_classes.include?(Symbol) && (! key_classes.include?(String)) + assert key_classes.include?(Symbol) && (! key_classes.include?(String)) end end From 934e43323856431ceba4a6078a87a03140baa7e2 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 25 Sep 2020 20:28:31 +0900 Subject: [PATCH 0097/1010] Extract assert assertion to assert_include and assert_not_include. --- tests/json_common_interface_test.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/json_common_interface_test.rb b/tests/json_common_interface_test.rb index 6939d4b69..faa908e0b 100644 --- a/tests/json_common_interface_test.rb +++ b/tests/json_common_interface_test.rb @@ -152,7 +152,8 @@ def test_load_file_with_option_shared(method_name) temp_file_containing(@json) do |filespec| parsed_object = JSON.public_send(method_name, filespec, symbolize_names: true) key_classes = parsed_object.keys.map(&:class) - assert key_classes.include?(Symbol) && (! key_classes.include?(String)) + assert_include(key_classes, Symbol) + assert_not_include(key_classes, String) end end From cd2e02c2115a5c0e25eaf59db53333fa7a68fbf9 Mon Sep 17 00:00:00 2001 From: Kazuhiro NISHIYAMA Date: Wed, 30 Sep 2020 15:41:14 +0900 Subject: [PATCH 0098/1010] Fix `Leaked tempfile`s http://rubyci.s3.amazonaws.com/debian10/ruby-master/log/20200930T033004Z.diff.html.gz ``` [n/n] JSONCommonInterfaceTest#test_load = s [n/n] JSONCommonInterfaceTest#test_load_file = s +Leaked tempfile: JSONCommonInterfaceTest#test_load_file: #/tmp/20200930-7601-ptnv6i (closed)> [n/n] JSONCommonInterfaceTest#test_load_file! = s +Leaked tempfile: JSONCommonInterfaceTest#test_load_file!: #/tmp/20200930-7601-1la6m9 (closed)> [n/n] JSONCommonInterfaceTest#test_load_file_with_option = s +Leaked tempfile: JSONCommonInterfaceTest#test_load_file_with_option: #/tmp/20200930-7601-blf9hz (closed)> [n/n] JSONCommonInterfaceTest#test_load_file_with_option! = s +Leaked tempfile: JSONCommonInterfaceTest#test_load_file_with_option!: #/tmp/20200930-7601-b5gsdb (closed)> ``` --- tests/json_common_interface_test.rb | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/tests/json_common_interface_test.rb b/tests/json_common_interface_test.rb index faa908e0b..68ad756f0 100644 --- a/tests/json_common_interface_test.rb +++ b/tests/json_common_interface_test.rb @@ -157,27 +157,15 @@ def test_load_file_with_option_shared(method_name) end end - # Copied and slightly modified from https://github.com/keithrbennett/trick_bag - # (https://github.com/keithrbennett/trick_bag/blob/master/lib/trick_bag/io/temp_files.rb). - # - # For the easy creation and deletion of a temp file populated with text, - # wrapped around the code block you provide. - # - # @param text the text to write to the temporary file - # @param file_prefix optional prefix for the temporary file's name - # @yield filespec of the temporary file def temp_file_containing(text, file_prefix = '') raise "This method must be called with a code block." unless block_given? - filespec = nil begin - Tempfile.open(file_prefix) do |file| + Tempfile.create(file_prefix) do |file| file << text - filespec = file.path + file.close + yield file.path end - yield(filespec) - ensure - File.delete(filespec) if filespec && File.exist?(filespec) end end end From 939f641740ff66d62b3fcf7f798dfe997500e03e Mon Sep 17 00:00:00 2001 From: Kazuhiro NISHIYAMA Date: Wed, 30 Sep 2020 15:55:07 +0900 Subject: [PATCH 0099/1010] Remove unneeded `begin` and `end` --- tests/json_common_interface_test.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/json_common_interface_test.rb b/tests/json_common_interface_test.rb index 68ad756f0..8cf74b44b 100644 --- a/tests/json_common_interface_test.rb +++ b/tests/json_common_interface_test.rb @@ -160,12 +160,10 @@ def test_load_file_with_option_shared(method_name) def temp_file_containing(text, file_prefix = '') raise "This method must be called with a code block." unless block_given? - begin - Tempfile.create(file_prefix) do |file| - file << text - file.close - yield file.path - end + Tempfile.create(file_prefix) do |file| + file << text + file.close + yield file.path end end end From dca43ea3b43f475869a4731aa3bc138be16a6e9e Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Fri, 25 Sep 2020 23:37:12 +0900 Subject: [PATCH 0100/1010] t/json/json_common_interface_test.rb: fix wrong indentation to prevent: ``` test/json/json_common_interface_test.rb:182: warning: mismatched indentations at 'end' with 'def' at 169 ``` --- tests/json_common_interface_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/json_common_interface_test.rb b/tests/json_common_interface_test.rb index 8cf74b44b..9148b78c8 100644 --- a/tests/json_common_interface_test.rb +++ b/tests/json_common_interface_test.rb @@ -165,5 +165,5 @@ def temp_file_containing(text, file_prefix = '') file.close yield file.path end - end + end end From db91e90ac1375283f29e1f4a235cc7abb043c2f0 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 13 Nov 2020 11:04:56 +0900 Subject: [PATCH 0101/1010] Drop to support Ruby 2.0 --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 78b19dd86..0aaa2fbb5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ language: ruby # Specify which ruby versions you wish to run your tests on, each version will be used rvm: - - 2.0.0 - 2.1 - 2.2 - 2.3 From c30183311113908e697d05f873264aad246f24c8 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 4 Dec 2020 19:34:06 +0900 Subject: [PATCH 0102/1010] SortedSet was removed at a3db08d7b6ff119223f77e3df00b4f6deac971e2 --- tests/json_addition_test.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/json_addition_test.rb b/tests/json_addition_test.rb index 61625f89e..e95ace837 100644 --- a/tests/json_addition_test.rb +++ b/tests/json_addition_test.rb @@ -195,9 +195,5 @@ def test_ostruct def test_set s = Set.new([:a, :b, :c, :a]) assert_equal s, JSON.parse(JSON(s), :create_additions => true) - ss = SortedSet.new([:d, :b, :a, :c]) - ss_again = JSON.parse(JSON(ss), :create_additions => true) - assert_kind_of ss.class, ss_again - assert_equal ss, ss_again end end From 1b9941782a2a15b8fb87d12123f43b36e3f0c09d Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Wed, 16 Dec 2020 13:06:53 +0100 Subject: [PATCH 0103/1010] gemspec: Fix a typo in RDoc title --- json.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json.gemspec b/json.gemspec index 04d543fd2..736479482 100644 --- a/json.gemspec +++ b/json.gemspec @@ -12,7 +12,7 @@ Gem::Specification.new do |s| s.extensions = ["ext/json/ext/generator/extconf.rb", "ext/json/ext/parser/extconf.rb", "ext/json/extconf.rb"] s.extra_rdoc_files = ["README.md"] - s.rdoc_options = ["--title", "JSON implemention for Ruby", "--main", "README.md"] + s.rdoc_options = ["--title", "JSON implementation for Ruby", "--main", "README.md"] s.files = [ ".gitignore", ".travis.yml", From d9e2b028a6261d1410e7ac6652bb43c764392953 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 15 Dec 2020 17:58:35 +0900 Subject: [PATCH 0104/1010] Bump version to 2.4.0 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 2bf1c1ccf..197c4d5c2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.3.1 +2.4.0 From cba92443df586b94c0b4b6f1246d15b6de883750 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 17 Dec 2020 11:29:50 +0900 Subject: [PATCH 0105/1010] Removed needless files from gemspec --- json.gemspec | 67 ----------------------------------------------- json_pure.gemspec | 32 +++++++++++++++++++++- 2 files changed, 31 insertions(+), 68 deletions(-) diff --git a/json.gemspec b/json.gemspec index 736479482..d8ce92996 100644 --- a/json.gemspec +++ b/json.gemspec @@ -14,16 +14,11 @@ Gem::Specification.new do |s| s.extra_rdoc_files = ["README.md"] s.rdoc_options = ["--title", "JSON implementation for Ruby", "--main", "README.md"] s.files = [ - ".gitignore", - ".travis.yml", "CHANGES.md", "Gemfile", "LICENSE", - "README-json-jruby.md", "README.md", - "Rakefile", "VERSION", - "diagrams/.keep", "ext/json/ext/fbuffer/fbuffer.h", "ext/json/ext/generator/depend", "ext/json/ext/generator/extconf.rb", @@ -35,23 +30,7 @@ Gem::Specification.new do |s| "ext/json/ext/parser/parser.h", "ext/json/ext/parser/parser.rl", "ext/json/extconf.rb", - "install.rb", - "java/src/json/ext/ByteListTranscoder.java", - "java/src/json/ext/Generator.java", - "java/src/json/ext/GeneratorMethods.java", - "java/src/json/ext/GeneratorService.java", - "java/src/json/ext/GeneratorState.java", - "java/src/json/ext/OptionsReader.java", - "java/src/json/ext/Parser.java", - "java/src/json/ext/Parser.rl", - "java/src/json/ext/ParserService.java", - "java/src/json/ext/RuntimeInfo.java", - "java/src/json/ext/StringDecoder.java", - "java/src/json/ext/StringEncoder.java", - "java/src/json/ext/Utils.java", - "json-java.gemspec", "json.gemspec", - "json_pure.gemspec", "lib/json.rb", "lib/json/add/bigdecimal.rb", "lib/json/add/complex.rb", @@ -75,52 +54,6 @@ Gem::Specification.new do |s| "lib/json/pure/generator.rb", "lib/json/pure/parser.rb", "lib/json/version.rb", - "references/rfc7159.txt", - "tests/fixtures/fail10.json", - "tests/fixtures/fail11.json", - "tests/fixtures/fail12.json", - "tests/fixtures/fail13.json", - "tests/fixtures/fail14.json", - "tests/fixtures/fail18.json", - "tests/fixtures/fail19.json", - "tests/fixtures/fail2.json", - "tests/fixtures/fail20.json", - "tests/fixtures/fail21.json", - "tests/fixtures/fail22.json", - "tests/fixtures/fail23.json", - "tests/fixtures/fail24.json", - "tests/fixtures/fail25.json", - "tests/fixtures/fail27.json", - "tests/fixtures/fail28.json", - "tests/fixtures/fail3.json", - "tests/fixtures/fail4.json", - "tests/fixtures/fail5.json", - "tests/fixtures/fail6.json", - "tests/fixtures/fail7.json", - "tests/fixtures/fail8.json", - "tests/fixtures/fail9.json", - "tests/fixtures/obsolete_fail1.json", - "tests/fixtures/pass1.json", - "tests/fixtures/pass15.json", - "tests/fixtures/pass16.json", - "tests/fixtures/pass17.json", - "tests/fixtures/pass2.json", - "tests/fixtures/pass26.json", - "tests/fixtures/pass3.json", - "tests/json_addition_test.rb", - "tests/json_common_interface_test.rb", - "tests/json_encoding_test.rb", - "tests/json_ext_parser_test.rb", - "tests/json_fixtures_test.rb", - "tests/json_generator_test.rb", - "tests/json_generic_object_test.rb", - "tests/json_parser_test.rb", - "tests/json_string_matching_test.rb", - "tests/test_helper.rb", - "tests/test_helper.rb", - "tools/diff.sh", - "tools/fuzz.rb", - "tools/server.rb", ] s.homepage = "http://flori.github.com/json" s.metadata = { diff --git a/json_pure.gemspec b/json_pure.gemspec index 007acd367..1b2999f34 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -10,7 +10,37 @@ Gem::Specification.new do |s| s.description = "This is a JSON implementation in pure Ruby.".freeze s.email = "flori@ping.de".freeze s.extra_rdoc_files = ["README.md".freeze] - s.files = ["./tests/test_helper.rb".freeze, ".gitignore".freeze, ".travis.yml".freeze, "CHANGES.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README-json-jruby.md".freeze, "README.md".freeze, "Rakefile".freeze, "VERSION".freeze, "diagrams/.keep".freeze, "ext/json/ext/fbuffer/fbuffer.h".freeze, "ext/json/ext/generator/depend".freeze, "ext/json/ext/generator/extconf.rb".freeze, "ext/json/ext/generator/generator.c".freeze, "ext/json/ext/generator/generator.h".freeze, "ext/json/ext/parser/depend".freeze, "ext/json/ext/parser/extconf.rb".freeze, "ext/json/ext/parser/parser.c".freeze, "ext/json/ext/parser/parser.h".freeze, "ext/json/ext/parser/parser.rl".freeze, "ext/json/extconf.rb".freeze, "install.rb".freeze, "java/src/json/ext/ByteListTranscoder.java".freeze, "java/src/json/ext/Generator.java".freeze, "java/src/json/ext/GeneratorMethods.java".freeze, "java/src/json/ext/GeneratorService.java".freeze, "java/src/json/ext/GeneratorState.java".freeze, "java/src/json/ext/OptionsReader.java".freeze, "java/src/json/ext/Parser.java".freeze, "java/src/json/ext/Parser.rl".freeze, "java/src/json/ext/ParserService.java".freeze, "java/src/json/ext/RuntimeInfo.java".freeze, "java/src/json/ext/StringDecoder.java".freeze, "java/src/json/ext/StringEncoder.java".freeze, "java/src/json/ext/Utils.java".freeze, "json-java.gemspec".freeze, "json.gemspec".freeze, "json_pure.gemspec".freeze, "lib/json.rb".freeze, "lib/json/add/bigdecimal.rb".freeze, "lib/json/add/complex.rb".freeze, "lib/json/add/core.rb".freeze, "lib/json/add/date.rb".freeze, "lib/json/add/date_time.rb".freeze, "lib/json/add/exception.rb".freeze, "lib/json/add/ostruct.rb".freeze, "lib/json/add/range.rb".freeze, "lib/json/add/rational.rb".freeze, "lib/json/add/regexp.rb".freeze, "lib/json/add/set.rb".freeze, "lib/json/add/struct.rb".freeze, "lib/json/add/symbol.rb".freeze, "lib/json/add/time.rb".freeze, "lib/json/common.rb".freeze, "lib/json/ext.rb".freeze, "lib/json/ext/.keep".freeze, "lib/json/generic_object.rb".freeze, "lib/json/pure.rb".freeze, "lib/json/pure/generator.rb".freeze, "lib/json/pure/parser.rb".freeze, "lib/json/version.rb".freeze, "references/rfc7159.txt".freeze, "tests/fixtures/fail10.json".freeze, "tests/fixtures/fail11.json".freeze, "tests/fixtures/fail12.json".freeze, "tests/fixtures/fail13.json".freeze, "tests/fixtures/fail14.json".freeze, "tests/fixtures/fail18.json".freeze, "tests/fixtures/fail19.json".freeze, "tests/fixtures/fail2.json".freeze, "tests/fixtures/fail20.json".freeze, "tests/fixtures/fail21.json".freeze, "tests/fixtures/fail22.json".freeze, "tests/fixtures/fail23.json".freeze, "tests/fixtures/fail24.json".freeze, "tests/fixtures/fail25.json".freeze, "tests/fixtures/fail27.json".freeze, "tests/fixtures/fail28.json".freeze, "tests/fixtures/fail3.json".freeze, "tests/fixtures/fail4.json".freeze, "tests/fixtures/fail5.json".freeze, "tests/fixtures/fail6.json".freeze, "tests/fixtures/fail7.json".freeze, "tests/fixtures/fail8.json".freeze, "tests/fixtures/fail9.json".freeze, "tests/fixtures/obsolete_fail1.json".freeze, "tests/fixtures/pass1.json".freeze, "tests/fixtures/pass15.json".freeze, "tests/fixtures/pass16.json".freeze, "tests/fixtures/pass17.json".freeze, "tests/fixtures/pass2.json".freeze, "tests/fixtures/pass26.json".freeze, "tests/fixtures/pass3.json".freeze, "tests/json_addition_test.rb".freeze, "tests/json_common_interface_test.rb".freeze, "tests/json_encoding_test.rb".freeze, "tests/json_ext_parser_test.rb".freeze, "tests/json_fixtures_test.rb".freeze, "tests/json_generator_test.rb".freeze, "tests/json_generic_object_test.rb".freeze, "tests/json_parser_test.rb".freeze, "tests/json_string_matching_test.rb".freeze, "tests/test_helper.rb".freeze, "tools/diff.sh".freeze, "tools/fuzz.rb".freeze, "tools/server.rb".freeze] + s.files = [ + "CHANGES.md".freeze, + "Gemfile".freeze, + "LICENSE".freeze, + "README.md".freeze, + "VERSION".freeze, + "json_pure.gemspec".freeze, + "lib/json.rb".freeze, + "lib/json/add/bigdecimal.rb".freeze, + "lib/json/add/complex.rb".freeze, + "lib/json/add/core.rb".freeze, + "lib/json/add/date.rb".freeze, + "lib/json/add/date_time.rb".freeze, + "lib/json/add/exception.rb".freeze, + "lib/json/add/ostruct.rb".freeze, + "lib/json/add/range.rb".freeze, + "lib/json/add/rational.rb".freeze, + "lib/json/add/regexp.rb".freeze, + "lib/json/add/set.rb".freeze, + "lib/json/add/struct.rb".freeze, + "lib/json/add/symbol.rb".freeze, + "lib/json/add/time.rb".freeze, + "lib/json/common.rb".freeze, + "lib/json/ext.rb".freeze, + "lib/json/ext/.keep".freeze, + "lib/json/generic_object.rb".freeze, + "lib/json/pure.rb".freeze, + "lib/json/pure/generator.rb".freeze, + "lib/json/pure/parser.rb".freeze, + "lib/json/version.rb".freeze, + ] s.homepage = "http://flori.github.com/json".freeze s.licenses = ["Ruby".freeze] s.rdoc_options = ["--title".freeze, "JSON implemention for ruby".freeze, "--main".freeze, "README.md".freeze] From 2d1cb9bfa11939847dbac35ed3ee99da30abc138 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 17 Dec 2020 11:30:32 +0900 Subject: [PATCH 0106/1010] Removed too old compatibility code in json_pure.gemspec --- json_pure.gemspec | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/json_pure.gemspec b/json_pure.gemspec index 1b2999f34..edca78a8b 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -49,15 +49,6 @@ Gem::Specification.new do |s| s.summary = "JSON Implementation for Ruby".freeze s.test_files = ["./tests/test_helper.rb".freeze] - if s.respond_to? :specification_version then - s.specification_version = 4 - end - - if s.respond_to? :add_runtime_dependency then - s.add_development_dependency(%q.freeze, [">= 0"]) - s.add_development_dependency(%q.freeze, [">= 2.0", "< 4.0"]) - else - s.add_dependency(%q.freeze, [">= 0"]) - s.add_dependency(%q.freeze, [">= 2.0", "< 4.0"]) - end + s.add_development_dependency(%q.freeze, [">= 0"]) + s.add_development_dependency(%q.freeze, [">= 2.0", "< 4.0"]) end From 569cc4507ce88d6b486a23aa7e605df91d52b021 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 17 Dec 2020 12:14:07 +0900 Subject: [PATCH 0107/1010] Adjust json_pure gemspec with json gemspec --- json_pure.gemspec | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/json_pure.gemspec b/json_pure.gemspec index edca78a8b..47de54a13 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -4,12 +4,14 @@ Gem::Specification.new do |s| s.name = "json_pure".freeze s.version = File.read("VERSION").chomp - s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= - s.require_paths = ["lib".freeze] - s.authors = ["Florian Frank".freeze] + s.summary = "JSON Implementation for Ruby".freeze s.description = "This is a JSON implementation in pure Ruby.".freeze + s.licenses = ["Ruby".freeze] + s.authors = ["Florian Frank".freeze] s.email = "flori@ping.de".freeze + s.extra_rdoc_files = ["README.md".freeze] + s.rdoc_options = ["--title".freeze, "JSON implemention for ruby".freeze, "--main".freeze, "README.md".freeze] s.files = [ "CHANGES.md".freeze, "Gemfile".freeze, @@ -42,11 +44,8 @@ Gem::Specification.new do |s| "lib/json/version.rb".freeze, ] s.homepage = "http://flori.github.com/json".freeze - s.licenses = ["Ruby".freeze] - s.rdoc_options = ["--title".freeze, "JSON implemention for ruby".freeze, "--main".freeze, "README.md".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 2.0".freeze) - s.rubygems_version = "3.1.2".freeze - s.summary = "JSON Implementation for Ruby".freeze s.test_files = ["./tests/test_helper.rb".freeze] s.add_development_dependency(%q.freeze, [">= 0"]) From e4e8e0f4cfeb7f5138f2156aa8f434eaf71bc393 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 17 Dec 2020 12:16:13 +0900 Subject: [PATCH 0108/1010] Added metadata to json_pure.gemspec same as json and json-java --- json_pure.gemspec | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/json_pure.gemspec b/json_pure.gemspec index 47de54a13..a09c68ab6 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -44,6 +44,14 @@ Gem::Specification.new do |s| "lib/json/version.rb".freeze, ] s.homepage = "http://flori.github.com/json".freeze + s.metadata = { + 'bug_tracker_uri' => 'https://github.com/flori/json/issues', + 'changelog_uri' => 'https://github.com/flori/json/blob/master/CHANGES.md', + 'documentation_uri' => 'http://flori.github.io/json/doc/index.html', + 'homepage_uri' => 'http://flori.github.io/json/', + 'source_code_uri' => 'https://github.com/flori/json', + 'wiki_uri' => 'https://github.com/flori/json/wiki' + } s.required_ruby_version = Gem::Requirement.new(">= 2.0".freeze) s.test_files = ["./tests/test_helper.rb".freeze] From 67827a291d53892a8e813dbb3c668649a4270959 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 17 Dec 2020 12:19:54 +0900 Subject: [PATCH 0109/1010] Added tests direcotry for test_files --- json.gemspec | 2 +- json_pure.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/json.gemspec b/json.gemspec index d8ce92996..b9645405c 100644 --- a/json.gemspec +++ b/json.gemspec @@ -54,7 +54,7 @@ Gem::Specification.new do |s| "lib/json/pure/generator.rb", "lib/json/pure/parser.rb", "lib/json/version.rb", - ] + ] + Dir["tests/**/*"] s.homepage = "http://flori.github.com/json" s.metadata = { 'bug_tracker_uri' => 'https://github.com/flori/json/issues', diff --git a/json_pure.gemspec b/json_pure.gemspec index a09c68ab6..af63e28dd 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -42,7 +42,7 @@ Gem::Specification.new do |s| "lib/json/pure/generator.rb".freeze, "lib/json/pure/parser.rb".freeze, "lib/json/version.rb".freeze, - ] + ] + Dir["tests/**/*"] s.homepage = "http://flori.github.com/json".freeze s.metadata = { 'bug_tracker_uri' => 'https://github.com/flori/json/issues', From 40c5d7448c79fcd9700ac97ae9fe87c95cca61e2 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 17 Dec 2020 13:30:12 +0900 Subject: [PATCH 0110/1010] Relaxed dev dependencies --- json-java.gemspec | 4 ++-- json.gemspec | 4 ++-- json_pure.gemspec | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/json-java.gemspec b/json-java.gemspec index a34103bff..8b6789300 100755 --- a/json-java.gemspec +++ b/json-java.gemspec @@ -27,8 +27,8 @@ spec = Gem::Specification.new do |s| s.required_ruby_version = Gem::Requirement.new(">= 2.0") s.test_files = ["tests/test_helper.rb"] - s.add_development_dependency("rake", [">= 0"]) - s.add_development_dependency("test-unit", [">= 2.0", "< 4.0"]) + s.add_development_dependency "rake" + s.add_development_dependency "test-unit" end if $0 == __FILE__ diff --git a/json.gemspec b/json.gemspec index b9645405c..13196dfae 100644 --- a/json.gemspec +++ b/json.gemspec @@ -68,6 +68,6 @@ Gem::Specification.new do |s| s.required_ruby_version = Gem::Requirement.new(">= 2.0") s.test_files = ["tests/test_helper.rb"] - s.add_development_dependency("rake", [">= 0"]) - s.add_development_dependency("test-unit", [">= 2.0", "< 4.0"]) + s.add_development_dependency "rake" + s.add_development_dependency "test-unit" end diff --git a/json_pure.gemspec b/json_pure.gemspec index af63e28dd..872f8a0ac 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -56,6 +56,6 @@ Gem::Specification.new do |s| s.required_ruby_version = Gem::Requirement.new(">= 2.0".freeze) s.test_files = ["./tests/test_helper.rb".freeze] - s.add_development_dependency(%q.freeze, [">= 0"]) - s.add_development_dependency(%q.freeze, [">= 2.0", "< 4.0"]) + s.add_development_dependency "rake" + s.add_development_dependency "test-unit" end From 4885410da178171c074177b7e1d9cf71d270ca3e Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 17 Dec 2020 13:32:12 +0900 Subject: [PATCH 0111/1010] Fixed broken build task for json and json_pure --- Rakefile | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Rakefile b/Rakefile index 5b422926e..f48c377b2 100644 --- a/Rakefile +++ b/Rakefile @@ -4,12 +4,7 @@ rescue LoadError end require 'rbconfig' -include\ - begin - RbConfig - rescue NameError - Config - end +include RbConfig require 'rake/clean' CLOBBER.include 'doc', 'Gemfile.lock' @@ -30,10 +25,8 @@ which = lambda { |c| MAKE = ENV['MAKE'] || %w[gmake make].find(&which) BUNDLE = ENV['BUNDLE'] || %w[bundle].find(&which) -PKG_NAME = 'json' -PKG_TITLE = 'JSON Implementation for Ruby' + PKG_VERSION = File.read('VERSION').chomp -PKG_FILES = FileList[`git ls-files`.split(/\n/)] EXT_ROOT_DIR = 'ext/json/ext' EXT_PARSER_DIR = "#{EXT_ROOT_DIR}/parser" @@ -324,8 +317,17 @@ else desc "Generate diagrams of ragel parser" task :ragel_dot => [ :ragel_dot_png, :ragel_dot_ps ] + desc "Create the gem packages" + task :package do + sh "gem build json.gemspec" + sh "gem build json_pure.gemspec" + mkdir_p 'pkg' + mv "json-#{PKG_VERSION}.gem", 'pkg' + mv "json_pure-#{PKG_VERSION}.gem", 'pkg' + end + desc "Build all gems and archives for a new release of json and json_pure." - task :build => [ :clean, :gemspec, :package ] + task :build => [ :clean, :version, :package ] task :release => :build end From bfc93517406bb6ea5848cec9608156196ce2edd7 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 17 Dec 2020 13:46:07 +0900 Subject: [PATCH 0112/1010] Update version.rb from rake version task --- lib/json/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/json/version.rb b/lib/json/version.rb index e4dd3e336..784c91f9b 100644 --- a/lib/json/version.rb +++ b/lib/json/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module JSON # JSON version - VERSION = '2.3.1' + VERSION = '2.4.0' VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc: VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc: VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: From 1459c5dec98f9afed9b87e6ab042cf2bf0e2a3c0 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 17 Dec 2020 14:01:04 +0900 Subject: [PATCH 0113/1010] Restore 2.4.0 changes --- CHANGES.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 0d39f21ec..dad686521 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,15 @@ # Changes +## 2020-12-15 (2.4.0) + +* Implement a freeze: parser option #447 +* Fix an issue with generate_pretty and empty objects in the Ruby and Java implementations #449 +* Fix JSON.load_file doc #448 +* Fix pure parser with unclosed arrays / objects #425 +* bundle the LICENSE file in the gem #444 +* Add an option to escape forward slash character #405 +* RDoc for JSON #439 #446 #442 #434 #433 #430 + ## 2020-06-30 (2.3.1) * Spelling and grammar fixes for comments. Pull request #191 by Josh From 398ec004c666d330d03a0c2f7afa2868c553b1f6 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 17 Dec 2020 14:02:37 +0900 Subject: [PATCH 0114/1010] Bump version to json-2.4.1 --- CHANGES.md | 4 ++++ VERSION | 2 +- lib/json/version.rb | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index dad686521..b5eeb4f15 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## 2020-12-17 (2.4.1) + +* Restore version.rb with 2.4.1 + ## 2020-12-15 (2.4.0) * Implement a freeze: parser option #447 diff --git a/VERSION b/VERSION index 197c4d5c2..005119baa 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.4.0 +2.4.1 diff --git a/lib/json/version.rb b/lib/json/version.rb index 784c91f9b..5502a4d27 100644 --- a/lib/json/version.rb +++ b/lib/json/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module JSON # JSON version - VERSION = '2.4.0' + VERSION = '2.4.1' VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc: VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc: VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: From 1bd46c49c064fb0f30fe679b887bb4034b6af9f5 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 17 Dec 2020 14:37:45 +0900 Subject: [PATCH 0115/1010] expand VERSION path for ruby repository. --- json.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json.gemspec b/json.gemspec index 13196dfae..d5f0a4de1 100644 --- a/json.gemspec +++ b/json.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |s| s.name = "json" - s.version = File.read('VERSION').chomp + s.version = File.read(File.expand_path('../VERSION', __FILE__)).chomp s.summary = "JSON Implementation for Ruby" s.description = "This is a JSON implementation as a Ruby extension in C." From 483009dd8a6e9f2a19bce8f77a9541531f4a3eec Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Sun, 20 Dec 2020 12:17:32 +0900 Subject: [PATCH 0116/1010] [json] Stop using prototype objects --- ext/json/ext/generator/generator.c | 7 ++---- lib/json/common.rb | 21 ++++++++++------ tests/json_generator_test.rb | 39 +++--------------------------- 3 files changed, 18 insertions(+), 49 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 80d1ca7bf..2e802c8ea 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -15,8 +15,7 @@ static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject, #endif mFloat, mString, mString_Extend, mTrueClass, mFalseClass, mNilClass, eGeneratorError, - eNestingError, - i_SAFE_STATE_PROTOTYPE; + eNestingError; static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before, i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only, @@ -1166,8 +1165,7 @@ static VALUE cState_from_state_s(VALUE self, VALUE opts) } else if (rb_obj_is_kind_of(opts, rb_cHash)) { return rb_funcall(self, i_new, 1, opts); } else { - VALUE prototype = rb_const_get(mJSON, i_SAFE_STATE_PROTOTYPE); - return rb_funcall(prototype, i_dup, 0); + return rb_class_new_instance(0, NULL, cState); } } @@ -1608,5 +1606,4 @@ void Init_generator(void) i_encoding = rb_intern("encoding"); i_encode = rb_intern("encode"); #endif - i_SAFE_STATE_PROTOTYPE = rb_intern("SAFE_STATE_PROTOTYPE"); } diff --git a/lib/json/common.rb b/lib/json/common.rb index 111d70c33..c34fa61eb 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -71,22 +71,27 @@ def generator=(generator) # :nodoc: end self.state = generator::State const_set :State, self.state - const_set :SAFE_STATE_PROTOTYPE, State.new - const_set :FAST_STATE_PROTOTYPE, State.new( + ensure + $VERBOSE = old + end + + def create_fast_state + State.new( :indent => '', :space => '', :object_nl => "", :array_nl => "", :max_nesting => false ) - const_set :PRETTY_STATE_PROTOTYPE, State.new( + end + + def create_pretty_state + State.new( :indent => ' ', :space => ' ', :object_nl => "\n", :array_nl => "\n" ) - ensure - $VERBOSE = old end # Returns the JSON generator module that is used by JSON. This is @@ -276,7 +281,7 @@ def generate(obj, opts = nil) if State === opts state, opts = opts, nil else - state = SAFE_STATE_PROTOTYPE.dup + state = State.new end if opts if opts.respond_to? :to_hash @@ -315,7 +320,7 @@ def fast_generate(obj, opts = nil) if State === opts state, opts = opts, nil else - state = FAST_STATE_PROTOTYPE.dup + state = JSON.create_fast_state end if opts if opts.respond_to? :to_hash @@ -370,7 +375,7 @@ def pretty_generate(obj, opts = nil) if State === opts state, opts = opts, nil else - state = PRETTY_STATE_PROTOTYPE.dup + state = JSON.create_pretty_state end if opts if opts.respond_to? :to_hash diff --git a/tests/json_generator_test.rb b/tests/json_generator_test.rb index 2ecdc9729..5bafc3ead 100644 --- a/tests/json_generator_test.rb +++ b/tests/json_generator_test.rb @@ -48,35 +48,6 @@ def silence $VERBOSE = v end - def test_remove_const_segv - stress = GC.stress - const = JSON::SAFE_STATE_PROTOTYPE.dup - - bignum_too_long_to_embed_as_string = 1234567890123456789012345 - expect = bignum_too_long_to_embed_as_string.to_s - GC.stress = true - - 10.times do |i| - tmp = bignum_too_long_to_embed_as_string.to_json - raise "'\#{expect}' is expected, but '\#{tmp}'" unless tmp == expect - end - - silence do - JSON.const_set :SAFE_STATE_PROTOTYPE, nil - end - - 10.times do |i| - assert_raise TypeError do - bignum_too_long_to_embed_as_string.to_json - end - end - ensure - GC.stress = stress - silence do - JSON.const_set :SAFE_STATE_PROTOTYPE, const - end - end if JSON.const_defined?("Ext") && RUBY_ENGINE != 'jruby' - def test_generate json = generate(@hash) assert_equal(parse(@json2), parse(json)) @@ -171,7 +142,7 @@ def test_states end def test_pretty_state - state = PRETTY_STATE_PROTOTYPE.dup + state = JSON.create_pretty_state assert_equal({ :allow_nan => false, :array_nl => "\n", @@ -188,7 +159,7 @@ def test_pretty_state end def test_safe_state - state = SAFE_STATE_PROTOTYPE.dup + state = JSON::State.new assert_equal({ :allow_nan => false, :array_nl => "", @@ -205,7 +176,7 @@ def test_safe_state end def test_fast_state - state = FAST_STATE_PROTOTYPE.dup + state = JSON.create_fast_state assert_equal({ :allow_nan => false, :array_nl => "", @@ -241,12 +212,8 @@ def test_allow_nan def test_depth ary = []; ary << ary - assert_equal 0, JSON::SAFE_STATE_PROTOTYPE.depth assert_raise(JSON::NestingError) { generate(ary) } - assert_equal 0, JSON::SAFE_STATE_PROTOTYPE.depth - assert_equal 0, JSON::PRETTY_STATE_PROTOTYPE.depth assert_raise(JSON::NestingError) { JSON.pretty_generate(ary) } - assert_equal 0, JSON::PRETTY_STATE_PROTOTYPE.depth s = JSON.state.new assert_equal 0, s.depth assert_raise(JSON::NestingError) { ary.to_json(s) } From 4d6482bb16e1214927c962c363bb765a4954fdc4 Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Mon, 21 Dec 2020 15:45:50 +0900 Subject: [PATCH 0117/1010] [json] Make JSON.create_id thread-safe --- lib/json/common.rb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/json/common.rb b/lib/json/common.rb index c34fa61eb..747effee1 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -109,7 +109,20 @@ def create_pretty_state # JSON.create_id # => 'json_class' attr_accessor :create_id end - self.create_id = 'json_class' + + DEFAULT_CREATE_ID = 'json_class'.freeze + private_constant :DEFAULT_CREATE_ID + + CREATE_ID_TLS_KEY = "JSON.create_id".freeze + private_constant :CREATE_ID_TLS_KEY + + def self.create_id + Thread.current[CREATE_ID_TLS_KEY] || DEFAULT_CREATE_ID + end + + def self.create_id=(new_value) + Thread.current[CREATE_ID_TLS_KEY] = new_value.dup.freeze + end NaN = 0.0/0 From ae5ef25af52b2b92d7ecf40feeca09c324c0d777 Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Mon, 21 Dec 2020 15:50:04 +0900 Subject: [PATCH 0118/1010] [json] JSON_parse_float: Fix how to convert number Stop BigDecimal-specific optimization. Instead, it tries the conversion methods in the following order: 1. `try_convert`, 2. `new`, and 3. class-named function, e.g. `Foo::Bar.Baz` function for `Foo::Bar::Baz` class If all the above candidates are unavailable, it fallbacks to Float. --- ext/json/ext/parser/parser.c | 61 +++++++++++++++++++++-------------- ext/json/ext/parser/parser.rl | 61 +++++++++++++++++++++-------------- 2 files changed, 72 insertions(+), 50 deletions(-) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index aaef53aac..f9972956c 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -91,13 +91,12 @@ static int convert_UTF32_to_UTF8(char *buf, UTF32 ch) static VALUE mJSON, mExt, cParser, eParserError, eNestingError; static VALUE CNaN, CInfinity, CMinusInfinity; -static VALUE cBigDecimal = Qundef; static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_object_class, i_array_class, i_decimal_class, i_key_p, i_deep_const_get, i_match, i_match_string, i_aset, i_aref, - i_leftshift, i_new, i_BigDecimal, i_freeze, i_uminus; + i_leftshift, i_new, i_try_convert, i_freeze, i_uminus; #line 126 "parser.rl" @@ -991,19 +990,6 @@ enum {JSON_float_en_main = 1}; #line 346 "parser.rl" -static int is_bigdecimal_class(VALUE obj) -{ - if (cBigDecimal == Qundef) { - if (rb_const_defined(rb_cObject, i_BigDecimal)) { - cBigDecimal = rb_const_get_at(rb_cObject, i_BigDecimal); - } - else { - return 0; - } - } - return obj == cBigDecimal; -} - static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result) { int cs = EVIL; @@ -1146,21 +1132,46 @@ case 7: #line 368 "parser.rl" if (cs >= JSON_float_first_final) { + VALUE mod = Qnil; + ID method_id = 0; + if (rb_respond_to(json->decimal_class, i_try_convert)) { + mod = json->decimal_class; + method_id = i_try_convert; + } else if (rb_respond_to(json->decimal_class, i_new)) { + mod = json->decimal_class; + method_id = i_new; + } else if (RB_TYPE_P(json->decimal_class, T_CLASS)) { + VALUE name = rb_class_name(json->decimal_class); + const char *name_cstr = RSTRING_PTR(name); + const char *last_colon = strrchr(name_cstr, ':'); + if (last_colon) { + const char *mod_path_end = last_colon - 1; + VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr); + mod = rb_path_to_class(mod_path); + + const char *method_name_beg = last_colon + 1; + long before_len = method_name_beg - name_cstr; + long len = RSTRING_LEN(name) - before_len; + VALUE method_name = rb_str_substr(name, before_len, len); + method_id = SYM2ID(rb_str_intern(method_name)); + } else { + mod = rb_mKernel; + method_id = SYM2ID(rb_str_intern(name)); + } + } + long len = p - json->memo; fbuffer_clear(json->fbuffer); fbuffer_append(json->fbuffer, json->memo, len); fbuffer_append_char(json->fbuffer, '\0'); - if (NIL_P(json->decimal_class)) { - *result = rb_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1)); + + if (method_id) { + VALUE text = rb_str_new2(FBUFFER_PTR(json->fbuffer)); + *result = rb_funcallv(mod, method_id, 1, &text); } else { - VALUE text; - text = rb_str_new2(FBUFFER_PTR(json->fbuffer)); - if (is_bigdecimal_class(json->decimal_class)) { - *result = rb_funcall(Qnil, i_BigDecimal, 1, text); - } else { - *result = rb_funcall(json->decimal_class, i_new, 1, text); - } + *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1)); } + return p + 1; } else { return NULL; @@ -2150,7 +2161,7 @@ void Init_parser(void) i_aref = rb_intern("[]"); i_leftshift = rb_intern("<<"); i_new = rb_intern("new"); - i_BigDecimal = rb_intern("BigDecimal"); + i_try_convert = rb_intern("try_convert"); i_freeze = rb_intern("freeze"); i_uminus = rb_intern("-@"); } diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl index 462905209..24aed600e 100644 --- a/ext/json/ext/parser/parser.rl +++ b/ext/json/ext/parser/parser.rl @@ -89,13 +89,12 @@ static int convert_UTF32_to_UTF8(char *buf, UTF32 ch) static VALUE mJSON, mExt, cParser, eParserError, eNestingError; static VALUE CNaN, CInfinity, CMinusInfinity; -static VALUE cBigDecimal = Qundef; static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_object_class, i_array_class, i_decimal_class, i_key_p, i_deep_const_get, i_match, i_match_string, i_aset, i_aref, - i_leftshift, i_new, i_BigDecimal, i_freeze, i_uminus; + i_leftshift, i_new, i_try_convert, i_freeze, i_uminus; %%{ machine JSON_common; @@ -345,19 +344,6 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res ) (^[0-9Ee.\-]? @exit ); }%% -static int is_bigdecimal_class(VALUE obj) -{ - if (cBigDecimal == Qundef) { - if (rb_const_defined(rb_cObject, i_BigDecimal)) { - cBigDecimal = rb_const_get_at(rb_cObject, i_BigDecimal); - } - else { - return 0; - } - } - return obj == cBigDecimal; -} - static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result) { int cs = EVIL; @@ -367,21 +353,46 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul %% write exec; if (cs >= JSON_float_first_final) { + VALUE mod = Qnil; + ID method_id = 0; + if (rb_respond_to(json->decimal_class, i_try_convert)) { + mod = json->decimal_class; + method_id = i_try_convert; + } else if (rb_respond_to(json->decimal_class, i_new)) { + mod = json->decimal_class; + method_id = i_new; + } else if (RB_TYPE_P(json->decimal_class, T_CLASS)) { + VALUE name = rb_class_name(json->decimal_class); + const char *name_cstr = RSTRING_PTR(name); + const char *last_colon = strrchr(name_cstr, ':'); + if (last_colon) { + const char *mod_path_end = last_colon - 1; + VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr); + mod = rb_path_to_class(mod_path); + + const char *method_name_beg = last_colon + 1; + long before_len = method_name_beg - name_cstr; + long len = RSTRING_LEN(name) - before_len; + VALUE method_name = rb_str_substr(name, before_len, len); + method_id = SYM2ID(rb_str_intern(method_name)); + } else { + mod = rb_mKernel; + method_id = SYM2ID(rb_str_intern(name)); + } + } + long len = p - json->memo; fbuffer_clear(json->fbuffer); fbuffer_append(json->fbuffer, json->memo, len); fbuffer_append_char(json->fbuffer, '\0'); - if (NIL_P(json->decimal_class)) { - *result = rb_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1)); + + if (method_id) { + VALUE text = rb_str_new2(FBUFFER_PTR(json->fbuffer)); + *result = rb_funcallv(mod, method_id, 1, &text); } else { - VALUE text; - text = rb_str_new2(FBUFFER_PTR(json->fbuffer)); - if (is_bigdecimal_class(json->decimal_class)) { - *result = rb_funcall(Qnil, i_BigDecimal, 1, text); - } else { - *result = rb_funcall(json->decimal_class, i_new, 1, text); - } + *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1)); } + return p + 1; } else { return NULL; @@ -910,7 +921,7 @@ void Init_parser(void) i_aref = rb_intern("[]"); i_leftshift = rb_intern("<<"); i_new = rb_intern("new"); - i_BigDecimal = rb_intern("BigDecimal"); + i_try_convert = rb_intern("try_convert"); i_freeze = rb_intern("freeze"); i_uminus = rb_intern("-@"); } From f1d5fb030ce758b96e13817290964d92e3516d82 Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Mon, 21 Dec 2020 15:57:42 +0900 Subject: [PATCH 0119/1010] [json] Make json Ractor safe --- ext/json/ext/generator/generator.c | 11 +++++++++- ext/json/ext/parser/parser.c | 4 ++++ ext/json/ext/parser/parser.rl | 4 ++++ tests/ractor_test.rb | 34 ++++++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 tests/ractor_test.rb diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 2e802c8ea..407c1af4a 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -619,13 +619,18 @@ static size_t State_memsize(const void *ptr) return size; } +#ifndef HAVE_RB_EXT_RACTOR_SAFE +# undef RUBY_TYPED_FROZEN_SHAREABLE +# define RUBY_TYPED_FROZEN_SHAREABLE 0 +#endif + #ifdef NEW_TYPEDDATA_WRAPPER static const rb_data_type_t JSON_Generator_State_type = { "JSON/Generator/State", {NULL, State_free, State_memsize,}, #ifdef RUBY_TYPED_FREE_IMMEDIATELY 0, 0, - RUBY_TYPED_FREE_IMMEDIATELY, + RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE, #endif }; #endif @@ -1497,6 +1502,10 @@ static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_l */ void Init_generator(void) { +#ifdef HAVE_RB_EXT_RACTOR_SAFE + rb_ext_ractor_safe(true); +#endif + #undef rb_intern rb_require("json/common"); diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index f9972956c..29b5674d9 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -2119,6 +2119,10 @@ static VALUE cParser_source(VALUE self) void Init_parser(void) { +#ifdef HAVE_RB_EXT_RACTOR_SAFE + rb_ext_ractor_safe(true); +#endif + #undef rb_intern rb_require("json/common"); mJSON = rb_define_module("JSON"); diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl index 24aed600e..1da70c544 100644 --- a/ext/json/ext/parser/parser.rl +++ b/ext/json/ext/parser/parser.rl @@ -879,6 +879,10 @@ static VALUE cParser_source(VALUE self) void Init_parser(void) { +#ifdef HAVE_RB_EXT_RACTOR_SAFE + rb_ext_ractor_safe(true); +#endif + #undef rb_intern rb_require("json/common"); mJSON = rb_define_module("JSON"); diff --git a/tests/ractor_test.rb b/tests/ractor_test.rb new file mode 100644 index 000000000..96d1528c5 --- /dev/null +++ b/tests/ractor_test.rb @@ -0,0 +1,34 @@ +# encoding: utf-8 +# frozen_string_literal: false + +require 'test_helper' + +class JSONInRactorTest < Test::Unit::TestCase + def setup + skip unless defined? Ractor + end + + def test_generate + assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + $VERBOSE = nil + require "json" + r = Ractor.new do + json = JSON.generate({ + 'a' => 2, + 'b' => 3.141, + 'c' => 'c', + 'd' => [ 1, "b", 3.14 ], + 'e' => { 'foo' => 'bar' }, + 'g' => "\"\0\037", + 'h' => 1000.0, + 'i' => 0.001 + }) + JSON.parse(json) + end + expected_json = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},' + + '"g":"\\"\\u0000\\u001f","h":1000.0,"i":0.001}' + assert_equal(JSON.parse(expected_json), r.take) + end; + end +end From f35ab5ee4cf6e3f1855885f86e8ed72d16752401 Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Mon, 21 Dec 2020 22:40:38 +0900 Subject: [PATCH 0120/1010] [json] Avoid method redefinition --- lib/json/common.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/json/common.rb b/lib/json/common.rb index 747effee1..d58c7eae9 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -103,11 +103,6 @@ def create_pretty_state # either JSON::Ext::Generator::State or JSON::Pure::Generator::State: # JSON.state # => JSON::Ext::Generator::State attr_accessor :state - - # Sets or returns create identifier, which is used to decide if the _json_create_ - # hook of a class should be called; initial value is +json_class+: - # JSON.create_id # => 'json_class' - attr_accessor :create_id end DEFAULT_CREATE_ID = 'json_class'.freeze @@ -116,14 +111,19 @@ def create_pretty_state CREATE_ID_TLS_KEY = "JSON.create_id".freeze private_constant :CREATE_ID_TLS_KEY - def self.create_id - Thread.current[CREATE_ID_TLS_KEY] || DEFAULT_CREATE_ID - end - + # Sets create identifier, which is used to decide if the _json_create_ + # hook of a class should be called; initial value is +json_class+: + # JSON.create_id # => 'json_class' def self.create_id=(new_value) Thread.current[CREATE_ID_TLS_KEY] = new_value.dup.freeze end + # Returns the current create identifier. + # See also JSON.create_id=. + def self.create_id + Thread.current[CREATE_ID_TLS_KEY] || DEFAULT_CREATE_ID + end + NaN = 0.0/0 Infinity = 1.0/0 From fc7bd74843d73f8f65d0ab2f48e1c512f556da48 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 22 Dec 2020 14:21:33 +0900 Subject: [PATCH 0121/1010] Import test assertions from ruby/ruby --- Rakefile | 6 +- tests/lib/core_assertions.rb | 763 +++++++++++++++++++++++++++++++++++ tests/lib/envutil.rb | 365 +++++++++++++++++ tests/lib/find_executable.rb | 22 + tests/lib/helper.rb | 4 + 5 files changed, 1158 insertions(+), 2 deletions(-) create mode 100644 tests/lib/core_assertions.rb create mode 100644 tests/lib/envutil.rb create mode 100644 tests/lib/find_executable.rb create mode 100644 tests/lib/helper.rb diff --git a/Rakefile b/Rakefile index f48c377b2..de330b101 100644 --- a/Rakefile +++ b/Rakefile @@ -99,7 +99,8 @@ task(:set_env_pure) { ENV['JSON'] = 'pure' } UndocumentedTestTask.new do |t| t.name = 'do_test_pure' - t.libs << 'lib' << 'tests' + t.libs << 'lib' << 'tests' << 'tests/lib' + t.ruby_opts << "-rhelper" t.test_files = FileList['tests/*_test.rb'] t.verbose = true t.options = '-v' @@ -252,7 +253,8 @@ else UndocumentedTestTask.new do |t| t.name = 'do_test_ext' - t.libs << 'ext' << 'lib' << 'tests' + t.libs << 'lib' << 'tests' << "tests/lib" + t.ruby_opts << '-rhelper' t.test_files = FileList['tests/*_test.rb'] t.verbose = true t.options = '-v' diff --git a/tests/lib/core_assertions.rb b/tests/lib/core_assertions.rb new file mode 100644 index 000000000..abd0e4503 --- /dev/null +++ b/tests/lib/core_assertions.rb @@ -0,0 +1,763 @@ +# frozen_string_literal: true + +module Test + module Unit + module Assertions + def _assertions= n # :nodoc: + @_assertions = n + end + + def _assertions # :nodoc: + @_assertions ||= 0 + end + + ## + # Returns a proc that will output +msg+ along with the default message. + + def message msg = nil, ending = nil, &default + proc { + msg = msg.call.chomp(".") if Proc === msg + custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty? + "#{custom_message}#{default.call}#{ending || "."}" + } + end + end + + module CoreAssertions + if defined?(MiniTest) + require_relative '../../envutil' + # for ruby core testing + include MiniTest::Assertions + + # Compatibility hack for assert_raise + Test::Unit::AssertionFailedError = MiniTest::Assertion + else + module MiniTest + class Assertion < Exception; end + class Skip < Assertion; end + end + + require 'pp' + require_relative 'envutil' + include Test::Unit::Assertions + end + + def mu_pp(obj) #:nodoc: + obj.pretty_inspect.chomp + end + + def assert_file + AssertFile + end + + FailDesc = proc do |status, message = "", out = ""| + now = Time.now + proc do + EnvUtil.failure_description(status, now, message, out) + end + end + + def assert_in_out_err(args, test_stdin = "", test_stdout = [], test_stderr = [], message = nil, + success: nil, **opt) + args = Array(args).dup + args.insert((Hash === args[0] ? 1 : 0), '--disable=gems') + stdout, stderr, status = EnvUtil.invoke_ruby(args, test_stdin, true, true, **opt) + desc = FailDesc[status, message, stderr] + if block_given? + raise "test_stdout ignored, use block only or without block" if test_stdout != [] + raise "test_stderr ignored, use block only or without block" if test_stderr != [] + yield(stdout.lines.map {|l| l.chomp }, stderr.lines.map {|l| l.chomp }, status) + else + all_assertions(desc) do |a| + [["stdout", test_stdout, stdout], ["stderr", test_stderr, stderr]].each do |key, exp, act| + a.for(key) do + if exp.is_a?(Regexp) + assert_match(exp, act) + elsif exp.all? {|e| String === e} + assert_equal(exp, act.lines.map {|l| l.chomp }) + else + assert_pattern_list(exp, act) + end + end + end + unless success.nil? + a.for("success?") do + if success + assert_predicate(status, :success?) + else + assert_not_predicate(status, :success?) + end + end + end + end + status + end + end + + if defined?(RubyVM::InstructionSequence) + def syntax_check(code, fname, line) + code = code.dup.force_encoding(Encoding::UTF_8) + RubyVM::InstructionSequence.compile(code, fname, fname, line) + :ok + ensure + raise if SyntaxError === $! + end + else + def syntax_check(code, fname, line) + code = code.b + code.sub!(/\A(?:\xef\xbb\xbf)?(\s*\#.*$)*(\n)?/n) { + "#$&#{"\n" if $1 && !$2}BEGIN{throw tag, :ok}\n" + } + code = code.force_encoding(Encoding::UTF_8) + catch {|tag| eval(code, binding, fname, line - 1)} + end + end + + def assert_no_memory_leak(args, prepare, code, message=nil, limit: 2.0, rss: false, **opt) + # TODO: consider choosing some appropriate limit for MJIT and stop skipping this once it does not randomly fail + pend 'assert_no_memory_leak may consider MJIT memory usage as leak' if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? + + require_relative '../../memory_status' + raise MiniTest::Skip, "unsupported platform" unless defined?(Memory::Status) + + token = "\e[7;1m#{$$.to_s}:#{Time.now.strftime('%s.%L')}:#{rand(0x10000).to_s(16)}:\e[m" + token_dump = token.dump + token_re = Regexp.quote(token) + envs = args.shift if Array === args and Hash === args.first + args = [ + "--disable=gems", + "-r", File.expand_path("../../../memory_status", __FILE__), + *args, + "-v", "-", + ] + if defined? Memory::NO_MEMORY_LEAK_ENVS then + envs ||= {} + newenvs = envs.merge(Memory::NO_MEMORY_LEAK_ENVS) { |_, _, _| break } + envs = newenvs if newenvs + end + args.unshift(envs) if envs + cmd = [ + 'END {STDERR.puts '"#{token_dump}"'"FINAL=#{Memory::Status.new}"}', + prepare, + 'STDERR.puts('"#{token_dump}"'"START=#{$initial_status = Memory::Status.new}")', + '$initial_size = $initial_status.size', + code, + 'GC.start', + ].join("\n") + _, err, status = EnvUtil.invoke_ruby(args, cmd, true, true, **opt) + before = err.sub!(/^#{token_re}START=(\{.*\})\n/, '') && Memory::Status.parse($1) + after = err.sub!(/^#{token_re}FINAL=(\{.*\})\n/, '') && Memory::Status.parse($1) + assert(status.success?, FailDesc[status, message, err]) + ([:size, (rss && :rss)] & after.members).each do |n| + b = before[n] + a = after[n] + next unless a > 0 and b > 0 + assert_operator(a.fdiv(b), :<, limit, message(message) {"#{n}: #{b} => #{a}"}) + end + rescue LoadError + pend + end + + # :call-seq: + # assert_nothing_raised( *args, &block ) + # + #If any exceptions are given as arguments, the assertion will + #fail if one of those exceptions are raised. Otherwise, the test fails + #if any exceptions are raised. + # + #The final argument may be a failure message. + # + # assert_nothing_raised RuntimeError do + # raise Exception #Assertion passes, Exception is not a RuntimeError + # end + # + # assert_nothing_raised do + # raise Exception #Assertion fails + # end + def assert_nothing_raised(*args) + self._assertions += 1 + if Module === args.last + msg = nil + else + msg = args.pop + end + begin + line = __LINE__; yield + rescue MiniTest::Skip + raise + rescue Exception => e + bt = e.backtrace + as = e.instance_of?(MiniTest::Assertion) + if as + ans = /\A#{Regexp.quote(__FILE__)}:#{line}:in /o + bt.reject! {|ln| ans =~ ln} + end + if ((args.empty? && !as) || + args.any? {|a| a.instance_of?(Module) ? e.is_a?(a) : e.class == a }) + msg = message(msg) { + "Exception raised:\n<#{mu_pp(e)}>\n" + + "Backtrace:\n" + + e.backtrace.map{|frame| " #{frame}"}.join("\n") + } + raise MiniTest::Assertion, msg.call, bt + else + raise + end + end + end + + def prepare_syntax_check(code, fname = nil, mesg = nil, verbose: nil) + fname ||= caller_locations(2, 1)[0] + mesg ||= fname.to_s + verbose, $VERBOSE = $VERBOSE, verbose + case + when Array === fname + fname, line = *fname + when defined?(fname.path) && defined?(fname.lineno) + fname, line = fname.path, fname.lineno + else + line = 1 + end + yield(code, fname, line, message(mesg) { + if code.end_with?("\n") + "```\n#{code}```\n" + else + "```\n#{code}\n```\n""no-newline" + end + }) + ensure + $VERBOSE = verbose + end + + def assert_valid_syntax(code, *args, **opt) + prepare_syntax_check(code, *args, **opt) do |src, fname, line, mesg| + yield if defined?(yield) + assert_nothing_raised(SyntaxError, mesg) do + assert_equal(:ok, syntax_check(src, fname, line), mesg) + end + end + end + + def assert_normal_exit(testsrc, message = '', child_env: nil, **opt) + assert_valid_syntax(testsrc, caller_locations(1, 1)[0]) + if child_env + child_env = [child_env] + else + child_env = [] + end + out, _, status = EnvUtil.invoke_ruby(child_env + %W'-W0', testsrc, true, :merge_to_stdout, **opt) + assert !status.signaled?, FailDesc[status, message, out] + end + + def assert_ruby_status(args, test_stdin="", message=nil, **opt) + out, _, status = EnvUtil.invoke_ruby(args, test_stdin, true, :merge_to_stdout, **opt) + desc = FailDesc[status, message, out] + assert(!status.signaled?, desc) + message ||= "ruby exit status is not success:" + assert(status.success?, desc) + end + + ABORT_SIGNALS = Signal.list.values_at(*%w"ILL ABRT BUS SEGV TERM") + + def separated_runner(out = nil) + out = out ? IO.new(out, 'w') : STDOUT + at_exit { + out.puts [Marshal.dump($!)].pack('m'), "assertions=\#{self._assertions}" + } + Test::Unit::Runner.class_variable_set(:@@stop_auto_run, true) if defined?(Test::Unit::Runner) + end + + def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **opt) + unless file and line + loc, = caller_locations(1,1) + file ||= loc.path + line ||= loc.lineno + end + capture_stdout = true + unless /mswin|mingw/ =~ RUBY_PLATFORM + capture_stdout = false + opt[:out] = MiniTest::Unit.output if defined?(MiniTest::Unit) + res_p, res_c = IO.pipe + opt[res_c.fileno] = res_c.fileno + end + src = < marshal_error + ignore_stderr = nil + res = nil + end + if res and !(SystemExit === res) + if bt = res.backtrace + bt.each do |l| + l.sub!(/\A-:(\d+)/){"#{file}:#{line + $1.to_i}"} + end + bt.concat(caller) + else + res.set_backtrace(caller) + end + raise res + end + + # really is it succeed? + unless ignore_stderr + # the body of assert_separately must not output anything to detect error + assert(stderr.empty?, FailDesc[status, "assert_separately failed with error message", stderr]) + end + assert(status.success?, FailDesc[status, "assert_separately failed", stderr]) + raise marshal_error if marshal_error + end + + # Run Ractor-related test without influencing the main test suite + def assert_ractor(src, args: [], require: nil, require_relative: nil, file: nil, line: nil, ignore_stderr: nil, **opt) + return unless defined?(Ractor) + + require = "require #{require.inspect}" if require + if require_relative + dir = File.dirname(caller_locations[0,1][0].absolute_path) + full_path = File.expand_path(require_relative, dir) + require = "#{require}; require #{full_path.inspect}" + end + + assert_separately(args, file, line, <<~RUBY, ignore_stderr: ignore_stderr, **opt) + #{require} + previous_verbose = $VERBOSE + $VERBOSE = nil + Ractor.new {} # trigger initial warning + $VERBOSE = previous_verbose + #{src} + RUBY + end + + # :call-seq: + # assert_throw( tag, failure_message = nil, &block ) + # + #Fails unless the given block throws +tag+, returns the caught + #value otherwise. + # + #An optional failure message may be provided as the final argument. + # + # tag = Object.new + # assert_throw(tag, "#{tag} was not thrown!") do + # throw tag + # end + def assert_throw(tag, msg = nil) + ret = catch(tag) do + begin + yield(tag) + rescue UncaughtThrowError => e + thrown = e.tag + end + msg = message(msg) { + "Expected #{mu_pp(tag)} to have been thrown"\ + "#{%Q[, not #{thrown}] if thrown}" + } + assert(false, msg) + end + assert(true) + ret + end + + # :call-seq: + # assert_raise( *args, &block ) + # + #Tests if the given block raises an exception. Acceptable exception + #types may be given as optional arguments. If the last argument is a + #String, it will be used as the error message. + # + # assert_raise do #Fails, no Exceptions are raised + # end + # + # assert_raise NameError do + # puts x #Raises NameError, so assertion succeeds + # end + def assert_raise(*exp, &b) + case exp.last + when String, Proc + msg = exp.pop + end + + begin + yield + rescue MiniTest::Skip => e + return e if exp.include? MiniTest::Skip + raise e + rescue Exception => e + expected = exp.any? { |ex| + if ex.instance_of? Module then + e.kind_of? ex + else + e.instance_of? ex + end + } + + assert expected, proc { + flunk(message(msg) {"#{mu_pp(exp)} exception expected, not #{mu_pp(e)}"}) + } + + return e + ensure + unless e + exp = exp.first if exp.size == 1 + + flunk(message(msg) {"#{mu_pp(exp)} expected but nothing was raised"}) + end + end + end + + # :call-seq: + # assert_raise_with_message(exception, expected, msg = nil, &block) + # + #Tests if the given block raises an exception with the expected + #message. + # + # assert_raise_with_message(RuntimeError, "foo") do + # nil #Fails, no Exceptions are raised + # end + # + # assert_raise_with_message(RuntimeError, "foo") do + # raise ArgumentError, "foo" #Fails, different Exception is raised + # end + # + # assert_raise_with_message(RuntimeError, "foo") do + # raise "bar" #Fails, RuntimeError is raised but the message differs + # end + # + # assert_raise_with_message(RuntimeError, "foo") do + # raise "foo" #Raises RuntimeError with the message, so assertion succeeds + # end + def assert_raise_with_message(exception, expected, msg = nil, &block) + case expected + when String + assert = :assert_equal + when Regexp + assert = :assert_match + else + raise TypeError, "Expected #{expected.inspect} to be a kind of String or Regexp, not #{expected.class}" + end + + ex = m = nil + EnvUtil.with_default_internal(expected.encoding) do + ex = assert_raise(exception, msg || proc {"Exception(#{exception}) with message matches to #{expected.inspect}"}) do + yield + end + m = ex.message + end + msg = message(msg, "") {"Expected Exception(#{exception}) was raised, but the message doesn't match"} + + if assert == :assert_equal + assert_equal(expected, m, msg) + else + msg = message(msg) { "Expected #{mu_pp expected} to match #{mu_pp m}" } + assert expected =~ m, msg + block.binding.eval("proc{|_|$~=_}").call($~) + end + ex + end + + MINI_DIR = File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), "minitest") #:nodoc: + + # :call-seq: + # assert(test, [failure_message]) + # + #Tests if +test+ is true. + # + #+msg+ may be a String or a Proc. If +msg+ is a String, it will be used + #as the failure message. Otherwise, the result of calling +msg+ will be + #used as the message if the assertion fails. + # + #If no +msg+ is given, a default message will be used. + # + # assert(false, "This was expected to be true") + def assert(test, *msgs) + case msg = msgs.first + when String, Proc + when nil + msgs.shift + else + bt = caller.reject { |s| s.start_with?(MINI_DIR) } + raise ArgumentError, "assertion message must be String or Proc, but #{msg.class} was given.", bt + end unless msgs.empty? + super + end + + # :call-seq: + # assert_respond_to( object, method, failure_message = nil ) + # + #Tests if the given Object responds to +method+. + # + #An optional failure message may be provided as the final argument. + # + # assert_respond_to("hello", :reverse) #Succeeds + # assert_respond_to("hello", :does_not_exist) #Fails + def assert_respond_to(obj, (meth, *priv), msg = nil) + unless priv.empty? + msg = message(msg) { + "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}#{" privately" if priv[0]}" + } + return assert obj.respond_to?(meth, *priv), msg + end + #get rid of overcounting + if caller_locations(1, 1)[0].path.start_with?(MINI_DIR) + return if obj.respond_to?(meth) + end + super(obj, meth, msg) + end + + # :call-seq: + # assert_not_respond_to( object, method, failure_message = nil ) + # + #Tests if the given Object does not respond to +method+. + # + #An optional failure message may be provided as the final argument. + # + # assert_not_respond_to("hello", :reverse) #Fails + # assert_not_respond_to("hello", :does_not_exist) #Succeeds + def assert_not_respond_to(obj, (meth, *priv), msg = nil) + unless priv.empty? + msg = message(msg) { + "Expected #{mu_pp(obj)} (#{obj.class}) to not respond to ##{meth}#{" privately" if priv[0]}" + } + return assert !obj.respond_to?(meth, *priv), msg + end + #get rid of overcounting + if caller_locations(1, 1)[0].path.start_with?(MINI_DIR) + return unless obj.respond_to?(meth) + end + refute_respond_to(obj, meth, msg) + end + + # pattern_list is an array which contains regexp and :*. + # :* means any sequence. + # + # pattern_list is anchored. + # Use [:*, regexp, :*] for non-anchored match. + def assert_pattern_list(pattern_list, actual, message=nil) + rest = actual + anchored = true + pattern_list.each_with_index {|pattern, i| + if pattern == :* + anchored = false + else + if anchored + match = /\A#{pattern}/.match(rest) + else + match = pattern.match(rest) + end + unless match + msg = message(msg) { + expect_msg = "Expected #{mu_pp pattern}\n" + if /\n[^\n]/ =~ rest + actual_mesg = +"to match\n" + rest.scan(/.*\n+/) { + actual_mesg << ' ' << $&.inspect << "+\n" + } + actual_mesg.sub!(/\+\n\z/, '') + else + actual_mesg = "to match " + mu_pp(rest) + end + actual_mesg << "\nafter #{i} patterns with #{actual.length - rest.length} characters" + expect_msg + actual_mesg + } + assert false, msg + end + rest = match.post_match + anchored = true + end + } + if anchored + assert_equal("", rest) + end + end + + def assert_warning(pat, msg = nil) + result = nil + stderr = EnvUtil.with_default_internal(pat.encoding) { + EnvUtil.verbose_warning { + result = yield + } + } + msg = message(msg) {diff pat, stderr} + assert(pat === stderr, msg) + result + end + + def assert_warn(*args) + assert_warning(*args) {$VERBOSE = false; yield} + end + + def assert_deprecated_warning(mesg = /deprecated/) + assert_warning(mesg) do + Warning[:deprecated] = true + yield + end + end + + def assert_deprecated_warn(mesg = /deprecated/) + assert_warn(mesg) do + Warning[:deprecated] = true + yield + end + end + + class << (AssertFile = Struct.new(:failure_message).new) + include CoreAssertions + def assert_file_predicate(predicate, *args) + if /\Anot_/ =~ predicate + predicate = $' + neg = " not" + end + result = File.__send__(predicate, *args) + result = !result if neg + mesg = "Expected file ".dup << args.shift.inspect + mesg << "#{neg} to be #{predicate}" + mesg << mu_pp(args).sub(/\A\[(.*)\]\z/m, '(\1)') unless args.empty? + mesg << " #{failure_message}" if failure_message + assert(result, mesg) + end + alias method_missing assert_file_predicate + + def for(message) + clone.tap {|a| a.failure_message = message} + end + end + + class AllFailures + attr_reader :failures + + def initialize + @count = 0 + @failures = {} + end + + def for(key) + @count += 1 + yield + rescue Exception => e + @failures[key] = [@count, e] + end + + def foreach(*keys) + keys.each do |key| + @count += 1 + begin + yield key + rescue Exception => e + @failures[key] = [@count, e] + end + end + end + + def message + i = 0 + total = @count.to_s + fmt = "%#{total.size}d" + @failures.map {|k, (n, v)| + v = v.message + "\n#{i+=1}. [#{fmt%n}/#{total}] Assertion for #{k.inspect}\n#{v.b.gsub(/^/, ' | ').force_encoding(v.encoding)}" + }.join("\n") + end + + def pass? + @failures.empty? + end + end + + # threads should respond to shift method. + # Array can be used. + def assert_join_threads(threads, message = nil) + errs = [] + values = [] + while th = threads.shift + begin + values << th.value + rescue Exception + errs << [th, $!] + th = nil + end + end + values + ensure + if th&.alive? + th.raise(Timeout::Error.new) + th.join rescue errs << [th, $!] + end + if !errs.empty? + msg = "exceptions on #{errs.length} threads:\n" + + errs.map {|t, err| + "#{t.inspect}:\n" + + RUBY_VERSION >= "2.5.0" ? err.full_message(highlight: false, order: :top) : err.message + }.join("\n---\n") + if message + msg = "#{message}\n#{msg}" + end + raise MiniTest::Assertion, msg + end + end + + def assert_all_assertions(msg = nil) + all = AllFailures.new + yield all + ensure + assert(all.pass?, message(msg) {all.message.chomp(".")}) + end + alias all_assertions assert_all_assertions + + def message(msg = nil, *args, &default) # :nodoc: + if Proc === msg + super(nil, *args) do + ary = [msg.call, (default.call if default)].compact.reject(&:empty?) + if 1 < ary.length + ary[0...-1] = ary[0...-1].map {|str| str.sub(/(? Date: Tue, 22 Dec 2020 15:03:22 +0900 Subject: [PATCH 0122/1010] skip ractor_test with JRuby --- tests/ractor_test.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ractor_test.rb b/tests/ractor_test.rb index 96d1528c5..cbca9d222 100644 --- a/tests/ractor_test.rb +++ b/tests/ractor_test.rb @@ -5,6 +5,7 @@ class JSONInRactorTest < Test::Unit::TestCase def setup + skip if RUBY_PLATFORM =~ /java/ skip unless defined? Ractor end From 795b274d1ce497172913ec31ab6ecb671ee9e766 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 22 Dec 2020 15:25:56 +0900 Subject: [PATCH 0123/1010] Change the condition for Ractor --- tests/ractor_test.rb | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/ractor_test.rb b/tests/ractor_test.rb index cbca9d222..71105e55e 100644 --- a/tests/ractor_test.rb +++ b/tests/ractor_test.rb @@ -4,11 +4,6 @@ require 'test_helper' class JSONInRactorTest < Test::Unit::TestCase - def setup - skip if RUBY_PLATFORM =~ /java/ - skip unless defined? Ractor - end - def test_generate assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") begin; @@ -32,4 +27,4 @@ def test_generate assert_equal(JSON.parse(expected_json), r.take) end; end -end +end if defined?(Ractor) From 147cbfa499f411851cb509a28b3f093eb838a08c Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 22 Dec 2020 15:57:19 +0900 Subject: [PATCH 0124/1010] Use 2.7 in Travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0aaa2fbb5..3824394a0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ rvm: - 2.4 - 2.5 - 2.6 - - 2.7.0-preview3 + - 2.7 - ruby-head - jruby-9.1 # Ruby 2.3 - jruby-9.2 # Ruby 2.5 From dece2379dcc8d8ebbeaf59d6a4fd7eb97b2c3cde Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 22 Dec 2020 16:09:52 +0900 Subject: [PATCH 0125/1010] Guard for Ruby 3.0 --- tests/lib/envutil.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lib/envutil.rb b/tests/lib/envutil.rb index 323d7100d..63e23ba95 100644 --- a/tests/lib/envutil.rb +++ b/tests/lib/envutil.rb @@ -53,7 +53,7 @@ def capture_global_values @original_internal_encoding = Encoding.default_internal @original_external_encoding = Encoding.default_external @original_verbose = $VERBOSE - @original_warning = %i[deprecated experimental].to_h {|i| [i, Warning[i]]} + @original_warning = %i[deprecated experimental].to_h {|i| [i, Warning[i]]} if RUBY_VERSION > "2.7" end end From d1a341fafdf86202acfce6b95aed7a3e65b63ac8 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 22 Dec 2020 16:56:41 +0900 Subject: [PATCH 0126/1010] Skip tests with JRuby platform --- tests/json_generator_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/json_generator_test.rb b/tests/json_generator_test.rb index 5bafc3ead..f31b6b290 100644 --- a/tests/json_generator_test.rb +++ b/tests/json_generator_test.rb @@ -232,7 +232,7 @@ def test_buffer_initial_length end def test_gc - if respond_to?(:assert_in_out_err) + if respond_to?(:assert_in_out_err) && !(RUBY_PLATFORM =~ /java/) assert_in_out_err(%w[-rjson --disable-gems], <<-EOS, [], []) bignum_too_long_to_embed_as_string = 1234567890123456789012345 expect = bignum_too_long_to_embed_as_string.to_s From 951c7d108af91612b81790e7b31e831998c91b92 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 22 Dec 2020 16:57:00 +0900 Subject: [PATCH 0127/1010] Drop to tests with too old Ruby --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3824394a0..38a858418 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,6 @@ language: ruby # Specify which ruby versions you wish to run your tests on, each version will be used rvm: - - 2.1 - - 2.2 - 2.3 - 2.4 - 2.5 From 0c446fa9b7c57647b11085304a532da70a64e231 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 22 Dec 2020 17:41:47 +0900 Subject: [PATCH 0128/1010] Workaround for JRuby --- lib/json/common.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/json/common.rb b/lib/json/common.rb index d58c7eae9..3e390f2d6 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -71,6 +71,7 @@ def generator=(generator) # :nodoc: end self.state = generator::State const_set :State, self.state + const_set :SAFE_STATE_PROTOTYPE, State.new # for JRuby ensure $VERBOSE = old end From ea58503ee5ea96dece5abe0d582c258a6a11840e Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 22 Dec 2020 18:16:29 +0900 Subject: [PATCH 0129/1010] Bump version to 2.5.0 --- CHANGES.md | 4 ++++ VERSION | 2 +- lib/json/version.rb | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b5eeb4f15..4f536da13 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## 2020-12-22 (2.5.0) + +* Ready to Ractor-safe at Ruby 3.0. + ## 2020-12-17 (2.4.1) * Restore version.rb with 2.4.1 diff --git a/VERSION b/VERSION index 005119baa..437459cd9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.4.1 +2.5.0 diff --git a/lib/json/version.rb b/lib/json/version.rb index 5502a4d27..2a0a6e8e5 100644 --- a/lib/json/version.rb +++ b/lib/json/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module JSON # JSON version - VERSION = '2.4.1' + VERSION = '2.5.0' VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc: VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc: VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: From 5910beff3fc932a6a62dcd214583041e119da082 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 22 Dec 2020 20:23:59 +0900 Subject: [PATCH 0130/1010] Added backword compatible for < json-2.5 --- lib/json/common.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/json/common.rb b/lib/json/common.rb index 3e390f2d6..ea46896fc 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -72,6 +72,8 @@ def generator=(generator) # :nodoc: self.state = generator::State const_set :State, self.state const_set :SAFE_STATE_PROTOTYPE, State.new # for JRuby + const_set :FAST_STATE_PROTOTYPE, create_fast_state + const_set :PRETTY_STATE_PROTOTYPE, create_pretty_state ensure $VERBOSE = old end From 9dc86f903bb07ca3c4e1f654eefc86e1d0dda400 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 22 Dec 2020 21:48:50 +0900 Subject: [PATCH 0131/1010] Bump version to 2.5.1 --- CHANGES.md | 4 ++++ VERSION | 2 +- lib/json/version.rb | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4f536da13..04159a0f4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## 2020-12-22 (2.5.1) + +* Restore the compatibility for constants of JSON class. + ## 2020-12-22 (2.5.0) * Ready to Ractor-safe at Ruby 3.0. diff --git a/VERSION b/VERSION index 437459cd9..73462a5a1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.5.0 +2.5.1 diff --git a/lib/json/version.rb b/lib/json/version.rb index 2a0a6e8e5..7a72272bd 100644 --- a/lib/json/version.rb +++ b/lib/json/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module JSON # JSON version - VERSION = '2.5.0' + VERSION = '2.5.1' VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc: VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc: VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: From dc4b62424f7bf10450b884cce9ebb7f8c6d0891c Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Wed, 23 Dec 2020 11:20:13 +0900 Subject: [PATCH 0132/1010] Fix incorrect `#` position in API doc This change fixes an incorrect `#` position in the API documentation of the `JSON` module. --- lib/json.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/json.rb b/lib/json.rb index aeb9774ee..1e64bfcb1 100644 --- a/lib/json.rb +++ b/lib/json.rb @@ -493,7 +493,7 @@ # json = JSON.generate(ruby0) # {"json_class":"Customer","v":["Dave","123 Main"]} # ruby1 = JSON.parse(json, create_additions: true) # # # ruby1.class # Customer - # +# # \Symbol: # require 'json/add/symbol' # ruby0 = :foo # foo From 3895711dd88fbad592652c99409af30fa1ae04fc Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 23 Dec 2020 13:05:19 +0900 Subject: [PATCH 0133/1010] Added helper task for test suite sync --- Rakefile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Rakefile b/Rakefile index de330b101..813a6d9b9 100644 --- a/Rakefile +++ b/Rakefile @@ -334,5 +334,12 @@ else task :release => :build end +task :sync_tool do + require 'fileutils' + FileUtils.cp "../../ruby/ruby/tool/lib/test/unit/core_assertions.rb", "./tests/lib" + FileUtils.cp "../../ruby//ruby/tool/lib/envutil.rb", "./tests/lib" + FileUtils.cp "../../ruby//ruby/tool/lib/find_executable.rb", "./tests/lib" +end + desc "Compile in the the source directory" task :default => [ :clean, :test ] From 9913ed2930bd79301239dc55773c4e7b74c25ea9 Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Mon, 28 Dec 2020 12:19:19 +0900 Subject: [PATCH 0134/1010] CI: Stop using Travis CI --- .travis.yml | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 38a858418..000000000 --- a/.travis.yml +++ /dev/null @@ -1,21 +0,0 @@ -# Passes arguments to bundle install (http://gembundler.com/man/bundle-install.1.html) -#bundler_args: --binstubs -language: ruby - -# Specify which ruby versions you wish to run your tests on, each version will be used -rvm: - - 2.3 - - 2.4 - - 2.5 - - 2.6 - - 2.7 - - ruby-head - - jruby-9.1 # Ruby 2.3 - - jruby-9.2 # Ruby 2.5 - - truffleruby -matrix: - allow_failures: - - rvm: ruby-head - - rvm: truffleruby -script: "bundle exec rake" -sudo: false From 44476539de1f098bd5a77f236a0c9a44d6a5ea5c Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Mon, 28 Dec 2020 12:19:38 +0900 Subject: [PATCH 0135/1010] CI: Use GitHub Actions for CI --- .github/workflows/ci.yml | 55 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..1c240d454 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,55 @@ +name: CI + +on: +- push +- pull_request + +jobs: + host: + name: ${{ matrix.os }} ${{ matrix.ruby }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - ubuntu-20.04 + - ubuntu-18.04 + - macos-11.0 + - macos-10.15 + - windows-latest + ruby: + - 3.0 + - 2.7 + - 2.6 + - 2.5 + - 2.4 + - 2.3 + - debug + include: + - { os: windows-latest , ruby: mingw } + - { os: windows-latest , ruby: mswin } + - { os: ubuntu-latest , ruby: jruby-9.1 } # Ruby 2.3 + - { os: ubuntu-latest , ruby: jruby-9.2 } # Ruby 2.5 + - { os: ubuntu-latest , ruby: truffleruby } + exclude: + - { os: windows-latest , ruby: 3.0 } + - { os: windows-latest , ruby: debug } + + steps: + - uses: actions/checkout@v2 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + + - run: bundle install + + - run: rake compile + + - run: rake test + + - run: rake build + + - run: gem install pkg/*.gem + From e5926922e3e8dff3b446ec16e7cd5ccdc922ac4b Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Mon, 28 Dec 2020 12:25:39 +0900 Subject: [PATCH 0136/1010] CI: Install ragel on macOS --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c240d454..e8ce3d991 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,9 @@ jobs: with: ruby-version: ${{ matrix.ruby }} + - run: brew install ragel + if: ${{ startsWith(matrix.os, 'macos') }} + - run: bundle install - run: rake compile From 3b13e75b11f3790f16149a21461767b67dece9f0 Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Mon, 28 Dec 2020 12:29:04 +0900 Subject: [PATCH 0137/1010] CI: Install ragel on Ubuntu --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e8ce3d991..2215eb19e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,9 @@ jobs: with: ruby-version: ${{ matrix.ruby }} + - run: sudo apt install ragel + if: ${{ startsWith(matrix.os, 'ubuntu') }} + - run: brew install ragel if: ${{ startsWith(matrix.os, 'macos') }} From 949cdaa6006eb4f95d3870071025d3bf35e525f9 Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Mon, 28 Dec 2020 12:30:06 +0900 Subject: [PATCH 0138/1010] CI: Specify the version 3.0 as a String --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2215eb19e..c278a1cc8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: - macos-10.15 - windows-latest ruby: - - 3.0 + - '3.0' - 2.7 - 2.6 - 2.5 @@ -32,7 +32,7 @@ jobs: - { os: ubuntu-latest , ruby: jruby-9.2 } # Ruby 2.5 - { os: ubuntu-latest , ruby: truffleruby } exclude: - - { os: windows-latest , ruby: 3.0 } + - { os: windows-latest , ruby: '3.0' } - { os: windows-latest , ruby: debug } steps: From d49c5de49e54a5ad3f6fcf587f98d63266ef9439 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Mon, 28 Dec 2020 16:24:13 +0900 Subject: [PATCH 0139/1010] Use better condition for the old ruby versions --- tests/lib/envutil.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/lib/envutil.rb b/tests/lib/envutil.rb index 63e23ba95..937e1128a 100644 --- a/tests/lib/envutil.rb +++ b/tests/lib/envutil.rb @@ -53,7 +53,7 @@ def capture_global_values @original_internal_encoding = Encoding.default_internal @original_external_encoding = Encoding.default_external @original_verbose = $VERBOSE - @original_warning = %i[deprecated experimental].to_h {|i| [i, Warning[i]]} if RUBY_VERSION > "2.7" + @original_warning = defined?(Warning.[]) ? %i[deprecated experimental].to_h {|i| [i, Warning[i]]} : nil end end @@ -211,7 +211,7 @@ def flush; end ensure stderr, $stderr = $stderr, stderr $VERBOSE = EnvUtil.original_verbose - EnvUtil.original_warning.each {|i, v| Warning[i] = v} + EnvUtil.original_warning&.each {|i, v| Warning[i] = v} end module_function :verbose_warning From 96b106ebe16f6d6315d4bad77ec8d302458b2e0e Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Mon, 28 Dec 2020 12:19:19 +0900 Subject: [PATCH 0140/1010] CI: Stop using Travis CI --- .travis.yml | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 38a858418..000000000 --- a/.travis.yml +++ /dev/null @@ -1,21 +0,0 @@ -# Passes arguments to bundle install (http://gembundler.com/man/bundle-install.1.html) -#bundler_args: --binstubs -language: ruby - -# Specify which ruby versions you wish to run your tests on, each version will be used -rvm: - - 2.3 - - 2.4 - - 2.5 - - 2.6 - - 2.7 - - ruby-head - - jruby-9.1 # Ruby 2.3 - - jruby-9.2 # Ruby 2.5 - - truffleruby -matrix: - allow_failures: - - rvm: ruby-head - - rvm: truffleruby -script: "bundle exec rake" -sudo: false From 2f2bf5dedfe1a4fe4d9f5e395f058a2dc4ede84f Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Mon, 28 Dec 2020 12:19:38 +0900 Subject: [PATCH 0141/1010] CI: Use GitHub Actions for CI --- .github/workflows/ci.yml | 55 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..1c240d454 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,55 @@ +name: CI + +on: +- push +- pull_request + +jobs: + host: + name: ${{ matrix.os }} ${{ matrix.ruby }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - ubuntu-20.04 + - ubuntu-18.04 + - macos-11.0 + - macos-10.15 + - windows-latest + ruby: + - 3.0 + - 2.7 + - 2.6 + - 2.5 + - 2.4 + - 2.3 + - debug + include: + - { os: windows-latest , ruby: mingw } + - { os: windows-latest , ruby: mswin } + - { os: ubuntu-latest , ruby: jruby-9.1 } # Ruby 2.3 + - { os: ubuntu-latest , ruby: jruby-9.2 } # Ruby 2.5 + - { os: ubuntu-latest , ruby: truffleruby } + exclude: + - { os: windows-latest , ruby: 3.0 } + - { os: windows-latest , ruby: debug } + + steps: + - uses: actions/checkout@v2 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + + - run: bundle install + + - run: rake compile + + - run: rake test + + - run: rake build + + - run: gem install pkg/*.gem + From 2ee35ec513d0f46d92043a130ce0a3585a614820 Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Mon, 28 Dec 2020 12:25:39 +0900 Subject: [PATCH 0142/1010] CI: Install ragel on macOS --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c240d454..e8ce3d991 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,9 @@ jobs: with: ruby-version: ${{ matrix.ruby }} + - run: brew install ragel + if: ${{ startsWith(matrix.os, 'macos') }} + - run: bundle install - run: rake compile From b496b978006ba585ae09a5224f56432c88679b9d Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Mon, 28 Dec 2020 12:29:04 +0900 Subject: [PATCH 0143/1010] CI: Install ragel on Ubuntu --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e8ce3d991..2215eb19e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,9 @@ jobs: with: ruby-version: ${{ matrix.ruby }} + - run: sudo apt install ragel + if: ${{ startsWith(matrix.os, 'ubuntu') }} + - run: brew install ragel if: ${{ startsWith(matrix.os, 'macos') }} From 357272624c45e8205920adde0339600cff0700b9 Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Mon, 28 Dec 2020 12:30:06 +0900 Subject: [PATCH 0144/1010] CI: Specify the version 3.0 as a String --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2215eb19e..c278a1cc8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: - macos-10.15 - windows-latest ruby: - - 3.0 + - '3.0' - 2.7 - 2.6 - 2.5 @@ -32,7 +32,7 @@ jobs: - { os: ubuntu-latest , ruby: jruby-9.2 } # Ruby 2.5 - { os: ubuntu-latest , ruby: truffleruby } exclude: - - { os: windows-latest , ruby: 3.0 } + - { os: windows-latest , ruby: '3.0' } - { os: windows-latest , ruby: debug } steps: From f3987693324571a575e9de557ad1f3455d1f5329 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 18 Nov 2020 11:33:42 +0100 Subject: [PATCH 0145/1010] Refactor json_string_unescape --- ext/json/ext/parser/parser.c | 4779 ++++++++++++++++++++------------- ext/json/ext/parser/parser.h | 2 +- ext/json/ext/parser/parser.rl | 48 +- 3 files changed, 3007 insertions(+), 1822 deletions(-) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 29b5674d9..241ec0d26 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -9,14 +9,14 @@ static void enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...) { - va_list args; - VALUE mesg; + va_list args; + VALUE mesg; - va_start(args, fmt); - mesg = rb_enc_vsprintf(enc, fmt, args); - va_end(args); + va_start(args, fmt); + mesg = rb_enc_vsprintf(enc, fmt, args); + va_end(args); - rb_exc_raise(rb_exc_new3(exc, mesg)); + rb_exc_raise(rb_exc_new3(exc, mesg)); } # define rb_enc_raise enc_raise # endif @@ -28,2152 +28,3305 @@ enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...) /* unicode */ static const signed char digit_values[256] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, - -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, + -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1 }; static UTF32 unescape_unicode(const unsigned char *p) { - signed char b; - UTF32 result = 0; - b = digit_values[p[0]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - b = digit_values[p[1]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - b = digit_values[p[2]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - b = digit_values[p[3]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - return result; + signed char b; + UTF32 result = 0; + b = digit_values[p[0]]; + if (b < 0) return UNI_REPLACEMENT_CHAR; + result = (result << 4) | (unsigned char)b; + b = digit_values[p[1]]; + if (b < 0) return UNI_REPLACEMENT_CHAR; + result = (result << 4) | (unsigned char)b; + b = digit_values[p[2]]; + if (b < 0) return UNI_REPLACEMENT_CHAR; + result = (result << 4) | (unsigned char)b; + b = digit_values[p[3]]; + if (b < 0) return UNI_REPLACEMENT_CHAR; + result = (result << 4) | (unsigned char)b; + return result; } static int convert_UTF32_to_UTF8(char *buf, UTF32 ch) { - int len = 1; - if (ch <= 0x7F) { - buf[0] = (char) ch; - } else if (ch <= 0x07FF) { - buf[0] = (char) ((ch >> 6) | 0xC0); - buf[1] = (char) ((ch & 0x3F) | 0x80); - len++; - } else if (ch <= 0xFFFF) { - buf[0] = (char) ((ch >> 12) | 0xE0); - buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80); - buf[2] = (char) ((ch & 0x3F) | 0x80); - len += 2; - } else if (ch <= 0x1fffff) { - buf[0] =(char) ((ch >> 18) | 0xF0); - buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80); - buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80); - buf[3] =(char) ((ch & 0x3F) | 0x80); - len += 3; - } else { - buf[0] = '?'; - } - return len; + int len = 1; + if (ch <= 0x7F) { + buf[0] = (char) ch; + } else if (ch <= 0x07FF) { + buf[0] = (char) ((ch >> 6) | 0xC0); + buf[1] = (char) ((ch & 0x3F) | 0x80); + len++; + } else if (ch <= 0xFFFF) { + buf[0] = (char) ((ch >> 12) | 0xE0); + buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80); + buf[2] = (char) ((ch & 0x3F) | 0x80); + len += 2; + } else if (ch <= 0x1fffff) { + buf[0] =(char) ((ch >> 18) | 0xF0); + buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80); + buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80); + buf[3] =(char) ((ch & 0x3F) | 0x80); + len += 3; + } else { + buf[0] = '?'; + } + return len; } static VALUE mJSON, mExt, cParser, eParserError, eNestingError; static VALUE CNaN, CInfinity, CMinusInfinity; static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, - i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, - i_object_class, i_array_class, i_decimal_class, i_key_p, - i_deep_const_get, i_match, i_match_string, i_aset, i_aref, - i_leftshift, i_new, i_try_convert, i_freeze, i_uminus; +i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, +i_object_class, i_array_class, i_decimal_class, i_key_p, +i_deep_const_get, i_match, i_match_string, i_aset, i_aref, +i_leftshift, i_new, i_try_convert, i_freeze, i_uminus; -#line 126 "parser.rl" +#line 125 "parser.rl" -#line 108 "parser.c" enum {JSON_object_start = 1}; enum {JSON_object_first_final = 27}; enum {JSON_object_error = 0}; enum {JSON_object_en_main = 1}; +static const char _JSON_object_nfa_targs[] = { + 0, 0 +}; -#line 168 "parser.rl" +static const char _JSON_object_nfa_offsets[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 +}; + +static const char _JSON_object_nfa_push_actions[] = { + 0, 0 +}; + +static const char _JSON_object_nfa_pop_trans[] = { + 0, 0 +}; + + +#line 167 "parser.rl" static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) { - int cs = EVIL; - VALUE last_name = Qnil; - VALUE object_class = json->object_class; + int cs = EVIL; + VALUE last_name = Qnil; + VALUE object_class = json->object_class; - if (json->max_nesting && current_nesting > json->max_nesting) { - rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); - } + if (json->max_nesting && current_nesting > json->max_nesting) { + rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); + } - *result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class); + *result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class); -#line 132 "parser.c" { - cs = JSON_object_start; + cs = (int)JSON_object_start; } -#line 183 "parser.rl" + #line 182 "parser.rl" + -#line 139 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { -case 1: - if ( (*p) == 123 ) - goto st2; - goto st0; -st0: -cs = 0; - goto _out; -st2: - if ( ++p == pe ) + switch ( cs ) + { + case 1: + goto st_case_1; + case 0: + goto st_case_0; + case 2: + goto st_case_2; + case 3: + goto st_case_3; + case 4: + goto st_case_4; + case 5: + goto st_case_5; + case 6: + goto st_case_6; + case 7: + goto st_case_7; + case 8: + goto st_case_8; + case 9: + goto st_case_9; + case 10: + goto st_case_10; + case 11: + goto st_case_11; + case 12: + goto st_case_12; + case 13: + goto st_case_13; + case 14: + goto st_case_14; + case 15: + goto st_case_15; + case 16: + goto st_case_16; + case 17: + goto st_case_17; + case 18: + goto st_case_18; + case 27: + goto st_case_27; + case 19: + goto st_case_19; + case 20: + goto st_case_20; + case 21: + goto st_case_21; + case 22: + goto st_case_22; + case 23: + goto st_case_23; + case 24: + goto st_case_24; + case 25: + goto st_case_25; + case 26: + goto st_case_26; + } + goto st_out; + st_case_1: + if ( ( (*( p))) == 123 ) { + goto st2; + } + { + goto st0; + } + st_case_0: + st0: + cs = 0; + goto _out; + st2: + p+= 1; + if ( p == pe ) goto _test_eof2; -case 2: - switch( (*p) ) { - case 13: goto st2; - case 32: goto st2; - case 34: goto tr2; - case 47: goto st23; - case 125: goto tr4; - } - if ( 9 <= (*p) && (*p) <= 10 ) - goto st2; - goto st0; -tr2: -#line 150 "parser.rl" - { - char *np; - json->parsing_name = 1; - np = JSON_parse_string(json, p, pe, &last_name); - json->parsing_name = 0; - if (np == NULL) { p--; {p++; cs = 3; goto _out;} } else {p = (( np))-1;} - } - goto st3; -st3: - if ( ++p == pe ) - goto _test_eof3; -case 3: -#line 180 "parser.c" - switch( (*p) ) { - case 13: goto st3; - case 32: goto st3; - case 47: goto st4; - case 58: goto st8; - } - if ( 9 <= (*p) && (*p) <= 10 ) + st_case_2: + switch( ( (*( p))) ) { + case 13: { + goto st2; + } + case 32: { + goto st2; + } + case 34: { + goto ctr2; + } + case 47: { + goto st23; + } + case 125: { + goto ctr4; + } + } + if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { + goto st2; + } + { + goto st0; + } + ctr2: + { + #line 149 "parser.rl" + + char *np; + json->parsing_name = 1; + np = JSON_parse_string(json, p, pe, &last_name); + json->parsing_name = 0; + if (np == NULL) { {p = p - 1; } {p+= 1; cs = 3; goto _out;} } else {p = (( np))-1;} + + } + goto st3; - goto st0; -st4: - if ( ++p == pe ) + st3: + p+= 1; + if ( p == pe ) + goto _test_eof3; + st_case_3: + switch( ( (*( p))) ) { + case 13: { + goto st3; + } + case 32: { + goto st3; + } + case 47: { + goto st4; + } + case 58: { + goto st8; + } + } + if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { + goto st3; + } + { + goto st0; + } + st4: + p+= 1; + if ( p == pe ) goto _test_eof4; -case 4: - switch( (*p) ) { - case 42: goto st5; - case 47: goto st7; - } - goto st0; -st5: - if ( ++p == pe ) + st_case_4: + switch( ( (*( p))) ) { + case 42: { + goto st5; + } + case 47: { + goto st7; + } + } + { + goto st0; + } + st5: + p+= 1; + if ( p == pe ) goto _test_eof5; -case 5: - if ( (*p) == 42 ) - goto st6; - goto st5; -st6: - if ( ++p == pe ) + st_case_5: + if ( ( (*( p))) == 42 ) { + goto st6; + } + { + goto st5; + } + st6: + p+= 1; + if ( p == pe ) goto _test_eof6; -case 6: - switch( (*p) ) { - case 42: goto st6; - case 47: goto st3; - } - goto st5; -st7: - if ( ++p == pe ) + st_case_6: + switch( ( (*( p))) ) { + case 42: { + goto st6; + } + case 47: { + goto st3; + } + } + { + goto st5; + } + st7: + p+= 1; + if ( p == pe ) goto _test_eof7; -case 7: - if ( (*p) == 10 ) - goto st3; - goto st7; -st8: - if ( ++p == pe ) + st_case_7: + if ( ( (*( p))) == 10 ) { + goto st3; + } + { + goto st7; + } + st8: + p+= 1; + if ( p == pe ) goto _test_eof8; -case 8: - switch( (*p) ) { - case 13: goto st8; - case 32: goto st8; - case 34: goto tr11; - case 45: goto tr11; - case 47: goto st19; - case 73: goto tr11; - case 78: goto tr11; - case 91: goto tr11; - case 102: goto tr11; - case 110: goto tr11; - case 116: goto tr11; - case 123: goto tr11; - } - if ( (*p) > 10 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto tr11; - } else if ( (*p) >= 9 ) - goto st8; - goto st0; -tr11: -#line 134 "parser.rl" - { - VALUE v = Qnil; - char *np = JSON_parse_value(json, p, pe, &v, current_nesting); - if (np == NULL) { - p--; {p++; cs = 9; goto _out;} - } else { - if (NIL_P(json->object_class)) { - OBJ_FREEZE(last_name); - rb_hash_aset(*result, last_name, v); - } else { - rb_funcall(*result, i_aset, 2, last_name, v); - } - {p = (( np))-1;} - } - } - goto st9; -st9: - if ( ++p == pe ) - goto _test_eof9; -case 9: -#line 268 "parser.c" - switch( (*p) ) { - case 13: goto st9; - case 32: goto st9; - case 44: goto st10; - case 47: goto st15; - case 125: goto tr4; - } - if ( 9 <= (*p) && (*p) <= 10 ) + st_case_8: + switch( ( (*( p))) ) { + case 13: { + goto st8; + } + case 32: { + goto st8; + } + case 34: { + goto ctr11; + } + case 45: { + goto ctr11; + } + case 47: { + goto st19; + } + case 73: { + goto ctr11; + } + case 78: { + goto ctr11; + } + case 91: { + goto ctr11; + } + case 102: { + goto ctr11; + } + case 110: { + goto ctr11; + } + case 116: { + goto ctr11; + } + case 123: { + goto ctr11; + } + } + if ( ( (*( p))) > 10 ) { + if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { + goto ctr11; + } + } else if ( ( (*( p))) >= 9 ) { + goto st8; + } + { + goto st0; + } + ctr11: + { + #line 133 "parser.rl" + + VALUE v = Qnil; + char *np = JSON_parse_value(json, p, pe, &v, current_nesting); + if (np == NULL) { + {p = p - 1; } {p+= 1; cs = 9; goto _out;} + } else { + if (NIL_P(json->object_class)) { + OBJ_FREEZE(last_name); + rb_hash_aset(*result, last_name, v); + } else { + rb_funcall(*result, i_aset, 2, last_name, v); + } + {p = (( np))-1;} + + } + } + goto st9; - goto st0; -st10: - if ( ++p == pe ) + st9: + p+= 1; + if ( p == pe ) + goto _test_eof9; + st_case_9: + switch( ( (*( p))) ) { + case 13: { + goto st9; + } + case 32: { + goto st9; + } + case 44: { + goto st10; + } + case 47: { + goto st15; + } + case 125: { + goto ctr4; + } + } + if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { + goto st9; + } + { + goto st0; + } + st10: + p+= 1; + if ( p == pe ) goto _test_eof10; -case 10: - switch( (*p) ) { - case 13: goto st10; - case 32: goto st10; - case 34: goto tr2; - case 47: goto st11; - } - if ( 9 <= (*p) && (*p) <= 10 ) - goto st10; - goto st0; -st11: - if ( ++p == pe ) + st_case_10: + switch( ( (*( p))) ) { + case 13: { + goto st10; + } + case 32: { + goto st10; + } + case 34: { + goto ctr2; + } + case 47: { + goto st11; + } + } + if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { + goto st10; + } + { + goto st0; + } + st11: + p+= 1; + if ( p == pe ) goto _test_eof11; -case 11: - switch( (*p) ) { - case 42: goto st12; - case 47: goto st14; - } - goto st0; -st12: - if ( ++p == pe ) + st_case_11: + switch( ( (*( p))) ) { + case 42: { + goto st12; + } + case 47: { + goto st14; + } + } + { + goto st0; + } + st12: + p+= 1; + if ( p == pe ) goto _test_eof12; -case 12: - if ( (*p) == 42 ) - goto st13; - goto st12; -st13: - if ( ++p == pe ) + st_case_12: + if ( ( (*( p))) == 42 ) { + goto st13; + } + { + goto st12; + } + st13: + p+= 1; + if ( p == pe ) goto _test_eof13; -case 13: - switch( (*p) ) { - case 42: goto st13; - case 47: goto st10; - } - goto st12; -st14: - if ( ++p == pe ) + st_case_13: + switch( ( (*( p))) ) { + case 42: { + goto st13; + } + case 47: { + goto st10; + } + } + { + goto st12; + } + st14: + p+= 1; + if ( p == pe ) goto _test_eof14; -case 14: - if ( (*p) == 10 ) - goto st10; - goto st14; -st15: - if ( ++p == pe ) + st_case_14: + if ( ( (*( p))) == 10 ) { + goto st10; + } + { + goto st14; + } + st15: + p+= 1; + if ( p == pe ) goto _test_eof15; -case 15: - switch( (*p) ) { - case 42: goto st16; - case 47: goto st18; - } - goto st0; -st16: - if ( ++p == pe ) + st_case_15: + switch( ( (*( p))) ) { + case 42: { + goto st16; + } + case 47: { + goto st18; + } + } + { + goto st0; + } + st16: + p+= 1; + if ( p == pe ) goto _test_eof16; -case 16: - if ( (*p) == 42 ) - goto st17; - goto st16; -st17: - if ( ++p == pe ) + st_case_16: + if ( ( (*( p))) == 42 ) { + goto st17; + } + { + goto st16; + } + st17: + p+= 1; + if ( p == pe ) goto _test_eof17; -case 17: - switch( (*p) ) { - case 42: goto st17; - case 47: goto st9; - } - goto st16; -st18: - if ( ++p == pe ) + st_case_17: + switch( ( (*( p))) ) { + case 42: { + goto st17; + } + case 47: { + goto st9; + } + } + { + goto st16; + } + st18: + p+= 1; + if ( p == pe ) goto _test_eof18; -case 18: - if ( (*p) == 10 ) - goto st9; - goto st18; -tr4: -#line 158 "parser.rl" - { p--; {p++; cs = 27; goto _out;} } - goto st27; -st27: - if ( ++p == pe ) + st_case_18: + if ( ( (*( p))) == 10 ) { + goto st9; + } + { + goto st18; + } + ctr4: + { + #line 157 "parser.rl" + {p = p - 1; } {p+= 1; cs = 27; goto _out;} } + + goto st27; + st27: + p+= 1; + if ( p == pe ) goto _test_eof27; -case 27: -#line 364 "parser.c" - goto st0; -st19: - if ( ++p == pe ) + st_case_27: + { + goto st0; + } + st19: + p+= 1; + if ( p == pe ) goto _test_eof19; -case 19: - switch( (*p) ) { - case 42: goto st20; - case 47: goto st22; - } - goto st0; -st20: - if ( ++p == pe ) + st_case_19: + switch( ( (*( p))) ) { + case 42: { + goto st20; + } + case 47: { + goto st22; + } + } + { + goto st0; + } + st20: + p+= 1; + if ( p == pe ) goto _test_eof20; -case 20: - if ( (*p) == 42 ) - goto st21; - goto st20; -st21: - if ( ++p == pe ) + st_case_20: + if ( ( (*( p))) == 42 ) { + goto st21; + } + { + goto st20; + } + st21: + p+= 1; + if ( p == pe ) goto _test_eof21; -case 21: - switch( (*p) ) { - case 42: goto st21; - case 47: goto st8; - } - goto st20; -st22: - if ( ++p == pe ) + st_case_21: + switch( ( (*( p))) ) { + case 42: { + goto st21; + } + case 47: { + goto st8; + } + } + { + goto st20; + } + st22: + p+= 1; + if ( p == pe ) goto _test_eof22; -case 22: - if ( (*p) == 10 ) - goto st8; - goto st22; -st23: - if ( ++p == pe ) + st_case_22: + if ( ( (*( p))) == 10 ) { + goto st8; + } + { + goto st22; + } + st23: + p+= 1; + if ( p == pe ) goto _test_eof23; -case 23: - switch( (*p) ) { - case 42: goto st24; - case 47: goto st26; - } - goto st0; -st24: - if ( ++p == pe ) + st_case_23: + switch( ( (*( p))) ) { + case 42: { + goto st24; + } + case 47: { + goto st26; + } + } + { + goto st0; + } + st24: + p+= 1; + if ( p == pe ) goto _test_eof24; -case 24: - if ( (*p) == 42 ) - goto st25; - goto st24; -st25: - if ( ++p == pe ) + st_case_24: + if ( ( (*( p))) == 42 ) { + goto st25; + } + { + goto st24; + } + st25: + p+= 1; + if ( p == pe ) goto _test_eof25; -case 25: - switch( (*p) ) { - case 42: goto st25; - case 47: goto st2; - } - goto st24; -st26: - if ( ++p == pe ) + st_case_25: + switch( ( (*( p))) ) { + case 42: { + goto st25; + } + case 47: { + goto st2; + } + } + { + goto st24; + } + st26: + p+= 1; + if ( p == pe ) goto _test_eof26; -case 26: - if ( (*p) == 10 ) - goto st2; - goto st26; - } - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof11: cs = 11; goto _test_eof; - _test_eof12: cs = 12; goto _test_eof; - _test_eof13: cs = 13; goto _test_eof; - _test_eof14: cs = 14; goto _test_eof; - _test_eof15: cs = 15; goto _test_eof; - _test_eof16: cs = 16; goto _test_eof; - _test_eof17: cs = 17; goto _test_eof; - _test_eof18: cs = 18; goto _test_eof; - _test_eof27: cs = 27; goto _test_eof; - _test_eof19: cs = 19; goto _test_eof; - _test_eof20: cs = 20; goto _test_eof; - _test_eof21: cs = 21; goto _test_eof; - _test_eof22: cs = 22; goto _test_eof; - _test_eof23: cs = 23; goto _test_eof; - _test_eof24: cs = 24; goto _test_eof; - _test_eof25: cs = 25; goto _test_eof; - _test_eof26: cs = 26; goto _test_eof; - - _test_eof: {} - _out: {} + st_case_26: + if ( ( (*( p))) == 10 ) { + goto st2; + } + { + goto st26; + } + st_out: + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; + _test_eof11: cs = 11; goto _test_eof; + _test_eof12: cs = 12; goto _test_eof; + _test_eof13: cs = 13; goto _test_eof; + _test_eof14: cs = 14; goto _test_eof; + _test_eof15: cs = 15; goto _test_eof; + _test_eof16: cs = 16; goto _test_eof; + _test_eof17: cs = 17; goto _test_eof; + _test_eof18: cs = 18; goto _test_eof; + _test_eof27: cs = 27; goto _test_eof; + _test_eof19: cs = 19; goto _test_eof; + _test_eof20: cs = 20; goto _test_eof; + _test_eof21: cs = 21; goto _test_eof; + _test_eof22: cs = 22; goto _test_eof; + _test_eof23: cs = 23; goto _test_eof; + _test_eof24: cs = 24; goto _test_eof; + _test_eof25: cs = 25; goto _test_eof; + _test_eof26: cs = 26; goto _test_eof; + + _test_eof: {} + _out: {} + } + + #line 183 "parser.rl" + + + if (cs >= JSON_object_first_final) { + if (json->create_additions) { + VALUE klassname; + if (NIL_P(json->object_class)) { + klassname = rb_hash_aref(*result, json->create_id); + } else { + klassname = rb_funcall(*result, i_aref, 1, json->create_id); + } + if (!NIL_P(klassname)) { + VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname); + if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) { + *result = rb_funcall(klass, i_json_create, 1, *result); + } + } + } + return p + 1; + } else { + return NULL; } - -#line 184 "parser.rl" - - if (cs >= JSON_object_first_final) { - if (json->create_additions) { - VALUE klassname; - if (NIL_P(json->object_class)) { - klassname = rb_hash_aref(*result, json->create_id); - } else { - klassname = rb_funcall(*result, i_aref, 1, json->create_id); - } - if (!NIL_P(klassname)) { - VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname); - if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) { - *result = rb_funcall(klass, i_json_create, 1, *result); - } - } - } - return p + 1; - } else { - return NULL; - } } -#line 487 "parser.c" enum {JSON_value_start = 1}; enum {JSON_value_first_final = 29}; enum {JSON_value_error = 0}; enum {JSON_value_en_main = 1}; +static const char _JSON_value_nfa_targs[] = { + 0, 0 +}; + +static const char _JSON_value_nfa_offsets[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 +}; + +static const char _JSON_value_nfa_push_actions[] = { + 0, 0 +}; + +static const char _JSON_value_nfa_pop_trans[] = { + 0, 0 +}; -#line 284 "parser.rl" + +#line 283 "parser.rl" static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) { - int cs = EVIL; + int cs = EVIL; -#line 503 "parser.c" { - cs = JSON_value_start; + cs = (int)JSON_value_start; } -#line 291 "parser.rl" + #line 290 "parser.rl" + -#line 510 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { -st1: - if ( ++p == pe ) + switch ( cs ) + { + case 1: + goto st_case_1; + case 0: + goto st_case_0; + case 29: + goto st_case_29; + case 2: + goto st_case_2; + case 3: + goto st_case_3; + case 4: + goto st_case_4; + case 5: + goto st_case_5; + case 6: + goto st_case_6; + case 7: + goto st_case_7; + case 8: + goto st_case_8; + case 9: + goto st_case_9; + case 10: + goto st_case_10; + case 11: + goto st_case_11; + case 12: + goto st_case_12; + case 13: + goto st_case_13; + case 14: + goto st_case_14; + case 15: + goto st_case_15; + case 16: + goto st_case_16; + case 17: + goto st_case_17; + case 18: + goto st_case_18; + case 19: + goto st_case_19; + case 20: + goto st_case_20; + case 21: + goto st_case_21; + case 22: + goto st_case_22; + case 23: + goto st_case_23; + case 24: + goto st_case_24; + case 25: + goto st_case_25; + case 26: + goto st_case_26; + case 27: + goto st_case_27; + case 28: + goto st_case_28; + } + goto st_out; + st1: + p+= 1; + if ( p == pe ) goto _test_eof1; -case 1: - switch( (*p) ) { - case 13: goto st1; - case 32: goto st1; - case 34: goto tr2; - case 45: goto tr3; - case 47: goto st6; - case 73: goto st10; - case 78: goto st17; - case 91: goto tr7; - case 102: goto st19; - case 110: goto st23; - case 116: goto st26; - case 123: goto tr11; - } - if ( (*p) > 10 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto tr3; - } else if ( (*p) >= 9 ) - goto st1; - goto st0; -st0: -cs = 0; - goto _out; -tr2: -#line 236 "parser.rl" - { - char *np = JSON_parse_string(json, p, pe, result); - if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;} - } - goto st29; -tr3: -#line 241 "parser.rl" - { - char *np; - if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) { - if (json->allow_nan) { - *result = CMinusInfinity; - {p = (( p + 10))-1;} - p--; {p++; cs = 29; goto _out;} - } else { - rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p); - } - } - np = JSON_parse_float(json, p, pe, result); - if (np != NULL) {p = (( np))-1;} - np = JSON_parse_integer(json, p, pe, result); - if (np != NULL) {p = (( np))-1;} - p--; {p++; cs = 29; goto _out;} - } - goto st29; -tr7: -#line 259 "parser.rl" - { - char *np; - np = JSON_parse_array(json, p, pe, result, current_nesting + 1); - if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;} - } - goto st29; -tr11: -#line 265 "parser.rl" - { - char *np; - np = JSON_parse_object(json, p, pe, result, current_nesting + 1); - if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;} - } - goto st29; -tr25: -#line 229 "parser.rl" - { - if (json->allow_nan) { - *result = CInfinity; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p - 8); - } - } - goto st29; -tr27: -#line 222 "parser.rl" - { - if (json->allow_nan) { - *result = CNaN; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p - 2); - } - } - goto st29; -tr31: -#line 216 "parser.rl" - { - *result = Qfalse; - } - goto st29; -tr34: -#line 213 "parser.rl" - { - *result = Qnil; - } - goto st29; -tr37: -#line 219 "parser.rl" - { - *result = Qtrue; - } - goto st29; -st29: - if ( ++p == pe ) - goto _test_eof29; -case 29: -#line 271 "parser.rl" - { p--; {p++; cs = 29; goto _out;} } -#line 630 "parser.c" - switch( (*p) ) { - case 13: goto st29; - case 32: goto st29; - case 47: goto st2; - } - if ( 9 <= (*p) && (*p) <= 10 ) + st_case_1: + switch( ( (*( p))) ) { + case 13: { + goto st1; + } + case 32: { + goto st1; + } + case 34: { + goto ctr2; + } + case 45: { + goto ctr3; + } + case 47: { + goto st6; + } + case 73: { + goto st10; + } + case 78: { + goto st17; + } + case 91: { + goto ctr7; + } + case 102: { + goto st19; + } + case 110: { + goto st23; + } + case 116: { + goto st26; + } + case 123: { + goto ctr11; + } + } + if ( ( (*( p))) > 10 ) { + if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { + goto ctr3; + } + } else if ( ( (*( p))) >= 9 ) { + goto st1; + } + { + goto st0; + } + st_case_0: + st0: + cs = 0; + goto _out; + ctr2: + { + #line 235 "parser.rl" + + char *np = JSON_parse_string(json, p, pe, result); + if (np == NULL) { {p = p - 1; } {p+= 1; cs = 29; goto _out;} } else {p = (( np))-1;} + + } + + goto st29; + ctr3: + { + #line 240 "parser.rl" + + char *np; + if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) { + if (json->allow_nan) { + *result = CMinusInfinity; + {p = (( p + 10))-1;} + + {p = p - 1; } {p+= 1; cs = 29; goto _out;} + } else { + rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p); + } + } + np = JSON_parse_float(json, p, pe, result); + if (np != NULL) {p = (( np))-1;} + + np = JSON_parse_integer(json, p, pe, result); + if (np != NULL) {p = (( np))-1;} + + {p = p - 1; } {p+= 1; cs = 29; goto _out;} + } + + goto st29; + ctr7: + { + #line 258 "parser.rl" + + char *np; + np = JSON_parse_array(json, p, pe, result, current_nesting + 1); + if (np == NULL) { {p = p - 1; } {p+= 1; cs = 29; goto _out;} } else {p = (( np))-1;} + + } + + goto st29; + ctr11: + { + #line 264 "parser.rl" + + char *np; + np = JSON_parse_object(json, p, pe, result, current_nesting + 1); + if (np == NULL) { {p = p - 1; } {p+= 1; cs = 29; goto _out;} } else {p = (( np))-1;} + + } + goto st29; - goto st0; -st2: - if ( ++p == pe ) + ctr25: + { + #line 228 "parser.rl" + + if (json->allow_nan) { + *result = CInfinity; + } else { + rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p - 8); + } + } + + goto st29; + ctr27: + { + #line 221 "parser.rl" + + if (json->allow_nan) { + *result = CNaN; + } else { + rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p - 2); + } + } + + goto st29; + ctr31: + { + #line 215 "parser.rl" + + *result = Qfalse; + } + + goto st29; + ctr34: + { + #line 212 "parser.rl" + + *result = Qnil; + } + + goto st29; + ctr37: + { + #line 218 "parser.rl" + + *result = Qtrue; + } + + goto st29; + st29: + p+= 1; + if ( p == pe ) + goto _test_eof29; + st_case_29: + { + #line 270 "parser.rl" + {p = p - 1; } {p+= 1; cs = 29; goto _out;} } + switch( ( (*( p))) ) { + case 13: { + goto st29; + } + case 32: { + goto st29; + } + case 47: { + goto st2; + } + } + if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { + goto st29; + } + { + goto st0; + } + st2: + p+= 1; + if ( p == pe ) goto _test_eof2; -case 2: - switch( (*p) ) { - case 42: goto st3; - case 47: goto st5; - } - goto st0; -st3: - if ( ++p == pe ) + st_case_2: + switch( ( (*( p))) ) { + case 42: { + goto st3; + } + case 47: { + goto st5; + } + } + { + goto st0; + } + st3: + p+= 1; + if ( p == pe ) goto _test_eof3; -case 3: - if ( (*p) == 42 ) - goto st4; - goto st3; -st4: - if ( ++p == pe ) + st_case_3: + if ( ( (*( p))) == 42 ) { + goto st4; + } + { + goto st3; + } + st4: + p+= 1; + if ( p == pe ) goto _test_eof4; -case 4: - switch( (*p) ) { - case 42: goto st4; - case 47: goto st29; - } - goto st3; -st5: - if ( ++p == pe ) + st_case_4: + switch( ( (*( p))) ) { + case 42: { + goto st4; + } + case 47: { + goto st29; + } + } + { + goto st3; + } + st5: + p+= 1; + if ( p == pe ) goto _test_eof5; -case 5: - if ( (*p) == 10 ) - goto st29; - goto st5; -st6: - if ( ++p == pe ) + st_case_5: + if ( ( (*( p))) == 10 ) { + goto st29; + } + { + goto st5; + } + st6: + p+= 1; + if ( p == pe ) goto _test_eof6; -case 6: - switch( (*p) ) { - case 42: goto st7; - case 47: goto st9; - } - goto st0; -st7: - if ( ++p == pe ) + st_case_6: + switch( ( (*( p))) ) { + case 42: { + goto st7; + } + case 47: { + goto st9; + } + } + { + goto st0; + } + st7: + p+= 1; + if ( p == pe ) goto _test_eof7; -case 7: - if ( (*p) == 42 ) - goto st8; - goto st7; -st8: - if ( ++p == pe ) + st_case_7: + if ( ( (*( p))) == 42 ) { + goto st8; + } + { + goto st7; + } + st8: + p+= 1; + if ( p == pe ) goto _test_eof8; -case 8: - switch( (*p) ) { - case 42: goto st8; - case 47: goto st1; - } - goto st7; -st9: - if ( ++p == pe ) + st_case_8: + switch( ( (*( p))) ) { + case 42: { + goto st8; + } + case 47: { + goto st1; + } + } + { + goto st7; + } + st9: + p+= 1; + if ( p == pe ) goto _test_eof9; -case 9: - if ( (*p) == 10 ) - goto st1; - goto st9; -st10: - if ( ++p == pe ) + st_case_9: + if ( ( (*( p))) == 10 ) { + goto st1; + } + { + goto st9; + } + st10: + p+= 1; + if ( p == pe ) goto _test_eof10; -case 10: - if ( (*p) == 110 ) - goto st11; - goto st0; -st11: - if ( ++p == pe ) + st_case_10: + if ( ( (*( p))) == 110 ) { + goto st11; + } + { + goto st0; + } + st11: + p+= 1; + if ( p == pe ) goto _test_eof11; -case 11: - if ( (*p) == 102 ) - goto st12; - goto st0; -st12: - if ( ++p == pe ) + st_case_11: + if ( ( (*( p))) == 102 ) { + goto st12; + } + { + goto st0; + } + st12: + p+= 1; + if ( p == pe ) goto _test_eof12; -case 12: - if ( (*p) == 105 ) - goto st13; - goto st0; -st13: - if ( ++p == pe ) + st_case_12: + if ( ( (*( p))) == 105 ) { + goto st13; + } + { + goto st0; + } + st13: + p+= 1; + if ( p == pe ) goto _test_eof13; -case 13: - if ( (*p) == 110 ) - goto st14; - goto st0; -st14: - if ( ++p == pe ) + st_case_13: + if ( ( (*( p))) == 110 ) { + goto st14; + } + { + goto st0; + } + st14: + p+= 1; + if ( p == pe ) goto _test_eof14; -case 14: - if ( (*p) == 105 ) - goto st15; - goto st0; -st15: - if ( ++p == pe ) + st_case_14: + if ( ( (*( p))) == 105 ) { + goto st15; + } + { + goto st0; + } + st15: + p+= 1; + if ( p == pe ) goto _test_eof15; -case 15: - if ( (*p) == 116 ) - goto st16; - goto st0; -st16: - if ( ++p == pe ) + st_case_15: + if ( ( (*( p))) == 116 ) { + goto st16; + } + { + goto st0; + } + st16: + p+= 1; + if ( p == pe ) goto _test_eof16; -case 16: - if ( (*p) == 121 ) - goto tr25; - goto st0; -st17: - if ( ++p == pe ) + st_case_16: + if ( ( (*( p))) == 121 ) { + goto ctr25; + } + { + goto st0; + } + st17: + p+= 1; + if ( p == pe ) goto _test_eof17; -case 17: - if ( (*p) == 97 ) - goto st18; - goto st0; -st18: - if ( ++p == pe ) + st_case_17: + if ( ( (*( p))) == 97 ) { + goto st18; + } + { + goto st0; + } + st18: + p+= 1; + if ( p == pe ) goto _test_eof18; -case 18: - if ( (*p) == 78 ) - goto tr27; - goto st0; -st19: - if ( ++p == pe ) + st_case_18: + if ( ( (*( p))) == 78 ) { + goto ctr27; + } + { + goto st0; + } + st19: + p+= 1; + if ( p == pe ) goto _test_eof19; -case 19: - if ( (*p) == 97 ) - goto st20; - goto st0; -st20: - if ( ++p == pe ) + st_case_19: + if ( ( (*( p))) == 97 ) { + goto st20; + } + { + goto st0; + } + st20: + p+= 1; + if ( p == pe ) goto _test_eof20; -case 20: - if ( (*p) == 108 ) - goto st21; - goto st0; -st21: - if ( ++p == pe ) + st_case_20: + if ( ( (*( p))) == 108 ) { + goto st21; + } + { + goto st0; + } + st21: + p+= 1; + if ( p == pe ) goto _test_eof21; -case 21: - if ( (*p) == 115 ) - goto st22; - goto st0; -st22: - if ( ++p == pe ) + st_case_21: + if ( ( (*( p))) == 115 ) { + goto st22; + } + { + goto st0; + } + st22: + p+= 1; + if ( p == pe ) goto _test_eof22; -case 22: - if ( (*p) == 101 ) - goto tr31; - goto st0; -st23: - if ( ++p == pe ) + st_case_22: + if ( ( (*( p))) == 101 ) { + goto ctr31; + } + { + goto st0; + } + st23: + p+= 1; + if ( p == pe ) goto _test_eof23; -case 23: - if ( (*p) == 117 ) - goto st24; - goto st0; -st24: - if ( ++p == pe ) + st_case_23: + if ( ( (*( p))) == 117 ) { + goto st24; + } + { + goto st0; + } + st24: + p+= 1; + if ( p == pe ) goto _test_eof24; -case 24: - if ( (*p) == 108 ) - goto st25; - goto st0; -st25: - if ( ++p == pe ) + st_case_24: + if ( ( (*( p))) == 108 ) { + goto st25; + } + { + goto st0; + } + st25: + p+= 1; + if ( p == pe ) goto _test_eof25; -case 25: - if ( (*p) == 108 ) - goto tr34; - goto st0; -st26: - if ( ++p == pe ) + st_case_25: + if ( ( (*( p))) == 108 ) { + goto ctr34; + } + { + goto st0; + } + st26: + p+= 1; + if ( p == pe ) goto _test_eof26; -case 26: - if ( (*p) == 114 ) - goto st27; - goto st0; -st27: - if ( ++p == pe ) + st_case_26: + if ( ( (*( p))) == 114 ) { + goto st27; + } + { + goto st0; + } + st27: + p+= 1; + if ( p == pe ) goto _test_eof27; -case 27: - if ( (*p) == 117 ) - goto st28; - goto st0; -st28: - if ( ++p == pe ) + st_case_27: + if ( ( (*( p))) == 117 ) { + goto st28; + } + { + goto st0; + } + st28: + p+= 1; + if ( p == pe ) goto _test_eof28; -case 28: - if ( (*p) == 101 ) - goto tr37; - goto st0; - } - _test_eof1: cs = 1; goto _test_eof; - _test_eof29: cs = 29; goto _test_eof; - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof11: cs = 11; goto _test_eof; - _test_eof12: cs = 12; goto _test_eof; - _test_eof13: cs = 13; goto _test_eof; - _test_eof14: cs = 14; goto _test_eof; - _test_eof15: cs = 15; goto _test_eof; - _test_eof16: cs = 16; goto _test_eof; - _test_eof17: cs = 17; goto _test_eof; - _test_eof18: cs = 18; goto _test_eof; - _test_eof19: cs = 19; goto _test_eof; - _test_eof20: cs = 20; goto _test_eof; - _test_eof21: cs = 21; goto _test_eof; - _test_eof22: cs = 22; goto _test_eof; - _test_eof23: cs = 23; goto _test_eof; - _test_eof24: cs = 24; goto _test_eof; - _test_eof25: cs = 25; goto _test_eof; - _test_eof26: cs = 26; goto _test_eof; - _test_eof27: cs = 27; goto _test_eof; - _test_eof28: cs = 28; goto _test_eof; - - _test_eof: {} - _out: {} + st_case_28: + if ( ( (*( p))) == 101 ) { + goto ctr37; + } + { + goto st0; + } + st_out: + _test_eof1: cs = 1; goto _test_eof; + _test_eof29: cs = 29; goto _test_eof; + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; + _test_eof11: cs = 11; goto _test_eof; + _test_eof12: cs = 12; goto _test_eof; + _test_eof13: cs = 13; goto _test_eof; + _test_eof14: cs = 14; goto _test_eof; + _test_eof15: cs = 15; goto _test_eof; + _test_eof16: cs = 16; goto _test_eof; + _test_eof17: cs = 17; goto _test_eof; + _test_eof18: cs = 18; goto _test_eof; + _test_eof19: cs = 19; goto _test_eof; + _test_eof20: cs = 20; goto _test_eof; + _test_eof21: cs = 21; goto _test_eof; + _test_eof22: cs = 22; goto _test_eof; + _test_eof23: cs = 23; goto _test_eof; + _test_eof24: cs = 24; goto _test_eof; + _test_eof25: cs = 25; goto _test_eof; + _test_eof26: cs = 26; goto _test_eof; + _test_eof27: cs = 27; goto _test_eof; + _test_eof28: cs = 28; goto _test_eof; + + _test_eof: {} + _out: {} + } + + #line 291 "parser.rl" + + + if (json->freeze) { + OBJ_FREEZE(*result); + } + + if (cs >= JSON_value_first_final) { + return p; + } else { + return NULL; } - -#line 292 "parser.rl" - - if (json->freeze) { - OBJ_FREEZE(*result); - } - - if (cs >= JSON_value_first_final) { - return p; - } else { - return NULL; - } } -#line 885 "parser.c" enum {JSON_integer_start = 1}; enum {JSON_integer_first_final = 3}; enum {JSON_integer_error = 0}; enum {JSON_integer_en_main = 1}; +static const char _JSON_integer_nfa_targs[] = { + 0, 0 +}; -#line 312 "parser.rl" +static const char _JSON_integer_nfa_offsets[] = { + 0, 0, 0, 0, 0, 0, 0 +}; + +static const char _JSON_integer_nfa_push_actions[] = { + 0, 0 +}; + +static const char _JSON_integer_nfa_pop_trans[] = { + 0, 0 +}; + + +#line 311 "parser.rl" static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result) { - int cs = EVIL; + int cs = EVIL; -#line 901 "parser.c" { - cs = JSON_integer_start; + cs = (int)JSON_integer_start; } -#line 319 "parser.rl" - json->memo = p; + #line 318 "parser.rl" + + json->memo = p; -#line 909 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { -case 1: - switch( (*p) ) { - case 45: goto st2; - case 48: goto st3; - } - if ( 49 <= (*p) && (*p) <= 57 ) - goto st5; - goto st0; -st0: -cs = 0; - goto _out; -st2: - if ( ++p == pe ) + switch ( cs ) + { + case 1: + goto st_case_1; + case 0: + goto st_case_0; + case 2: + goto st_case_2; + case 3: + goto st_case_3; + case 4: + goto st_case_4; + case 5: + goto st_case_5; + } + goto st_out; + st_case_1: + switch( ( (*( p))) ) { + case 45: { + goto st2; + } + case 48: { + goto st3; + } + } + if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) { + goto st5; + } + { + goto st0; + } + st_case_0: + st0: + cs = 0; + goto _out; + st2: + p+= 1; + if ( p == pe ) goto _test_eof2; -case 2: - if ( (*p) == 48 ) - goto st3; - if ( 49 <= (*p) && (*p) <= 57 ) - goto st5; - goto st0; -st3: - if ( ++p == pe ) + st_case_2: + if ( ( (*( p))) == 48 ) { + goto st3; + } + if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) { + goto st5; + } + { + goto st0; + } + st3: + p+= 1; + if ( p == pe ) goto _test_eof3; -case 3: - if ( 48 <= (*p) && (*p) <= 57 ) - goto st0; - goto tr4; -tr4: -#line 309 "parser.rl" - { p--; {p++; cs = 4; goto _out;} } - goto st4; -st4: - if ( ++p == pe ) + st_case_3: + if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { + goto st0; + } + { + goto ctr4; + } + ctr4: + { + #line 308 "parser.rl" + {p = p - 1; } {p+= 1; cs = 4; goto _out;} } + + goto st4; + st4: + p+= 1; + if ( p == pe ) goto _test_eof4; -case 4: -#line 950 "parser.c" - goto st0; -st5: - if ( ++p == pe ) + st_case_4: + { + goto st0; + } + st5: + p+= 1; + if ( p == pe ) goto _test_eof5; -case 5: - if ( 48 <= (*p) && (*p) <= 57 ) - goto st5; - goto tr4; - } - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; + st_case_5: + if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { + goto st5; + } + { + goto ctr4; + } + st_out: + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; - _test_eof: {} - _out: {} + _test_eof: {} + _out: {} } -#line 321 "parser.rl" - - if (cs >= JSON_integer_first_final) { - long len = p - json->memo; - fbuffer_clear(json->fbuffer); - fbuffer_append(json->fbuffer, json->memo, len); - fbuffer_append_char(json->fbuffer, '\0'); - *result = rb_cstr2inum(FBUFFER_PTR(json->fbuffer), 10); - return p + 1; - } else { - return NULL; - } + #line 320 "parser.rl" + + + if (cs >= JSON_integer_first_final) { + long len = p - json->memo; + fbuffer_clear(json->fbuffer); + fbuffer_append(json->fbuffer, json->memo, len); + fbuffer_append_char(json->fbuffer, '\0'); + *result = rb_cstr2inum(FBUFFER_PTR(json->fbuffer), 10); + return p + 1; + } else { + return NULL; + } } -#line 984 "parser.c" enum {JSON_float_start = 1}; enum {JSON_float_first_final = 8}; enum {JSON_float_error = 0}; enum {JSON_float_en_main = 1}; +static const char _JSON_float_nfa_targs[] = { + 0, 0 +}; + +static const char _JSON_float_nfa_offsets[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 +}; + +static const char _JSON_float_nfa_push_actions[] = { + 0, 0 +}; + +static const char _JSON_float_nfa_pop_trans[] = { + 0, 0 +}; + -#line 346 "parser.rl" +#line 345 "parser.rl" static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result) { - int cs = EVIL; + int cs = EVIL; -#line 1013 "parser.c" { - cs = JSON_float_start; + cs = (int)JSON_float_start; } -#line 366 "parser.rl" - json->memo = p; + #line 352 "parser.rl" + + json->memo = p; -#line 1021 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { -case 1: - switch( (*p) ) { - case 45: goto st2; - case 48: goto st3; - } - if ( 49 <= (*p) && (*p) <= 57 ) - goto st7; - goto st0; -st0: -cs = 0; - goto _out; -st2: - if ( ++p == pe ) + switch ( cs ) + { + case 1: + goto st_case_1; + case 0: + goto st_case_0; + case 2: + goto st_case_2; + case 3: + goto st_case_3; + case 4: + goto st_case_4; + case 8: + goto st_case_8; + case 9: + goto st_case_9; + case 5: + goto st_case_5; + case 6: + goto st_case_6; + case 10: + goto st_case_10; + case 7: + goto st_case_7; + } + goto st_out; + st_case_1: + switch( ( (*( p))) ) { + case 45: { + goto st2; + } + case 48: { + goto st3; + } + } + if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) { + goto st7; + } + { + goto st0; + } + st_case_0: + st0: + cs = 0; + goto _out; + st2: + p+= 1; + if ( p == pe ) goto _test_eof2; -case 2: - if ( (*p) == 48 ) - goto st3; - if ( 49 <= (*p) && (*p) <= 57 ) - goto st7; - goto st0; -st3: - if ( ++p == pe ) + st_case_2: + if ( ( (*( p))) == 48 ) { + goto st3; + } + if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) { + goto st7; + } + { + goto st0; + } + st3: + p+= 1; + if ( p == pe ) goto _test_eof3; -case 3: - switch( (*p) ) { - case 46: goto st4; - case 69: goto st5; - case 101: goto st5; - } - goto st0; -st4: - if ( ++p == pe ) + st_case_3: + switch( ( (*( p))) ) { + case 46: { + goto st4; + } + case 69: { + goto st5; + } + case 101: { + goto st5; + } + } + { + goto st0; + } + st4: + p+= 1; + if ( p == pe ) goto _test_eof4; -case 4: - if ( 48 <= (*p) && (*p) <= 57 ) - goto st8; - goto st0; -st8: - if ( ++p == pe ) - goto _test_eof8; -case 8: - switch( (*p) ) { - case 69: goto st5; - case 101: goto st5; - } - if ( (*p) > 46 ) { - if ( 48 <= (*p) && (*p) <= 57 ) + st_case_4: + if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { goto st8; - } else if ( (*p) >= 45 ) - goto st0; - goto tr9; -tr9: -#line 340 "parser.rl" - { p--; {p++; cs = 9; goto _out;} } - goto st9; -st9: - if ( ++p == pe ) + } + { + goto st0; + } + st8: + p+= 1; + if ( p == pe ) + goto _test_eof8; + st_case_8: + switch( ( (*( p))) ) { + case 69: { + goto st5; + } + case 101: { + goto st5; + } + } + if ( ( (*( p))) > 46 ) { + if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { + goto st8; + } + } else if ( ( (*( p))) >= 45 ) { + goto st0; + } + { + goto ctr9; + } + ctr9: + { + #line 339 "parser.rl" + {p = p - 1; } {p+= 1; cs = 9; goto _out;} } + + goto st9; + st9: + p+= 1; + if ( p == pe ) goto _test_eof9; -case 9: -#line 1086 "parser.c" - goto st0; -st5: - if ( ++p == pe ) + st_case_9: + { + goto st0; + } + st5: + p+= 1; + if ( p == pe ) goto _test_eof5; -case 5: - switch( (*p) ) { - case 43: goto st6; - case 45: goto st6; - } - if ( 48 <= (*p) && (*p) <= 57 ) - goto st10; - goto st0; -st6: - if ( ++p == pe ) + st_case_5: + switch( ( (*( p))) ) { + case 43: { + goto st6; + } + case 45: { + goto st6; + } + } + if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { + goto st10; + } + { + goto st0; + } + st6: + p+= 1; + if ( p == pe ) goto _test_eof6; -case 6: - if ( 48 <= (*p) && (*p) <= 57 ) - goto st10; - goto st0; -st10: - if ( ++p == pe ) - goto _test_eof10; -case 10: - switch( (*p) ) { - case 69: goto st0; - case 101: goto st0; - } - if ( (*p) > 46 ) { - if ( 48 <= (*p) && (*p) <= 57 ) + st_case_6: + if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { goto st10; - } else if ( (*p) >= 45 ) - goto st0; - goto tr9; -st7: - if ( ++p == pe ) + } + { + goto st0; + } + st10: + p+= 1; + if ( p == pe ) + goto _test_eof10; + st_case_10: + switch( ( (*( p))) ) { + case 69: { + goto st0; + } + case 101: { + goto st0; + } + } + if ( ( (*( p))) > 46 ) { + if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { + goto st10; + } + } else if ( ( (*( p))) >= 45 ) { + goto st0; + } + { + goto ctr9; + } + st7: + p+= 1; + if ( p == pe ) goto _test_eof7; -case 7: - switch( (*p) ) { - case 46: goto st4; - case 69: goto st5; - case 101: goto st5; - } - if ( 48 <= (*p) && (*p) <= 57 ) - goto st7; - goto st0; - } - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - - _test_eof: {} - _out: {} + st_case_7: + switch( ( (*( p))) ) { + case 46: { + goto st4; + } + case 69: { + goto st5; + } + case 101: { + goto st5; + } + } + if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { + goto st7; + } + { + goto st0; + } + st_out: + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + + _test_eof: {} + _out: {} + } + + #line 354 "parser.rl" + + + if (cs >= JSON_float_first_final) { + VALUE mod = Qnil; + ID method_id = 0; + if (rb_respond_to(json->decimal_class, i_try_convert)) { + mod = json->decimal_class; + method_id = i_try_convert; + } else if (rb_respond_to(json->decimal_class, i_new)) { + mod = json->decimal_class; + method_id = i_new; + } else if (RB_TYPE_P(json->decimal_class, T_CLASS)) { + VALUE name = rb_class_name(json->decimal_class); + const char *name_cstr = RSTRING_PTR(name); + const char *last_colon = strrchr(name_cstr, ':'); + if (last_colon) { + const char *mod_path_end = last_colon - 1; + VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr); + mod = rb_path_to_class(mod_path); + + const char *method_name_beg = last_colon + 1; + long before_len = method_name_beg - name_cstr; + long len = RSTRING_LEN(name) - before_len; + VALUE method_name = rb_str_substr(name, before_len, len); + method_id = SYM2ID(rb_str_intern(method_name)); + } else { + mod = rb_mKernel; + method_id = SYM2ID(rb_str_intern(name)); + } + } + + long len = p - json->memo; + fbuffer_clear(json->fbuffer); + fbuffer_append(json->fbuffer, json->memo, len); + fbuffer_append_char(json->fbuffer, '\0'); + + if (method_id) { + VALUE text = rb_str_new2(FBUFFER_PTR(json->fbuffer)); + *result = rb_funcallv(mod, method_id, 1, &text); + } else { + *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1)); + } + + return p + 1; + } else { + return NULL; } - -#line 368 "parser.rl" - - if (cs >= JSON_float_first_final) { - VALUE mod = Qnil; - ID method_id = 0; - if (rb_respond_to(json->decimal_class, i_try_convert)) { - mod = json->decimal_class; - method_id = i_try_convert; - } else if (rb_respond_to(json->decimal_class, i_new)) { - mod = json->decimal_class; - method_id = i_new; - } else if (RB_TYPE_P(json->decimal_class, T_CLASS)) { - VALUE name = rb_class_name(json->decimal_class); - const char *name_cstr = RSTRING_PTR(name); - const char *last_colon = strrchr(name_cstr, ':'); - if (last_colon) { - const char *mod_path_end = last_colon - 1; - VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr); - mod = rb_path_to_class(mod_path); - - const char *method_name_beg = last_colon + 1; - long before_len = method_name_beg - name_cstr; - long len = RSTRING_LEN(name) - before_len; - VALUE method_name = rb_str_substr(name, before_len, len); - method_id = SYM2ID(rb_str_intern(method_name)); - } else { - mod = rb_mKernel; - method_id = SYM2ID(rb_str_intern(name)); - } - } - - long len = p - json->memo; - fbuffer_clear(json->fbuffer); - fbuffer_append(json->fbuffer, json->memo, len); - fbuffer_append_char(json->fbuffer, '\0'); - - if (method_id) { - VALUE text = rb_str_new2(FBUFFER_PTR(json->fbuffer)); - *result = rb_funcallv(mod, method_id, 1, &text); - } else { - *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1)); - } - - return p + 1; - } else { - return NULL; - } } -#line 1173 "parser.c" enum {JSON_array_start = 1}; enum {JSON_array_first_final = 17}; enum {JSON_array_error = 0}; enum {JSON_array_en_main = 1}; +static const char _JSON_array_nfa_targs[] = { + 0, 0 +}; + +static const char _JSON_array_nfa_offsets[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 +}; + +static const char _JSON_array_nfa_push_actions[] = { + 0, 0 +}; + +static const char _JSON_array_nfa_pop_trans[] = { + 0, 0 +}; -#line 421 "parser.rl" + +#line 432 "parser.rl" static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) { - int cs = EVIL; - VALUE array_class = json->array_class; + int cs = EVIL; + VALUE array_class = json->array_class; - if (json->max_nesting && current_nesting > json->max_nesting) { - rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); - } - *result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class); + if (json->max_nesting && current_nesting > json->max_nesting) { + rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); + } + *result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class); -#line 1195 "parser.c" { - cs = JSON_array_start; + cs = (int)JSON_array_start; } -#line 434 "parser.rl" + #line 445 "parser.rl" + -#line 1202 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { -case 1: - if ( (*p) == 91 ) - goto st2; - goto st0; -st0: -cs = 0; - goto _out; -st2: - if ( ++p == pe ) + switch ( cs ) + { + case 1: + goto st_case_1; + case 0: + goto st_case_0; + case 2: + goto st_case_2; + case 3: + goto st_case_3; + case 4: + goto st_case_4; + case 5: + goto st_case_5; + case 6: + goto st_case_6; + case 7: + goto st_case_7; + case 8: + goto st_case_8; + case 9: + goto st_case_9; + case 10: + goto st_case_10; + case 11: + goto st_case_11; + case 12: + goto st_case_12; + case 17: + goto st_case_17; + case 13: + goto st_case_13; + case 14: + goto st_case_14; + case 15: + goto st_case_15; + case 16: + goto st_case_16; + } + goto st_out; + st_case_1: + if ( ( (*( p))) == 91 ) { + goto st2; + } + { + goto st0; + } + st_case_0: + st0: + cs = 0; + goto _out; + st2: + p+= 1; + if ( p == pe ) goto _test_eof2; -case 2: - switch( (*p) ) { - case 13: goto st2; - case 32: goto st2; - case 34: goto tr2; - case 45: goto tr2; - case 47: goto st13; - case 73: goto tr2; - case 78: goto tr2; - case 91: goto tr2; - case 93: goto tr4; - case 102: goto tr2; - case 110: goto tr2; - case 116: goto tr2; - case 123: goto tr2; - } - if ( (*p) > 10 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto tr2; - } else if ( (*p) >= 9 ) - goto st2; - goto st0; -tr2: -#line 398 "parser.rl" - { - VALUE v = Qnil; - char *np = JSON_parse_value(json, p, pe, &v, current_nesting); - if (np == NULL) { - p--; {p++; cs = 3; goto _out;} - } else { - if (NIL_P(json->array_class)) { - rb_ary_push(*result, v); - } else { - rb_funcall(*result, i_leftshift, 1, v); - } - {p = (( np))-1;} - } - } - goto st3; -st3: - if ( ++p == pe ) - goto _test_eof3; -case 3: -#line 1261 "parser.c" - switch( (*p) ) { - case 13: goto st3; - case 32: goto st3; - case 44: goto st4; - case 47: goto st9; - case 93: goto tr4; - } - if ( 9 <= (*p) && (*p) <= 10 ) + st_case_2: + switch( ( (*( p))) ) { + case 13: { + goto st2; + } + case 32: { + goto st2; + } + case 34: { + goto ctr2; + } + case 45: { + goto ctr2; + } + case 47: { + goto st13; + } + case 73: { + goto ctr2; + } + case 78: { + goto ctr2; + } + case 91: { + goto ctr2; + } + case 93: { + goto ctr4; + } + case 102: { + goto ctr2; + } + case 110: { + goto ctr2; + } + case 116: { + goto ctr2; + } + case 123: { + goto ctr2; + } + } + if ( ( (*( p))) > 10 ) { + if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { + goto ctr2; + } + } else if ( ( (*( p))) >= 9 ) { + goto st2; + } + { + goto st0; + } + ctr2: + { + #line 409 "parser.rl" + + VALUE v = Qnil; + char *np = JSON_parse_value(json, p, pe, &v, current_nesting); + if (np == NULL) { + {p = p - 1; } {p+= 1; cs = 3; goto _out;} + } else { + if (NIL_P(json->array_class)) { + rb_ary_push(*result, v); + } else { + rb_funcall(*result, i_leftshift, 1, v); + } + {p = (( np))-1;} + + } + } + goto st3; - goto st0; -st4: - if ( ++p == pe ) + st3: + p+= 1; + if ( p == pe ) + goto _test_eof3; + st_case_3: + switch( ( (*( p))) ) { + case 13: { + goto st3; + } + case 32: { + goto st3; + } + case 44: { + goto st4; + } + case 47: { + goto st9; + } + case 93: { + goto ctr4; + } + } + if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { + goto st3; + } + { + goto st0; + } + st4: + p+= 1; + if ( p == pe ) goto _test_eof4; -case 4: - switch( (*p) ) { - case 13: goto st4; - case 32: goto st4; - case 34: goto tr2; - case 45: goto tr2; - case 47: goto st5; - case 73: goto tr2; - case 78: goto tr2; - case 91: goto tr2; - case 102: goto tr2; - case 110: goto tr2; - case 116: goto tr2; - case 123: goto tr2; - } - if ( (*p) > 10 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto tr2; - } else if ( (*p) >= 9 ) - goto st4; - goto st0; -st5: - if ( ++p == pe ) + st_case_4: + switch( ( (*( p))) ) { + case 13: { + goto st4; + } + case 32: { + goto st4; + } + case 34: { + goto ctr2; + } + case 45: { + goto ctr2; + } + case 47: { + goto st5; + } + case 73: { + goto ctr2; + } + case 78: { + goto ctr2; + } + case 91: { + goto ctr2; + } + case 102: { + goto ctr2; + } + case 110: { + goto ctr2; + } + case 116: { + goto ctr2; + } + case 123: { + goto ctr2; + } + } + if ( ( (*( p))) > 10 ) { + if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { + goto ctr2; + } + } else if ( ( (*( p))) >= 9 ) { + goto st4; + } + { + goto st0; + } + st5: + p+= 1; + if ( p == pe ) goto _test_eof5; -case 5: - switch( (*p) ) { - case 42: goto st6; - case 47: goto st8; - } - goto st0; -st6: - if ( ++p == pe ) + st_case_5: + switch( ( (*( p))) ) { + case 42: { + goto st6; + } + case 47: { + goto st8; + } + } + { + goto st0; + } + st6: + p+= 1; + if ( p == pe ) goto _test_eof6; -case 6: - if ( (*p) == 42 ) - goto st7; - goto st6; -st7: - if ( ++p == pe ) + st_case_6: + if ( ( (*( p))) == 42 ) { + goto st7; + } + { + goto st6; + } + st7: + p+= 1; + if ( p == pe ) goto _test_eof7; -case 7: - switch( (*p) ) { - case 42: goto st7; - case 47: goto st4; - } - goto st6; -st8: - if ( ++p == pe ) + st_case_7: + switch( ( (*( p))) ) { + case 42: { + goto st7; + } + case 47: { + goto st4; + } + } + { + goto st6; + } + st8: + p+= 1; + if ( p == pe ) goto _test_eof8; -case 8: - if ( (*p) == 10 ) - goto st4; - goto st8; -st9: - if ( ++p == pe ) + st_case_8: + if ( ( (*( p))) == 10 ) { + goto st4; + } + { + goto st8; + } + st9: + p+= 1; + if ( p == pe ) goto _test_eof9; -case 9: - switch( (*p) ) { - case 42: goto st10; - case 47: goto st12; - } - goto st0; -st10: - if ( ++p == pe ) + st_case_9: + switch( ( (*( p))) ) { + case 42: { + goto st10; + } + case 47: { + goto st12; + } + } + { + goto st0; + } + st10: + p+= 1; + if ( p == pe ) goto _test_eof10; -case 10: - if ( (*p) == 42 ) - goto st11; - goto st10; -st11: - if ( ++p == pe ) + st_case_10: + if ( ( (*( p))) == 42 ) { + goto st11; + } + { + goto st10; + } + st11: + p+= 1; + if ( p == pe ) goto _test_eof11; -case 11: - switch( (*p) ) { - case 42: goto st11; - case 47: goto st3; - } - goto st10; -st12: - if ( ++p == pe ) + st_case_11: + switch( ( (*( p))) ) { + case 42: { + goto st11; + } + case 47: { + goto st3; + } + } + { + goto st10; + } + st12: + p+= 1; + if ( p == pe ) goto _test_eof12; -case 12: - if ( (*p) == 10 ) - goto st3; - goto st12; -tr4: -#line 413 "parser.rl" - { p--; {p++; cs = 17; goto _out;} } - goto st17; -st17: - if ( ++p == pe ) + st_case_12: + if ( ( (*( p))) == 10 ) { + goto st3; + } + { + goto st12; + } + ctr4: + { + #line 424 "parser.rl" + {p = p - 1; } {p+= 1; cs = 17; goto _out;} } + + goto st17; + st17: + p+= 1; + if ( p == pe ) goto _test_eof17; -case 17: -#line 1368 "parser.c" - goto st0; -st13: - if ( ++p == pe ) + st_case_17: + { + goto st0; + } + st13: + p+= 1; + if ( p == pe ) goto _test_eof13; -case 13: - switch( (*p) ) { - case 42: goto st14; - case 47: goto st16; - } - goto st0; -st14: - if ( ++p == pe ) + st_case_13: + switch( ( (*( p))) ) { + case 42: { + goto st14; + } + case 47: { + goto st16; + } + } + { + goto st0; + } + st14: + p+= 1; + if ( p == pe ) goto _test_eof14; -case 14: - if ( (*p) == 42 ) - goto st15; - goto st14; -st15: - if ( ++p == pe ) + st_case_14: + if ( ( (*( p))) == 42 ) { + goto st15; + } + { + goto st14; + } + st15: + p+= 1; + if ( p == pe ) goto _test_eof15; -case 15: - switch( (*p) ) { - case 42: goto st15; - case 47: goto st2; - } - goto st14; -st16: - if ( ++p == pe ) + st_case_15: + switch( ( (*( p))) ) { + case 42: { + goto st15; + } + case 47: { + goto st2; + } + } + { + goto st14; + } + st16: + p+= 1; + if ( p == pe ) goto _test_eof16; -case 16: - if ( (*p) == 10 ) - goto st2; - goto st16; - } - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof11: cs = 11; goto _test_eof; - _test_eof12: cs = 12; goto _test_eof; - _test_eof17: cs = 17; goto _test_eof; - _test_eof13: cs = 13; goto _test_eof; - _test_eof14: cs = 14; goto _test_eof; - _test_eof15: cs = 15; goto _test_eof; - _test_eof16: cs = 16; goto _test_eof; - - _test_eof: {} - _out: {} + st_case_16: + if ( ( (*( p))) == 10 ) { + goto st2; + } + { + goto st16; + } + st_out: + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; + _test_eof11: cs = 11; goto _test_eof; + _test_eof12: cs = 12; goto _test_eof; + _test_eof17: cs = 17; goto _test_eof; + _test_eof13: cs = 13; goto _test_eof; + _test_eof14: cs = 14; goto _test_eof; + _test_eof15: cs = 15; goto _test_eof; + _test_eof16: cs = 16; goto _test_eof; + + _test_eof: {} + _out: {} + } + + #line 446 "parser.rl" + + + if(cs >= JSON_array_first_final) { + return p + 1; + } else { + rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p); + return NULL; } - -#line 435 "parser.rl" - - if(cs >= JSON_array_first_final) { - return p + 1; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p); - return NULL; - } } -static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd) +static const size_t MAX_STACK_BUFFER_SIZE = 128; +static VALUE json_string_unescape(char *string, char *stringEnd) { - char *p = string, *pe = string, *unescape; - int unescape_len; - char buf[4]; - - while (pe < stringEnd) { - if (*pe == '\\') { - unescape = (char *) "?"; - unescape_len = 1; - if (pe > p) rb_str_buf_cat(result, p, pe - p); - switch (*++pe) { - case 'n': - unescape = (char *) "\n"; - break; - case 'r': - unescape = (char *) "\r"; - break; - case 't': - unescape = (char *) "\t"; - break; - case '"': - unescape = (char *) "\""; - break; - case '\\': - unescape = (char *) "\\"; - break; - case 'b': - unescape = (char *) "\b"; - break; - case 'f': - unescape = (char *) "\f"; - break; - case 'u': - if (pe > stringEnd - 4) { - rb_enc_raise( - EXC_ENCODING eParserError, - "%u: incomplete unicode character escape sequence at '%s'", __LINE__, p - ); - } else { - UTF32 ch = unescape_unicode((unsigned char *) ++pe); - pe += 3; - if (UNI_SUR_HIGH_START == (ch & 0xFC00)) { - pe++; - if (pe > stringEnd - 6) { - rb_enc_raise( - EXC_ENCODING eParserError, - "%u: incomplete surrogate pair at '%s'", __LINE__, p - ); - } - if (pe[0] == '\\' && pe[1] == 'u') { - UTF32 sur = unescape_unicode((unsigned char *) pe + 2); - ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16) - | (sur & 0x3FF)); - pe += 5; - } else { - unescape = (char *) "?"; - break; - } - } - unescape_len = convert_UTF32_to_UTF8(buf, ch); - unescape = buf; - } - break; - default: - p = pe; - continue; - } - rb_str_buf_cat(result, unescape, unescape_len); - p = ++pe; - } else { - pe++; - } - } - rb_str_buf_cat(result, p, pe - p); - return result; + VALUE result = Qnil; + size_t bufferSize = stringEnd - string; + char *p = string, *pe = string, *unescape, *bufferStart, *buffer; + int unescape_len; + char buf[4]; + + if (bufferSize > MAX_STACK_BUFFER_SIZE) { + buffer = xmalloc(bufferSize); + bufferStart = buffer; + } else { + bufferStart = buffer = alloca(bufferSize); + } + + while (pe < stringEnd) { + if (*pe == '\\') { + unescape = (char *) "?"; + unescape_len = 1; + if (pe > p) { + MEMCPY(buffer, p, char, pe - p); + buffer += pe - p; + } + switch (*++pe) { + case 'n': + unescape = (char *) "\n"; + break; + case 'r': + unescape = (char *) "\r"; + break; + case 't': + unescape = (char *) "\t"; + break; + case '"': + unescape = (char *) "\""; + break; + case '\\': + unescape = (char *) "\\"; + break; + case 'b': + unescape = (char *) "\b"; + break; + case 'f': + unescape = (char *) "\f"; + break; + case 'u': + if (pe > stringEnd - 4) { + if (bufferSize > MAX_STACK_BUFFER_SIZE) { + free(bufferStart); + } + rb_enc_raise( + EXC_ENCODING eParserError, + "%u: incomplete unicode character escape sequence at '%s'", __LINE__, p + ); + } else { + UTF32 ch = unescape_unicode((unsigned char *) ++pe); + pe += 3; + if (UNI_SUR_HIGH_START == (ch & 0xFC00)) { + pe++; + if (pe > stringEnd - 6) { + if (bufferSize > MAX_STACK_BUFFER_SIZE) { + free(bufferStart); + } + rb_enc_raise( + EXC_ENCODING eParserError, + "%u: incomplete surrogate pair at '%s'", __LINE__, p + ); + } + if (pe[0] == '\\' && pe[1] == 'u') { + UTF32 sur = unescape_unicode((unsigned char *) pe + 2); + ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16) + | (sur & 0x3FF)); + pe += 5; + } else { + unescape = (char *) "?"; + break; + } + } + unescape_len = convert_UTF32_to_UTF8(buf, ch); + unescape = buf; + } + break; + default: + p = pe; + continue; + } + MEMCPY(buffer, unescape, char, unescape_len); + buffer += unescape_len; + p = ++pe; + } else { + pe++; + } + } + + if (pe > p) { + MEMCPY(buffer, p, char, pe - p); + buffer += pe - p; + } + + #ifdef HAVE_RUBY_ENCODING_H + result = rb_utf8_str_new(bufferStart, buffer - bufferStart); + #else + result = rb_str_new(bufferStart, buffer - bufferStart); + #endif + + if (bufferSize > MAX_STACK_BUFFER_SIZE) { + free(bufferStart); + } + return result; } -#line 1513 "parser.c" enum {JSON_string_start = 1}; enum {JSON_string_first_final = 8}; enum {JSON_string_error = 0}; enum {JSON_string_en_main = 1}; +static const char _JSON_string_nfa_targs[] = { + 0, 0 +}; + +static const char _JSON_string_nfa_offsets[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0 +}; + +static const char _JSON_string_nfa_push_actions[] = { + 0, 0 +}; + +static const char _JSON_string_nfa_pop_trans[] = { + 0, 0 +}; + -#line 542 "parser.rl" +#line 586 "parser.rl" static int match_i(VALUE regexp, VALUE klass, VALUE memo) { - if (regexp == Qundef) return ST_STOP; - if (RTEST(rb_funcall(klass, i_json_creatable_p, 0)) && - RTEST(rb_funcall(regexp, i_match, 1, rb_ary_entry(memo, 0)))) { - rb_ary_push(memo, klass); - return ST_STOP; - } - return ST_CONTINUE; + if (regexp == Qundef) return ST_STOP; + if (RTEST(rb_funcall(klass, i_json_creatable_p, 0)) && + RTEST(rb_funcall(regexp, i_match, 1, rb_ary_entry(memo, 0)))) { + rb_ary_push(memo, klass); + return ST_STOP; + } + return ST_CONTINUE; } static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result) { - int cs = EVIL; - VALUE match_string; + int cs = EVIL; + VALUE match_string; - *result = rb_str_buf_new(0); -#line 1543 "parser.c" { - cs = JSON_string_start; + cs = (int)JSON_string_start; } -#line 563 "parser.rl" - json->memo = p; + #line 606 "parser.rl" + + json->memo = p; -#line 1551 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { -case 1: - if ( (*p) == 34 ) - goto st2; - goto st0; -st0: -cs = 0; - goto _out; -st2: - if ( ++p == pe ) + switch ( cs ) + { + case 1: + goto st_case_1; + case 0: + goto st_case_0; + case 2: + goto st_case_2; + case 8: + goto st_case_8; + case 3: + goto st_case_3; + case 4: + goto st_case_4; + case 5: + goto st_case_5; + case 6: + goto st_case_6; + case 7: + goto st_case_7; + } + goto st_out; + st_case_1: + if ( ( (*( p))) == 34 ) { + goto st2; + } + { + goto st0; + } + st_case_0: + st0: + cs = 0; + goto _out; + st2: + p+= 1; + if ( p == pe ) goto _test_eof2; -case 2: - switch( (*p) ) { - case 34: goto tr2; - case 92: goto st3; - } - if ( 0 <= (signed char)(*p) && (*p) <= 31 ) - goto st0; - goto st2; -tr2: -#line 528 "parser.rl" - { - *result = json_string_unescape(*result, json->memo + 1, p); - if (NIL_P(*result)) { - p--; - {p++; cs = 8; goto _out;} - } else { - FORCE_UTF8(*result); - {p = (( p + 1))-1;} - } - } -#line 539 "parser.rl" - { p--; {p++; cs = 8; goto _out;} } - goto st8; -st8: - if ( ++p == pe ) + st_case_2: + switch( ( (*( p))) ) { + case 34: { + goto ctr2; + } + case 92: { + goto st3; + } + } + if ( 0 <= ( (*( p))) && ( (*( p))) <= 31 ) { + goto st0; + } + { + goto st2; + } + ctr2: + { + #line 573 "parser.rl" + + *result = json_string_unescape(json->memo + 1, p); + if (NIL_P(*result)) { + {p = p - 1; } + {p+= 1; cs = 8; goto _out;} + } else { + {p = (( p + 1))-1;} + + } + } + { + #line 583 "parser.rl" + {p = p - 1; } {p+= 1; cs = 8; goto _out;} } + + goto st8; + st8: + p+= 1; + if ( p == pe ) goto _test_eof8; -case 8: -#line 1594 "parser.c" - goto st0; -st3: - if ( ++p == pe ) + st_case_8: + { + goto st0; + } + st3: + p+= 1; + if ( p == pe ) goto _test_eof3; -case 3: - if ( (*p) == 117 ) - goto st4; - if ( 0 <= (signed char)(*p) && (*p) <= 31 ) - goto st0; - goto st2; -st4: - if ( ++p == pe ) + st_case_3: + if ( ( (*( p))) == 117 ) { + goto st4; + } + if ( 0 <= ( (*( p))) && ( (*( p))) <= 31 ) { + goto st0; + } + { + goto st2; + } + st4: + p+= 1; + if ( p == pe ) goto _test_eof4; -case 4: - if ( (*p) < 65 ) { - if ( 48 <= (*p) && (*p) <= 57 ) + st_case_4: + if ( ( (*( p))) < 65 ) { + if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { + goto st5; + } + } else if ( ( (*( p))) > 70 ) { + if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) { + goto st5; + } + } else { goto st5; - } else if ( (*p) > 70 ) { - if ( 97 <= (*p) && (*p) <= 102 ) - goto st5; - } else - goto st5; - goto st0; -st5: - if ( ++p == pe ) + } + { + goto st0; + } + st5: + p+= 1; + if ( p == pe ) goto _test_eof5; -case 5: - if ( (*p) < 65 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto st6; - } else if ( (*p) > 70 ) { - if ( 97 <= (*p) && (*p) <= 102 ) + st_case_5: + if ( ( (*( p))) < 65 ) { + if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { + goto st6; + } + } else if ( ( (*( p))) > 70 ) { + if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) { + goto st6; + } + } else { goto st6; - } else - goto st6; - goto st0; -st6: - if ( ++p == pe ) + } + { + goto st0; + } + st6: + p+= 1; + if ( p == pe ) goto _test_eof6; -case 6: - if ( (*p) < 65 ) { - if ( 48 <= (*p) && (*p) <= 57 ) + st_case_6: + if ( ( (*( p))) < 65 ) { + if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { + goto st7; + } + } else if ( ( (*( p))) > 70 ) { + if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) { + goto st7; + } + } else { goto st7; - } else if ( (*p) > 70 ) { - if ( 97 <= (*p) && (*p) <= 102 ) - goto st7; - } else - goto st7; - goto st0; -st7: - if ( ++p == pe ) + } + { + goto st0; + } + st7: + p+= 1; + if ( p == pe ) goto _test_eof7; -case 7: - if ( (*p) < 65 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto st2; - } else if ( (*p) > 70 ) { - if ( 97 <= (*p) && (*p) <= 102 ) + st_case_7: + if ( ( (*( p))) < 65 ) { + if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { + goto st2; + } + } else if ( ( (*( p))) > 70 ) { + if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) { + goto st2; + } + } else { goto st2; - } else - goto st2; - goto st0; + } + { + goto st0; + } + st_out: + _test_eof2: cs = 2; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + + _test_eof: {} + _out: {} + } + + #line 608 "parser.rl" + + + if (json->create_additions && RTEST(match_string = json->match_string)) { + VALUE klass; + VALUE memo = rb_ary_new2(2); + rb_ary_push(memo, *result); + rb_hash_foreach(match_string, match_i, memo); + klass = rb_ary_entry(memo, 1); + if (RTEST(klass)) { + *result = rb_funcall(klass, i_json_create, 1, *result); + } + } + + if (json->symbolize_names && json->parsing_name) { + *result = rb_str_intern(*result); + } else if (RB_TYPE_P(*result, T_STRING)) { + # if STR_UMINUS_DEDUPE_FROZEN + if (json->freeze) { + // Starting from MRI 2.8 it is preferable to freeze the string + // before deduplication so that it can be interned directly + // otherwise it would be duplicated first which is wasteful. + *result = rb_funcall(rb_str_freeze(*result), i_uminus, 0); + } + # elif STR_UMINUS_DEDUPE + if (json->freeze) { + // MRI 2.5 and older do not deduplicate strings that are already + // frozen. + *result = rb_funcall(*result, i_uminus, 0); + } + # else + rb_str_resize(*result, RSTRING_LEN(*result)); + # endif + } + if (cs >= JSON_string_first_final) { + return p + 1; + } else { + return NULL; } - _test_eof2: cs = 2; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - - _test_eof: {} - _out: {} - } - -#line 565 "parser.rl" - - if (json->create_additions && RTEST(match_string = json->match_string)) { - VALUE klass; - VALUE memo = rb_ary_new2(2); - rb_ary_push(memo, *result); - rb_hash_foreach(match_string, match_i, memo); - klass = rb_ary_entry(memo, 1); - if (RTEST(klass)) { - *result = rb_funcall(klass, i_json_create, 1, *result); - } - } - - if (json->symbolize_names && json->parsing_name) { - *result = rb_str_intern(*result); - } else if (RB_TYPE_P(*result, T_STRING)) { -# if STR_UMINUS_DEDUPE_FROZEN - if (json->freeze) { - // Starting from MRI 2.8 it is preferable to freeze the string - // before deduplication so that it can be interned directly - // otherwise it would be duplicated first which is wasteful. - *result = rb_funcall(rb_str_freeze(*result), i_uminus, 0); - } -# elif STR_UMINUS_DEDUPE - if (json->freeze) { - // MRI 2.5 and older do not deduplicate strings that are already - // frozen. - *result = rb_funcall(*result, i_uminus, 0); - } -# else - rb_str_resize(*result, RSTRING_LEN(*result)); -# endif - } - if (cs >= JSON_string_first_final) { - return p + 1; - } else { - return NULL; - } } /* - * Document-class: JSON::Ext::Parser - * - * This is the JSON parser implemented as a C extension. It can be configured - * to be used by setting - * - * JSON.parser = JSON::Ext::Parser - * - * with the method parser= in JSON. - * - */ +* Document-class: JSON::Ext::Parser +* +* This is the JSON parser implemented as a C extension. It can be configured +* to be used by setting +* +* JSON.parser = JSON::Ext::Parser +* +* with the method parser= in JSON. +* +*/ static VALUE convert_encoding(VALUE source) { -#ifdef HAVE_RUBY_ENCODING_H - rb_encoding *enc = rb_enc_get(source); - if (enc == rb_ascii8bit_encoding()) { - if (OBJ_FROZEN(source)) { - source = rb_str_dup(source); - } - FORCE_UTF8(source); - } else { - source = rb_str_conv_enc(source, rb_enc_get(source), rb_utf8_encoding()); - } -#endif - return source; + #ifdef HAVE_RUBY_ENCODING_H + rb_encoding *enc = rb_enc_get(source); + if (enc == rb_ascii8bit_encoding()) { + if (OBJ_FROZEN(source)) { + source = rb_str_dup(source); + } + FORCE_UTF8(source); + } else { + source = rb_str_conv_enc(source, rb_enc_get(source), rb_utf8_encoding()); + } + #endif + return source; } /* - * call-seq: new(source, opts => {}) - * - * Creates a new JSON::Ext::Parser instance for the string _source_. - * - * Creates a new JSON::Ext::Parser instance for the string _source_. - * - * It will be configured by the _opts_ hash. _opts_ can have the following - * keys: - * - * _opts_ can have the following keys: - * * *max_nesting*: The maximum depth of nesting allowed in the parsed data - * structures. Disable depth checking with :max_nesting => false|nil|0, it - * defaults to 100. - * * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in - * defiance of RFC 4627 to be parsed by the Parser. This option defaults to - * false. - * * *symbolize_names*: If set to true, returns symbols for the names - * (keys) in a JSON object. Otherwise strings are returned, which is - * also the default. It's not possible to use this option in - * conjunction with the *create_additions* option. - * * *create_additions*: If set to false, the Parser doesn't create - * additions even if a matching class and create_id was found. This option - * defaults to false. - * * *object_class*: Defaults to Hash - * * *array_class*: Defaults to Array - */ +* call-seq: new(source, opts => {}) +* +* Creates a new JSON::Ext::Parser instance for the string _source_. +* +* Creates a new JSON::Ext::Parser instance for the string _source_. +* +* It will be configured by the _opts_ hash. _opts_ can have the following +* keys: +* +* _opts_ can have the following keys: +* * *max_nesting*: The maximum depth of nesting allowed in the parsed data +* structures. Disable depth checking with :max_nesting => false|nil|0, it +* defaults to 100. +* * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in +* defiance of RFC 4627 to be parsed by the Parser. This option defaults to +* false. +* * *symbolize_names*: If set to true, returns symbols for the names +* (keys) in a JSON object. Otherwise strings are returned, which is +* also the default. It's not possible to use this option in +* conjunction with the *create_additions* option. +* * *create_additions*: If set to false, the Parser doesn't create +* additions even if a matching class and create_id was found. This option +* defaults to false. +* * *object_class*: Defaults to Hash +* * *array_class*: Defaults to Array +*/ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) { - VALUE source, opts; - GET_PARSER_INIT; - - if (json->Vsource) { - rb_raise(rb_eTypeError, "already initialized instance"); - } -#ifdef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH - rb_scan_args(argc, argv, "1:", &source, &opts); -#else - rb_scan_args(argc, argv, "11", &source, &opts); -#endif - if (!NIL_P(opts)) { -#ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH - opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash"); - if (NIL_P(opts)) { - rb_raise(rb_eArgError, "opts needs to be like a hash"); - } else { -#endif - VALUE tmp = ID2SYM(i_max_nesting); - if (option_given_p(opts, tmp)) { - VALUE max_nesting = rb_hash_aref(opts, tmp); - if (RTEST(max_nesting)) { - Check_Type(max_nesting, T_FIXNUM); - json->max_nesting = FIX2INT(max_nesting); - } else { - json->max_nesting = 0; - } - } else { - json->max_nesting = 100; - } - tmp = ID2SYM(i_allow_nan); - if (option_given_p(opts, tmp)) { - json->allow_nan = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; - } else { - json->allow_nan = 0; - } - tmp = ID2SYM(i_symbolize_names); - if (option_given_p(opts, tmp)) { - json->symbolize_names = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; - } else { - json->symbolize_names = 0; - } - tmp = ID2SYM(i_freeze); - if (option_given_p(opts, tmp)) { - json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; - } else { - json->freeze = 0; - } - tmp = ID2SYM(i_create_additions); - if (option_given_p(opts, tmp)) { - json->create_additions = RTEST(rb_hash_aref(opts, tmp)); - } else { - json->create_additions = 0; - } - if (json->symbolize_names && json->create_additions) { - rb_raise(rb_eArgError, - "options :symbolize_names and :create_additions cannot be " - " used in conjunction"); - } - tmp = ID2SYM(i_create_id); - if (option_given_p(opts, tmp)) { - json->create_id = rb_hash_aref(opts, tmp); - } else { - json->create_id = rb_funcall(mJSON, i_create_id, 0); - } - tmp = ID2SYM(i_object_class); - if (option_given_p(opts, tmp)) { - json->object_class = rb_hash_aref(opts, tmp); - } else { - json->object_class = Qnil; - } - tmp = ID2SYM(i_array_class); - if (option_given_p(opts, tmp)) { - json->array_class = rb_hash_aref(opts, tmp); - } else { - json->array_class = Qnil; - } - tmp = ID2SYM(i_decimal_class); - if (option_given_p(opts, tmp)) { - json->decimal_class = rb_hash_aref(opts, tmp); - } else { - json->decimal_class = Qnil; - } - tmp = ID2SYM(i_match_string); - if (option_given_p(opts, tmp)) { - VALUE match_string = rb_hash_aref(opts, tmp); - json->match_string = RTEST(match_string) ? match_string : Qnil; - } else { - json->match_string = Qnil; - } -#ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH - } -#endif - } else { - json->max_nesting = 100; - json->allow_nan = 0; - json->create_additions = 0; - json->create_id = rb_funcall(mJSON, i_create_id, 0); - json->object_class = Qnil; - json->array_class = Qnil; - json->decimal_class = Qnil; - } - source = convert_encoding(StringValue(source)); - StringValue(source); - json->len = RSTRING_LEN(source); - json->source = RSTRING_PTR(source);; - json->Vsource = source; - return self; + VALUE source, opts; + GET_PARSER_INIT; + + if (json->Vsource) { + rb_raise(rb_eTypeError, "already initialized instance"); + } + #ifdef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH + rb_scan_args(argc, argv, "1:", &source, &opts); + #else + rb_scan_args(argc, argv, "11", &source, &opts); + #endif + if (!NIL_P(opts)) { + #ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH + opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash"); + if (NIL_P(opts)) { + rb_raise(rb_eArgError, "opts needs to be like a hash"); + } else { + #endif + VALUE tmp = ID2SYM(i_max_nesting); + if (option_given_p(opts, tmp)) { + VALUE max_nesting = rb_hash_aref(opts, tmp); + if (RTEST(max_nesting)) { + Check_Type(max_nesting, T_FIXNUM); + json->max_nesting = FIX2INT(max_nesting); + } else { + json->max_nesting = 0; + } + } else { + json->max_nesting = 100; + } + tmp = ID2SYM(i_allow_nan); + if (option_given_p(opts, tmp)) { + json->allow_nan = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; + } else { + json->allow_nan = 0; + } + tmp = ID2SYM(i_symbolize_names); + if (option_given_p(opts, tmp)) { + json->symbolize_names = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; + } else { + json->symbolize_names = 0; + } + tmp = ID2SYM(i_freeze); + if (option_given_p(opts, tmp)) { + json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; + } else { + json->freeze = 0; + } + tmp = ID2SYM(i_create_additions); + if (option_given_p(opts, tmp)) { + json->create_additions = RTEST(rb_hash_aref(opts, tmp)); + } else { + json->create_additions = 0; + } + if (json->symbolize_names && json->create_additions) { + rb_raise(rb_eArgError, + "options :symbolize_names and :create_additions cannot be " + " used in conjunction"); + } + tmp = ID2SYM(i_create_id); + if (option_given_p(opts, tmp)) { + json->create_id = rb_hash_aref(opts, tmp); + } else { + json->create_id = rb_funcall(mJSON, i_create_id, 0); + } + tmp = ID2SYM(i_object_class); + if (option_given_p(opts, tmp)) { + json->object_class = rb_hash_aref(opts, tmp); + } else { + json->object_class = Qnil; + } + tmp = ID2SYM(i_array_class); + if (option_given_p(opts, tmp)) { + json->array_class = rb_hash_aref(opts, tmp); + } else { + json->array_class = Qnil; + } + tmp = ID2SYM(i_decimal_class); + if (option_given_p(opts, tmp)) { + json->decimal_class = rb_hash_aref(opts, tmp); + } else { + json->decimal_class = Qnil; + } + tmp = ID2SYM(i_match_string); + if (option_given_p(opts, tmp)) { + VALUE match_string = rb_hash_aref(opts, tmp); + json->match_string = RTEST(match_string) ? match_string : Qnil; + } else { + json->match_string = Qnil; + } + #ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH + } + #endif + } else { + json->max_nesting = 100; + json->allow_nan = 0; + json->create_additions = 0; + json->create_id = rb_funcall(mJSON, i_create_id, 0); + json->object_class = Qnil; + json->array_class = Qnil; + json->decimal_class = Qnil; + } + source = convert_encoding(StringValue(source)); + StringValue(source); + json->len = RSTRING_LEN(source); + json->source = RSTRING_PTR(source);; + json->Vsource = source; + return self; } -#line 1878 "parser.c" enum {JSON_start = 1}; enum {JSON_first_final = 10}; enum {JSON_error = 0}; enum {JSON_en_main = 1}; +static const char _JSON_nfa_targs[] = { + 0, 0 +}; -#line 786 "parser.rl" +static const char _JSON_nfa_offsets[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 +}; + +static const char _JSON_nfa_push_actions[] = { + 0, 0 +}; + +static const char _JSON_nfa_pop_trans[] = { + 0, 0 +}; + + +#line 829 "parser.rl" /* - * call-seq: parse() - * - * Parses the current JSON text _source_ and returns the complete data - * structure as a result. - */ +* call-seq: parse() +* +* Parses the current JSON text _source_ and returns the complete data +* structure as a result. +*/ static VALUE cParser_parse(VALUE self) { - char *p, *pe; - int cs = EVIL; - VALUE result = Qnil; - GET_PARSER; + char *p, *pe; + int cs = EVIL; + VALUE result = Qnil; + GET_PARSER; -#line 1903 "parser.c" { - cs = JSON_start; + cs = (int)JSON_start; } -#line 802 "parser.rl" - p = json->source; - pe = p + json->len; + #line 845 "parser.rl" + + p = json->source; + pe = p + json->len; -#line 1912 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { -st1: - if ( ++p == pe ) + switch ( cs ) + { + case 1: + goto st_case_1; + case 0: + goto st_case_0; + case 10: + goto st_case_10; + case 2: + goto st_case_2; + case 3: + goto st_case_3; + case 4: + goto st_case_4; + case 5: + goto st_case_5; + case 6: + goto st_case_6; + case 7: + goto st_case_7; + case 8: + goto st_case_8; + case 9: + goto st_case_9; + } + goto st_out; + st1: + p+= 1; + if ( p == pe ) goto _test_eof1; -case 1: - switch( (*p) ) { - case 13: goto st1; - case 32: goto st1; - case 34: goto tr2; - case 45: goto tr2; - case 47: goto st6; - case 73: goto tr2; - case 78: goto tr2; - case 91: goto tr2; - case 102: goto tr2; - case 110: goto tr2; - case 116: goto tr2; - case 123: goto tr2; - } - if ( (*p) > 10 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto tr2; - } else if ( (*p) >= 9 ) - goto st1; - goto st0; -st0: -cs = 0; - goto _out; -tr2: -#line 778 "parser.rl" - { - char *np = JSON_parse_value(json, p, pe, &result, 0); - if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;} - } - goto st10; -st10: - if ( ++p == pe ) - goto _test_eof10; -case 10: -#line 1956 "parser.c" - switch( (*p) ) { - case 13: goto st10; - case 32: goto st10; - case 47: goto st2; - } - if ( 9 <= (*p) && (*p) <= 10 ) + st_case_1: + switch( ( (*( p))) ) { + case 13: { + goto st1; + } + case 32: { + goto st1; + } + case 34: { + goto ctr2; + } + case 45: { + goto ctr2; + } + case 47: { + goto st6; + } + case 73: { + goto ctr2; + } + case 78: { + goto ctr2; + } + case 91: { + goto ctr2; + } + case 102: { + goto ctr2; + } + case 110: { + goto ctr2; + } + case 116: { + goto ctr2; + } + case 123: { + goto ctr2; + } + } + if ( ( (*( p))) > 10 ) { + if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { + goto ctr2; + } + } else if ( ( (*( p))) >= 9 ) { + goto st1; + } + { + goto st0; + } + st_case_0: + st0: + cs = 0; + goto _out; + ctr2: + { + #line 821 "parser.rl" + + char *np = JSON_parse_value(json, p, pe, &result, 0); + if (np == NULL) { {p = p - 1; } {p+= 1; cs = 10; goto _out;} } else {p = (( np))-1;} + + } + goto st10; - goto st0; -st2: - if ( ++p == pe ) + st10: + p+= 1; + if ( p == pe ) + goto _test_eof10; + st_case_10: + switch( ( (*( p))) ) { + case 13: { + goto st10; + } + case 32: { + goto st10; + } + case 47: { + goto st2; + } + } + if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { + goto st10; + } + { + goto st0; + } + st2: + p+= 1; + if ( p == pe ) goto _test_eof2; -case 2: - switch( (*p) ) { - case 42: goto st3; - case 47: goto st5; - } - goto st0; -st3: - if ( ++p == pe ) + st_case_2: + switch( ( (*( p))) ) { + case 42: { + goto st3; + } + case 47: { + goto st5; + } + } + { + goto st0; + } + st3: + p+= 1; + if ( p == pe ) goto _test_eof3; -case 3: - if ( (*p) == 42 ) - goto st4; - goto st3; -st4: - if ( ++p == pe ) + st_case_3: + if ( ( (*( p))) == 42 ) { + goto st4; + } + { + goto st3; + } + st4: + p+= 1; + if ( p == pe ) goto _test_eof4; -case 4: - switch( (*p) ) { - case 42: goto st4; - case 47: goto st10; - } - goto st3; -st5: - if ( ++p == pe ) + st_case_4: + switch( ( (*( p))) ) { + case 42: { + goto st4; + } + case 47: { + goto st10; + } + } + { + goto st3; + } + st5: + p+= 1; + if ( p == pe ) goto _test_eof5; -case 5: - if ( (*p) == 10 ) - goto st10; - goto st5; -st6: - if ( ++p == pe ) + st_case_5: + if ( ( (*( p))) == 10 ) { + goto st10; + } + { + goto st5; + } + st6: + p+= 1; + if ( p == pe ) goto _test_eof6; -case 6: - switch( (*p) ) { - case 42: goto st7; - case 47: goto st9; - } - goto st0; -st7: - if ( ++p == pe ) + st_case_6: + switch( ( (*( p))) ) { + case 42: { + goto st7; + } + case 47: { + goto st9; + } + } + { + goto st0; + } + st7: + p+= 1; + if ( p == pe ) goto _test_eof7; -case 7: - if ( (*p) == 42 ) - goto st8; - goto st7; -st8: - if ( ++p == pe ) + st_case_7: + if ( ( (*( p))) == 42 ) { + goto st8; + } + { + goto st7; + } + st8: + p+= 1; + if ( p == pe ) goto _test_eof8; -case 8: - switch( (*p) ) { - case 42: goto st8; - case 47: goto st1; - } - goto st7; -st9: - if ( ++p == pe ) + st_case_8: + switch( ( (*( p))) ) { + case 42: { + goto st8; + } + case 47: { + goto st1; + } + } + { + goto st7; + } + st9: + p+= 1; + if ( p == pe ) goto _test_eof9; -case 9: - if ( (*p) == 10 ) - goto st1; - goto st9; + st_case_9: + if ( ( (*( p))) == 10 ) { + goto st1; + } + { + goto st9; + } + st_out: + _test_eof1: cs = 1; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; + + _test_eof: {} + _out: {} + } + + #line 848 "parser.rl" + + + if (cs >= JSON_first_final && p == pe) { + return result; + } else { + rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p); + return Qnil; } - _test_eof1: cs = 1; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - - _test_eof: {} - _out: {} - } - -#line 805 "parser.rl" - - if (cs >= JSON_first_final && p == pe) { - return result; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p); - return Qnil; - } } static void JSON_mark(void *ptr) { - JSON_Parser *json = ptr; - rb_gc_mark_maybe(json->Vsource); - rb_gc_mark_maybe(json->create_id); - rb_gc_mark_maybe(json->object_class); - rb_gc_mark_maybe(json->array_class); - rb_gc_mark_maybe(json->decimal_class); - rb_gc_mark_maybe(json->match_string); + JSON_Parser *json = ptr; + rb_gc_mark_maybe(json->Vsource); + rb_gc_mark_maybe(json->create_id); + rb_gc_mark_maybe(json->object_class); + rb_gc_mark_maybe(json->array_class); + rb_gc_mark_maybe(json->decimal_class); + rb_gc_mark_maybe(json->match_string); } static void JSON_free(void *ptr) { - JSON_Parser *json = ptr; - fbuffer_free(json->fbuffer); - ruby_xfree(json); + JSON_Parser *json = ptr; + fbuffer_free(json->fbuffer); + ruby_xfree(json); } static size_t JSON_memsize(const void *ptr) { - const JSON_Parser *json = ptr; - return sizeof(*json) + FBUFFER_CAPA(json->fbuffer); + const JSON_Parser *json = ptr; + return sizeof(*json) + FBUFFER_CAPA(json->fbuffer); } #ifdef NEW_TYPEDDATA_WRAPPER static const rb_data_type_t JSON_Parser_type = { - "JSON/Parser", - {JSON_mark, JSON_free, JSON_memsize,}, -#ifdef RUBY_TYPED_FREE_IMMEDIATELY - 0, 0, - RUBY_TYPED_FREE_IMMEDIATELY, -#endif + "JSON/Parser", + {JSON_mark, JSON_free, JSON_memsize,}, + #ifdef RUBY_TYPED_FREE_IMMEDIATELY + 0, 0, + RUBY_TYPED_FREE_IMMEDIATELY, + #endif }; #endif static VALUE cJSON_parser_s_allocate(VALUE klass) { - JSON_Parser *json; - VALUE obj = TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json); - json->fbuffer = fbuffer_alloc(0); - return obj; + JSON_Parser *json; + VALUE obj = TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json); + json->fbuffer = fbuffer_alloc(0); + return obj; } /* - * call-seq: source() - * - * Returns a copy of the current _source_ string, that was used to construct - * this Parser. - */ +* call-seq: source() +* +* Returns a copy of the current _source_ string, that was used to construct +* this Parser. +*/ static VALUE cParser_source(VALUE self) { - GET_PARSER; - return rb_str_dup(json->Vsource); + GET_PARSER; + return rb_str_dup(json->Vsource); } void Init_parser(void) { -#ifdef HAVE_RB_EXT_RACTOR_SAFE - rb_ext_ractor_safe(true); -#endif - -#undef rb_intern - rb_require("json/common"); - mJSON = rb_define_module("JSON"); - mExt = rb_define_module_under(mJSON, "Ext"); - cParser = rb_define_class_under(mExt, "Parser", rb_cObject); - eParserError = rb_path2class("JSON::ParserError"); - eNestingError = rb_path2class("JSON::NestingError"); - rb_gc_register_mark_object(eParserError); - rb_gc_register_mark_object(eNestingError); - rb_define_alloc_func(cParser, cJSON_parser_s_allocate); - rb_define_method(cParser, "initialize", cParser_initialize, -1); - rb_define_method(cParser, "parse", cParser_parse, 0); - rb_define_method(cParser, "source", cParser_source, 0); - - CNaN = rb_const_get(mJSON, rb_intern("NaN")); - rb_gc_register_mark_object(CNaN); - - CInfinity = rb_const_get(mJSON, rb_intern("Infinity")); - rb_gc_register_mark_object(CInfinity); - - CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity")); - rb_gc_register_mark_object(CMinusInfinity); - - i_json_creatable_p = rb_intern("json_creatable?"); - i_json_create = rb_intern("json_create"); - i_create_id = rb_intern("create_id"); - i_create_additions = rb_intern("create_additions"); - i_chr = rb_intern("chr"); - i_max_nesting = rb_intern("max_nesting"); - i_allow_nan = rb_intern("allow_nan"); - i_symbolize_names = rb_intern("symbolize_names"); - i_object_class = rb_intern("object_class"); - i_array_class = rb_intern("array_class"); - i_decimal_class = rb_intern("decimal_class"); - i_match = rb_intern("match"); - i_match_string = rb_intern("match_string"); - i_key_p = rb_intern("key?"); - i_deep_const_get = rb_intern("deep_const_get"); - i_aset = rb_intern("[]="); - i_aref = rb_intern("[]"); - i_leftshift = rb_intern("<<"); - i_new = rb_intern("new"); - i_try_convert = rb_intern("try_convert"); - i_freeze = rb_intern("freeze"); - i_uminus = rb_intern("-@"); + #ifdef HAVE_RB_EXT_RACTOR_SAFE + rb_ext_ractor_safe(true); + #endif + + #undef rb_intern + rb_require("json/common"); + mJSON = rb_define_module("JSON"); + mExt = rb_define_module_under(mJSON, "Ext"); + cParser = rb_define_class_under(mExt, "Parser", rb_cObject); + eParserError = rb_path2class("JSON::ParserError"); + eNestingError = rb_path2class("JSON::NestingError"); + rb_gc_register_mark_object(eParserError); + rb_gc_register_mark_object(eNestingError); + rb_define_alloc_func(cParser, cJSON_parser_s_allocate); + rb_define_method(cParser, "initialize", cParser_initialize, -1); + rb_define_method(cParser, "parse", cParser_parse, 0); + rb_define_method(cParser, "source", cParser_source, 0); + + CNaN = rb_const_get(mJSON, rb_intern("NaN")); + rb_gc_register_mark_object(CNaN); + + CInfinity = rb_const_get(mJSON, rb_intern("Infinity")); + rb_gc_register_mark_object(CInfinity); + + CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity")); + rb_gc_register_mark_object(CMinusInfinity); + + i_json_creatable_p = rb_intern("json_creatable?"); + i_json_create = rb_intern("json_create"); + i_create_id = rb_intern("create_id"); + i_create_additions = rb_intern("create_additions"); + i_chr = rb_intern("chr"); + i_max_nesting = rb_intern("max_nesting"); + i_allow_nan = rb_intern("allow_nan"); + i_symbolize_names = rb_intern("symbolize_names"); + i_object_class = rb_intern("object_class"); + i_array_class = rb_intern("array_class"); + i_decimal_class = rb_intern("decimal_class"); + i_match = rb_intern("match"); + i_match_string = rb_intern("match_string"); + i_key_p = rb_intern("key?"); + i_deep_const_get = rb_intern("deep_const_get"); + i_aset = rb_intern("[]="); + i_aref = rb_intern("[]"); + i_leftshift = rb_intern("<<"); + i_new = rb_intern("new"); + i_try_convert = rb_intern("try_convert"); + i_freeze = rb_intern("freeze"); + i_uminus = rb_intern("-@"); } /* - * Local variables: - * mode: c - * c-file-style: ruby - * indent-tabs-mode: nil - * End: - */ +* Local variables: +* mode: c +* c-file-style: ruby +* indent-tabs-mode: nil +* End: +*/ diff --git a/ext/json/ext/parser/parser.h b/ext/json/ext/parser/parser.h index e3eb920cc..d2b074de8 100644 --- a/ext/json/ext/parser/parser.h +++ b/ext/json/ext/parser/parser.h @@ -63,7 +63,7 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result); static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result); static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting); -static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd); +static VALUE json_string_unescape(char *string, char *stringEnd); static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result); static VALUE convert_encoding(VALUE source); static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self); diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl index 1da70c544..230d72523 100644 --- a/ext/json/ext/parser/parser.rl +++ b/ext/json/ext/parser/parser.rl @@ -452,17 +452,30 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul } } -static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd) +static const size_t MAX_STACK_BUFFER_SIZE = 128; +static VALUE json_string_unescape(char *string, char *stringEnd) { - char *p = string, *pe = string, *unescape; + VALUE result = Qnil; + size_t bufferSize = stringEnd - string; + char *p = string, *pe = string, *unescape, *bufferStart, *buffer; int unescape_len; char buf[4]; + if (bufferSize > MAX_STACK_BUFFER_SIZE) { + buffer = xmalloc(bufferSize); + bufferStart = buffer; + } else { + bufferStart = buffer = alloca(bufferSize); + } + while (pe < stringEnd) { if (*pe == '\\') { unescape = (char *) "?"; unescape_len = 1; - if (pe > p) rb_str_buf_cat(result, p, pe - p); + if (pe > p) { + MEMCPY(buffer, p, char, pe - p); + buffer += pe - p; + } switch (*++pe) { case 'n': unescape = (char *) "\n"; @@ -487,6 +500,9 @@ static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd) break; case 'u': if (pe > stringEnd - 4) { + if (bufferSize > MAX_STACK_BUFFER_SIZE) { + free(bufferStart); + } rb_enc_raise( EXC_ENCODING eParserError, "%u: incomplete unicode character escape sequence at '%s'", __LINE__, p @@ -497,6 +513,9 @@ static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd) if (UNI_SUR_HIGH_START == (ch & 0xFC00)) { pe++; if (pe > stringEnd - 6) { + if (bufferSize > MAX_STACK_BUFFER_SIZE) { + free(bufferStart); + } rb_enc_raise( EXC_ENCODING eParserError, "%u: incomplete surrogate pair at '%s'", __LINE__, p @@ -520,13 +539,28 @@ static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd) p = pe; continue; } - rb_str_buf_cat(result, unescape, unescape_len); + MEMCPY(buffer, unescape, char, unescape_len); + buffer += unescape_len; p = ++pe; } else { pe++; } } - rb_str_buf_cat(result, p, pe - p); + + if (pe > p) { + MEMCPY(buffer, p, char, pe - p); + buffer += pe - p; + } + + #ifdef HAVE_RUBY_ENCODING_H + result = rb_utf8_str_new(bufferStart, buffer - bufferStart); + #else + result = rb_str_new(bufferStart, buffer - bufferStart); + #endif + + if (bufferSize > MAX_STACK_BUFFER_SIZE) { + free(bufferStart); + } return result; } @@ -537,12 +571,11 @@ static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd) write data; action parse_string { - *result = json_string_unescape(*result, json->memo + 1, p); + *result = json_string_unescape(json->memo + 1, p); if (NIL_P(*result)) { fhold; fbreak; } else { - FORCE_UTF8(*result); fexec p + 1; } } @@ -569,7 +602,6 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu int cs = EVIL; VALUE match_string; - *result = rb_str_buf_new(0); %% write init; json->memo = p; %% write exec; From 1982070cb84a38793277f6359387938d80e4d2c4 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 18 Nov 2020 11:59:27 +0100 Subject: [PATCH 0146/1010] Deduplicate strings inside json_string_unescape [ci 2] --- ext/json/ext/parser/extconf.rb | 1 + ext/json/ext/parser/parser.c | 84 ++++++++++++++++++---------------- ext/json/ext/parser/parser.h | 2 +- ext/json/ext/parser/parser.rl | 70 +++++++++++++++------------- 4 files changed, 85 insertions(+), 72 deletions(-) diff --git a/ext/json/ext/parser/extconf.rb b/ext/json/ext/parser/extconf.rb index f832b56a6..feb586e1b 100644 --- a/ext/json/ext/parser/extconf.rb +++ b/ext/json/ext/parser/extconf.rb @@ -2,6 +2,7 @@ require 'mkmf' have_func("rb_enc_raise", "ruby.h") +have_func("rb_enc_interned_str", "ruby.h") # checking if String#-@ (str_uminus) dedupes... ' begin diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 241ec0d26..a15d20a43 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -2354,7 +2354,7 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul } static const size_t MAX_STACK_BUFFER_SIZE = 128; -static VALUE json_string_unescape(char *string, char *stringEnd) +static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int symbolize) { VALUE result = Qnil; size_t bufferSize = stringEnd - string; @@ -2363,10 +2363,9 @@ static VALUE json_string_unescape(char *string, char *stringEnd) char buf[4]; if (bufferSize > MAX_STACK_BUFFER_SIZE) { - buffer = xmalloc(bufferSize); - bufferStart = buffer; + bufferStart = buffer = ALLOC_N(char, bufferSize); } else { - bufferStart = buffer = alloca(bufferSize); + bufferStart = buffer = ALLOCA_N(char, bufferSize); } while (pe < stringEnd) { @@ -2453,15 +2452,42 @@ static VALUE json_string_unescape(char *string, char *stringEnd) buffer += pe - p; } - #ifdef HAVE_RUBY_ENCODING_H - result = rb_utf8_str_new(bufferStart, buffer - bufferStart); - #else - result = rb_str_new(bufferStart, buffer - bufferStart); - #endif + # ifdef HAVE_RB_ENC_INTERNED_STR + if (intern) { + result = rb_enc_interned_str(bufferStart, (long)(buffer - bufferStart), rb_utf8_encoding()); + } else { + result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); + } + if (bufferSize > MAX_STACK_BUFFER_SIZE) { + free(bufferStart); + } + # else + result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); if (bufferSize > MAX_STACK_BUFFER_SIZE) { free(bufferStart); } + + if (intern) { + # if STR_UMINUS_DEDUPE_FROZEN + // Starting from MRI 2.8 it is preferable to freeze the string + // before deduplication so that it can be interned directly + // otherwise it would be duplicated first which is wasteful. + result = rb_funcall(rb_str_freeze(result), i_uminus, 0); + # elif STR_UMINUS_DEDUPE + // MRI 2.5 and older do not deduplicate strings that are already + // frozen. + result = rb_funcall(result, i_uminus, 0); + # else + result = rb_str_freeze(result); + # endif + } + # endif + + if (symbolize) { + result = rb_str_intern(result); + } + return result; } @@ -2490,7 +2516,7 @@ static const char _JSON_string_nfa_pop_trans[] = { }; -#line 586 "parser.rl" +#line 612 "parser.rl" static int @@ -2515,7 +2541,7 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu cs = (int)JSON_string_start; } - #line 606 "parser.rl" + #line 632 "parser.rl" json->memo = p; @@ -2576,9 +2602,9 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu } ctr2: { - #line 573 "parser.rl" + #line 599 "parser.rl" - *result = json_string_unescape(json->memo + 1, p); + *result = json_string_unescape(json->memo + 1, p, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names); if (NIL_P(*result)) { {p = p - 1; } {p+= 1; cs = 8; goto _out;} @@ -2588,7 +2614,7 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu } } { - #line 583 "parser.rl" + #line 609 "parser.rl" {p = p - 1; } {p+= 1; cs = 8; goto _out;} } goto st8; @@ -2703,7 +2729,7 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu _out: {} } - #line 608 "parser.rl" + #line 634 "parser.rl" if (json->create_additions && RTEST(match_string = json->match_string)) { @@ -2717,26 +2743,6 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu } } - if (json->symbolize_names && json->parsing_name) { - *result = rb_str_intern(*result); - } else if (RB_TYPE_P(*result, T_STRING)) { - # if STR_UMINUS_DEDUPE_FROZEN - if (json->freeze) { - // Starting from MRI 2.8 it is preferable to freeze the string - // before deduplication so that it can be interned directly - // otherwise it would be duplicated first which is wasteful. - *result = rb_funcall(rb_str_freeze(*result), i_uminus, 0); - } - # elif STR_UMINUS_DEDUPE - if (json->freeze) { - // MRI 2.5 and older do not deduplicate strings that are already - // frozen. - *result = rb_funcall(*result, i_uminus, 0); - } - # else - rb_str_resize(*result, RSTRING_LEN(*result)); - # endif - } if (cs >= JSON_string_first_final) { return p + 1; } else { @@ -2936,7 +2942,7 @@ static const char _JSON_nfa_pop_trans[] = { }; -#line 829 "parser.rl" +#line 835 "parser.rl" /* @@ -2957,7 +2963,7 @@ static VALUE cParser_parse(VALUE self) cs = (int)JSON_start; } - #line 845 "parser.rl" + #line 851 "parser.rl" p = json->source; pe = p + json->len; @@ -3050,7 +3056,7 @@ static VALUE cParser_parse(VALUE self) goto _out; ctr2: { - #line 821 "parser.rl" + #line 827 "parser.rl" char *np = JSON_parse_value(json, p, pe, &result, 0); if (np == NULL) { {p = p - 1; } {p+= 1; cs = 10; goto _out;} } else {p = (( np))-1;} @@ -3204,7 +3210,7 @@ static VALUE cParser_parse(VALUE self) _out: {} } - #line 848 "parser.rl" + #line 854 "parser.rl" if (cs >= JSON_first_final && p == pe) { diff --git a/ext/json/ext/parser/parser.h b/ext/json/ext/parser/parser.h index d2b074de8..0992bbf6e 100644 --- a/ext/json/ext/parser/parser.h +++ b/ext/json/ext/parser/parser.h @@ -63,7 +63,7 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result); static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result); static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting); -static VALUE json_string_unescape(char *string, char *stringEnd); +static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int symbolize); static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result); static VALUE convert_encoding(VALUE source); static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self); diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl index 230d72523..f7be1a5ac 100644 --- a/ext/json/ext/parser/parser.rl +++ b/ext/json/ext/parser/parser.rl @@ -453,7 +453,7 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul } static const size_t MAX_STACK_BUFFER_SIZE = 128; -static VALUE json_string_unescape(char *string, char *stringEnd) +static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int symbolize) { VALUE result = Qnil; size_t bufferSize = stringEnd - string; @@ -462,10 +462,9 @@ static VALUE json_string_unescape(char *string, char *stringEnd) char buf[4]; if (bufferSize > MAX_STACK_BUFFER_SIZE) { - buffer = xmalloc(bufferSize); - bufferStart = buffer; + bufferStart = buffer = ALLOC_N(char, bufferSize); } else { - bufferStart = buffer = alloca(bufferSize); + bufferStart = buffer = ALLOCA_N(char, bufferSize); } while (pe < stringEnd) { @@ -552,15 +551,42 @@ static VALUE json_string_unescape(char *string, char *stringEnd) buffer += pe - p; } - #ifdef HAVE_RUBY_ENCODING_H - result = rb_utf8_str_new(bufferStart, buffer - bufferStart); - #else - result = rb_str_new(bufferStart, buffer - bufferStart); - #endif +# ifdef HAVE_RB_ENC_INTERNED_STR + if (intern) { + result = rb_enc_interned_str(bufferStart, (long)(buffer - bufferStart), rb_utf8_encoding()); + } else { + result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); + } + if (bufferSize > MAX_STACK_BUFFER_SIZE) { + free(bufferStart); + } +# else + result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); - if (bufferSize > MAX_STACK_BUFFER_SIZE) { - free(bufferStart); + if (bufferSize > MAX_STACK_BUFFER_SIZE) { + free(bufferStart); + } + + if (intern) { + # if STR_UMINUS_DEDUPE_FROZEN + // Starting from MRI 2.8 it is preferable to freeze the string + // before deduplication so that it can be interned directly + // otherwise it would be duplicated first which is wasteful. + result = rb_funcall(rb_str_freeze(result), i_uminus, 0); + # elif STR_UMINUS_DEDUPE + // MRI 2.5 and older do not deduplicate strings that are already + // frozen. + result = rb_funcall(result, i_uminus, 0); + # else + result = rb_str_freeze(result); + # endif + } +# endif + + if (symbolize) { + result = rb_str_intern(result); } + return result; } @@ -571,7 +597,7 @@ static VALUE json_string_unescape(char *string, char *stringEnd) write data; action parse_string { - *result = json_string_unescape(json->memo + 1, p); + *result = json_string_unescape(json->memo + 1, p, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names); if (NIL_P(*result)) { fhold; fbreak; @@ -617,26 +643,6 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu } } - if (json->symbolize_names && json->parsing_name) { - *result = rb_str_intern(*result); - } else if (RB_TYPE_P(*result, T_STRING)) { -# if STR_UMINUS_DEDUPE_FROZEN - if (json->freeze) { - // Starting from MRI 2.8 it is preferable to freeze the string - // before deduplication so that it can be interned directly - // otherwise it would be duplicated first which is wasteful. - *result = rb_funcall(rb_str_freeze(*result), i_uminus, 0); - } -# elif STR_UMINUS_DEDUPE - if (json->freeze) { - // MRI 2.5 and older do not deduplicate strings that are already - // frozen. - *result = rb_funcall(*result, i_uminus, 0); - } -# else - rb_str_resize(*result, RSTRING_LEN(*result)); -# endif - } if (cs >= JSON_string_first_final) { return p + 1; } else { From 7b452b290502a5cca8fc6403f31275c83e0e3d48 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 23 Feb 2021 10:05:13 +0900 Subject: [PATCH 0147/1010] Bump version of required_ruby_version to 2.3. Fixes #464 --- json-java.gemspec | 2 +- json.gemspec | 2 +- json_pure.gemspec | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/json-java.gemspec b/json-java.gemspec index 8b6789300..259b908ce 100755 --- a/json-java.gemspec +++ b/json-java.gemspec @@ -24,7 +24,7 @@ spec = Gem::Specification.new do |s| 'wiki_uri' => 'https://github.com/flori/json/wiki' } - s.required_ruby_version = Gem::Requirement.new(">= 2.0") + s.required_ruby_version = Gem::Requirement.new(">= 2.3") s.test_files = ["tests/test_helper.rb"] s.add_development_dependency "rake" diff --git a/json.gemspec b/json.gemspec index d5f0a4de1..e5ef34079 100644 --- a/json.gemspec +++ b/json.gemspec @@ -65,7 +65,7 @@ Gem::Specification.new do |s| 'wiki_uri' => 'https://github.com/flori/json/wiki' } - s.required_ruby_version = Gem::Requirement.new(">= 2.0") + s.required_ruby_version = Gem::Requirement.new(">= 2.3") s.test_files = ["tests/test_helper.rb"] s.add_development_dependency "rake" diff --git a/json_pure.gemspec b/json_pure.gemspec index 872f8a0ac..3f3199da5 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -53,7 +53,7 @@ Gem::Specification.new do |s| 'wiki_uri' => 'https://github.com/flori/json/wiki' } - s.required_ruby_version = Gem::Requirement.new(">= 2.0".freeze) + s.required_ruby_version = Gem::Requirement.new(">= 2.3".freeze) s.test_files = ["./tests/test_helper.rb".freeze] s.add_development_dependency "rake" From 2a42c646a963ce559d20618e94a78244f799d376 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 21 Apr 2021 21:08:56 +0900 Subject: [PATCH 0148/1010] Removed executable bit --- json-java.gemspec | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 json-java.gemspec diff --git a/json-java.gemspec b/json-java.gemspec old mode 100755 new mode 100644 From a98c72a313483e1a0baebe99d2534904c19faf1f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 1 Feb 2021 11:10:22 -0800 Subject: [PATCH 0149/1010] Fix GC compatibility: Don't stash encodings in global constants This value should either be pinned, or looked up when needed at runtime. Without pinning, the GC may move the encoding object, and that could cause a crash. In this case it is easier to find the value at runtime, and there is no performance penalty (as Ruby caches encoding indexes). We can shorten the code, be compaction friendly, and incur no performance penalty. --- ext/json/ext/generator/generator.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 407c1af4a..e3a83472e 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -1,11 +1,6 @@ #include "../fbuffer/fbuffer.h" #include "generator.h" -#ifdef HAVE_RUBY_ENCODING_H -static VALUE CEncoding_UTF_8; -static ID i_encoding, i_encode; -#endif - static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject, mHash, mArray, #ifdef RUBY_INTEGER_UNIFICATION @@ -948,7 +943,7 @@ static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_S fbuffer_append_char(buffer, '"'); #ifdef HAVE_RUBY_ENCODING_H if (!enc_utf8_compatible_p(rb_enc_get(obj))) { - obj = rb_str_encode(obj, CEncoding_UTF_8, 0, Qnil); + obj = rb_str_export_to_enc(obj, rb_utf8_encoding()); } #endif if (state->ascii_only) { @@ -1610,9 +1605,4 @@ void Init_generator(void) i_match = rb_intern("match"); i_keys = rb_intern("keys"); i_dup = rb_intern("dup"); -#ifdef HAVE_RUBY_ENCODING_H - CEncoding_UTF_8 = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-8")); - i_encoding = rb_intern("encoding"); - i_encode = rb_intern("encode"); -#endif } From 6ebeeff92f77060082bac1f1afa71687e1cb03dd Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 26 Apr 2021 02:59:47 +0900 Subject: [PATCH 0150/1010] Fix some typos --- CHANGES.md | 10 +++++----- README.md | 4 ++-- java/src/json/ext/GeneratorState.java | 2 +- json_pure.gemspec | 2 +- lib/json/pure/parser.rb | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 04159a0f4..51df7726d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -83,7 +83,7 @@ ## 2015-09-11 (2.0.0) * Now complies to newest JSON RFC 7159. - * Implements compatibiliy to ruby 2.4 integer unification. + * Implements compatibility to ruby 2.4 integer unification. * Drops support for old rubies whose life has ended, that is rubies < 2.0. Also see https://www.ruby-lang.org/en/news/2014/07/01/eol-for-1-8-7-and-1-9-2/ * There were still some mentions of dual GPL licensing in the source, but JSON @@ -124,9 +124,9 @@ ## 2013-02-04 (1.7.7) * Security fix for JSON create_additions default value and `JSON::GenericObject`. It should not be possible to create additions unless - explicitely requested by setting the create_additions argument to true or + explicitly requested by setting the create_additions argument to true or using the JSON.load/dump interface. If `JSON::GenericObject` is supposed to - be automatically deserialised, this has to be explicitely enabled by + be automatically deserialised, this has to be explicitly enabled by setting JSON::GenericObject.json_creatable = true as well. @@ -272,7 +272,7 @@ ## 2010-04-23 (1.4.0) * Major speed improvements and building with simplified directory/file-structure. - * Extension should at least be comapatible with MRI, YARV and Rubinius. + * Extension should at least be compatible with MRI, YARV and Rubinius. ## 2010-04-07 (1.2.4) * Triger const_missing callback to make Rails' dynamic class loading work. @@ -290,7 +290,7 @@ strings in object names/keys. ## 2009-10-01 (1.2.0) - * `fast_generate` now raises an exeception for nan and infinite floats. + * `fast_generate` now raises an exception for nan and infinite floats. * On Ruby 1.8 json supports parsing of UTF-8, UTF-16BE, UTF-16LE, UTF-32BE, and UTF-32LE JSON documents now. Under Ruby 1.9 the M17n conversion functions are used to convert from all supported encodings. ASCII-8BIT diff --git a/README.md b/README.md index 682db2f25..e0205574f 100644 --- a/README.md +++ b/README.md @@ -267,7 +267,7 @@ the `pp` library's `pp` methods. The script `tools/server.rb` contains a small example if you want to test, how receiving a JSON object from a webrick server in your browser with the -javasript prototype library http://www.prototypejs.org works. +JavaScript prototype library http://www.prototypejs.org works. ## Speed Comparisons @@ -294,7 +294,7 @@ extension: ``` In the table above 1 is `JSON::Ext::Parser`, 2 is `YAML.load` with YAML -compatbile JSON document, 3 is is `JSON::Pure::Parser`, and 4 is +compatible JSON document, 3 is is `JSON::Pure::Parser`, and 4 is `ActiveSupport::JSON.decode`. The ActiveSupport JSON-decoder converts the input first to YAML and then uses the YAML-parser, the conversion seems to slow it down so much that it is only as fast as the `JSON::Pure::Parser`! diff --git a/java/src/json/ext/GeneratorState.java b/java/src/json/ext/GeneratorState.java index 7fec09d4f..a0541d674 100644 --- a/java/src/json/ext/GeneratorState.java +++ b/java/src/json/ext/GeneratorState.java @@ -508,7 +508,7 @@ public int decreaseDepth() { /** * Checks if the current depth is allowed as per this state's options. * @param context - * @param depth The corrent depth + * @param depth The current depth */ private void checkMaxNesting() { if (maxNesting != 0 && depth > maxNesting) { diff --git a/json_pure.gemspec b/json_pure.gemspec index 3f3199da5..146ef6706 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |s| s.email = "flori@ping.de".freeze s.extra_rdoc_files = ["README.md".freeze] - s.rdoc_options = ["--title".freeze, "JSON implemention for ruby".freeze, "--main".freeze, "README.md".freeze] + s.rdoc_options = ["--title".freeze, "JSON implementation for ruby".freeze, "--main".freeze, "README.md".freeze] s.files = [ "CHANGES.md".freeze, "Gemfile".freeze, diff --git a/lib/json/pure/parser.rb b/lib/json/pure/parser.rb index d795a3b92..e1d701b30 100644 --- a/lib/json/pure/parser.rb +++ b/lib/json/pure/parser.rb @@ -45,7 +45,7 @@ class Parser < StringScanner /(?=\*/) # single slash before this comment's end )* \*/ # the End of this comment - |[ \t\r\n]+ # whitespaces: space, horicontal tab, lf, cr + |[ \t\r\n]+ # whitespaces: space, horizontal tab, lf, cr )+ )mx From e2ad91fc2094d3cc2f76adc6c55d420cd06f34d8 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 18 May 2021 11:48:21 +0900 Subject: [PATCH 0151/1010] parser: fix warnings against code generated by ragel * type-limits when plain-char is unsigned * unused-const-variable for NFA constants --- Rakefile | 3 +- ext/json/ext/parser/parser.c | 60 ++++++++++++++++++------------------ ext/json/ext/parser/parser.h | 4 +++ 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/Rakefile b/Rakefile index 813a6d9b9..8de6dedfb 100644 --- a/Rakefile +++ b/Rakefile @@ -282,7 +282,8 @@ else end src = File.read("parser.c").gsub(/[ \t]+$/, '') src.gsub!(/^static const int (JSON_.*=.*);$/, 'enum {\1};') - src.gsub!(/0 <= \(\*p\) && \(\*p\) <= 31/, "0 <= (signed char)(*p) && (*p) <= 31") + src.gsub!(/^(static const char) (_JSON(?:_\w+)?_nfa_\w+)(?=\[\] =)/, '\1 MAYBE_UNUSED(\2)') + src.gsub!(/0 <= ([\( ]+\*[\( ]*p\)+) && \1 <= 31/, "0 <= (signed char)(*(p)) && (*(p)) <= 31") src[0, 0] = "/* This file is automatically generated from parser.rl by using ragel */" File.open("parser.c", "w") {|f| f.print src} end diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index a15d20a43..b1dc8810c 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -109,22 +109,22 @@ enum {JSON_object_error = 0}; enum {JSON_object_en_main = 1}; -static const char _JSON_object_nfa_targs[] = { +static const char MAYBE_UNUSED(_JSON_object_nfa_targs)[] = { 0, 0 }; -static const char _JSON_object_nfa_offsets[] = { +static const char MAYBE_UNUSED(_JSON_object_nfa_offsets)[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static const char _JSON_object_nfa_push_actions[] = { +static const char MAYBE_UNUSED(_JSON_object_nfa_push_actions)[] = { 0, 0 }; -static const char _JSON_object_nfa_pop_trans[] = { +static const char MAYBE_UNUSED(_JSON_object_nfa_pop_trans)[] = { 0, 0 }; @@ -767,22 +767,22 @@ enum {JSON_value_error = 0}; enum {JSON_value_en_main = 1}; -static const char _JSON_value_nfa_targs[] = { +static const char MAYBE_UNUSED(_JSON_value_nfa_targs)[] = { 0, 0 }; -static const char _JSON_value_nfa_offsets[] = { +static const char MAYBE_UNUSED(_JSON_value_nfa_offsets)[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static const char _JSON_value_nfa_push_actions[] = { +static const char MAYBE_UNUSED(_JSON_value_nfa_push_actions)[] = { 0, 0 }; -static const char _JSON_value_nfa_pop_trans[] = { +static const char MAYBE_UNUSED(_JSON_value_nfa_pop_trans)[] = { 0, 0 }; @@ -1429,19 +1429,19 @@ enum {JSON_integer_error = 0}; enum {JSON_integer_en_main = 1}; -static const char _JSON_integer_nfa_targs[] = { +static const char MAYBE_UNUSED(_JSON_integer_nfa_targs)[] = { 0, 0 }; -static const char _JSON_integer_nfa_offsets[] = { +static const char MAYBE_UNUSED(_JSON_integer_nfa_offsets)[] = { 0, 0, 0, 0, 0, 0, 0 }; -static const char _JSON_integer_nfa_push_actions[] = { +static const char MAYBE_UNUSED(_JSON_integer_nfa_push_actions)[] = { 0, 0 }; -static const char _JSON_integer_nfa_pop_trans[] = { +static const char MAYBE_UNUSED(_JSON_integer_nfa_pop_trans)[] = { 0, 0 }; @@ -1582,20 +1582,20 @@ enum {JSON_float_error = 0}; enum {JSON_float_en_main = 1}; -static const char _JSON_float_nfa_targs[] = { +static const char MAYBE_UNUSED(_JSON_float_nfa_targs)[] = { 0, 0 }; -static const char _JSON_float_nfa_offsets[] = { +static const char MAYBE_UNUSED(_JSON_float_nfa_offsets)[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static const char _JSON_float_nfa_push_actions[] = { +static const char MAYBE_UNUSED(_JSON_float_nfa_push_actions)[] = { 0, 0 }; -static const char _JSON_float_nfa_pop_trans[] = { +static const char MAYBE_UNUSED(_JSON_float_nfa_pop_trans)[] = { 0, 0 }; @@ -1893,21 +1893,21 @@ enum {JSON_array_error = 0}; enum {JSON_array_en_main = 1}; -static const char _JSON_array_nfa_targs[] = { +static const char MAYBE_UNUSED(_JSON_array_nfa_targs)[] = { 0, 0 }; -static const char _JSON_array_nfa_offsets[] = { +static const char MAYBE_UNUSED(_JSON_array_nfa_offsets)[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static const char _JSON_array_nfa_push_actions[] = { +static const char MAYBE_UNUSED(_JSON_array_nfa_push_actions)[] = { 0, 0 }; -static const char _JSON_array_nfa_pop_trans[] = { +static const char MAYBE_UNUSED(_JSON_array_nfa_pop_trans)[] = { 0, 0 }; @@ -2498,20 +2498,20 @@ enum {JSON_string_error = 0}; enum {JSON_string_en_main = 1}; -static const char _JSON_string_nfa_targs[] = { +static const char MAYBE_UNUSED(_JSON_string_nfa_targs)[] = { 0, 0 }; -static const char _JSON_string_nfa_offsets[] = { +static const char MAYBE_UNUSED(_JSON_string_nfa_offsets)[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static const char _JSON_string_nfa_push_actions[] = { +static const char MAYBE_UNUSED(_JSON_string_nfa_push_actions)[] = { 0, 0 }; -static const char _JSON_string_nfa_pop_trans[] = { +static const char MAYBE_UNUSED(_JSON_string_nfa_pop_trans)[] = { 0, 0 }; @@ -2594,7 +2594,7 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu goto st3; } } - if ( 0 <= ( (*( p))) && ( (*( p))) <= 31 ) { + if ( 0 <= (signed char)(*(p)) && (*(p)) <= 31 ) { goto st0; } { @@ -2634,7 +2634,7 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu if ( ( (*( p))) == 117 ) { goto st4; } - if ( 0 <= ( (*( p))) && ( (*( p))) <= 31 ) { + if ( 0 <= (signed char)(*(p)) && (*(p)) <= 31 ) { goto st0; } { @@ -2924,20 +2924,20 @@ enum {JSON_error = 0}; enum {JSON_en_main = 1}; -static const char _JSON_nfa_targs[] = { +static const char MAYBE_UNUSED(_JSON_nfa_targs)[] = { 0, 0 }; -static const char _JSON_nfa_offsets[] = { +static const char MAYBE_UNUSED(_JSON_nfa_offsets)[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static const char _JSON_nfa_push_actions[] = { +static const char MAYBE_UNUSED(_JSON_nfa_push_actions)[] = { 0, 0 }; -static const char _JSON_nfa_pop_trans[] = { +static const char MAYBE_UNUSED(_JSON_nfa_pop_trans)[] = { 0, 0 }; diff --git a/ext/json/ext/parser/parser.h b/ext/json/ext/parser/parser.h index 0992bbf6e..92ed3fdc5 100644 --- a/ext/json/ext/parser/parser.h +++ b/ext/json/ext/parser/parser.h @@ -13,6 +13,10 @@ #include "st.h" #endif +#ifndef MAYBE_UNUSED +# define MAYBE_UNUSED(x) x +#endif + #define option_given_p(opts, key) RTEST(rb_funcall(opts, i_key_p, 1, key)) /* unicode */ From 615da3b07af694045384d1c25c23c0acf86ba0df Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 27 May 2021 16:33:35 +0900 Subject: [PATCH 0152/1010] Update the status badge for CI --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e0205574f..288ccdfcb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # JSON implementation for Ruby -[![Travis Widget](http://travis-ci.org/flori/json.svg?branch=master)](https://travis-ci.org/flori/json) +[![CI](https://github.com/flori/json/actions/workflows/ci.yml/badge.svg)](https://github.com/flori/json/actions/workflows/ci.yml) ## Description From 29f8354d6408453335998cc919f932cb285cc42d Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 27 May 2021 16:35:06 +0900 Subject: [PATCH 0153/1010] Removed debug environment --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c278a1cc8..70a39c87d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,6 @@ jobs: - 2.5 - 2.4 - 2.3 - - debug include: - { os: windows-latest , ruby: mingw } - { os: windows-latest , ruby: mswin } @@ -33,7 +32,6 @@ jobs: - { os: ubuntu-latest , ruby: truffleruby } exclude: - { os: windows-latest , ruby: '3.0' } - - { os: windows-latest , ruby: debug } steps: - uses: actions/checkout@v2 From c7dd0b18f870ab7701ececd66f2df5d1bd36b0e7 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 27 May 2021 16:35:22 +0900 Subject: [PATCH 0154/1010] Removed mswin and mingw because they are broken now --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 70a39c87d..da36a91f9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,8 +25,6 @@ jobs: - 2.4 - 2.3 include: - - { os: windows-latest , ruby: mingw } - - { os: windows-latest , ruby: mswin } - { os: ubuntu-latest , ruby: jruby-9.1 } # Ruby 2.3 - { os: ubuntu-latest , ruby: jruby-9.2 } # Ruby 2.5 - { os: ubuntu-latest , ruby: truffleruby } From c4ae05bba8859c4d1b736e7493ccd9600921b272 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 27 May 2021 16:35:49 +0900 Subject: [PATCH 0155/1010] Try to enable Windows with Ruby 3.0 --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index da36a91f9..5ed36dc19 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,8 +28,6 @@ jobs: - { os: ubuntu-latest , ruby: jruby-9.1 } # Ruby 2.3 - { os: ubuntu-latest , ruby: jruby-9.2 } # Ruby 2.5 - { os: ubuntu-latest , ruby: truffleruby } - exclude: - - { os: windows-latest , ruby: '3.0' } steps: - uses: actions/checkout@v2 From 099433d0314a96cb0c9ecfa08c77888924c72268 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 6 Jul 2021 14:45:37 +0900 Subject: [PATCH 0156/1010] Removed needless files from gemspec --- json-java.gemspec | 3 +-- json.gemspec | 5 +---- json_pure.gemspec | 5 +---- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/json-java.gemspec b/json-java.gemspec index 259b908ce..7ff9c3b0b 100644 --- a/json-java.gemspec +++ b/json-java.gemspec @@ -12,7 +12,7 @@ spec = Gem::Specification.new do |s| s.platform = 'java' - s.files = Dir["{docs,lib,tests}/**/*", "LICENSE"] + s.files = Dir["lib/**/*", "LICENSE"] s.homepage = "http://flori.github.com/json" s.metadata = { @@ -25,7 +25,6 @@ spec = Gem::Specification.new do |s| } s.required_ruby_version = Gem::Requirement.new(">= 2.3") - s.test_files = ["tests/test_helper.rb"] s.add_development_dependency "rake" s.add_development_dependency "test-unit" diff --git a/json.gemspec b/json.gemspec index e5ef34079..4095789cb 100644 --- a/json.gemspec +++ b/json.gemspec @@ -15,7 +15,6 @@ Gem::Specification.new do |s| s.rdoc_options = ["--title", "JSON implementation for Ruby", "--main", "README.md"] s.files = [ "CHANGES.md", - "Gemfile", "LICENSE", "README.md", "VERSION", @@ -48,13 +47,12 @@ Gem::Specification.new do |s| "lib/json/add/time.rb", "lib/json/common.rb", "lib/json/ext.rb", - "lib/json/ext/.keep", "lib/json/generic_object.rb", "lib/json/pure.rb", "lib/json/pure/generator.rb", "lib/json/pure/parser.rb", "lib/json/version.rb", - ] + Dir["tests/**/*"] + ] s.homepage = "http://flori.github.com/json" s.metadata = { 'bug_tracker_uri' => 'https://github.com/flori/json/issues', @@ -66,7 +64,6 @@ Gem::Specification.new do |s| } s.required_ruby_version = Gem::Requirement.new(">= 2.3") - s.test_files = ["tests/test_helper.rb"] s.add_development_dependency "rake" s.add_development_dependency "test-unit" diff --git a/json_pure.gemspec b/json_pure.gemspec index 146ef6706..13168f087 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -14,7 +14,6 @@ Gem::Specification.new do |s| s.rdoc_options = ["--title".freeze, "JSON implementation for ruby".freeze, "--main".freeze, "README.md".freeze] s.files = [ "CHANGES.md".freeze, - "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "VERSION".freeze, @@ -36,13 +35,12 @@ Gem::Specification.new do |s| "lib/json/add/time.rb".freeze, "lib/json/common.rb".freeze, "lib/json/ext.rb".freeze, - "lib/json/ext/.keep".freeze, "lib/json/generic_object.rb".freeze, "lib/json/pure.rb".freeze, "lib/json/pure/generator.rb".freeze, "lib/json/pure/parser.rb".freeze, "lib/json/version.rb".freeze, - ] + Dir["tests/**/*"] + ] s.homepage = "http://flori.github.com/json".freeze s.metadata = { 'bug_tracker_uri' => 'https://github.com/flori/json/issues', @@ -54,7 +52,6 @@ Gem::Specification.new do |s| } s.required_ruby_version = Gem::Requirement.new(">= 2.3".freeze) - s.test_files = ["./tests/test_helper.rb".freeze] s.add_development_dependency "rake" s.add_development_dependency "test-unit" From 882beccef601172782a393ddd1393558dddc4bc2 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 6 Jul 2021 14:51:37 +0900 Subject: [PATCH 0157/1010] Use Gemfile instead of Gem::Specification#add_development_dependency --- Gemfile | 3 +++ json-java.gemspec | 3 --- json.gemspec | 3 --- json_pure.gemspec | 3 --- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/Gemfile b/Gemfile index 8fe591e03..721964460 100644 --- a/Gemfile +++ b/Gemfile @@ -12,3 +12,6 @@ when 'ext', nil when 'pure' gemspec :name => 'json_pure' end + +gem "rake" +gem "test-unit" diff --git a/json-java.gemspec b/json-java.gemspec index 7ff9c3b0b..1ddfdc951 100644 --- a/json-java.gemspec +++ b/json-java.gemspec @@ -25,9 +25,6 @@ spec = Gem::Specification.new do |s| } s.required_ruby_version = Gem::Requirement.new(">= 2.3") - - s.add_development_dependency "rake" - s.add_development_dependency "test-unit" end if $0 == __FILE__ diff --git a/json.gemspec b/json.gemspec index 4095789cb..948e92c50 100644 --- a/json.gemspec +++ b/json.gemspec @@ -64,7 +64,4 @@ Gem::Specification.new do |s| } s.required_ruby_version = Gem::Requirement.new(">= 2.3") - - s.add_development_dependency "rake" - s.add_development_dependency "test-unit" end diff --git a/json_pure.gemspec b/json_pure.gemspec index 13168f087..e6e291454 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -52,7 +52,4 @@ Gem::Specification.new do |s| } s.required_ruby_version = Gem::Requirement.new(">= 2.3".freeze) - - s.add_development_dependency "rake" - s.add_development_dependency "test-unit" end From 1942689b6719af1371f54814999d25307696f71c Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 14 Oct 2021 16:44:36 +0900 Subject: [PATCH 0158/1010] Bump up json version to 2.6.0 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 73462a5a1..e70b4523a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.5.1 +2.6.0 From 739e06472270180e2b0920d827237b36ab65bb5b Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 14 Oct 2021 19:33:47 +0900 Subject: [PATCH 0159/1010] Delete CHANGES.md because there is no maintanance resource --- CHANGES.md | 446 ----------------------------------------------------- 1 file changed, 446 deletions(-) delete mode 100644 CHANGES.md diff --git a/CHANGES.md b/CHANGES.md deleted file mode 100644 index 51df7726d..000000000 --- a/CHANGES.md +++ /dev/null @@ -1,446 +0,0 @@ -# Changes - -## 2020-12-22 (2.5.1) - -* Restore the compatibility for constants of JSON class. - -## 2020-12-22 (2.5.0) - -* Ready to Ractor-safe at Ruby 3.0. - -## 2020-12-17 (2.4.1) - -* Restore version.rb with 2.4.1 - -## 2020-12-15 (2.4.0) - -* Implement a freeze: parser option #447 -* Fix an issue with generate_pretty and empty objects in the Ruby and Java implementations #449 -* Fix JSON.load_file doc #448 -* Fix pure parser with unclosed arrays / objects #425 -* bundle the LICENSE file in the gem #444 -* Add an option to escape forward slash character #405 -* RDoc for JSON #439 #446 #442 #434 #433 #430 - -## 2020-06-30 (2.3.1) - -* Spelling and grammar fixes for comments. Pull request #191 by Josh - Kline. -* Enhance generic JSON and #generate docs. Pull request #347 by Victor - Shepelev. -* Add :nodoc: for GeneratorMethods. Pull request #349 by Victor Shepelev. -* Baseline changes to help (JRuby) development. Pull request #371 by Karol - Bucek. -* Add metadata for rubygems.org. Pull request #379 by Alexandre ZANNI. -* Remove invalid JSON.generate description from JSON module rdoc. Pull - request #384 by Jeremy Evans. -* Test with TruffleRuby in CI. Pull request #402 by Benoit Daloze. -* Rdoc enhancements. Pull request #413 by Burdette Lamar. -* Fixtures/ are not being tested... Pull request #416 by Marc-André - Lafortune. -* Use frozen string for hash key. Pull request #420 by Marc-André - Lafortune. -* Added :call-seq: to RDoc for some methods. Pull request #422 by Burdette - Lamar. -* Small typo fix. Pull request #423 by Marc-André Lafortune. - -## 2019-12-11 (2.3.0) - * Fix default of `create_additions` to always be `false` for `JSON(user_input)` - and `JSON.parse(user_input, nil)`. - Note that `JSON.load` remains with default `true` and is meant for internal - serialization of trusted data. [CVE-2020-10663] - * Fix passing args all #to_json in json/add/*. - * Fix encoding issues - * Fix issues of keyword vs positional parameter - * Fix JSON::Parser against bigdecimal updates - * Bug fixes to JRuby port - -## 2019-02-21 (2.2.0) - * Adds support for 2.6 BigDecimal and ruby standard library Set datetype. - -## 2017-04-18 (2.1.0) - * Allow passing of `decimal_class` option to specify a class as which to parse - JSON float numbers. -## 2017-03-23 (2.0.4) - * Raise exception for incomplete unicode surrogates/character escape - sequences. This problem was reported by Daniel Gollahon (dgollahon). - * Fix arbitrary heap exposure problem. This problem was reported by Ahmad - Sherif (ahmadsherif). - -## 2017-01-12 (2.0.3) - * Set `required_ruby_version` to 1.9 - * Some small fixes - -## 2016-07-26 (2.0.2) - * Specify `required_ruby_version` for json\_pure. - * Fix issue #295 failure when parsing frozen strings. - -## 2016-07-01 (2.0.1) - * Fix problem when requiring json\_pure and Parser constant was defined top - level. - * Add `RB_GC_GUARD` to avoid possible GC problem via Pete Johns. - * Store `current_nesting` on stack by Aaron Patterson. - -## 2015-09-11 (2.0.0) - * Now complies to newest JSON RFC 7159. - * Implements compatibility to ruby 2.4 integer unification. - * Drops support for old rubies whose life has ended, that is rubies < 2.0. - Also see https://www.ruby-lang.org/en/news/2014/07/01/eol-for-1-8-7-and-1-9-2/ - * There were still some mentions of dual GPL licensing in the source, but JSON - has just the Ruby license that itself includes an explicit dual-licensing - clause that allows covered software to be distributed under the terms of - the Simplified BSD License instead for all ruby versions >= 1.9.3. This is - however a GPL compatible license according to the Free Software Foundation. - I changed these mentions to be consistent with the Ruby license setting in - the gemspec files which were already correct now. - -## 2015-06-01 (1.8.3) - * Fix potential memory leak, thx to nobu. - -## 2015-01-08 (1.8.2) - * Some performance improvements by Vipul A M . - * Fix by Jason R. Clark to avoid mutation of - `JSON.dump_default_options`. - * More tests by Michael Mac-Vicar and fixing - `space_before` accessor in generator. - * Performance on Jruby improved by Ben Browning . - * Some fixes to be compatible with the new Ruby 2.2 by Zachary Scott - and SHIBATA Hiroshi . - -## 2013-05-13 (1.8.1) - * Remove Rubinius exception since transcoding should be working now. - -## 2013-05-13 (1.8.0) - * Fix https://github.com/flori/json/issues/162 reported by Marc-Andre - Lafortune . Thanks! - * Applied patches by Yui NARUSE to suppress warning with - -Wchar-subscripts and better validate UTF-8 strings. - * Applied patch by ginriki@github to remove unnecessary if. - * Add load/dump interface to `JSON::GenericObject` to make - serialize :some_attribute, `JSON::GenericObject` - work in Rails active models for convenient `SomeModel#some_attribute.foo.bar` - access to serialised JSON data. - -## 2013-02-04 (1.7.7) - * Security fix for JSON create_additions default value and - `JSON::GenericObject`. It should not be possible to create additions unless - explicitly requested by setting the create_additions argument to true or - using the JSON.load/dump interface. If `JSON::GenericObject` is supposed to - be automatically deserialised, this has to be explicitly enabled by - setting - JSON::GenericObject.json_creatable = true - as well. - * Remove useless assert in fbuffer implementation. - * Apply patch attached to https://github.com/flori/json/issues#issue/155 - provided by John Shahid , Thx! - * Add license information to rubygems spec data, reported by Jordi Massaguer Pla . - * Improve documentation, thx to Zachary Scott . - -## 2012-11-29 (1.7.6) - * Add `GeneratorState#merge` alias for JRuby, fix state accessor methods. Thx to - jvshahid@github. - * Increase hash likeness of state objects. - -## 2012-08-17 (1.7.5) - * Fix compilation of extension on older rubies. - -## 2012-07-26 (1.7.4) - * Fix compilation problem on AIX, see https://github.com/flori/json/issues/142 - -## 2012-05-12 (1.7.3) - * Work around Rubinius encoding issues using iconv for conversion instead. - -## 2012-05-11 (1.7.2) - * Fix some encoding issues, that cause problems for the pure and the - extension variant in jruby 1.9 mode. - -## 2012-04-28 (1.7.1) - * Some small fixes for building - -## 2012-04-28 (1.7.0) - * Add `JSON::GenericObject` for method access to objects transmitted via JSON. - -## 2012-04-27 (1.6.7) - * Fix possible crash when trying to parse nil value. - -## 2012-02-11 (1.6.6) - * Propagate src encoding to values made from it (fixes 1.9 mode converting - everything to ascii-8bit; harmless for 1.8 mode too) (Thomas E. Enebo - ), should fix - https://github.com/flori/json/issues#issue/119. - * Fix https://github.com/flori/json/issues#issue/124 Thx to Jason Hutchens. - * Fix https://github.com/flori/json/issues#issue/117 - -## 2012-01-15 (1.6.5) - * Vit Ondruch reported a bug that shows up when using - optimisation under GCC 4.7. Thx to him, Bohuslav Kabrda - and Yui NARUSE for debugging and - developing a patch fix. - -## 2011-12-24 (1.6.4) - * Patches that improve speed on JRuby contributed by Charles Oliver Nutter - . - * Support `object_class`/`array_class` with duck typed hash/array. - -## 2011-12-01 (1.6.3) - * Let `JSON.load('')` return nil as well to make mysql text columns (default to - `''`) work better for serialization. - -## 2011-11-21 (1.6.2) - * Add support for OpenStruct and BigDecimal. - * Fix bug when parsing nil in `quirks_mode`. - * Make JSON.dump and JSON.load methods better cooperate with Rails' serialize - method. Just use: - serialize :value, JSON - * Fix bug with time serialization concerning nanoseconds. Thanks for the - patch go to Josh Partlow (jpartlow@github). - * Improve parsing speed for JSON numbers (integers and floats) in a similar way to - what Evan Phoenix suggested in: - https://github.com/flori/json/pull/103 - -## 2011-09-18 (1.6.1) - * Using -target 1.5 to force Java bits to compile with 1.5. - -## 2011-09-12 (1.6.0) - * Extract utilities (prettifier and GUI-editor) in its own gem json-utils. - * Split json/add/core into different files for classes to be serialised. - -## 2011-08-31 (1.5.4) - * Fix memory leak when used from multiple JRuby. (Patch by - jfirebaugh@github). - * Apply patch by Eric Wong that fixes garbage collection problem - reported in https://github.com/flori/json/issues/46. - * Add :quirks_mode option to parser and generator. - * Add support for Rational and Complex number additions via json/add/complex - and json/add/rational requires. - -## 2011-06-20 (1.5.3) - * Alias State#configure method as State#merge to increase duck type synonymy with Hash. - * Add `as_json` methods in json/add/core, so rails can create its json objects - the new way. - -## 2011-05-11 (1.5.2) - * Apply documentation patch by Cory Monty . - * Add gemspecs for json and json\_pure. - * Fix bug in jruby pretty printing. - * Fix bug in `object_class` and `array_class` when inheriting from Hash or - Array. - -## 2011-01-24 (1.5.1) - * Made rake-compiler build a fat binary gem. This should fix issue - https://github.com/flori/json/issues#issue/54. - -## 2011-01-22 (1.5.0) - * Included Java source codes for the Jruby extension made by Daniel Luz - . - * Output full exception message of `deep_const_get` to aid debugging. - * Fixed an issue with ruby 1.9 `Module#const_defined?` method, that was - reported by Riley Goodside. - -## 2010-08-09 (1.4.6) - * Fixed oversight reported in http://github.com/flori/json/issues/closed#issue/23, - always create a new object from the state prototype. - * Made pure and ext api more similar again. - -## 2010-08-07 (1.4.5) - * Manage data structure nesting depth in state object during generation. This - should reduce problems with `to_json` method definіtions that only have one - argument. - * Some fixes in the state objects and additional tests. -## 2010-08-06 (1.4.4) - * Fixes build problem for rubinius under OS X, http://github.com/flori/json/issues/closed#issue/25 - * Fixes crashes described in http://github.com/flori/json/issues/closed#issue/21 and - http://github.com/flori/json/issues/closed#issue/23 -## 2010-05-05 (1.4.3) - * Fixed some test assertions, from Ruby r27587 and r27590, patch by nobu. - * Fixed issue http://github.com/flori/json/issues/#issue/20 reported by - electronicwhisper@github. Thx! - -## 2010-04-26 (1.4.2) - * Applied patch from naruse Yui NARUSE to make building with - Microsoft Visual C possible again. - * Applied patch from devrandom in order to allow building of - json_pure if extensiontask is not present. - * Thanks to Dustin Schneider , who reported a memory - leak, which is fixed in this release. - * Applied 993f261ccb8f911d2ae57e9db48ec7acd0187283 patch from josh@github. - -## 2010-04-25 (1.4.1) - * Fix for a bug reported by Dan DeLeo , caused by T_FIXNUM - being different on 32bit/64bit architectures. - -## 2010-04-23 (1.4.0) - * Major speed improvements and building with simplified - directory/file-structure. - * Extension should at least be compatible with MRI, YARV and Rubinius. - -## 2010-04-07 (1.2.4) - * Triger const_missing callback to make Rails' dynamic class loading work. - -## 2010-03-11 (1.2.3) - * Added a `State#[]` method which returns an attribute's value in order to - increase duck type compatibility to Hash. - -## 2010-02-27 (1.2.2) - * Made some changes to make the building of the parser/generator compatible - to Rubinius. - -## 2009-11-25 (1.2.1) - * Added `:symbolize_names` option to Parser, which returns symbols instead of - strings in object names/keys. - -## 2009-10-01 (1.2.0) - * `fast_generate` now raises an exception for nan and infinite floats. - * On Ruby 1.8 json supports parsing of UTF-8, UTF-16BE, UTF-16LE, UTF-32BE, - and UTF-32LE JSON documents now. Under Ruby 1.9 the M17n conversion - functions are used to convert from all supported encodings. ASCII-8BIT - encoded strings are handled like all strings under Ruby 1.8 were. - * Better documentation - -## 2009-08-23 (1.1.9) - * Added forgotten main doc file `extra_rdoc_files`. - -## 2009-08-23 (1.1.8) - * Applied a patch by OZAWA Sakuro to make json/pure - work in environments that don't provide iconv. - * Applied patch by okkez_ in order to fix Ruby Bug #1768: - http://redmine.ruby-lang.org/issues/show/1768. - * Finally got around to avoid the rather paranoid escaping of ?/ characters - in the generator's output. The parsers aren't affected by this change. - Thanks to Rich Apodaca for the suggestion. - -## 2009-06-29 (1.1.7) - * Security Fix for JSON::Pure::Parser. A specially designed string could - cause catastrophic backtracking in one of the parser's regular expressions - in earlier 1.1.x versions. JSON::Ext::Parser isn't affected by this issue. - Thanks to Bartosz Blimke for reporting this - problem. - * This release also uses a less strict ruby version requirement for the - creation of the mswin32 native gem. - -## 2009-05-10 (1.1.6) - * No changes. І tested native linux gems in the last release and they don't - play well with different ruby versions other than the one the gem was built - with. This release is just to bump the version number in order to skip the - native gem on rubyforge. - -## 2009-05-10 (1.1.5) - * Started to build gems with rake-compiler gem. - * Applied patch object/array class patch from Brian Candler - and fixes. - -## 2009-04-01 (1.1.4) - * Fixed a bug in the creation of serialized generic rails objects reported by - Friedrich Graeter . - * Deleted tests/runner.rb, we're using testrb instead. - * Editor supports Infinity in numbers now. - * Made some changes in order to get the library to compile/run under Ruby - 1.9. - * Improved speed of the code path for the fast_generate method in the pure - variant. - -## 2008-07-10 (1.1.3) - * Wesley Beary reported a bug in json/add/core's DateTime - handling: If the nominator and denominator of the offset were divisible by - each other Ruby's Rational#to_s returns them as an integer not a fraction - with '/'. This caused a ZeroDivisionError during parsing. - * Use Date#start and DateTime#start instead of sg method, while - remaining backwards compatible. - * Supports ragel >= 6.0 now. - * Corrected some tests. - * Some minor changes. - -## 2007-11-27 (1.1.2) - * Remember default dir (last used directory) in editor. - * JSON::Editor.edit method added, the editor can now receive json texts from - the clipboard via C-v. - * Load json texts from an URL pasted via middle button press. - * Added :create_additions option to Parser. This makes it possible to disable - the creation of additions by force, in order to treat json texts as data - while having additions loaded. - * Jacob Maine reported, that JSON(:foo) outputs a JSON - object if the rails addition is enabled, which is wrong. It now outputs a - JSON string "foo" instead, like suggested by Jacob Maine. - * Discovered a bug in the Ruby Bugs Tracker on rubyforge, that was reported - by John Evans lgastako@gmail.com. He could produce a crash in the JSON - generator by returning something other than a String instance from a - to_json method. I now guard against this by doing a rather crude type - check, which raises an exception instead of crashing. - -## 2007-07-06 (1.1.1) - * Yui NARUSE sent some patches to fix tests for Ruby - 1.9. I applied them and adapted some of them a bit to run both on 1.8 and - 1.9. - * Introduced a `JSON.parse!` method without depth checking for people who - like danger. - * Made generate and `pretty_generate` methods configurable by an options hash. - * Added :allow_nan option to parser and generator in order to handle NaN, - Infinity, and -Infinity correctly - if requested. Floats, which aren't numbers, - aren't valid JSON according to RFC4627, so by default an exception will be - raised if any of these symbols are encountered. Thanks to Andrea Censi - for his hint about this. - * Fixed some more tests for Ruby 1.9. - * Implemented dump/load interface of Marshal as suggested in ruby-core:11405 - by murphy . - * Implemented the `max_nesting` feature for generate methods, too. - * Added some implementations for ruby core's custom objects for - serialisation/deserialisation purposes. - -## 2007-05-21 (1.1.0) - * Implemented max_nesting feature for parser to avoid stack overflows for - data from untrusted sources. If you trust the source, you can disable it - with the option max_nesting => false. - * Piers Cawley reported a bug, that not every - character can be escaped by `\` as required by RFC4627. There's a - contradiction between David Crockford's JSON checker test vectors (in - tests/fixtures) and RFC4627, though. I decided to stick to the RFC, because - the JSON checker seems to be a bit older than the RFC. - * Extended license to Ruby License, which includes the GPL. - * Added keyboard shortcuts, and 'Open location' menu item to edit_json.rb. - -## 2007-05-09 (1.0.4) - * Applied a patch from Yui NARUSE to make JSON compile - under Ruby 1.9. Thank you very much for mailing it to me! - * Made binary variants of JSON fail early, instead of falling back to the - pure version. This should avoid overshadowing of eventual problems while - loading of the binary. - -## 2007-03-24 (1.0.3) - * Improved performance of pure variant a bit. - * The ext variant of this release supports the mswin32 platform. Ugh! - -## 2007-03-24 (1.0.2) - * Ext Parser didn't parse 0e0 correctly into 0.0: Fixed! - -## 2007-03-24 (1.0.1) - * Forgot some object files in the build dir. I really like that - not! - -## 2007-03-24 (1.0.0) - * Added C implementations for the JSON generator and a ragel based JSON - parser in C. - * Much more tests, especially fixtures from json.org. - * Further improved conformance to RFC4627. - -## 2007-02-09 (0.4.3) - * Conform more to RFC4627 for JSON: This means JSON strings - now always must contain exactly one object `"{ ... }"` or array `"[ ... ]"` in - order to be parsed without raising an exception. The definition of what - constitutes a whitespace is narrower in JSON than in Ruby ([ \t\r\n]), and - there are differences in floats and integers (no octals or hexadecimals) as - well. - * Added aliases generate and `pretty_generate` of unparse and `pretty_unparse`. - * Fixed a test case. - * Catch an `Iconv::InvalidEncoding` exception, that seems to occur on some Sun - boxes with SunOS 5.8, if iconv doesn't support utf16 conversions. This was - reported by Andrew R Jackson , thanks a bunch! - -## 2006-08-25 (0.4.2) - * Fixed a bug in handling solidi (/-characters), that was reported by - Kevin Gilpin . - -## 2006-02-06 (0.4.1) - * Fixed a bug related to escaping with backslashes. Thanks for the report go - to Florian Munz . - -## 2005-09-23 (0.4.0) - * Initial Rubyforge Version From e34e2225e59509b38d5b4abcafce24084ade6e59 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Fri, 15 Oct 2021 09:50:59 +0200 Subject: [PATCH 0160/1010] Revert "Delete CHANGES.md because there is no maintanance resource" This reverts commit 739e06472270180e2b0920d827237b36ab65bb5b. --- CHANGES.md | 446 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 446 insertions(+) create mode 100644 CHANGES.md diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 000000000..51df7726d --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,446 @@ +# Changes + +## 2020-12-22 (2.5.1) + +* Restore the compatibility for constants of JSON class. + +## 2020-12-22 (2.5.0) + +* Ready to Ractor-safe at Ruby 3.0. + +## 2020-12-17 (2.4.1) + +* Restore version.rb with 2.4.1 + +## 2020-12-15 (2.4.0) + +* Implement a freeze: parser option #447 +* Fix an issue with generate_pretty and empty objects in the Ruby and Java implementations #449 +* Fix JSON.load_file doc #448 +* Fix pure parser with unclosed arrays / objects #425 +* bundle the LICENSE file in the gem #444 +* Add an option to escape forward slash character #405 +* RDoc for JSON #439 #446 #442 #434 #433 #430 + +## 2020-06-30 (2.3.1) + +* Spelling and grammar fixes for comments. Pull request #191 by Josh + Kline. +* Enhance generic JSON and #generate docs. Pull request #347 by Victor + Shepelev. +* Add :nodoc: for GeneratorMethods. Pull request #349 by Victor Shepelev. +* Baseline changes to help (JRuby) development. Pull request #371 by Karol + Bucek. +* Add metadata for rubygems.org. Pull request #379 by Alexandre ZANNI. +* Remove invalid JSON.generate description from JSON module rdoc. Pull + request #384 by Jeremy Evans. +* Test with TruffleRuby in CI. Pull request #402 by Benoit Daloze. +* Rdoc enhancements. Pull request #413 by Burdette Lamar. +* Fixtures/ are not being tested... Pull request #416 by Marc-André + Lafortune. +* Use frozen string for hash key. Pull request #420 by Marc-André + Lafortune. +* Added :call-seq: to RDoc for some methods. Pull request #422 by Burdette + Lamar. +* Small typo fix. Pull request #423 by Marc-André Lafortune. + +## 2019-12-11 (2.3.0) + * Fix default of `create_additions` to always be `false` for `JSON(user_input)` + and `JSON.parse(user_input, nil)`. + Note that `JSON.load` remains with default `true` and is meant for internal + serialization of trusted data. [CVE-2020-10663] + * Fix passing args all #to_json in json/add/*. + * Fix encoding issues + * Fix issues of keyword vs positional parameter + * Fix JSON::Parser against bigdecimal updates + * Bug fixes to JRuby port + +## 2019-02-21 (2.2.0) + * Adds support for 2.6 BigDecimal and ruby standard library Set datetype. + +## 2017-04-18 (2.1.0) + * Allow passing of `decimal_class` option to specify a class as which to parse + JSON float numbers. +## 2017-03-23 (2.0.4) + * Raise exception for incomplete unicode surrogates/character escape + sequences. This problem was reported by Daniel Gollahon (dgollahon). + * Fix arbitrary heap exposure problem. This problem was reported by Ahmad + Sherif (ahmadsherif). + +## 2017-01-12 (2.0.3) + * Set `required_ruby_version` to 1.9 + * Some small fixes + +## 2016-07-26 (2.0.2) + * Specify `required_ruby_version` for json\_pure. + * Fix issue #295 failure when parsing frozen strings. + +## 2016-07-01 (2.0.1) + * Fix problem when requiring json\_pure and Parser constant was defined top + level. + * Add `RB_GC_GUARD` to avoid possible GC problem via Pete Johns. + * Store `current_nesting` on stack by Aaron Patterson. + +## 2015-09-11 (2.0.0) + * Now complies to newest JSON RFC 7159. + * Implements compatibility to ruby 2.4 integer unification. + * Drops support for old rubies whose life has ended, that is rubies < 2.0. + Also see https://www.ruby-lang.org/en/news/2014/07/01/eol-for-1-8-7-and-1-9-2/ + * There were still some mentions of dual GPL licensing in the source, but JSON + has just the Ruby license that itself includes an explicit dual-licensing + clause that allows covered software to be distributed under the terms of + the Simplified BSD License instead for all ruby versions >= 1.9.3. This is + however a GPL compatible license according to the Free Software Foundation. + I changed these mentions to be consistent with the Ruby license setting in + the gemspec files which were already correct now. + +## 2015-06-01 (1.8.3) + * Fix potential memory leak, thx to nobu. + +## 2015-01-08 (1.8.2) + * Some performance improvements by Vipul A M . + * Fix by Jason R. Clark to avoid mutation of + `JSON.dump_default_options`. + * More tests by Michael Mac-Vicar and fixing + `space_before` accessor in generator. + * Performance on Jruby improved by Ben Browning . + * Some fixes to be compatible with the new Ruby 2.2 by Zachary Scott + and SHIBATA Hiroshi . + +## 2013-05-13 (1.8.1) + * Remove Rubinius exception since transcoding should be working now. + +## 2013-05-13 (1.8.0) + * Fix https://github.com/flori/json/issues/162 reported by Marc-Andre + Lafortune . Thanks! + * Applied patches by Yui NARUSE to suppress warning with + -Wchar-subscripts and better validate UTF-8 strings. + * Applied patch by ginriki@github to remove unnecessary if. + * Add load/dump interface to `JSON::GenericObject` to make + serialize :some_attribute, `JSON::GenericObject` + work in Rails active models for convenient `SomeModel#some_attribute.foo.bar` + access to serialised JSON data. + +## 2013-02-04 (1.7.7) + * Security fix for JSON create_additions default value and + `JSON::GenericObject`. It should not be possible to create additions unless + explicitly requested by setting the create_additions argument to true or + using the JSON.load/dump interface. If `JSON::GenericObject` is supposed to + be automatically deserialised, this has to be explicitly enabled by + setting + JSON::GenericObject.json_creatable = true + as well. + * Remove useless assert in fbuffer implementation. + * Apply patch attached to https://github.com/flori/json/issues#issue/155 + provided by John Shahid , Thx! + * Add license information to rubygems spec data, reported by Jordi Massaguer Pla . + * Improve documentation, thx to Zachary Scott . + +## 2012-11-29 (1.7.6) + * Add `GeneratorState#merge` alias for JRuby, fix state accessor methods. Thx to + jvshahid@github. + * Increase hash likeness of state objects. + +## 2012-08-17 (1.7.5) + * Fix compilation of extension on older rubies. + +## 2012-07-26 (1.7.4) + * Fix compilation problem on AIX, see https://github.com/flori/json/issues/142 + +## 2012-05-12 (1.7.3) + * Work around Rubinius encoding issues using iconv for conversion instead. + +## 2012-05-11 (1.7.2) + * Fix some encoding issues, that cause problems for the pure and the + extension variant in jruby 1.9 mode. + +## 2012-04-28 (1.7.1) + * Some small fixes for building + +## 2012-04-28 (1.7.0) + * Add `JSON::GenericObject` for method access to objects transmitted via JSON. + +## 2012-04-27 (1.6.7) + * Fix possible crash when trying to parse nil value. + +## 2012-02-11 (1.6.6) + * Propagate src encoding to values made from it (fixes 1.9 mode converting + everything to ascii-8bit; harmless for 1.8 mode too) (Thomas E. Enebo + ), should fix + https://github.com/flori/json/issues#issue/119. + * Fix https://github.com/flori/json/issues#issue/124 Thx to Jason Hutchens. + * Fix https://github.com/flori/json/issues#issue/117 + +## 2012-01-15 (1.6.5) + * Vit Ondruch reported a bug that shows up when using + optimisation under GCC 4.7. Thx to him, Bohuslav Kabrda + and Yui NARUSE for debugging and + developing a patch fix. + +## 2011-12-24 (1.6.4) + * Patches that improve speed on JRuby contributed by Charles Oliver Nutter + . + * Support `object_class`/`array_class` with duck typed hash/array. + +## 2011-12-01 (1.6.3) + * Let `JSON.load('')` return nil as well to make mysql text columns (default to + `''`) work better for serialization. + +## 2011-11-21 (1.6.2) + * Add support for OpenStruct and BigDecimal. + * Fix bug when parsing nil in `quirks_mode`. + * Make JSON.dump and JSON.load methods better cooperate with Rails' serialize + method. Just use: + serialize :value, JSON + * Fix bug with time serialization concerning nanoseconds. Thanks for the + patch go to Josh Partlow (jpartlow@github). + * Improve parsing speed for JSON numbers (integers and floats) in a similar way to + what Evan Phoenix suggested in: + https://github.com/flori/json/pull/103 + +## 2011-09-18 (1.6.1) + * Using -target 1.5 to force Java bits to compile with 1.5. + +## 2011-09-12 (1.6.0) + * Extract utilities (prettifier and GUI-editor) in its own gem json-utils. + * Split json/add/core into different files for classes to be serialised. + +## 2011-08-31 (1.5.4) + * Fix memory leak when used from multiple JRuby. (Patch by + jfirebaugh@github). + * Apply patch by Eric Wong that fixes garbage collection problem + reported in https://github.com/flori/json/issues/46. + * Add :quirks_mode option to parser and generator. + * Add support for Rational and Complex number additions via json/add/complex + and json/add/rational requires. + +## 2011-06-20 (1.5.3) + * Alias State#configure method as State#merge to increase duck type synonymy with Hash. + * Add `as_json` methods in json/add/core, so rails can create its json objects + the new way. + +## 2011-05-11 (1.5.2) + * Apply documentation patch by Cory Monty . + * Add gemspecs for json and json\_pure. + * Fix bug in jruby pretty printing. + * Fix bug in `object_class` and `array_class` when inheriting from Hash or + Array. + +## 2011-01-24 (1.5.1) + * Made rake-compiler build a fat binary gem. This should fix issue + https://github.com/flori/json/issues#issue/54. + +## 2011-01-22 (1.5.0) + * Included Java source codes for the Jruby extension made by Daniel Luz + . + * Output full exception message of `deep_const_get` to aid debugging. + * Fixed an issue with ruby 1.9 `Module#const_defined?` method, that was + reported by Riley Goodside. + +## 2010-08-09 (1.4.6) + * Fixed oversight reported in http://github.com/flori/json/issues/closed#issue/23, + always create a new object from the state prototype. + * Made pure and ext api more similar again. + +## 2010-08-07 (1.4.5) + * Manage data structure nesting depth in state object during generation. This + should reduce problems with `to_json` method definіtions that only have one + argument. + * Some fixes in the state objects and additional tests. +## 2010-08-06 (1.4.4) + * Fixes build problem for rubinius under OS X, http://github.com/flori/json/issues/closed#issue/25 + * Fixes crashes described in http://github.com/flori/json/issues/closed#issue/21 and + http://github.com/flori/json/issues/closed#issue/23 +## 2010-05-05 (1.4.3) + * Fixed some test assertions, from Ruby r27587 and r27590, patch by nobu. + * Fixed issue http://github.com/flori/json/issues/#issue/20 reported by + electronicwhisper@github. Thx! + +## 2010-04-26 (1.4.2) + * Applied patch from naruse Yui NARUSE to make building with + Microsoft Visual C possible again. + * Applied patch from devrandom in order to allow building of + json_pure if extensiontask is not present. + * Thanks to Dustin Schneider , who reported a memory + leak, which is fixed in this release. + * Applied 993f261ccb8f911d2ae57e9db48ec7acd0187283 patch from josh@github. + +## 2010-04-25 (1.4.1) + * Fix for a bug reported by Dan DeLeo , caused by T_FIXNUM + being different on 32bit/64bit architectures. + +## 2010-04-23 (1.4.0) + * Major speed improvements and building with simplified + directory/file-structure. + * Extension should at least be compatible with MRI, YARV and Rubinius. + +## 2010-04-07 (1.2.4) + * Triger const_missing callback to make Rails' dynamic class loading work. + +## 2010-03-11 (1.2.3) + * Added a `State#[]` method which returns an attribute's value in order to + increase duck type compatibility to Hash. + +## 2010-02-27 (1.2.2) + * Made some changes to make the building of the parser/generator compatible + to Rubinius. + +## 2009-11-25 (1.2.1) + * Added `:symbolize_names` option to Parser, which returns symbols instead of + strings in object names/keys. + +## 2009-10-01 (1.2.0) + * `fast_generate` now raises an exception for nan and infinite floats. + * On Ruby 1.8 json supports parsing of UTF-8, UTF-16BE, UTF-16LE, UTF-32BE, + and UTF-32LE JSON documents now. Under Ruby 1.9 the M17n conversion + functions are used to convert from all supported encodings. ASCII-8BIT + encoded strings are handled like all strings under Ruby 1.8 were. + * Better documentation + +## 2009-08-23 (1.1.9) + * Added forgotten main doc file `extra_rdoc_files`. + +## 2009-08-23 (1.1.8) + * Applied a patch by OZAWA Sakuro to make json/pure + work in environments that don't provide iconv. + * Applied patch by okkez_ in order to fix Ruby Bug #1768: + http://redmine.ruby-lang.org/issues/show/1768. + * Finally got around to avoid the rather paranoid escaping of ?/ characters + in the generator's output. The parsers aren't affected by this change. + Thanks to Rich Apodaca for the suggestion. + +## 2009-06-29 (1.1.7) + * Security Fix for JSON::Pure::Parser. A specially designed string could + cause catastrophic backtracking in one of the parser's regular expressions + in earlier 1.1.x versions. JSON::Ext::Parser isn't affected by this issue. + Thanks to Bartosz Blimke for reporting this + problem. + * This release also uses a less strict ruby version requirement for the + creation of the mswin32 native gem. + +## 2009-05-10 (1.1.6) + * No changes. І tested native linux gems in the last release and they don't + play well with different ruby versions other than the one the gem was built + with. This release is just to bump the version number in order to skip the + native gem on rubyforge. + +## 2009-05-10 (1.1.5) + * Started to build gems with rake-compiler gem. + * Applied patch object/array class patch from Brian Candler + and fixes. + +## 2009-04-01 (1.1.4) + * Fixed a bug in the creation of serialized generic rails objects reported by + Friedrich Graeter . + * Deleted tests/runner.rb, we're using testrb instead. + * Editor supports Infinity in numbers now. + * Made some changes in order to get the library to compile/run under Ruby + 1.9. + * Improved speed of the code path for the fast_generate method in the pure + variant. + +## 2008-07-10 (1.1.3) + * Wesley Beary reported a bug in json/add/core's DateTime + handling: If the nominator and denominator of the offset were divisible by + each other Ruby's Rational#to_s returns them as an integer not a fraction + with '/'. This caused a ZeroDivisionError during parsing. + * Use Date#start and DateTime#start instead of sg method, while + remaining backwards compatible. + * Supports ragel >= 6.0 now. + * Corrected some tests. + * Some minor changes. + +## 2007-11-27 (1.1.2) + * Remember default dir (last used directory) in editor. + * JSON::Editor.edit method added, the editor can now receive json texts from + the clipboard via C-v. + * Load json texts from an URL pasted via middle button press. + * Added :create_additions option to Parser. This makes it possible to disable + the creation of additions by force, in order to treat json texts as data + while having additions loaded. + * Jacob Maine reported, that JSON(:foo) outputs a JSON + object if the rails addition is enabled, which is wrong. It now outputs a + JSON string "foo" instead, like suggested by Jacob Maine. + * Discovered a bug in the Ruby Bugs Tracker on rubyforge, that was reported + by John Evans lgastako@gmail.com. He could produce a crash in the JSON + generator by returning something other than a String instance from a + to_json method. I now guard against this by doing a rather crude type + check, which raises an exception instead of crashing. + +## 2007-07-06 (1.1.1) + * Yui NARUSE sent some patches to fix tests for Ruby + 1.9. I applied them and adapted some of them a bit to run both on 1.8 and + 1.9. + * Introduced a `JSON.parse!` method without depth checking for people who + like danger. + * Made generate and `pretty_generate` methods configurable by an options hash. + * Added :allow_nan option to parser and generator in order to handle NaN, + Infinity, and -Infinity correctly - if requested. Floats, which aren't numbers, + aren't valid JSON according to RFC4627, so by default an exception will be + raised if any of these symbols are encountered. Thanks to Andrea Censi + for his hint about this. + * Fixed some more tests for Ruby 1.9. + * Implemented dump/load interface of Marshal as suggested in ruby-core:11405 + by murphy . + * Implemented the `max_nesting` feature for generate methods, too. + * Added some implementations for ruby core's custom objects for + serialisation/deserialisation purposes. + +## 2007-05-21 (1.1.0) + * Implemented max_nesting feature for parser to avoid stack overflows for + data from untrusted sources. If you trust the source, you can disable it + with the option max_nesting => false. + * Piers Cawley reported a bug, that not every + character can be escaped by `\` as required by RFC4627. There's a + contradiction between David Crockford's JSON checker test vectors (in + tests/fixtures) and RFC4627, though. I decided to stick to the RFC, because + the JSON checker seems to be a bit older than the RFC. + * Extended license to Ruby License, which includes the GPL. + * Added keyboard shortcuts, and 'Open location' menu item to edit_json.rb. + +## 2007-05-09 (1.0.4) + * Applied a patch from Yui NARUSE to make JSON compile + under Ruby 1.9. Thank you very much for mailing it to me! + * Made binary variants of JSON fail early, instead of falling back to the + pure version. This should avoid overshadowing of eventual problems while + loading of the binary. + +## 2007-03-24 (1.0.3) + * Improved performance of pure variant a bit. + * The ext variant of this release supports the mswin32 platform. Ugh! + +## 2007-03-24 (1.0.2) + * Ext Parser didn't parse 0e0 correctly into 0.0: Fixed! + +## 2007-03-24 (1.0.1) + * Forgot some object files in the build dir. I really like that - not! + +## 2007-03-24 (1.0.0) + * Added C implementations for the JSON generator and a ragel based JSON + parser in C. + * Much more tests, especially fixtures from json.org. + * Further improved conformance to RFC4627. + +## 2007-02-09 (0.4.3) + * Conform more to RFC4627 for JSON: This means JSON strings + now always must contain exactly one object `"{ ... }"` or array `"[ ... ]"` in + order to be parsed without raising an exception. The definition of what + constitutes a whitespace is narrower in JSON than in Ruby ([ \t\r\n]), and + there are differences in floats and integers (no octals or hexadecimals) as + well. + * Added aliases generate and `pretty_generate` of unparse and `pretty_unparse`. + * Fixed a test case. + * Catch an `Iconv::InvalidEncoding` exception, that seems to occur on some Sun + boxes with SunOS 5.8, if iconv doesn't support utf16 conversions. This was + reported by Andrew R Jackson , thanks a bunch! + +## 2006-08-25 (0.4.2) + * Fixed a bug in handling solidi (/-characters), that was reported by + Kevin Gilpin . + +## 2006-02-06 (0.4.1) + * Fixed a bug related to escaping with backslashes. Thanks for the report go + to Florian Munz . + +## 2005-09-23 (0.4.0) + * Initial Rubyforge Version From 102284895b01c22647de53a8ac6b67d6b374f53c Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Fri, 15 Oct 2021 09:56:12 +0200 Subject: [PATCH 0161/1010] Write changelog for 2.6.0 --- CHANGES.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 51df7726d..b65c1b174 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,12 @@ # Changes +### 2021-10-14 (2.6.0) + +* Use `rb_enc_interned_str` if available to reduce allocations in `freeze: true` mode. #451. +* Bump required_ruby_version to 2.3. +* Fix compatibility with `GC.compact`. +* Fix some compilation warnings. #469 + ## 2020-12-22 (2.5.1) * Restore the compatibility for constants of JSON class. From 9bef2bd53715ee0193000fb3a3b50b8cec067d8f Mon Sep 17 00:00:00 2001 From: Watson Date: Sat, 17 Apr 2021 02:50:04 +0900 Subject: [PATCH 0162/1010] Remove unnecessary initialization of create_id in JSON.parse() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `create_id` has no effect if `create_additions` is false. > * * *create_additions*: If set to false, the Parser doesn't create > * additions even if a matching class and create_id was found. This option > * defaults to false. https://github.com/flori/json/blob/8f7ddba3cd343c2bada3c31c9660c63f0c6df114/ext/json/ext/parser/parser.c#L1758-L1760 So, this PR will remove initialization of `create_id` when `create_additions` is false. This PR will improve JSON.parse() performance about 6%. ### Environment - MacBook Pro (16-inch, 2019) - macOS 11.1 - Intel Core i9 2.4 GHz - Ruby 2.7.2 ### Before ``` Warming up -------------------------------------- short_string_json 10.920k i/100ms long_string_json 10.326k i/100ms Calculating ------------------------------------- short_string_json 107.833k (± 1.1%) i/s - 546.000k in 5.064049s long_string_json 103.035k (± 0.9%) i/s - 516.300k in 5.011344s ``` ### After ``` Warming up -------------------------------------- short_string_json 11.654k i/100ms long_string_json 11.021k i/100ms Calculating ------------------------------------- short_string_json 115.564k (± 0.7%) i/s - 582.700k in 5.042516s long_string_json 109.216k (± 0.6%) i/s - 551.050k in 5.045726s ``` ### Test code ```ruby require 'benchmark/ips' require 'json' short_string_json = { "a" => "b" * 23, "a" * 23 => "b" }.to_json.freeze long_string_json = { "a" => "b" * 50, "a" * 50 => "b" }.to_json.freeze Benchmark.ips do |x| x.report("short_string_json") { JSON.parse(short_string_json) } x.report("long_string_json") { JSON.parse(long_string_json) } end ``` --- ext/json/ext/parser/parser.c | 2 +- ext/json/ext/parser/parser.rl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index b1dc8810c..06d3d0c14 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -2904,7 +2904,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) json->max_nesting = 100; json->allow_nan = 0; json->create_additions = 0; - json->create_id = rb_funcall(mJSON, i_create_id, 0); + json->create_id = Qnil; json->object_class = Qnil; json->array_class = Qnil; json->decimal_class = Qnil; diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl index f7be1a5ac..7c62336c9 100644 --- a/ext/json/ext/parser/parser.rl +++ b/ext/json/ext/parser/parser.rl @@ -804,7 +804,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) json->max_nesting = 100; json->allow_nan = 0; json->create_additions = 0; - json->create_id = rb_funcall(mJSON, i_create_id, 0); + json->create_id = Qnil; json->object_class = Qnil; json->array_class = Qnil; json->decimal_class = Qnil; From da94d9f059ce54952e52f8bd570be9a0f4960a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20=C5=A0im=C3=A1nek?= Date: Sun, 24 Oct 2021 01:56:25 +0200 Subject: [PATCH 0163/1010] Bump JSON::VERSION to 2.6.0. --- lib/json/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/json/version.rb b/lib/json/version.rb index 7a72272bd..1bf6a90ac 100644 --- a/lib/json/version.rb +++ b/lib/json/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module JSON # JSON version - VERSION = '2.5.1' + VERSION = '2.6.0' VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc: VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc: VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: From 2db5894cfa7322b00e24ad21a76ea584b0b22f5a Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Sun, 24 Oct 2021 09:21:02 +0900 Subject: [PATCH 0164/1010] Bump version to v2.6.1 --- CHANGES.md | 4 ++++ VERSION | 2 +- lib/json/version.rb | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b65c1b174..ba66514d8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Changes +### 2021-10-24 (2.6.1) + +* Restore version.rb with 2.6.1 + ### 2021-10-14 (2.6.0) * Use `rb_enc_interned_str` if available to reduce allocations in `freeze: true` mode. #451. diff --git a/VERSION b/VERSION index e70b4523a..6a6a3d8e3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.6.0 +2.6.1 diff --git a/lib/json/version.rb b/lib/json/version.rb index 1bf6a90ac..35e8dd325 100644 --- a/lib/json/version.rb +++ b/lib/json/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module JSON # JSON version - VERSION = '2.6.0' + VERSION = '2.6.1' VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc: VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc: VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: From cd8bbe56a3d0a3eca1419ae850088ce204f12ee5 Mon Sep 17 00:00:00 2001 From: Ufuk Kayserilioglu Date: Mon, 25 Oct 2021 15:48:18 +0300 Subject: [PATCH 0165/1010] Call `super` in `included` hook The C extension defines an `included` hook for the `JSON::Ext::Generator::GeneratorMethods::String` module but neglects to call `super` in the hook. This can break the functionality of various other code that rely on the fact that `included` on `Module` will always be called. --- ext/json/ext/generator/generator.c | 1 + tests/json_generator_test.rb | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index e3a83472e..711b7b845 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -478,6 +478,7 @@ static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mString_included_s(VALUE self, VALUE modul) { VALUE result = rb_funcall(modul, i_extend, 1, mString_Extend); + rb_call_super(1, &modul); return result; } diff --git a/tests/json_generator_test.rb b/tests/json_generator_test.rb index f31b6b290..b0f9a0175 100644 --- a/tests/json_generator_test.rb +++ b/tests/json_generator_test.rb @@ -391,6 +391,29 @@ def to_s; self; end end end + if defined?(JSON::Ext::Generator) + def test_string_ext_included_calls_super + included = false + + Module.alias_method(:included_orig, :included) + Module.define_method(:included) do |base| + included_orig(base) + included = true + end + + Class.new(String) do + include JSON::Ext::Generator::GeneratorMethods::String + end + + assert included + ensure + if Module.private_method_defined?(:included_orig) + Module.alias_method(:included, :included_orig) + Module.remove_method(:included_orig) + end + end + end + if defined?(Encoding) def test_nonutf8_encoding assert_equal("\"5\u{b0}\"", "5\xb0".force_encoding("iso-8859-1").to_json) From b1007dff664194ca0e800bd158e94456c5ab202a Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 1 Dec 2021 09:13:13 -0600 Subject: [PATCH 0166/1010] Remove unknown keyword arg from DateTime.parse This snuck in while addding tests for the `create_additions` feature. Caught by JRuby when we added the `limit` option to the Date/DateTime parsing methods, which causes this to be rejected as an unknown keyword. --- tests/json_addition_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/json_addition_test.rb b/tests/json_addition_test.rb index e95ace837..614c73556 100644 --- a/tests/json_addition_test.rb +++ b/tests/json_addition_test.rb @@ -165,7 +165,7 @@ def test_core def test_utc_datetime now = Time.now - d = DateTime.parse(now.to_s, :create_additions => true) # usual case + d = DateTime.parse(now.to_s) # usual case assert_equal d, parse(d.to_json, :create_additions => true) d = DateTime.parse(now.utc.to_s) # of = 0 assert_equal d, parse(d.to_json, :create_additions => true) From e816481e060a00394c5bd2e480cb35c87b1648e8 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 2 Dec 2021 15:55:01 +0900 Subject: [PATCH 0167/1010] Ignore java artifacts --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index e8b1a5226..274cae363 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,7 @@ Gemfile.lock .DS_Store */**/Makefile */**/*.o +*/**/*.class +*/**/*.jar .byebug_history *.log From 75ada77b9664c1d1f0ae6e210f8db4919849561e Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 2 Dec 2021 16:42:41 +0900 Subject: [PATCH 0168/1010] Doc: Improve documentation on JSON#parse and JSON#parse! Co-authored-by: Bruno Gomes da Silva --- ext/json/ext/parser/parser.c | 1 + ext/json/ext/parser/parser.rl | 1 + 2 files changed, 2 insertions(+) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index b1dc8810c..b7de60ddf 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -2950,6 +2950,7 @@ static const char MAYBE_UNUSED(_JSON_nfa_pop_trans)[] = { * * Parses the current JSON text _source_ and returns the complete data * structure as a result. +* It raises JSON::ParseError if fail to parse. */ static VALUE cParser_parse(VALUE self) { diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl index f7be1a5ac..15e6b929f 100644 --- a/ext/json/ext/parser/parser.rl +++ b/ext/json/ext/parser/parser.rl @@ -839,6 +839,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) * * Parses the current JSON text _source_ and returns the complete data * structure as a result. + * It raises JSON::ParseError if fail to parse. */ static VALUE cParser_parse(VALUE self) { From de9eb1d28e2c1357758f1dba50cffe3baa98de34 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Thu, 20 May 2021 12:40:04 +0200 Subject: [PATCH 0169/1010] Stop including the parser source __LINE__ in exceptions It makes testing for JSON errors very tedious. You either have to use a Regexp or to regularly update all your assertions when JSON is upgraded. --- ext/json/ext/generator/generator.c | 4 ++-- ext/json/ext/parser/parser.c | 14 +++++++------- ext/json/ext/parser/parser.rl | 14 +++++++------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index e3a83472e..98d0ea46c 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -997,10 +997,10 @@ static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_St if (!allow_nan) { if (isinf(value)) { fbuffer_free(buffer); - rb_raise(eGeneratorError, "%u: %"PRIsVALUE" not allowed in JSON", __LINE__, RB_OBJ_STRING(tmp)); + rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp)); } else if (isnan(value)) { fbuffer_free(buffer); - rb_raise(eGeneratorError, "%u: %"PRIsVALUE" not allowed in JSON", __LINE__, RB_OBJ_STRING(tmp)); + rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp)); } } fbuffer_append_str(buffer, tmp); diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index b7de60ddf..745284cb0 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -948,7 +948,7 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul {p = p - 1; } {p+= 1; cs = 29; goto _out;} } else { - rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p); + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); } } np = JSON_parse_float(json, p, pe, result); @@ -990,7 +990,7 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul if (json->allow_nan) { *result = CInfinity; } else { - rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p - 8); + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 8); } } @@ -1002,7 +1002,7 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul if (json->allow_nan) { *result = CNaN; } else { - rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p - 2); + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 2); } } @@ -2348,7 +2348,7 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul if(cs >= JSON_array_first_final) { return p + 1; } else { - rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p); + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); return NULL; } } @@ -2405,7 +2405,7 @@ static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int } rb_enc_raise( EXC_ENCODING eParserError, - "%u: incomplete unicode character escape sequence at '%s'", __LINE__, p + "incomplete unicode character escape sequence at '%s'", p ); } else { UTF32 ch = unescape_unicode((unsigned char *) ++pe); @@ -2418,7 +2418,7 @@ static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int } rb_enc_raise( EXC_ENCODING eParserError, - "%u: incomplete surrogate pair at '%s'", __LINE__, p + "incomplete surrogate pair at '%s'", p ); } if (pe[0] == '\\' && pe[1] == 'u') { @@ -3217,7 +3217,7 @@ static VALUE cParser_parse(VALUE self) if (cs >= JSON_first_final && p == pe) { return result; } else { - rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p); + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); return Qnil; } } diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl index 15e6b929f..d6d9d3685 100644 --- a/ext/json/ext/parser/parser.rl +++ b/ext/json/ext/parser/parser.rl @@ -222,14 +222,14 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu if (json->allow_nan) { *result = CNaN; } else { - rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p - 2); + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 2); } } action parse_infinity { if (json->allow_nan) { *result = CInfinity; } else { - rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p - 8); + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 8); } } action parse_string { @@ -245,7 +245,7 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu fexec p + 10; fhold; fbreak; } else { - rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p); + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); } } np = JSON_parse_float(json, fpc, pe, result); @@ -447,7 +447,7 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul if(cs >= JSON_array_first_final) { return p + 1; } else { - rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p); + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); return NULL; } } @@ -504,7 +504,7 @@ static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int } rb_enc_raise( EXC_ENCODING eParserError, - "%u: incomplete unicode character escape sequence at '%s'", __LINE__, p + "incomplete unicode character escape sequence at '%s'", p ); } else { UTF32 ch = unescape_unicode((unsigned char *) ++pe); @@ -517,7 +517,7 @@ static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int } rb_enc_raise( EXC_ENCODING eParserError, - "%u: incomplete surrogate pair at '%s'", __LINE__, p + "incomplete surrogate pair at '%s'", p ); } if (pe[0] == '\\' && pe[1] == 'u') { @@ -856,7 +856,7 @@ static VALUE cParser_parse(VALUE self) if (cs >= JSON_first_final && p == pe) { return result; } else { - rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p); + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); return Qnil; } } From 82fe866da296c5ab73157b85eebe0483b818ad8e Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 20 Oct 2021 13:53:18 -0700 Subject: [PATCH 0170/1010] test parsing of unicode, mixing literal characters with escaped --- tests/json_parser_test.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/json_parser_test.rb b/tests/json_parser_test.rb index dce693e54..01da0485a 100644 --- a/tests/json_parser_test.rb +++ b/tests/json_parser_test.rb @@ -114,6 +114,10 @@ def test_parse_bigdecimals assert_equal(BigDecimal("0.901234567890123456789E1"),JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"] ) end + def test_parse_string_mixed_unicode + assert_equal(["éé"], JSON.parse("[\"\\u00e9é\"]")) + end + if Array.method_defined?(:permutation) def test_parse_more_complex_arrays a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }] From 766f81657536fd629dc02dc34c489209c6db2a2e Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 20 Oct 2021 13:53:18 -0700 Subject: [PATCH 0171/1010] fix parsing of unicode, mixing literal characters with escaped, by forcing encoding back to ASCII_8BIT before gsubing back into parse_string result --- lib/json/pure/parser.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/json/pure/parser.rb b/lib/json/pure/parser.rb index e1d701b30..be48c6074 100644 --- a/lib/json/pure/parser.rb +++ b/lib/json/pure/parser.rb @@ -179,7 +179,7 @@ def parse_string bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16) i += 1 end - JSON.iconv('utf-8', 'utf-16be', bytes) + JSON.iconv('utf-8', 'utf-16be', bytes).force_encoding(::Encoding::ASCII_8BIT) end end if string.respond_to?(:force_encoding) From b59368a8c23976d9e44adc8f8c284fdd954a0d33 Mon Sep 17 00:00:00 2001 From: Andrew Bromwich Date: Wed, 20 Apr 2022 22:30:35 +1000 Subject: [PATCH 0172/1010] Fix parser bug for empty string allocation When `HAVE_RB_ENC_INTERNED_STR` is enabled it is possible to pass through a null pointer to `rb_enc_interned_str` resulting in a segfault Fixes #495 --- ext/json/ext/parser/parser.c | 8 ++++++++ ext/json/ext/parser/parser.rl | 8 ++++++++ tests/json_parser_test.rb | 1 + 3 files changed, 17 insertions(+) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index b7de60ddf..8b860c410 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -2363,9 +2363,17 @@ static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int char buf[4]; if (bufferSize > MAX_STACK_BUFFER_SIZE) { +# ifdef HAVE_RB_ENC_INTERNED_STR + bufferStart = buffer = ALLOC_N(char, bufferSize ? bufferSize : 1); +# else bufferStart = buffer = ALLOC_N(char, bufferSize); +# endif } else { +# ifdef HAVE_RB_ENC_INTERNED_STR + bufferStart = buffer = ALLOCA_N(char, bufferSize ? bufferSize : 1); +# else bufferStart = buffer = ALLOCA_N(char, bufferSize); +# endif } while (pe < stringEnd) { diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl index 15e6b929f..2dee80ee3 100644 --- a/ext/json/ext/parser/parser.rl +++ b/ext/json/ext/parser/parser.rl @@ -462,9 +462,17 @@ static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int char buf[4]; if (bufferSize > MAX_STACK_BUFFER_SIZE) { +# ifdef HAVE_RB_ENC_INTERNED_STR + bufferStart = buffer = ALLOC_N(char, bufferSize ? bufferSize : 1); +# else bufferStart = buffer = ALLOC_N(char, bufferSize); +# endif } else { +# ifdef HAVE_RB_ENC_INTERNED_STR + bufferStart = buffer = ALLOCA_N(char, bufferSize ? bufferSize : 1); +# else bufferStart = buffer = ALLOCA_N(char, bufferSize); +# endif } while (pe < stringEnd) { diff --git a/tests/json_parser_test.rb b/tests/json_parser_test.rb index dce693e54..00b254fc6 100644 --- a/tests/json_parser_test.rb +++ b/tests/json_parser_test.rb @@ -84,6 +84,7 @@ def test_parse_simple_objects assert_equal({ "a" => 23 }, parse(' { "a" : 23 } ')) assert_equal({ "a" => 0.23 }, parse(' { "a" : 0.23 } ')) assert_equal({ "a" => 0.23 }, parse(' { "a" : 0.23 } ')) + assert_equal({ "" => 123 }, parse('{"":123}')) end def test_parse_numbers From 7555edaaedd6d3947ca2655cd8c01e11f41433c4 Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Mon, 16 May 2022 14:16:48 +0200 Subject: [PATCH 0173/1010] Add all_images gem for local testing --- .all_images.yml | 17 +++++++++++++++++ Gemfile | 1 + 2 files changed, 18 insertions(+) create mode 100644 .all_images.yml diff --git a/.all_images.yml b/.all_images.yml new file mode 100644 index 000000000..9178beb50 --- /dev/null +++ b/.all_images.yml @@ -0,0 +1,17 @@ +dockerfile: |- + RUN apk add --no-cache build-base git ragel + RUN gem update --system + RUN gem install gem_hadar bundler +script: &script |- + echo -e "\e[1m" + ruby -v + echo -e "\e[0m" + bundle + rake clean test + +images: + ruby:3.1-alpine: *script + ruby:3.0-alpine: *script + ruby:2.7-alpine: *script + ruby:2.6-alpine: *script + ruby:2.5-alpine: *script diff --git a/Gemfile b/Gemfile index 721964460..dedab3ca7 100644 --- a/Gemfile +++ b/Gemfile @@ -15,3 +15,4 @@ end gem "rake" gem "test-unit" +gem "all_images" From 5de358f655ea7bc17b56a9e0c1191b93ac254d85 Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Mon, 16 May 2022 14:20:48 +0200 Subject: [PATCH 0174/1010] Bump version to 2.6.2 --- VERSION | 2 +- lib/json/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 6a6a3d8e3..097a15a2a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.6.1 +2.6.2 diff --git a/lib/json/version.rb b/lib/json/version.rb index 35e8dd325..9bedb65fa 100644 --- a/lib/json/version.rb +++ b/lib/json/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module JSON # JSON version - VERSION = '2.6.1' + VERSION = '2.6.2' VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc: VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc: VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: From 5d9d8f3799f2f65ebaa7b485fd6078ce5c79818c Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Mon, 16 May 2022 14:33:21 +0200 Subject: [PATCH 0175/1010] Upgrade to newest release w/out required_ruby_version --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index dedab3ca7..a90dd9d27 100644 --- a/Gemfile +++ b/Gemfile @@ -15,4 +15,4 @@ end gem "rake" gem "test-unit" -gem "all_images" +gem "all_images", "~> 0" From a81bcc03281e940e4e23d4a158ef6d7e27c6d968 Mon Sep 17 00:00:00 2001 From: Lucas Kanashiro Date: Thu, 10 Nov 2022 17:21:36 -0300 Subject: [PATCH 0176/1010] tests/ractor_test.rb: make assert_separately available Require tests/lib/helper.rb to avoid: NoMethodError: undefined method `assert_separately' --- tests/ractor_test.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ractor_test.rb b/tests/ractor_test.rb index 71105e55e..2ce90211d 100644 --- a/tests/ractor_test.rb +++ b/tests/ractor_test.rb @@ -3,6 +3,8 @@ require 'test_helper' +require_relative './lib/helper' + class JSONInRactorTest < Test::Unit::TestCase def test_generate assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") From 63bc6ae289a419195ff9bcab0f0fc58e2ace372a Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Mon, 5 Dec 2022 20:07:18 +0900 Subject: [PATCH 0177/1010] Bump version to 2.6.3 --- VERSION | 2 +- lib/json/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 097a15a2a..ec1cf33c3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.6.2 +2.6.3 diff --git a/lib/json/version.rb b/lib/json/version.rb index 9bedb65fa..3d4326d83 100644 --- a/lib/json/version.rb +++ b/lib/json/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module JSON # JSON version - VERSION = '2.6.2' + VERSION = '2.6.3' VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc: VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc: VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: From f4739c9e49daa3bcbe31c2129286279e398bda26 Mon Sep 17 00:00:00 2001 From: Yusuke Nakamura Date: Wed, 18 Jan 2023 20:40:09 +0900 Subject: [PATCH 0178/1010] Fix homepage url in gemspec GitHub Pages now hosted on *.github.io --- json-java.gemspec | 6 +++--- json.gemspec | 6 +++--- json_pure.gemspec | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/json-java.gemspec b/json-java.gemspec index 1ddfdc951..87d8c87f5 100644 --- a/json-java.gemspec +++ b/json-java.gemspec @@ -14,12 +14,12 @@ spec = Gem::Specification.new do |s| s.files = Dir["lib/**/*", "LICENSE"] - s.homepage = "http://flori.github.com/json" + s.homepage = "https://flori.github.io/json" s.metadata = { 'bug_tracker_uri' => 'https://github.com/flori/json/issues', 'changelog_uri' => 'https://github.com/flori/json/blob/master/CHANGES.md', - 'documentation_uri' => 'http://flori.github.io/json/doc/index.html', - 'homepage_uri' => 'http://flori.github.io/json/', + 'documentation_uri' => 'https://flori.github.io/json/doc/index.html', + 'homepage_uri' => s.homepage, 'source_code_uri' => 'https://github.com/flori/json', 'wiki_uri' => 'https://github.com/flori/json/wiki' } diff --git a/json.gemspec b/json.gemspec index 948e92c50..f4b2ae791 100644 --- a/json.gemspec +++ b/json.gemspec @@ -53,12 +53,12 @@ Gem::Specification.new do |s| "lib/json/pure/parser.rb", "lib/json/version.rb", ] - s.homepage = "http://flori.github.com/json" + s.homepage = "https://flori.github.io/json" s.metadata = { 'bug_tracker_uri' => 'https://github.com/flori/json/issues', 'changelog_uri' => 'https://github.com/flori/json/blob/master/CHANGES.md', - 'documentation_uri' => 'http://flori.github.io/json/doc/index.html', - 'homepage_uri' => 'http://flori.github.io/json/', + 'documentation_uri' => 'https://flori.github.io/json/doc/index.html', + 'homepage_uri' => s.homepage, 'source_code_uri' => 'https://github.com/flori/json', 'wiki_uri' => 'https://github.com/flori/json/wiki' } diff --git a/json_pure.gemspec b/json_pure.gemspec index e6e291454..883beb0e6 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -41,12 +41,12 @@ Gem::Specification.new do |s| "lib/json/pure/parser.rb".freeze, "lib/json/version.rb".freeze, ] - s.homepage = "http://flori.github.com/json".freeze + s.homepage = "https://flori.github.io/json".freeze s.metadata = { 'bug_tracker_uri' => 'https://github.com/flori/json/issues', 'changelog_uri' => 'https://github.com/flori/json/blob/master/CHANGES.md', - 'documentation_uri' => 'http://flori.github.io/json/doc/index.html', - 'homepage_uri' => 'http://flori.github.io/json/', + 'documentation_uri' => 'https://flori.github.io/json/doc/index.html', + 'homepage_uri' => s.homepage, 'source_code_uri' => 'https://github.com/flori/json', 'wiki_uri' => 'https://github.com/flori/json/wiki' } From 42e91752ad9ad85280432863042a57c7327b4486 Mon Sep 17 00:00:00 2001 From: OKURA Masafumi Date: Mon, 23 Jan 2023 13:07:54 +0900 Subject: [PATCH 0179/1010] Fix dead link to Ragel in README The working link is http://www.colm.net/open-source/ragel/ --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 288ccdfcb..8c63c2c70 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ will be two variants available: * The quite a bit faster native extension variant, which is in parts implemented in C or Java and comes with its own unicode conversion functions and a parser generated by the ragel state machine compiler - http://www.complang.org/ragel/ . + http://www.colm.net/open-source/ragel/ . Both variants of the JSON generator generate UTF-8 character sequences by default. If an :ascii\_only option with a true value is given, they escape all @@ -72,7 +72,7 @@ with: If you want to create the `parser.c` file from its `parser.rl` file or draw nice graphviz images of the state machines, you need ragel from: -http://www.complang.org/ragel/ +http://www.colm.net/open-source/ragel/ ## Usage From 0a4e3ac77477b353c433a0e8daf33993ec8b38cd Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 23 Jan 2023 15:34:03 +0900 Subject: [PATCH 0180/1010] [DOC] Fix yet another dead link to Ragel And extract the same links to a footnote. --- README-json-jruby.md | 2 +- README.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README-json-jruby.md b/README-json-jruby.md index 13368378d..7ea4f0f81 100644 --- a/README-json-jruby.md +++ b/README-json-jruby.md @@ -23,7 +23,7 @@ Its path must be set on the `jruby.dir` property of `nbproject/project.properties` (defaults to `../jruby`). Additionally, you'll need [Ant](http://ant.apache.org/), and -[Ragel](http://www.cs.queensu.ca/~thurston/ragel/) 6.4 or greater. +[Ragel](http://www.colm.net/open-source/ragel/) 6.4 or greater. Then, from the folder where the sources are located, type: diff --git a/README.md b/README.md index 8c63c2c70..981313eeb 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,7 @@ will be two variants available: extensions, which are both part of the ruby standard library. * The quite a bit faster native extension variant, which is in parts implemented in C or Java and comes with its own unicode conversion - functions and a parser generated by the ragel state machine compiler - http://www.colm.net/open-source/ragel/ . + functions and a parser generated by the [Ragel] state machine compiler. Both variants of the JSON generator generate UTF-8 character sequences by default. If an :ascii\_only option with a true value is given, they escape all @@ -71,8 +70,7 @@ with: ## Compiling the extensions yourself If you want to create the `parser.c` file from its `parser.rl` file or draw nice -graphviz images of the state machines, you need ragel from: -http://www.colm.net/open-source/ragel/ +graphviz images of the state machines, you need [Ragel]. ## Usage @@ -423,3 +421,5 @@ The latest version of this library can be downloaded at Online Documentation should be located at * https://www.rubydoc.info/gems/json + +[Ragel]: http://www.colm.net/open-source/ragel/ From eda3e8ea91588cfee734805743ebc10f3d6a5e3e Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 8 Feb 2023 13:34:13 +0100 Subject: [PATCH 0181/1010] Always indent even if empty Fixes #515 --- java/src/json/ext/Generator.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index fc1764039..6a996868e 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -292,7 +292,7 @@ void generate(Session session, RubyArray object, ByteList buffer) { buffer.append(delim); } buffer.append(shift); - Handler handler = getHandlerFor(runtime, element); + Handler handler = (Handler) getHandlerFor(runtime, element); handler.generate(session, element, buffer); } @@ -354,7 +354,7 @@ public void visit(IRubyObject key, IRubyObject value) { buffer.append((byte)':'); buffer.append(space); - Handler valueHandler = getHandlerFor(runtime, value); + Handler valueHandler = (Handler) getHandlerFor(runtime, value); valueHandler.generate(session, value, buffer); session.infectBy(value); } @@ -362,8 +362,8 @@ public void visit(IRubyObject key, IRubyObject value) { state.decreaseDepth(); if (!firstPair[0] && objectNl.length() != 0) { buffer.append(objectNl); - buffer.append(Utils.repeat(state.getIndent(), state.getDepth())); } + buffer.append(Utils.repeat(state.getIndent(), state.getDepth())); buffer.append((byte)'}'); } }; From 65ec9c692196bd3926cf411488c7708f6691fb9e Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 20 Feb 2023 11:09:32 +0100 Subject: [PATCH 0182/1010] Use ruby_xfree to free buffers They are allocated with ruby_xmalloc, they should be freed with ruby_xfree. --- ext/json/ext/parser/parser.c | 4809 +++++++++++++-------------------- ext/json/ext/parser/parser.rl | 8 +- 2 files changed, 1848 insertions(+), 2969 deletions(-) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 9bd7f1971..317d7d0c3 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -9,14 +9,14 @@ static void enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...) { - va_list args; - VALUE mesg; + va_list args; + VALUE mesg; - va_start(args, fmt); - mesg = rb_enc_vsprintf(enc, fmt, args); - va_end(args); + va_start(args, fmt); + mesg = rb_enc_vsprintf(enc, fmt, args); + va_end(args); - rb_exc_raise(rb_exc_new3(exc, mesg)); + rb_exc_raise(rb_exc_new3(exc, mesg)); } # define rb_enc_raise enc_raise # endif @@ -28,3320 +28,2199 @@ enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...) /* unicode */ static const signed char digit_values[256] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, - -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, + -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1 }; static UTF32 unescape_unicode(const unsigned char *p) { - signed char b; - UTF32 result = 0; - b = digit_values[p[0]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - b = digit_values[p[1]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - b = digit_values[p[2]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - b = digit_values[p[3]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - return result; + signed char b; + UTF32 result = 0; + b = digit_values[p[0]]; + if (b < 0) return UNI_REPLACEMENT_CHAR; + result = (result << 4) | (unsigned char)b; + b = digit_values[p[1]]; + if (b < 0) return UNI_REPLACEMENT_CHAR; + result = (result << 4) | (unsigned char)b; + b = digit_values[p[2]]; + if (b < 0) return UNI_REPLACEMENT_CHAR; + result = (result << 4) | (unsigned char)b; + b = digit_values[p[3]]; + if (b < 0) return UNI_REPLACEMENT_CHAR; + result = (result << 4) | (unsigned char)b; + return result; } static int convert_UTF32_to_UTF8(char *buf, UTF32 ch) { - int len = 1; - if (ch <= 0x7F) { - buf[0] = (char) ch; - } else if (ch <= 0x07FF) { - buf[0] = (char) ((ch >> 6) | 0xC0); - buf[1] = (char) ((ch & 0x3F) | 0x80); - len++; - } else if (ch <= 0xFFFF) { - buf[0] = (char) ((ch >> 12) | 0xE0); - buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80); - buf[2] = (char) ((ch & 0x3F) | 0x80); - len += 2; - } else if (ch <= 0x1fffff) { - buf[0] =(char) ((ch >> 18) | 0xF0); - buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80); - buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80); - buf[3] =(char) ((ch & 0x3F) | 0x80); - len += 3; - } else { - buf[0] = '?'; - } - return len; + int len = 1; + if (ch <= 0x7F) { + buf[0] = (char) ch; + } else if (ch <= 0x07FF) { + buf[0] = (char) ((ch >> 6) | 0xC0); + buf[1] = (char) ((ch & 0x3F) | 0x80); + len++; + } else if (ch <= 0xFFFF) { + buf[0] = (char) ((ch >> 12) | 0xE0); + buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80); + buf[2] = (char) ((ch & 0x3F) | 0x80); + len += 2; + } else if (ch <= 0x1fffff) { + buf[0] =(char) ((ch >> 18) | 0xF0); + buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80); + buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80); + buf[3] =(char) ((ch & 0x3F) | 0x80); + len += 3; + } else { + buf[0] = '?'; + } + return len; } static VALUE mJSON, mExt, cParser, eParserError, eNestingError; static VALUE CNaN, CInfinity, CMinusInfinity; static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, -i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, -i_object_class, i_array_class, i_decimal_class, i_key_p, -i_deep_const_get, i_match, i_match_string, i_aset, i_aref, -i_leftshift, i_new, i_try_convert, i_freeze, i_uminus; + i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, + i_object_class, i_array_class, i_decimal_class, i_key_p, + i_deep_const_get, i_match, i_match_string, i_aset, i_aref, + i_leftshift, i_new, i_try_convert, i_freeze, i_uminus; #line 125 "parser.rl" +#line 107 "parser.c" enum {JSON_object_start = 1}; enum {JSON_object_first_final = 27}; enum {JSON_object_error = 0}; enum {JSON_object_en_main = 1}; -static const char MAYBE_UNUSED(_JSON_object_nfa_targs)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_object_nfa_offsets)[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_object_nfa_push_actions)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_object_nfa_pop_trans)[] = { - 0, 0 -}; - #line 167 "parser.rl" static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) { - int cs = EVIL; - VALUE last_name = Qnil; - VALUE object_class = json->object_class; + int cs = EVIL; + VALUE last_name = Qnil; + VALUE object_class = json->object_class; - if (json->max_nesting && current_nesting > json->max_nesting) { - rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); - } + if (json->max_nesting && current_nesting > json->max_nesting) { + rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); + } - *result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class); + *result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class); +#line 131 "parser.c" { - cs = (int)JSON_object_start; + cs = JSON_object_start; } - #line 182 "parser.rl" - +#line 182 "parser.rl" +#line 138 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { - case 1: - goto st_case_1; - case 0: - goto st_case_0; - case 2: - goto st_case_2; - case 3: - goto st_case_3; - case 4: - goto st_case_4; - case 5: - goto st_case_5; - case 6: - goto st_case_6; - case 7: - goto st_case_7; - case 8: - goto st_case_8; - case 9: - goto st_case_9; - case 10: - goto st_case_10; - case 11: - goto st_case_11; - case 12: - goto st_case_12; - case 13: - goto st_case_13; - case 14: - goto st_case_14; - case 15: - goto st_case_15; - case 16: - goto st_case_16; - case 17: - goto st_case_17; - case 18: - goto st_case_18; - case 27: - goto st_case_27; - case 19: - goto st_case_19; - case 20: - goto st_case_20; - case 21: - goto st_case_21; - case 22: - goto st_case_22; - case 23: - goto st_case_23; - case 24: - goto st_case_24; - case 25: - goto st_case_25; - case 26: - goto st_case_26; - } - goto st_out; - st_case_1: - if ( ( (*( p))) == 123 ) { - goto st2; - } - { - goto st0; - } - st_case_0: - st0: - cs = 0; - goto _out; - st2: - p+= 1; - if ( p == pe ) + switch ( cs ) + { +case 1: + if ( (*p) == 123 ) + goto st2; + goto st0; +st0: +cs = 0; + goto _out; +st2: + if ( ++p == pe ) goto _test_eof2; - st_case_2: - switch( ( (*( p))) ) { - case 13: { - goto st2; - } - case 32: { - goto st2; - } - case 34: { - goto ctr2; - } - case 47: { - goto st23; - } - case 125: { - goto ctr4; - } - } - if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { - goto st2; - } - { - goto st0; - } - ctr2: - { - #line 149 "parser.rl" - - char *np; - json->parsing_name = 1; - np = JSON_parse_string(json, p, pe, &last_name); - json->parsing_name = 0; - if (np == NULL) { {p = p - 1; } {p+= 1; cs = 3; goto _out;} } else {p = (( np))-1;} - - } - - goto st3; - st3: - p+= 1; - if ( p == pe ) +case 2: + switch( (*p) ) { + case 13: goto st2; + case 32: goto st2; + case 34: goto tr2; + case 47: goto st23; + case 125: goto tr4; + } + if ( 9 <= (*p) && (*p) <= 10 ) + goto st2; + goto st0; +tr2: +#line 149 "parser.rl" + { + char *np; + json->parsing_name = 1; + np = JSON_parse_string(json, p, pe, &last_name); + json->parsing_name = 0; + if (np == NULL) { p--; {p++; cs = 3; goto _out;} } else {p = (( np))-1;} + } + goto st3; +st3: + if ( ++p == pe ) goto _test_eof3; - st_case_3: - switch( ( (*( p))) ) { - case 13: { - goto st3; - } - case 32: { - goto st3; - } - case 47: { - goto st4; - } - case 58: { - goto st8; - } - } - if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { - goto st3; - } - { - goto st0; - } - st4: - p+= 1; - if ( p == pe ) +case 3: +#line 179 "parser.c" + switch( (*p) ) { + case 13: goto st3; + case 32: goto st3; + case 47: goto st4; + case 58: goto st8; + } + if ( 9 <= (*p) && (*p) <= 10 ) + goto st3; + goto st0; +st4: + if ( ++p == pe ) goto _test_eof4; - st_case_4: - switch( ( (*( p))) ) { - case 42: { - goto st5; - } - case 47: { - goto st7; - } - } - { - goto st0; - } - st5: - p+= 1; - if ( p == pe ) +case 4: + switch( (*p) ) { + case 42: goto st5; + case 47: goto st7; + } + goto st0; +st5: + if ( ++p == pe ) goto _test_eof5; - st_case_5: - if ( ( (*( p))) == 42 ) { - goto st6; - } - { - goto st5; - } - st6: - p+= 1; - if ( p == pe ) +case 5: + if ( (*p) == 42 ) + goto st6; + goto st5; +st6: + if ( ++p == pe ) goto _test_eof6; - st_case_6: - switch( ( (*( p))) ) { - case 42: { - goto st6; - } - case 47: { - goto st3; - } - } - { - goto st5; - } - st7: - p+= 1; - if ( p == pe ) +case 6: + switch( (*p) ) { + case 42: goto st6; + case 47: goto st3; + } + goto st5; +st7: + if ( ++p == pe ) goto _test_eof7; - st_case_7: - if ( ( (*( p))) == 10 ) { - goto st3; - } - { - goto st7; - } - st8: - p+= 1; - if ( p == pe ) +case 7: + if ( (*p) == 10 ) + goto st3; + goto st7; +st8: + if ( ++p == pe ) goto _test_eof8; - st_case_8: - switch( ( (*( p))) ) { - case 13: { - goto st8; - } - case 32: { - goto st8; - } - case 34: { - goto ctr11; - } - case 45: { - goto ctr11; - } - case 47: { - goto st19; - } - case 73: { - goto ctr11; - } - case 78: { - goto ctr11; - } - case 91: { - goto ctr11; - } - case 102: { - goto ctr11; - } - case 110: { - goto ctr11; - } - case 116: { - goto ctr11; - } - case 123: { - goto ctr11; - } - } - if ( ( (*( p))) > 10 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto ctr11; - } - } else if ( ( (*( p))) >= 9 ) { - goto st8; - } - { - goto st0; - } - ctr11: - { - #line 133 "parser.rl" - - VALUE v = Qnil; - char *np = JSON_parse_value(json, p, pe, &v, current_nesting); - if (np == NULL) { - {p = p - 1; } {p+= 1; cs = 9; goto _out;} - } else { - if (NIL_P(json->object_class)) { - OBJ_FREEZE(last_name); - rb_hash_aset(*result, last_name, v); - } else { - rb_funcall(*result, i_aset, 2, last_name, v); - } - {p = (( np))-1;} - - } - } - - goto st9; - st9: - p+= 1; - if ( p == pe ) +case 8: + switch( (*p) ) { + case 13: goto st8; + case 32: goto st8; + case 34: goto tr11; + case 45: goto tr11; + case 47: goto st19; + case 73: goto tr11; + case 78: goto tr11; + case 91: goto tr11; + case 102: goto tr11; + case 110: goto tr11; + case 116: goto tr11; + case 123: goto tr11; + } + if ( (*p) > 10 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr11; + } else if ( (*p) >= 9 ) + goto st8; + goto st0; +tr11: +#line 133 "parser.rl" + { + VALUE v = Qnil; + char *np = JSON_parse_value(json, p, pe, &v, current_nesting); + if (np == NULL) { + p--; {p++; cs = 9; goto _out;} + } else { + if (NIL_P(json->object_class)) { + OBJ_FREEZE(last_name); + rb_hash_aset(*result, last_name, v); + } else { + rb_funcall(*result, i_aset, 2, last_name, v); + } + {p = (( np))-1;} + } + } + goto st9; +st9: + if ( ++p == pe ) goto _test_eof9; - st_case_9: - switch( ( (*( p))) ) { - case 13: { - goto st9; - } - case 32: { - goto st9; - } - case 44: { - goto st10; - } - case 47: { - goto st15; - } - case 125: { - goto ctr4; - } - } - if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { - goto st9; - } - { - goto st0; - } - st10: - p+= 1; - if ( p == pe ) +case 9: +#line 267 "parser.c" + switch( (*p) ) { + case 13: goto st9; + case 32: goto st9; + case 44: goto st10; + case 47: goto st15; + case 125: goto tr4; + } + if ( 9 <= (*p) && (*p) <= 10 ) + goto st9; + goto st0; +st10: + if ( ++p == pe ) goto _test_eof10; - st_case_10: - switch( ( (*( p))) ) { - case 13: { - goto st10; - } - case 32: { - goto st10; - } - case 34: { - goto ctr2; - } - case 47: { - goto st11; - } - } - if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { - goto st10; - } - { - goto st0; - } - st11: - p+= 1; - if ( p == pe ) +case 10: + switch( (*p) ) { + case 13: goto st10; + case 32: goto st10; + case 34: goto tr2; + case 47: goto st11; + } + if ( 9 <= (*p) && (*p) <= 10 ) + goto st10; + goto st0; +st11: + if ( ++p == pe ) goto _test_eof11; - st_case_11: - switch( ( (*( p))) ) { - case 42: { - goto st12; - } - case 47: { - goto st14; - } - } - { - goto st0; - } - st12: - p+= 1; - if ( p == pe ) +case 11: + switch( (*p) ) { + case 42: goto st12; + case 47: goto st14; + } + goto st0; +st12: + if ( ++p == pe ) goto _test_eof12; - st_case_12: - if ( ( (*( p))) == 42 ) { - goto st13; - } - { - goto st12; - } - st13: - p+= 1; - if ( p == pe ) +case 12: + if ( (*p) == 42 ) + goto st13; + goto st12; +st13: + if ( ++p == pe ) goto _test_eof13; - st_case_13: - switch( ( (*( p))) ) { - case 42: { - goto st13; - } - case 47: { - goto st10; - } - } - { - goto st12; - } - st14: - p+= 1; - if ( p == pe ) +case 13: + switch( (*p) ) { + case 42: goto st13; + case 47: goto st10; + } + goto st12; +st14: + if ( ++p == pe ) goto _test_eof14; - st_case_14: - if ( ( (*( p))) == 10 ) { - goto st10; - } - { - goto st14; - } - st15: - p+= 1; - if ( p == pe ) +case 14: + if ( (*p) == 10 ) + goto st10; + goto st14; +st15: + if ( ++p == pe ) goto _test_eof15; - st_case_15: - switch( ( (*( p))) ) { - case 42: { - goto st16; - } - case 47: { - goto st18; - } - } - { - goto st0; - } - st16: - p+= 1; - if ( p == pe ) +case 15: + switch( (*p) ) { + case 42: goto st16; + case 47: goto st18; + } + goto st0; +st16: + if ( ++p == pe ) goto _test_eof16; - st_case_16: - if ( ( (*( p))) == 42 ) { - goto st17; - } - { - goto st16; - } - st17: - p+= 1; - if ( p == pe ) +case 16: + if ( (*p) == 42 ) + goto st17; + goto st16; +st17: + if ( ++p == pe ) goto _test_eof17; - st_case_17: - switch( ( (*( p))) ) { - case 42: { - goto st17; - } - case 47: { - goto st9; - } - } - { - goto st16; - } - st18: - p+= 1; - if ( p == pe ) +case 17: + switch( (*p) ) { + case 42: goto st17; + case 47: goto st9; + } + goto st16; +st18: + if ( ++p == pe ) goto _test_eof18; - st_case_18: - if ( ( (*( p))) == 10 ) { - goto st9; - } - { - goto st18; - } - ctr4: - { - #line 157 "parser.rl" - {p = p - 1; } {p+= 1; cs = 27; goto _out;} } - - goto st27; - st27: - p+= 1; - if ( p == pe ) +case 18: + if ( (*p) == 10 ) + goto st9; + goto st18; +tr4: +#line 157 "parser.rl" + { p--; {p++; cs = 27; goto _out;} } + goto st27; +st27: + if ( ++p == pe ) goto _test_eof27; - st_case_27: - { - goto st0; - } - st19: - p+= 1; - if ( p == pe ) +case 27: +#line 363 "parser.c" + goto st0; +st19: + if ( ++p == pe ) goto _test_eof19; - st_case_19: - switch( ( (*( p))) ) { - case 42: { - goto st20; - } - case 47: { - goto st22; - } - } - { - goto st0; - } - st20: - p+= 1; - if ( p == pe ) +case 19: + switch( (*p) ) { + case 42: goto st20; + case 47: goto st22; + } + goto st0; +st20: + if ( ++p == pe ) goto _test_eof20; - st_case_20: - if ( ( (*( p))) == 42 ) { - goto st21; - } - { - goto st20; - } - st21: - p+= 1; - if ( p == pe ) +case 20: + if ( (*p) == 42 ) + goto st21; + goto st20; +st21: + if ( ++p == pe ) goto _test_eof21; - st_case_21: - switch( ( (*( p))) ) { - case 42: { - goto st21; - } - case 47: { - goto st8; - } - } - { - goto st20; - } - st22: - p+= 1; - if ( p == pe ) +case 21: + switch( (*p) ) { + case 42: goto st21; + case 47: goto st8; + } + goto st20; +st22: + if ( ++p == pe ) goto _test_eof22; - st_case_22: - if ( ( (*( p))) == 10 ) { - goto st8; - } - { - goto st22; - } - st23: - p+= 1; - if ( p == pe ) +case 22: + if ( (*p) == 10 ) + goto st8; + goto st22; +st23: + if ( ++p == pe ) goto _test_eof23; - st_case_23: - switch( ( (*( p))) ) { - case 42: { - goto st24; - } - case 47: { - goto st26; - } - } - { - goto st0; - } - st24: - p+= 1; - if ( p == pe ) +case 23: + switch( (*p) ) { + case 42: goto st24; + case 47: goto st26; + } + goto st0; +st24: + if ( ++p == pe ) goto _test_eof24; - st_case_24: - if ( ( (*( p))) == 42 ) { - goto st25; - } - { - goto st24; - } - st25: - p+= 1; - if ( p == pe ) +case 24: + if ( (*p) == 42 ) + goto st25; + goto st24; +st25: + if ( ++p == pe ) goto _test_eof25; - st_case_25: - switch( ( (*( p))) ) { - case 42: { - goto st25; - } - case 47: { - goto st2; - } - } - { - goto st24; - } - st26: - p+= 1; - if ( p == pe ) +case 25: + switch( (*p) ) { + case 42: goto st25; + case 47: goto st2; + } + goto st24; +st26: + if ( ++p == pe ) goto _test_eof26; - st_case_26: - if ( ( (*( p))) == 10 ) { - goto st2; - } - { - goto st26; - } - st_out: - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof11: cs = 11; goto _test_eof; - _test_eof12: cs = 12; goto _test_eof; - _test_eof13: cs = 13; goto _test_eof; - _test_eof14: cs = 14; goto _test_eof; - _test_eof15: cs = 15; goto _test_eof; - _test_eof16: cs = 16; goto _test_eof; - _test_eof17: cs = 17; goto _test_eof; - _test_eof18: cs = 18; goto _test_eof; - _test_eof27: cs = 27; goto _test_eof; - _test_eof19: cs = 19; goto _test_eof; - _test_eof20: cs = 20; goto _test_eof; - _test_eof21: cs = 21; goto _test_eof; - _test_eof22: cs = 22; goto _test_eof; - _test_eof23: cs = 23; goto _test_eof; - _test_eof24: cs = 24; goto _test_eof; - _test_eof25: cs = 25; goto _test_eof; - _test_eof26: cs = 26; goto _test_eof; - - _test_eof: {} - _out: {} - } - - #line 183 "parser.rl" - - - if (cs >= JSON_object_first_final) { - if (json->create_additions) { - VALUE klassname; - if (NIL_P(json->object_class)) { - klassname = rb_hash_aref(*result, json->create_id); - } else { - klassname = rb_funcall(*result, i_aref, 1, json->create_id); - } - if (!NIL_P(klassname)) { - VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname); - if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) { - *result = rb_funcall(klass, i_json_create, 1, *result); - } - } - } - return p + 1; - } else { - return NULL; +case 26: + if ( (*p) == 10 ) + goto st2; + goto st26; } + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; + _test_eof11: cs = 11; goto _test_eof; + _test_eof12: cs = 12; goto _test_eof; + _test_eof13: cs = 13; goto _test_eof; + _test_eof14: cs = 14; goto _test_eof; + _test_eof15: cs = 15; goto _test_eof; + _test_eof16: cs = 16; goto _test_eof; + _test_eof17: cs = 17; goto _test_eof; + _test_eof18: cs = 18; goto _test_eof; + _test_eof27: cs = 27; goto _test_eof; + _test_eof19: cs = 19; goto _test_eof; + _test_eof20: cs = 20; goto _test_eof; + _test_eof21: cs = 21; goto _test_eof; + _test_eof22: cs = 22; goto _test_eof; + _test_eof23: cs = 23; goto _test_eof; + _test_eof24: cs = 24; goto _test_eof; + _test_eof25: cs = 25; goto _test_eof; + _test_eof26: cs = 26; goto _test_eof; + + _test_eof: {} + _out: {} + } + +#line 183 "parser.rl" + + if (cs >= JSON_object_first_final) { + if (json->create_additions) { + VALUE klassname; + if (NIL_P(json->object_class)) { + klassname = rb_hash_aref(*result, json->create_id); + } else { + klassname = rb_funcall(*result, i_aref, 1, json->create_id); + } + if (!NIL_P(klassname)) { + VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname); + if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) { + *result = rb_funcall(klass, i_json_create, 1, *result); + } + } + } + return p + 1; + } else { + return NULL; + } } +#line 486 "parser.c" enum {JSON_value_start = 1}; enum {JSON_value_first_final = 29}; enum {JSON_value_error = 0}; enum {JSON_value_en_main = 1}; -static const char MAYBE_UNUSED(_JSON_value_nfa_targs)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_value_nfa_offsets)[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_value_nfa_push_actions)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_value_nfa_pop_trans)[] = { - 0, 0 -}; - #line 283 "parser.rl" static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) { - int cs = EVIL; + int cs = EVIL; +#line 502 "parser.c" { - cs = (int)JSON_value_start; + cs = JSON_value_start; } - #line 290 "parser.rl" - +#line 290 "parser.rl" +#line 509 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { - case 1: - goto st_case_1; - case 0: - goto st_case_0; - case 29: - goto st_case_29; - case 2: - goto st_case_2; - case 3: - goto st_case_3; - case 4: - goto st_case_4; - case 5: - goto st_case_5; - case 6: - goto st_case_6; - case 7: - goto st_case_7; - case 8: - goto st_case_8; - case 9: - goto st_case_9; - case 10: - goto st_case_10; - case 11: - goto st_case_11; - case 12: - goto st_case_12; - case 13: - goto st_case_13; - case 14: - goto st_case_14; - case 15: - goto st_case_15; - case 16: - goto st_case_16; - case 17: - goto st_case_17; - case 18: - goto st_case_18; - case 19: - goto st_case_19; - case 20: - goto st_case_20; - case 21: - goto st_case_21; - case 22: - goto st_case_22; - case 23: - goto st_case_23; - case 24: - goto st_case_24; - case 25: - goto st_case_25; - case 26: - goto st_case_26; - case 27: - goto st_case_27; - case 28: - goto st_case_28; - } - goto st_out; - st1: - p+= 1; - if ( p == pe ) + switch ( cs ) + { +st1: + if ( ++p == pe ) goto _test_eof1; - st_case_1: - switch( ( (*( p))) ) { - case 13: { - goto st1; - } - case 32: { - goto st1; - } - case 34: { - goto ctr2; - } - case 45: { - goto ctr3; - } - case 47: { - goto st6; - } - case 73: { - goto st10; - } - case 78: { - goto st17; - } - case 91: { - goto ctr7; - } - case 102: { - goto st19; - } - case 110: { - goto st23; - } - case 116: { - goto st26; - } - case 123: { - goto ctr11; - } - } - if ( ( (*( p))) > 10 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto ctr3; - } - } else if ( ( (*( p))) >= 9 ) { - goto st1; - } - { - goto st0; - } - st_case_0: - st0: - cs = 0; - goto _out; - ctr2: - { - #line 235 "parser.rl" - - char *np = JSON_parse_string(json, p, pe, result); - if (np == NULL) { {p = p - 1; } {p+= 1; cs = 29; goto _out;} } else {p = (( np))-1;} - - } - - goto st29; - ctr3: - { - #line 240 "parser.rl" - - char *np; - if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) { - if (json->allow_nan) { - *result = CMinusInfinity; - {p = (( p + 10))-1;} - - {p = p - 1; } {p+= 1; cs = 29; goto _out;} - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); - } - } - np = JSON_parse_float(json, p, pe, result); - if (np != NULL) {p = (( np))-1;} - - np = JSON_parse_integer(json, p, pe, result); - if (np != NULL) {p = (( np))-1;} - - {p = p - 1; } {p+= 1; cs = 29; goto _out;} - } - - goto st29; - ctr7: - { - #line 258 "parser.rl" - - char *np; - np = JSON_parse_array(json, p, pe, result, current_nesting + 1); - if (np == NULL) { {p = p - 1; } {p+= 1; cs = 29; goto _out;} } else {p = (( np))-1;} - - } - - goto st29; - ctr11: - { - #line 264 "parser.rl" - - char *np; - np = JSON_parse_object(json, p, pe, result, current_nesting + 1); - if (np == NULL) { {p = p - 1; } {p+= 1; cs = 29; goto _out;} } else {p = (( np))-1;} - - } - - goto st29; - ctr25: - { - #line 228 "parser.rl" - - if (json->allow_nan) { - *result = CInfinity; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 8); - } - } - - goto st29; - ctr27: - { - #line 221 "parser.rl" - - if (json->allow_nan) { - *result = CNaN; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 2); - } - } - - goto st29; - ctr31: - { - #line 215 "parser.rl" - - *result = Qfalse; - } - - goto st29; - ctr34: - { - #line 212 "parser.rl" - - *result = Qnil; - } - - goto st29; - ctr37: - { - #line 218 "parser.rl" - - *result = Qtrue; - } - - goto st29; - st29: - p+= 1; - if ( p == pe ) +case 1: + switch( (*p) ) { + case 13: goto st1; + case 32: goto st1; + case 34: goto tr2; + case 45: goto tr3; + case 47: goto st6; + case 73: goto st10; + case 78: goto st17; + case 91: goto tr7; + case 102: goto st19; + case 110: goto st23; + case 116: goto st26; + case 123: goto tr11; + } + if ( (*p) > 10 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3; + } else if ( (*p) >= 9 ) + goto st1; + goto st0; +st0: +cs = 0; + goto _out; +tr2: +#line 235 "parser.rl" + { + char *np = JSON_parse_string(json, p, pe, result); + if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;} + } + goto st29; +tr3: +#line 240 "parser.rl" + { + char *np; + if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) { + if (json->allow_nan) { + *result = CMinusInfinity; + {p = (( p + 10))-1;} + p--; {p++; cs = 29; goto _out;} + } else { + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); + } + } + np = JSON_parse_float(json, p, pe, result); + if (np != NULL) {p = (( np))-1;} + np = JSON_parse_integer(json, p, pe, result); + if (np != NULL) {p = (( np))-1;} + p--; {p++; cs = 29; goto _out;} + } + goto st29; +tr7: +#line 258 "parser.rl" + { + char *np; + np = JSON_parse_array(json, p, pe, result, current_nesting + 1); + if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;} + } + goto st29; +tr11: +#line 264 "parser.rl" + { + char *np; + np = JSON_parse_object(json, p, pe, result, current_nesting + 1); + if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;} + } + goto st29; +tr25: +#line 228 "parser.rl" + { + if (json->allow_nan) { + *result = CInfinity; + } else { + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 8); + } + } + goto st29; +tr27: +#line 221 "parser.rl" + { + if (json->allow_nan) { + *result = CNaN; + } else { + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 2); + } + } + goto st29; +tr31: +#line 215 "parser.rl" + { + *result = Qfalse; + } + goto st29; +tr34: +#line 212 "parser.rl" + { + *result = Qnil; + } + goto st29; +tr37: +#line 218 "parser.rl" + { + *result = Qtrue; + } + goto st29; +st29: + if ( ++p == pe ) goto _test_eof29; - st_case_29: - { - #line 270 "parser.rl" - {p = p - 1; } {p+= 1; cs = 29; goto _out;} } - switch( ( (*( p))) ) { - case 13: { - goto st29; - } - case 32: { - goto st29; - } - case 47: { - goto st2; - } - } - if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { - goto st29; - } - { - goto st0; - } - st2: - p+= 1; - if ( p == pe ) +case 29: +#line 270 "parser.rl" + { p--; {p++; cs = 29; goto _out;} } +#line 629 "parser.c" + switch( (*p) ) { + case 13: goto st29; + case 32: goto st29; + case 47: goto st2; + } + if ( 9 <= (*p) && (*p) <= 10 ) + goto st29; + goto st0; +st2: + if ( ++p == pe ) goto _test_eof2; - st_case_2: - switch( ( (*( p))) ) { - case 42: { - goto st3; - } - case 47: { - goto st5; - } - } - { - goto st0; - } - st3: - p+= 1; - if ( p == pe ) +case 2: + switch( (*p) ) { + case 42: goto st3; + case 47: goto st5; + } + goto st0; +st3: + if ( ++p == pe ) goto _test_eof3; - st_case_3: - if ( ( (*( p))) == 42 ) { - goto st4; - } - { - goto st3; - } - st4: - p+= 1; - if ( p == pe ) +case 3: + if ( (*p) == 42 ) + goto st4; + goto st3; +st4: + if ( ++p == pe ) goto _test_eof4; - st_case_4: - switch( ( (*( p))) ) { - case 42: { - goto st4; - } - case 47: { - goto st29; - } - } - { - goto st3; - } - st5: - p+= 1; - if ( p == pe ) +case 4: + switch( (*p) ) { + case 42: goto st4; + case 47: goto st29; + } + goto st3; +st5: + if ( ++p == pe ) goto _test_eof5; - st_case_5: - if ( ( (*( p))) == 10 ) { - goto st29; - } - { - goto st5; - } - st6: - p+= 1; - if ( p == pe ) +case 5: + if ( (*p) == 10 ) + goto st29; + goto st5; +st6: + if ( ++p == pe ) goto _test_eof6; - st_case_6: - switch( ( (*( p))) ) { - case 42: { - goto st7; - } - case 47: { - goto st9; - } - } - { - goto st0; - } - st7: - p+= 1; - if ( p == pe ) +case 6: + switch( (*p) ) { + case 42: goto st7; + case 47: goto st9; + } + goto st0; +st7: + if ( ++p == pe ) goto _test_eof7; - st_case_7: - if ( ( (*( p))) == 42 ) { - goto st8; - } - { - goto st7; - } - st8: - p+= 1; - if ( p == pe ) +case 7: + if ( (*p) == 42 ) + goto st8; + goto st7; +st8: + if ( ++p == pe ) goto _test_eof8; - st_case_8: - switch( ( (*( p))) ) { - case 42: { - goto st8; - } - case 47: { - goto st1; - } - } - { - goto st7; - } - st9: - p+= 1; - if ( p == pe ) +case 8: + switch( (*p) ) { + case 42: goto st8; + case 47: goto st1; + } + goto st7; +st9: + if ( ++p == pe ) goto _test_eof9; - st_case_9: - if ( ( (*( p))) == 10 ) { - goto st1; - } - { - goto st9; - } - st10: - p+= 1; - if ( p == pe ) +case 9: + if ( (*p) == 10 ) + goto st1; + goto st9; +st10: + if ( ++p == pe ) goto _test_eof10; - st_case_10: - if ( ( (*( p))) == 110 ) { - goto st11; - } - { - goto st0; - } - st11: - p+= 1; - if ( p == pe ) +case 10: + if ( (*p) == 110 ) + goto st11; + goto st0; +st11: + if ( ++p == pe ) goto _test_eof11; - st_case_11: - if ( ( (*( p))) == 102 ) { - goto st12; - } - { - goto st0; - } - st12: - p+= 1; - if ( p == pe ) +case 11: + if ( (*p) == 102 ) + goto st12; + goto st0; +st12: + if ( ++p == pe ) goto _test_eof12; - st_case_12: - if ( ( (*( p))) == 105 ) { - goto st13; - } - { - goto st0; - } - st13: - p+= 1; - if ( p == pe ) +case 12: + if ( (*p) == 105 ) + goto st13; + goto st0; +st13: + if ( ++p == pe ) goto _test_eof13; - st_case_13: - if ( ( (*( p))) == 110 ) { - goto st14; - } - { - goto st0; - } - st14: - p+= 1; - if ( p == pe ) +case 13: + if ( (*p) == 110 ) + goto st14; + goto st0; +st14: + if ( ++p == pe ) goto _test_eof14; - st_case_14: - if ( ( (*( p))) == 105 ) { - goto st15; - } - { - goto st0; - } - st15: - p+= 1; - if ( p == pe ) +case 14: + if ( (*p) == 105 ) + goto st15; + goto st0; +st15: + if ( ++p == pe ) goto _test_eof15; - st_case_15: - if ( ( (*( p))) == 116 ) { - goto st16; - } - { - goto st0; - } - st16: - p+= 1; - if ( p == pe ) +case 15: + if ( (*p) == 116 ) + goto st16; + goto st0; +st16: + if ( ++p == pe ) goto _test_eof16; - st_case_16: - if ( ( (*( p))) == 121 ) { - goto ctr25; - } - { - goto st0; - } - st17: - p+= 1; - if ( p == pe ) +case 16: + if ( (*p) == 121 ) + goto tr25; + goto st0; +st17: + if ( ++p == pe ) goto _test_eof17; - st_case_17: - if ( ( (*( p))) == 97 ) { - goto st18; - } - { - goto st0; - } - st18: - p+= 1; - if ( p == pe ) +case 17: + if ( (*p) == 97 ) + goto st18; + goto st0; +st18: + if ( ++p == pe ) goto _test_eof18; - st_case_18: - if ( ( (*( p))) == 78 ) { - goto ctr27; - } - { - goto st0; - } - st19: - p+= 1; - if ( p == pe ) +case 18: + if ( (*p) == 78 ) + goto tr27; + goto st0; +st19: + if ( ++p == pe ) goto _test_eof19; - st_case_19: - if ( ( (*( p))) == 97 ) { - goto st20; - } - { - goto st0; - } - st20: - p+= 1; - if ( p == pe ) +case 19: + if ( (*p) == 97 ) + goto st20; + goto st0; +st20: + if ( ++p == pe ) goto _test_eof20; - st_case_20: - if ( ( (*( p))) == 108 ) { - goto st21; - } - { - goto st0; - } - st21: - p+= 1; - if ( p == pe ) +case 20: + if ( (*p) == 108 ) + goto st21; + goto st0; +st21: + if ( ++p == pe ) goto _test_eof21; - st_case_21: - if ( ( (*( p))) == 115 ) { - goto st22; - } - { - goto st0; - } - st22: - p+= 1; - if ( p == pe ) +case 21: + if ( (*p) == 115 ) + goto st22; + goto st0; +st22: + if ( ++p == pe ) goto _test_eof22; - st_case_22: - if ( ( (*( p))) == 101 ) { - goto ctr31; - } - { - goto st0; - } - st23: - p+= 1; - if ( p == pe ) +case 22: + if ( (*p) == 101 ) + goto tr31; + goto st0; +st23: + if ( ++p == pe ) goto _test_eof23; - st_case_23: - if ( ( (*( p))) == 117 ) { - goto st24; - } - { - goto st0; - } - st24: - p+= 1; - if ( p == pe ) +case 23: + if ( (*p) == 117 ) + goto st24; + goto st0; +st24: + if ( ++p == pe ) goto _test_eof24; - st_case_24: - if ( ( (*( p))) == 108 ) { - goto st25; - } - { - goto st0; - } - st25: - p+= 1; - if ( p == pe ) +case 24: + if ( (*p) == 108 ) + goto st25; + goto st0; +st25: + if ( ++p == pe ) goto _test_eof25; - st_case_25: - if ( ( (*( p))) == 108 ) { - goto ctr34; - } - { - goto st0; - } - st26: - p+= 1; - if ( p == pe ) +case 25: + if ( (*p) == 108 ) + goto tr34; + goto st0; +st26: + if ( ++p == pe ) goto _test_eof26; - st_case_26: - if ( ( (*( p))) == 114 ) { - goto st27; - } - { - goto st0; - } - st27: - p+= 1; - if ( p == pe ) +case 26: + if ( (*p) == 114 ) + goto st27; + goto st0; +st27: + if ( ++p == pe ) goto _test_eof27; - st_case_27: - if ( ( (*( p))) == 117 ) { - goto st28; - } - { - goto st0; - } - st28: - p+= 1; - if ( p == pe ) +case 27: + if ( (*p) == 117 ) + goto st28; + goto st0; +st28: + if ( ++p == pe ) goto _test_eof28; - st_case_28: - if ( ( (*( p))) == 101 ) { - goto ctr37; - } - { - goto st0; - } - st_out: - _test_eof1: cs = 1; goto _test_eof; - _test_eof29: cs = 29; goto _test_eof; - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof11: cs = 11; goto _test_eof; - _test_eof12: cs = 12; goto _test_eof; - _test_eof13: cs = 13; goto _test_eof; - _test_eof14: cs = 14; goto _test_eof; - _test_eof15: cs = 15; goto _test_eof; - _test_eof16: cs = 16; goto _test_eof; - _test_eof17: cs = 17; goto _test_eof; - _test_eof18: cs = 18; goto _test_eof; - _test_eof19: cs = 19; goto _test_eof; - _test_eof20: cs = 20; goto _test_eof; - _test_eof21: cs = 21; goto _test_eof; - _test_eof22: cs = 22; goto _test_eof; - _test_eof23: cs = 23; goto _test_eof; - _test_eof24: cs = 24; goto _test_eof; - _test_eof25: cs = 25; goto _test_eof; - _test_eof26: cs = 26; goto _test_eof; - _test_eof27: cs = 27; goto _test_eof; - _test_eof28: cs = 28; goto _test_eof; - - _test_eof: {} - _out: {} - } - - #line 291 "parser.rl" - - - if (json->freeze) { - OBJ_FREEZE(*result); - } - - if (cs >= JSON_value_first_final) { - return p; - } else { - return NULL; +case 28: + if ( (*p) == 101 ) + goto tr37; + goto st0; } + _test_eof1: cs = 1; goto _test_eof; + _test_eof29: cs = 29; goto _test_eof; + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; + _test_eof11: cs = 11; goto _test_eof; + _test_eof12: cs = 12; goto _test_eof; + _test_eof13: cs = 13; goto _test_eof; + _test_eof14: cs = 14; goto _test_eof; + _test_eof15: cs = 15; goto _test_eof; + _test_eof16: cs = 16; goto _test_eof; + _test_eof17: cs = 17; goto _test_eof; + _test_eof18: cs = 18; goto _test_eof; + _test_eof19: cs = 19; goto _test_eof; + _test_eof20: cs = 20; goto _test_eof; + _test_eof21: cs = 21; goto _test_eof; + _test_eof22: cs = 22; goto _test_eof; + _test_eof23: cs = 23; goto _test_eof; + _test_eof24: cs = 24; goto _test_eof; + _test_eof25: cs = 25; goto _test_eof; + _test_eof26: cs = 26; goto _test_eof; + _test_eof27: cs = 27; goto _test_eof; + _test_eof28: cs = 28; goto _test_eof; + + _test_eof: {} + _out: {} + } + +#line 291 "parser.rl" + + if (json->freeze) { + OBJ_FREEZE(*result); + } + + if (cs >= JSON_value_first_final) { + return p; + } else { + return NULL; + } } +#line 884 "parser.c" enum {JSON_integer_start = 1}; enum {JSON_integer_first_final = 3}; enum {JSON_integer_error = 0}; enum {JSON_integer_en_main = 1}; -static const char MAYBE_UNUSED(_JSON_integer_nfa_targs)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_integer_nfa_offsets)[] = { - 0, 0, 0, 0, 0, 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_integer_nfa_push_actions)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_integer_nfa_pop_trans)[] = { - 0, 0 -}; - #line 311 "parser.rl" static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result) { - int cs = EVIL; + int cs = EVIL; +#line 900 "parser.c" { - cs = (int)JSON_integer_start; + cs = JSON_integer_start; } - #line 318 "parser.rl" - - json->memo = p; +#line 318 "parser.rl" + json->memo = p; +#line 908 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { - case 1: - goto st_case_1; - case 0: - goto st_case_0; - case 2: - goto st_case_2; - case 3: - goto st_case_3; - case 4: - goto st_case_4; - case 5: - goto st_case_5; - } - goto st_out; - st_case_1: - switch( ( (*( p))) ) { - case 45: { - goto st2; - } - case 48: { - goto st3; - } - } - if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st5; - } - { - goto st0; - } - st_case_0: - st0: - cs = 0; - goto _out; - st2: - p+= 1; - if ( p == pe ) + switch ( cs ) + { +case 1: + switch( (*p) ) { + case 45: goto st2; + case 48: goto st3; + } + if ( 49 <= (*p) && (*p) <= 57 ) + goto st5; + goto st0; +st0: +cs = 0; + goto _out; +st2: + if ( ++p == pe ) goto _test_eof2; - st_case_2: - if ( ( (*( p))) == 48 ) { - goto st3; - } - if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st5; - } - { - goto st0; - } - st3: - p+= 1; - if ( p == pe ) +case 2: + if ( (*p) == 48 ) + goto st3; + if ( 49 <= (*p) && (*p) <= 57 ) + goto st5; + goto st0; +st3: + if ( ++p == pe ) goto _test_eof3; - st_case_3: - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st0; - } - { - goto ctr4; - } - ctr4: - { - #line 308 "parser.rl" - {p = p - 1; } {p+= 1; cs = 4; goto _out;} } - - goto st4; - st4: - p+= 1; - if ( p == pe ) +case 3: + if ( 48 <= (*p) && (*p) <= 57 ) + goto st0; + goto tr4; +tr4: +#line 308 "parser.rl" + { p--; {p++; cs = 4; goto _out;} } + goto st4; +st4: + if ( ++p == pe ) goto _test_eof4; - st_case_4: - { - goto st0; - } - st5: - p+= 1; - if ( p == pe ) +case 4: +#line 949 "parser.c" + goto st0; +st5: + if ( ++p == pe ) goto _test_eof5; - st_case_5: - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st5; - } - { - goto ctr4; - } - st_out: - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - - _test_eof: {} - _out: {} +case 5: + if ( 48 <= (*p) && (*p) <= 57 ) + goto st5; + goto tr4; } + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; - #line 320 "parser.rl" - - - if (cs >= JSON_integer_first_final) { - long len = p - json->memo; - fbuffer_clear(json->fbuffer); - fbuffer_append(json->fbuffer, json->memo, len); - fbuffer_append_char(json->fbuffer, '\0'); - *result = rb_cstr2inum(FBUFFER_PTR(json->fbuffer), 10); - return p + 1; - } else { - return NULL; + _test_eof: {} + _out: {} } + +#line 320 "parser.rl" + + if (cs >= JSON_integer_first_final) { + long len = p - json->memo; + fbuffer_clear(json->fbuffer); + fbuffer_append(json->fbuffer, json->memo, len); + fbuffer_append_char(json->fbuffer, '\0'); + *result = rb_cstr2inum(FBUFFER_PTR(json->fbuffer), 10); + return p + 1; + } else { + return NULL; + } } +#line 983 "parser.c" enum {JSON_float_start = 1}; enum {JSON_float_first_final = 8}; enum {JSON_float_error = 0}; enum {JSON_float_en_main = 1}; -static const char MAYBE_UNUSED(_JSON_float_nfa_targs)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_float_nfa_offsets)[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_float_nfa_push_actions)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_float_nfa_pop_trans)[] = { - 0, 0 -}; - #line 345 "parser.rl" static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result) { - int cs = EVIL; + int cs = EVIL; +#line 999 "parser.c" { - cs = (int)JSON_float_start; + cs = JSON_float_start; } - #line 352 "parser.rl" - - json->memo = p; +#line 352 "parser.rl" + json->memo = p; +#line 1007 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { - case 1: - goto st_case_1; - case 0: - goto st_case_0; - case 2: - goto st_case_2; - case 3: - goto st_case_3; - case 4: - goto st_case_4; - case 8: - goto st_case_8; - case 9: - goto st_case_9; - case 5: - goto st_case_5; - case 6: - goto st_case_6; - case 10: - goto st_case_10; - case 7: - goto st_case_7; - } - goto st_out; - st_case_1: - switch( ( (*( p))) ) { - case 45: { - goto st2; - } - case 48: { - goto st3; - } - } - if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st7; - } - { - goto st0; - } - st_case_0: - st0: - cs = 0; - goto _out; - st2: - p+= 1; - if ( p == pe ) + switch ( cs ) + { +case 1: + switch( (*p) ) { + case 45: goto st2; + case 48: goto st3; + } + if ( 49 <= (*p) && (*p) <= 57 ) + goto st7; + goto st0; +st0: +cs = 0; + goto _out; +st2: + if ( ++p == pe ) goto _test_eof2; - st_case_2: - if ( ( (*( p))) == 48 ) { - goto st3; - } - if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st7; - } - { - goto st0; - } - st3: - p+= 1; - if ( p == pe ) +case 2: + if ( (*p) == 48 ) + goto st3; + if ( 49 <= (*p) && (*p) <= 57 ) + goto st7; + goto st0; +st3: + if ( ++p == pe ) goto _test_eof3; - st_case_3: - switch( ( (*( p))) ) { - case 46: { - goto st4; - } - case 69: { - goto st5; - } - case 101: { - goto st5; - } - } - { - goto st0; - } - st4: - p+= 1; - if ( p == pe ) +case 3: + switch( (*p) ) { + case 46: goto st4; + case 69: goto st5; + case 101: goto st5; + } + goto st0; +st4: + if ( ++p == pe ) goto _test_eof4; - st_case_4: - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st8; - } - { - goto st0; - } - st8: - p+= 1; - if ( p == pe ) +case 4: + if ( 48 <= (*p) && (*p) <= 57 ) + goto st8; + goto st0; +st8: + if ( ++p == pe ) goto _test_eof8; - st_case_8: - switch( ( (*( p))) ) { - case 69: { - goto st5; - } - case 101: { - goto st5; - } - } - if ( ( (*( p))) > 46 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st8; - } - } else if ( ( (*( p))) >= 45 ) { - goto st0; - } - { - goto ctr9; - } - ctr9: - { - #line 339 "parser.rl" - {p = p - 1; } {p+= 1; cs = 9; goto _out;} } - - goto st9; - st9: - p+= 1; - if ( p == pe ) +case 8: + switch( (*p) ) { + case 69: goto st5; + case 101: goto st5; + } + if ( (*p) > 46 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto st8; + } else if ( (*p) >= 45 ) + goto st0; + goto tr9; +tr9: +#line 339 "parser.rl" + { p--; {p++; cs = 9; goto _out;} } + goto st9; +st9: + if ( ++p == pe ) goto _test_eof9; - st_case_9: - { - goto st0; - } - st5: - p+= 1; - if ( p == pe ) +case 9: +#line 1072 "parser.c" + goto st0; +st5: + if ( ++p == pe ) goto _test_eof5; - st_case_5: - switch( ( (*( p))) ) { - case 43: { - goto st6; - } - case 45: { - goto st6; - } - } - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st10; - } - { - goto st0; - } - st6: - p+= 1; - if ( p == pe ) +case 5: + switch( (*p) ) { + case 43: goto st6; + case 45: goto st6; + } + if ( 48 <= (*p) && (*p) <= 57 ) + goto st10; + goto st0; +st6: + if ( ++p == pe ) goto _test_eof6; - st_case_6: - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st10; - } - { - goto st0; - } - st10: - p+= 1; - if ( p == pe ) +case 6: + if ( 48 <= (*p) && (*p) <= 57 ) + goto st10; + goto st0; +st10: + if ( ++p == pe ) goto _test_eof10; - st_case_10: - switch( ( (*( p))) ) { - case 69: { - goto st0; - } - case 101: { - goto st0; - } - } - if ( ( (*( p))) > 46 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st10; - } - } else if ( ( (*( p))) >= 45 ) { - goto st0; - } - { - goto ctr9; - } - st7: - p+= 1; - if ( p == pe ) +case 10: + switch( (*p) ) { + case 69: goto st0; + case 101: goto st0; + } + if ( (*p) > 46 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto st10; + } else if ( (*p) >= 45 ) + goto st0; + goto tr9; +st7: + if ( ++p == pe ) goto _test_eof7; - st_case_7: - switch( ( (*( p))) ) { - case 46: { - goto st4; - } - case 69: { - goto st5; - } - case 101: { - goto st5; - } - } - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st7; - } - { - goto st0; - } - st_out: - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - - _test_eof: {} - _out: {} - } - - #line 354 "parser.rl" - - - if (cs >= JSON_float_first_final) { - VALUE mod = Qnil; - ID method_id = 0; - if (rb_respond_to(json->decimal_class, i_try_convert)) { - mod = json->decimal_class; - method_id = i_try_convert; - } else if (rb_respond_to(json->decimal_class, i_new)) { - mod = json->decimal_class; - method_id = i_new; - } else if (RB_TYPE_P(json->decimal_class, T_CLASS)) { - VALUE name = rb_class_name(json->decimal_class); - const char *name_cstr = RSTRING_PTR(name); - const char *last_colon = strrchr(name_cstr, ':'); - if (last_colon) { - const char *mod_path_end = last_colon - 1; - VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr); - mod = rb_path_to_class(mod_path); - - const char *method_name_beg = last_colon + 1; - long before_len = method_name_beg - name_cstr; - long len = RSTRING_LEN(name) - before_len; - VALUE method_name = rb_str_substr(name, before_len, len); - method_id = SYM2ID(rb_str_intern(method_name)); - } else { - mod = rb_mKernel; - method_id = SYM2ID(rb_str_intern(name)); - } - } - - long len = p - json->memo; - fbuffer_clear(json->fbuffer); - fbuffer_append(json->fbuffer, json->memo, len); - fbuffer_append_char(json->fbuffer, '\0'); - - if (method_id) { - VALUE text = rb_str_new2(FBUFFER_PTR(json->fbuffer)); - *result = rb_funcallv(mod, method_id, 1, &text); - } else { - *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1)); - } - - return p + 1; - } else { - return NULL; +case 7: + switch( (*p) ) { + case 46: goto st4; + case 69: goto st5; + case 101: goto st5; } + if ( 48 <= (*p) && (*p) <= 57 ) + goto st7; + goto st0; + } + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + + _test_eof: {} + _out: {} + } + +#line 354 "parser.rl" + + if (cs >= JSON_float_first_final) { + VALUE mod = Qnil; + ID method_id = 0; + if (rb_respond_to(json->decimal_class, i_try_convert)) { + mod = json->decimal_class; + method_id = i_try_convert; + } else if (rb_respond_to(json->decimal_class, i_new)) { + mod = json->decimal_class; + method_id = i_new; + } else if (RB_TYPE_P(json->decimal_class, T_CLASS)) { + VALUE name = rb_class_name(json->decimal_class); + const char *name_cstr = RSTRING_PTR(name); + const char *last_colon = strrchr(name_cstr, ':'); + if (last_colon) { + const char *mod_path_end = last_colon - 1; + VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr); + mod = rb_path_to_class(mod_path); + + const char *method_name_beg = last_colon + 1; + long before_len = method_name_beg - name_cstr; + long len = RSTRING_LEN(name) - before_len; + VALUE method_name = rb_str_substr(name, before_len, len); + method_id = SYM2ID(rb_str_intern(method_name)); + } else { + mod = rb_mKernel; + method_id = SYM2ID(rb_str_intern(name)); + } + } + + long len = p - json->memo; + fbuffer_clear(json->fbuffer); + fbuffer_append(json->fbuffer, json->memo, len); + fbuffer_append_char(json->fbuffer, '\0'); + + if (method_id) { + VALUE text = rb_str_new2(FBUFFER_PTR(json->fbuffer)); + *result = rb_funcallv(mod, method_id, 1, &text); + } else { + *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1)); + } + + return p + 1; + } else { + return NULL; + } } +#line 1184 "parser.c" enum {JSON_array_start = 1}; enum {JSON_array_first_final = 17}; enum {JSON_array_error = 0}; enum {JSON_array_en_main = 1}; -static const char MAYBE_UNUSED(_JSON_array_nfa_targs)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_array_nfa_offsets)[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_array_nfa_push_actions)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_array_nfa_pop_trans)[] = { - 0, 0 -}; - #line 432 "parser.rl" static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) { - int cs = EVIL; - VALUE array_class = json->array_class; + int cs = EVIL; + VALUE array_class = json->array_class; - if (json->max_nesting && current_nesting > json->max_nesting) { - rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); - } - *result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class); + if (json->max_nesting && current_nesting > json->max_nesting) { + rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); + } + *result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class); +#line 1206 "parser.c" { - cs = (int)JSON_array_start; + cs = JSON_array_start; } - #line 445 "parser.rl" - +#line 445 "parser.rl" +#line 1213 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { - case 1: - goto st_case_1; - case 0: - goto st_case_0; - case 2: - goto st_case_2; - case 3: - goto st_case_3; - case 4: - goto st_case_4; - case 5: - goto st_case_5; - case 6: - goto st_case_6; - case 7: - goto st_case_7; - case 8: - goto st_case_8; - case 9: - goto st_case_9; - case 10: - goto st_case_10; - case 11: - goto st_case_11; - case 12: - goto st_case_12; - case 17: - goto st_case_17; - case 13: - goto st_case_13; - case 14: - goto st_case_14; - case 15: - goto st_case_15; - case 16: - goto st_case_16; - } - goto st_out; - st_case_1: - if ( ( (*( p))) == 91 ) { - goto st2; - } - { - goto st0; - } - st_case_0: - st0: - cs = 0; - goto _out; - st2: - p+= 1; - if ( p == pe ) + switch ( cs ) + { +case 1: + if ( (*p) == 91 ) + goto st2; + goto st0; +st0: +cs = 0; + goto _out; +st2: + if ( ++p == pe ) goto _test_eof2; - st_case_2: - switch( ( (*( p))) ) { - case 13: { - goto st2; - } - case 32: { - goto st2; - } - case 34: { - goto ctr2; - } - case 45: { - goto ctr2; - } - case 47: { - goto st13; - } - case 73: { - goto ctr2; - } - case 78: { - goto ctr2; - } - case 91: { - goto ctr2; - } - case 93: { - goto ctr4; - } - case 102: { - goto ctr2; - } - case 110: { - goto ctr2; - } - case 116: { - goto ctr2; - } - case 123: { - goto ctr2; - } - } - if ( ( (*( p))) > 10 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto ctr2; - } - } else if ( ( (*( p))) >= 9 ) { - goto st2; - } - { - goto st0; - } - ctr2: - { - #line 409 "parser.rl" - - VALUE v = Qnil; - char *np = JSON_parse_value(json, p, pe, &v, current_nesting); - if (np == NULL) { - {p = p - 1; } {p+= 1; cs = 3; goto _out;} - } else { - if (NIL_P(json->array_class)) { - rb_ary_push(*result, v); - } else { - rb_funcall(*result, i_leftshift, 1, v); - } - {p = (( np))-1;} - - } - } - - goto st3; - st3: - p+= 1; - if ( p == pe ) +case 2: + switch( (*p) ) { + case 13: goto st2; + case 32: goto st2; + case 34: goto tr2; + case 45: goto tr2; + case 47: goto st13; + case 73: goto tr2; + case 78: goto tr2; + case 91: goto tr2; + case 93: goto tr4; + case 102: goto tr2; + case 110: goto tr2; + case 116: goto tr2; + case 123: goto tr2; + } + if ( (*p) > 10 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2; + } else if ( (*p) >= 9 ) + goto st2; + goto st0; +tr2: +#line 409 "parser.rl" + { + VALUE v = Qnil; + char *np = JSON_parse_value(json, p, pe, &v, current_nesting); + if (np == NULL) { + p--; {p++; cs = 3; goto _out;} + } else { + if (NIL_P(json->array_class)) { + rb_ary_push(*result, v); + } else { + rb_funcall(*result, i_leftshift, 1, v); + } + {p = (( np))-1;} + } + } + goto st3; +st3: + if ( ++p == pe ) goto _test_eof3; - st_case_3: - switch( ( (*( p))) ) { - case 13: { - goto st3; - } - case 32: { - goto st3; - } - case 44: { - goto st4; - } - case 47: { - goto st9; - } - case 93: { - goto ctr4; - } - } - if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { - goto st3; - } - { - goto st0; - } - st4: - p+= 1; - if ( p == pe ) +case 3: +#line 1272 "parser.c" + switch( (*p) ) { + case 13: goto st3; + case 32: goto st3; + case 44: goto st4; + case 47: goto st9; + case 93: goto tr4; + } + if ( 9 <= (*p) && (*p) <= 10 ) + goto st3; + goto st0; +st4: + if ( ++p == pe ) goto _test_eof4; - st_case_4: - switch( ( (*( p))) ) { - case 13: { - goto st4; - } - case 32: { - goto st4; - } - case 34: { - goto ctr2; - } - case 45: { - goto ctr2; - } - case 47: { - goto st5; - } - case 73: { - goto ctr2; - } - case 78: { - goto ctr2; - } - case 91: { - goto ctr2; - } - case 102: { - goto ctr2; - } - case 110: { - goto ctr2; - } - case 116: { - goto ctr2; - } - case 123: { - goto ctr2; - } - } - if ( ( (*( p))) > 10 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto ctr2; - } - } else if ( ( (*( p))) >= 9 ) { - goto st4; - } - { - goto st0; - } - st5: - p+= 1; - if ( p == pe ) +case 4: + switch( (*p) ) { + case 13: goto st4; + case 32: goto st4; + case 34: goto tr2; + case 45: goto tr2; + case 47: goto st5; + case 73: goto tr2; + case 78: goto tr2; + case 91: goto tr2; + case 102: goto tr2; + case 110: goto tr2; + case 116: goto tr2; + case 123: goto tr2; + } + if ( (*p) > 10 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2; + } else if ( (*p) >= 9 ) + goto st4; + goto st0; +st5: + if ( ++p == pe ) goto _test_eof5; - st_case_5: - switch( ( (*( p))) ) { - case 42: { - goto st6; - } - case 47: { - goto st8; - } - } - { - goto st0; - } - st6: - p+= 1; - if ( p == pe ) +case 5: + switch( (*p) ) { + case 42: goto st6; + case 47: goto st8; + } + goto st0; +st6: + if ( ++p == pe ) goto _test_eof6; - st_case_6: - if ( ( (*( p))) == 42 ) { - goto st7; - } - { - goto st6; - } - st7: - p+= 1; - if ( p == pe ) +case 6: + if ( (*p) == 42 ) + goto st7; + goto st6; +st7: + if ( ++p == pe ) goto _test_eof7; - st_case_7: - switch( ( (*( p))) ) { - case 42: { - goto st7; - } - case 47: { - goto st4; - } - } - { - goto st6; - } - st8: - p+= 1; - if ( p == pe ) +case 7: + switch( (*p) ) { + case 42: goto st7; + case 47: goto st4; + } + goto st6; +st8: + if ( ++p == pe ) goto _test_eof8; - st_case_8: - if ( ( (*( p))) == 10 ) { - goto st4; - } - { - goto st8; - } - st9: - p+= 1; - if ( p == pe ) +case 8: + if ( (*p) == 10 ) + goto st4; + goto st8; +st9: + if ( ++p == pe ) goto _test_eof9; - st_case_9: - switch( ( (*( p))) ) { - case 42: { - goto st10; - } - case 47: { - goto st12; - } - } - { - goto st0; - } - st10: - p+= 1; - if ( p == pe ) +case 9: + switch( (*p) ) { + case 42: goto st10; + case 47: goto st12; + } + goto st0; +st10: + if ( ++p == pe ) goto _test_eof10; - st_case_10: - if ( ( (*( p))) == 42 ) { - goto st11; - } - { - goto st10; - } - st11: - p+= 1; - if ( p == pe ) +case 10: + if ( (*p) == 42 ) + goto st11; + goto st10; +st11: + if ( ++p == pe ) goto _test_eof11; - st_case_11: - switch( ( (*( p))) ) { - case 42: { - goto st11; - } - case 47: { - goto st3; - } - } - { - goto st10; - } - st12: - p+= 1; - if ( p == pe ) +case 11: + switch( (*p) ) { + case 42: goto st11; + case 47: goto st3; + } + goto st10; +st12: + if ( ++p == pe ) goto _test_eof12; - st_case_12: - if ( ( (*( p))) == 10 ) { - goto st3; - } - { - goto st12; - } - ctr4: - { - #line 424 "parser.rl" - {p = p - 1; } {p+= 1; cs = 17; goto _out;} } - - goto st17; - st17: - p+= 1; - if ( p == pe ) +case 12: + if ( (*p) == 10 ) + goto st3; + goto st12; +tr4: +#line 424 "parser.rl" + { p--; {p++; cs = 17; goto _out;} } + goto st17; +st17: + if ( ++p == pe ) goto _test_eof17; - st_case_17: - { - goto st0; - } - st13: - p+= 1; - if ( p == pe ) +case 17: +#line 1379 "parser.c" + goto st0; +st13: + if ( ++p == pe ) goto _test_eof13; - st_case_13: - switch( ( (*( p))) ) { - case 42: { - goto st14; - } - case 47: { - goto st16; - } - } - { - goto st0; - } - st14: - p+= 1; - if ( p == pe ) +case 13: + switch( (*p) ) { + case 42: goto st14; + case 47: goto st16; + } + goto st0; +st14: + if ( ++p == pe ) goto _test_eof14; - st_case_14: - if ( ( (*( p))) == 42 ) { - goto st15; - } - { - goto st14; - } - st15: - p+= 1; - if ( p == pe ) +case 14: + if ( (*p) == 42 ) + goto st15; + goto st14; +st15: + if ( ++p == pe ) goto _test_eof15; - st_case_15: - switch( ( (*( p))) ) { - case 42: { - goto st15; - } - case 47: { - goto st2; - } - } - { - goto st14; - } - st16: - p+= 1; - if ( p == pe ) +case 15: + switch( (*p) ) { + case 42: goto st15; + case 47: goto st2; + } + goto st14; +st16: + if ( ++p == pe ) goto _test_eof16; - st_case_16: - if ( ( (*( p))) == 10 ) { - goto st2; - } - { - goto st16; - } - st_out: - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof11: cs = 11; goto _test_eof; - _test_eof12: cs = 12; goto _test_eof; - _test_eof17: cs = 17; goto _test_eof; - _test_eof13: cs = 13; goto _test_eof; - _test_eof14: cs = 14; goto _test_eof; - _test_eof15: cs = 15; goto _test_eof; - _test_eof16: cs = 16; goto _test_eof; - - _test_eof: {} - _out: {} - } - - #line 446 "parser.rl" - - - if(cs >= JSON_array_first_final) { - return p + 1; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); - return NULL; +case 16: + if ( (*p) == 10 ) + goto st2; + goto st16; } + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; + _test_eof11: cs = 11; goto _test_eof; + _test_eof12: cs = 12; goto _test_eof; + _test_eof17: cs = 17; goto _test_eof; + _test_eof13: cs = 13; goto _test_eof; + _test_eof14: cs = 14; goto _test_eof; + _test_eof15: cs = 15; goto _test_eof; + _test_eof16: cs = 16; goto _test_eof; + + _test_eof: {} + _out: {} + } + +#line 446 "parser.rl" + + if(cs >= JSON_array_first_final) { + return p + 1; + } else { + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); + return NULL; + } } static const size_t MAX_STACK_BUFFER_SIZE = 128; static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int symbolize) { - VALUE result = Qnil; - size_t bufferSize = stringEnd - string; - char *p = string, *pe = string, *unescape, *bufferStart, *buffer; - int unescape_len; - char buf[4]; + VALUE result = Qnil; + size_t bufferSize = stringEnd - string; + char *p = string, *pe = string, *unescape, *bufferStart, *buffer; + int unescape_len; + char buf[4]; - if (bufferSize > MAX_STACK_BUFFER_SIZE) { + if (bufferSize > MAX_STACK_BUFFER_SIZE) { # ifdef HAVE_RB_ENC_INTERNED_STR - bufferStart = buffer = ALLOC_N(char, bufferSize ? bufferSize : 1); + bufferStart = buffer = ALLOC_N(char, bufferSize ? bufferSize : 1); # else - bufferStart = buffer = ALLOC_N(char, bufferSize); + bufferStart = buffer = ALLOC_N(char, bufferSize); # endif - } else { + } else { # ifdef HAVE_RB_ENC_INTERNED_STR - bufferStart = buffer = ALLOCA_N(char, bufferSize ? bufferSize : 1); + bufferStart = buffer = ALLOCA_N(char, bufferSize ? bufferSize : 1); # else - bufferStart = buffer = ALLOCA_N(char, bufferSize); + bufferStart = buffer = ALLOCA_N(char, bufferSize); # endif - } + } + + while (pe < stringEnd) { + if (*pe == '\\') { + unescape = (char *) "?"; + unescape_len = 1; + if (pe > p) { + MEMCPY(buffer, p, char, pe - p); + buffer += pe - p; + } + switch (*++pe) { + case 'n': + unescape = (char *) "\n"; + break; + case 'r': + unescape = (char *) "\r"; + break; + case 't': + unescape = (char *) "\t"; + break; + case '"': + unescape = (char *) "\""; + break; + case '\\': + unescape = (char *) "\\"; + break; + case 'b': + unescape = (char *) "\b"; + break; + case 'f': + unescape = (char *) "\f"; + break; + case 'u': + if (pe > stringEnd - 4) { + if (bufferSize > MAX_STACK_BUFFER_SIZE) { + ruby_xfree(bufferStart); + } + rb_enc_raise( + EXC_ENCODING eParserError, + "incomplete unicode character escape sequence at '%s'", p + ); + } else { + UTF32 ch = unescape_unicode((unsigned char *) ++pe); + pe += 3; + if (UNI_SUR_HIGH_START == (ch & 0xFC00)) { + pe++; + if (pe > stringEnd - 6) { + if (bufferSize > MAX_STACK_BUFFER_SIZE) { + ruby_xfree(bufferStart); + } + rb_enc_raise( + EXC_ENCODING eParserError, + "incomplete surrogate pair at '%s'", p + ); + } + if (pe[0] == '\\' && pe[1] == 'u') { + UTF32 sur = unescape_unicode((unsigned char *) pe + 2); + ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16) + | (sur & 0x3FF)); + pe += 5; + } else { + unescape = (char *) "?"; + break; + } + } + unescape_len = convert_UTF32_to_UTF8(buf, ch); + unescape = buf; + } + break; + default: + p = pe; + continue; + } + MEMCPY(buffer, unescape, char, unescape_len); + buffer += unescape_len; + p = ++pe; + } else { + pe++; + } + } + + if (pe > p) { + MEMCPY(buffer, p, char, pe - p); + buffer += pe - p; + } - while (pe < stringEnd) { - if (*pe == '\\') { - unescape = (char *) "?"; - unescape_len = 1; - if (pe > p) { - MEMCPY(buffer, p, char, pe - p); - buffer += pe - p; - } - switch (*++pe) { - case 'n': - unescape = (char *) "\n"; - break; - case 'r': - unescape = (char *) "\r"; - break; - case 't': - unescape = (char *) "\t"; - break; - case '"': - unescape = (char *) "\""; - break; - case '\\': - unescape = (char *) "\\"; - break; - case 'b': - unescape = (char *) "\b"; - break; - case 'f': - unescape = (char *) "\f"; - break; - case 'u': - if (pe > stringEnd - 4) { - if (bufferSize > MAX_STACK_BUFFER_SIZE) { - free(bufferStart); - } - rb_enc_raise( - EXC_ENCODING eParserError, - "incomplete unicode character escape sequence at '%s'", p - ); - } else { - UTF32 ch = unescape_unicode((unsigned char *) ++pe); - pe += 3; - if (UNI_SUR_HIGH_START == (ch & 0xFC00)) { - pe++; - if (pe > stringEnd - 6) { - if (bufferSize > MAX_STACK_BUFFER_SIZE) { - free(bufferStart); - } - rb_enc_raise( - EXC_ENCODING eParserError, - "incomplete surrogate pair at '%s'", p - ); - } - if (pe[0] == '\\' && pe[1] == 'u') { - UTF32 sur = unescape_unicode((unsigned char *) pe + 2); - ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16) - | (sur & 0x3FF)); - pe += 5; - } else { - unescape = (char *) "?"; - break; - } - } - unescape_len = convert_UTF32_to_UTF8(buf, ch); - unescape = buf; - } - break; - default: - p = pe; - continue; - } - MEMCPY(buffer, unescape, char, unescape_len); - buffer += unescape_len; - p = ++pe; - } else { - pe++; - } - } - - if (pe > p) { - MEMCPY(buffer, p, char, pe - p); - buffer += pe - p; - } - - # ifdef HAVE_RB_ENC_INTERNED_STR - if (intern) { - result = rb_enc_interned_str(bufferStart, (long)(buffer - bufferStart), rb_utf8_encoding()); - } else { - result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); - } - if (bufferSize > MAX_STACK_BUFFER_SIZE) { - free(bufferStart); - } - # else - result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); - - if (bufferSize > MAX_STACK_BUFFER_SIZE) { - free(bufferStart); - } - - if (intern) { - # if STR_UMINUS_DEDUPE_FROZEN - // Starting from MRI 2.8 it is preferable to freeze the string - // before deduplication so that it can be interned directly - // otherwise it would be duplicated first which is wasteful. - result = rb_funcall(rb_str_freeze(result), i_uminus, 0); - # elif STR_UMINUS_DEDUPE - // MRI 2.5 and older do not deduplicate strings that are already - // frozen. - result = rb_funcall(result, i_uminus, 0); - # else - result = rb_str_freeze(result); - # endif - } - # endif - - if (symbolize) { - result = rb_str_intern(result); - } - - return result; +# ifdef HAVE_RB_ENC_INTERNED_STR + if (intern) { + result = rb_enc_interned_str(bufferStart, (long)(buffer - bufferStart), rb_utf8_encoding()); + } else { + result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); + } + if (bufferSize > MAX_STACK_BUFFER_SIZE) { + ruby_xfree(bufferStart); + } +# else + result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); + + if (bufferSize > MAX_STACK_BUFFER_SIZE) { + ruby_xfree(bufferStart); + } + + if (intern) { + # if STR_UMINUS_DEDUPE_FROZEN + // Starting from MRI 2.8 it is preferable to freeze the string + // before deduplication so that it can be interned directly + // otherwise it would be duplicated first which is wasteful. + result = rb_funcall(rb_str_freeze(result), i_uminus, 0); + # elif STR_UMINUS_DEDUPE + // MRI 2.5 and older do not deduplicate strings that are already + // frozen. + result = rb_funcall(result, i_uminus, 0); + # else + result = rb_str_freeze(result); + # endif + } +# endif + + if (symbolize) { + result = rb_str_intern(result); + } + + return result; } +#line 1592 "parser.c" enum {JSON_string_start = 1}; enum {JSON_string_first_final = 8}; enum {JSON_string_error = 0}; enum {JSON_string_en_main = 1}; -static const char MAYBE_UNUSED(_JSON_string_nfa_targs)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_string_nfa_offsets)[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_string_nfa_push_actions)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_string_nfa_pop_trans)[] = { - 0, 0 -}; - -#line 612 "parser.rl" +#line 620 "parser.rl" static int match_i(VALUE regexp, VALUE klass, VALUE memo) { - if (regexp == Qundef) return ST_STOP; - if (RTEST(rb_funcall(klass, i_json_creatable_p, 0)) && - RTEST(rb_funcall(regexp, i_match, 1, rb_ary_entry(memo, 0)))) { - rb_ary_push(memo, klass); - return ST_STOP; - } - return ST_CONTINUE; + if (regexp == Qundef) return ST_STOP; + if (RTEST(rb_funcall(klass, i_json_creatable_p, 0)) && + RTEST(rb_funcall(regexp, i_match, 1, rb_ary_entry(memo, 0)))) { + rb_ary_push(memo, klass); + return ST_STOP; + } + return ST_CONTINUE; } static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result) { - int cs = EVIL; - VALUE match_string; + int cs = EVIL; + VALUE match_string; +#line 1621 "parser.c" { - cs = (int)JSON_string_start; + cs = JSON_string_start; } - #line 632 "parser.rl" - - json->memo = p; +#line 640 "parser.rl" + json->memo = p; +#line 1629 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { - case 1: - goto st_case_1; - case 0: - goto st_case_0; - case 2: - goto st_case_2; - case 8: - goto st_case_8; - case 3: - goto st_case_3; - case 4: - goto st_case_4; - case 5: - goto st_case_5; - case 6: - goto st_case_6; - case 7: - goto st_case_7; - } - goto st_out; - st_case_1: - if ( ( (*( p))) == 34 ) { - goto st2; - } - { - goto st0; - } - st_case_0: - st0: - cs = 0; - goto _out; - st2: - p+= 1; - if ( p == pe ) + switch ( cs ) + { +case 1: + if ( (*p) == 34 ) + goto st2; + goto st0; +st0: +cs = 0; + goto _out; +st2: + if ( ++p == pe ) goto _test_eof2; - st_case_2: - switch( ( (*( p))) ) { - case 34: { - goto ctr2; - } - case 92: { - goto st3; - } - } - if ( 0 <= (signed char)(*(p)) && (*(p)) <= 31 ) { - goto st0; - } - { - goto st2; - } - ctr2: - { - #line 599 "parser.rl" - - *result = json_string_unescape(json->memo + 1, p, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names); - if (NIL_P(*result)) { - {p = p - 1; } - {p+= 1; cs = 8; goto _out;} - } else { - {p = (( p + 1))-1;} - - } - } - { - #line 609 "parser.rl" - {p = p - 1; } {p+= 1; cs = 8; goto _out;} } - - goto st8; - st8: - p+= 1; - if ( p == pe ) +case 2: + switch( (*p) ) { + case 34: goto tr2; + case 92: goto st3; + } + if ( 0 <= (signed char)(*(p)) && (*(p)) <= 31 ) + goto st0; + goto st2; +tr2: +#line 607 "parser.rl" + { + *result = json_string_unescape(json->memo + 1, p, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names); + if (NIL_P(*result)) { + p--; + {p++; cs = 8; goto _out;} + } else { + {p = (( p + 1))-1;} + } + } +#line 617 "parser.rl" + { p--; {p++; cs = 8; goto _out;} } + goto st8; +st8: + if ( ++p == pe ) goto _test_eof8; - st_case_8: - { - goto st0; - } - st3: - p+= 1; - if ( p == pe ) +case 8: +#line 1671 "parser.c" + goto st0; +st3: + if ( ++p == pe ) goto _test_eof3; - st_case_3: - if ( ( (*( p))) == 117 ) { - goto st4; - } - if ( 0 <= (signed char)(*(p)) && (*(p)) <= 31 ) { - goto st0; - } - { - goto st2; - } - st4: - p+= 1; - if ( p == pe ) +case 3: + if ( (*p) == 117 ) + goto st4; + if ( 0 <= (signed char)(*(p)) && (*(p)) <= 31 ) + goto st0; + goto st2; +st4: + if ( ++p == pe ) goto _test_eof4; - st_case_4: - if ( ( (*( p))) < 65 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st5; - } - } else if ( ( (*( p))) > 70 ) { - if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) { - goto st5; - } - } else { +case 4: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto st5; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) goto st5; - } - { - goto st0; - } - st5: - p+= 1; - if ( p == pe ) + } else + goto st5; + goto st0; +st5: + if ( ++p == pe ) goto _test_eof5; - st_case_5: - if ( ( (*( p))) < 65 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st6; - } - } else if ( ( (*( p))) > 70 ) { - if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) { - goto st6; - } - } else { +case 5: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) goto st6; - } - { - goto st0; - } - st6: - p+= 1; - if ( p == pe ) + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto st6; + } else + goto st6; + goto st0; +st6: + if ( ++p == pe ) goto _test_eof6; - st_case_6: - if ( ( (*( p))) < 65 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st7; - } - } else if ( ( (*( p))) > 70 ) { - if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) { - goto st7; - } - } else { +case 6: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto st7; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) goto st7; - } - { - goto st0; - } - st7: - p+= 1; - if ( p == pe ) + } else + goto st7; + goto st0; +st7: + if ( ++p == pe ) goto _test_eof7; - st_case_7: - if ( ( (*( p))) < 65 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st2; - } - } else if ( ( (*( p))) > 70 ) { - if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) { - goto st2; - } - } else { +case 7: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) goto st2; - } - { - goto st0; - } - st_out: - _test_eof2: cs = 2; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - - _test_eof: {} - _out: {} - } - - #line 634 "parser.rl" - - - if (json->create_additions && RTEST(match_string = json->match_string)) { - VALUE klass; - VALUE memo = rb_ary_new2(2); - rb_ary_push(memo, *result); - rb_hash_foreach(match_string, match_i, memo); - klass = rb_ary_entry(memo, 1); - if (RTEST(klass)) { - *result = rb_funcall(klass, i_json_create, 1, *result); - } - } - - if (cs >= JSON_string_first_final) { - return p + 1; - } else { - return NULL; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto st2; + } else + goto st2; + goto st0; + } + _test_eof2: cs = 2; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + + _test_eof: {} + _out: {} } + +#line 642 "parser.rl" + + if (json->create_additions && RTEST(match_string = json->match_string)) { + VALUE klass; + VALUE memo = rb_ary_new2(2); + rb_ary_push(memo, *result); + rb_hash_foreach(match_string, match_i, memo); + klass = rb_ary_entry(memo, 1); + if (RTEST(klass)) { + *result = rb_funcall(klass, i_json_create, 1, *result); + } + } + + if (cs >= JSON_string_first_final) { + return p + 1; + } else { + return NULL; + } } /* -* Document-class: JSON::Ext::Parser -* -* This is the JSON parser implemented as a C extension. It can be configured -* to be used by setting -* -* JSON.parser = JSON::Ext::Parser -* -* with the method parser= in JSON. -* -*/ + * Document-class: JSON::Ext::Parser + * + * This is the JSON parser implemented as a C extension. It can be configured + * to be used by setting + * + * JSON.parser = JSON::Ext::Parser + * + * with the method parser= in JSON. + * + */ static VALUE convert_encoding(VALUE source) { - #ifdef HAVE_RUBY_ENCODING_H - rb_encoding *enc = rb_enc_get(source); - if (enc == rb_ascii8bit_encoding()) { - if (OBJ_FROZEN(source)) { - source = rb_str_dup(source); - } - FORCE_UTF8(source); - } else { - source = rb_str_conv_enc(source, rb_enc_get(source), rb_utf8_encoding()); - } - #endif - return source; +#ifdef HAVE_RUBY_ENCODING_H + rb_encoding *enc = rb_enc_get(source); + if (enc == rb_ascii8bit_encoding()) { + if (OBJ_FROZEN(source)) { + source = rb_str_dup(source); + } + FORCE_UTF8(source); + } else { + source = rb_str_conv_enc(source, rb_enc_get(source), rb_utf8_encoding()); + } +#endif + return source; } /* -* call-seq: new(source, opts => {}) -* -* Creates a new JSON::Ext::Parser instance for the string _source_. -* -* Creates a new JSON::Ext::Parser instance for the string _source_. -* -* It will be configured by the _opts_ hash. _opts_ can have the following -* keys: -* -* _opts_ can have the following keys: -* * *max_nesting*: The maximum depth of nesting allowed in the parsed data -* structures. Disable depth checking with :max_nesting => false|nil|0, it -* defaults to 100. -* * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in -* defiance of RFC 4627 to be parsed by the Parser. This option defaults to -* false. -* * *symbolize_names*: If set to true, returns symbols for the names -* (keys) in a JSON object. Otherwise strings are returned, which is -* also the default. It's not possible to use this option in -* conjunction with the *create_additions* option. -* * *create_additions*: If set to false, the Parser doesn't create -* additions even if a matching class and create_id was found. This option -* defaults to false. -* * *object_class*: Defaults to Hash -* * *array_class*: Defaults to Array -*/ + * call-seq: new(source, opts => {}) + * + * Creates a new JSON::Ext::Parser instance for the string _source_. + * + * Creates a new JSON::Ext::Parser instance for the string _source_. + * + * It will be configured by the _opts_ hash. _opts_ can have the following + * keys: + * + * _opts_ can have the following keys: + * * *max_nesting*: The maximum depth of nesting allowed in the parsed data + * structures. Disable depth checking with :max_nesting => false|nil|0, it + * defaults to 100. + * * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in + * defiance of RFC 4627 to be parsed by the Parser. This option defaults to + * false. + * * *symbolize_names*: If set to true, returns symbols for the names + * (keys) in a JSON object. Otherwise strings are returned, which is + * also the default. It's not possible to use this option in + * conjunction with the *create_additions* option. + * * *create_additions*: If set to false, the Parser doesn't create + * additions even if a matching class and create_id was found. This option + * defaults to false. + * * *object_class*: Defaults to Hash + * * *array_class*: Defaults to Array + */ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) { - VALUE source, opts; - GET_PARSER_INIT; - - if (json->Vsource) { - rb_raise(rb_eTypeError, "already initialized instance"); - } - #ifdef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH - rb_scan_args(argc, argv, "1:", &source, &opts); - #else - rb_scan_args(argc, argv, "11", &source, &opts); - #endif - if (!NIL_P(opts)) { - #ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH - opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash"); - if (NIL_P(opts)) { - rb_raise(rb_eArgError, "opts needs to be like a hash"); - } else { - #endif - VALUE tmp = ID2SYM(i_max_nesting); - if (option_given_p(opts, tmp)) { - VALUE max_nesting = rb_hash_aref(opts, tmp); - if (RTEST(max_nesting)) { - Check_Type(max_nesting, T_FIXNUM); - json->max_nesting = FIX2INT(max_nesting); - } else { - json->max_nesting = 0; - } - } else { - json->max_nesting = 100; - } - tmp = ID2SYM(i_allow_nan); - if (option_given_p(opts, tmp)) { - json->allow_nan = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; - } else { - json->allow_nan = 0; - } - tmp = ID2SYM(i_symbolize_names); - if (option_given_p(opts, tmp)) { - json->symbolize_names = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; - } else { - json->symbolize_names = 0; - } - tmp = ID2SYM(i_freeze); - if (option_given_p(opts, tmp)) { - json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; - } else { - json->freeze = 0; - } - tmp = ID2SYM(i_create_additions); - if (option_given_p(opts, tmp)) { - json->create_additions = RTEST(rb_hash_aref(opts, tmp)); - } else { - json->create_additions = 0; - } - if (json->symbolize_names && json->create_additions) { - rb_raise(rb_eArgError, - "options :symbolize_names and :create_additions cannot be " - " used in conjunction"); - } - tmp = ID2SYM(i_create_id); - if (option_given_p(opts, tmp)) { - json->create_id = rb_hash_aref(opts, tmp); - } else { - json->create_id = rb_funcall(mJSON, i_create_id, 0); - } - tmp = ID2SYM(i_object_class); - if (option_given_p(opts, tmp)) { - json->object_class = rb_hash_aref(opts, tmp); - } else { - json->object_class = Qnil; - } - tmp = ID2SYM(i_array_class); - if (option_given_p(opts, tmp)) { - json->array_class = rb_hash_aref(opts, tmp); - } else { - json->array_class = Qnil; - } - tmp = ID2SYM(i_decimal_class); - if (option_given_p(opts, tmp)) { - json->decimal_class = rb_hash_aref(opts, tmp); - } else { - json->decimal_class = Qnil; - } - tmp = ID2SYM(i_match_string); - if (option_given_p(opts, tmp)) { - VALUE match_string = rb_hash_aref(opts, tmp); - json->match_string = RTEST(match_string) ? match_string : Qnil; - } else { - json->match_string = Qnil; - } - #ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH - } - #endif - } else { - json->max_nesting = 100; - json->allow_nan = 0; - json->create_additions = 0; - json->create_id = rb_funcall(mJSON, i_create_id, 0); - json->object_class = Qnil; - json->array_class = Qnil; - json->decimal_class = Qnil; - } - source = convert_encoding(StringValue(source)); - StringValue(source); - json->len = RSTRING_LEN(source); - json->source = RSTRING_PTR(source);; - json->Vsource = source; - return self; + VALUE source, opts; + GET_PARSER_INIT; + + if (json->Vsource) { + rb_raise(rb_eTypeError, "already initialized instance"); + } +#ifdef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH + rb_scan_args(argc, argv, "1:", &source, &opts); +#else + rb_scan_args(argc, argv, "11", &source, &opts); +#endif + if (!NIL_P(opts)) { +#ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH + opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash"); + if (NIL_P(opts)) { + rb_raise(rb_eArgError, "opts needs to be like a hash"); + } else { +#endif + VALUE tmp = ID2SYM(i_max_nesting); + if (option_given_p(opts, tmp)) { + VALUE max_nesting = rb_hash_aref(opts, tmp); + if (RTEST(max_nesting)) { + Check_Type(max_nesting, T_FIXNUM); + json->max_nesting = FIX2INT(max_nesting); + } else { + json->max_nesting = 0; + } + } else { + json->max_nesting = 100; + } + tmp = ID2SYM(i_allow_nan); + if (option_given_p(opts, tmp)) { + json->allow_nan = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; + } else { + json->allow_nan = 0; + } + tmp = ID2SYM(i_symbolize_names); + if (option_given_p(opts, tmp)) { + json->symbolize_names = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; + } else { + json->symbolize_names = 0; + } + tmp = ID2SYM(i_freeze); + if (option_given_p(opts, tmp)) { + json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; + } else { + json->freeze = 0; + } + tmp = ID2SYM(i_create_additions); + if (option_given_p(opts, tmp)) { + json->create_additions = RTEST(rb_hash_aref(opts, tmp)); + } else { + json->create_additions = 0; + } + if (json->symbolize_names && json->create_additions) { + rb_raise(rb_eArgError, + "options :symbolize_names and :create_additions cannot be " + " used in conjunction"); + } + tmp = ID2SYM(i_create_id); + if (option_given_p(opts, tmp)) { + json->create_id = rb_hash_aref(opts, tmp); + } else { + json->create_id = rb_funcall(mJSON, i_create_id, 0); + } + tmp = ID2SYM(i_object_class); + if (option_given_p(opts, tmp)) { + json->object_class = rb_hash_aref(opts, tmp); + } else { + json->object_class = Qnil; + } + tmp = ID2SYM(i_array_class); + if (option_given_p(opts, tmp)) { + json->array_class = rb_hash_aref(opts, tmp); + } else { + json->array_class = Qnil; + } + tmp = ID2SYM(i_decimal_class); + if (option_given_p(opts, tmp)) { + json->decimal_class = rb_hash_aref(opts, tmp); + } else { + json->decimal_class = Qnil; + } + tmp = ID2SYM(i_match_string); + if (option_given_p(opts, tmp)) { + VALUE match_string = rb_hash_aref(opts, tmp); + json->match_string = RTEST(match_string) ? match_string : Qnil; + } else { + json->match_string = Qnil; + } +#ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH + } +#endif + } else { + json->max_nesting = 100; + json->allow_nan = 0; + json->create_additions = 0; + json->create_id = rb_funcall(mJSON, i_create_id, 0); + json->object_class = Qnil; + json->array_class = Qnil; + json->decimal_class = Qnil; + } + source = convert_encoding(StringValue(source)); + StringValue(source); + json->len = RSTRING_LEN(source); + json->source = RSTRING_PTR(source);; + json->Vsource = source; + return self; } +#line 1935 "parser.c" enum {JSON_start = 1}; enum {JSON_first_final = 10}; enum {JSON_error = 0}; enum {JSON_en_main = 1}; -static const char MAYBE_UNUSED(_JSON_nfa_targs)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_nfa_offsets)[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 -}; -static const char MAYBE_UNUSED(_JSON_nfa_push_actions)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_nfa_pop_trans)[] = { - 0, 0 -}; - - -#line 835 "parser.rl" +#line 843 "parser.rl" /* -* call-seq: parse() -* -* Parses the current JSON text _source_ and returns the complete data -* structure as a result. -* It raises JSON::ParseError if fail to parse. -*/ + * call-seq: parse() + * + * Parses the current JSON text _source_ and returns the complete data + * structure as a result. + * It raises JSON::ParseError if fail to parse. + */ static VALUE cParser_parse(VALUE self) { - char *p, *pe; - int cs = EVIL; - VALUE result = Qnil; - GET_PARSER; + char *p, *pe; + int cs = EVIL; + VALUE result = Qnil; + GET_PARSER; +#line 1961 "parser.c" { - cs = (int)JSON_start; + cs = JSON_start; } - #line 851 "parser.rl" - - p = json->source; - pe = p + json->len; +#line 860 "parser.rl" + p = json->source; + pe = p + json->len; +#line 1970 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { - case 1: - goto st_case_1; - case 0: - goto st_case_0; - case 10: - goto st_case_10; - case 2: - goto st_case_2; - case 3: - goto st_case_3; - case 4: - goto st_case_4; - case 5: - goto st_case_5; - case 6: - goto st_case_6; - case 7: - goto st_case_7; - case 8: - goto st_case_8; - case 9: - goto st_case_9; - } - goto st_out; - st1: - p+= 1; - if ( p == pe ) + switch ( cs ) + { +st1: + if ( ++p == pe ) goto _test_eof1; - st_case_1: - switch( ( (*( p))) ) { - case 13: { - goto st1; - } - case 32: { - goto st1; - } - case 34: { - goto ctr2; - } - case 45: { - goto ctr2; - } - case 47: { - goto st6; - } - case 73: { - goto ctr2; - } - case 78: { - goto ctr2; - } - case 91: { - goto ctr2; - } - case 102: { - goto ctr2; - } - case 110: { - goto ctr2; - } - case 116: { - goto ctr2; - } - case 123: { - goto ctr2; - } - } - if ( ( (*( p))) > 10 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto ctr2; - } - } else if ( ( (*( p))) >= 9 ) { - goto st1; - } - { - goto st0; - } - st_case_0: - st0: - cs = 0; - goto _out; - ctr2: - { - #line 827 "parser.rl" - - char *np = JSON_parse_value(json, p, pe, &result, 0); - if (np == NULL) { {p = p - 1; } {p+= 1; cs = 10; goto _out;} } else {p = (( np))-1;} - - } - - goto st10; - st10: - p+= 1; - if ( p == pe ) +case 1: + switch( (*p) ) { + case 13: goto st1; + case 32: goto st1; + case 34: goto tr2; + case 45: goto tr2; + case 47: goto st6; + case 73: goto tr2; + case 78: goto tr2; + case 91: goto tr2; + case 102: goto tr2; + case 110: goto tr2; + case 116: goto tr2; + case 123: goto tr2; + } + if ( (*p) > 10 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2; + } else if ( (*p) >= 9 ) + goto st1; + goto st0; +st0: +cs = 0; + goto _out; +tr2: +#line 835 "parser.rl" + { + char *np = JSON_parse_value(json, p, pe, &result, 0); + if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;} + } + goto st10; +st10: + if ( ++p == pe ) goto _test_eof10; - st_case_10: - switch( ( (*( p))) ) { - case 13: { - goto st10; - } - case 32: { - goto st10; - } - case 47: { - goto st2; - } - } - if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { - goto st10; - } - { - goto st0; - } - st2: - p+= 1; - if ( p == pe ) +case 10: +#line 2014 "parser.c" + switch( (*p) ) { + case 13: goto st10; + case 32: goto st10; + case 47: goto st2; + } + if ( 9 <= (*p) && (*p) <= 10 ) + goto st10; + goto st0; +st2: + if ( ++p == pe ) goto _test_eof2; - st_case_2: - switch( ( (*( p))) ) { - case 42: { - goto st3; - } - case 47: { - goto st5; - } - } - { - goto st0; - } - st3: - p+= 1; - if ( p == pe ) +case 2: + switch( (*p) ) { + case 42: goto st3; + case 47: goto st5; + } + goto st0; +st3: + if ( ++p == pe ) goto _test_eof3; - st_case_3: - if ( ( (*( p))) == 42 ) { - goto st4; - } - { - goto st3; - } - st4: - p+= 1; - if ( p == pe ) +case 3: + if ( (*p) == 42 ) + goto st4; + goto st3; +st4: + if ( ++p == pe ) goto _test_eof4; - st_case_4: - switch( ( (*( p))) ) { - case 42: { - goto st4; - } - case 47: { - goto st10; - } - } - { - goto st3; - } - st5: - p+= 1; - if ( p == pe ) +case 4: + switch( (*p) ) { + case 42: goto st4; + case 47: goto st10; + } + goto st3; +st5: + if ( ++p == pe ) goto _test_eof5; - st_case_5: - if ( ( (*( p))) == 10 ) { - goto st10; - } - { - goto st5; - } - st6: - p+= 1; - if ( p == pe ) +case 5: + if ( (*p) == 10 ) + goto st10; + goto st5; +st6: + if ( ++p == pe ) goto _test_eof6; - st_case_6: - switch( ( (*( p))) ) { - case 42: { - goto st7; - } - case 47: { - goto st9; - } - } - { - goto st0; - } - st7: - p+= 1; - if ( p == pe ) +case 6: + switch( (*p) ) { + case 42: goto st7; + case 47: goto st9; + } + goto st0; +st7: + if ( ++p == pe ) goto _test_eof7; - st_case_7: - if ( ( (*( p))) == 42 ) { - goto st8; - } - { - goto st7; - } - st8: - p+= 1; - if ( p == pe ) +case 7: + if ( (*p) == 42 ) + goto st8; + goto st7; +st8: + if ( ++p == pe ) goto _test_eof8; - st_case_8: - switch( ( (*( p))) ) { - case 42: { - goto st8; - } - case 47: { - goto st1; - } - } - { - goto st7; - } - st9: - p+= 1; - if ( p == pe ) +case 8: + switch( (*p) ) { + case 42: goto st8; + case 47: goto st1; + } + goto st7; +st9: + if ( ++p == pe ) goto _test_eof9; - st_case_9: - if ( ( (*( p))) == 10 ) { - goto st1; - } - { - goto st9; - } - st_out: - _test_eof1: cs = 1; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - - _test_eof: {} - _out: {} - } - - #line 854 "parser.rl" - - - if (cs >= JSON_first_final && p == pe) { - return result; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); - return Qnil; +case 9: + if ( (*p) == 10 ) + goto st1; + goto st9; } + _test_eof1: cs = 1; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; + + _test_eof: {} + _out: {} + } + +#line 863 "parser.rl" + + if (cs >= JSON_first_final && p == pe) { + return result; + } else { + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); + return Qnil; + } } static void JSON_mark(void *ptr) { - JSON_Parser *json = ptr; - rb_gc_mark_maybe(json->Vsource); - rb_gc_mark_maybe(json->create_id); - rb_gc_mark_maybe(json->object_class); - rb_gc_mark_maybe(json->array_class); - rb_gc_mark_maybe(json->decimal_class); - rb_gc_mark_maybe(json->match_string); + JSON_Parser *json = ptr; + rb_gc_mark_maybe(json->Vsource); + rb_gc_mark_maybe(json->create_id); + rb_gc_mark_maybe(json->object_class); + rb_gc_mark_maybe(json->array_class); + rb_gc_mark_maybe(json->decimal_class); + rb_gc_mark_maybe(json->match_string); } static void JSON_free(void *ptr) { - JSON_Parser *json = ptr; - fbuffer_free(json->fbuffer); - ruby_xfree(json); + JSON_Parser *json = ptr; + fbuffer_free(json->fbuffer); + ruby_xfree(json); } static size_t JSON_memsize(const void *ptr) { - const JSON_Parser *json = ptr; - return sizeof(*json) + FBUFFER_CAPA(json->fbuffer); + const JSON_Parser *json = ptr; + return sizeof(*json) + FBUFFER_CAPA(json->fbuffer); } #ifdef NEW_TYPEDDATA_WRAPPER static const rb_data_type_t JSON_Parser_type = { - "JSON/Parser", - {JSON_mark, JSON_free, JSON_memsize,}, - #ifdef RUBY_TYPED_FREE_IMMEDIATELY - 0, 0, - RUBY_TYPED_FREE_IMMEDIATELY, - #endif + "JSON/Parser", + {JSON_mark, JSON_free, JSON_memsize,}, +#ifdef RUBY_TYPED_FREE_IMMEDIATELY + 0, 0, + RUBY_TYPED_FREE_IMMEDIATELY, +#endif }; #endif static VALUE cJSON_parser_s_allocate(VALUE klass) { - JSON_Parser *json; - VALUE obj = TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json); - json->fbuffer = fbuffer_alloc(0); - return obj; + JSON_Parser *json; + VALUE obj = TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json); + json->fbuffer = fbuffer_alloc(0); + return obj; } /* -* call-seq: source() -* -* Returns a copy of the current _source_ string, that was used to construct -* this Parser. -*/ + * call-seq: source() + * + * Returns a copy of the current _source_ string, that was used to construct + * this Parser. + */ static VALUE cParser_source(VALUE self) { - GET_PARSER; - return rb_str_dup(json->Vsource); + GET_PARSER; + return rb_str_dup(json->Vsource); } void Init_parser(void) { - #ifdef HAVE_RB_EXT_RACTOR_SAFE - rb_ext_ractor_safe(true); - #endif - - #undef rb_intern - rb_require("json/common"); - mJSON = rb_define_module("JSON"); - mExt = rb_define_module_under(mJSON, "Ext"); - cParser = rb_define_class_under(mExt, "Parser", rb_cObject); - eParserError = rb_path2class("JSON::ParserError"); - eNestingError = rb_path2class("JSON::NestingError"); - rb_gc_register_mark_object(eParserError); - rb_gc_register_mark_object(eNestingError); - rb_define_alloc_func(cParser, cJSON_parser_s_allocate); - rb_define_method(cParser, "initialize", cParser_initialize, -1); - rb_define_method(cParser, "parse", cParser_parse, 0); - rb_define_method(cParser, "source", cParser_source, 0); - - CNaN = rb_const_get(mJSON, rb_intern("NaN")); - rb_gc_register_mark_object(CNaN); - - CInfinity = rb_const_get(mJSON, rb_intern("Infinity")); - rb_gc_register_mark_object(CInfinity); - - CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity")); - rb_gc_register_mark_object(CMinusInfinity); - - i_json_creatable_p = rb_intern("json_creatable?"); - i_json_create = rb_intern("json_create"); - i_create_id = rb_intern("create_id"); - i_create_additions = rb_intern("create_additions"); - i_chr = rb_intern("chr"); - i_max_nesting = rb_intern("max_nesting"); - i_allow_nan = rb_intern("allow_nan"); - i_symbolize_names = rb_intern("symbolize_names"); - i_object_class = rb_intern("object_class"); - i_array_class = rb_intern("array_class"); - i_decimal_class = rb_intern("decimal_class"); - i_match = rb_intern("match"); - i_match_string = rb_intern("match_string"); - i_key_p = rb_intern("key?"); - i_deep_const_get = rb_intern("deep_const_get"); - i_aset = rb_intern("[]="); - i_aref = rb_intern("[]"); - i_leftshift = rb_intern("<<"); - i_new = rb_intern("new"); - i_try_convert = rb_intern("try_convert"); - i_freeze = rb_intern("freeze"); - i_uminus = rb_intern("-@"); +#ifdef HAVE_RB_EXT_RACTOR_SAFE + rb_ext_ractor_safe(true); +#endif + +#undef rb_intern + rb_require("json/common"); + mJSON = rb_define_module("JSON"); + mExt = rb_define_module_under(mJSON, "Ext"); + cParser = rb_define_class_under(mExt, "Parser", rb_cObject); + eParserError = rb_path2class("JSON::ParserError"); + eNestingError = rb_path2class("JSON::NestingError"); + rb_gc_register_mark_object(eParserError); + rb_gc_register_mark_object(eNestingError); + rb_define_alloc_func(cParser, cJSON_parser_s_allocate); + rb_define_method(cParser, "initialize", cParser_initialize, -1); + rb_define_method(cParser, "parse", cParser_parse, 0); + rb_define_method(cParser, "source", cParser_source, 0); + + CNaN = rb_const_get(mJSON, rb_intern("NaN")); + rb_gc_register_mark_object(CNaN); + + CInfinity = rb_const_get(mJSON, rb_intern("Infinity")); + rb_gc_register_mark_object(CInfinity); + + CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity")); + rb_gc_register_mark_object(CMinusInfinity); + + i_json_creatable_p = rb_intern("json_creatable?"); + i_json_create = rb_intern("json_create"); + i_create_id = rb_intern("create_id"); + i_create_additions = rb_intern("create_additions"); + i_chr = rb_intern("chr"); + i_max_nesting = rb_intern("max_nesting"); + i_allow_nan = rb_intern("allow_nan"); + i_symbolize_names = rb_intern("symbolize_names"); + i_object_class = rb_intern("object_class"); + i_array_class = rb_intern("array_class"); + i_decimal_class = rb_intern("decimal_class"); + i_match = rb_intern("match"); + i_match_string = rb_intern("match_string"); + i_key_p = rb_intern("key?"); + i_deep_const_get = rb_intern("deep_const_get"); + i_aset = rb_intern("[]="); + i_aref = rb_intern("[]"); + i_leftshift = rb_intern("<<"); + i_new = rb_intern("new"); + i_try_convert = rb_intern("try_convert"); + i_freeze = rb_intern("freeze"); + i_uminus = rb_intern("-@"); } /* -* Local variables: -* mode: c -* c-file-style: ruby -* indent-tabs-mode: nil -* End: -*/ + * Local variables: + * mode: c + * c-file-style: ruby + * indent-tabs-mode: nil + * End: + */ diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl index 2dbdc7ef2..01201b691 100644 --- a/ext/json/ext/parser/parser.rl +++ b/ext/json/ext/parser/parser.rl @@ -508,7 +508,7 @@ static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int case 'u': if (pe > stringEnd - 4) { if (bufferSize > MAX_STACK_BUFFER_SIZE) { - free(bufferStart); + ruby_xfree(bufferStart); } rb_enc_raise( EXC_ENCODING eParserError, @@ -521,7 +521,7 @@ static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int pe++; if (pe > stringEnd - 6) { if (bufferSize > MAX_STACK_BUFFER_SIZE) { - free(bufferStart); + ruby_xfree(bufferStart); } rb_enc_raise( EXC_ENCODING eParserError, @@ -566,13 +566,13 @@ static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); } if (bufferSize > MAX_STACK_BUFFER_SIZE) { - free(bufferStart); + ruby_xfree(bufferStart); } # else result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); if (bufferSize > MAX_STACK_BUFFER_SIZE) { - free(bufferStart); + ruby_xfree(bufferStart); } if (intern) { From f65f2282d465a34f0745cc6835f137dc85bcbeb4 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 20 Feb 2023 13:22:00 +0100 Subject: [PATCH 0183/1010] Add a `strict` option to Generator The default behavior of calling `to_json` or even `to_s` on unsupported object is problematic in many cases. ``` >> JSON.parse(JSON.dump(Object.new)) => "#" ``` In some cases like blind serailization into some kind of store, you may want to assert that `JSON.parse(JSON.dump(obj)) == obj`. The `strict` option is a way to ensure this by rejecting any type that doesn't map cleanly to a JSON native type. --- ext/json/ext/generator/generator.c | 44 +- ext/json/ext/generator/generator.h | 5 +- ext/json/ext/parser/parser.c | 4809 ++++++++++--------------- java/src/json/ext/Generator.java | 6 +- java/src/json/ext/GeneratorState.java | 28 + lib/json/common.rb | 6 +- lib/json/pure/generator.rb | 27 +- tests/json_generator_test.rb | 11 +- 8 files changed, 1962 insertions(+), 2974 deletions(-) mode change 100644 => 100755 tests/json_generator_test.rb diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 98d0ea46c..8635c686c 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -16,7 +16,7 @@ static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before, i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only, i_pack, i_unpack, i_create_id, i_extend, i_key_p, i_aref, i_send, i_respond_to_p, i_match, i_keys, i_depth, - i_buffer_initial_length, i_dup, i_escape_slash; + i_buffer_initial_length, i_dup, i_escape_slash, i_strict; /* * Copyright 2001-2004 Unicode, Inc. @@ -728,6 +728,8 @@ static VALUE cState_configure(VALUE self, VALUE opts) state->ascii_only = RTEST(tmp); tmp = rb_hash_aref(opts, ID2SYM(i_escape_slash)); state->escape_slash = RTEST(tmp); + tmp = rb_hash_aref(opts, ID2SYM(i_strict)); + state->strict = RTEST(tmp); return self; } @@ -763,6 +765,7 @@ static VALUE cState_to_h(VALUE self) rb_hash_aset(result, ID2SYM(i_ascii_only), state->ascii_only ? Qtrue : Qfalse); rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting)); rb_hash_aset(result, ID2SYM(i_escape_slash), state->escape_slash ? Qtrue : Qfalse); + rb_hash_aset(result, ID2SYM(i_strict), state->strict ? Qtrue : Qfalse); rb_hash_aset(result, ID2SYM(i_depth), LONG2FIX(state->depth)); rb_hash_aset(result, ID2SYM(i_buffer_initial_length), LONG2FIX(state->buffer_initial_length)); return result; @@ -1028,6 +1031,8 @@ static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *s generate_json_bignum(buffer, Vstate, state, obj); } else if (klass == rb_cFloat) { generate_json_float(buffer, Vstate, state, obj); + } else if (state->strict) { + rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(CLASS_OF(obj))); } else if (rb_respond_to(obj, i_to_json)) { tmp = rb_funcall(obj, i_to_json, 1, Vstate); Check_Type(tmp, T_STRING); @@ -1402,7 +1407,7 @@ static VALUE cState_escape_slash(VALUE self) } /* - * call-seq: escape_slash=(depth) + * call-seq: escape_slash=(enable) * * This sets whether or not the forward slashes will be escaped in * the json output. @@ -1414,6 +1419,37 @@ static VALUE cState_escape_slash_set(VALUE self, VALUE enable) return Qnil; } +/* + * call-seq: strict + * + * If this boolean is false, types unsupported by the JSON format will + * be serialized as strings. + * If this boolean is true, types unsupported by the JSON format will + * raise a JSON::GeneratorError. + */ +static VALUE cState_strict(VALUE self) +{ + GET_STATE(self); + return state->strict ? Qtrue : Qfalse; +} + +/* + * call-seq: strict=(enable) + * + * This sets whether or not to serialize types unsupported by the + * JSON format as strings. + * If this boolean is false, types unsupported by the JSON format will + * be serialized as strings. + * If this boolean is true, types unsupported by the JSON format will + * raise a JSON::GeneratorError. + */ +static VALUE cState_strict_set(VALUE self, VALUE enable) +{ + GET_STATE(self); + state->strict = RTEST(enable); + return Qnil; +} + /* * call-seq: allow_nan? * @@ -1533,6 +1569,9 @@ void Init_generator(void) rb_define_method(cState, "escape_slash", cState_escape_slash, 0); rb_define_method(cState, "escape_slash?", cState_escape_slash, 0); rb_define_method(cState, "escape_slash=", cState_escape_slash_set, 1); + rb_define_method(cState, "strict", cState_strict, 0); + rb_define_method(cState, "strict?", cState_strict, 0); + rb_define_method(cState, "strict=", cState_strict_set, 1); rb_define_method(cState, "check_circular?", cState_check_circular_p, 0); rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0); rb_define_method(cState, "ascii_only?", cState_ascii_only_p, 0); @@ -1590,6 +1629,7 @@ void Init_generator(void) i_array_nl = rb_intern("array_nl"); i_max_nesting = rb_intern("max_nesting"); i_escape_slash = rb_intern("escape_slash"); + i_strict = rb_intern("strict"); i_allow_nan = rb_intern("allow_nan"); i_ascii_only = rb_intern("ascii_only"); i_depth = rb_intern("depth"); diff --git a/ext/json/ext/generator/generator.h b/ext/json/ext/generator/generator.h index 3ebd62255..925cc0dfc 100644 --- a/ext/json/ext/generator/generator.h +++ b/ext/json/ext/generator/generator.h @@ -73,6 +73,7 @@ typedef struct JSON_Generator_StateStruct { char allow_nan; char ascii_only; char escape_slash; + char strict; long depth; long buffer_initial_length; } JSON_Generator_State; @@ -152,7 +153,9 @@ static VALUE cState_ascii_only_p(VALUE self); static VALUE cState_depth(VALUE self); static VALUE cState_depth_set(VALUE self, VALUE depth); static VALUE cState_escape_slash(VALUE self); -static VALUE cState_escape_slash_set(VALUE self, VALUE depth); +static VALUE cState_escape_slash_set(VALUE self, VALUE escape_slash); +static VALUE cState_strict(VALUE self); +static VALUE cState_strict_set(VALUE self, VALUE strict); static FBuffer *cState_prepare_buffer(VALUE self); #ifndef ZALLOC #define ZALLOC(type) ((type *)ruby_zalloc(sizeof(type))) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 9bd7f1971..877a2cb66 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -9,14 +9,14 @@ static void enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...) { - va_list args; - VALUE mesg; + va_list args; + VALUE mesg; - va_start(args, fmt); - mesg = rb_enc_vsprintf(enc, fmt, args); - va_end(args); + va_start(args, fmt); + mesg = rb_enc_vsprintf(enc, fmt, args); + va_end(args); - rb_exc_raise(rb_exc_new3(exc, mesg)); + rb_exc_raise(rb_exc_new3(exc, mesg)); } # define rb_enc_raise enc_raise # endif @@ -28,3320 +28,2199 @@ enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...) /* unicode */ static const signed char digit_values[256] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, - -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, + -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1 }; static UTF32 unescape_unicode(const unsigned char *p) { - signed char b; - UTF32 result = 0; - b = digit_values[p[0]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - b = digit_values[p[1]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - b = digit_values[p[2]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - b = digit_values[p[3]]; - if (b < 0) return UNI_REPLACEMENT_CHAR; - result = (result << 4) | (unsigned char)b; - return result; + signed char b; + UTF32 result = 0; + b = digit_values[p[0]]; + if (b < 0) return UNI_REPLACEMENT_CHAR; + result = (result << 4) | (unsigned char)b; + b = digit_values[p[1]]; + if (b < 0) return UNI_REPLACEMENT_CHAR; + result = (result << 4) | (unsigned char)b; + b = digit_values[p[2]]; + if (b < 0) return UNI_REPLACEMENT_CHAR; + result = (result << 4) | (unsigned char)b; + b = digit_values[p[3]]; + if (b < 0) return UNI_REPLACEMENT_CHAR; + result = (result << 4) | (unsigned char)b; + return result; } static int convert_UTF32_to_UTF8(char *buf, UTF32 ch) { - int len = 1; - if (ch <= 0x7F) { - buf[0] = (char) ch; - } else if (ch <= 0x07FF) { - buf[0] = (char) ((ch >> 6) | 0xC0); - buf[1] = (char) ((ch & 0x3F) | 0x80); - len++; - } else if (ch <= 0xFFFF) { - buf[0] = (char) ((ch >> 12) | 0xE0); - buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80); - buf[2] = (char) ((ch & 0x3F) | 0x80); - len += 2; - } else if (ch <= 0x1fffff) { - buf[0] =(char) ((ch >> 18) | 0xF0); - buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80); - buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80); - buf[3] =(char) ((ch & 0x3F) | 0x80); - len += 3; - } else { - buf[0] = '?'; - } - return len; + int len = 1; + if (ch <= 0x7F) { + buf[0] = (char) ch; + } else if (ch <= 0x07FF) { + buf[0] = (char) ((ch >> 6) | 0xC0); + buf[1] = (char) ((ch & 0x3F) | 0x80); + len++; + } else if (ch <= 0xFFFF) { + buf[0] = (char) ((ch >> 12) | 0xE0); + buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80); + buf[2] = (char) ((ch & 0x3F) | 0x80); + len += 2; + } else if (ch <= 0x1fffff) { + buf[0] =(char) ((ch >> 18) | 0xF0); + buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80); + buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80); + buf[3] =(char) ((ch & 0x3F) | 0x80); + len += 3; + } else { + buf[0] = '?'; + } + return len; } static VALUE mJSON, mExt, cParser, eParserError, eNestingError; static VALUE CNaN, CInfinity, CMinusInfinity; static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, -i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, -i_object_class, i_array_class, i_decimal_class, i_key_p, -i_deep_const_get, i_match, i_match_string, i_aset, i_aref, -i_leftshift, i_new, i_try_convert, i_freeze, i_uminus; + i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, + i_object_class, i_array_class, i_decimal_class, i_key_p, + i_deep_const_get, i_match, i_match_string, i_aset, i_aref, + i_leftshift, i_new, i_try_convert, i_freeze, i_uminus; #line 125 "parser.rl" +#line 107 "parser.c" enum {JSON_object_start = 1}; enum {JSON_object_first_final = 27}; enum {JSON_object_error = 0}; enum {JSON_object_en_main = 1}; -static const char MAYBE_UNUSED(_JSON_object_nfa_targs)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_object_nfa_offsets)[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_object_nfa_push_actions)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_object_nfa_pop_trans)[] = { - 0, 0 -}; - #line 167 "parser.rl" static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) { - int cs = EVIL; - VALUE last_name = Qnil; - VALUE object_class = json->object_class; + int cs = EVIL; + VALUE last_name = Qnil; + VALUE object_class = json->object_class; - if (json->max_nesting && current_nesting > json->max_nesting) { - rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); - } + if (json->max_nesting && current_nesting > json->max_nesting) { + rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); + } - *result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class); + *result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class); +#line 131 "parser.c" { - cs = (int)JSON_object_start; + cs = JSON_object_start; } - #line 182 "parser.rl" - +#line 182 "parser.rl" +#line 138 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { - case 1: - goto st_case_1; - case 0: - goto st_case_0; - case 2: - goto st_case_2; - case 3: - goto st_case_3; - case 4: - goto st_case_4; - case 5: - goto st_case_5; - case 6: - goto st_case_6; - case 7: - goto st_case_7; - case 8: - goto st_case_8; - case 9: - goto st_case_9; - case 10: - goto st_case_10; - case 11: - goto st_case_11; - case 12: - goto st_case_12; - case 13: - goto st_case_13; - case 14: - goto st_case_14; - case 15: - goto st_case_15; - case 16: - goto st_case_16; - case 17: - goto st_case_17; - case 18: - goto st_case_18; - case 27: - goto st_case_27; - case 19: - goto st_case_19; - case 20: - goto st_case_20; - case 21: - goto st_case_21; - case 22: - goto st_case_22; - case 23: - goto st_case_23; - case 24: - goto st_case_24; - case 25: - goto st_case_25; - case 26: - goto st_case_26; - } - goto st_out; - st_case_1: - if ( ( (*( p))) == 123 ) { - goto st2; - } - { - goto st0; - } - st_case_0: - st0: - cs = 0; - goto _out; - st2: - p+= 1; - if ( p == pe ) + switch ( cs ) + { +case 1: + if ( (*p) == 123 ) + goto st2; + goto st0; +st0: +cs = 0; + goto _out; +st2: + if ( ++p == pe ) goto _test_eof2; - st_case_2: - switch( ( (*( p))) ) { - case 13: { - goto st2; - } - case 32: { - goto st2; - } - case 34: { - goto ctr2; - } - case 47: { - goto st23; - } - case 125: { - goto ctr4; - } - } - if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { - goto st2; - } - { - goto st0; - } - ctr2: - { - #line 149 "parser.rl" - - char *np; - json->parsing_name = 1; - np = JSON_parse_string(json, p, pe, &last_name); - json->parsing_name = 0; - if (np == NULL) { {p = p - 1; } {p+= 1; cs = 3; goto _out;} } else {p = (( np))-1;} - - } - - goto st3; - st3: - p+= 1; - if ( p == pe ) +case 2: + switch( (*p) ) { + case 13: goto st2; + case 32: goto st2; + case 34: goto tr2; + case 47: goto st23; + case 125: goto tr4; + } + if ( 9 <= (*p) && (*p) <= 10 ) + goto st2; + goto st0; +tr2: +#line 149 "parser.rl" + { + char *np; + json->parsing_name = 1; + np = JSON_parse_string(json, p, pe, &last_name); + json->parsing_name = 0; + if (np == NULL) { p--; {p++; cs = 3; goto _out;} } else {p = (( np))-1;} + } + goto st3; +st3: + if ( ++p == pe ) goto _test_eof3; - st_case_3: - switch( ( (*( p))) ) { - case 13: { - goto st3; - } - case 32: { - goto st3; - } - case 47: { - goto st4; - } - case 58: { - goto st8; - } - } - if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { - goto st3; - } - { - goto st0; - } - st4: - p+= 1; - if ( p == pe ) +case 3: +#line 179 "parser.c" + switch( (*p) ) { + case 13: goto st3; + case 32: goto st3; + case 47: goto st4; + case 58: goto st8; + } + if ( 9 <= (*p) && (*p) <= 10 ) + goto st3; + goto st0; +st4: + if ( ++p == pe ) goto _test_eof4; - st_case_4: - switch( ( (*( p))) ) { - case 42: { - goto st5; - } - case 47: { - goto st7; - } - } - { - goto st0; - } - st5: - p+= 1; - if ( p == pe ) +case 4: + switch( (*p) ) { + case 42: goto st5; + case 47: goto st7; + } + goto st0; +st5: + if ( ++p == pe ) goto _test_eof5; - st_case_5: - if ( ( (*( p))) == 42 ) { - goto st6; - } - { - goto st5; - } - st6: - p+= 1; - if ( p == pe ) +case 5: + if ( (*p) == 42 ) + goto st6; + goto st5; +st6: + if ( ++p == pe ) goto _test_eof6; - st_case_6: - switch( ( (*( p))) ) { - case 42: { - goto st6; - } - case 47: { - goto st3; - } - } - { - goto st5; - } - st7: - p+= 1; - if ( p == pe ) +case 6: + switch( (*p) ) { + case 42: goto st6; + case 47: goto st3; + } + goto st5; +st7: + if ( ++p == pe ) goto _test_eof7; - st_case_7: - if ( ( (*( p))) == 10 ) { - goto st3; - } - { - goto st7; - } - st8: - p+= 1; - if ( p == pe ) +case 7: + if ( (*p) == 10 ) + goto st3; + goto st7; +st8: + if ( ++p == pe ) goto _test_eof8; - st_case_8: - switch( ( (*( p))) ) { - case 13: { - goto st8; - } - case 32: { - goto st8; - } - case 34: { - goto ctr11; - } - case 45: { - goto ctr11; - } - case 47: { - goto st19; - } - case 73: { - goto ctr11; - } - case 78: { - goto ctr11; - } - case 91: { - goto ctr11; - } - case 102: { - goto ctr11; - } - case 110: { - goto ctr11; - } - case 116: { - goto ctr11; - } - case 123: { - goto ctr11; - } - } - if ( ( (*( p))) > 10 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto ctr11; - } - } else if ( ( (*( p))) >= 9 ) { - goto st8; - } - { - goto st0; - } - ctr11: - { - #line 133 "parser.rl" - - VALUE v = Qnil; - char *np = JSON_parse_value(json, p, pe, &v, current_nesting); - if (np == NULL) { - {p = p - 1; } {p+= 1; cs = 9; goto _out;} - } else { - if (NIL_P(json->object_class)) { - OBJ_FREEZE(last_name); - rb_hash_aset(*result, last_name, v); - } else { - rb_funcall(*result, i_aset, 2, last_name, v); - } - {p = (( np))-1;} - - } - } - - goto st9; - st9: - p+= 1; - if ( p == pe ) +case 8: + switch( (*p) ) { + case 13: goto st8; + case 32: goto st8; + case 34: goto tr11; + case 45: goto tr11; + case 47: goto st19; + case 73: goto tr11; + case 78: goto tr11; + case 91: goto tr11; + case 102: goto tr11; + case 110: goto tr11; + case 116: goto tr11; + case 123: goto tr11; + } + if ( (*p) > 10 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr11; + } else if ( (*p) >= 9 ) + goto st8; + goto st0; +tr11: +#line 133 "parser.rl" + { + VALUE v = Qnil; + char *np = JSON_parse_value(json, p, pe, &v, current_nesting); + if (np == NULL) { + p--; {p++; cs = 9; goto _out;} + } else { + if (NIL_P(json->object_class)) { + OBJ_FREEZE(last_name); + rb_hash_aset(*result, last_name, v); + } else { + rb_funcall(*result, i_aset, 2, last_name, v); + } + {p = (( np))-1;} + } + } + goto st9; +st9: + if ( ++p == pe ) goto _test_eof9; - st_case_9: - switch( ( (*( p))) ) { - case 13: { - goto st9; - } - case 32: { - goto st9; - } - case 44: { - goto st10; - } - case 47: { - goto st15; - } - case 125: { - goto ctr4; - } - } - if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { - goto st9; - } - { - goto st0; - } - st10: - p+= 1; - if ( p == pe ) +case 9: +#line 267 "parser.c" + switch( (*p) ) { + case 13: goto st9; + case 32: goto st9; + case 44: goto st10; + case 47: goto st15; + case 125: goto tr4; + } + if ( 9 <= (*p) && (*p) <= 10 ) + goto st9; + goto st0; +st10: + if ( ++p == pe ) goto _test_eof10; - st_case_10: - switch( ( (*( p))) ) { - case 13: { - goto st10; - } - case 32: { - goto st10; - } - case 34: { - goto ctr2; - } - case 47: { - goto st11; - } - } - if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { - goto st10; - } - { - goto st0; - } - st11: - p+= 1; - if ( p == pe ) +case 10: + switch( (*p) ) { + case 13: goto st10; + case 32: goto st10; + case 34: goto tr2; + case 47: goto st11; + } + if ( 9 <= (*p) && (*p) <= 10 ) + goto st10; + goto st0; +st11: + if ( ++p == pe ) goto _test_eof11; - st_case_11: - switch( ( (*( p))) ) { - case 42: { - goto st12; - } - case 47: { - goto st14; - } - } - { - goto st0; - } - st12: - p+= 1; - if ( p == pe ) +case 11: + switch( (*p) ) { + case 42: goto st12; + case 47: goto st14; + } + goto st0; +st12: + if ( ++p == pe ) goto _test_eof12; - st_case_12: - if ( ( (*( p))) == 42 ) { - goto st13; - } - { - goto st12; - } - st13: - p+= 1; - if ( p == pe ) +case 12: + if ( (*p) == 42 ) + goto st13; + goto st12; +st13: + if ( ++p == pe ) goto _test_eof13; - st_case_13: - switch( ( (*( p))) ) { - case 42: { - goto st13; - } - case 47: { - goto st10; - } - } - { - goto st12; - } - st14: - p+= 1; - if ( p == pe ) +case 13: + switch( (*p) ) { + case 42: goto st13; + case 47: goto st10; + } + goto st12; +st14: + if ( ++p == pe ) goto _test_eof14; - st_case_14: - if ( ( (*( p))) == 10 ) { - goto st10; - } - { - goto st14; - } - st15: - p+= 1; - if ( p == pe ) +case 14: + if ( (*p) == 10 ) + goto st10; + goto st14; +st15: + if ( ++p == pe ) goto _test_eof15; - st_case_15: - switch( ( (*( p))) ) { - case 42: { - goto st16; - } - case 47: { - goto st18; - } - } - { - goto st0; - } - st16: - p+= 1; - if ( p == pe ) +case 15: + switch( (*p) ) { + case 42: goto st16; + case 47: goto st18; + } + goto st0; +st16: + if ( ++p == pe ) goto _test_eof16; - st_case_16: - if ( ( (*( p))) == 42 ) { - goto st17; - } - { - goto st16; - } - st17: - p+= 1; - if ( p == pe ) +case 16: + if ( (*p) == 42 ) + goto st17; + goto st16; +st17: + if ( ++p == pe ) goto _test_eof17; - st_case_17: - switch( ( (*( p))) ) { - case 42: { - goto st17; - } - case 47: { - goto st9; - } - } - { - goto st16; - } - st18: - p+= 1; - if ( p == pe ) +case 17: + switch( (*p) ) { + case 42: goto st17; + case 47: goto st9; + } + goto st16; +st18: + if ( ++p == pe ) goto _test_eof18; - st_case_18: - if ( ( (*( p))) == 10 ) { - goto st9; - } - { - goto st18; - } - ctr4: - { - #line 157 "parser.rl" - {p = p - 1; } {p+= 1; cs = 27; goto _out;} } - - goto st27; - st27: - p+= 1; - if ( p == pe ) +case 18: + if ( (*p) == 10 ) + goto st9; + goto st18; +tr4: +#line 157 "parser.rl" + { p--; {p++; cs = 27; goto _out;} } + goto st27; +st27: + if ( ++p == pe ) goto _test_eof27; - st_case_27: - { - goto st0; - } - st19: - p+= 1; - if ( p == pe ) +case 27: +#line 363 "parser.c" + goto st0; +st19: + if ( ++p == pe ) goto _test_eof19; - st_case_19: - switch( ( (*( p))) ) { - case 42: { - goto st20; - } - case 47: { - goto st22; - } - } - { - goto st0; - } - st20: - p+= 1; - if ( p == pe ) +case 19: + switch( (*p) ) { + case 42: goto st20; + case 47: goto st22; + } + goto st0; +st20: + if ( ++p == pe ) goto _test_eof20; - st_case_20: - if ( ( (*( p))) == 42 ) { - goto st21; - } - { - goto st20; - } - st21: - p+= 1; - if ( p == pe ) +case 20: + if ( (*p) == 42 ) + goto st21; + goto st20; +st21: + if ( ++p == pe ) goto _test_eof21; - st_case_21: - switch( ( (*( p))) ) { - case 42: { - goto st21; - } - case 47: { - goto st8; - } - } - { - goto st20; - } - st22: - p+= 1; - if ( p == pe ) +case 21: + switch( (*p) ) { + case 42: goto st21; + case 47: goto st8; + } + goto st20; +st22: + if ( ++p == pe ) goto _test_eof22; - st_case_22: - if ( ( (*( p))) == 10 ) { - goto st8; - } - { - goto st22; - } - st23: - p+= 1; - if ( p == pe ) +case 22: + if ( (*p) == 10 ) + goto st8; + goto st22; +st23: + if ( ++p == pe ) goto _test_eof23; - st_case_23: - switch( ( (*( p))) ) { - case 42: { - goto st24; - } - case 47: { - goto st26; - } - } - { - goto st0; - } - st24: - p+= 1; - if ( p == pe ) +case 23: + switch( (*p) ) { + case 42: goto st24; + case 47: goto st26; + } + goto st0; +st24: + if ( ++p == pe ) goto _test_eof24; - st_case_24: - if ( ( (*( p))) == 42 ) { - goto st25; - } - { - goto st24; - } - st25: - p+= 1; - if ( p == pe ) +case 24: + if ( (*p) == 42 ) + goto st25; + goto st24; +st25: + if ( ++p == pe ) goto _test_eof25; - st_case_25: - switch( ( (*( p))) ) { - case 42: { - goto st25; - } - case 47: { - goto st2; - } - } - { - goto st24; - } - st26: - p+= 1; - if ( p == pe ) +case 25: + switch( (*p) ) { + case 42: goto st25; + case 47: goto st2; + } + goto st24; +st26: + if ( ++p == pe ) goto _test_eof26; - st_case_26: - if ( ( (*( p))) == 10 ) { - goto st2; - } - { - goto st26; - } - st_out: - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof11: cs = 11; goto _test_eof; - _test_eof12: cs = 12; goto _test_eof; - _test_eof13: cs = 13; goto _test_eof; - _test_eof14: cs = 14; goto _test_eof; - _test_eof15: cs = 15; goto _test_eof; - _test_eof16: cs = 16; goto _test_eof; - _test_eof17: cs = 17; goto _test_eof; - _test_eof18: cs = 18; goto _test_eof; - _test_eof27: cs = 27; goto _test_eof; - _test_eof19: cs = 19; goto _test_eof; - _test_eof20: cs = 20; goto _test_eof; - _test_eof21: cs = 21; goto _test_eof; - _test_eof22: cs = 22; goto _test_eof; - _test_eof23: cs = 23; goto _test_eof; - _test_eof24: cs = 24; goto _test_eof; - _test_eof25: cs = 25; goto _test_eof; - _test_eof26: cs = 26; goto _test_eof; - - _test_eof: {} - _out: {} - } - - #line 183 "parser.rl" - - - if (cs >= JSON_object_first_final) { - if (json->create_additions) { - VALUE klassname; - if (NIL_P(json->object_class)) { - klassname = rb_hash_aref(*result, json->create_id); - } else { - klassname = rb_funcall(*result, i_aref, 1, json->create_id); - } - if (!NIL_P(klassname)) { - VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname); - if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) { - *result = rb_funcall(klass, i_json_create, 1, *result); - } - } - } - return p + 1; - } else { - return NULL; +case 26: + if ( (*p) == 10 ) + goto st2; + goto st26; } + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; + _test_eof11: cs = 11; goto _test_eof; + _test_eof12: cs = 12; goto _test_eof; + _test_eof13: cs = 13; goto _test_eof; + _test_eof14: cs = 14; goto _test_eof; + _test_eof15: cs = 15; goto _test_eof; + _test_eof16: cs = 16; goto _test_eof; + _test_eof17: cs = 17; goto _test_eof; + _test_eof18: cs = 18; goto _test_eof; + _test_eof27: cs = 27; goto _test_eof; + _test_eof19: cs = 19; goto _test_eof; + _test_eof20: cs = 20; goto _test_eof; + _test_eof21: cs = 21; goto _test_eof; + _test_eof22: cs = 22; goto _test_eof; + _test_eof23: cs = 23; goto _test_eof; + _test_eof24: cs = 24; goto _test_eof; + _test_eof25: cs = 25; goto _test_eof; + _test_eof26: cs = 26; goto _test_eof; + + _test_eof: {} + _out: {} + } + +#line 183 "parser.rl" + + if (cs >= JSON_object_first_final) { + if (json->create_additions) { + VALUE klassname; + if (NIL_P(json->object_class)) { + klassname = rb_hash_aref(*result, json->create_id); + } else { + klassname = rb_funcall(*result, i_aref, 1, json->create_id); + } + if (!NIL_P(klassname)) { + VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname); + if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) { + *result = rb_funcall(klass, i_json_create, 1, *result); + } + } + } + return p + 1; + } else { + return NULL; + } } +#line 486 "parser.c" enum {JSON_value_start = 1}; enum {JSON_value_first_final = 29}; enum {JSON_value_error = 0}; enum {JSON_value_en_main = 1}; -static const char MAYBE_UNUSED(_JSON_value_nfa_targs)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_value_nfa_offsets)[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_value_nfa_push_actions)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_value_nfa_pop_trans)[] = { - 0, 0 -}; - #line 283 "parser.rl" static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) { - int cs = EVIL; + int cs = EVIL; +#line 502 "parser.c" { - cs = (int)JSON_value_start; + cs = JSON_value_start; } - #line 290 "parser.rl" - +#line 290 "parser.rl" +#line 509 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { - case 1: - goto st_case_1; - case 0: - goto st_case_0; - case 29: - goto st_case_29; - case 2: - goto st_case_2; - case 3: - goto st_case_3; - case 4: - goto st_case_4; - case 5: - goto st_case_5; - case 6: - goto st_case_6; - case 7: - goto st_case_7; - case 8: - goto st_case_8; - case 9: - goto st_case_9; - case 10: - goto st_case_10; - case 11: - goto st_case_11; - case 12: - goto st_case_12; - case 13: - goto st_case_13; - case 14: - goto st_case_14; - case 15: - goto st_case_15; - case 16: - goto st_case_16; - case 17: - goto st_case_17; - case 18: - goto st_case_18; - case 19: - goto st_case_19; - case 20: - goto st_case_20; - case 21: - goto st_case_21; - case 22: - goto st_case_22; - case 23: - goto st_case_23; - case 24: - goto st_case_24; - case 25: - goto st_case_25; - case 26: - goto st_case_26; - case 27: - goto st_case_27; - case 28: - goto st_case_28; - } - goto st_out; - st1: - p+= 1; - if ( p == pe ) + switch ( cs ) + { +st1: + if ( ++p == pe ) goto _test_eof1; - st_case_1: - switch( ( (*( p))) ) { - case 13: { - goto st1; - } - case 32: { - goto st1; - } - case 34: { - goto ctr2; - } - case 45: { - goto ctr3; - } - case 47: { - goto st6; - } - case 73: { - goto st10; - } - case 78: { - goto st17; - } - case 91: { - goto ctr7; - } - case 102: { - goto st19; - } - case 110: { - goto st23; - } - case 116: { - goto st26; - } - case 123: { - goto ctr11; - } - } - if ( ( (*( p))) > 10 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto ctr3; - } - } else if ( ( (*( p))) >= 9 ) { - goto st1; - } - { - goto st0; - } - st_case_0: - st0: - cs = 0; - goto _out; - ctr2: - { - #line 235 "parser.rl" - - char *np = JSON_parse_string(json, p, pe, result); - if (np == NULL) { {p = p - 1; } {p+= 1; cs = 29; goto _out;} } else {p = (( np))-1;} - - } - - goto st29; - ctr3: - { - #line 240 "parser.rl" - - char *np; - if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) { - if (json->allow_nan) { - *result = CMinusInfinity; - {p = (( p + 10))-1;} - - {p = p - 1; } {p+= 1; cs = 29; goto _out;} - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); - } - } - np = JSON_parse_float(json, p, pe, result); - if (np != NULL) {p = (( np))-1;} - - np = JSON_parse_integer(json, p, pe, result); - if (np != NULL) {p = (( np))-1;} - - {p = p - 1; } {p+= 1; cs = 29; goto _out;} - } - - goto st29; - ctr7: - { - #line 258 "parser.rl" - - char *np; - np = JSON_parse_array(json, p, pe, result, current_nesting + 1); - if (np == NULL) { {p = p - 1; } {p+= 1; cs = 29; goto _out;} } else {p = (( np))-1;} - - } - - goto st29; - ctr11: - { - #line 264 "parser.rl" - - char *np; - np = JSON_parse_object(json, p, pe, result, current_nesting + 1); - if (np == NULL) { {p = p - 1; } {p+= 1; cs = 29; goto _out;} } else {p = (( np))-1;} - - } - - goto st29; - ctr25: - { - #line 228 "parser.rl" - - if (json->allow_nan) { - *result = CInfinity; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 8); - } - } - - goto st29; - ctr27: - { - #line 221 "parser.rl" - - if (json->allow_nan) { - *result = CNaN; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 2); - } - } - - goto st29; - ctr31: - { - #line 215 "parser.rl" - - *result = Qfalse; - } - - goto st29; - ctr34: - { - #line 212 "parser.rl" - - *result = Qnil; - } - - goto st29; - ctr37: - { - #line 218 "parser.rl" - - *result = Qtrue; - } - - goto st29; - st29: - p+= 1; - if ( p == pe ) +case 1: + switch( (*p) ) { + case 13: goto st1; + case 32: goto st1; + case 34: goto tr2; + case 45: goto tr3; + case 47: goto st6; + case 73: goto st10; + case 78: goto st17; + case 91: goto tr7; + case 102: goto st19; + case 110: goto st23; + case 116: goto st26; + case 123: goto tr11; + } + if ( (*p) > 10 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3; + } else if ( (*p) >= 9 ) + goto st1; + goto st0; +st0: +cs = 0; + goto _out; +tr2: +#line 235 "parser.rl" + { + char *np = JSON_parse_string(json, p, pe, result); + if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;} + } + goto st29; +tr3: +#line 240 "parser.rl" + { + char *np; + if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) { + if (json->allow_nan) { + *result = CMinusInfinity; + {p = (( p + 10))-1;} + p--; {p++; cs = 29; goto _out;} + } else { + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); + } + } + np = JSON_parse_float(json, p, pe, result); + if (np != NULL) {p = (( np))-1;} + np = JSON_parse_integer(json, p, pe, result); + if (np != NULL) {p = (( np))-1;} + p--; {p++; cs = 29; goto _out;} + } + goto st29; +tr7: +#line 258 "parser.rl" + { + char *np; + np = JSON_parse_array(json, p, pe, result, current_nesting + 1); + if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;} + } + goto st29; +tr11: +#line 264 "parser.rl" + { + char *np; + np = JSON_parse_object(json, p, pe, result, current_nesting + 1); + if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;} + } + goto st29; +tr25: +#line 228 "parser.rl" + { + if (json->allow_nan) { + *result = CInfinity; + } else { + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 8); + } + } + goto st29; +tr27: +#line 221 "parser.rl" + { + if (json->allow_nan) { + *result = CNaN; + } else { + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p - 2); + } + } + goto st29; +tr31: +#line 215 "parser.rl" + { + *result = Qfalse; + } + goto st29; +tr34: +#line 212 "parser.rl" + { + *result = Qnil; + } + goto st29; +tr37: +#line 218 "parser.rl" + { + *result = Qtrue; + } + goto st29; +st29: + if ( ++p == pe ) goto _test_eof29; - st_case_29: - { - #line 270 "parser.rl" - {p = p - 1; } {p+= 1; cs = 29; goto _out;} } - switch( ( (*( p))) ) { - case 13: { - goto st29; - } - case 32: { - goto st29; - } - case 47: { - goto st2; - } - } - if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { - goto st29; - } - { - goto st0; - } - st2: - p+= 1; - if ( p == pe ) +case 29: +#line 270 "parser.rl" + { p--; {p++; cs = 29; goto _out;} } +#line 629 "parser.c" + switch( (*p) ) { + case 13: goto st29; + case 32: goto st29; + case 47: goto st2; + } + if ( 9 <= (*p) && (*p) <= 10 ) + goto st29; + goto st0; +st2: + if ( ++p == pe ) goto _test_eof2; - st_case_2: - switch( ( (*( p))) ) { - case 42: { - goto st3; - } - case 47: { - goto st5; - } - } - { - goto st0; - } - st3: - p+= 1; - if ( p == pe ) +case 2: + switch( (*p) ) { + case 42: goto st3; + case 47: goto st5; + } + goto st0; +st3: + if ( ++p == pe ) goto _test_eof3; - st_case_3: - if ( ( (*( p))) == 42 ) { - goto st4; - } - { - goto st3; - } - st4: - p+= 1; - if ( p == pe ) +case 3: + if ( (*p) == 42 ) + goto st4; + goto st3; +st4: + if ( ++p == pe ) goto _test_eof4; - st_case_4: - switch( ( (*( p))) ) { - case 42: { - goto st4; - } - case 47: { - goto st29; - } - } - { - goto st3; - } - st5: - p+= 1; - if ( p == pe ) +case 4: + switch( (*p) ) { + case 42: goto st4; + case 47: goto st29; + } + goto st3; +st5: + if ( ++p == pe ) goto _test_eof5; - st_case_5: - if ( ( (*( p))) == 10 ) { - goto st29; - } - { - goto st5; - } - st6: - p+= 1; - if ( p == pe ) +case 5: + if ( (*p) == 10 ) + goto st29; + goto st5; +st6: + if ( ++p == pe ) goto _test_eof6; - st_case_6: - switch( ( (*( p))) ) { - case 42: { - goto st7; - } - case 47: { - goto st9; - } - } - { - goto st0; - } - st7: - p+= 1; - if ( p == pe ) +case 6: + switch( (*p) ) { + case 42: goto st7; + case 47: goto st9; + } + goto st0; +st7: + if ( ++p == pe ) goto _test_eof7; - st_case_7: - if ( ( (*( p))) == 42 ) { - goto st8; - } - { - goto st7; - } - st8: - p+= 1; - if ( p == pe ) +case 7: + if ( (*p) == 42 ) + goto st8; + goto st7; +st8: + if ( ++p == pe ) goto _test_eof8; - st_case_8: - switch( ( (*( p))) ) { - case 42: { - goto st8; - } - case 47: { - goto st1; - } - } - { - goto st7; - } - st9: - p+= 1; - if ( p == pe ) +case 8: + switch( (*p) ) { + case 42: goto st8; + case 47: goto st1; + } + goto st7; +st9: + if ( ++p == pe ) goto _test_eof9; - st_case_9: - if ( ( (*( p))) == 10 ) { - goto st1; - } - { - goto st9; - } - st10: - p+= 1; - if ( p == pe ) +case 9: + if ( (*p) == 10 ) + goto st1; + goto st9; +st10: + if ( ++p == pe ) goto _test_eof10; - st_case_10: - if ( ( (*( p))) == 110 ) { - goto st11; - } - { - goto st0; - } - st11: - p+= 1; - if ( p == pe ) +case 10: + if ( (*p) == 110 ) + goto st11; + goto st0; +st11: + if ( ++p == pe ) goto _test_eof11; - st_case_11: - if ( ( (*( p))) == 102 ) { - goto st12; - } - { - goto st0; - } - st12: - p+= 1; - if ( p == pe ) +case 11: + if ( (*p) == 102 ) + goto st12; + goto st0; +st12: + if ( ++p == pe ) goto _test_eof12; - st_case_12: - if ( ( (*( p))) == 105 ) { - goto st13; - } - { - goto st0; - } - st13: - p+= 1; - if ( p == pe ) +case 12: + if ( (*p) == 105 ) + goto st13; + goto st0; +st13: + if ( ++p == pe ) goto _test_eof13; - st_case_13: - if ( ( (*( p))) == 110 ) { - goto st14; - } - { - goto st0; - } - st14: - p+= 1; - if ( p == pe ) +case 13: + if ( (*p) == 110 ) + goto st14; + goto st0; +st14: + if ( ++p == pe ) goto _test_eof14; - st_case_14: - if ( ( (*( p))) == 105 ) { - goto st15; - } - { - goto st0; - } - st15: - p+= 1; - if ( p == pe ) +case 14: + if ( (*p) == 105 ) + goto st15; + goto st0; +st15: + if ( ++p == pe ) goto _test_eof15; - st_case_15: - if ( ( (*( p))) == 116 ) { - goto st16; - } - { - goto st0; - } - st16: - p+= 1; - if ( p == pe ) +case 15: + if ( (*p) == 116 ) + goto st16; + goto st0; +st16: + if ( ++p == pe ) goto _test_eof16; - st_case_16: - if ( ( (*( p))) == 121 ) { - goto ctr25; - } - { - goto st0; - } - st17: - p+= 1; - if ( p == pe ) +case 16: + if ( (*p) == 121 ) + goto tr25; + goto st0; +st17: + if ( ++p == pe ) goto _test_eof17; - st_case_17: - if ( ( (*( p))) == 97 ) { - goto st18; - } - { - goto st0; - } - st18: - p+= 1; - if ( p == pe ) +case 17: + if ( (*p) == 97 ) + goto st18; + goto st0; +st18: + if ( ++p == pe ) goto _test_eof18; - st_case_18: - if ( ( (*( p))) == 78 ) { - goto ctr27; - } - { - goto st0; - } - st19: - p+= 1; - if ( p == pe ) +case 18: + if ( (*p) == 78 ) + goto tr27; + goto st0; +st19: + if ( ++p == pe ) goto _test_eof19; - st_case_19: - if ( ( (*( p))) == 97 ) { - goto st20; - } - { - goto st0; - } - st20: - p+= 1; - if ( p == pe ) +case 19: + if ( (*p) == 97 ) + goto st20; + goto st0; +st20: + if ( ++p == pe ) goto _test_eof20; - st_case_20: - if ( ( (*( p))) == 108 ) { - goto st21; - } - { - goto st0; - } - st21: - p+= 1; - if ( p == pe ) +case 20: + if ( (*p) == 108 ) + goto st21; + goto st0; +st21: + if ( ++p == pe ) goto _test_eof21; - st_case_21: - if ( ( (*( p))) == 115 ) { - goto st22; - } - { - goto st0; - } - st22: - p+= 1; - if ( p == pe ) +case 21: + if ( (*p) == 115 ) + goto st22; + goto st0; +st22: + if ( ++p == pe ) goto _test_eof22; - st_case_22: - if ( ( (*( p))) == 101 ) { - goto ctr31; - } - { - goto st0; - } - st23: - p+= 1; - if ( p == pe ) +case 22: + if ( (*p) == 101 ) + goto tr31; + goto st0; +st23: + if ( ++p == pe ) goto _test_eof23; - st_case_23: - if ( ( (*( p))) == 117 ) { - goto st24; - } - { - goto st0; - } - st24: - p+= 1; - if ( p == pe ) +case 23: + if ( (*p) == 117 ) + goto st24; + goto st0; +st24: + if ( ++p == pe ) goto _test_eof24; - st_case_24: - if ( ( (*( p))) == 108 ) { - goto st25; - } - { - goto st0; - } - st25: - p+= 1; - if ( p == pe ) +case 24: + if ( (*p) == 108 ) + goto st25; + goto st0; +st25: + if ( ++p == pe ) goto _test_eof25; - st_case_25: - if ( ( (*( p))) == 108 ) { - goto ctr34; - } - { - goto st0; - } - st26: - p+= 1; - if ( p == pe ) +case 25: + if ( (*p) == 108 ) + goto tr34; + goto st0; +st26: + if ( ++p == pe ) goto _test_eof26; - st_case_26: - if ( ( (*( p))) == 114 ) { - goto st27; - } - { - goto st0; - } - st27: - p+= 1; - if ( p == pe ) +case 26: + if ( (*p) == 114 ) + goto st27; + goto st0; +st27: + if ( ++p == pe ) goto _test_eof27; - st_case_27: - if ( ( (*( p))) == 117 ) { - goto st28; - } - { - goto st0; - } - st28: - p+= 1; - if ( p == pe ) +case 27: + if ( (*p) == 117 ) + goto st28; + goto st0; +st28: + if ( ++p == pe ) goto _test_eof28; - st_case_28: - if ( ( (*( p))) == 101 ) { - goto ctr37; - } - { - goto st0; - } - st_out: - _test_eof1: cs = 1; goto _test_eof; - _test_eof29: cs = 29; goto _test_eof; - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof11: cs = 11; goto _test_eof; - _test_eof12: cs = 12; goto _test_eof; - _test_eof13: cs = 13; goto _test_eof; - _test_eof14: cs = 14; goto _test_eof; - _test_eof15: cs = 15; goto _test_eof; - _test_eof16: cs = 16; goto _test_eof; - _test_eof17: cs = 17; goto _test_eof; - _test_eof18: cs = 18; goto _test_eof; - _test_eof19: cs = 19; goto _test_eof; - _test_eof20: cs = 20; goto _test_eof; - _test_eof21: cs = 21; goto _test_eof; - _test_eof22: cs = 22; goto _test_eof; - _test_eof23: cs = 23; goto _test_eof; - _test_eof24: cs = 24; goto _test_eof; - _test_eof25: cs = 25; goto _test_eof; - _test_eof26: cs = 26; goto _test_eof; - _test_eof27: cs = 27; goto _test_eof; - _test_eof28: cs = 28; goto _test_eof; - - _test_eof: {} - _out: {} - } - - #line 291 "parser.rl" - - - if (json->freeze) { - OBJ_FREEZE(*result); - } - - if (cs >= JSON_value_first_final) { - return p; - } else { - return NULL; +case 28: + if ( (*p) == 101 ) + goto tr37; + goto st0; } + _test_eof1: cs = 1; goto _test_eof; + _test_eof29: cs = 29; goto _test_eof; + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; + _test_eof11: cs = 11; goto _test_eof; + _test_eof12: cs = 12; goto _test_eof; + _test_eof13: cs = 13; goto _test_eof; + _test_eof14: cs = 14; goto _test_eof; + _test_eof15: cs = 15; goto _test_eof; + _test_eof16: cs = 16; goto _test_eof; + _test_eof17: cs = 17; goto _test_eof; + _test_eof18: cs = 18; goto _test_eof; + _test_eof19: cs = 19; goto _test_eof; + _test_eof20: cs = 20; goto _test_eof; + _test_eof21: cs = 21; goto _test_eof; + _test_eof22: cs = 22; goto _test_eof; + _test_eof23: cs = 23; goto _test_eof; + _test_eof24: cs = 24; goto _test_eof; + _test_eof25: cs = 25; goto _test_eof; + _test_eof26: cs = 26; goto _test_eof; + _test_eof27: cs = 27; goto _test_eof; + _test_eof28: cs = 28; goto _test_eof; + + _test_eof: {} + _out: {} + } + +#line 291 "parser.rl" + + if (json->freeze) { + OBJ_FREEZE(*result); + } + + if (cs >= JSON_value_first_final) { + return p; + } else { + return NULL; + } } +#line 884 "parser.c" enum {JSON_integer_start = 1}; enum {JSON_integer_first_final = 3}; enum {JSON_integer_error = 0}; enum {JSON_integer_en_main = 1}; -static const char MAYBE_UNUSED(_JSON_integer_nfa_targs)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_integer_nfa_offsets)[] = { - 0, 0, 0, 0, 0, 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_integer_nfa_push_actions)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_integer_nfa_pop_trans)[] = { - 0, 0 -}; - #line 311 "parser.rl" static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result) { - int cs = EVIL; + int cs = EVIL; +#line 900 "parser.c" { - cs = (int)JSON_integer_start; + cs = JSON_integer_start; } - #line 318 "parser.rl" - - json->memo = p; +#line 318 "parser.rl" + json->memo = p; +#line 908 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { - case 1: - goto st_case_1; - case 0: - goto st_case_0; - case 2: - goto st_case_2; - case 3: - goto st_case_3; - case 4: - goto st_case_4; - case 5: - goto st_case_5; - } - goto st_out; - st_case_1: - switch( ( (*( p))) ) { - case 45: { - goto st2; - } - case 48: { - goto st3; - } - } - if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st5; - } - { - goto st0; - } - st_case_0: - st0: - cs = 0; - goto _out; - st2: - p+= 1; - if ( p == pe ) + switch ( cs ) + { +case 1: + switch( (*p) ) { + case 45: goto st2; + case 48: goto st3; + } + if ( 49 <= (*p) && (*p) <= 57 ) + goto st5; + goto st0; +st0: +cs = 0; + goto _out; +st2: + if ( ++p == pe ) goto _test_eof2; - st_case_2: - if ( ( (*( p))) == 48 ) { - goto st3; - } - if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st5; - } - { - goto st0; - } - st3: - p+= 1; - if ( p == pe ) +case 2: + if ( (*p) == 48 ) + goto st3; + if ( 49 <= (*p) && (*p) <= 57 ) + goto st5; + goto st0; +st3: + if ( ++p == pe ) goto _test_eof3; - st_case_3: - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st0; - } - { - goto ctr4; - } - ctr4: - { - #line 308 "parser.rl" - {p = p - 1; } {p+= 1; cs = 4; goto _out;} } - - goto st4; - st4: - p+= 1; - if ( p == pe ) +case 3: + if ( 48 <= (*p) && (*p) <= 57 ) + goto st0; + goto tr4; +tr4: +#line 308 "parser.rl" + { p--; {p++; cs = 4; goto _out;} } + goto st4; +st4: + if ( ++p == pe ) goto _test_eof4; - st_case_4: - { - goto st0; - } - st5: - p+= 1; - if ( p == pe ) +case 4: +#line 949 "parser.c" + goto st0; +st5: + if ( ++p == pe ) goto _test_eof5; - st_case_5: - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st5; - } - { - goto ctr4; - } - st_out: - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - - _test_eof: {} - _out: {} +case 5: + if ( 48 <= (*p) && (*p) <= 57 ) + goto st5; + goto tr4; } + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; - #line 320 "parser.rl" - - - if (cs >= JSON_integer_first_final) { - long len = p - json->memo; - fbuffer_clear(json->fbuffer); - fbuffer_append(json->fbuffer, json->memo, len); - fbuffer_append_char(json->fbuffer, '\0'); - *result = rb_cstr2inum(FBUFFER_PTR(json->fbuffer), 10); - return p + 1; - } else { - return NULL; + _test_eof: {} + _out: {} } + +#line 320 "parser.rl" + + if (cs >= JSON_integer_first_final) { + long len = p - json->memo; + fbuffer_clear(json->fbuffer); + fbuffer_append(json->fbuffer, json->memo, len); + fbuffer_append_char(json->fbuffer, '\0'); + *result = rb_cstr2inum(FBUFFER_PTR(json->fbuffer), 10); + return p + 1; + } else { + return NULL; + } } +#line 983 "parser.c" enum {JSON_float_start = 1}; enum {JSON_float_first_final = 8}; enum {JSON_float_error = 0}; enum {JSON_float_en_main = 1}; -static const char MAYBE_UNUSED(_JSON_float_nfa_targs)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_float_nfa_offsets)[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_float_nfa_push_actions)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_float_nfa_pop_trans)[] = { - 0, 0 -}; - #line 345 "parser.rl" static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result) { - int cs = EVIL; + int cs = EVIL; +#line 999 "parser.c" { - cs = (int)JSON_float_start; + cs = JSON_float_start; } - #line 352 "parser.rl" - - json->memo = p; +#line 352 "parser.rl" + json->memo = p; +#line 1007 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { - case 1: - goto st_case_1; - case 0: - goto st_case_0; - case 2: - goto st_case_2; - case 3: - goto st_case_3; - case 4: - goto st_case_4; - case 8: - goto st_case_8; - case 9: - goto st_case_9; - case 5: - goto st_case_5; - case 6: - goto st_case_6; - case 10: - goto st_case_10; - case 7: - goto st_case_7; - } - goto st_out; - st_case_1: - switch( ( (*( p))) ) { - case 45: { - goto st2; - } - case 48: { - goto st3; - } - } - if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st7; - } - { - goto st0; - } - st_case_0: - st0: - cs = 0; - goto _out; - st2: - p+= 1; - if ( p == pe ) + switch ( cs ) + { +case 1: + switch( (*p) ) { + case 45: goto st2; + case 48: goto st3; + } + if ( 49 <= (*p) && (*p) <= 57 ) + goto st7; + goto st0; +st0: +cs = 0; + goto _out; +st2: + if ( ++p == pe ) goto _test_eof2; - st_case_2: - if ( ( (*( p))) == 48 ) { - goto st3; - } - if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st7; - } - { - goto st0; - } - st3: - p+= 1; - if ( p == pe ) +case 2: + if ( (*p) == 48 ) + goto st3; + if ( 49 <= (*p) && (*p) <= 57 ) + goto st7; + goto st0; +st3: + if ( ++p == pe ) goto _test_eof3; - st_case_3: - switch( ( (*( p))) ) { - case 46: { - goto st4; - } - case 69: { - goto st5; - } - case 101: { - goto st5; - } - } - { - goto st0; - } - st4: - p+= 1; - if ( p == pe ) +case 3: + switch( (*p) ) { + case 46: goto st4; + case 69: goto st5; + case 101: goto st5; + } + goto st0; +st4: + if ( ++p == pe ) goto _test_eof4; - st_case_4: - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st8; - } - { - goto st0; - } - st8: - p+= 1; - if ( p == pe ) +case 4: + if ( 48 <= (*p) && (*p) <= 57 ) + goto st8; + goto st0; +st8: + if ( ++p == pe ) goto _test_eof8; - st_case_8: - switch( ( (*( p))) ) { - case 69: { - goto st5; - } - case 101: { - goto st5; - } - } - if ( ( (*( p))) > 46 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st8; - } - } else if ( ( (*( p))) >= 45 ) { - goto st0; - } - { - goto ctr9; - } - ctr9: - { - #line 339 "parser.rl" - {p = p - 1; } {p+= 1; cs = 9; goto _out;} } - - goto st9; - st9: - p+= 1; - if ( p == pe ) +case 8: + switch( (*p) ) { + case 69: goto st5; + case 101: goto st5; + } + if ( (*p) > 46 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto st8; + } else if ( (*p) >= 45 ) + goto st0; + goto tr9; +tr9: +#line 339 "parser.rl" + { p--; {p++; cs = 9; goto _out;} } + goto st9; +st9: + if ( ++p == pe ) goto _test_eof9; - st_case_9: - { - goto st0; - } - st5: - p+= 1; - if ( p == pe ) +case 9: +#line 1072 "parser.c" + goto st0; +st5: + if ( ++p == pe ) goto _test_eof5; - st_case_5: - switch( ( (*( p))) ) { - case 43: { - goto st6; - } - case 45: { - goto st6; - } - } - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st10; - } - { - goto st0; - } - st6: - p+= 1; - if ( p == pe ) +case 5: + switch( (*p) ) { + case 43: goto st6; + case 45: goto st6; + } + if ( 48 <= (*p) && (*p) <= 57 ) + goto st10; + goto st0; +st6: + if ( ++p == pe ) goto _test_eof6; - st_case_6: - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st10; - } - { - goto st0; - } - st10: - p+= 1; - if ( p == pe ) +case 6: + if ( 48 <= (*p) && (*p) <= 57 ) + goto st10; + goto st0; +st10: + if ( ++p == pe ) goto _test_eof10; - st_case_10: - switch( ( (*( p))) ) { - case 69: { - goto st0; - } - case 101: { - goto st0; - } - } - if ( ( (*( p))) > 46 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st10; - } - } else if ( ( (*( p))) >= 45 ) { - goto st0; - } - { - goto ctr9; - } - st7: - p+= 1; - if ( p == pe ) +case 10: + switch( (*p) ) { + case 69: goto st0; + case 101: goto st0; + } + if ( (*p) > 46 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto st10; + } else if ( (*p) >= 45 ) + goto st0; + goto tr9; +st7: + if ( ++p == pe ) goto _test_eof7; - st_case_7: - switch( ( (*( p))) ) { - case 46: { - goto st4; - } - case 69: { - goto st5; - } - case 101: { - goto st5; - } - } - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st7; - } - { - goto st0; - } - st_out: - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - - _test_eof: {} - _out: {} - } - - #line 354 "parser.rl" - - - if (cs >= JSON_float_first_final) { - VALUE mod = Qnil; - ID method_id = 0; - if (rb_respond_to(json->decimal_class, i_try_convert)) { - mod = json->decimal_class; - method_id = i_try_convert; - } else if (rb_respond_to(json->decimal_class, i_new)) { - mod = json->decimal_class; - method_id = i_new; - } else if (RB_TYPE_P(json->decimal_class, T_CLASS)) { - VALUE name = rb_class_name(json->decimal_class); - const char *name_cstr = RSTRING_PTR(name); - const char *last_colon = strrchr(name_cstr, ':'); - if (last_colon) { - const char *mod_path_end = last_colon - 1; - VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr); - mod = rb_path_to_class(mod_path); - - const char *method_name_beg = last_colon + 1; - long before_len = method_name_beg - name_cstr; - long len = RSTRING_LEN(name) - before_len; - VALUE method_name = rb_str_substr(name, before_len, len); - method_id = SYM2ID(rb_str_intern(method_name)); - } else { - mod = rb_mKernel; - method_id = SYM2ID(rb_str_intern(name)); - } - } - - long len = p - json->memo; - fbuffer_clear(json->fbuffer); - fbuffer_append(json->fbuffer, json->memo, len); - fbuffer_append_char(json->fbuffer, '\0'); - - if (method_id) { - VALUE text = rb_str_new2(FBUFFER_PTR(json->fbuffer)); - *result = rb_funcallv(mod, method_id, 1, &text); - } else { - *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1)); - } - - return p + 1; - } else { - return NULL; +case 7: + switch( (*p) ) { + case 46: goto st4; + case 69: goto st5; + case 101: goto st5; } + if ( 48 <= (*p) && (*p) <= 57 ) + goto st7; + goto st0; + } + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + + _test_eof: {} + _out: {} + } + +#line 354 "parser.rl" + + if (cs >= JSON_float_first_final) { + VALUE mod = Qnil; + ID method_id = 0; + if (rb_respond_to(json->decimal_class, i_try_convert)) { + mod = json->decimal_class; + method_id = i_try_convert; + } else if (rb_respond_to(json->decimal_class, i_new)) { + mod = json->decimal_class; + method_id = i_new; + } else if (RB_TYPE_P(json->decimal_class, T_CLASS)) { + VALUE name = rb_class_name(json->decimal_class); + const char *name_cstr = RSTRING_PTR(name); + const char *last_colon = strrchr(name_cstr, ':'); + if (last_colon) { + const char *mod_path_end = last_colon - 1; + VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr); + mod = rb_path_to_class(mod_path); + + const char *method_name_beg = last_colon + 1; + long before_len = method_name_beg - name_cstr; + long len = RSTRING_LEN(name) - before_len; + VALUE method_name = rb_str_substr(name, before_len, len); + method_id = SYM2ID(rb_str_intern(method_name)); + } else { + mod = rb_mKernel; + method_id = SYM2ID(rb_str_intern(name)); + } + } + + long len = p - json->memo; + fbuffer_clear(json->fbuffer); + fbuffer_append(json->fbuffer, json->memo, len); + fbuffer_append_char(json->fbuffer, '\0'); + + if (method_id) { + VALUE text = rb_str_new2(FBUFFER_PTR(json->fbuffer)); + *result = rb_funcallv(mod, method_id, 1, &text); + } else { + *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1)); + } + + return p + 1; + } else { + return NULL; + } } +#line 1184 "parser.c" enum {JSON_array_start = 1}; enum {JSON_array_first_final = 17}; enum {JSON_array_error = 0}; enum {JSON_array_en_main = 1}; -static const char MAYBE_UNUSED(_JSON_array_nfa_targs)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_array_nfa_offsets)[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_array_nfa_push_actions)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_array_nfa_pop_trans)[] = { - 0, 0 -}; - #line 432 "parser.rl" static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) { - int cs = EVIL; - VALUE array_class = json->array_class; + int cs = EVIL; + VALUE array_class = json->array_class; - if (json->max_nesting && current_nesting > json->max_nesting) { - rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); - } - *result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class); + if (json->max_nesting && current_nesting > json->max_nesting) { + rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); + } + *result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class); +#line 1206 "parser.c" { - cs = (int)JSON_array_start; + cs = JSON_array_start; } - #line 445 "parser.rl" - +#line 445 "parser.rl" +#line 1213 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { - case 1: - goto st_case_1; - case 0: - goto st_case_0; - case 2: - goto st_case_2; - case 3: - goto st_case_3; - case 4: - goto st_case_4; - case 5: - goto st_case_5; - case 6: - goto st_case_6; - case 7: - goto st_case_7; - case 8: - goto st_case_8; - case 9: - goto st_case_9; - case 10: - goto st_case_10; - case 11: - goto st_case_11; - case 12: - goto st_case_12; - case 17: - goto st_case_17; - case 13: - goto st_case_13; - case 14: - goto st_case_14; - case 15: - goto st_case_15; - case 16: - goto st_case_16; - } - goto st_out; - st_case_1: - if ( ( (*( p))) == 91 ) { - goto st2; - } - { - goto st0; - } - st_case_0: - st0: - cs = 0; - goto _out; - st2: - p+= 1; - if ( p == pe ) + switch ( cs ) + { +case 1: + if ( (*p) == 91 ) + goto st2; + goto st0; +st0: +cs = 0; + goto _out; +st2: + if ( ++p == pe ) goto _test_eof2; - st_case_2: - switch( ( (*( p))) ) { - case 13: { - goto st2; - } - case 32: { - goto st2; - } - case 34: { - goto ctr2; - } - case 45: { - goto ctr2; - } - case 47: { - goto st13; - } - case 73: { - goto ctr2; - } - case 78: { - goto ctr2; - } - case 91: { - goto ctr2; - } - case 93: { - goto ctr4; - } - case 102: { - goto ctr2; - } - case 110: { - goto ctr2; - } - case 116: { - goto ctr2; - } - case 123: { - goto ctr2; - } - } - if ( ( (*( p))) > 10 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto ctr2; - } - } else if ( ( (*( p))) >= 9 ) { - goto st2; - } - { - goto st0; - } - ctr2: - { - #line 409 "parser.rl" - - VALUE v = Qnil; - char *np = JSON_parse_value(json, p, pe, &v, current_nesting); - if (np == NULL) { - {p = p - 1; } {p+= 1; cs = 3; goto _out;} - } else { - if (NIL_P(json->array_class)) { - rb_ary_push(*result, v); - } else { - rb_funcall(*result, i_leftshift, 1, v); - } - {p = (( np))-1;} - - } - } - - goto st3; - st3: - p+= 1; - if ( p == pe ) +case 2: + switch( (*p) ) { + case 13: goto st2; + case 32: goto st2; + case 34: goto tr2; + case 45: goto tr2; + case 47: goto st13; + case 73: goto tr2; + case 78: goto tr2; + case 91: goto tr2; + case 93: goto tr4; + case 102: goto tr2; + case 110: goto tr2; + case 116: goto tr2; + case 123: goto tr2; + } + if ( (*p) > 10 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2; + } else if ( (*p) >= 9 ) + goto st2; + goto st0; +tr2: +#line 409 "parser.rl" + { + VALUE v = Qnil; + char *np = JSON_parse_value(json, p, pe, &v, current_nesting); + if (np == NULL) { + p--; {p++; cs = 3; goto _out;} + } else { + if (NIL_P(json->array_class)) { + rb_ary_push(*result, v); + } else { + rb_funcall(*result, i_leftshift, 1, v); + } + {p = (( np))-1;} + } + } + goto st3; +st3: + if ( ++p == pe ) goto _test_eof3; - st_case_3: - switch( ( (*( p))) ) { - case 13: { - goto st3; - } - case 32: { - goto st3; - } - case 44: { - goto st4; - } - case 47: { - goto st9; - } - case 93: { - goto ctr4; - } - } - if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { - goto st3; - } - { - goto st0; - } - st4: - p+= 1; - if ( p == pe ) +case 3: +#line 1272 "parser.c" + switch( (*p) ) { + case 13: goto st3; + case 32: goto st3; + case 44: goto st4; + case 47: goto st9; + case 93: goto tr4; + } + if ( 9 <= (*p) && (*p) <= 10 ) + goto st3; + goto st0; +st4: + if ( ++p == pe ) goto _test_eof4; - st_case_4: - switch( ( (*( p))) ) { - case 13: { - goto st4; - } - case 32: { - goto st4; - } - case 34: { - goto ctr2; - } - case 45: { - goto ctr2; - } - case 47: { - goto st5; - } - case 73: { - goto ctr2; - } - case 78: { - goto ctr2; - } - case 91: { - goto ctr2; - } - case 102: { - goto ctr2; - } - case 110: { - goto ctr2; - } - case 116: { - goto ctr2; - } - case 123: { - goto ctr2; - } - } - if ( ( (*( p))) > 10 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto ctr2; - } - } else if ( ( (*( p))) >= 9 ) { - goto st4; - } - { - goto st0; - } - st5: - p+= 1; - if ( p == pe ) +case 4: + switch( (*p) ) { + case 13: goto st4; + case 32: goto st4; + case 34: goto tr2; + case 45: goto tr2; + case 47: goto st5; + case 73: goto tr2; + case 78: goto tr2; + case 91: goto tr2; + case 102: goto tr2; + case 110: goto tr2; + case 116: goto tr2; + case 123: goto tr2; + } + if ( (*p) > 10 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2; + } else if ( (*p) >= 9 ) + goto st4; + goto st0; +st5: + if ( ++p == pe ) goto _test_eof5; - st_case_5: - switch( ( (*( p))) ) { - case 42: { - goto st6; - } - case 47: { - goto st8; - } - } - { - goto st0; - } - st6: - p+= 1; - if ( p == pe ) +case 5: + switch( (*p) ) { + case 42: goto st6; + case 47: goto st8; + } + goto st0; +st6: + if ( ++p == pe ) goto _test_eof6; - st_case_6: - if ( ( (*( p))) == 42 ) { - goto st7; - } - { - goto st6; - } - st7: - p+= 1; - if ( p == pe ) +case 6: + if ( (*p) == 42 ) + goto st7; + goto st6; +st7: + if ( ++p == pe ) goto _test_eof7; - st_case_7: - switch( ( (*( p))) ) { - case 42: { - goto st7; - } - case 47: { - goto st4; - } - } - { - goto st6; - } - st8: - p+= 1; - if ( p == pe ) +case 7: + switch( (*p) ) { + case 42: goto st7; + case 47: goto st4; + } + goto st6; +st8: + if ( ++p == pe ) goto _test_eof8; - st_case_8: - if ( ( (*( p))) == 10 ) { - goto st4; - } - { - goto st8; - } - st9: - p+= 1; - if ( p == pe ) +case 8: + if ( (*p) == 10 ) + goto st4; + goto st8; +st9: + if ( ++p == pe ) goto _test_eof9; - st_case_9: - switch( ( (*( p))) ) { - case 42: { - goto st10; - } - case 47: { - goto st12; - } - } - { - goto st0; - } - st10: - p+= 1; - if ( p == pe ) +case 9: + switch( (*p) ) { + case 42: goto st10; + case 47: goto st12; + } + goto st0; +st10: + if ( ++p == pe ) goto _test_eof10; - st_case_10: - if ( ( (*( p))) == 42 ) { - goto st11; - } - { - goto st10; - } - st11: - p+= 1; - if ( p == pe ) +case 10: + if ( (*p) == 42 ) + goto st11; + goto st10; +st11: + if ( ++p == pe ) goto _test_eof11; - st_case_11: - switch( ( (*( p))) ) { - case 42: { - goto st11; - } - case 47: { - goto st3; - } - } - { - goto st10; - } - st12: - p+= 1; - if ( p == pe ) +case 11: + switch( (*p) ) { + case 42: goto st11; + case 47: goto st3; + } + goto st10; +st12: + if ( ++p == pe ) goto _test_eof12; - st_case_12: - if ( ( (*( p))) == 10 ) { - goto st3; - } - { - goto st12; - } - ctr4: - { - #line 424 "parser.rl" - {p = p - 1; } {p+= 1; cs = 17; goto _out;} } - - goto st17; - st17: - p+= 1; - if ( p == pe ) +case 12: + if ( (*p) == 10 ) + goto st3; + goto st12; +tr4: +#line 424 "parser.rl" + { p--; {p++; cs = 17; goto _out;} } + goto st17; +st17: + if ( ++p == pe ) goto _test_eof17; - st_case_17: - { - goto st0; - } - st13: - p+= 1; - if ( p == pe ) +case 17: +#line 1379 "parser.c" + goto st0; +st13: + if ( ++p == pe ) goto _test_eof13; - st_case_13: - switch( ( (*( p))) ) { - case 42: { - goto st14; - } - case 47: { - goto st16; - } - } - { - goto st0; - } - st14: - p+= 1; - if ( p == pe ) +case 13: + switch( (*p) ) { + case 42: goto st14; + case 47: goto st16; + } + goto st0; +st14: + if ( ++p == pe ) goto _test_eof14; - st_case_14: - if ( ( (*( p))) == 42 ) { - goto st15; - } - { - goto st14; - } - st15: - p+= 1; - if ( p == pe ) +case 14: + if ( (*p) == 42 ) + goto st15; + goto st14; +st15: + if ( ++p == pe ) goto _test_eof15; - st_case_15: - switch( ( (*( p))) ) { - case 42: { - goto st15; - } - case 47: { - goto st2; - } - } - { - goto st14; - } - st16: - p+= 1; - if ( p == pe ) +case 15: + switch( (*p) ) { + case 42: goto st15; + case 47: goto st2; + } + goto st14; +st16: + if ( ++p == pe ) goto _test_eof16; - st_case_16: - if ( ( (*( p))) == 10 ) { - goto st2; - } - { - goto st16; - } - st_out: - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof11: cs = 11; goto _test_eof; - _test_eof12: cs = 12; goto _test_eof; - _test_eof17: cs = 17; goto _test_eof; - _test_eof13: cs = 13; goto _test_eof; - _test_eof14: cs = 14; goto _test_eof; - _test_eof15: cs = 15; goto _test_eof; - _test_eof16: cs = 16; goto _test_eof; - - _test_eof: {} - _out: {} - } - - #line 446 "parser.rl" - - - if(cs >= JSON_array_first_final) { - return p + 1; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); - return NULL; +case 16: + if ( (*p) == 10 ) + goto st2; + goto st16; } + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; + _test_eof11: cs = 11; goto _test_eof; + _test_eof12: cs = 12; goto _test_eof; + _test_eof17: cs = 17; goto _test_eof; + _test_eof13: cs = 13; goto _test_eof; + _test_eof14: cs = 14; goto _test_eof; + _test_eof15: cs = 15; goto _test_eof; + _test_eof16: cs = 16; goto _test_eof; + + _test_eof: {} + _out: {} + } + +#line 446 "parser.rl" + + if(cs >= JSON_array_first_final) { + return p + 1; + } else { + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); + return NULL; + } } static const size_t MAX_STACK_BUFFER_SIZE = 128; static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int symbolize) { - VALUE result = Qnil; - size_t bufferSize = stringEnd - string; - char *p = string, *pe = string, *unescape, *bufferStart, *buffer; - int unescape_len; - char buf[4]; + VALUE result = Qnil; + size_t bufferSize = stringEnd - string; + char *p = string, *pe = string, *unescape, *bufferStart, *buffer; + int unescape_len; + char buf[4]; - if (bufferSize > MAX_STACK_BUFFER_SIZE) { + if (bufferSize > MAX_STACK_BUFFER_SIZE) { # ifdef HAVE_RB_ENC_INTERNED_STR - bufferStart = buffer = ALLOC_N(char, bufferSize ? bufferSize : 1); + bufferStart = buffer = ALLOC_N(char, bufferSize ? bufferSize : 1); # else - bufferStart = buffer = ALLOC_N(char, bufferSize); + bufferStart = buffer = ALLOC_N(char, bufferSize); # endif - } else { + } else { # ifdef HAVE_RB_ENC_INTERNED_STR - bufferStart = buffer = ALLOCA_N(char, bufferSize ? bufferSize : 1); + bufferStart = buffer = ALLOCA_N(char, bufferSize ? bufferSize : 1); # else - bufferStart = buffer = ALLOCA_N(char, bufferSize); + bufferStart = buffer = ALLOCA_N(char, bufferSize); # endif - } + } + + while (pe < stringEnd) { + if (*pe == '\\') { + unescape = (char *) "?"; + unescape_len = 1; + if (pe > p) { + MEMCPY(buffer, p, char, pe - p); + buffer += pe - p; + } + switch (*++pe) { + case 'n': + unescape = (char *) "\n"; + break; + case 'r': + unescape = (char *) "\r"; + break; + case 't': + unescape = (char *) "\t"; + break; + case '"': + unescape = (char *) "\""; + break; + case '\\': + unescape = (char *) "\\"; + break; + case 'b': + unescape = (char *) "\b"; + break; + case 'f': + unescape = (char *) "\f"; + break; + case 'u': + if (pe > stringEnd - 4) { + if (bufferSize > MAX_STACK_BUFFER_SIZE) { + free(bufferStart); + } + rb_enc_raise( + EXC_ENCODING eParserError, + "incomplete unicode character escape sequence at '%s'", p + ); + } else { + UTF32 ch = unescape_unicode((unsigned char *) ++pe); + pe += 3; + if (UNI_SUR_HIGH_START == (ch & 0xFC00)) { + pe++; + if (pe > stringEnd - 6) { + if (bufferSize > MAX_STACK_BUFFER_SIZE) { + free(bufferStart); + } + rb_enc_raise( + EXC_ENCODING eParserError, + "incomplete surrogate pair at '%s'", p + ); + } + if (pe[0] == '\\' && pe[1] == 'u') { + UTF32 sur = unescape_unicode((unsigned char *) pe + 2); + ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16) + | (sur & 0x3FF)); + pe += 5; + } else { + unescape = (char *) "?"; + break; + } + } + unescape_len = convert_UTF32_to_UTF8(buf, ch); + unescape = buf; + } + break; + default: + p = pe; + continue; + } + MEMCPY(buffer, unescape, char, unescape_len); + buffer += unescape_len; + p = ++pe; + } else { + pe++; + } + } + + if (pe > p) { + MEMCPY(buffer, p, char, pe - p); + buffer += pe - p; + } - while (pe < stringEnd) { - if (*pe == '\\') { - unescape = (char *) "?"; - unescape_len = 1; - if (pe > p) { - MEMCPY(buffer, p, char, pe - p); - buffer += pe - p; - } - switch (*++pe) { - case 'n': - unescape = (char *) "\n"; - break; - case 'r': - unescape = (char *) "\r"; - break; - case 't': - unescape = (char *) "\t"; - break; - case '"': - unescape = (char *) "\""; - break; - case '\\': - unescape = (char *) "\\"; - break; - case 'b': - unescape = (char *) "\b"; - break; - case 'f': - unescape = (char *) "\f"; - break; - case 'u': - if (pe > stringEnd - 4) { - if (bufferSize > MAX_STACK_BUFFER_SIZE) { - free(bufferStart); - } - rb_enc_raise( - EXC_ENCODING eParserError, - "incomplete unicode character escape sequence at '%s'", p - ); - } else { - UTF32 ch = unescape_unicode((unsigned char *) ++pe); - pe += 3; - if (UNI_SUR_HIGH_START == (ch & 0xFC00)) { - pe++; - if (pe > stringEnd - 6) { - if (bufferSize > MAX_STACK_BUFFER_SIZE) { - free(bufferStart); - } - rb_enc_raise( - EXC_ENCODING eParserError, - "incomplete surrogate pair at '%s'", p - ); - } - if (pe[0] == '\\' && pe[1] == 'u') { - UTF32 sur = unescape_unicode((unsigned char *) pe + 2); - ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16) - | (sur & 0x3FF)); - pe += 5; - } else { - unescape = (char *) "?"; - break; - } - } - unescape_len = convert_UTF32_to_UTF8(buf, ch); - unescape = buf; - } - break; - default: - p = pe; - continue; - } - MEMCPY(buffer, unescape, char, unescape_len); - buffer += unescape_len; - p = ++pe; - } else { - pe++; - } - } - - if (pe > p) { - MEMCPY(buffer, p, char, pe - p); - buffer += pe - p; - } - - # ifdef HAVE_RB_ENC_INTERNED_STR - if (intern) { - result = rb_enc_interned_str(bufferStart, (long)(buffer - bufferStart), rb_utf8_encoding()); - } else { - result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); - } - if (bufferSize > MAX_STACK_BUFFER_SIZE) { - free(bufferStart); - } - # else - result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); - - if (bufferSize > MAX_STACK_BUFFER_SIZE) { - free(bufferStart); - } - - if (intern) { - # if STR_UMINUS_DEDUPE_FROZEN - // Starting from MRI 2.8 it is preferable to freeze the string - // before deduplication so that it can be interned directly - // otherwise it would be duplicated first which is wasteful. - result = rb_funcall(rb_str_freeze(result), i_uminus, 0); - # elif STR_UMINUS_DEDUPE - // MRI 2.5 and older do not deduplicate strings that are already - // frozen. - result = rb_funcall(result, i_uminus, 0); - # else - result = rb_str_freeze(result); - # endif - } - # endif - - if (symbolize) { - result = rb_str_intern(result); - } - - return result; +# ifdef HAVE_RB_ENC_INTERNED_STR + if (intern) { + result = rb_enc_interned_str(bufferStart, (long)(buffer - bufferStart), rb_utf8_encoding()); + } else { + result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); + } + if (bufferSize > MAX_STACK_BUFFER_SIZE) { + free(bufferStart); + } +# else + result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); + + if (bufferSize > MAX_STACK_BUFFER_SIZE) { + free(bufferStart); + } + + if (intern) { + # if STR_UMINUS_DEDUPE_FROZEN + // Starting from MRI 2.8 it is preferable to freeze the string + // before deduplication so that it can be interned directly + // otherwise it would be duplicated first which is wasteful. + result = rb_funcall(rb_str_freeze(result), i_uminus, 0); + # elif STR_UMINUS_DEDUPE + // MRI 2.5 and older do not deduplicate strings that are already + // frozen. + result = rb_funcall(result, i_uminus, 0); + # else + result = rb_str_freeze(result); + # endif + } +# endif + + if (symbolize) { + result = rb_str_intern(result); + } + + return result; } +#line 1592 "parser.c" enum {JSON_string_start = 1}; enum {JSON_string_first_final = 8}; enum {JSON_string_error = 0}; enum {JSON_string_en_main = 1}; -static const char MAYBE_UNUSED(_JSON_string_nfa_targs)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_string_nfa_offsets)[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_string_nfa_push_actions)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_string_nfa_pop_trans)[] = { - 0, 0 -}; - -#line 612 "parser.rl" +#line 620 "parser.rl" static int match_i(VALUE regexp, VALUE klass, VALUE memo) { - if (regexp == Qundef) return ST_STOP; - if (RTEST(rb_funcall(klass, i_json_creatable_p, 0)) && - RTEST(rb_funcall(regexp, i_match, 1, rb_ary_entry(memo, 0)))) { - rb_ary_push(memo, klass); - return ST_STOP; - } - return ST_CONTINUE; + if (regexp == Qundef) return ST_STOP; + if (RTEST(rb_funcall(klass, i_json_creatable_p, 0)) && + RTEST(rb_funcall(regexp, i_match, 1, rb_ary_entry(memo, 0)))) { + rb_ary_push(memo, klass); + return ST_STOP; + } + return ST_CONTINUE; } static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result) { - int cs = EVIL; - VALUE match_string; + int cs = EVIL; + VALUE match_string; +#line 1621 "parser.c" { - cs = (int)JSON_string_start; + cs = JSON_string_start; } - #line 632 "parser.rl" - - json->memo = p; +#line 640 "parser.rl" + json->memo = p; +#line 1629 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { - case 1: - goto st_case_1; - case 0: - goto st_case_0; - case 2: - goto st_case_2; - case 8: - goto st_case_8; - case 3: - goto st_case_3; - case 4: - goto st_case_4; - case 5: - goto st_case_5; - case 6: - goto st_case_6; - case 7: - goto st_case_7; - } - goto st_out; - st_case_1: - if ( ( (*( p))) == 34 ) { - goto st2; - } - { - goto st0; - } - st_case_0: - st0: - cs = 0; - goto _out; - st2: - p+= 1; - if ( p == pe ) + switch ( cs ) + { +case 1: + if ( (*p) == 34 ) + goto st2; + goto st0; +st0: +cs = 0; + goto _out; +st2: + if ( ++p == pe ) goto _test_eof2; - st_case_2: - switch( ( (*( p))) ) { - case 34: { - goto ctr2; - } - case 92: { - goto st3; - } - } - if ( 0 <= (signed char)(*(p)) && (*(p)) <= 31 ) { - goto st0; - } - { - goto st2; - } - ctr2: - { - #line 599 "parser.rl" - - *result = json_string_unescape(json->memo + 1, p, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names); - if (NIL_P(*result)) { - {p = p - 1; } - {p+= 1; cs = 8; goto _out;} - } else { - {p = (( p + 1))-1;} - - } - } - { - #line 609 "parser.rl" - {p = p - 1; } {p+= 1; cs = 8; goto _out;} } - - goto st8; - st8: - p+= 1; - if ( p == pe ) +case 2: + switch( (*p) ) { + case 34: goto tr2; + case 92: goto st3; + } + if ( 0 <= (signed char)(*(p)) && (*(p)) <= 31 ) + goto st0; + goto st2; +tr2: +#line 607 "parser.rl" + { + *result = json_string_unescape(json->memo + 1, p, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names); + if (NIL_P(*result)) { + p--; + {p++; cs = 8; goto _out;} + } else { + {p = (( p + 1))-1;} + } + } +#line 617 "parser.rl" + { p--; {p++; cs = 8; goto _out;} } + goto st8; +st8: + if ( ++p == pe ) goto _test_eof8; - st_case_8: - { - goto st0; - } - st3: - p+= 1; - if ( p == pe ) +case 8: +#line 1671 "parser.c" + goto st0; +st3: + if ( ++p == pe ) goto _test_eof3; - st_case_3: - if ( ( (*( p))) == 117 ) { - goto st4; - } - if ( 0 <= (signed char)(*(p)) && (*(p)) <= 31 ) { - goto st0; - } - { - goto st2; - } - st4: - p+= 1; - if ( p == pe ) +case 3: + if ( (*p) == 117 ) + goto st4; + if ( 0 <= (signed char)(*(p)) && (*(p)) <= 31 ) + goto st0; + goto st2; +st4: + if ( ++p == pe ) goto _test_eof4; - st_case_4: - if ( ( (*( p))) < 65 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st5; - } - } else if ( ( (*( p))) > 70 ) { - if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) { - goto st5; - } - } else { +case 4: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto st5; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) goto st5; - } - { - goto st0; - } - st5: - p+= 1; - if ( p == pe ) + } else + goto st5; + goto st0; +st5: + if ( ++p == pe ) goto _test_eof5; - st_case_5: - if ( ( (*( p))) < 65 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st6; - } - } else if ( ( (*( p))) > 70 ) { - if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) { - goto st6; - } - } else { +case 5: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) goto st6; - } - { - goto st0; - } - st6: - p+= 1; - if ( p == pe ) + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto st6; + } else + goto st6; + goto st0; +st6: + if ( ++p == pe ) goto _test_eof6; - st_case_6: - if ( ( (*( p))) < 65 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st7; - } - } else if ( ( (*( p))) > 70 ) { - if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) { - goto st7; - } - } else { +case 6: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto st7; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) goto st7; - } - { - goto st0; - } - st7: - p+= 1; - if ( p == pe ) + } else + goto st7; + goto st0; +st7: + if ( ++p == pe ) goto _test_eof7; - st_case_7: - if ( ( (*( p))) < 65 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto st2; - } - } else if ( ( (*( p))) > 70 ) { - if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) { - goto st2; - } - } else { +case 7: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) goto st2; - } - { - goto st0; - } - st_out: - _test_eof2: cs = 2; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - - _test_eof: {} - _out: {} - } - - #line 634 "parser.rl" - - - if (json->create_additions && RTEST(match_string = json->match_string)) { - VALUE klass; - VALUE memo = rb_ary_new2(2); - rb_ary_push(memo, *result); - rb_hash_foreach(match_string, match_i, memo); - klass = rb_ary_entry(memo, 1); - if (RTEST(klass)) { - *result = rb_funcall(klass, i_json_create, 1, *result); - } - } - - if (cs >= JSON_string_first_final) { - return p + 1; - } else { - return NULL; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto st2; + } else + goto st2; + goto st0; + } + _test_eof2: cs = 2; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + + _test_eof: {} + _out: {} } + +#line 642 "parser.rl" + + if (json->create_additions && RTEST(match_string = json->match_string)) { + VALUE klass; + VALUE memo = rb_ary_new2(2); + rb_ary_push(memo, *result); + rb_hash_foreach(match_string, match_i, memo); + klass = rb_ary_entry(memo, 1); + if (RTEST(klass)) { + *result = rb_funcall(klass, i_json_create, 1, *result); + } + } + + if (cs >= JSON_string_first_final) { + return p + 1; + } else { + return NULL; + } } /* -* Document-class: JSON::Ext::Parser -* -* This is the JSON parser implemented as a C extension. It can be configured -* to be used by setting -* -* JSON.parser = JSON::Ext::Parser -* -* with the method parser= in JSON. -* -*/ + * Document-class: JSON::Ext::Parser + * + * This is the JSON parser implemented as a C extension. It can be configured + * to be used by setting + * + * JSON.parser = JSON::Ext::Parser + * + * with the method parser= in JSON. + * + */ static VALUE convert_encoding(VALUE source) { - #ifdef HAVE_RUBY_ENCODING_H - rb_encoding *enc = rb_enc_get(source); - if (enc == rb_ascii8bit_encoding()) { - if (OBJ_FROZEN(source)) { - source = rb_str_dup(source); - } - FORCE_UTF8(source); - } else { - source = rb_str_conv_enc(source, rb_enc_get(source), rb_utf8_encoding()); - } - #endif - return source; +#ifdef HAVE_RUBY_ENCODING_H + rb_encoding *enc = rb_enc_get(source); + if (enc == rb_ascii8bit_encoding()) { + if (OBJ_FROZEN(source)) { + source = rb_str_dup(source); + } + FORCE_UTF8(source); + } else { + source = rb_str_conv_enc(source, rb_enc_get(source), rb_utf8_encoding()); + } +#endif + return source; } /* -* call-seq: new(source, opts => {}) -* -* Creates a new JSON::Ext::Parser instance for the string _source_. -* -* Creates a new JSON::Ext::Parser instance for the string _source_. -* -* It will be configured by the _opts_ hash. _opts_ can have the following -* keys: -* -* _opts_ can have the following keys: -* * *max_nesting*: The maximum depth of nesting allowed in the parsed data -* structures. Disable depth checking with :max_nesting => false|nil|0, it -* defaults to 100. -* * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in -* defiance of RFC 4627 to be parsed by the Parser. This option defaults to -* false. -* * *symbolize_names*: If set to true, returns symbols for the names -* (keys) in a JSON object. Otherwise strings are returned, which is -* also the default. It's not possible to use this option in -* conjunction with the *create_additions* option. -* * *create_additions*: If set to false, the Parser doesn't create -* additions even if a matching class and create_id was found. This option -* defaults to false. -* * *object_class*: Defaults to Hash -* * *array_class*: Defaults to Array -*/ + * call-seq: new(source, opts => {}) + * + * Creates a new JSON::Ext::Parser instance for the string _source_. + * + * Creates a new JSON::Ext::Parser instance for the string _source_. + * + * It will be configured by the _opts_ hash. _opts_ can have the following + * keys: + * + * _opts_ can have the following keys: + * * *max_nesting*: The maximum depth of nesting allowed in the parsed data + * structures. Disable depth checking with :max_nesting => false|nil|0, it + * defaults to 100. + * * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in + * defiance of RFC 4627 to be parsed by the Parser. This option defaults to + * false. + * * *symbolize_names*: If set to true, returns symbols for the names + * (keys) in a JSON object. Otherwise strings are returned, which is + * also the default. It's not possible to use this option in + * conjunction with the *create_additions* option. + * * *create_additions*: If set to false, the Parser doesn't create + * additions even if a matching class and create_id was found. This option + * defaults to false. + * * *object_class*: Defaults to Hash + * * *array_class*: Defaults to Array + */ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) { - VALUE source, opts; - GET_PARSER_INIT; - - if (json->Vsource) { - rb_raise(rb_eTypeError, "already initialized instance"); - } - #ifdef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH - rb_scan_args(argc, argv, "1:", &source, &opts); - #else - rb_scan_args(argc, argv, "11", &source, &opts); - #endif - if (!NIL_P(opts)) { - #ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH - opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash"); - if (NIL_P(opts)) { - rb_raise(rb_eArgError, "opts needs to be like a hash"); - } else { - #endif - VALUE tmp = ID2SYM(i_max_nesting); - if (option_given_p(opts, tmp)) { - VALUE max_nesting = rb_hash_aref(opts, tmp); - if (RTEST(max_nesting)) { - Check_Type(max_nesting, T_FIXNUM); - json->max_nesting = FIX2INT(max_nesting); - } else { - json->max_nesting = 0; - } - } else { - json->max_nesting = 100; - } - tmp = ID2SYM(i_allow_nan); - if (option_given_p(opts, tmp)) { - json->allow_nan = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; - } else { - json->allow_nan = 0; - } - tmp = ID2SYM(i_symbolize_names); - if (option_given_p(opts, tmp)) { - json->symbolize_names = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; - } else { - json->symbolize_names = 0; - } - tmp = ID2SYM(i_freeze); - if (option_given_p(opts, tmp)) { - json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; - } else { - json->freeze = 0; - } - tmp = ID2SYM(i_create_additions); - if (option_given_p(opts, tmp)) { - json->create_additions = RTEST(rb_hash_aref(opts, tmp)); - } else { - json->create_additions = 0; - } - if (json->symbolize_names && json->create_additions) { - rb_raise(rb_eArgError, - "options :symbolize_names and :create_additions cannot be " - " used in conjunction"); - } - tmp = ID2SYM(i_create_id); - if (option_given_p(opts, tmp)) { - json->create_id = rb_hash_aref(opts, tmp); - } else { - json->create_id = rb_funcall(mJSON, i_create_id, 0); - } - tmp = ID2SYM(i_object_class); - if (option_given_p(opts, tmp)) { - json->object_class = rb_hash_aref(opts, tmp); - } else { - json->object_class = Qnil; - } - tmp = ID2SYM(i_array_class); - if (option_given_p(opts, tmp)) { - json->array_class = rb_hash_aref(opts, tmp); - } else { - json->array_class = Qnil; - } - tmp = ID2SYM(i_decimal_class); - if (option_given_p(opts, tmp)) { - json->decimal_class = rb_hash_aref(opts, tmp); - } else { - json->decimal_class = Qnil; - } - tmp = ID2SYM(i_match_string); - if (option_given_p(opts, tmp)) { - VALUE match_string = rb_hash_aref(opts, tmp); - json->match_string = RTEST(match_string) ? match_string : Qnil; - } else { - json->match_string = Qnil; - } - #ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH - } - #endif - } else { - json->max_nesting = 100; - json->allow_nan = 0; - json->create_additions = 0; - json->create_id = rb_funcall(mJSON, i_create_id, 0); - json->object_class = Qnil; - json->array_class = Qnil; - json->decimal_class = Qnil; - } - source = convert_encoding(StringValue(source)); - StringValue(source); - json->len = RSTRING_LEN(source); - json->source = RSTRING_PTR(source);; - json->Vsource = source; - return self; + VALUE source, opts; + GET_PARSER_INIT; + + if (json->Vsource) { + rb_raise(rb_eTypeError, "already initialized instance"); + } +#ifdef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH + rb_scan_args(argc, argv, "1:", &source, &opts); +#else + rb_scan_args(argc, argv, "11", &source, &opts); +#endif + if (!NIL_P(opts)) { +#ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH + opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash"); + if (NIL_P(opts)) { + rb_raise(rb_eArgError, "opts needs to be like a hash"); + } else { +#endif + VALUE tmp = ID2SYM(i_max_nesting); + if (option_given_p(opts, tmp)) { + VALUE max_nesting = rb_hash_aref(opts, tmp); + if (RTEST(max_nesting)) { + Check_Type(max_nesting, T_FIXNUM); + json->max_nesting = FIX2INT(max_nesting); + } else { + json->max_nesting = 0; + } + } else { + json->max_nesting = 100; + } + tmp = ID2SYM(i_allow_nan); + if (option_given_p(opts, tmp)) { + json->allow_nan = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; + } else { + json->allow_nan = 0; + } + tmp = ID2SYM(i_symbolize_names); + if (option_given_p(opts, tmp)) { + json->symbolize_names = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; + } else { + json->symbolize_names = 0; + } + tmp = ID2SYM(i_freeze); + if (option_given_p(opts, tmp)) { + json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; + } else { + json->freeze = 0; + } + tmp = ID2SYM(i_create_additions); + if (option_given_p(opts, tmp)) { + json->create_additions = RTEST(rb_hash_aref(opts, tmp)); + } else { + json->create_additions = 0; + } + if (json->symbolize_names && json->create_additions) { + rb_raise(rb_eArgError, + "options :symbolize_names and :create_additions cannot be " + " used in conjunction"); + } + tmp = ID2SYM(i_create_id); + if (option_given_p(opts, tmp)) { + json->create_id = rb_hash_aref(opts, tmp); + } else { + json->create_id = rb_funcall(mJSON, i_create_id, 0); + } + tmp = ID2SYM(i_object_class); + if (option_given_p(opts, tmp)) { + json->object_class = rb_hash_aref(opts, tmp); + } else { + json->object_class = Qnil; + } + tmp = ID2SYM(i_array_class); + if (option_given_p(opts, tmp)) { + json->array_class = rb_hash_aref(opts, tmp); + } else { + json->array_class = Qnil; + } + tmp = ID2SYM(i_decimal_class); + if (option_given_p(opts, tmp)) { + json->decimal_class = rb_hash_aref(opts, tmp); + } else { + json->decimal_class = Qnil; + } + tmp = ID2SYM(i_match_string); + if (option_given_p(opts, tmp)) { + VALUE match_string = rb_hash_aref(opts, tmp); + json->match_string = RTEST(match_string) ? match_string : Qnil; + } else { + json->match_string = Qnil; + } +#ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH + } +#endif + } else { + json->max_nesting = 100; + json->allow_nan = 0; + json->create_additions = 0; + json->create_id = rb_funcall(mJSON, i_create_id, 0); + json->object_class = Qnil; + json->array_class = Qnil; + json->decimal_class = Qnil; + } + source = convert_encoding(StringValue(source)); + StringValue(source); + json->len = RSTRING_LEN(source); + json->source = RSTRING_PTR(source);; + json->Vsource = source; + return self; } +#line 1935 "parser.c" enum {JSON_start = 1}; enum {JSON_first_final = 10}; enum {JSON_error = 0}; enum {JSON_en_main = 1}; -static const char MAYBE_UNUSED(_JSON_nfa_targs)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_nfa_offsets)[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 -}; -static const char MAYBE_UNUSED(_JSON_nfa_push_actions)[] = { - 0, 0 -}; - -static const char MAYBE_UNUSED(_JSON_nfa_pop_trans)[] = { - 0, 0 -}; - - -#line 835 "parser.rl" +#line 843 "parser.rl" /* -* call-seq: parse() -* -* Parses the current JSON text _source_ and returns the complete data -* structure as a result. -* It raises JSON::ParseError if fail to parse. -*/ + * call-seq: parse() + * + * Parses the current JSON text _source_ and returns the complete data + * structure as a result. + * It raises JSON::ParseError if fail to parse. + */ static VALUE cParser_parse(VALUE self) { - char *p, *pe; - int cs = EVIL; - VALUE result = Qnil; - GET_PARSER; + char *p, *pe; + int cs = EVIL; + VALUE result = Qnil; + GET_PARSER; +#line 1961 "parser.c" { - cs = (int)JSON_start; + cs = JSON_start; } - #line 851 "parser.rl" - - p = json->source; - pe = p + json->len; +#line 860 "parser.rl" + p = json->source; + pe = p + json->len; +#line 1970 "parser.c" { - if ( p == pe ) + if ( p == pe ) goto _test_eof; - switch ( cs ) - { - case 1: - goto st_case_1; - case 0: - goto st_case_0; - case 10: - goto st_case_10; - case 2: - goto st_case_2; - case 3: - goto st_case_3; - case 4: - goto st_case_4; - case 5: - goto st_case_5; - case 6: - goto st_case_6; - case 7: - goto st_case_7; - case 8: - goto st_case_8; - case 9: - goto st_case_9; - } - goto st_out; - st1: - p+= 1; - if ( p == pe ) + switch ( cs ) + { +st1: + if ( ++p == pe ) goto _test_eof1; - st_case_1: - switch( ( (*( p))) ) { - case 13: { - goto st1; - } - case 32: { - goto st1; - } - case 34: { - goto ctr2; - } - case 45: { - goto ctr2; - } - case 47: { - goto st6; - } - case 73: { - goto ctr2; - } - case 78: { - goto ctr2; - } - case 91: { - goto ctr2; - } - case 102: { - goto ctr2; - } - case 110: { - goto ctr2; - } - case 116: { - goto ctr2; - } - case 123: { - goto ctr2; - } - } - if ( ( (*( p))) > 10 ) { - if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) { - goto ctr2; - } - } else if ( ( (*( p))) >= 9 ) { - goto st1; - } - { - goto st0; - } - st_case_0: - st0: - cs = 0; - goto _out; - ctr2: - { - #line 827 "parser.rl" - - char *np = JSON_parse_value(json, p, pe, &result, 0); - if (np == NULL) { {p = p - 1; } {p+= 1; cs = 10; goto _out;} } else {p = (( np))-1;} - - } - - goto st10; - st10: - p+= 1; - if ( p == pe ) +case 1: + switch( (*p) ) { + case 13: goto st1; + case 32: goto st1; + case 34: goto tr2; + case 45: goto tr2; + case 47: goto st6; + case 73: goto tr2; + case 78: goto tr2; + case 91: goto tr2; + case 102: goto tr2; + case 110: goto tr2; + case 116: goto tr2; + case 123: goto tr2; + } + if ( (*p) > 10 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2; + } else if ( (*p) >= 9 ) + goto st1; + goto st0; +st0: +cs = 0; + goto _out; +tr2: +#line 835 "parser.rl" + { + char *np = JSON_parse_value(json, p, pe, &result, 0); + if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;} + } + goto st10; +st10: + if ( ++p == pe ) goto _test_eof10; - st_case_10: - switch( ( (*( p))) ) { - case 13: { - goto st10; - } - case 32: { - goto st10; - } - case 47: { - goto st2; - } - } - if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) { - goto st10; - } - { - goto st0; - } - st2: - p+= 1; - if ( p == pe ) +case 10: +#line 2014 "parser.c" + switch( (*p) ) { + case 13: goto st10; + case 32: goto st10; + case 47: goto st2; + } + if ( 9 <= (*p) && (*p) <= 10 ) + goto st10; + goto st0; +st2: + if ( ++p == pe ) goto _test_eof2; - st_case_2: - switch( ( (*( p))) ) { - case 42: { - goto st3; - } - case 47: { - goto st5; - } - } - { - goto st0; - } - st3: - p+= 1; - if ( p == pe ) +case 2: + switch( (*p) ) { + case 42: goto st3; + case 47: goto st5; + } + goto st0; +st3: + if ( ++p == pe ) goto _test_eof3; - st_case_3: - if ( ( (*( p))) == 42 ) { - goto st4; - } - { - goto st3; - } - st4: - p+= 1; - if ( p == pe ) +case 3: + if ( (*p) == 42 ) + goto st4; + goto st3; +st4: + if ( ++p == pe ) goto _test_eof4; - st_case_4: - switch( ( (*( p))) ) { - case 42: { - goto st4; - } - case 47: { - goto st10; - } - } - { - goto st3; - } - st5: - p+= 1; - if ( p == pe ) +case 4: + switch( (*p) ) { + case 42: goto st4; + case 47: goto st10; + } + goto st3; +st5: + if ( ++p == pe ) goto _test_eof5; - st_case_5: - if ( ( (*( p))) == 10 ) { - goto st10; - } - { - goto st5; - } - st6: - p+= 1; - if ( p == pe ) +case 5: + if ( (*p) == 10 ) + goto st10; + goto st5; +st6: + if ( ++p == pe ) goto _test_eof6; - st_case_6: - switch( ( (*( p))) ) { - case 42: { - goto st7; - } - case 47: { - goto st9; - } - } - { - goto st0; - } - st7: - p+= 1; - if ( p == pe ) +case 6: + switch( (*p) ) { + case 42: goto st7; + case 47: goto st9; + } + goto st0; +st7: + if ( ++p == pe ) goto _test_eof7; - st_case_7: - if ( ( (*( p))) == 42 ) { - goto st8; - } - { - goto st7; - } - st8: - p+= 1; - if ( p == pe ) +case 7: + if ( (*p) == 42 ) + goto st8; + goto st7; +st8: + if ( ++p == pe ) goto _test_eof8; - st_case_8: - switch( ( (*( p))) ) { - case 42: { - goto st8; - } - case 47: { - goto st1; - } - } - { - goto st7; - } - st9: - p+= 1; - if ( p == pe ) +case 8: + switch( (*p) ) { + case 42: goto st8; + case 47: goto st1; + } + goto st7; +st9: + if ( ++p == pe ) goto _test_eof9; - st_case_9: - if ( ( (*( p))) == 10 ) { - goto st1; - } - { - goto st9; - } - st_out: - _test_eof1: cs = 1; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - - _test_eof: {} - _out: {} - } - - #line 854 "parser.rl" - - - if (cs >= JSON_first_final && p == pe) { - return result; - } else { - rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); - return Qnil; +case 9: + if ( (*p) == 10 ) + goto st1; + goto st9; } + _test_eof1: cs = 1; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; + + _test_eof: {} + _out: {} + } + +#line 863 "parser.rl" + + if (cs >= JSON_first_final && p == pe) { + return result; + } else { + rb_enc_raise(EXC_ENCODING eParserError, "unexpected token at '%s'", p); + return Qnil; + } } static void JSON_mark(void *ptr) { - JSON_Parser *json = ptr; - rb_gc_mark_maybe(json->Vsource); - rb_gc_mark_maybe(json->create_id); - rb_gc_mark_maybe(json->object_class); - rb_gc_mark_maybe(json->array_class); - rb_gc_mark_maybe(json->decimal_class); - rb_gc_mark_maybe(json->match_string); + JSON_Parser *json = ptr; + rb_gc_mark_maybe(json->Vsource); + rb_gc_mark_maybe(json->create_id); + rb_gc_mark_maybe(json->object_class); + rb_gc_mark_maybe(json->array_class); + rb_gc_mark_maybe(json->decimal_class); + rb_gc_mark_maybe(json->match_string); } static void JSON_free(void *ptr) { - JSON_Parser *json = ptr; - fbuffer_free(json->fbuffer); - ruby_xfree(json); + JSON_Parser *json = ptr; + fbuffer_free(json->fbuffer); + ruby_xfree(json); } static size_t JSON_memsize(const void *ptr) { - const JSON_Parser *json = ptr; - return sizeof(*json) + FBUFFER_CAPA(json->fbuffer); + const JSON_Parser *json = ptr; + return sizeof(*json) + FBUFFER_CAPA(json->fbuffer); } #ifdef NEW_TYPEDDATA_WRAPPER static const rb_data_type_t JSON_Parser_type = { - "JSON/Parser", - {JSON_mark, JSON_free, JSON_memsize,}, - #ifdef RUBY_TYPED_FREE_IMMEDIATELY - 0, 0, - RUBY_TYPED_FREE_IMMEDIATELY, - #endif + "JSON/Parser", + {JSON_mark, JSON_free, JSON_memsize,}, +#ifdef RUBY_TYPED_FREE_IMMEDIATELY + 0, 0, + RUBY_TYPED_FREE_IMMEDIATELY, +#endif }; #endif static VALUE cJSON_parser_s_allocate(VALUE klass) { - JSON_Parser *json; - VALUE obj = TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json); - json->fbuffer = fbuffer_alloc(0); - return obj; + JSON_Parser *json; + VALUE obj = TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json); + json->fbuffer = fbuffer_alloc(0); + return obj; } /* -* call-seq: source() -* -* Returns a copy of the current _source_ string, that was used to construct -* this Parser. -*/ + * call-seq: source() + * + * Returns a copy of the current _source_ string, that was used to construct + * this Parser. + */ static VALUE cParser_source(VALUE self) { - GET_PARSER; - return rb_str_dup(json->Vsource); + GET_PARSER; + return rb_str_dup(json->Vsource); } void Init_parser(void) { - #ifdef HAVE_RB_EXT_RACTOR_SAFE - rb_ext_ractor_safe(true); - #endif - - #undef rb_intern - rb_require("json/common"); - mJSON = rb_define_module("JSON"); - mExt = rb_define_module_under(mJSON, "Ext"); - cParser = rb_define_class_under(mExt, "Parser", rb_cObject); - eParserError = rb_path2class("JSON::ParserError"); - eNestingError = rb_path2class("JSON::NestingError"); - rb_gc_register_mark_object(eParserError); - rb_gc_register_mark_object(eNestingError); - rb_define_alloc_func(cParser, cJSON_parser_s_allocate); - rb_define_method(cParser, "initialize", cParser_initialize, -1); - rb_define_method(cParser, "parse", cParser_parse, 0); - rb_define_method(cParser, "source", cParser_source, 0); - - CNaN = rb_const_get(mJSON, rb_intern("NaN")); - rb_gc_register_mark_object(CNaN); - - CInfinity = rb_const_get(mJSON, rb_intern("Infinity")); - rb_gc_register_mark_object(CInfinity); - - CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity")); - rb_gc_register_mark_object(CMinusInfinity); - - i_json_creatable_p = rb_intern("json_creatable?"); - i_json_create = rb_intern("json_create"); - i_create_id = rb_intern("create_id"); - i_create_additions = rb_intern("create_additions"); - i_chr = rb_intern("chr"); - i_max_nesting = rb_intern("max_nesting"); - i_allow_nan = rb_intern("allow_nan"); - i_symbolize_names = rb_intern("symbolize_names"); - i_object_class = rb_intern("object_class"); - i_array_class = rb_intern("array_class"); - i_decimal_class = rb_intern("decimal_class"); - i_match = rb_intern("match"); - i_match_string = rb_intern("match_string"); - i_key_p = rb_intern("key?"); - i_deep_const_get = rb_intern("deep_const_get"); - i_aset = rb_intern("[]="); - i_aref = rb_intern("[]"); - i_leftshift = rb_intern("<<"); - i_new = rb_intern("new"); - i_try_convert = rb_intern("try_convert"); - i_freeze = rb_intern("freeze"); - i_uminus = rb_intern("-@"); +#ifdef HAVE_RB_EXT_RACTOR_SAFE + rb_ext_ractor_safe(true); +#endif + +#undef rb_intern + rb_require("json/common"); + mJSON = rb_define_module("JSON"); + mExt = rb_define_module_under(mJSON, "Ext"); + cParser = rb_define_class_under(mExt, "Parser", rb_cObject); + eParserError = rb_path2class("JSON::ParserError"); + eNestingError = rb_path2class("JSON::NestingError"); + rb_gc_register_mark_object(eParserError); + rb_gc_register_mark_object(eNestingError); + rb_define_alloc_func(cParser, cJSON_parser_s_allocate); + rb_define_method(cParser, "initialize", cParser_initialize, -1); + rb_define_method(cParser, "parse", cParser_parse, 0); + rb_define_method(cParser, "source", cParser_source, 0); + + CNaN = rb_const_get(mJSON, rb_intern("NaN")); + rb_gc_register_mark_object(CNaN); + + CInfinity = rb_const_get(mJSON, rb_intern("Infinity")); + rb_gc_register_mark_object(CInfinity); + + CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity")); + rb_gc_register_mark_object(CMinusInfinity); + + i_json_creatable_p = rb_intern("json_creatable?"); + i_json_create = rb_intern("json_create"); + i_create_id = rb_intern("create_id"); + i_create_additions = rb_intern("create_additions"); + i_chr = rb_intern("chr"); + i_max_nesting = rb_intern("max_nesting"); + i_allow_nan = rb_intern("allow_nan"); + i_symbolize_names = rb_intern("symbolize_names"); + i_object_class = rb_intern("object_class"); + i_array_class = rb_intern("array_class"); + i_decimal_class = rb_intern("decimal_class"); + i_match = rb_intern("match"); + i_match_string = rb_intern("match_string"); + i_key_p = rb_intern("key?"); + i_deep_const_get = rb_intern("deep_const_get"); + i_aset = rb_intern("[]="); + i_aref = rb_intern("[]"); + i_leftshift = rb_intern("<<"); + i_new = rb_intern("new"); + i_try_convert = rb_intern("try_convert"); + i_freeze = rb_intern("freeze"); + i_uminus = rb_intern("-@"); } /* -* Local variables: -* mode: c -* c-file-style: ruby -* indent-tabs-mode: nil -* End: -*/ + * Local variables: + * mode: c + * c-file-style: ruby + * indent-tabs-mode: nil + * End: + */ diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index fc1764039..1f89da492 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -428,7 +428,11 @@ void generate(Session session, IRubyObject object, ByteList buffer) { new Handler() { @Override RubyString generateNew(Session session, IRubyObject object) { - if (object.respondsTo("to_json")) { + if (session.getState().strict()) { + throw Utils.newException(session.getContext(), + Utils.M_GENERATOR_ERROR, + object + " not allowed in JSON"); + } else if (object.respondsTo("to_json")) { IRubyObject result = object.callMethod(session.getContext(), "to_json", new IRubyObject[] {session.getState()}); if (result instanceof RubyString) return (RubyString)result; diff --git a/java/src/json/ext/GeneratorState.java b/java/src/json/ext/GeneratorState.java index a0541d674..7b607f0e2 100644 --- a/java/src/json/ext/GeneratorState.java +++ b/java/src/json/ext/GeneratorState.java @@ -88,6 +88,13 @@ public class GeneratorState extends RubyObject { */ private boolean escapeSlash = DEFAULT_ESCAPE_SLASH; static final boolean DEFAULT_ESCAPE_SLASH = false; + + /** + * If set to true types unsupported by the JSON format will + * raise a JSON::GeneratorError. + */ + private boolean strict = DEFAULT_STRICT; + static final boolean DEFAULT_STRICT = false; /** * The initial buffer length of this state. (This isn't really used on all * non-C implementations.) @@ -204,6 +211,7 @@ public IRubyObject initialize_copy(ThreadContext context, IRubyObject vOrig) { this.asciiOnly = orig.asciiOnly; this.quirksMode = orig.quirksMode; this.escapeSlash = orig.escapeSlash; + this.strict = orig.strict; this.bufferInitialLength = orig.bufferInitialLength; this.depth = orig.depth; return this; @@ -374,6 +382,24 @@ public IRubyObject escape_slash_set(IRubyObject escape_slash) { return escape_slash.getRuntime().newBoolean(escapeSlash); } + /** + * Returns true if strict mode is enabled. + */ + public boolean strict() { + return strict; + } + + @JRubyMethod(name="strict") + public RubyBoolean strict_get(ThreadContext context) { + return context.getRuntime().newBoolean(strict); + } + + @JRubyMethod(name="strict=") + public IRubyObject strict_set(IRubyObject isStrict) { + strict = isStrict.isTrue(); + return isStrict.getRuntime().newBoolean(strict); + } + public boolean allowNaN() { return allowNaN; } @@ -459,6 +485,7 @@ public IRubyObject configure(ThreadContext context, IRubyObject vOpts) { allowNaN = opts.getBool("allow_nan", DEFAULT_ALLOW_NAN); asciiOnly = opts.getBool("ascii_only", DEFAULT_ASCII_ONLY); escapeSlash = opts.getBool("escape_slash", DEFAULT_ESCAPE_SLASH); + strict = opts.getBool("strict", DEFAULT_STRICT); bufferInitialLength = opts.getInt("buffer_initial_length", DEFAULT_BUFFER_INITIAL_LENGTH); depth = opts.getInt("depth", 0); @@ -487,6 +514,7 @@ public RubyHash to_h(ThreadContext context) { result.op_aset(context, runtime.newSymbol("ascii_only"), ascii_only_p(context)); result.op_aset(context, runtime.newSymbol("max_nesting"), max_nesting_get(context)); result.op_aset(context, runtime.newSymbol("escape_slash"), escape_slash_get(context)); + result.op_aset(context, runtime.newSymbol("strict"), strict_get(context)); result.op_aset(context, runtime.newSymbol("depth"), depth_get(context)); result.op_aset(context, runtime.newSymbol("buffer_initial_length"), buffer_initial_length_get(context)); for (String name: getInstanceVariableNameList()) { diff --git a/lib/json/common.rb b/lib/json/common.rb index ea46896fc..b596f4ff7 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -3,6 +3,9 @@ require 'json/generic_object' module JSON + NOT_SET = Object.new.freeze + private_constant :NOT_SET + class << self # :call-seq: # JSON[object] -> new_array or new_string @@ -628,7 +631,7 @@ class << self # puts File.read(path) # Output: # {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"} - def dump(obj, anIO = nil, limit = nil) + def dump(obj, anIO = nil, limit = nil, strict: NOT_SET) if anIO and limit.nil? anIO = anIO.to_io if anIO.respond_to?(:to_io) unless anIO.respond_to?(:write) @@ -638,6 +641,7 @@ def dump(obj, anIO = nil, limit = nil) end opts = JSON.dump_default_options opts = opts.merge(:max_nesting => limit) if limit + opts[:strict] = strict if NOT_SET != strict result = generate(obj, opts) if anIO anIO.write result diff --git a/lib/json/pure/generator.rb b/lib/json/pure/generator.rb index 2257ee346..2e42230b3 100644 --- a/lib/json/pure/generator.rb +++ b/lib/json/pure/generator.rb @@ -131,6 +131,7 @@ def initialize(opts = {}) @allow_nan = false @ascii_only = false @escape_slash = false + @strict = false @buffer_initial_length = 1024 configure opts end @@ -160,6 +161,10 @@ def initialize(opts = {}) # all json strings. attr_accessor :escape_slash + # If this attribute is set to true, attempting to serialize types not + # supported by the JSON spec will raise a JSON::GeneratorError + attr_accessor :strict + # :stopdoc: attr_reader :buffer_initial_length @@ -204,6 +209,11 @@ def escape_slash? @escape_slash end + # Returns true, if forward slashes are escaped. Otherwise returns false. + def strict? + @strict + end + # Configure this State instance with the Hash _opts_, and return # itself. def configure(opts) @@ -227,6 +237,7 @@ def configure(opts) @depth = opts[:depth] || 0 @buffer_initial_length ||= opts[:buffer_initial_length] @escape_slash = !!opts[:escape_slash] if opts.key?(:escape_slash) + @strict = !!opts[:strict] if opts.key?(:strict) if !opts.key?(:max_nesting) # defaults to 100 @max_nesting = 100 @@ -287,7 +298,13 @@ module Object # Converts this object to a string (calling #to_s), converts # it to a JSON string, and returns the result. This is a fallback, if no # special method #to_json was defined for some object. - def to_json(*) to_s.to_json end + def to_json(generator_state) + if generator_state.strict? + raise GeneratorError, "#{self.class} not allowed in JSON" + else + to_s.to_json + end + end end module Hash @@ -324,7 +341,9 @@ def json_transform(state) result << state.space_before result << ':' result << state.space - if value.respond_to?(:to_json) + if state.strict? + raise GeneratorError, "#{value.class} not allowed in JSON" + elsif value.respond_to?(:to_json) result << value.to_json(state) else result << %{"#{String(value)}"} @@ -365,7 +384,9 @@ def json_transform(state) each { |value| result << delim unless first result << state.indent * depth if indent - if value.respond_to?(:to_json) + if state.strict? + raise GeneratorError, "#{value.class} not allowed in JSON" + elsif value.respond_to?(:to_json) result << value.to_json(state) else result << %{"#{String(value)}"} diff --git a/tests/json_generator_test.rb b/tests/json_generator_test.rb old mode 100644 new mode 100755 index f31b6b290..878945982 --- a/tests/json_generator_test.rb +++ b/tests/json_generator_test.rb @@ -150,6 +150,7 @@ def test_pretty_state :buffer_initial_length => 1024, :depth => 0, :escape_slash => false, + :strict => false, :indent => " ", :max_nesting => 100, :object_nl => "\n", @@ -167,6 +168,7 @@ def test_safe_state :buffer_initial_length => 1024, :depth => 0, :escape_slash => false, + :strict => false, :indent => "", :max_nesting => 100, :object_nl => "", @@ -184,6 +186,7 @@ def test_fast_state :buffer_initial_length => 1024, :depth => 0, :escape_slash => false, + :strict => false, :indent => "", :max_nesting => 0, :object_nl => "", @@ -336,7 +339,13 @@ def test_hash_likeness_set_string def test_json_generate assert_raise JSON::GeneratorError do - assert_equal true, generate(["\xea"]) + generate(["\xea"]) + end + end + + def test_json_generate_unsupported_types + assert_raise JSON::GeneratorError do + generate(Object.new, strict: true) end end From d295e732361eb091871131f00c0a2bf337c0212e Mon Sep 17 00:00:00 2001 From: r7kamura Date: Mon, 6 Mar 2023 05:26:16 +0900 Subject: [PATCH 0184/1010] Add missing changelog entries for 1.8.5 and 1.8.6 --- CHANGES.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index ba66514d8..11e3632a3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -105,6 +105,19 @@ I changed these mentions to be consistent with the Ruby license setting in the gemspec files which were already correct now. +## 2017-01-13 (1.8.6) + * Be compatible with ancient ruby 1.8 (maybe?) + +## 2015-09-11 (1.8.5) + * Be compatible with ruby 2.4.0 + * There were still some mentions of dual GPL licensing in the source, but JSON + has just the Ruby license that itself includes an explicit dual-licensing + clause that allows covered software to be distributed under the terms of + the Simplified BSD License instead for all ruby versions >= 1.9.3. This is + however a GPL compatible license according to the Free Software Foundation. + I changed these mentions to be consistent with the Ruby license setting in + the gemspec files which were already correct now. + ## 2015-06-01 (1.8.3) * Fix potential memory leak, thx to nobu. From e5e5bbc4f81903782654a1c890b33f9b87a0e1ea Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 8 Mar 2023 12:05:04 +0900 Subject: [PATCH 0185/1010] Use macos-12 and ubuntu-22.04 instead of deprecated platforms. --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ed36dc19..68fb1acf9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,9 +13,9 @@ jobs: matrix: os: - ubuntu-20.04 - - ubuntu-18.04 - - macos-11.0 - - macos-10.15 + - ubuntu-22.04 + - macos-11 + - macos-12 - windows-latest ruby: - '3.0' From c75b63937ffa4a8f5751c9a2717f0a287377d818 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 8 Mar 2023 12:07:50 +0900 Subject: [PATCH 0186/1010] Switch to use ruby_versions callable workflow --- .github/workflows/ci.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 68fb1acf9..70c488bf4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,13 @@ on: - pull_request jobs: + ruby-versions: + uses: ruby/actions/.github/workflows/ruby_versions.yml@master + with: + min_version: 2.3 + host: + needs: ruby-versions name: ${{ matrix.os }} ${{ matrix.ruby }} runs-on: ${{ matrix.os }} strategy: @@ -17,17 +23,13 @@ jobs: - macos-11 - macos-12 - windows-latest - ruby: - - '3.0' - - 2.7 - - 2.6 - - 2.5 - - 2.4 - - 2.3 + ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }} include: - { os: ubuntu-latest , ruby: jruby-9.1 } # Ruby 2.3 - { os: ubuntu-latest , ruby: jruby-9.2 } # Ruby 2.5 - - { os: ubuntu-latest , ruby: truffleruby } + exclude: + - { os: windows-latest, ruby: truffleruby } + - { os: windows-latest, ruby: truffleruby-head } steps: - uses: actions/checkout@v2 From 8fb876e8c8045529441ad5fa6c8450f8dc11e35b Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 8 Mar 2023 12:12:14 +0900 Subject: [PATCH 0187/1010] Skip to install test caused by https://github.com/rubygems/rubygems/pull/6430 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 70c488bf4..4ccf3036c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,4 +54,4 @@ jobs: - run: rake build - run: gem install pkg/*.gem - + if: ${{ matrix.ruby != '3.2' }} From be079fc575d4a32edef988285bcb3fc527130043 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 8 Mar 2023 12:20:40 +0900 Subject: [PATCH 0188/1010] Added JRuby 9.3 --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ccf3036c..195c1cc47 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,7 @@ jobs: include: - { os: ubuntu-latest , ruby: jruby-9.1 } # Ruby 2.3 - { os: ubuntu-latest , ruby: jruby-9.2 } # Ruby 2.5 + - { os: ubuntu-latest , ruby: jruby-9.3 } # Ruby 2.7 exclude: - { os: windows-latest, ruby: truffleruby } - { os: windows-latest, ruby: truffleruby-head } From 811f2f455dcb7516746793506edb3c330b26a81d Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 8 Mar 2023 12:48:03 +0900 Subject: [PATCH 0189/1010] JRuby 9.4+ on Windows is not working with current master --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 195c1cc47..db4e3603f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,8 @@ jobs: exclude: - { os: windows-latest, ruby: truffleruby } - { os: windows-latest, ruby: truffleruby-head } + - { os: windows-latest, ruby: jruby } + - { os: windows-latest, ruby: jruby-head } steps: - uses: actions/checkout@v2 From 04d6e5f630309124bcf513c707e18a481611c294 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Sat, 15 Jan 2022 08:37:49 -0600 Subject: [PATCH 0190/1010] [CI] Update Ruby 3.1, Ruby master, Windows --- .github/workflows/ci.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db4e3603f..3074be5f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,7 @@ name: CI on: - push - pull_request +- workflow_dispatch jobs: ruby-versions: @@ -35,18 +36,16 @@ jobs: - { os: windows-latest, ruby: jruby-head } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Ruby - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby-pkgs@v1 with: ruby-version: ${{ matrix.ruby }} - - - run: sudo apt install ragel - if: ${{ startsWith(matrix.os, 'ubuntu') }} - - - run: brew install ragel - if: ${{ startsWith(matrix.os, 'macos') }} + apt-get: ragel + brew: ragel + # only needed for Ruby 2.3 + mingw: ragel - run: bundle install From 1d38e4d3725ae26b143b63cdd049d4efa1bc7011 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 8 Feb 2023 13:32:12 +0100 Subject: [PATCH 0191/1010] JRuby requires a minimum of Java 8 More recent JDKs will reject this command line because 1.6 is too old to build for. --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 8de6dedfb..4d792521b 100644 --- a/Rakefile +++ b/Rakefile @@ -155,7 +155,7 @@ if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby' classpath = (Dir['java/lib/*.jar'] << 'java/src' << JRUBY_JAR) * ':' obj = src.sub(/\.java\Z/, '.class') file obj => src do - sh 'javac', '-classpath', classpath, '-source', '1.6', '-target', '1.6', src + sh 'javac', '-classpath', classpath, '-source', '1.8', '-target', '1.8', src end JAVA_CLASSES << obj end From 29e5ccfc1b48ca267d2b34a057f9a797af2d437f Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Thu, 13 Apr 2023 19:22:29 +0200 Subject: [PATCH 0192/1010] Rename escape_slash in script_safe and also escape E+2028 and E+2029 It is rather common to directly interpolate JSON string inside ": "Browser Challenge" +} \ No newline at end of file diff --git a/test/json/fixtures/rfc8785/output/arrays.json b/test/json/fixtures/rfc8785/output/arrays.json new file mode 100644 index 000000000..5efb93db7 --- /dev/null +++ b/test/json/fixtures/rfc8785/output/arrays.json @@ -0,0 +1 @@ +[56,{"1":[],"10":null,"d":true}] \ No newline at end of file diff --git a/test/json/fixtures/rfc8785/output/french.json b/test/json/fixtures/rfc8785/output/french.json new file mode 100644 index 000000000..2e15cd1b2 --- /dev/null +++ b/test/json/fixtures/rfc8785/output/french.json @@ -0,0 +1 @@ +{"peach":"This sorting order","péché":"is wrong according to French","pêche":"but canonicalization MUST","sin":"ignore locale"} \ No newline at end of file diff --git a/test/json/fixtures/rfc8785/output/structures.json b/test/json/fixtures/rfc8785/output/structures.json new file mode 100644 index 000000000..dc21e2424 --- /dev/null +++ b/test/json/fixtures/rfc8785/output/structures.json @@ -0,0 +1 @@ +{"":"empty","1":{"\n":56,"f":{"F":5,"f":"hi"}},"10":{},"111":[{"E":"no","e":"yes"}],"A":{},"a":{}} \ No newline at end of file diff --git a/test/json/fixtures/rfc8785/output/unicode.json b/test/json/fixtures/rfc8785/output/unicode.json new file mode 100644 index 000000000..ee60fd121 --- /dev/null +++ b/test/json/fixtures/rfc8785/output/unicode.json @@ -0,0 +1 @@ +{"Unnormalized Unicode":"Å"} \ No newline at end of file diff --git a/test/json/fixtures/rfc8785/output/values.json b/test/json/fixtures/rfc8785/output/values.json new file mode 100644 index 000000000..29b720b6e --- /dev/null +++ b/test/json/fixtures/rfc8785/output/values.json @@ -0,0 +1 @@ +{"literals":[null,true,false],"numbers":[333333333.3333333,1e+30,4.5,0.002,1e-27],"string":"€$\u000f\nA'B\"\\\\\"/"} \ No newline at end of file diff --git a/test/json/fixtures/rfc8785/output/wierd.json b/test/json/fixtures/rfc8785/output/wierd.json new file mode 100644 index 000000000..62c83a333 --- /dev/null +++ b/test/json/fixtures/rfc8785/output/wierd.json @@ -0,0 +1 @@ +{"\n":"Newline","\r":"Carriage Return","1":"One","":"Browser Challenge","€":"Control","ö":"Latin Small Letter O With Diaeresis","€":"Euro Sign","😂":"Smiley","דּ":"Hebrew Letter Dalet With Dagesh"} \ No newline at end of file diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index a901840d9..0a20357f0 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -366,6 +366,7 @@ def test_state_defaults space: "", space_before: "", sort_keys: false, + rfc8785: false, }.sort_by { |n,| n.to_s }.to_h, state.to_h.sort_by { |n,| n.to_s }.to_h) state = JSON::State.new(allow_duplicate_key: true) @@ -385,6 +386,7 @@ def test_state_defaults space: "", space_before: "", sort_keys: false, + rfc8785: false, }.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s }) end @@ -1227,4 +1229,34 @@ def test_large_depth_raises end end + def test_rfc8785_numbers + assert_rfc8785 '-9007199254740992', -9007199254740992 + assert_rfc8785 '0', 0 + assert_rfc8785 '0.000001', 0.000001 + assert_rfc8785 '1e+21', 1e+21 + assert_rfc8785 '9.999999999999997e+22', 9.999999999999997e+22 + assert_rfc8785 '9.999999999999997e-7', 9.999999999999997e-7 + assert_rfc8785 '9007199254740992', 9007199254740992 + assert_rfc8785 '9007199254740994', 9007199254740994 + assert_rfc8785 '9007199254740996', 9007199254740996 + assert_rfc8785 '999999999999999700000', 999999999999999700000 + assert_rfc8785 '999999999999999900000', 999999999999999900000 + assert_rfc8785 '333333333.3333333', 333333333.33333329 + end + + fixtures_path = File.expand_path('../fixtures/rfc8785/', __FILE__) + Dir[File.join(fixtures_path, "input/*.json", __FILE__)].each do |input| + filename = File.basename(input) + name, _ = File.basename(filename, ".json") + expected = File.join(fixtures_path, 'output', filename) + define_method("test_rfc8785_#{name}") do + assert_rfc8785(File.read(output), JSON.load_file(input)) + end + end + + private + + def assert_rfc8785(expected, value) + assert_equal(expected, JSON.generate(value, rfc8785: true)) + end end From 42cc52a89caaa84319d52403d5477246846db163 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 5 Oct 2026 22:37:31 +0900 Subject: [PATCH 0975/1010] Open gzip in binary mode It is binary format, read in text mode stops at 0x1a (^Z). --- test/json/json_common_interface_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/json/json_common_interface_test.rb b/test/json/json_common_interface_test.rb index 0fbc2df7a..cf11c8f0e 100644 --- a/test/json/json_common_interface_test.rb +++ b/test/json/json_common_interface_test.rb @@ -135,7 +135,7 @@ def test_load stringio.rewind assert_equal @hash, JSON.load(stringio) - tempfile = Tempfile.open('@json.gzip') + tempfile = Tempfile.open('@json.gzip', mode: File::BINARY) gzw = Zlib::GzipWriter.new(tempfile) gzw.write(@json) gzw.close From b63b4f1506e9ead59e084cf4018126534db50d35 Mon Sep 17 00:00:00 2001 From: ydah Date: Mon, 5 Oct 2026 19:49:57 +0900 Subject: [PATCH 0976/1010] Honor as_json and reject disabled strict mode in JSON::Coder --- lib/json/common.rb | 9 +++++++-- lib/json/truffle_ruby/generator.rb | 17 +++++++++++++++-- test/json/json_coder_test.rb | 26 ++++++++++++++++++++++++++ test/json/json_generator_test.rb | 5 ++--- 4 files changed, 50 insertions(+), 7 deletions(-) diff --git a/lib/json/common.rb b/lib/json/common.rb index e1a8f6814..11f564083 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -827,7 +827,12 @@ class Coder # # puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z" # - def initialize(object_class: nil, array_class: nil, on_load: nil, **options, &as_json) + # The conversion callback can also be passed as +as_json:+. If both are given, the block takes precedence. + # Passing +false+ or +nil+ for +strict:+ raises ArgumentError. + # + def initialize(object_class: nil, array_class: nil, on_load: nil, strict: true, **options, &as_json) + raise ArgumentError, "JSON::Coder requires strict mode" unless strict + if object_class || array_class on_load = ParserOptions.on_load(on_load, object_class, array_class) end @@ -839,7 +844,7 @@ def initialize(object_class: nil, array_class: nil, on_load: nil, **options, &as @state = State.new( **generator_options, strict: true, - as_json: as_json, + as_json: as_json || generator_options[:as_json], ).freeze end diff --git a/lib/json/truffle_ruby/generator.rb b/lib/json/truffle_ruby/generator.rb index 99e5e7ec6..de3bcf366 100644 --- a/lib/json/truffle_ruby/generator.rb +++ b/lib/json/truffle_ruby/generator.rb @@ -192,7 +192,16 @@ def initialize(opts = nil) attr_accessor :array_nl # This proc converts unsupported types into native JSON types. - attr_accessor :as_json + attr_reader :as_json + + def as_json=(value) + raise FrozenError, "can't modify frozen #{self.class}: #{inspect}" if frozen? + value = value.to_proc if !(Proc === value) && value.respond_to?(:to_proc) + unless Proc === value + raise TypeError, "as_json must be a Proc" + end + @as_json = value + end # This integer returns the maximum level of data structure nesting in # the generated JSON, max_nesting = 0 if no maximum is checked. @@ -335,7 +344,11 @@ def configure(options) @object_nl = object_nl || '' @array_nl = array_nl || '' @allow_nan = allow_nan || false - @as_json = as_json || false + if as_json + self.as_json = as_json + else + @as_json = false + end @ascii_only = ascii_only || false self.sort_keys = sort_keys @depth = depth diff --git a/test/json/json_coder_test.rb b/test/json/json_coder_test.rb index 5ff314478..574222bf2 100755 --- a/test/json/json_coder_test.rb +++ b/test/json/json_coder_test.rb @@ -10,6 +10,32 @@ def test_json_coder_with_proc assert_equal %(["[Object object]"]), coder.dump([Object.new]) end + def test_json_coder_with_as_json_option + callback = ->(object, is_key) { 42 } + [callback, callback.method(:call)].each do |as_json| + coder = JSON::Coder.new(as_json: as_json) + assert_equal '[42]', coder.dump([Object.new]) + end + end + + def test_json_coder_block_overrides_as_json_option + coder = JSON::Coder.new(as_json: ->(object) { 1 }) { 2 } + assert_equal '[2]', coder.dump([Object.new]) + end + + def test_json_coder_as_json_invalid_type + assert_raise(TypeError) { JSON::Coder.new(as_json: 'x') } + end + + def test_json_coder_rejects_disabling_strict_mode + [false, nil].each do |strict| + error = assert_raise(ArgumentError) { JSON::Coder.new(strict: strict) } + assert_equal 'JSON::Coder requires strict mode', error.message + end + assert_raise(JSON::GeneratorError) { JSON::Coder.new.dump(Object.new) } + assert_raise(JSON::GeneratorError) { JSON::Coder.new(strict: true).dump(Object.new) } + end + def test_json_coder_with_proc_with_unsupported_value coder = JSON::Coder.new do |object, is_key| assert_equal false, is_key diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index 0a20357f0..326780177 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -1086,26 +1086,25 @@ def test_state_as_json_method object = Object.new state = JSON.state.new(strict: true) state.as_json = -> (o, is_key) { o.object_id }.method(:call) + assert_kind_of Proc, state.as_json assert_equal object.object_id.to_json, state.generate(object) end def test_json_generate_as_json_invalid_type - omit 'TruffleRuby does not convert as_json to Proc' if RUBY_ENGINE == 'truffleruby' [Object.new, Time.now].each do |as_json| assert_raise(TypeError) { JSON.generate(Object.new, strict: true, as_json: as_json) } end end def test_state_as_json_invalid_type - omit 'TruffleRuby does not convert as_json to Proc' if RUBY_ENGINE == 'truffleruby' state = JSON.state.new(strict: true) [Object.new, Time.now].each do |as_json| assert_raise(TypeError) { state.as_json = as_json } + assert_raise(TypeError) { state.configure(as_json: as_json) } end end def test_as_json_to_proc_returns_invalid_type - omit 'TruffleRuby does not convert as_json to Proc' if RUBY_ENGINE == 'truffleruby' as_json = Object.new def as_json.to_proc method(:to_proc) From 08e72297b252dc5b48eb5932d5b6299603872084 Mon Sep 17 00:00:00 2001 From: ydah Date: Mon, 5 Oct 2026 20:47:53 +0900 Subject: [PATCH 0977/1010] Exclude custom instance variables from State#to_h --- lib/json/ext/generator/state.rb | 5 ----- lib/json/truffle_ruby/generator.rb | 25 ++++++++++++++++++------- test/json/json_generator_test.rb | 10 ++++++++++ 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/lib/json/ext/generator/state.rb b/lib/json/ext/generator/state.rb index 2715a03f6..92c8fc480 100644 --- a/lib/json/ext/generator/state.rb +++ b/lib/json/ext/generator/state.rb @@ -63,11 +63,6 @@ def to_h result[:allow_duplicate_key] = allow_duplicate_key end - instance_variables.each do |iv| - iv = iv.to_s[1..-1] - result[iv.to_sym] = self[iv] - end - result end diff --git a/lib/json/truffle_ruby/generator.rb b/lib/json/truffle_ruby/generator.rb index de3bcf366..6a755659d 100644 --- a/lib/json/truffle_ruby/generator.rb +++ b/lib/json/truffle_ruby/generator.rb @@ -369,13 +369,24 @@ def allow_duplicate_key? # :nodoc: # Returns the configuration instance variables as a hash, that can be # passed to the configure method. def to_h - result = {} - instance_variables.each do |iv| - key = iv.to_s[1..-1] - result[key.to_sym] = instance_variable_get(iv) - end - - result + { + indent: @indent, + space: @space, + space_before: @space_before, + object_nl: @object_nl, + array_nl: @array_nl, + as_json: @as_json, + allow_nan: @allow_nan, + ascii_only: @ascii_only, + max_nesting: @max_nesting, + script_safe: @script_safe, + strict: @strict, + depth: @depth, + buffer_initial_length: @buffer_initial_length, + sort_keys: @sort_keys, + allow_duplicate_key: @allow_duplicate_key, + rfc8785: @rfc8785, + } end alias to_hash to_h diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index 326780177..2fbe3f9f6 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -660,6 +660,16 @@ def test_json_state_to_h_roundtrip assert_equal state.to_h, JSON.state.new(state.to_h).to_h end + def test_json_state_to_h_ignores_instance_variables + state = JSON.state.new(indent: ' ') + expected = state.to_h + state.instance_variable_set(:@custom, 42) + state.instance_variable_set(:@enabled, false) + + assert_equal expected, state.to_h + assert_equal expected, state.to_hash + end + def test_json_generate assert_raise JSON::GeneratorError do generate(["\xea"]) From 8bab92d63be4e3345ffeb6d931be1c9120ff799e Mon Sep 17 00:00:00 2001 From: ydah Date: Tue, 6 Oct 2026 20:53:07 +0900 Subject: [PATCH 0978/1010] Reject non-ASCII BINARY strings during JSON generation --- ext/json/ext/generator/generator.c | 17 +--------------- java/src/json/ext/StringEncoder.java | 21 ++------------------ test/json/json_generator_test.rb | 29 +++++++++++++++------------- 3 files changed, 19 insertions(+), 48 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index fbef1980b..45f95fb94 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -75,7 +75,7 @@ static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *dat static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj); static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, VALUE obj); -static int usascii_encindex, utf8_encindex, binary_encindex; +static int usascii_encindex, utf8_encindex; NORETURN(static void) raise_generator_error_str(VALUE invalid_object, VALUE str) { @@ -865,20 +865,6 @@ NOINLINE(static) VALUE convert_invalid_encoding(struct generate_json_data *data, } } - if (RB_ENCODING_GET_INLINED(str) == binary_encindex) { - VALUE utf8_string = rb_enc_associate_index(rb_str_dup(str), utf8_encindex); - switch (rb_enc_str_coderange(utf8_string)) { - case ENC_CODERANGE_7BIT: - return utf8_string; - case ENC_CODERANGE_VALID: - // For historical reason, we silently reinterpret binary strings as UTF-8 if it would work. - // TODO: Raise in 3.0.0 - rb_warn("JSON.generate: UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0"); - return utf8_string; - break; - } - } - return rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str); } @@ -2140,7 +2126,6 @@ void Init_generator(void) usascii_encindex = rb_usascii_encindex(); utf8_encindex = rb_utf8_encindex(); - binary_encindex = rb_ascii8bit_encindex(); rb_require("json/ext/generator/state"); diff --git a/java/src/json/ext/StringEncoder.java b/java/src/json/ext/StringEncoder.java index e059fc993..347635cad 100644 --- a/java/src/json/ext/StringEncoder.java +++ b/java/src/json/ext/StringEncoder.java @@ -13,7 +13,6 @@ import java.nio.charset.StandardCharsets; import org.jcodings.Encoding; -import org.jcodings.specific.ASCIIEncoding; import org.jcodings.specific.USASCIIEncoding; import org.jcodings.specific.UTF8Encoding; import org.jruby.Ruby; @@ -229,28 +228,12 @@ static RubyString ensureValidEncoding(ThreadContext context, RubyString str) { return str; } - return tryWeirdEncodings(context, str, encoding); + return tryWeirdEncodings(context, str); } - private static RubyString tryWeirdEncodings(ThreadContext context, RubyString str, Encoding encoding) { + private static RubyString tryWeirdEncodings(ThreadContext context, RubyString str) { Ruby runtime = context.runtime; - RubyString utf8String; - - if (encoding == ASCIIEncoding.INSTANCE) { - utf8String = str.strDup(runtime); - utf8String.setEncoding(UTF8Encoding.INSTANCE); - switch (utf8String.getCodeRange()) { - case StringSupport.CR_7BIT: - return utf8String; - case StringSupport.CR_VALID: - // For historical reason, we silently reinterpret binary strings as UTF-8 if it would work. - // TODO: Raise in 3.0.0 - runtime.getWarnings().warn("JSON.generate: UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0"); - return utf8String; - } - } - try { str = (RubyString) str.encode(context, runtime.getEncodingService().convertEncodingToRubyEncoding(UTF8Encoding.INSTANCE)); } catch (RaiseException re) { diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index 2fbe3f9f6..80f761e7b 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -1049,22 +1049,25 @@ def to_s end end - if defined?(JSON::Ext::Generator) and RUBY_PLATFORM != "java" - def test_valid_utf8_in_different_encoding - utf8_string = "€™" - wrong_encoding_string = utf8_string.b - # This behavior is historical. Not necessary desirable. We should deprecated it. - # The pure and java version of the gem already don't behave this way. - assert_warning(/UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0/) do - assert_equal utf8_string.to_json, wrong_encoding_string.to_json - end - - assert_warning(/UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0/) do - assert_equal JSON.dump(utf8_string), JSON.dump(wrong_encoding_string) - end + def test_valid_utf8_in_binary_encoding + string = "€™".b.freeze + assert_raise(JSON::GeneratorError) { string.to_json } + assert_raise(JSON::GeneratorError) { JSON.dump(string) } + [string, [string], { string => 1 }, { value: string }].each do |object| + error = assert_raise(JSON::GeneratorError) { JSON.generate(object) } + assert_same string, error.invalid_object + assert_kind_of Encoding::UndefinedConversionError, error.cause end end + def test_ascii_in_binary_encoding + string = "ascii".b + assert_equal '"ascii"', string.to_json + assert_equal '"ascii"', JSON.dump(string) + assert_equal '["ascii"]', JSON.generate([string]) + assert_equal '{"ascii":1}', JSON.generate(string => 1) + end + def test_nonutf8_encoding assert_equal("\"5\u{b0}\"", "5\xb0".dup.force_encoding(Encoding::ISO_8859_1).to_json) end From 37a48ef6b8ab528c352b56f2351af7123164ed66 Mon Sep 17 00:00:00 2001 From: ydah Date: Mon, 5 Oct 2026 23:15:50 +0900 Subject: [PATCH 0979/1010] Report the attempted depth in generator nesting errors --- ext/json/ext/generator/generator.c | 7 +++---- java/src/json/ext/GeneratorState.java | 10 ++++------ test/json/json_generator_test.rb | 12 ++++++++++++ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 45f95fb94..b2a35bcb2 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -1031,11 +1031,10 @@ json_object_i(VALUE key, VALUE val, VALUE _arg) static inline long increase_depth(struct generate_json_data *data) { JSON_Generator_State *state = data->state; - long depth = ++data->depth; - if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) { - rb_raise(eNestingError, "nesting of %ld is too deep. Did you try to serialize objects with circular references?", --data->depth); + if (RB_UNLIKELY(data->depth >= state->max_nesting && state->max_nesting)) { + rb_raise(eNestingError, "nesting of %ld is too deep. Did you try to serialize objects with circular references?", data->depth + 1); } - return depth; + return ++data->depth; } static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj) diff --git a/java/src/json/ext/GeneratorState.java b/java/src/json/ext/GeneratorState.java index 778500ef7..97809134c 100644 --- a/java/src/json/ext/GeneratorState.java +++ b/java/src/json/ext/GeneratorState.java @@ -649,9 +649,8 @@ public RubyHash to_h(ThreadContext context) { } public int increaseDepth(ThreadContext context) { - depth++; checkMaxNesting(context); - return depth; + return ++depth; } public int decreaseDepth() { @@ -659,13 +658,12 @@ public int decreaseDepth() { } /** - * Checks if the current depth is allowed as per this state's options. + * Checks if increasing the depth is allowed as per this state's options. * @param context The current context */ private void checkMaxNesting(ThreadContext context) { - if (maxNesting != 0 && depth > maxNesting) { - depth--; - throw Utils.newException(context, Utils.M_NESTING_ERROR, "nesting of " + depth + " is too deep. Did you try to serialize objects with circular references?"); + if (maxNesting != 0 && depth >= maxNesting) { + throw Utils.newException(context, Utils.M_NESTING_ERROR, "nesting of " + (depth + 1) + " is too deep. Did you try to serialize objects with circular references?"); } } } diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index 80f761e7b..831443bf8 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -477,6 +477,18 @@ def test_depth_nesting_error assert_raise(JSON::NestingError) { JSON.pretty_generate(ary) } end + def test_nesting_error_reports_attempted_depth + [[[]], { a: {} }].each do |object| + [0, 1].each do |depth| + state = JSON.state.new(depth: depth, max_nesting: depth + 1) + error = assert_raise(JSON::NestingError) { state.generate(object) } + assert_match(/\Anesting of #{depth + 2} is too deep\./, error.message) + assert_equal depth, state.depth + assert_equal '[]', state.generate([]) + end + end + end + def test_depth_nesting_error_to_json ary = []; ary << ary s = JSON.state.new(depth: 1) From 193df5585202d40ea1e3800d650b114b117e49f2 Mon Sep 17 00:00:00 2001 From: ydah Date: Tue, 6 Oct 2026 21:44:31 +0900 Subject: [PATCH 0980/1010] Preserve float precision in RFC 8785 number formatting --- lib/json/ext.rb | 4 +-- test/json/json_generator_test.rb | 42 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/lib/json/ext.rb b/lib/json/ext.rb index 19683f024..1c7965d08 100644 --- a/lib/json/ext.rb +++ b/lib/json/ext.rb @@ -66,8 +66,8 @@ def shareable_lambda(block) # :nodoc: if num < 0 num, sign = -num, '-' end - native_rep = "%.15E" % num - decimal, exponential = native_rep.split('E') + native_rep = num.to_s + decimal, exponential = native_rep.split('e') exp_val = exponential.to_i exponential = exp_val > 0 ? ('+' + exp_val.to_s) : exp_val.to_s diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index 831443bf8..75aa39dee 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -1266,6 +1266,48 @@ def test_rfc8785_numbers assert_rfc8785 '999999999999999700000', 999999999999999700000 assert_rfc8785 '999999999999999900000', 999999999999999900000 assert_rfc8785 '333333333.3333333', 333333333.33333329 + assert_rfc8785 '0.30000000000000004', 0.1 + 0.2 + end + + # https://www.rfc-editor.org/rfc/rfc8785.html#appendix-B + { + '0000000000000000' => '0', + '8000000000000000' => '0', + '0000000000000001' => '5e-324', + '8000000000000001' => '-5e-324', + '7fefffffffffffff' => '1.7976931348623157e+308', + 'ffefffffffffffff' => '-1.7976931348623157e+308', + '4340000000000000' => '9007199254740992', + 'c340000000000000' => '-9007199254740992', + '4430000000000000' => '295147905179352830000', + '7fffffffffffffff' => nil, + '7ff0000000000000' => nil, + '44b52d02c7e14af5' => '9.999999999999997e+22', + '44b52d02c7e14af6' => '1e+23', + '44b52d02c7e14af7' => '1.0000000000000001e+23', + '444b1ae4d6e2ef4e' => '999999999999999700000', + '444b1ae4d6e2ef4f' => '999999999999999900000', + '444b1ae4d6e2ef50' => '1e+21', + '3eb0c6f7a0b5ed8c' => '9.999999999999997e-7', + '3eb0c6f7a0b5ed8d' => '0.000001', + '41b3de4355555553' => '333333333.3333332', + '41b3de4355555554' => '333333333.33333325', + '41b3de4355555555' => '333333333.3333333', + '41b3de4355555556' => '333333333.3333334', + '41b3de4355555557' => '333333333.33333343', + 'becbf647612f3696' => '-0.0000033333333333333333', + '43143ff3c1cb0959' => '1424953923781206.2', + }.each do |bits, expected| + define_method("test_rfc8785_number_#{bits}") do + omit "Float#to_s is not always shortest on this engine" unless RUBY_ENGINE == "ruby" + number = [bits].pack('H*').unpack('G').first + if expected + assert_rfc8785 expected, number + assert_equal number, JSON.parse(JSON.generate(number, rfc8785: true)).to_f + else + assert_raise(JSON::GeneratorError) { JSON.generate(number, rfc8785: true) } + end + end end fixtures_path = File.expand_path('../fixtures/rfc8785/', __FILE__) From 808577ae1d6b32712d7cbe4377caf29bba70e19f Mon Sep 17 00:00:00 2001 From: ydah Date: Wed, 7 Oct 2026 22:50:43 +0900 Subject: [PATCH 0981/1010] Sort RFC 8785 object properties by their keys --- lib/json/ext.rb | 2 +- test/json/json_generator_test.rb | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/json/ext.rb b/lib/json/ext.rb index 1c7965d08..9c7e090bb 100644 --- a/lib/json/ext.rb +++ b/lib/json/ext.rb @@ -95,7 +95,7 @@ def shareable_lambda(block) # :nodoc: }) generator::State.rfc8785_sort_keys_proc = shareable_lambda(->(hash) { - hash.sort_by { |k| k.to_s.encode(Encoding::UTF_16) }.to_h + hash.sort_by { |k,| k.to_s.encode(Encoding::UTF_16) }.to_h }) JSON.generator = generator diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index 75aa39dee..02cff38cd 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -1253,6 +1253,36 @@ def test_large_depth_raises end end + def test_rfc8785_key_order + assert_rfc8785 '{"a":1,"a b":2}', { 'a' => 1, 'a b' => 2 } + assert_rfc8785 '{"a":2,"a!":1}', { 'a!' => 1, 'a' => 2 } + assert_rfc8785 '{"\n":1,"A":2}', { "\n" => 1, 'A' => 2 } + assert_rfc8785 '[{"a":1,"a b":2}]', [{ 'a b' => 2, 'a' => 1 }] + end + + def test_rfc8785_mixed_key_order + assert_rfc8785 '{"10":2,"9":1}', { '9' => 1, 10 => 2 } + assert_rfc8785 '{"a":1,"b":2}', { a: 1, 'b' => 2 } + end + + def test_rfc8785_key_order_with_default_external_encoding + # https://www.rfc-editor.org/rfc/rfc8785.html#section-3.2.3 + hash = { "\u20ac" => 1, "\r" => 2, "\ufb33" => 3, '1' => 4, + "\u{1f600}" => 5, "\u0080" => 6, "\u00f6" => 7 } + expected = "{\"\\r\":2,\"1\":4,\"\u0080\":6,\"\u00f6\":7,\"\u20ac\":1,\"\u{1f600}\":5,\"\ufb33\":3}" + verbose = $VERBOSE + previous_encoding = Encoding.default_external + $VERBOSE = nil + outputs = [Encoding::UTF_8, Encoding::US_ASCII].map do |encoding| + Encoding.default_external = encoding + JSON.generate(hash, rfc8785: true) + end + assert_equal [expected, expected], outputs + ensure + Encoding.default_external = previous_encoding + $VERBOSE = verbose + end + def test_rfc8785_numbers assert_rfc8785 '-9007199254740992', -9007199254740992 assert_rfc8785 '0', 0 From 3e3807c82a03d09d2db9a2a1425d6d1635f55ae8 Mon Sep 17 00:00:00 2001 From: ydah Date: Wed, 7 Oct 2026 23:05:27 +0900 Subject: [PATCH 0982/1010] Fix RFC 8785 number formatter spelling --- ext/json/ext/generator/generator.c | 18 +++++++++--------- java/src/json/ext/Generator.java | 2 +- java/src/json/ext/GeneratorState.java | 8 ++++---- lib/json/ext.rb | 2 +- lib/json/truffle_ruby/generator.rb | 4 ++-- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index b2a35bcb2..bdc34b71f 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -32,7 +32,7 @@ typedef struct JSON_Generator_StateStruct { } JSON_Generator_State; static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_UTF_8, default_sort_keys_proc, - rfc8785_number_formater_proc, rfc8785_sort_keys_proc; + rfc8785_number_formatter_proc, rfc8785_sort_keys_proc; static ID i_to_s, i_to_json, i_new, i_encode; static VALUE sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl, sym_max_nesting, sym_allow_nan, sym_allow_duplicate_key, @@ -1171,7 +1171,7 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data } if (RB_UNLIKELY(data->state->rfc8785)) { - VALUE str = rb_proc_call_with_block(rfc8785_number_formater_proc, 1, &obj, Qnil); + VALUE str = rb_proc_call_with_block(rfc8785_number_formatter_proc, 1, &obj, Qnil); Check_Type(str, T_STRING); fbuffer_append_str(buffer, str); return; @@ -1754,15 +1754,15 @@ static VALUE cState_set_default_sort_keys_proc(VALUE self, VALUE proc) return default_sort_keys_proc = proc; } -static VALUE cState_set_rfc8785_number_formater_proc(VALUE self, VALUE proc) +static VALUE cState_set_rfc8785_number_formatter_proc(VALUE self, VALUE proc) { if (!rb_obj_is_proc(proc)) { - rb_raise(rb_eTypeError, "rfc8785_number_formater_proc must be a Proc"); + rb_raise(rb_eTypeError, "rfc8785_number_formatter_proc must be a Proc"); } - if (rfc8785_number_formater_proc) { - rb_raise(rb_eArgError, "rfc8785_number_formater_proc can only be set once"); + if (rfc8785_number_formatter_proc) { + rb_raise(rb_eArgError, "rfc8785_number_formatter_proc can only be set once"); } - return rfc8785_number_formater_proc = proc; + return rfc8785_number_formatter_proc = proc; } static VALUE cState_set_rfc8785_sort_keys_proc(VALUE self, VALUE proc) @@ -2036,7 +2036,7 @@ void Init_generator(void) VALUE mGenerator = rb_define_module_under(mExt, "Generator"); rb_global_variable(&default_sort_keys_proc); - rb_global_variable(&rfc8785_number_formater_proc); + rb_global_variable(&rfc8785_number_formatter_proc); rb_global_variable(&rfc8785_sort_keys_proc); rb_global_variable(&eGeneratorError); @@ -2049,7 +2049,7 @@ void Init_generator(void) rb_define_alloc_func(cState, cState_s_allocate); rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1); rb_define_singleton_method(cState, "default_sort_keys_proc=", cState_set_default_sort_keys_proc, 1); - rb_define_singleton_method(cState, "rfc8785_number_formater_proc=", cState_set_rfc8785_number_formater_proc, 1); + rb_define_singleton_method(cState, "rfc8785_number_formatter_proc=", cState_set_rfc8785_number_formatter_proc, 1); rb_define_singleton_method(cState, "rfc8785_sort_keys_proc=", cState_set_rfc8785_sort_keys_proc, 1); rb_define_method(cState, "initialize", cState_initialize, -1); diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index a38c31d83..0f6bc8248 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -421,7 +421,7 @@ static void generateFloat(ThreadContext context, Session session, RubyFloat obje } if (state.rfc8785()) { - RubyProc numberProc = (RubyProc)GeneratorState.rfc8785NumberFormaterProc; + RubyProc numberProc = (RubyProc)GeneratorState.rfc8785NumberFormatterProc; RubyString numberString = (RubyString)Helpers.invoke(context, numberProc, "call", object); buffer.write(numberString.toString().getBytes(UTF_8)); } else { diff --git a/java/src/json/ext/GeneratorState.java b/java/src/json/ext/GeneratorState.java index 97809134c..d702ae8a0 100644 --- a/java/src/json/ext/GeneratorState.java +++ b/java/src/json/ext/GeneratorState.java @@ -37,7 +37,7 @@ public class GeneratorState extends RubyObject { private boolean allowDuplicateKey = false; private static IRubyObject defaultSortKeyProc; - public static IRubyObject rfc8785NumberFormaterProc; + public static IRubyObject rfc8785NumberFormatterProc; public static IRubyObject rfc8785SortKeysProc; /** @@ -176,9 +176,9 @@ public static IRubyObject setDefaultSortKeyProc(IRubyObject klass, IRubyObject p return proc; } - @JRubyMethod(meta=true, name="rfc8785_number_formater_proc=") - public static IRubyObject setRfc8785NumberFormaterProc(IRubyObject klass, IRubyObject proc) { - rfc8785NumberFormaterProc = proc; + @JRubyMethod(meta=true, name="rfc8785_number_formatter_proc=") + public static IRubyObject setRfc8785NumberFormatterProc(IRubyObject klass, IRubyObject proc) { + rfc8785NumberFormatterProc = proc; return proc; } diff --git a/lib/json/ext.rb b/lib/json/ext.rb index 9c7e090bb..4443d6687 100644 --- a/lib/json/ext.rb +++ b/lib/json/ext.rb @@ -59,7 +59,7 @@ def shareable_lambda(block) # :nodoc: }) # Directly lifted from Gregg Kellogg's json-canonicalization - generator::State.rfc8785_number_formater_proc = shareable_lambda(->(num) { + generator::State.rfc8785_number_formatter_proc = shareable_lambda(->(num) { if num.zero? "0" else diff --git a/lib/json/truffle_ruby/generator.rb b/lib/json/truffle_ruby/generator.rb index 6a755659d..852ad4e55 100644 --- a/lib/json/truffle_ruby/generator.rb +++ b/lib/json/truffle_ruby/generator.rb @@ -112,7 +112,7 @@ def self.valid_utf8?(string) # while generating a JSON text from a Ruby data structure. class State singleton_class.attr_accessor :default_sort_keys_proc # :nodoc: - singleton_class.attr_accessor :rfc8785_number_formater_proc # :nodoc: + singleton_class.attr_accessor :rfc8785_number_formatter_proc # :nodoc: singleton_class.attr_accessor :rfc8785_sort_keys_proc # :nodoc: def self.generate(obj, opts = nil, io = nil) @@ -728,7 +728,7 @@ def to_json(state = nil, *args) raise GeneratorError.new("#{self} not allowed in JSON", self) end elsif state.rfc8785? - State.rfc8785_number_formater_proc.call(self) + State.rfc8785_number_formatter_proc.call(self) else to_s end From 3d9d6dd32d0185a39583bac7e12e3e9b07dd4544 Mon Sep 17 00:00:00 2001 From: ydah Date: Thu, 8 Oct 2026 07:34:03 +0900 Subject: [PATCH 0983/1010] Canonicalize integers as doubles in RFC 8785 mode --- ext/json/ext/generator/generator.c | 19 +++++++++++--- java/src/json/ext/Generator.java | 30 ++++++++++++++------- lib/json.rb | 2 ++ lib/json/ext.rb | 6 +++++ lib/json/truffle_ruby/generator.rb | 10 +++++-- test/json/json_generator_test.rb | 42 ++++++++++++++++++++++++++++++ 6 files changed, 95 insertions(+), 14 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index bdc34b71f..32f38a58d 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -1135,13 +1135,28 @@ static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, fbuffer_append(buffer, "true", 4); } +static void generate_json_rfc8785_number(FBuffer *buffer, VALUE obj) +{ + VALUE str = rb_proc_call_with_block(rfc8785_number_formatter_proc, 1, &obj, Qnil); + Check_Type(str, T_STRING); + fbuffer_append_str(buffer, str); +} + static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj) { + if (RB_UNLIKELY(data->state->rfc8785)) { + generate_json_rfc8785_number(buffer, obj); + return; + } fbuffer_append_long(buffer, FIX2LONG(obj)); } static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj) { + if (RB_UNLIKELY(data->state->rfc8785)) { + generate_json_rfc8785_number(buffer, obj); + return; + } VALUE tmp = rb_funcall(obj, i_to_s, 0); fbuffer_append_str(buffer, StringValue(tmp)); } @@ -1171,9 +1186,7 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data } if (RB_UNLIKELY(data->state->rfc8785)) { - VALUE str = rb_proc_call_with_block(rfc8785_number_formatter_proc, 1, &obj, Qnil); - Check_Type(str, T_STRING); - fbuffer_append_str(buffer, str); + generate_json_rfc8785_number(buffer, obj); return; } diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index 0f6bc8248..f191993d7 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -174,8 +174,8 @@ private static void generateFor(ThreadContext context, S case TRUE : buffer.write(TRUE_STRING); return; case FALSE : buffer.write(FALSE_STRING); return; case FLOAT : generateFloat(context, session, (RubyFloat) object, buffer); return; - case FIXNUM : generateFixnum(session, (RubyFixnum) object, buffer); return; - case BIGNUM : generateBignum((RubyBignum) object, buffer); return; + case FIXNUM : generateFixnum(context, session, (RubyFixnum) object, buffer); return; + case BIGNUM : generateBignum(context, session, (RubyBignum) object, buffer); return; case SYMBOL : generateSymbol(context, session, (RubySymbol) object, buffer); return; case STRING : if (Helpers.metaclass(object) != context.runtime.getString()) break; @@ -345,11 +345,15 @@ void generate(ThreadContext context, Session session, T object, OutputStream buf private static class BignumHandler extends Handler { @Override void generate(ThreadContext context, Session session, RubyBignum object, OutputStream buffer) throws IOException { - generateBignum(object, buffer); + generateBignum(context, session, object, buffer); } } - private static void generateBignum(RubyBignum object, OutputStream buffer) throws IOException { + private static void generateBignum(ThreadContext context, Session session, RubyBignum object, OutputStream buffer) throws IOException { + if (session.getState(context).rfc8785()) { + generateRfc8785Number(context, object, buffer); + return; + } BigInteger bigInt = object.getValue(); buffer.write(bigInt.toString().getBytes(UTF_8)); } @@ -357,11 +361,15 @@ private static void generateBignum(RubyBignum object, OutputStream buffer) throw private static class FixnumHandler extends Handler { @Override void generate(ThreadContext context, Session session, RubyFixnum object, OutputStream buffer) throws IOException { - generateFixnum(session, object, buffer); + generateFixnum(context, session, object, buffer); } } - static void generateFixnum(Session session, RubyFixnum object, OutputStream buffer) throws IOException { + static void generateFixnum(ThreadContext context, Session session, RubyFixnum object, OutputStream buffer) throws IOException { + if (session.getState(context).rfc8785()) { + generateRfc8785Number(context, object, buffer); + return; + } long i = object.getLongValue(); if (i == 0) { buffer.write('0'); @@ -421,14 +429,18 @@ static void generateFloat(ThreadContext context, Session session, RubyFloat obje } if (state.rfc8785()) { - RubyProc numberProc = (RubyProc)GeneratorState.rfc8785NumberFormatterProc; - RubyString numberString = (RubyString)Helpers.invoke(context, numberProc, "call", object); - buffer.write(numberString.toString().getBytes(UTF_8)); + generateRfc8785Number(context, object, buffer); } else { buffer.write(Double.toString(value).getBytes(UTF_8)); } } + private static void generateRfc8785Number(ThreadContext context, IRubyObject object, OutputStream buffer) throws IOException { + RubyProc numberProc = (RubyProc)GeneratorState.rfc8785NumberFormatterProc; + RubyString numberString = (RubyString)Helpers.invoke(context, numberProc, "call", object); + buffer.write(numberString.toString().getBytes(UTF_8)); + } + private static final byte[] EMPTY_ARRAY_BYTES = "[]".getBytes(); private static class ArrayHandler extends Handler> { @Override diff --git a/lib/json.rb b/lib/json.rb index 576d86de9..72ca9f1de 100644 --- a/lib/json.rb +++ b/lib/json.rb @@ -416,6 +416,8 @@ # order, allowing for arbitrary sort orders. # - Option +rfc8785+ (boolean) controls whether the generated JSON will be canonicalized # as defined in RFC8785. +# Integers are converted to Float before formatting and may be rounded. +# Integers outside the finite Float range raise JSON::GeneratorError. # # In this example, +obj+ is used first to generate the shortest # \JSON data (no whitespace), then again with all formatting options diff --git a/lib/json/ext.rb b/lib/json/ext.rb index 4443d6687..7382867a2 100644 --- a/lib/json/ext.rb +++ b/lib/json/ext.rb @@ -60,6 +60,12 @@ def shareable_lambda(block) # :nodoc: # Directly lifted from Gregg Kellogg's json-canonicalization generator::State.rfc8785_number_formatter_proc = shareable_lambda(->(num) { + if Integer === num + if num > Float::MAX || num < -Float::MAX + raise GeneratorError.new("Integer out of range for RFC 8785", num) + end + num = num.to_f + end if num.zero? "0" else diff --git a/lib/json/truffle_ruby/generator.rb b/lib/json/truffle_ruby/generator.rb index 852ad4e55..748a18c1d 100644 --- a/lib/json/truffle_ruby/generator.rb +++ b/lib/json/truffle_ruby/generator.rb @@ -473,7 +473,7 @@ def generate(obj, anIO = nil) buf << obj.to_json(self) end when Integer - buf << obj.to_s + buf << (@rfc8785 ? State.rfc8785_number_formatter_proc.call(obj) : obj.to_s) when Symbol if @strict fast_serialize_string(obj.name, buf) @@ -698,7 +698,13 @@ def json_transform(state) module Integer # Returns a JSON string representation for this Integer number. - def to_json(*) to_s end + def to_json(state = nil, *) + if state && State.from_state(state).rfc8785? + State.rfc8785_number_formatter_proc.call(self) + else + to_s + end + end end module Float diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index 02cff38cd..ee781194d 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -1283,6 +1283,48 @@ def test_rfc8785_key_order_with_default_external_encoding $VERBOSE = verbose end + { + 0 => '0', + 1 => '1', + 2**53 - 1 => '9007199254740991', + 2**53 => '9007199254740992', + 2**53 + 1 => '9007199254740992', + 2**53 + 3 => '9007199254740996', + 2**63 - 1 => '9223372036854776000', + 2**68 => '295147905179352830000', + 10**20 => '100000000000000000000', + 10**21 => '1e+21', + Float::MAX.to_i => '1.7976931348623157e+308', + }.each do |integer, expected| + define_method("test_rfc8785_integer_#{integer}") do + [1, -1].each do |sign| + number = sign * integer + json = sign < 0 && integer != 0 ? "-#{expected}" : expected + assert_rfc8785 json, number + assert_rfc8785 %({"n":[#{json}]}), { 'n' => [number] } + assert_equal json, number.to_json(rfc8785: true) + assert_equal json, JSON.generate(number, rfc8785: true, max_nesting: false) + assert_equal json, JSON::Coder.new(rfc8785: true).dump(number) + io = StringIO.new + JSON.dump(number, io, rfc8785: true) + assert_equal json, io.string + assert_equal number.to_s, JSON.generate(number) + end + end + end + + def test_rfc8785_rejects_out_of_range_integers + [Float::MAX.to_i + 1, 10**400].each do |integer| + [integer, -integer].each do |number| + error = assert_raise(JSON::GeneratorError) { JSON.generate(number, rfc8785: true) } + assert_same number, error.invalid_object + assert_equal 'Integer out of range for RFC 8785', error.message + assert_raise(JSON::GeneratorError) { JSON.dump([number], rfc8785: true) } + assert_equal number.to_s, JSON.generate(number) + end + end + end + def test_rfc8785_numbers assert_rfc8785 '-9007199254740992', -9007199254740992 assert_rfc8785 '0', 0 From 49e8df0e1639e7f825e9cd2cf665ccfc6d97d279 Mon Sep 17 00:00:00 2001 From: ydah Date: Wed, 7 Oct 2026 23:39:35 +0900 Subject: [PATCH 0984/1010] Reject incompatible RFC 8785 generation options --- ext/json/ext/generator/generator.c | 19 +++++++++ java/src/json/ext/Generator.java | 2 + java/src/json/ext/GeneratorState.java | 17 ++++++++ lib/json.rb | 4 ++ lib/json/common.rb | 2 +- lib/json/truffle_ruby/generator.rb | 41 +++++++++++++++----- test/json/json_generator_test.rb | 56 +++++++++++++++++++++++++++ 7 files changed, 130 insertions(+), 11 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 32f38a58d..9af35a086 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -1307,10 +1307,28 @@ static void generate_json_no_fallback(FBuffer *buffer, struct generate_json_data generate_json_general(buffer, data, obj, false); } +static void validate_rfc8785(JSON_Generator_State *state) +{ + if (!state->rfc8785) return; + + const char *option = state->indent ? "indent" : + state->space ? "space" : + state->space_before ? "space_before" : + state->object_nl ? "object_nl" : + state->array_nl ? "array_nl" : + state->ascii_only ? "ascii_only" : + state->script_safe ? "script_safe" : + state->allow_nan ? "allow_nan" : NULL; + if (option) { + rb_raise(rb_eArgError, "%s cannot be used with rfc8785", option); + } +} + static VALUE generate_json_try(VALUE d) { struct generate_json_data *data = (struct generate_json_data *)d; + validate_rfc8785(data->state); data->func(data->buffer, data, data->obj); return fbuffer_finalize(data->buffer); @@ -1990,6 +2008,7 @@ static void configure_state(JSON_Generator_State *state, VALUE vstate, VALUE con rb_hash_foreach(config, configure_state_i, (VALUE)&data); raise_argument_error_on_unknown_keywords(data.unknown_keywords); + validate_rfc8785(state); } static VALUE cState_configure(VALUE self, VALUE opts) diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index f191993d7..d653e582c 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -56,6 +56,7 @@ static RubyString generateJson(ThreadContext context, T static RubyString generateJson(ThreadContext context, T object, Handler handler, IRubyObject arg0) { Session session = new Session(arg0); GeneratorState state = session.getState(context); + state.validateRfc8785(context); int depth = state.depth; try { return handler.generateNew(context, session, object); @@ -87,6 +88,7 @@ static RubyString generateJson(ThreadContext context, T GeneratorState config, IRubyObject io) { Session session = new Session(config); GeneratorState state = session.getState(context); + state.validateRfc8785(context); int depth = state.depth; try { diff --git a/java/src/json/ext/GeneratorState.java b/java/src/json/ext/GeneratorState.java index d702ae8a0..a0f3dfc21 100644 --- a/java/src/json/ext/GeneratorState.java +++ b/java/src/json/ext/GeneratorState.java @@ -525,6 +525,22 @@ public boolean rfc8785() { return this.rfc8785; } + public void validateRfc8785(ThreadContext context) { + if (!rfc8785) return; + + String option = !indent.isEmpty() ? "indent" : + !space.isEmpty() ? "space" : + !spaceBefore.isEmpty() ? "space_before" : + !objectNl.isEmpty() ? "object_nl" : + !arrayNl.isEmpty() ? "array_nl" : + asciiOnly ? "ascii_only" : + scriptSafe ? "script_safe" : + allowNaN ? "allow_nan" : null; + if (option != null) { + throw context.runtime.newArgumentError(option + " cannot be used with rfc8785"); + } + } + public int getDepth() { return depth; } @@ -603,6 +619,7 @@ public IRubyObject _configure(ThreadContext context, IRubyObject vOpts) { rfc8785 = opts.getBool("rfc8785", rfc8785); opts.ensureEmpty(); + validateRfc8785(context); return this; } diff --git a/lib/json.rb b/lib/json.rb index 72ca9f1de..1969b026d 100644 --- a/lib/json.rb +++ b/lib/json.rb @@ -418,6 +418,10 @@ # as defined in RFC8785. # Integers are converted to Float before formatting and may be rounded. # Integers outside the finite Float range raise JSON::GeneratorError. +# Nonempty +indent+, +space+, +space_before+, +object_nl+, or +array_nl+, +# and enabled +ascii_only+, +script_safe+, or +allow_nan+ raise ArgumentError. +# JSON.dump defaults +allow_nan+ to +false+ when +rfc8785+ is enabled. +# JSON::Fragment contents are inserted as is; the caller must ensure they are canonical. # # In this example, +obj+ is used first to generate the shortest # \JSON data (no whitespace), then again with all formatting options diff --git a/lib/json/common.rb b/lib/json/common.rb index 11f564083..01a4be18c 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -763,7 +763,7 @@ def dump(obj, anIO = nil, _deprecated_limit = nil, kwargs = nil) end opts = { - allow_nan: true, + allow_nan: !(kwargs && kwargs[:rfc8785]), } opts[:max_nesting] = _deprecated_limit if _deprecated_limit opts.merge!(kwargs) if kwargs diff --git a/lib/json/truffle_ruby/generator.rb b/lib/json/truffle_ruby/generator.rb index 748a18c1d..895b09e75 100644 --- a/lib/json/truffle_ruby/generator.rb +++ b/lib/json/truffle_ruby/generator.rb @@ -127,7 +127,7 @@ def self.from_state(opts) if opts case when self === opts - return opts + return opts.validate_rfc8785 when opts.respond_to?(:to_hash) return new(opts.to_hash) when opts.respond_to?(:to_h) @@ -308,6 +308,22 @@ def rfc8785? @rfc8785 end + def validate_rfc8785 # :nodoc: + if @rfc8785 + option = if !@indent.empty? then :indent + elsif !@space.empty? then :space + elsif !@space_before.empty? then :space_before + elsif !@object_nl.empty? then :object_nl + elsif !@array_nl.empty? then :array_nl + elsif @ascii_only then :ascii_only + elsif @script_safe then :script_safe + elsif @allow_nan then :allow_nan + end + raise ArgumentError, "#{option} cannot be used with rfc8785" if option + end + self + end + # Configure this State instance with the Hash _opts_, and return # itself. def configure(options) @@ -359,7 +375,7 @@ def configure(options) @max_nesting = max_nesting || 0 self.rfc8785 = rfc8785 - self + validate_rfc8785 end def allow_duplicate_key? # :nodoc: @@ -399,6 +415,7 @@ def generate(obj, anIO = nil) return dup.generate(obj, anIO) if frozen? depth = @depth + validate_rfc8785 if @indent.empty? and @space.empty? and @space_before.empty? and @object_nl.empty? and @array_nl.empty? and !@ascii_only and !@script_safe and @max_nesting == 0 and (!@strict || Symbol === obj) and !@sort_keys result = generate_json(obj, ''.dup) @@ -542,10 +559,12 @@ module Hash def to_json(state = nil, *) state = State.from_state(state) depth = state.depth - state.check_max_nesting - json_transform(state) - ensure - state.depth = depth + begin + state.check_max_nesting + json_transform(state) + ensure + state.depth = depth + end end private @@ -642,10 +661,12 @@ module Array def to_json(state = nil, *) state = State.from_state(state) depth = state.depth - state.check_max_nesting - json_transform(state) - ensure - state.depth = depth + begin + state.check_max_nesting + json_transform(state) + ensure + state.depth = depth + end end private diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index ee781194d..a5f697ded 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -1325,6 +1325,62 @@ def test_rfc8785_rejects_out_of_range_integers end end + { + indent: ' ', space: ' ', space_before: ' ', object_nl: "\n", array_nl: "\n", + ascii_only: true, script_safe: true, allow_nan: true, + }.each do |option, value| + define_method("test_rfc8785_rejects_#{option}") do + options = { rfc8785: true, option => value } + error = assert_raise(ArgumentError) { JSON.generate({ 'a' => [1] }, options) } + assert_equal "#{option} cannot be used with rfc8785", error.message + assert_raise(ArgumentError) { JSON.generate(nil, options.to_a.reverse.to_h) } + assert_raise(ArgumentError) { [1].to_json(options) } + assert_raise(ArgumentError) { { 'a' => 1 }.to_json(options) } + assert_raise(ArgumentError) { JSON::Coder.new(**options).dump([1]) } + + state = JSON::State.new(option => value) + state.rfc8785 = true + io = StringIO.new + assert_raise(ArgumentError) { state.generate([1], io) } + assert_equal '', io.string + assert_raise(ArgumentError) { [1].to_json(state) } + assert_equal 0, state.depth + assert_raise(ArgumentError) { state.freeze.generate([1]) } + end + end + + def test_rfc8785_accepts_disabled_options + options = { rfc8785: true, indent: '', space: '', space_before: '', + object_nl: '', array_nl: '', ascii_only: false, + script_safe: false, allow_nan: false } + assert_equal '["é/",1]', JSON.generate(['é/', 1], options) + end + + def test_rfc8785_rejects_pretty_generation + assert_raise(ArgumentError) { JSON.pretty_generate({ 'a' => 1 }, rfc8785: true) } + end + + def test_rfc8785_dump_disables_allow_nan_by_default + assert_equal '[1]', JSON.dump([1], rfc8785: true) + [Float::NAN, Float::INFINITY, -Float::INFINITY].each do |number| + assert_raise(JSON::GeneratorError) { JSON.dump([number], rfc8785: true) } + end + assert_raise(ArgumentError) { JSON.dump([1], rfc8785: true, allow_nan: true) } + end + + def test_rfc8785_fragments + fragment = JSON::Fragment.new('[1,2]') + assert_rfc8785 '[1,2]', fragment + assert_rfc8785 '[[1,2]]', [fragment] + assert_rfc8785 '{"a":[1,2]}', { 'a' => fragment } + assert_equal '[[1,2]]', JSON.generate([fragment], rfc8785: true, max_nesting: false) + assert_equal '[1,2]', JSON::Coder.new(rfc8785: true).dump(fragment) + assert_equal '[1,2]', fragment.to_json(rfc8785: true) + + # Fragment contents are the caller's responsibility and are inserted as is. + assert_rfc8785 '[1, 2]', JSON::Fragment.new('[1, 2]') + end + def test_rfc8785_numbers assert_rfc8785 '-9007199254740992', -9007199254740992 assert_rfc8785 '0', 0 From cdcfdb80664613c832d35da52da8d1df42832544 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Thu, 8 Oct 2026 12:52:57 +0200 Subject: [PATCH 0985/1010] Better defend against `to_s` or `encode` not returning a T_STRING --- ext/json/ext/generator/generator.c | 25 ++++++++++++++--------- test/json/json_generator_test.rb | 32 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 9af35a086..380622235 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -770,6 +770,12 @@ static void vstate_spill(struct generate_json_data *data) RB_OBJ_WRITTEN(vstate, Qundef, state->sort_keys); } +static inline VALUE json_to_s(VALUE obj) +{ + VALUE tmp = rb_funcall(obj, i_to_s, 0); + return StringValue(tmp); +} + static inline VALUE json_call_to_json(struct generate_json_data *data, VALUE obj) { if (RB_UNLIKELY(!data->vstate)) { @@ -865,7 +871,12 @@ NOINLINE(static) VALUE convert_invalid_encoding(struct generate_json_data *data, } } - return rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str); + str = rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str); + Check_Type(str, T_STRING); + if (!valid_json_string_p(str)) { + raise_generator_error(str, "source sequence is illegal/malformed utf-8"); + } + return str; } ALWAYS_INLINE(static) VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key) @@ -1114,9 +1125,7 @@ static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *d Check_Type(tmp, T_STRING); fbuffer_append_str(buffer, tmp); } else { - tmp = rb_funcall(obj, i_to_s, 0); - Check_Type(tmp, T_STRING); - generate_json_string(buffer, data, tmp); + generate_json_string(buffer, data, json_to_s(obj)); } } @@ -1157,8 +1166,7 @@ static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *dat generate_json_rfc8785_number(buffer, obj); return; } - VALUE tmp = rb_funcall(obj, i_to_s, 0); - fbuffer_append_str(buffer, StringValue(tmp)); + fbuffer_append_str(buffer, json_to_s(obj)); } static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj) @@ -1177,11 +1185,10 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data return; } } - raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", rb_funcall(obj, i_to_s, 0)); + raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", json_to_s(obj)); } - VALUE tmp = rb_funcall(obj, i_to_s, 0); - fbuffer_append_str(buffer, tmp); + fbuffer_append_str(buffer, json_to_s(obj)); return; } diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index a5f697ded..fb38b1ab8 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -1223,6 +1223,38 @@ def test_frozen end end + def test_adversary_encode + fake_string = Class.new(String) do + def to_s + self + end + + def encode(*) + 42 + end + end + + assert_raise TypeError, JSON::GeneratorError do + JSON.generate({fake_string.new("\xff".b) => 1}) + end + + no_to_json = Class.new do + undef_method :to_json + + def initialize(str) + @str = str + end + + def to_s + @str + end + end + + assert_raise TypeError, JSON::GeneratorError do + JSON.generate([no_to_json.new(fake_string.new("\xff".b))]) + end + end + # The case when the State is frozen is tested in JSONCoderTest#test_nesting_recovery def test_nesting_recovery state = JSON::State.new From 88b358d8b2d28e0fba14db30f7942c0cfbf14be7 Mon Sep 17 00:00:00 2001 From: ydah Date: Thu, 8 Oct 2026 19:55:33 +0900 Subject: [PATCH 0986/1010] Sort RFC 8785 keys after as_json conversion --- ext/json/ext/generator/generator.c | 3 +- java/src/json/ext/Generator.java | 10 +++---- lib/json/ext.rb | 28 ++++++++++++++++- lib/json/truffle_ruby/generator.rb | 6 ++-- test/json/json_coder_test.rb | 48 ++++++++++++++++++++++++++++++ test/json/json_generator_test.rb | 19 ++++++++++++ 6 files changed, 103 insertions(+), 11 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 380622235..9413bf517 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -1051,7 +1051,8 @@ static inline long increase_depth(struct generate_json_data *data) static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj) { if (RB_UNLIKELY(data->state->rfc8785)) { - obj = rb_proc_call_with_block(rfc8785_sort_keys_proc, 1, &obj, Qnil); + VALUE args[2] = {obj, data->state->strict ? data->state->as_json : Qnil}; + obj = rb_proc_call_with_block(rfc8785_sort_keys_proc, 2, args, Qnil); Check_Type(obj, T_HASH); } else if (RB_UNLIKELY(data->state->sort_keys)) { obj = rb_proc_call_with_block(data->state->sort_keys, 1, &obj, Qnil); diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index d653e582c..3b05812f5 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -588,13 +588,11 @@ static void generateHash(ThreadContext context, Session session, RubyHash object return; } - RubyProc sortKeysProc = state.getSortKeysProc(); if (state.rfc8785()) { - sortKeysProc = (RubyProc)GeneratorState.rfc8785SortKeysProc; - } - - if (sortKeysProc != null) { - object = (RubyHash)Helpers.invoke(context, sortKeysProc, "call", object); + IRubyObject asJSON = state.strict() ? state.as_json_get(context) : context.nil; + object = (RubyHash)Helpers.invoke(context, GeneratorState.rfc8785SortKeysProc, "call", object, asJSON); + } else if (state.getSortKeysProc() != null) { + object = (RubyHash)Helpers.invoke(context, state.getSortKeysProc(), "call", object); } final ByteList objectNl = state.getObjectNl(); diff --git a/lib/json/ext.rb b/lib/json/ext.rb index 7382867a2..7d7b09b27 100644 --- a/lib/json/ext.rb +++ b/lib/json/ext.rb @@ -100,7 +100,33 @@ def shareable_lambda(block) # :nodoc: end }) - generator::State.rfc8785_sort_keys_proc = shareable_lambda(->(hash) { + generator::State.rfc8785_sort_keys_proc = shareable_lambda(->(hash, as_json) { + if as_json + converted = {} + hash.each do |key, value| + string = (String === key || Symbol === key) ? key.to_s : nil + unless string && (string.ascii_only? || (string.encoding == Encoding::UTF_8 && string.valid_encoding?)) + key = as_json.call(key, true) + unless String === key || Symbol === key + raise GeneratorError.new("#{key.class} not allowed as object key in JSON", key) + end + begin + string = key.to_s.encode(Encoding::UTF_8) + rescue EncodingError => error + raise GeneratorError.new(error.message, key) + end + unless string.valid_encoding? + raise GeneratorError.new("source sequence is illegal/malformed utf-8", key) + end + key = string + end + if converted.key?(key) + raise GeneratorError.new("detected duplicate key #{key.inspect}", key) + end + converted[key] = value + end + hash = converted + end hash.sort_by { |k,| k.to_s.encode(Encoding::UTF_16) }.to_h }) diff --git a/lib/json/truffle_ruby/generator.rb b/lib/json/truffle_ruby/generator.rb index 895b09e75..1cacd76c5 100644 --- a/lib/json/truffle_ruby/generator.rb +++ b/lib/json/truffle_ruby/generator.rb @@ -445,7 +445,7 @@ def generate(obj, anIO = nil) first = true key_type = nil if @rfc8785 - obj = State.rfc8785_sort_keys_proc.call(obj) + obj = State.rfc8785_sort_keys_proc.call(obj, @strict && @as_json) elsif @sort_keys obj = @sort_keys.call(obj) end @@ -585,7 +585,7 @@ def json_transform(state) hash = self if state.rfc8785? - hash = State.rfc8785_sort_keys_proc.call(hash) + hash = State.rfc8785_sort_keys_proc.call(hash, state.strict? && state.as_json) elsif state.sort_keys hash = state.sort_keys.call(hash) end @@ -596,7 +596,7 @@ def json_transform(state) else if key_type && !state.allow_duplicate_key? && key_type != key.class key_type = nil # stop checking - JSON.send(:on_mixed_keys_hash, self) + JSON.send(:on_mixed_keys_hash, hash) end result << delim end diff --git a/test/json/json_coder_test.rb b/test/json/json_coder_test.rb index 574222bf2..b91431821 100755 --- a/test/json/json_coder_test.rb +++ b/test/json/json_coder_test.rb @@ -64,6 +64,54 @@ def test_json_coder_hash_key assert_equal "Integer not allowed as object key in JSON", error.message end + def test_json_coder_rfc8785_converted_keys + time = Time.utc(1999) + calls = [] + coder = JSON::Coder.new(rfc8785: true) do |object, is_key| + calls << [object, is_key] + object.year.to_s + end + input = { time => 1, '1999!' => 2, Time.utc(2000) => 3 } + assert_equal '{"1999":1,"1999!":2,"2000":3}', coder.dump(input) + assert_equal [[time, true], [Time.utc(2000), true]], calls + assert_equal [time, '1999!', Time.utc(2000)], input.keys + end + + def test_json_coder_rfc8785_converted_key_validation + object = Object.new + coder = JSON::Coder.new(rfc8785: true) { 42 } + error = assert_raise(JSON::GeneratorError) { coder.dump(object => 1) } + assert_equal 'Integer not allowed as object key in JSON', error.message + assert_equal 42, error.invalid_object + + coder = JSON::Coder.new(rfc8785: true) { :a } + assert_equal '{"a":1,"b":2}', coder.dump('b' => 2, object => 1) + + invalid = "\xFF" + calls = 0 + coder = JSON::Coder.new(rfc8785: true) { calls += 1; invalid } + error = assert_raise(JSON::GeneratorError) { coder.dump(object => 1) } + assert_same invalid, error.invalid_object + assert_equal 1, calls + end + + def test_json_coder_rfc8785_converted_key_collision + coder = JSON::Coder.new(rfc8785: true) { 'a' } + assert_raise(JSON::GeneratorError) { coder.dump(Object.new => 1, 'a' => 2) } + assert_raise(JSON::GeneratorError) { coder.dump(Object.new => 1, Object.new => 2) } + assert_raise(JSON::GeneratorError) { coder.dump(Object.new => 1, :a => 2) } + end + + def test_json_coder_rfc8785_invalid_encoding_key + calls = [] + coder = JSON::Coder.new(rfc8785: true) do |object, is_key| + calls << [object, is_key] + 'a' + end + assert_equal '{"a":1,"b":2}', coder.dump('b' => 2, "\xFF" => 1) + assert_equal [["\xFF", true]], calls + end + def test_json_coder_options coder = JSON::Coder.new(array_nl: "\n") do |object| 42 diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index fb38b1ab8..7cf76c958 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -1297,6 +1297,25 @@ def test_rfc8785_mixed_key_order assert_rfc8785 '{"a":1,"b":2}', { a: 1, 'b' => 2 } end + def test_rfc8785_key_order_with_as_json + key = Object.new + def key.to_s + raise 'must not sort the original key' + end + as_json = ->(object, is_key) { is_key ? 'a' : object } + options = { rfc8785: true, strict: true, as_json: as_json } + object = [{ 'b' => 2, key => 1 }] + expected = '[{"a":1,"b":2}]' + assert_equal expected, JSON.generate(object, options) + assert_equal expected, object.to_json(options) + state = JSON::State.new(options) + io = StringIO.new + state.generate(object, io) + assert_equal expected, io.string + + assert_equal '{"1":1,"b":2}', JSON.generate({ 'b' => 2, 1 => 1 }, options.merge(strict: false)) + end + def test_rfc8785_key_order_with_default_external_encoding # https://www.rfc-editor.org/rfc/rfc8785.html#section-3.2.3 hash = { "\u20ac" => 1, "\r" => 2, "\ufb33" => 3, '1' => 4, From df19d581f0d570569d4d23a171d49e76bfad8c9d Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Sat, 10 Oct 2026 03:54:59 +0900 Subject: [PATCH 0987/1010] Use rb_len_t for String lengths RSTRING_LEN returns rb_len_t on Ruby master, so the variables receiving it follow. Older Rubies get a long typedef and build as before. https://bugs.ruby-lang.org/issues/22400 Co-Authored-By: Claude Opus 5.5 --- ext/json/ext/generator/generator.c | 2 +- ext/json/ext/json.h | 4 ++++ ext/json/ext/parser/parser.c | 10 +++++----- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 9413bf517..2352c7cec 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -898,7 +898,7 @@ static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data } fbuffer_append_char(buffer, '"'); - long len; + rb_len_t len; search_state search; search.buffer = buffer; RSTRING_GETMEM(str, search.ptr, len); diff --git a/ext/json/ext/json.h b/ext/json/ext/json.h index be69bf589..f2be3e552 100644 --- a/ext/json/ext/json.h +++ b/ext/json/ext/json.h @@ -5,6 +5,10 @@ #include "ruby/encoding.h" #include +#ifndef HAVE_RB_LEN_T +typedef long rb_len_t; +#endif + #ifndef RBIMPL_ASSERT_OR_ASSUME # define RBIMPL_ASSERT_OR_ASSUME(x) #endif diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index c912a8eec..80bc12d20 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -138,7 +138,7 @@ ALWAYS_INLINE(static) int rstring_cache_memcmp(const char *str, const char *rptr ALWAYS_INLINE(static) int rstring_cache_cmp(const char *str, const long length, VALUE rstring) { const char *rstring_ptr; - long rstring_length; + rb_len_t rstring_length; RSTRING_GETMEM(rstring, rstring_ptr, rstring_length); @@ -2044,7 +2044,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data) const char *method_name_beg = last_colon + 1; long before_len = method_name_beg - name_cstr; - long len = RSTRING_LEN(name) - before_len; + rb_len_t len = RSTRING_LEN(name) - before_len; VALUE method_name = rb_str_substr(name, before_len, len); config->decimal_method_id = SYM2ID(rb_str_intern(method_name)); } else { @@ -2137,7 +2137,7 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src) .head = 1, }; - long len; + rb_len_t len; const char *start; RSTRING_GETMEM(Vsource, start, len); @@ -2456,7 +2456,7 @@ static VALUE cResumableParser_feed(VALUE self, VALUE str) rb_str_append(parser->buffer, str); } - long len; + rb_len_t len; const char *start; RSTRING_GETMEM(parser->buffer, start, len); parser->state.start = start; @@ -2762,7 +2762,7 @@ static VALUE cResumableParser_rest(VALUE self) size_t offset = parser->state.cursor - parser->state.start; const char *ptr; - long len; + rb_len_t len; RSTRING_GETMEM(parser->buffer, ptr, len); return rb_utf8_str_new(ptr + offset, len - offset); } From e7a6313b72519eac3b5689d799959593bf8d8521 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Sat, 10 Oct 2026 03:55:23 +0900 Subject: [PATCH 0988/1010] Use rb_len_t for the column of parse errors The column is a character offset counted by rb_enc_strlen(), which returns rb_len_t. The line number stays long. Co-Authored-By: Claude Opus 5.5 --- ext/json/ext/json.h | 2 ++ ext/json/ext/parser/parser.c | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/ext/json/ext/json.h b/ext/json/ext/json.h index f2be3e552..a402182d0 100644 --- a/ext/json/ext/json.h +++ b/ext/json/ext/json.h @@ -7,6 +7,8 @@ #ifndef HAVE_RB_LEN_T typedef long rb_len_t; +# define LEN2NUM LONG2NUM +# define PRIdLEN "ld" #endif #ifndef RBIMPL_ASSERT_OR_ASSUME diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 80bc12d20..d35c14955 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -580,7 +580,7 @@ static inline char peek(JSON_ParserState *state) return *state->cursor; } -static void cursor_position(JSON_ParserState *state, long *line_out, long *column_out) +static void cursor_position(JSON_ParserState *state, long *line_out, rb_len_t *column_out) { JSON_ASSERT(!state->parser); JSON_ASSERT(state->cursor); @@ -597,7 +597,7 @@ static void cursor_position(JSON_ParserState *state, long *line_out, long *colum while (cursor > state->start && cursor[-1] != '\n') { cursor--; } - long column = rb_enc_strlen(cursor, state->cursor, enc_utf8) + 1; + rb_len_t column = rb_enc_strlen(cursor, state->cursor, enc_utf8) + 1; while (cursor > state->start) { if (*--cursor == '\n') { @@ -675,11 +675,11 @@ static VALUE json_path_new(JSON_ParserState *state, VALUE duplicate_key) return path; } -static VALUE parse_error_new(JSON_ParserState *state, VALUE message, long line, long column, bool eos) +static VALUE parse_error_new(JSON_ParserState *state, VALUE message, long line, rb_len_t column, bool eos) { VALUE exc = rb_exc_new_str(eParserError, message); rb_ivar_set(exc, i_at_line, LONG2NUM(line)); - rb_ivar_set(exc, i_at_column, LONG2NUM(column)); + rb_ivar_set(exc, i_at_column, LEN2NUM(column)); rb_ivar_set(exc, i_at_json_path, json_path_new(state, Qundef)); return exc; } @@ -697,9 +697,10 @@ NORETURN(static) void raise_parse_error(const char *format, JSON_ParserState *st } } else { VALUE message = build_parse_error_message(format, state); - long line, column; + long line; + rb_len_t column; cursor_position(state, &line, &column); - rb_str_catf(message, " at line %ld column %ld", line, column); + rb_str_catf(message, " at line %ld column %"PRIdLEN, line, column); rb_exc_raise(parse_error_new(state, message, line, column, eos)); } } @@ -1231,9 +1232,10 @@ NORETURN(static) void raise_duplicate_key_error(JSON_ParserState *state, VALUE d if (state->parser) { // line and columns can't be accurate in resumable exc = parse_error_new(state, message, 0, 0, false); } else { - long line, column; + long line; + rb_len_t column; cursor_position(state, &line, &column); - rb_str_catf(message, " at line %ld column %ld", line, column); + rb_str_catf(message, " at line %ld column %"PRIdLEN, line, column); exc = parse_error_new(state, message, line, column, false); } rb_ivar_set(exc, i_at_json_path, json_path_new(state, duplicate_key)); From a9cd923436310276f3a8721cc2ab3600236ae6b5 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Sat, 10 Oct 2026 04:03:49 +0900 Subject: [PATCH 0989/1010] Use rb_len_t for lengths given to the String API These lengths come from pointer differences and go to functions that take rb_len_t, so they no longer pass through long on the way. Co-Authored-By: Claude Opus 5.5 --- ext/json/ext/parser/parser.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index d35c14955..696a878a9 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -79,7 +79,7 @@ static rb_encoding *enc_utf8; #define JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH 55 -static inline VALUE build_interned_string(const char *str, const long length) +static inline VALUE build_interned_string(const char *str, const rb_len_t length) { # ifdef HAVE_RB_ENC_INTERNED_STR return rb_enc_interned_str(str, length, enc_utf8); @@ -89,7 +89,7 @@ static inline VALUE build_interned_string(const char *str, const long length) # endif } -static inline VALUE build_symbol(const char *str, const long length) +static inline VALUE build_symbol(const char *str, const rb_len_t length) { return rb_str_intern(build_interned_string(str, length)); } @@ -890,12 +890,12 @@ static inline VALUE build_string(const char *start, const char *end, bool intern VALUE result; # ifdef HAVE_RB_ENC_INTERNED_STR if (intern) { - result = rb_enc_interned_str(start, (long)(end - start), enc_utf8); + result = rb_enc_interned_str(start, (rb_len_t)(end - start), enc_utf8); } else { - result = rb_utf8_str_new(start, (long)(end - start)); + result = rb_utf8_str_new(start, (rb_len_t)(end - start)); } # else - result = rb_utf8_str_new(start, (long)(end - start)); + result = rb_utf8_str_new(start, (rb_len_t)(end - start)); if (intern) { result = rb_funcall(rb_str_freeze(result), i_uminus, 0); } @@ -1089,7 +1089,7 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser typedef VALUE (*json_number_decode_func_t)(const char *ptr); -static inline VALUE json_decode_large_number(const char *start, long len, json_number_decode_func_t func) +static inline VALUE json_decode_large_number(const char *start, rb_len_t len, json_number_decode_func_t func) { if (RB_LIKELY(len < MAX_NUMBER_STACK_BUFFER)) { char buffer[MAX_NUMBER_STACK_BUFFER]; @@ -1112,7 +1112,7 @@ static VALUE json_decode_inum(const char *buffer) return rb_cstr2inum(buffer, 10); } -NOINLINE(static) VALUE json_decode_large_integer(const char *start, long len) +NOINLINE(static) VALUE json_decode_large_integer(const char *start, rb_len_t len) { return json_decode_large_number(start, len, json_decode_inum); } @@ -1153,7 +1153,7 @@ static VALUE json_decode_dnum(const char *buffer) return DBL2NUM(rb_cstr_to_dbl(buffer, 1)); } -NOINLINE(static) VALUE json_decode_large_float(const char *start, long len) +NOINLINE(static) VALUE json_decode_large_float(const char *start, rb_len_t len) { return json_decode_large_number(start, len, json_decode_dnum); } @@ -2045,7 +2045,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data) parser_config_wb_write(self, &config->decimal_class, rb_path_to_class(mod_path)); const char *method_name_beg = last_colon + 1; - long before_len = method_name_beg - name_cstr; + rb_len_t before_len = method_name_beg - name_cstr; rb_len_t len = RSTRING_LEN(name) - before_len; VALUE method_name = rb_str_substr(name, before_len, len); config->decimal_method_id = SYM2ID(rb_str_intern(method_name)); From 21193d0be87294cc69a4149f798070a73e3eb3cc Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Sat, 10 Oct 2026 07:09:54 +0900 Subject: [PATCH 0990/1010] Match rb_hash_bulk_insert fallback to Ruby's declaration Ruby declares it with an rb_len_t count, and a mismatching definition fails builds where the fallback is compiled against those headers. Co-Authored-By: Claude Opus 5.5 --- ext/json/ext/parser/parser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 696a878a9..70e34edb7 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -20,9 +20,9 @@ static int utf8_encindex; #ifndef HAVE_RB_HASH_BULK_INSERT // For TruffleRuby static void -rb_hash_bulk_insert(long count, const VALUE *pairs, VALUE hash) +rb_hash_bulk_insert(rb_len_t count, const VALUE *pairs, VALUE hash) { - long index = 0; + rb_len_t index = 0; while (index < count) { VALUE name = pairs[index++]; VALUE value = pairs[index++]; From 8df32ab07485db5fc288046056ae8bf4e34e9366 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Mon, 12 Oct 2026 10:34:31 +0900 Subject: [PATCH 0991/1010] Index the generated JSON array by rb_len_t An int index overflows on an Array longer than INT_MAX, which LP64 and now mswin can hold. Co-Authored-By: Claude Opus 5.5 --- ext/json/ext/generator/generator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 2352c7cec..38c88f231 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -1098,7 +1098,7 @@ static void generate_json_array(FBuffer *buffer, struct generate_json_data *data fbuffer_append_char(buffer, '['); if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl); - for (int i = 0; i < RARRAY_LEN(obj); i++) { + for (rb_len_t i = 0; i < RARRAY_LEN(obj); i++) { if (i > 0) { fbuffer_append_char(buffer, ','); if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl); From 429c1094585d7f5c144d1accb536192ad31e0314 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 12 Oct 2026 09:28:28 +0200 Subject: [PATCH 0992/1010] More hardening against adversarial `encode` methods --- ext/json/ext/parser/parser.c | 4 +--- test/json/json_parser_test.rb | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 70e34edb7..b3b6bfdca 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -2113,9 +2113,7 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src) // Ensure the string isn't mutated under us. // The classic API to use is `rb_str_locktmp`, but then we'd // need to use `rb_protect` to make sure we always unlock. - if (Vsource == src) { - Vsource = rb_str_new_frozen(Vsource); - } + Vsource = rb_str_new_frozen(Vsource); VALUE rvalue_stack_buffer[RVALUE_STACK_INITIAL_CAPA]; rvalue_stack value_stack = { diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb index d9e3256bd..0b9d5fc96 100644 --- a/test/json/json_parser_test.rb +++ b/test/json/json_parser_test.rb @@ -1045,6 +1045,41 @@ def test_mutating_source_string_during_parsing assert_equal expected, actual end + def test_adversary_encode + fake_string = Class.new(String) do + def encode(*) + 42 + end + end + + assert_raise TypeError, Encoding::InvalidByteSequenceError do + JSON.parse(fake_string.new("\xff".b, encoding: Encoding::UTF_16)) + end + + if RUBY_ENGINE != "jruby" + fake_string = Class.new(String) do + attr_reader :encoded_str + + def initialize(...) + super + @encoded_str = JSON.dump(1000.times.to_a) + end + + def encode(*) + @encoded_str + end + end + + fake = fake_string.new("blah", encoding: Encoding::UTF_16) + assert_equal 1000.times.to_a, JSON.parse(fake, on_load: ->(v) { + if v == 20 + fake.encoded_str.clear + end + v + }) + end + end + private def assert_equal_float(expected, actual, delta = 1e-2) From c1a4372837233df9d78c9f9ff0e34a09272bf59e Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 12 Oct 2026 15:45:27 +0200 Subject: [PATCH 0993/1010] ResumableParser: fix handling of incomplete unicode escape `spe` is the string end, so if the escape is incomplete, it doesn't mean we reached the end of stream, hence it's a syntax error, not an EOS error. --- ext/json/ext/parser/parser.c | 2 +- test/json/resumable_parser_test.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index b3b6bfdca..52b6e2250 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -753,7 +753,7 @@ static const signed char digit_values[256] = { static uint32_t unescape_unicode(JSON_ParserState *state, const char *sp, const char *spe) { if (RB_UNLIKELY(sp > spe - 4)) { - raise_eos_error_at("incomplete unicode character escape sequence at %s", state, sp - 2); + raise_syntax_error_at("incomplete unicode character escape sequence at %s", state, sp - 2); } const unsigned char *p = (const unsigned char *)sp; diff --git a/test/json/resumable_parser_test.rb b/test/json/resumable_parser_test.rb index a704e9edd..dbf3ea7b6 100644 --- a/test/json/resumable_parser_test.rb +++ b/test/json/resumable_parser_test.rb @@ -179,6 +179,12 @@ def test_parse_byte_by_byte_string assert_resumed_parsing('"te\\u2028st"') assert_resumed_parsing(JSON.generate("te\u2028st")) assert_resumed_parsing(JSON.generate("te \u2028 st")) + + parser = JSON::ResumableParser.new + parser << '"\u12"' + assert_raise JSON::ParserError do + parser.parse + end end def test_parse_byte_by_byte_numbers From 1b67eb3db207ad17336b70b7a6b1b794885f0a0b Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 12 Oct 2026 15:50:51 +0200 Subject: [PATCH 0994/1010] Fix parsing of unescaped NULL bytes in strings --- ext/json/ext/parser/parser.c | 4 ++-- test/json/json_parser_test.rb | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 52b6e2250..288521e1c 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -1046,13 +1046,13 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser p = ++pe; break; } - case 0: - return Qundef; default: if ((unsigned char)*pe < 0x20) { if (!config->allow_control_characters) { if (*pe == '\n') { raise_syntax_error_at("Invalid unescaped newline character (\\n) in string: %s", state, pe - 1); + } else if (*pe == '\0') { + raise_syntax_error_at("Invalid unescaped NULL character (\\0) in string: %s", state, pe - 1); } raise_syntax_error_at("invalid ASCII control character in string: %s", state, pe - 1); } diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb index 0b9d5fc96..75e99904d 100644 --- a/test/json/json_parser_test.rb +++ b/test/json/json_parser_test.rb @@ -239,6 +239,8 @@ def test_parse_control_char_and_backslash end JSON.parse(%("#{'a' * 30}#{backslash_and_control_char}"), allow_control_characters: true, allow_invalid_escape: true) + + assert_equal ["a\u0000b", 2], JSON.parse("[\"a\\\x00b\", 2]", allow_control_characters: true, allow_invalid_escape: true) end def test_parse_invalid_escape From 6ea62016fb726739439b8a68454eb54550e70094 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 12 Oct 2026 16:10:06 +0200 Subject: [PATCH 0995/1010] Fix parsing of number with out of range negative exponents --- ext/json/ext/parser/parser.c | 6 +++--- java/src/json/ext/Parser.java | 6 +++--- test/json/json_parser_test.rb | 10 ++++++++++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 288521e1c..d86c347ad 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -1169,11 +1169,11 @@ static inline VALUE json_decode_float(JSON_ParserConfig *config, uint64_t mantis return rb_funcallv(config->decimal_class, config->decimal_method_id, 1, &text); } - if (RB_UNLIKELY(exponent > INT32_MAX)) { + if (RB_UNLIKELY(exponent >= (int64_t)INT32_MAX)) { return negative ? CMinusInfinity : CInfinity; } - if (RB_UNLIKELY(exponent < INT32_MIN)) { + if (RB_UNLIKELY(exponent <= INT32_MIN)) { return rb_float_new(negative ? -0.0 : 0.0); } @@ -1514,7 +1514,7 @@ static inline VALUE json_parse_number(JSON_ParserState *state, JSON_ParserConfig } if (RB_UNLIKELY(exponent_digits >= 20 || abs_exponent > (uint64_t)INT64_MAX)) { - exponent = negative_exponent ? INT64_MIN : INT64_MAX; + exponent = negative_exponent ? INT32_MIN : INT32_MAX; } else { exponent = negative_exponent ? -(int64_t)abs_exponent : (int64_t)abs_exponent; } diff --git a/java/src/json/ext/Parser.java b/java/src/json/ext/Parser.java index 88bc054ef..6fa5d4383 100644 --- a/java/src/json/ext/Parser.java +++ b/java/src/json/ext/Parser.java @@ -634,7 +634,7 @@ private IRubyObject parseNumber(int numberStart, byte first, boolean negative) { } if (exponent_digits >= 20 || Long.compareUnsigned(abs_exponent, Long.MAX_VALUE) > 0) { - exponent = negative_exponent ? Long.MIN_VALUE : Long.MAX_VALUE; + exponent = negative_exponent ? Integer.MIN_VALUE : Integer.MAX_VALUE; } else { exponent = negative_exponent ? -abs_exponent : abs_exponent; } @@ -655,8 +655,8 @@ private IRubyObject parseNumber(int numberStart, byte first, boolean negative) { IRubyObject decodeFloat(ThreadContext context, long mantissa, int mantissa_digits, long exponent, boolean negative, int start) { if (config.decimalClass != null) return config.decimalFactory.apply(context, absSubSequence(start, cursor)); - if (exponent > Integer.MAX_VALUE) return getConstant(negative ? CONST_MINUS_INFINITY : CONST_INFINITY); - if (exponent < Integer.MIN_VALUE) return RubyFloat.newFloat(context.runtime, negative ? -0.0 : 0.0); + if (exponent >= Integer.MAX_VALUE) return getConstant(negative ? CONST_MINUS_INFINITY : CONST_INFINITY); + if (exponent <= Integer.MIN_VALUE) return RubyFloat.newFloat(context.runtime, negative ? -0.0 : 0.0); // Ryu has rounding issues with subnormals around 1e-310 (< 2.225e-308) if (mantissa_digits > 17 || mantissa_digits + exponent < -307) { diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb index 75e99904d..47d84b398 100644 --- a/test/json/json_parser_test.rb +++ b/test/json/json_parser_test.rb @@ -147,6 +147,12 @@ def test_parse_numbers assert_raise(ParserError) { parse('-Infinity') } assert_equal(-1.0/0, parse('-Infinity', allow_nan: true)) capture_output { assert_equal(Float::INFINITY, parse("23456789012E666")) } + + assert_parse_float "1.5e-99999999999999999999" + assert_parse_float "1.5e-9223372036854775808" + assert_parse_float "0.0e-99999999999999999999" + assert_parse_float "-1.5e-99999999999999999999" + assert_parse_float "123.456e-18446744073709551615" end INTEGER_FAST_PATH_BOUNDARIES = [ @@ -1084,6 +1090,10 @@ def encode(*) private + def assert_parse_float(float_str) + assert_equal Float(float_str), JSON.parse(float_str) + end + def assert_equal_float(expected, actual, delta = 1e-2) Array === expected and expected = expected.first Array === actual and actual = actual.first From 78d6a83a3cdbc8c0fea6112d89cfe9d34164e733 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 12 Oct 2026 16:27:22 +0200 Subject: [PATCH 0996/1010] parser.c: only update the stack once fully spilled --- ext/json/ext/parser/parser.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index d86c347ad..b456a4e23 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -323,14 +323,17 @@ static const rb_data_type_t JSON_Parser_rvalue_stack_type = { static rvalue_stack *rvalue_stack_spill(rvalue_stack *old_stack, VALUE *handle, rvalue_stack **stack_ref) { rvalue_stack *stack; - *handle = TypedData_Make_Struct(0, rvalue_stack, &JSON_Parser_rvalue_stack_type, stack); - *stack_ref = stack; + VALUE stack_handle = TypedData_Make_Struct(0, rvalue_stack, &JSON_Parser_rvalue_stack_type, stack); MEMCPY(stack, old_stack, rvalue_stack, 1); stack->capa = old_stack->capa << 1; stack->ptr = ALLOC_N(VALUE, stack->capa); stack->type = RVALUE_STACK_HEAP_ALLOCATED; MEMCPY(stack->ptr, old_stack->ptr, VALUE, old_stack->head); + + *handle = stack_handle; + *stack_ref = stack; + return stack; } From e19f7d75a613ecd3f4fd7033c06925bd8c982cfe Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 12 Oct 2026 16:37:32 +0200 Subject: [PATCH 0997/1010] Handle invalid encoding in JSON path keys --- lib/json/common.rb | 5 ++++- test/json/json_parser_test.rb | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/json/common.rb b/lib/json/common.rb index 01a4be18c..4b2d6d87f 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -171,7 +171,10 @@ def build_json_path(segments) when Integer "[#{segment}]" when String, Symbol - if segment.match?(/\A[a-zA-Z\$\_][a-zA-Z\$\_0-9]*\z/) + if !segment.valid_encoding? + segment = segment.b.gsub(/["\\]/n, { '"' => '\\"', '\\' => '\\\\' }).force_encoding(Encoding::UTF_8) + %{["#{segment}"]} + elsif segment.match?(/\A[a-zA-Z\$\_][a-zA-Z\$\_0-9]*\z/) ".#{segment}" else segment = segment.to_s.gsub(/["\\]/, { '"' => '\\"', '\\' => '\\\\' }) diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb index 47d84b398..3607be33a 100644 --- a/test/json/json_parser_test.rb +++ b/test/json/json_parser_test.rb @@ -931,6 +931,10 @@ def test_parse_error_json_path assert_parse_error_at "$[4]", '[1,2,3,4,5' assert_parse_error_at "$[5]", '[1,2,3,4,5,' assert_parse_error_at "$[5]", '[1,2,3,4,5,]' + + if RUBY_ENGINE != "jruby" + assert_parse_error_at %($["\xff"][1]), %({"\xff":[1,x]}) + end end def test_parse_error_position From d09e383d19bb84d6209aa00999476eaeb4ccbd53 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Tue, 13 Oct 2026 09:15:24 +0200 Subject: [PATCH 0998/1010] Refactor `rfc8785_sort_keys_proc` --- lib/json/ext.rb | 86 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 26 deletions(-) diff --git a/lib/json/ext.rb b/lib/json/ext.rb index 7d7b09b27..75d0e4e3d 100644 --- a/lib/json/ext.rb +++ b/lib/json/ext.rb @@ -38,6 +38,56 @@ def shareable_lambda(block) # :nodoc: block end end + + def valid_encoding?(str) # :nodoc: + str.ascii_only? || (str.encoding == Encoding::UTF_8 && str.valid_encoding?) + end + + def coerce_key(key, as_json) # :nodoc: + as_json_called = false + + unless String === key || Symbol === key + key = as_json.call(key, true) + as_json_called = true + end + + if Symbol === key + key = key.to_s + end + + unless String === key + raise GeneratorError.new("#{key.class} not allowed as object key in JSON", key) + end + + unless valid_encoding?(key) + if as_json_called + raise GeneratorError.new("source sequence is illegal/malformed utf-8", key) + end + + key = as_json.call(key, true) + unless String === key + raise GeneratorError.new("#{key.class} not allowed as object key in JSON", key) + end + + unless Ext.valid_encoding?(key) + raise GeneratorError.new("source sequence is illegal/malformed utf-8", key) + end + end + + key + end + + def on_duplicate_key(hash, as_json) + set = {} + hash.each_key do |key| + key = coerce_key(key, as_json) + if set[key] + raise GeneratorError.new("detected duplicate key #{key.inspect}", key) + else + set[key] = true + end + end + end end require 'json/ext/parser' @@ -101,33 +151,17 @@ def shareable_lambda(block) # :nodoc: }) generator::State.rfc8785_sort_keys_proc = shareable_lambda(->(hash, as_json) { - if as_json - converted = {} - hash.each do |key, value| - string = (String === key || Symbol === key) ? key.to_s : nil - unless string && (string.ascii_only? || (string.encoding == Encoding::UTF_8 && string.valid_encoding?)) - key = as_json.call(key, true) - unless String === key || Symbol === key - raise GeneratorError.new("#{key.class} not allowed as object key in JSON", key) - end - begin - string = key.to_s.encode(Encoding::UTF_8) - rescue EncodingError => error - raise GeneratorError.new(error.message, key) - end - unless string.valid_encoding? - raise GeneratorError.new("source sequence is illegal/malformed utf-8", key) - end - key = string - end - if converted.key?(key) - raise GeneratorError.new("detected duplicate key #{key.inspect}", key) - end - converted[key] = value - end - hash = converted + converted = if as_json + hash.transform_keys { |k| Ext.coerce_key(k, as_json) } + else + hash.transform_keys(&:to_s) end - hash.sort_by { |k,| k.to_s.encode(Encoding::UTF_16) }.to_h + + if converted.size < hash.size + Ext.on_duplicate_key(hash, as_json) + end + + converted.sort_by { |k,| k.encode(Encoding::UTF_16) }.to_h }) JSON.generator = generator From a15b2ba603bf4692b9066dcc884c96f8984777e1 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Tue, 13 Oct 2026 10:03:21 +0200 Subject: [PATCH 0999/1010] Release 3.1.0 --- CHANGES.md | 7 +++++++ lib/json/version.rb | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 9ef0ce94a..d7c7ad118 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,14 @@ ### Unreleased +### 2026-10-13 (3.1.0) + +* Added `rfc8785` generator option to canonicalize the output. Replaces the `json-canonicalization` with numerous correctness fixes. +* Use the `rb_len_t` type introduced in Ruby 4.1 to allow for proper 64bit support on LP64 platforms (Windows). * JRuby: parser errors now include the position (`line`, `column` and the message suffix) as well as `json_path`, matching the C extension. +* Improved the accuracy of parser error's `line`, `column` and snipets in presence of multibyte characters. +* Fix `JSON.load` to work correctly when provided a `Zlib::GzipReader` as IO source. +* Reject non-ASCII BINARY strings during JSON generation. This was deprecated since a long time and scheduled to be removed for 3.0. ### 2026-09-09 (3.0.2) diff --git a/lib/json/version.rb b/lib/json/version.rb index 318272def..4d37baaa7 100644 --- a/lib/json/version.rb +++ b/lib/json/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module JSON - VERSION = '3.0.2' + VERSION = '3.1.0' end From faaccd3785ddbcbaf6d35855c997a8dff4066f78 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 13 Oct 2026 19:19:14 +0900 Subject: [PATCH 1000/1010] json: Use SSE2 on x86-64 without checking at runtime __builtin_cpu_supports refers to __cpu_model in compiler-rt, which clang-cl does not link, so generator.so and parser.so failed with an undefined symbol. SSE2 is part of the x86-64 baseline, as NEON is for AArch64, where no check is made either. Co-Authored-By: Claude Opus 5.5 --- ext/json/ext/simd/simd.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ext/json/ext/simd/simd.h b/ext/json/ext/simd/simd.h index 611b41b06..721972c9e 100644 --- a/ext/json/ext/simd/simd.h +++ b/ext/json/ext/simd/simd.h @@ -187,12 +187,8 @@ ALWAYS_INLINE(static) TARGET_SSE2 int string_scan_simd_sse2(const char **ptr, co static inline SIMD_Implementation find_simd_implementation(void) { - // TODO Revisit. I think the SSE version now only uses SSE2 instructions. - if (__builtin_cpu_supports("sse2")) { - return SIMD_SSE2; - } - - return SIMD_NONE; + // SSE2 is part of the x86-64 baseline. + return SIMD_SSE2; } #endif /* HAVE_X86INTRIN_H */ From fae76b9eb4723a32dc91d06edbf32248a6472622 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 15 Oct 2026 13:49:51 +0900 Subject: [PATCH 1001/1010] Drop the leftovers of runtime SSE2 detection Neither the `__builtin_cpu_supports` probe in conf.rb nor `cpuid.h` has a use now that SSE2 is assumed on x86-64. The probe kept SIMD off on GCC older than 4.8, and the `HAVE_CPUID_H` guard broke builds that have `x86intrin.h` but not `cpuid.h` with a redefinition of `find_simd_implementation`. Co-Authored-By: Claude Opus 5.5 --- ext/json/ext/simd/conf.rb | 7 ++----- ext/json/ext/simd/simd.h | 4 ---- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/ext/json/ext/simd/conf.rb b/ext/json/ext/simd/conf.rb index 76f774bc9..9b87473d5 100644 --- a/ext/json/ext/simd/conf.rb +++ b/ext/json/ext/simd/conf.rb @@ -1,16 +1,15 @@ case RbConfig::CONFIG['host_cpu'] when /^(arm|aarch64)/ # Try to compile a small program using NEON instructions - header, type, init, extra = 'arm_neon.h', 'uint8x16_t', 'vdupq_n_u8(32)', nil + header, type, init = 'arm_neon.h', 'uint8x16_t', 'vdupq_n_u8(32)' when /^(x86_64|x64)/ - header, type, init, extra = 'x86intrin.h', '__m128i', '_mm_set1_epi8(32)', 'if (__builtin_cpu_supports("sse2")) { printf("OK"); }' + header, type, init = 'x86intrin.h', '__m128i', '_mm_set1_epi8(32)' end if header if have_header(header) && try_compile(<<~SRC, '-Werror=implicit-function-declaration') #{cpp_include(header)} int main(int argc, char **argv) { #{type} test = #{init}; - #{extra} if (argc > 100000) printf("%p", &test); return 0; } @@ -20,5 +19,3 @@ puts "Disable SIMD" end end - -have_header('cpuid.h') diff --git a/ext/json/ext/simd/simd.h b/ext/json/ext/simd/simd.h index 721972c9e..2f3f838a9 100644 --- a/ext/json/ext/simd/simd.h +++ b/ext/json/ext/simd/simd.h @@ -143,7 +143,6 @@ ALWAYS_INLINE(static) int string_scan_simd_neon(const char **ptr, const char *en #define HAVE_SIMD 1 #define HAVE_SIMD_SSE2 1 -#ifdef HAVE_CPUID_H #define FIND_SIMD_IMPLEMENTATION_DEFINED 1 #if defined(__clang__) || defined(__GNUC__) @@ -182,9 +181,6 @@ ALWAYS_INLINE(static) TARGET_SSE2 int string_scan_simd_sse2(const char **ptr, co return 0; } -#include -#endif /* HAVE_CPUID_H */ - static inline SIMD_Implementation find_simd_implementation(void) { // SSE2 is part of the x86-64 baseline. From c839870678fd7506eede270b24fbf3484053b361 Mon Sep 17 00:00:00 2001 From: Sebastian Turner Date: Mon, 19 Oct 2026 08:47:29 -0700 Subject: [PATCH 1002/1010] Fix ParserError#json_path with symbolize_names: true Signed-off-by: Sebastian Turner --- CHANGES.md | 2 ++ lib/json/common.rb | 3 ++- test/json/json_parser_test.rb | 12 ++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index d7c7ad118..1e1b5a9ed 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,8 @@ ### Unreleased +* Fix `JSON::ParserError#json_path` raising `NoMethodError` when the document was parsed with `symbolize_names: true`. + ### 2026-10-13 (3.1.0) * Added `rfc8785` generator option to canonicalize the output. Replaces the `json-canonicalization` with numerous correctness fixes. diff --git a/lib/json/common.rb b/lib/json/common.rb index 4b2d6d87f..bf96f4137 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -171,13 +171,14 @@ def build_json_path(segments) when Integer "[#{segment}]" when String, Symbol + segment = segment.to_s if !segment.valid_encoding? segment = segment.b.gsub(/["\\]/n, { '"' => '\\"', '\\' => '\\\\' }).force_encoding(Encoding::UTF_8) %{["#{segment}"]} elsif segment.match?(/\A[a-zA-Z\$\_][a-zA-Z\$\_0-9]*\z/) ".#{segment}" else - segment = segment.to_s.gsub(/["\\]/, { '"' => '\\"', '\\' => '\\\\' }) + segment = segment.gsub(/["\\]/, { '"' => '\\"', '\\' => '\\\\' }) %{["#{segment}"]} end else diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb index 3607be33a..3f8575c8a 100644 --- a/test/json/json_parser_test.rb +++ b/test/json/json_parser_test.rb @@ -1004,6 +1004,18 @@ def test_parse_error_json_path_key_escaping assert_parse_error_at '$.foo["1x"]', '{"foo": {"1x": xyz}}' end + def test_parse_error_json_path_symbolize_names + assert_parse_error_at "$.a[1].b" do + JSON.parse('{"a": [1, {"b": xyz}]}', symbolize_names: true) + end + assert_parse_error_at '$["hello world"]' do + JSON.parse('{"hello world": xyz}', symbolize_names: true) + end + assert_parse_error_at "$.x.a" do + JSON.parse('{"x": {"a": 1, "a": 2}}', symbolize_names: true) + end + end + def test_parse_error_json_path_duplicate_key assert_parse_error_at "$.a", '{"a": 1, "a": 2}' assert_parse_error_at "$.x.a", '{"x": {"a": 1, "b": 2, "a": 3}}' From 9d783a28903539d5e3a0fac18407d3875c10120c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Barri=C3=A9?= Date: Mon, 2 Nov 2026 15:00:55 +0100 Subject: [PATCH 1003/1010] Fix RFC tests not running Because of the trailing __FILE__, no files were matched and the block didn't run, which was hiding the error in the block defining the test (`output` local variable not existing). --- test/json/json_generator_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index 7cf76c958..8de5f80f9 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -1490,12 +1490,12 @@ def test_rfc8785_numbers end fixtures_path = File.expand_path('../fixtures/rfc8785/', __FILE__) - Dir[File.join(fixtures_path, "input/*.json", __FILE__)].each do |input| + Dir[File.join(fixtures_path, "input/*.json")].each do |input| filename = File.basename(input) name, _ = File.basename(filename, ".json") expected = File.join(fixtures_path, 'output', filename) define_method("test_rfc8785_#{name}") do - assert_rfc8785(File.read(output), JSON.load_file(input)) + assert_rfc8785(File.read(expected), JSON.load_file(input)) end end From 4b431671b1506b01b10d9275f829f33ed26f0395 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 2 Nov 2026 15:54:41 +0100 Subject: [PATCH 1004/1010] Update changelog with 2.18.2 release --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 1e1b5a9ed..079fd0c49 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -140,6 +140,11 @@ Numerous rarely used aliases have been removed. * Fix `allow_blank` parsing option to no longer allow invalid types (e.g. `load([], allow_blank: true)` now raise a type error). * Add `allow_invalid_escape` parsing option to ignore backslashes that aren't followed by one of the valid escape characters. +### 2026-11-02 (2.18.2) + +* Backport handling of unescaped control characters preceeded by a backslash from 2.19.3. + GHSA-cjpr-jw2g-fw99. + ### 2026-02-03 (2.18.1) * Fix a potential crash in very specific circumstance if GC triggers during a call to `to_json` From dc731b3a767c73b66551b26922bb13a00c7785b5 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 3 Nov 2026 10:30:18 +0900 Subject: [PATCH 1005/1010] Test on macos-15 instead of the retired macos-14 GitHub Actions retired the macOS 14 images on 2026-11-02 (actions/runner-images#13518). macos-15 keeps the arm64 job one release behind macos-latest, which is now macOS 26. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d98aa5715..1d8d18e21 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: - { os: ubuntu-24.04-arm, ruby: 3.4 } - { os: ubuntu-latest , ruby: 3.4, env: "JSON_DISABLE_SIMD=1" } - { os: ubuntu-latest , ruby: 3.4, env: "JSON_DEBUG=1" } - - { os: macos-14, ruby: 3.4 } + - { os: macos-15, ruby: 3.4 } - { os: windows-latest , ruby: mswin } # ruby/ruby windows CI - { os: macos-latest , ruby: jruby-9.4 } # Ruby 3.1 - { os: ubuntu-latest , ruby: jruby-9.4 } # Ruby 3.1 From 1c67ef2aa9aaf412d37b808f9923dc4879c732c4 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 3 Nov 2026 11:46:08 +0900 Subject: [PATCH 1006/1010] Fix failures on non-UTF-8 environments --- test/json/json_generator_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index 8de5f80f9..1d9318fd5 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -1495,7 +1495,7 @@ def test_rfc8785_numbers name, _ = File.basename(filename, ".json") expected = File.join(fixtures_path, 'output', filename) define_method("test_rfc8785_#{name}") do - assert_rfc8785(File.read(expected), JSON.load_file(input)) + assert_rfc8785(File.read(expected, encoding: Encoding::UTF_8), JSON.load_file(input)) end end From 0292a445dfdbc450683c782275dd95c6b81e8bf8 Mon Sep 17 00:00:00 2001 From: Sampo Kuokkanen Date: Wed, 4 Nov 2026 21:31:14 +0900 Subject: [PATCH 1007/1010] Java parser: don't let the caller mutate the bytes being parsed ParserSession reads the source string's backing byte[] directly, but the string itself was never marked as shared. A decimal_class callback that calls source.tr! could then rewrite the bytes the parser is still reading. The existing test_mutating_source_string_during_parsing only passes on JRuby by accident: with an unknown coderange String#tr! happens to build a new buffer. Once the coderange is known (for example after source.ascii_only?), tr! writes in place and the parser sees the change. Freeze a copy of the source before parsing, like rb_str_new_frozen in parser.c. RubyString#newFrozen shares the ByteList and marks the original as shared, so a later mutation copies first. --- java/src/json/ext/Parser.java | 5 ++++- test/json/json_parser_test.rb | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/java/src/json/ext/Parser.java b/java/src/json/ext/Parser.java index 6fa5d4383..eb5ce3a9a 100644 --- a/java/src/json/ext/Parser.java +++ b/java/src/json/ext/Parser.java @@ -164,7 +164,10 @@ private RubyString convertEncoding(ThreadContext context, RubyString source) { */ @JRubyMethod public IRubyObject parse(ThreadContext context, IRubyObject source) { - return new ParserSession(this, convertEncoding(context, source.convertToString()), info).parse(context); + // Like rb_str_new_frozen in parser.c: the caller's string becomes shared, so mutating it during parsing + // (e.g. from a decimal_class callback) copies its bytes instead of changing the ones being parsed. + RubyString frozenSource = convertEncoding(context, source.convertToString()).newFrozen(); + return new ParserSession(this, frozenSource, info).parse(context); } private RubyFloat createFloat(final ThreadContext context, final ByteList num) { diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb index 3f8575c8a..0549f6f4c 100644 --- a/test/json/json_parser_test.rb +++ b/test/json/json_parser_test.rb @@ -1069,6 +1069,23 @@ def test_mutating_source_string_during_parsing assert_equal expected, actual end + def test_mutating_source_string_with_known_coderange_during_parsing + expected = ([1] * 100) + [2.3] + ([1] * 100) + source = JSON.generate(expected) + source.ascii_only? # caches the coderange, which lets JRuby's String#tr! write in place + expected.delete_at(100) + + fake_decimal_class = Class.new + fake_decimal_class.define_method(:initialize) do |number| + source.tr!('1', '0') + number.to_f + end + + actual = JSON.parse(source, decimal_class: fake_decimal_class) + actual.delete_at(100) + assert_equal expected, actual + end + def test_adversary_encode fake_string = Class.new(String) do def encode(*) From bbb92b5f871d2e6a293c1c3f454f1d8fa151215a Mon Sep 17 00:00:00 2001 From: Stephan Kaag <209887080+stephanmoneybird@users.noreply.github.com> Date: Thu, 5 Nov 2026 13:59:37 +0100 Subject: [PATCH 1008/1010] Check duplicate keys when a non String/Symbol key follows a String/Symbol key Fix: #1125 Co-Authored-By: Jean Boussier --- CHANGES.md | 1 + ext/json/ext/generator/generator.c | 3 +++ lib/json/truffle_ruby/generator.rb | 12 ++++++++++-- test/json/json_generator_test.rb | 25 +++++++++++++++++++++++-- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 079fd0c49..f405db416 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ ### Unreleased * Fix `JSON::ParserError#json_path` raising `NoMethodError` when the document was parsed with `symbolize_names: true`. +* Fix `JSON.generate` not detecting duplicate keys such as `{ "1" => 1, 1 => 2 }`. ### 2026-10-13 (3.1.0) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 38c88f231..87da10027 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -1020,6 +1020,9 @@ json_object_i(VALUE key, VALUE val, VALUE _arg) raise_generator_error(key, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(key)); } } + + json_inspect_hash_with_mixed_keys(arg); + key_to_s = rb_convert_type(key, T_STRING, "String", "to_s"); break; } diff --git a/lib/json/truffle_ruby/generator.rb b/lib/json/truffle_ruby/generator.rb index 1cacd76c5..f73708f2d 100644 --- a/lib/json/truffle_ruby/generator.rb +++ b/lib/json/truffle_ruby/generator.rb @@ -451,7 +451,11 @@ def generate(obj, anIO = nil) end obj.each_pair do |k,v| if first - key_type = k.class + if ::String == k.class || ::Symbol == k.class + key_type = k.class + else + JSON.send(:on_mixed_keys_hash, obj) + end else if key_type && !@allow_duplicate_key && key_type != k.class key_type = nil # stop checking @@ -592,7 +596,11 @@ def json_transform(state) hash.each { |key, value| if first - key_type = key.class + if ::String == key.class || ::Symbol == key.class + key_type = key.class + else + JSON.send(:on_mixed_keys_hash, hash) + end else if key_type && !state.allow_duplicate_key? && key_type != key.class key_type = nil # stop checking diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index 1d9318fd5..bfc324104 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -1202,11 +1202,32 @@ def test_generate_duplicate_keys_allowed end def test_generate_duplicate_keys_disallowed - hash = { foo: 1, "foo" => 2 } + assert_duplicate_key({ foo: 1, "foo" => 2 }) + assert_duplicate_key({ "1" => 1, 1 => 2 }) + assert_duplicate_key({ :"1" => 1, 1 => 2 }) + + fake_string = Class.new do + def initialize(str) + @str = str + end + + def to_s + @str.dup + end + end + + assert_duplicate_key({ fake_string.new("a") => 1, fake_string.new("a") => 2 }) + end + + def assert_duplicate_key(hash) error = assert_raise JSON::GeneratorError do JSON.generate(hash, allow_duplicate_key: false) end - assert_equal %(detected duplicate key "foo" in #{hash.inspect}), error.message + assert_equal %(detected duplicate key "#{hash.keys.first}" in #{hash.inspect}), error.message + end + + def test_generate_mixed_keys_without_duplicates + assert_equal %({"1":1,"2":2}), JSON.generate({ "1" => 1, 2 => 2 }, allow_duplicate_key: false) end def test_frozen From 8bda05413e5f154da639740476a55d154e628912 Mon Sep 17 00:00:00 2001 From: Erik Berlin Date: Thu, 5 Nov 2026 11:58:48 -0800 Subject: [PATCH 1009/1010] Run the RFC 8785 number tests on JRuby 10.1.3 and later The RFC 8785 number formatter uses Float#to_s, so these tests were skipped on every engine but CRuby. JRuby 10.1.3 gives the shortest round-trip digits in Float#to_s (https://github.com/jruby/jruby/pull/9916), and all 26 tests pass there. Older JRuby versions print the smallest subnormal as 4.9e-324, so they still skip them. Part of https://github.com/ruby/json/issues/1123 --- test/json/json_generator_test.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index bfc324104..ca1af4c4d 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -1469,6 +1469,11 @@ def test_rfc8785_numbers assert_rfc8785 '0.30000000000000004', 0.1 + 0.2 end + # RFC 8785 numbers are formatted with Float#to_s, which only gives the + # shortest round-trip digits on JRuby 10.1.3 and later. + SHORTEST_FLOAT_TO_S = RUBY_ENGINE == "ruby" || + (RUBY_ENGINE == "jruby" && Gem::Version.new(JRUBY_VERSION) >= Gem::Version.new("10.1.3")) + # https://www.rfc-editor.org/rfc/rfc8785.html#appendix-B { '0000000000000000' => '0', @@ -1499,7 +1504,7 @@ def test_rfc8785_numbers '43143ff3c1cb0959' => '1424953923781206.2', }.each do |bits, expected| define_method("test_rfc8785_number_#{bits}") do - omit "Float#to_s is not always shortest on this engine" unless RUBY_ENGINE == "ruby" + omit "Float#to_s is not always shortest on this engine" unless SHORTEST_FLOAT_TO_S number = [bits].pack('H*').unpack('G').first if expected assert_rfc8785 expected, number From cf987b116dadba97960de61d9fbde9022c956dcf Mon Sep 17 00:00:00 2001 From: Erik Berlin Date: Thu, 5 Nov 2026 12:11:57 -0800 Subject: [PATCH 1010/1010] Java generator: raise TypeError for a String key whose to_s is broken When a Hash key is a String subclass, both generators call its to_s. generator.c raises a TypeError in convert_string_subclass if to_s doesn't return a String. The Java generator passed the result on, then fell back to TypeConverter.convertToType, which returned the key itself because it already is a String. So JSON.generate(s => 1) produced {"test":1} on JRuby instead of raising. Raise the same TypeError, with the same message, in castKey. The class names go through to_s like PRIsVALUE does, so anonymous classes show as # the way they do on CRuby. This lets test_string_subclass_with_broken_to_s run on JRuby. Part of https://github.com/ruby/json/issues/1123 --- CHANGES.md | 1 + java/src/json/ext/Generator.java | 9 ++++++++- test/json/json_generator_test.rb | 2 -- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f405db416..2bad52aed 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,7 @@ * Fix `JSON::ParserError#json_path` raising `NoMethodError` when the document was parsed with `symbolize_names: true`. * Fix `JSON.generate` not detecting duplicate keys such as `{ "1" => 1, 1 => 2 }`. +* JRuby: raise `TypeError` when a String subclass used as a Hash key has a `to_s` that doesn't return a String, like the C extension. ### 2026-10-13 (3.1.0) diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index 3b05812f5..750be6bf6 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -631,7 +631,14 @@ private static IRubyObject castKey(ThreadContext context, IRubyObject key) { if (keyClass == runtime.getString()) { return key; } else { - return key.callMethod(context, "to_s"); + // C: convert_string_subclass + IRubyObject keyStr = key.callMethod(context, "to_s"); + if (!(keyStr instanceof RubyString)) { + IRubyObject className = keyClass.getRealClass().callMethod(context, "to_s"); + throw runtime.newTypeError("can't convert " + className + " to String (" + + className + "#to_s gives " + keyStr.getMetaClass().getRealClass().callMethod(context, "to_s") + ")"); + } + return keyStr; } } else if (keyClass == runtime.getSymbol()) { return ((RubySymbol) key).id2name(context); diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index ca1af4c4d..c4b1a8ace 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -1054,8 +1054,6 @@ def to_s s = klass.new("test") assert_equal '["test"]', JSON.generate([s]) - omit("Can't figure out how to match behavior in java code") if RUBY_PLATFORM == "java" - assert_raise TypeError do JSON.generate(s => 1) end