[DRE-commits] [ruby-json] 01/01: Imported Upstream 2.1.0

Cédric Boutillier boutil at moszumanska.debian.org
Tue Jul 18 21:57:23 UTC 2017


This is an automated email from the git hooks/post-receive script.

boutil pushed a commit to annotated tag upstream/2.1.0
in repository ruby-json.

commit 4e744120db453a2b0a0252a13f9bf4c792bf2e5c
Author: Cédric Boutillier <boutil at debian.org>
Date:   Fri Jul 7 01:18:54 2017 +0200

    Imported Upstream 2.1.0
---
 .gitignore                          |   1 +
 .travis.yml                         |   9 +-
 CHANGES.md                          |  21 +++-
 Gemfile                             |  13 ++-
 README.md                           |   6 +-
 Rakefile                            |  19 ++--
 VERSION                             |   2 +-
 ext/json/ext/fbuffer/fbuffer.h      |   3 -
 ext/json/ext/generator/generator.c  |  13 +--
 ext/json/ext/generator/generator.h  |   1 -
 ext/json/ext/parser/parser.c        | 186 +++++++++++++++++++++---------------
 ext/json/ext/parser/parser.h        |   1 +
 ext/json/ext/parser/parser.rl       |  40 ++++++--
 java/src/json/ext/Parser.java       | 172 ++++++++++++++++++---------------
 java/src/json/ext/Parser.rl         |  36 +++++--
 json.gemspec                        | Bin 5473 -> 5474 bytes
 json_pure.gemspec                   |   9 +-
 lib/json/add/ostruct.rb             |   2 +-
 lib/json/pure/parser.rb             |   6 +-
 lib/json/version.rb                 |   2 +-
 tests/json_common_interface_test.rb |   6 +-
 tests/json_encoding_test.rb         |   2 +
 tests/json_generator_test.rb        |   5 +-
 tests/json_parser_test.rb           |  25 ++++-
 tests/test_helper.rb                |   2 -
 25 files changed, 362 insertions(+), 220 deletions(-)

diff --git a/.gitignore b/.gitignore
index 5ef62af..69b608d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,4 @@ Gemfile.lock
 .DS_Store
 */**/Makefile
 */**/*.o
+.byebug_history
diff --git a/.travis.yml b/.travis.yml
index 49f9529..e2eeda5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,17 +4,16 @@ 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
-  - 2.3.1
+  - 2.3.3
+  - 2.4.1
+  - jruby
   - ruby-head
 matrix:
-  include:
-    - rvm: jruby
-      env: JRUBY_OPTS="--2.0"
   allow_failures:
-    - rvm: rbx-2
     - rvm: ruby-head
 script: "bundle exec rake"
 sudo: false
diff --git a/CHANGES.md b/CHANGES.md
index 89326f3..d96e9ae 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,7 +1,24 @@
 # Changes
 
+## 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 defiend top
+  * 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.
@@ -28,7 +45,7 @@
     `JSON.dump_default_options`.
   * More tests by Michael Mac-Vicar <mmacvicar at gmail.com> and fixing
     `space_before` accessor in generator.
-  * Performance on Jruby improvemed by Ben Browning <bbrownin at redhat.com>.
+  * Performance on Jruby improved by Ben Browning <bbrownin at redhat.com>.
   * Some fixes to be compatible with the new Ruby 2.2 by Zachary Scott <e at zzak.io>
     and SHIBATA Hiroshi <hsbt at ruby-lang.org>.
 
diff --git a/Gemfile b/Gemfile
index 51ff10a..64f98d6 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,8 +2,15 @@
 
 source 'https://rubygems.org'
 
-gemspec :name => 'json'
-gemspec :name => 'json_pure'
-gemspec :name => 'json-java'
+case ENV['JSON']
+when 'ext', nil
+  if ENV['RUBY_ENGINE'] == 'jruby'
+    gemspec :name => 'json-java'
+  else
+    gemspec :name => 'json'
+  end
+when 'pure'
+  gemspec :name => 'json_pure'
+end
 
 gem 'simplecov'
diff --git a/README.md b/README.md
index 5691f3e..f9cd95d 100644
--- a/README.md
+++ b/README.md
@@ -3,8 +3,8 @@
 
 ## Description
 
-This is a implementation of the JSON specification according to RFC 4627
-http://www.ietf.org/rfc/rfc4627.txt . Starting from version 1.0.0 on there
+This is a implementation of the JSON specification according to RFC 7159
+http://www.ietf.org/rfc/rfc7159.txt . Starting from version 1.0.0 on there
 will be two variants available:
 
 * A pure ruby variant, that relies on the iconv and the stringscan
@@ -115,7 +115,7 @@ generate a JSON document from an array or hash:
 
 ```ruby
 document = JSON 'test'  => 23 # => "{\"test\":23}"
-document = JSON['test'] => 23 # => "{\"test\":23}"
+document = JSON['test' => 23] # => "{\"test\":23}"
 ```
 
 and
diff --git a/Rakefile b/Rakefile
index 958dfa6..c6c195f 100644
--- a/Rakefile
+++ b/Rakefile
@@ -95,6 +95,7 @@ if defined?(Gem) and defined?(Gem::PackageTask)
     s.email = "flori at ping.de"
     s.homepage = "http://flori.github.com/#{PKG_NAME}"
     s.license = 'Ruby'
+    s.required_ruby_version = '>= 1.9'
   end
 
   desc 'Creates a json_pure.gemspec file'
@@ -132,7 +133,7 @@ if defined?(Gem) and defined?(Gem::PackageTask)
     s.email = "flori at ping.de"
     s.homepage = "http://flori.github.com/#{PKG_NAME}"
     s.license = 'Ruby'
-    s.required_ruby_version = '~> 2.0'
+    s.required_ruby_version = '>= 1.9'
   end
 
   desc 'Creates a json.gemspec file'
@@ -199,13 +200,11 @@ namespace :gems do
 end
 
 if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
-  if ENV.key?('JAVA_HOME')
-    warn " *** JAVA_HOME was set to #{ENV['JAVA_HOME'].inspect}"
-  elsif File.directory?(local_java = '/usr/local/java/jdk') ||
-    File.directory?(local_java = '/usr/lib/jvm/java-6-openjdk')
-  then
-    ENV['JAVA_HOME'] = local_java
-  end
+  ENV['JAVA_HOME'] ||= [
+    '/usr/local/java/jdk',
+    '/usr/lib/jvm/java-6-openjdk',
+    '/Library/Java/Home',
+  ].find { |c| File.directory?(c) }
   if ENV['JAVA_HOME']
     warn " *** JAVA_HOME is set to #{ENV['JAVA_HOME'].inspect}"
     ENV['PATH'] = ENV['PATH'].split(/:/).unshift(java_path = "#{ENV['JAVA_HOME']}/bin") * ':'
@@ -258,7 +257,7 @@ if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
   end
 
   desc "Testing library (jruby)"
-  task :test_ext => [ :create_jar, :check_env, :do_test_ext ]
+  task :test_ext => [ :check_env, :create_jar, :do_test_ext ]
 
   UndocumentedTestTask.new do |t|
     t.name = 'do_test_ext'
@@ -332,7 +331,7 @@ else
   end
 
   desc "Testing library (extension)"
-  task :test_ext => [ :compile, :check_env, :do_test_ext ]
+  task :test_ext => [ :check_env, :compile, :do_test_ext ]
 
   UndocumentedTestTask.new do |t|
     t.name = 'do_test_ext'
diff --git a/VERSION b/VERSION
index 38f77a6..7ec1d6d 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.0.1
+2.1.0
diff --git a/ext/json/ext/fbuffer/fbuffer.h b/ext/json/ext/fbuffer/fbuffer.h
index 5a0a27c..dc8f406 100644
--- a/ext/json/ext/fbuffer/fbuffer.h
+++ b/ext/json/ext/fbuffer/fbuffer.h
@@ -12,9 +12,6 @@
 #define RFLOAT_VALUE(val) (RFLOAT(val)->value)
 #endif
 
-#ifndef RARRAY_PTR
-#define RARRAY_PTR(ARRAY) RARRAY(ARRAY)->ptr
-#endif
 #ifndef RARRAY_LEN
 #define RARRAY_LEN(ARRAY) RARRAY(ARRAY)->len
 #endif
diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c
index 2b56721..2bf8074 100644
--- a/ext/json/ext/generator/generator.c
+++ b/ext/json/ext/generator/generator.c
@@ -308,7 +308,7 @@ static char *fstrndup(const char *ptr, unsigned long len) {
   char *result;
   if (len <= 0) return NULL;
   result = ALLOC_N(char, len);
-  memccpy(result, ptr, 0, len);
+  memcpy(result, ptr, len);
   return result;
 }
 
@@ -951,6 +951,7 @@ static VALUE cState_generate(VALUE self, VALUE obj)
 {
     VALUE result = cState_partial_generate(self, obj);
     GET_STATE(self);
+    (void)state;
     return result;
 }
 
@@ -1061,7 +1062,7 @@ static VALUE cState_indent_set(VALUE self, VALUE indent)
         }
     } else {
         if (state->indent) ruby_xfree(state->indent);
-        state->indent = strdup(RSTRING_PTR(indent));
+        state->indent = fstrndup(RSTRING_PTR(indent), len);
         state->indent_len = len;
     }
     return Qnil;
@@ -1099,7 +1100,7 @@ static VALUE cState_space_set(VALUE self, VALUE space)
         }
     } else {
         if (state->space) ruby_xfree(state->space);
-        state->space = strdup(RSTRING_PTR(space));
+        state->space = fstrndup(RSTRING_PTR(space), len);
         state->space_len = len;
     }
     return Qnil;
@@ -1135,7 +1136,7 @@ static VALUE cState_space_before_set(VALUE self, VALUE space_before)
         }
     } else {
         if (state->space_before) ruby_xfree(state->space_before);
-        state->space_before = strdup(RSTRING_PTR(space_before));
+        state->space_before = fstrndup(RSTRING_PTR(space_before), len);
         state->space_before_len = len;
     }
     return Qnil;
@@ -1172,7 +1173,7 @@ static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
         }
     } else {
         if (state->object_nl) ruby_xfree(state->object_nl);
-        state->object_nl = strdup(RSTRING_PTR(object_nl));
+        state->object_nl = fstrndup(RSTRING_PTR(object_nl), len);
         state->object_nl_len = len;
     }
     return Qnil;
@@ -1207,7 +1208,7 @@ static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
         }
     } else {
         if (state->array_nl) ruby_xfree(state->array_nl);
-        state->array_nl = strdup(RSTRING_PTR(array_nl));
+        state->array_nl = fstrndup(RSTRING_PTR(array_nl), len);
         state->array_nl_len = len;
     }
     return Qnil;
diff --git a/ext/json/ext/generator/generator.h b/ext/json/ext/generator/generator.h
index 900b4d5..c367a62 100644
--- a/ext/json/ext/generator/generator.h
+++ b/ext/json/ext/generator/generator.h
@@ -1,7 +1,6 @@
 #ifndef _GENERATOR_H_
 #define _GENERATOR_H_
 
-#include <string.h>
 #include <math.h>
 #include <ctype.h>
 
diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c
index 975a267..d2e4eb6 100644
--- a/ext/json/ext/parser/parser.c
+++ b/ext/json/ext/parser/parser.c
@@ -94,15 +94,16 @@ 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_key_p, i_deep_const_get, i_match,
-          i_match_string, i_aset, i_aref, i_leftshift;
+          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;
 
 
-#line 124 "parser.rl"
+#line 125 "parser.rl"
 
 
 
-#line 106 "parser.c"
+#line 107 "parser.c"
 enum {JSON_object_start = 1};
 enum {JSON_object_first_final = 27};
 enum {JSON_object_error = 0};
@@ -110,7 +111,7 @@ enum {JSON_object_error = 0};
 enum {JSON_object_en_main = 1};
 
 
-#line 165 "parser.rl"
+#line 166 "parser.rl"
 
 
 static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
@@ -126,14 +127,14 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
     *result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class);
 
 
-#line 130 "parser.c"
+#line 131 "parser.c"
 	{
 	cs = JSON_object_start;
 	}
 
-#line 180 "parser.rl"
+#line 181 "parser.rl"
 
-#line 137 "parser.c"
+#line 138 "parser.c"
 	{
 	if ( p == pe )
 		goto _test_eof;
@@ -161,7 +162,7 @@ case 2:
 		goto st2;
 	goto st0;
 tr2:
-#line 147 "parser.rl"
+#line 148 "parser.rl"
 	{
         char *np;
         json->parsing_name = 1;
@@ -174,7 +175,7 @@ st3:
 	if ( ++p == pe )
 		goto _test_eof3;
 case 3:
-#line 178 "parser.c"
+#line 179 "parser.c"
 	switch( (*p) ) {
 		case 13: goto st3;
 		case 32: goto st3;
@@ -241,7 +242,7 @@ case 8:
 		goto st8;
 	goto st0;
 tr11:
-#line 132 "parser.rl"
+#line 133 "parser.rl"
 	{
         VALUE v = Qnil;
         char *np = JSON_parse_value(json, p, pe, &v, current_nesting);
@@ -261,7 +262,7 @@ st9:
 	if ( ++p == pe )
 		goto _test_eof9;
 case 9:
-#line 265 "parser.c"
+#line 266 "parser.c"
 	switch( (*p) ) {
 		case 13: goto st9;
 		case 32: goto st9;
@@ -350,14 +351,14 @@ case 18:
 		goto st9;
 	goto st18;
 tr4:
-#line 155 "parser.rl"
+#line 156 "parser.rl"
 	{ p--; {p++; cs = 27; goto _out;} }
 	goto st27;
 st27:
 	if ( ++p == pe )
 		goto _test_eof27;
 case 27:
-#line 361 "parser.c"
+#line 362 "parser.c"
 	goto st0;
 st19:
 	if ( ++p == pe )
@@ -455,7 +456,7 @@ case 26:
 	_out: {}
 	}
 
-#line 181 "parser.rl"
+#line 182 "parser.rl"
 
     if (cs >= JSON_object_first_final) {
         if (json->create_additions) {
@@ -480,7 +481,7 @@ case 26:
 
 
 
-#line 484 "parser.c"
+#line 485 "parser.c"
 enum {JSON_value_start = 1};
 enum {JSON_value_first_final = 29};
 enum {JSON_value_error = 0};
@@ -488,7 +489,7 @@ enum {JSON_value_error = 0};
 enum {JSON_value_en_main = 1};
 
 
-#line 281 "parser.rl"
+#line 282 "parser.rl"
 
 
 static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
@@ -496,14 +497,14 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul
     int cs = EVIL;
 
 
-#line 500 "parser.c"
+#line 501 "parser.c"
 	{
 	cs = JSON_value_start;
 	}
 
-#line 288 "parser.rl"
+#line 289 "parser.rl"
 
-#line 507 "parser.c"
+#line 508 "parser.c"
 	{
 	if ( p == pe )
 		goto _test_eof;
@@ -537,14 +538,14 @@ st0:
 cs = 0;
 	goto _out;
 tr2:
-#line 233 "parser.rl"
+#line 234 "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 238 "parser.rl"
+#line 239 "parser.rl"
 	{
         char *np;
         if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) {
@@ -564,7 +565,7 @@ tr3:
     }
 	goto st29;
 tr7:
-#line 256 "parser.rl"
+#line 257 "parser.rl"
 	{
         char *np;
         np = JSON_parse_array(json, p, pe, result, current_nesting + 1);
@@ -572,7 +573,7 @@ tr7:
     }
 	goto st29;
 tr11:
-#line 262 "parser.rl"
+#line 263 "parser.rl"
 	{
         char *np;
         np =  JSON_parse_object(json, p, pe, result, current_nesting + 1);
@@ -580,7 +581,7 @@ tr11:
     }
 	goto st29;
 tr25:
-#line 226 "parser.rl"
+#line 227 "parser.rl"
 	{
         if (json->allow_nan) {
             *result = CInfinity;
@@ -590,7 +591,7 @@ tr25:
     }
 	goto st29;
 tr27:
-#line 219 "parser.rl"
+#line 220 "parser.rl"
 	{
         if (json->allow_nan) {
             *result = CNaN;
@@ -600,19 +601,19 @@ tr27:
     }
 	goto st29;
 tr31:
-#line 213 "parser.rl"
+#line 214 "parser.rl"
 	{
         *result = Qfalse;
     }
 	goto st29;
 tr34:
-#line 210 "parser.rl"
+#line 211 "parser.rl"
 	{
         *result = Qnil;
     }
 	goto st29;
 tr37:
-#line 216 "parser.rl"
+#line 217 "parser.rl"
 	{
         *result = Qtrue;
     }
@@ -621,9 +622,9 @@ st29:
 	if ( ++p == pe )
 		goto _test_eof29;
 case 29:
-#line 268 "parser.rl"
+#line 269 "parser.rl"
 	{ p--; {p++; cs = 29; goto _out;} }
-#line 627 "parser.c"
+#line 628 "parser.c"
 	switch( (*p) ) {
 		case 13: goto st29;
 		case 32: goto st29;
@@ -864,7 +865,7 @@ case 28:
 	_out: {}
 	}
 
-#line 289 "parser.rl"
+#line 290 "parser.rl"
 
     if (cs >= JSON_value_first_final) {
         return p;
@@ -874,7 +875,7 @@ case 28:
 }
 
 
-#line 878 "parser.c"
+#line 879 "parser.c"
 enum {JSON_integer_start = 1};
 enum {JSON_integer_first_final = 3};
 enum {JSON_integer_error = 0};
@@ -882,7 +883,7 @@ enum {JSON_integer_error = 0};
 enum {JSON_integer_en_main = 1};
 
 
-#line 305 "parser.rl"
+#line 306 "parser.rl"
 
 
 static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -890,15 +891,15 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res
     int cs = EVIL;
 
 
-#line 894 "parser.c"
+#line 895 "parser.c"
 	{
 	cs = JSON_integer_start;
 	}
 
-#line 312 "parser.rl"
+#line 313 "parser.rl"
     json->memo = p;
 
-#line 902 "parser.c"
+#line 903 "parser.c"
 	{
 	if ( p == pe )
 		goto _test_eof;
@@ -932,14 +933,14 @@ case 3:
 		goto st0;
 	goto tr4;
 tr4:
-#line 302 "parser.rl"
+#line 303 "parser.rl"
 	{ p--; {p++; cs = 4; goto _out;} }
 	goto st4;
 st4:
 	if ( ++p == pe )
 		goto _test_eof4;
 case 4:
-#line 943 "parser.c"
+#line 944 "parser.c"
 	goto st0;
 st5:
 	if ( ++p == pe )
@@ -958,7 +959,7 @@ case 5:
 	_out: {}
 	}
 
-#line 314 "parser.rl"
+#line 315 "parser.rl"
 
     if (cs >= JSON_integer_first_final) {
         long len = p - json->memo;
@@ -973,7 +974,7 @@ case 5:
 }
 
 
-#line 977 "parser.c"
+#line 978 "parser.c"
 enum {JSON_float_start = 1};
 enum {JSON_float_first_final = 8};
 enum {JSON_float_error = 0};
@@ -981,7 +982,7 @@ enum {JSON_float_error = 0};
 enum {JSON_float_en_main = 1};
 
 
-#line 339 "parser.rl"
+#line 340 "parser.rl"
 
 
 static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -989,15 +990,15 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
     int cs = EVIL;
 
 
-#line 993 "parser.c"
+#line 994 "parser.c"
 	{
 	cs = JSON_float_start;
 	}
 
-#line 346 "parser.rl"
+#line 347 "parser.rl"
     json->memo = p;
 
-#line 1001 "parser.c"
+#line 1002 "parser.c"
 	{
 	if ( p == pe )
 		goto _test_eof;
@@ -1055,14 +1056,14 @@ case 8:
 		goto st0;
 	goto tr9;
 tr9:
-#line 333 "parser.rl"
+#line 334 "parser.rl"
 	{ p--; {p++; cs = 9; goto _out;} }
 	goto st9;
 st9:
 	if ( ++p == pe )
 		goto _test_eof9;
 case 9:
-#line 1066 "parser.c"
+#line 1067 "parser.c"
 	goto st0;
 st5:
 	if ( ++p == pe )
@@ -1123,14 +1124,20 @@ case 7:
 	_out: {}
 	}
 
-#line 348 "parser.rl"
+#line 349 "parser.rl"
 
     if (cs >= JSON_float_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_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
+        if (NIL_P(json->decimal_class)) {
+          *result = rb_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
+        } else {
+          VALUE text;
+          text = rb_str_new2(FBUFFER_PTR(json->fbuffer));
+          *result = rb_funcall(json->decimal_class, i_new, 1, text);
+        }
         return p + 1;
     } else {
         return NULL;
@@ -1139,7 +1146,7 @@ case 7:
 
 
 
-#line 1143 "parser.c"
+#line 1150 "parser.c"
 enum {JSON_array_start = 1};
 enum {JSON_array_first_final = 17};
 enum {JSON_array_error = 0};
@@ -1147,7 +1154,7 @@ enum {JSON_array_error = 0};
 enum {JSON_array_en_main = 1};
 
 
-#line 391 "parser.rl"
+#line 398 "parser.rl"
 
 
 static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
@@ -1161,14 +1168,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 1165 "parser.c"
+#line 1172 "parser.c"
 	{
 	cs = JSON_array_start;
 	}
 
-#line 404 "parser.rl"
+#line 411 "parser.rl"
 
-#line 1172 "parser.c"
+#line 1179 "parser.c"
 	{
 	if ( p == pe )
 		goto _test_eof;
@@ -1207,7 +1214,7 @@ case 2:
 		goto st2;
 	goto st0;
 tr2:
-#line 368 "parser.rl"
+#line 375 "parser.rl"
 	{
         VALUE v = Qnil;
         char *np = JSON_parse_value(json, p, pe, &v, current_nesting);
@@ -1227,7 +1234,7 @@ st3:
 	if ( ++p == pe )
 		goto _test_eof3;
 case 3:
-#line 1231 "parser.c"
+#line 1238 "parser.c"
 	switch( (*p) ) {
 		case 13: goto st3;
 		case 32: goto st3;
@@ -1327,14 +1334,14 @@ case 12:
 		goto st3;
 	goto st12;
 tr4:
-#line 383 "parser.rl"
+#line 390 "parser.rl"
 	{ p--; {p++; cs = 17; goto _out;} }
 	goto st17;
 st17:
 	if ( ++p == pe )
 		goto _test_eof17;
 case 17:
-#line 1338 "parser.c"
+#line 1345 "parser.c"
 	goto st0;
 st13:
 	if ( ++p == pe )
@@ -1390,7 +1397,7 @@ case 16:
 	_out: {}
 	}
 
-#line 405 "parser.rl"
+#line 412 "parser.rl"
 
     if(cs >= JSON_array_first_final) {
         return p + 1;
@@ -1435,13 +1442,21 @@ static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd)
                     break;
                 case 'u':
                     if (pe > stringEnd - 4) {
-                        return Qnil;
+                      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) return Qnil;
+                            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)
@@ -1471,7 +1486,7 @@ static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd)
 }
 
 
-#line 1475 "parser.c"
+#line 1490 "parser.c"
 enum {JSON_string_start = 1};
 enum {JSON_string_first_final = 8};
 enum {JSON_string_error = 0};
@@ -1479,7 +1494,7 @@ enum {JSON_string_error = 0};
 enum {JSON_string_en_main = 1};
 
 
-#line 504 "parser.rl"
+#line 519 "parser.rl"
 
 
 static int
@@ -1501,15 +1516,15 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
 
     *result = rb_str_buf_new(0);
 
-#line 1505 "parser.c"
+#line 1520 "parser.c"
 	{
 	cs = JSON_string_start;
 	}
 
-#line 525 "parser.rl"
+#line 540 "parser.rl"
     json->memo = p;
 
-#line 1513 "parser.c"
+#line 1528 "parser.c"
 	{
 	if ( p == pe )
 		goto _test_eof;
@@ -1534,7 +1549,7 @@ case 2:
 		goto st0;
 	goto st2;
 tr2:
-#line 490 "parser.rl"
+#line 505 "parser.rl"
 	{
         *result = json_string_unescape(*result, json->memo + 1, p);
         if (NIL_P(*result)) {
@@ -1545,14 +1560,14 @@ tr2:
             {p = (( p + 1))-1;}
         }
     }
-#line 501 "parser.rl"
+#line 516 "parser.rl"
 	{ p--; {p++; cs = 8; goto _out;} }
 	goto st8;
 st8:
 	if ( ++p == pe )
 		goto _test_eof8;
 case 8:
-#line 1556 "parser.c"
+#line 1571 "parser.c"
 	goto st0;
 st3:
 	if ( ++p == pe )
@@ -1628,7 +1643,7 @@ case 7:
 	_out: {}
 	}
 
-#line 527 "parser.rl"
+#line 542 "parser.rl"
 
     if (json->create_additions && RTEST(match_string = json->match_string)) {
           VALUE klass;
@@ -1670,9 +1685,12 @@ 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, NULL, rb_utf8_encoding());
+    source = rb_str_conv_enc(source, rb_enc_get(source), rb_utf8_encoding());
   }
 #endif
     return source;
@@ -1778,6 +1796,12 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
             } 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);
@@ -1795,6 +1819,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
         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);
@@ -1805,7 +1830,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
 }
 
 
-#line 1809 "parser.c"
+#line 1834 "parser.c"
 enum {JSON_start = 1};
 enum {JSON_first_final = 10};
 enum {JSON_error = 0};
@@ -1813,7 +1838,7 @@ enum {JSON_error = 0};
 enum {JSON_en_main = 1};
 
 
-#line 717 "parser.rl"
+#line 742 "parser.rl"
 
 
 /*
@@ -1830,16 +1855,16 @@ static VALUE cParser_parse(VALUE self)
   GET_PARSER;
 
 
-#line 1834 "parser.c"
+#line 1859 "parser.c"
 	{
 	cs = JSON_start;
 	}
 
-#line 733 "parser.rl"
+#line 758 "parser.rl"
   p = json->source;
   pe = p + json->len;
 
-#line 1843 "parser.c"
+#line 1868 "parser.c"
 	{
 	if ( p == pe )
 		goto _test_eof;
@@ -1873,7 +1898,7 @@ st0:
 cs = 0;
 	goto _out;
 tr2:
-#line 709 "parser.rl"
+#line 734 "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;}
@@ -1883,7 +1908,7 @@ st10:
 	if ( ++p == pe )
 		goto _test_eof10;
 case 10:
-#line 1887 "parser.c"
+#line 1912 "parser.c"
 	switch( (*p) ) {
 		case 13: goto st10;
 		case 32: goto st10;
@@ -1972,7 +1997,7 @@ case 9:
 	_out: {}
 	}
 
-#line 736 "parser.rl"
+#line 761 "parser.rl"
 
   if (cs >= JSON_first_final && p == pe) {
     return result;
@@ -1989,6 +2014,7 @@ static void JSON_mark(void *ptr)
     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);
 }
 
@@ -2063,6 +2089,7 @@ void Init_parser(void)
     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?");
@@ -2070,6 +2097,7 @@ void Init_parser(void)
     i_aset = rb_intern("[]=");
     i_aref = rb_intern("[]");
     i_leftshift = rb_intern("<<");
+    i_new = rb_intern("new");
 }
 
 /*
diff --git a/ext/json/ext/parser/parser.h b/ext/json/ext/parser/parser.h
index 1d46831..e6cf779 100644
--- a/ext/json/ext/parser/parser.h
+++ b/ext/json/ext/parser/parser.h
@@ -39,6 +39,7 @@ typedef struct JSON_ParserStruct {
     int symbolize_names;
     VALUE object_class;
     VALUE array_class;
+    VALUE decimal_class;
     int create_additions;
     VALUE match_string;
     FBuffer *fbuffer;
diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl
index c67634b..29900a4 100644
--- a/ext/json/ext/parser/parser.rl
+++ b/ext/json/ext/parser/parser.rl
@@ -92,8 +92,9 @@ 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_key_p, i_deep_const_get, i_match,
-          i_match_string, i_aset, i_aref, i_leftshift;
+          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;
 
 %%{
     machine JSON_common;
@@ -351,7 +352,13 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
         fbuffer_clear(json->fbuffer);
         fbuffer_append(json->fbuffer, json->memo, len);
         fbuffer_append_char(json->fbuffer, '\0');
-        *result = rb_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
+        if (NIL_P(json->decimal_class)) {
+          *result = rb_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
+        } else {
+          VALUE text;
+          text = rb_str_new2(FBUFFER_PTR(json->fbuffer));
+          *result = rb_funcall(json->decimal_class, i_new, 1, text);
+        }
         return p + 1;
     } else {
         return NULL;
@@ -446,13 +453,21 @@ static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd)
                     break;
                 case 'u':
                     if (pe > stringEnd - 4) {
-                        return Qnil;
+                      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) return Qnil;
+                            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)
@@ -565,9 +580,12 @@ 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, NULL, rb_utf8_encoding());
+    source = rb_str_conv_enc(source, rb_enc_get(source), rb_utf8_encoding());
   }
 #endif
     return source;
@@ -673,6 +691,12 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
             } 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);
@@ -690,6 +714,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
         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);
@@ -749,6 +774,7 @@ static void JSON_mark(void *ptr)
     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);
 }
 
@@ -823,6 +849,7 @@ void Init_parser(void)
     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?");
@@ -830,6 +857,7 @@ void Init_parser(void)
     i_aset = rb_intern("[]=");
     i_aref = rb_intern("[]");
     i_leftshift = rb_intern("<<");
+    i_new = rb_intern("new");
 }
 
 /*
diff --git a/java/src/json/ext/Parser.java b/java/src/json/ext/Parser.java
index 8c33840..2ba9f8f 100644
--- a/java/src/json/ext/Parser.java
+++ b/java/src/json/ext/Parser.java
@@ -54,6 +54,7 @@ public class Parser extends RubyObject {
     private boolean symbolizeNames;
     private RubyClass objectClass;
     private RubyClass arrayClass;
+    private RubyClass decimalClass;
     private RubyHash matchString;
 
     private static final int DEFAULT_MAX_NESTING = 100;
@@ -133,6 +134,10 @@ public class Parser extends RubyObject {
      * <dt><code>:array_class</code>
      * <dd>Defaults to Array.
      *
+     * <dt><code>:decimal_class</code>
+     * <dd>Specifies which class to use instead of the default (Float) when
+     * parsing decimal numbers. This class must accept a single string argument
+     * in its constructor.
      * </dl>
      */
     @JRubyMethod(name = "new", required = 1, optional = 1, meta = true)
@@ -159,6 +164,7 @@ public class Parser extends RubyObject {
         this.createAdditions = opts.getBool("create_additions", false);
         this.objectClass     = opts.getClass("object_class", runtime.getHash());
         this.arrayClass      = opts.getClass("array_class", runtime.getArray());
+        this.decimalClass    = opts.getClass("decimal_class", null);
         this.matchString    = opts.getHash("match_string");
 
         if(symbolizeNames && createAdditions) {
@@ -181,6 +187,9 @@ public class Parser extends RubyObject {
     private RubyString convertEncoding(ThreadContext context, RubyString source) {
       RubyEncoding encoding = (RubyEncoding)source.encoding(context);
       if (encoding == info.ascii8bit.get()) {
+          if (source.isFrozen()) {
+            source = (RubyString) source.dup();
+          }
           source.force_encoding(context, info.utf8.get());
       } else {
         source = (RubyString) source.encode(context, info.utf8.get());
@@ -304,11 +313,11 @@ public class Parser extends RubyObject {
         }
 
         
-// line 330 "Parser.rl"
+// line 339 "Parser.rl"
 
 
         
-// line 312 "Parser.java"
+// line 321 "Parser.java"
 private static byte[] init__JSON_value_actions_0()
 {
 	return new byte [] {
@@ -422,7 +431,7 @@ static final int JSON_value_error = 0;
 static final int JSON_value_en_main = 1;
 
 
-// line 436 "Parser.rl"
+// line 445 "Parser.rl"
 
 
         void parseValue(ParserResult res, int p, int pe) {
@@ -430,14 +439,14 @@ static final int JSON_value_en_main = 1;
             IRubyObject result = null;
 
             
-// line 434 "Parser.java"
+// line 443 "Parser.java"
 	{
 	cs = JSON_value_start;
 	}
 
-// line 443 "Parser.rl"
+// line 452 "Parser.rl"
             
-// line 441 "Parser.java"
+// line 450 "Parser.java"
 	{
 	int _klen;
 	int _trans = 0;
@@ -463,13 +472,13 @@ case 1:
 	while ( _nacts-- > 0 ) {
 		switch ( _JSON_value_actions[_acts++] ) {
 	case 9:
-// line 421 "Parser.rl"
+// line 430 "Parser.rl"
 	{
                 p--;
                 { p += 1; _goto_targ = 5; if (true)  continue _goto;}
             }
 	break;
-// line 473 "Parser.java"
+// line 482 "Parser.java"
 		}
 	}
 
@@ -532,25 +541,25 @@ case 1:
 			switch ( _JSON_value_actions[_acts++] )
 			{
 	case 0:
-// line 338 "Parser.rl"
+// line 347 "Parser.rl"
 	{
                 result = getRuntime().getNil();
             }
 	break;
 	case 1:
-// line 341 "Parser.rl"
+// line 350 "Parser.rl"
 	{
                 result = getRuntime().getFalse();
             }
 	break;
 	case 2:
-// line 344 "Parser.rl"
+// line 353 "Parser.rl"
 	{
                 result = getRuntime().getTrue();
             }
 	break;
 	case 3:
-// line 347 "Parser.rl"
+// line 356 "Parser.rl"
 	{
                 if (parser.allowNaN) {
                     result = getConstant(CONST_NAN);
@@ -560,7 +569,7 @@ case 1:
             }
 	break;
 	case 4:
-// line 354 "Parser.rl"
+// line 363 "Parser.rl"
 	{
                 if (parser.allowNaN) {
                     result = getConstant(CONST_INFINITY);
@@ -570,7 +579,7 @@ case 1:
             }
 	break;
 	case 5:
-// line 361 "Parser.rl"
+// line 370 "Parser.rl"
 	{
                 if (pe > p + 8 &&
                     absSubSequence(p, p + 9).equals(JSON_MINUS_INFINITY)) {
@@ -599,7 +608,7 @@ case 1:
             }
 	break;
 	case 6:
-// line 387 "Parser.rl"
+// line 396 "Parser.rl"
 	{
                 parseString(res, p, pe);
                 if (res.result == null) {
@@ -612,7 +621,7 @@ case 1:
             }
 	break;
 	case 7:
-// line 397 "Parser.rl"
+// line 406 "Parser.rl"
 	{
                 currentNesting++;
                 parseArray(res, p, pe);
@@ -627,7 +636,7 @@ case 1:
             }
 	break;
 	case 8:
-// line 409 "Parser.rl"
+// line 418 "Parser.rl"
 	{
                 currentNesting++;
                 parseObject(res, p, pe);
@@ -641,7 +650,7 @@ case 1:
                 }
             }
 	break;
-// line 645 "Parser.java"
+// line 654 "Parser.java"
 			}
 		}
 	}
@@ -661,7 +670,7 @@ case 5:
 	break; }
 	}
 
-// line 444 "Parser.rl"
+// line 453 "Parser.rl"
 
             if (cs >= JSON_value_first_final && result != null) {
                 res.update(result, p);
@@ -671,7 +680,7 @@ case 5:
         }
 
         
-// line 675 "Parser.java"
+// line 684 "Parser.java"
 private static byte[] init__JSON_integer_actions_0()
 {
 	return new byte [] {
@@ -770,7 +779,7 @@ static final int JSON_integer_error = 0;
 static final int JSON_integer_en_main = 1;
 
 
-// line 463 "Parser.rl"
+// line 472 "Parser.rl"
 
 
         void parseInteger(ParserResult res, int p, int pe) {
@@ -788,15 +797,15 @@ static final int JSON_integer_en_main = 1;
             int cs = EVIL;
 
             
-// line 792 "Parser.java"
+// line 801 "Parser.java"
 	{
 	cs = JSON_integer_start;
 	}
 
-// line 480 "Parser.rl"
+// line 489 "Parser.rl"
             int memo = p;
             
-// line 800 "Parser.java"
+// line 809 "Parser.java"
 	{
 	int _klen;
 	int _trans = 0;
@@ -877,13 +886,13 @@ case 1:
 			switch ( _JSON_integer_actions[_acts++] )
 			{
 	case 0:
-// line 457 "Parser.rl"
+// line 466 "Parser.rl"
 	{
                 p--;
                 { p += 1; _goto_targ = 5; if (true)  continue _goto;}
             }
 	break;
-// line 887 "Parser.java"
+// line 896 "Parser.java"
 			}
 		}
 	}
@@ -903,7 +912,7 @@ case 5:
 	break; }
 	}
 
-// line 482 "Parser.rl"
+// line 491 "Parser.rl"
 
             if (cs < JSON_integer_first_final) {
                 return -1;
@@ -911,13 +920,13 @@ case 5:
 
             return p;
         }
-        
+
         RubyInteger createInteger(int p, int new_p) {
             Ruby runtime = getRuntime();
             ByteList num = absSubSequence(p, new_p);
             return bytesToInum(runtime, num);
         }
-        
+
         RubyInteger bytesToInum(Ruby runtime, ByteList num) {
             return runtime.is1_9() ?
                     ConvertBytes.byteListToInum19(runtime, num, 10, true) :
@@ -925,7 +934,7 @@ case 5:
         }
 
         
-// line 929 "Parser.java"
+// line 938 "Parser.java"
 private static byte[] init__JSON_float_actions_0()
 {
 	return new byte [] {
@@ -1027,7 +1036,7 @@ static final int JSON_float_error = 0;
 static final int JSON_float_en_main = 1;
 
 
-// line 517 "Parser.rl"
+// line 526 "Parser.rl"
 
 
         void parseFloat(ParserResult res, int p, int pe) {
@@ -1036,7 +1045,9 @@ static final int JSON_float_en_main = 1;
                 res.update(null, p);
                 return;
             }
-            RubyFloat number = createFloat(p, new_p);
+            IRubyObject number = parser.decimalClass == null ?
+                createFloat(p, new_p) : createCustomDecimal(p, new_p);
+
             res.update(number, new_p + 1);
             return;
         }
@@ -1045,15 +1056,15 @@ static final int JSON_float_en_main = 1;
             int cs = EVIL;
 
             
-// line 1049 "Parser.java"
+// line 1060 "Parser.java"
 	{
 	cs = JSON_float_start;
 	}
 
-// line 534 "Parser.rl"
+// line 545 "Parser.rl"
             int memo = p;
             
-// line 1057 "Parser.java"
+// line 1068 "Parser.java"
 	{
 	int _klen;
 	int _trans = 0;
@@ -1134,13 +1145,13 @@ case 1:
 			switch ( _JSON_float_actions[_acts++] )
 			{
 	case 0:
-// line 508 "Parser.rl"
+// line 517 "Parser.rl"
 	{
                 p--;
                 { p += 1; _goto_targ = 5; if (true)  continue _goto;}
             }
 	break;
-// line 1144 "Parser.java"
+// line 1155 "Parser.java"
 			}
 		}
 	}
@@ -1160,23 +1171,30 @@ case 5:
 	break; }
 	}
 
-// line 536 "Parser.rl"
+// line 547 "Parser.rl"
 
             if (cs < JSON_float_first_final) {
                 return -1;
             }
-            
+
             return p;
         }
-        
+
         RubyFloat createFloat(int p, int new_p) {
             Ruby runtime = getRuntime();
             ByteList num = absSubSequence(p, new_p);
             return RubyFloat.newFloat(runtime, dc.parse(num, true, runtime.is1_9()));
         }
 
+        IRubyObject createCustomDecimal(int p, int new_p) {
+            Ruby runtime = getRuntime();
+            ByteList num = absSubSequence(p, new_p);
+            IRubyObject numString = runtime.newString(num.toString());
+            return parser.decimalClass.callMethod(context, "new", numString);
+        }
+
         
-// line 1180 "Parser.java"
+// line 1198 "Parser.java"
 private static byte[] init__JSON_string_actions_0()
 {
 	return new byte [] {
@@ -1278,7 +1296,7 @@ static final int JSON_string_error = 0;
 static final int JSON_string_en_main = 1;
 
 
-// line 581 "Parser.rl"
+// line 599 "Parser.rl"
 
 
         void parseString(ParserResult res, int p, int pe) {
@@ -1286,15 +1304,15 @@ static final int JSON_string_en_main = 1;
             IRubyObject result = null;
 
             
-// line 1290 "Parser.java"
+// line 1308 "Parser.java"
 	{
 	cs = JSON_string_start;
 	}
 
-// line 588 "Parser.rl"
+// line 606 "Parser.rl"
             int memo = p;
             
-// line 1298 "Parser.java"
+// line 1316 "Parser.java"
 	{
 	int _klen;
 	int _trans = 0;
@@ -1375,7 +1393,7 @@ case 1:
 			switch ( _JSON_string_actions[_acts++] )
 			{
 	case 0:
-// line 556 "Parser.rl"
+// line 574 "Parser.rl"
 	{
                 int offset = byteList.begin();
                 ByteList decoded = decoder.decode(byteList, memo + 1 - offset,
@@ -1390,13 +1408,13 @@ case 1:
             }
 	break;
 	case 1:
-// line 569 "Parser.rl"
+// line 587 "Parser.rl"
 	{
                 p--;
                 { p += 1; _goto_targ = 5; if (true)  continue _goto;}
             }
 	break;
-// line 1400 "Parser.java"
+// line 1418 "Parser.java"
 			}
 		}
 	}
@@ -1416,7 +1434,7 @@ case 5:
 	break; }
 	}
 
-// line 590 "Parser.rl"
+// line 608 "Parser.rl"
 
             if (parser.createAdditions) {
                 RubyHash matchString = parser.matchString;
@@ -1443,7 +1461,7 @@ case 5:
                 }
             }
 
-            if (cs >= JSON_string_first_final && result != null) {                
+            if (cs >= JSON_string_first_final && result != null) {
                 if (result instanceof RubyString) {
                   ((RubyString)result).force_encoding(context, info.utf8.get());
                 }
@@ -1454,7 +1472,7 @@ case 5:
         }
 
         
-// line 1458 "Parser.java"
+// line 1476 "Parser.java"
 private static byte[] init__JSON_array_actions_0()
 {
 	return new byte [] {
@@ -1567,7 +1585,7 @@ static final int JSON_array_error = 0;
 static final int JSON_array_en_main = 1;
 
 
-// line 663 "Parser.rl"
+// line 681 "Parser.rl"
 
 
         void parseArray(ParserResult res, int p, int pe) {
@@ -1587,14 +1605,14 @@ static final int JSON_array_en_main = 1;
             }
 
             
-// line 1591 "Parser.java"
+// line 1609 "Parser.java"
 	{
 	cs = JSON_array_start;
 	}
 
-// line 682 "Parser.rl"
+// line 700 "Parser.rl"
             
-// line 1598 "Parser.java"
+// line 1616 "Parser.java"
 	{
 	int _klen;
 	int _trans = 0;
@@ -1675,7 +1693,7 @@ case 1:
 			switch ( _JSON_array_actions[_acts++] )
 			{
 	case 0:
-// line 632 "Parser.rl"
+// line 650 "Parser.rl"
 	{
                 parseValue(res, p, pe);
                 if (res.result == null) {
@@ -1692,13 +1710,13 @@ case 1:
             }
 	break;
 	case 1:
-// line 647 "Parser.rl"
+// line 665 "Parser.rl"
 	{
                 p--;
                 { p += 1; _goto_targ = 5; if (true)  continue _goto;}
             }
 	break;
-// line 1702 "Parser.java"
+// line 1720 "Parser.java"
 			}
 		}
 	}
@@ -1718,7 +1736,7 @@ case 5:
 	break; }
 	}
 
-// line 683 "Parser.rl"
+// line 701 "Parser.rl"
 
             if (cs >= JSON_array_first_final) {
                 res.update(result, p + 1);
@@ -1728,7 +1746,7 @@ case 5:
         }
 
         
-// line 1732 "Parser.java"
+// line 1750 "Parser.java"
 private static byte[] init__JSON_object_actions_0()
 {
 	return new byte [] {
@@ -1851,7 +1869,7 @@ static final int JSON_object_error = 0;
 static final int JSON_object_en_main = 1;
 
 
-// line 742 "Parser.rl"
+// line 760 "Parser.rl"
 
 
         void parseObject(ParserResult res, int p, int pe) {
@@ -1876,14 +1894,14 @@ static final int JSON_object_en_main = 1;
             }
 
             
-// line 1880 "Parser.java"
+// line 1898 "Parser.java"
 	{
 	cs = JSON_object_start;
 	}
 
-// line 766 "Parser.rl"
+// line 784 "Parser.rl"
             
-// line 1887 "Parser.java"
+// line 1905 "Parser.java"
 	{
 	int _klen;
 	int _trans = 0;
@@ -1964,7 +1982,7 @@ case 1:
 			switch ( _JSON_object_actions[_acts++] )
 			{
 	case 0:
-// line 697 "Parser.rl"
+// line 715 "Parser.rl"
 	{
                 parseValue(res, p, pe);
                 if (res.result == null) {
@@ -1981,7 +1999,7 @@ case 1:
             }
 	break;
 	case 1:
-// line 712 "Parser.rl"
+// line 730 "Parser.rl"
 	{
                 parseString(res, p, pe);
                 if (res.result == null) {
@@ -2001,13 +2019,13 @@ case 1:
             }
 	break;
 	case 2:
-// line 730 "Parser.rl"
+// line 748 "Parser.rl"
 	{
                 p--;
                 { p += 1; _goto_targ = 5; if (true)  continue _goto;}
             }
 	break;
-// line 2011 "Parser.java"
+// line 2029 "Parser.java"
 			}
 		}
 	}
@@ -2027,7 +2045,7 @@ case 5:
 	break; }
 	}
 
-// line 767 "Parser.rl"
+// line 785 "Parser.rl"
 
             if (cs < JSON_object_first_final) {
                 res.update(null, p + 1);
@@ -2060,7 +2078,7 @@ case 5:
         }
 
         
-// line 2064 "Parser.java"
+// line 2082 "Parser.java"
 private static byte[] init__JSON_actions_0()
 {
 	return new byte [] {
@@ -2163,7 +2181,7 @@ static final int JSON_error = 0;
 static final int JSON_en_main = 1;
 
 
-// line 818 "Parser.rl"
+// line 836 "Parser.rl"
 
 
         public IRubyObject parseImplemetation() {
@@ -2173,16 +2191,16 @@ static final int JSON_en_main = 1;
             ParserResult res = new ParserResult();
 
             
-// line 2177 "Parser.java"
+// line 2195 "Parser.java"
 	{
 	cs = JSON_start;
 	}
 
-// line 827 "Parser.rl"
+// line 845 "Parser.rl"
             p = byteList.begin();
             pe = p + byteList.length();
             
-// line 2186 "Parser.java"
+// line 2204 "Parser.java"
 	{
 	int _klen;
 	int _trans = 0;
@@ -2263,7 +2281,7 @@ case 1:
 			switch ( _JSON_actions[_acts++] )
 			{
 	case 0:
-// line 804 "Parser.rl"
+// line 822 "Parser.rl"
 	{
                 parseValue(res, p, pe);
                 if (res.result == null) {
@@ -2275,7 +2293,7 @@ case 1:
                 }
             }
 	break;
-// line 2279 "Parser.java"
+// line 2297 "Parser.java"
 			}
 		}
 	}
@@ -2295,7 +2313,7 @@ case 5:
 	break; }
 	}
 
-// line 830 "Parser.rl"
+// line 848 "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 28247ea..4d170e1 100644
--- a/java/src/json/ext/Parser.rl
+++ b/java/src/json/ext/Parser.rl
@@ -52,7 +52,8 @@ public class Parser extends RubyObject {
     private boolean symbolizeNames;
     private RubyClass objectClass;
     private RubyClass arrayClass;
-    private RubyHash matchString;
+    private RubyClass decimalClass;
+    private RubyHash match_string;
 
     private static final int DEFAULT_MAX_NESTING = 100;
 
@@ -131,6 +132,10 @@ public class Parser extends RubyObject {
      * <dt><code>:array_class</code>
      * <dd>Defaults to Array.
      *
+     * <dt><code>:decimal_class</code>
+     * <dd>Specifies which class to use instead of the default (Float) when
+     * parsing decimal numbers. This class must accept a single string argument
+     * in its constructor.
      * </dl>
      */
     @JRubyMethod(name = "new", required = 1, optional = 1, meta = true)
@@ -157,7 +162,8 @@ public class Parser extends RubyObject {
         this.createAdditions = opts.getBool("create_additions", false);
         this.objectClass     = opts.getClass("object_class", runtime.getHash());
         this.arrayClass      = opts.getClass("array_class", runtime.getArray());
-        this.matchString    = opts.getHash("match_string");
+        this.decimalClass    = opts.getClass("decimal_class", null);
+        this.match_string    = opts.getHash("match_string");
 
         if(symbolizeNames && createAdditions) {
           throw runtime.newArgumentError(
@@ -179,6 +185,9 @@ public class Parser extends RubyObject {
     private RubyString convertEncoding(ThreadContext context, RubyString source) {
       RubyEncoding encoding = (RubyEncoding)source.encoding(context);
       if (encoding == info.ascii8bit.get()) {
+          if (source.isFrozen()) {
+            source = (RubyString) source.dup();
+          }
           source.force_encoding(context, info.utf8.get());
       } else {
         source = (RubyString) source.encode(context, info.utf8.get());
@@ -486,13 +495,13 @@ public class Parser extends RubyObject {
 
             return p;
         }
-        
+
         RubyInteger createInteger(int p, int new_p) {
             Ruby runtime = getRuntime();
             ByteList num = absSubSequence(p, new_p);
             return bytesToInum(runtime, num);
         }
-        
+
         RubyInteger bytesToInum(Ruby runtime, ByteList num) {
             return runtime.is1_9() ?
                     ConvertBytes.byteListToInum19(runtime, num, 10, true) :
@@ -522,7 +531,9 @@ public class Parser extends RubyObject {
                 res.update(null, p);
                 return;
             }
-            RubyFloat number = createFloat(p, new_p);
+            IRubyObject number = parser.decimalClass == null ?
+                createFloat(p, new_p) : createCustomDecimal(p, new_p);
+
             res.update(number, new_p + 1);
             return;
         }
@@ -537,16 +548,23 @@ public class Parser extends RubyObject {
             if (cs < JSON_float_first_final) {
                 return -1;
             }
-            
+
             return p;
         }
-        
+
         RubyFloat createFloat(int p, int new_p) {
             Ruby runtime = getRuntime();
             ByteList num = absSubSequence(p, new_p);
             return RubyFloat.newFloat(runtime, dc.parse(num, true, runtime.is1_9()));
         }
 
+        IRubyObject createCustomDecimal(int p, int new_p) {
+            Ruby runtime = getRuntime();
+            ByteList num = absSubSequence(p, new_p);
+            IRubyObject numString = runtime.newString(num.toString());
+            return parser.decimalClass.callMethod(context, "new", numString);
+        }
+
         %%{
             machine JSON_string;
             include JSON_common;
@@ -613,7 +631,7 @@ public class Parser extends RubyObject {
                 }
             }
 
-            if (cs >= JSON_string_first_final && result != null) {                
+            if (cs >= JSON_string_first_final && result != null) {
                 if (result instanceof RubyString) {
                   ((RubyString)result).force_encoding(context, info.utf8.get());
                 }
@@ -731,7 +749,7 @@ public class Parser extends RubyObject {
                 fhold;
                 fbreak;
             }
-            
+
             pair      = ignore* begin_name >parse_name ignore* name_separator
               ignore* begin_value >parse_value;
             next_pair = ignore* value_separator pair;
diff --git a/json.gemspec b/json.gemspec
index c5a3c10..b8f3009 100644
Binary files a/json.gemspec and b/json.gemspec differ
diff --git a/json_pure.gemspec b/json_pure.gemspec
index bf7dc79..4964a42 100644
--- a/json_pure.gemspec
+++ b/json_pure.gemspec
@@ -1,14 +1,14 @@
 # -*- encoding: utf-8 -*-
-# stub: json_pure 2.0.1 ruby lib
+# stub: json_pure 2.1.0 ruby lib
 
 Gem::Specification.new do |s|
   s.name = "json_pure".freeze
-  s.version = "2.0.1"
+  s.version = "2.1.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 = "2016-07-01"
+  s.date = "2017-04-18"
   s.description = "This is a JSON implementation in pure Ruby.".freeze
   s.email = "flori at ping.de".freeze
   s.extra_rdoc_files = ["README.md".freeze]
@@ -16,7 +16,8 @@ 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.rubygems_version = "2.6.4".freeze
+  s.required_ruby_version = Gem::Requirement.new(">= 1.9".freeze)
+  s.rubygems_version = "2.6.11".freeze
   s.summary = "JSON Implementation for Ruby".freeze
   s.test_files = ["./tests/test_helper.rb".freeze]
 
diff --git a/lib/json/add/ostruct.rb b/lib/json/add/ostruct.rb
index 7c13910..e064c85 100644
--- a/lib/json/add/ostruct.rb
+++ b/lib/json/add/ostruct.rb
@@ -7,7 +7,7 @@ require 'ostruct'
 class OpenStruct
 
   # Deserializes JSON string by constructing new Struct object with values
-  # <tt>v</tt> serialized by <tt>to_json</tt>.
+  # <tt>t</tt> serialized by <tt>to_json</tt>.
   def self.json_create(object)
     new(object['t'] || object[:t])
   end
diff --git a/lib/json/pure/parser.rb b/lib/json/pure/parser.rb
index b907236..3a6343b 100644
--- a/lib/json/pure/parser.rb
+++ b/lib/json/pure/parser.rb
@@ -70,6 +70,9 @@ module JSON
       #   option defaults to false.
       # * *object_class*: Defaults to Hash
       # * *array_class*: Defaults to Array
+      # * *decimal_class*: Specifies which class to use instead of the default
+      #    (Float) when parsing decimal numbers. This class must accept a single
+      #    string argument in its constructor.
       def initialize(source, opts = {})
         opts ||= {}
         source = convert_encoding source
@@ -94,6 +97,7 @@ module JSON
         @create_id = @create_additions ? JSON.create_id : nil
         @object_class = opts[:object_class] || Hash
         @array_class  = opts[:array_class] || Array
+        @decimal_class = opts[:decimal_class]
         @match_string = opts[:match_string]
       end
 
@@ -193,7 +197,7 @@ module JSON
       def parse_value
         case
         when scan(FLOAT)
-          Float(self[1])
+          @decimal_class && @decimal_class.new(self[1]) || Float(self[1])
         when scan(INTEGER)
           Integer(self[1])
         when scan(TRUE)
diff --git a/lib/json/version.rb b/lib/json/version.rb
index 5184ad3..b65ed87 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.0.1'
+  VERSION         = '2.1.0'
   VERSION_ARRAY   = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
   VERSION_MAJOR   = VERSION_ARRAY[0] # :nodoc:
   VERSION_MINOR   = VERSION_ARRAY[1] # :nodoc:
diff --git a/tests/json_common_interface_test.rb b/tests/json_common_interface_test.rb
index b2051d4..29b4a5b 100644
--- a/tests/json_common_interface_test.rb
+++ b/tests/json_common_interface_test.rb
@@ -47,7 +47,7 @@ class JSONCommonInterfaceTest < Test::Unit::TestCase
   end
 
   def test_deep_const_get
-    assert_raises(ArgumentError) { JSON.deep_const_get('Nix::Da') }
+    assert_raise(ArgumentError) { JSON.deep_const_get('Nix::Da') }
     assert_equal File::SEPARATOR, JSON.deep_const_get('File::SEPARATOR')
   end
 
@@ -93,8 +93,8 @@ class JSONCommonInterfaceTest < Test::Unit::TestCase
 
   def test_load_null
     assert_equal nil, JSON.load(nil, nil, :allow_blank => true)
-    assert_raises(TypeError) { JSON.load(nil, nil, :allow_blank => false) }
-    assert_raises(JSON::ParserError) { JSON.load('', nil, :allow_blank => false) }
+    assert_raise(TypeError) { JSON.load(nil, nil, :allow_blank => false) }
+    assert_raise(JSON::ParserError) { JSON.load('', nil, :allow_blank => false) }
   end
 
   def test_dump
diff --git a/tests/json_encoding_test.rb b/tests/json_encoding_test.rb
index 29ae02e..cc7b715 100644
--- a/tests/json_encoding_test.rb
+++ b/tests/json_encoding_test.rb
@@ -79,6 +79,8 @@ class JSONEncodingTest < Test::Unit::TestCase
     json = '["\ud840\udc01"]'
     assert_equal json, generate(utf8, :ascii_only => true)
     assert_equal utf8, parse(json)
+    assert_raise(JSON::ParserError) { parse('"\u"') }
+    assert_raise(JSON::ParserError) { parse('"\ud800"') }
   end
 
   def test_chars
diff --git a/tests/json_generator_test.rb b/tests/json_generator_test.rb
index 18b0833..86be398 100644
--- a/tests/json_generator_test.rb
+++ b/tests/json_generator_test.rb
@@ -277,12 +277,13 @@ EOT
   if defined?(JSON::Ext::Generator)
     def test_broken_bignum # [ruby-core:38867]
       pid = fork do
-        Bignum.class_eval do
+        x = 1 << 64
+        x.class.class_eval do
           def to_s
           end
         end
         begin
-          JSON::Ext::Generator::State.new.generate(1<<64)
+          JSON::Ext::Generator::State.new.generate(x)
           exit 1
         rescue TypeError
           exit 0
diff --git a/tests/json_parser_test.rb b/tests/json_parser_test.rb
index c1c2779..f36e9c8 100644
--- a/tests/json_parser_test.rb
+++ b/tests/json_parser_test.rb
@@ -40,6 +40,18 @@ class JSONParserTest < Test::Unit::TestCase
     assert_equal({ 'a' => 'b' }, parser.parse)
   end
 
+  def test_parse_values
+    assert_equal(nil,      parse('null'))
+    assert_equal(false,    parse('false'))
+    assert_equal(true,     parse('true'))
+    assert_equal(-23,      parse('-23'))
+    assert_equal(23,       parse('23'))
+    assert_in_delta(0.23,  parse('0.23'), 1e-2)
+    assert_in_delta(0.0,   parse('0e0'), 1e-2)
+    assert_equal("",       parse('""'))
+    assert_equal("foobar", parse('"foobar"'))
+  end
+
   def test_parse_simple_arrays
     assert_equal([],             parse('[]'))
     assert_equal([],             parse('  [  ] '))
@@ -96,6 +108,11 @@ class JSONParserTest < Test::Unit::TestCase
     assert_equal -1.0/0, parse('-Infinity', :allow_nan => true)
   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"]      )
+  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" => {} }]
@@ -277,7 +294,6 @@ EOT
     assert_equal data, parse(json)
   end
 
-
   class SubArray < Array
     def <<(v)
       @shifted = true
@@ -438,6 +454,13 @@ EOT
     assert_equal obj, obj_again
   end
 
+  def test_parsing_frozen_ascii8bit_string
+    assert_equal(
+      { 'foo' => 'bar' },
+      JSON('{ "foo": "bar" }'.force_encoding(Encoding::ASCII_8BIT).freeze)
+    )
+  end
+
   private
 
   def assert_equal_float(expected, actual, delta = 1e-2)
diff --git a/tests/test_helper.rb b/tests/test_helper.rb
index 9d3665d..7e99c29 100644
--- a/tests/test_helper.rb
+++ b/tests/test_helper.rb
@@ -1,5 +1,3 @@
-gem 'json', File.read('VERSION').chomp
-
 case ENV['JSON']
 when 'pure'
   $:.unshift 'lib'

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/ruby-json.git



More information about the Pkg-ruby-extras-commits mailing list