diff --git a/CHANGES.md b/CHANGES.md index f9efe041..3e68fb59 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -18,6 +18,7 @@ pre-existing support for comments, make it suitable to parse `jsonc` documents. * Many performance improvements to `JSON.parse` and `JSON.load`, up to `1.7x` faster on real world documents. * Some minor performance improvements to `JSON.dump` and `JSON.generate`. +* `JSON.pretty_generate` no longer include newline inside empty object and arrays. ### 2024-11-04 (2.7.6) diff --git a/Rakefile b/Rakefile index c5b518a1..09b69a2e 100644 --- a/Rakefile +++ b/Rakefile @@ -161,7 +161,7 @@ if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby' file JRUBY_PARSER_JAR => :compile do cd 'java/src' do parser_classes = FileList[ - "json/ext/ByteListTranscoder*.class", + "json/ext/ByteList*.class", "json/ext/OptionsReader*.class", "json/ext/Parser*.class", "json/ext/RuntimeInfo*.class", @@ -179,7 +179,7 @@ if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby' file JRUBY_GENERATOR_JAR => :compile do cd 'java/src' do generator_classes = FileList[ - "json/ext/ByteListTranscoder*.class", + "json/ext/ByteList*.class", "json/ext/OptionsReader*.class", "json/ext/Generator*.class", "json/ext/RuntimeInfo*.class", diff --git a/ext/json/ext/fbuffer/fbuffer.h b/ext/json/ext/fbuffer/fbuffer.h index 3e154a5f..0774c7e4 100644 --- a/ext/json/ext/fbuffer/fbuffer.h +++ b/ext/json/ext/fbuffer/fbuffer.h @@ -46,9 +46,11 @@ typedef struct FBufferStruct { unsigned long len; unsigned long capa; char *ptr; + VALUE io; } FBuffer; #define FBUFFER_STACK_SIZE 512 +#define FBUFFER_IO_BUFFER_SIZE (16384 - 1) #define FBUFFER_INITIAL_LENGTH_DEFAULT 1024 #define FBUFFER_PTR(fb) ((fb)->ptr) @@ -66,7 +68,7 @@ static void fbuffer_append_long(FBuffer *fb, long number); #endif static inline void fbuffer_append_char(FBuffer *fb, char newchr); #ifdef JSON_GENERATOR -static VALUE fbuffer_to_s(FBuffer *fb); +static VALUE fbuffer_finalize(FBuffer *fb); #endif static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *stack_buffer, long stack_buffer_size) @@ -86,24 +88,19 @@ static void fbuffer_free(FBuffer *fb) } } -#ifndef JSON_GENERATOR static void fbuffer_clear(FBuffer *fb) { fb->len = 0; } -#endif -static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested) +static void fbuffer_flush(FBuffer *fb) { - unsigned long required; - - if (RB_UNLIKELY(!fb->ptr)) { - fb->ptr = ALLOC_N(char, fb->initial_length); - fb->capa = fb->initial_length; - } - - for (required = fb->capa; requested > required - fb->len; required <<= 1); + rb_io_write(fb->io, rb_utf8_str_new(fb->ptr, fb->len)); + fbuffer_clear(fb); +} +static void fbuffer_realloc(FBuffer *fb, unsigned long required) +{ if (required > fb->capa) { if (fb->type == FBUFFER_STACK_ALLOCATED) { const char *old_buffer = fb->ptr; @@ -117,6 +114,32 @@ static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested) } } +static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested) +{ + if (RB_UNLIKELY(fb->io)) { + if (fb->capa < FBUFFER_IO_BUFFER_SIZE) { + fbuffer_realloc(fb, FBUFFER_IO_BUFFER_SIZE); + } else { + fbuffer_flush(fb); + } + + if (RB_LIKELY(requested < fb->capa)) { + return; + } + } + + unsigned long required; + + if (RB_UNLIKELY(!fb->ptr)) { + fb->ptr = ALLOC_N(char, fb->initial_length); + fb->capa = fb->initial_length; + } + + for (required = fb->capa; requested > required - fb->len; required <<= 1); + + fbuffer_realloc(fb, required); +} + static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested) { if (RB_UNLIKELY(requested > fb->capa - fb->len)) { @@ -174,11 +197,18 @@ static void fbuffer_append_long(FBuffer *fb, long number) fbuffer_append(fb, buffer_end - len, len); } -static VALUE fbuffer_to_s(FBuffer *fb) +static VALUE fbuffer_finalize(FBuffer *fb) { - VALUE result = rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb)); - fbuffer_free(fb); - return result; + if (fb->io) { + fbuffer_flush(fb); + fbuffer_free(fb); + rb_io_flush(fb->io); + return fb->io; + } else { + VALUE result = rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb)); + fbuffer_free(fb); + return result; + } } #endif #endif diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index c4f356ac..d5c8bfd4 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -54,7 +54,7 @@ struct generate_json_data { }; static VALUE cState_from_state_s(VALUE self, VALUE opts); -static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func); +static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func, VALUE io); static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj); static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj); static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj); @@ -71,6 +71,31 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data static int usascii_encindex, utf8_encindex, binary_encindex; +#ifdef RBIMPL_ATTR_NORETURN +RBIMPL_ATTR_NORETURN() +#endif +static void raise_generator_error_str(VALUE invalid_object, VALUE str) +{ + VALUE exc = rb_exc_new_str(eGeneratorError, str); + rb_ivar_set(exc, rb_intern("@invalid_object"), invalid_object); + rb_exc_raise(exc); +} + +#ifdef RBIMPL_ATTR_NORETURN +RBIMPL_ATTR_NORETURN() +#endif +#ifdef RBIMPL_ATTR_FORMAT +RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3) +#endif +static void raise_generator_error(VALUE invalid_object, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + VALUE str = rb_vsprintf(fmt, args); + va_end(args); + raise_generator_error_str(invalid_object, str); +} + /* Converts in_string to a JSON string (without the wrapping '"' * characters) in FBuffer out_buffer. * @@ -130,7 +155,7 @@ static void convert_UTF8_to_JSON(FBuffer *out_buffer, VALUE str, const char esca } case 3: { unsigned char b2 = ptr[pos + 1]; - if (RB_UNLIKELY(out_script_safe && b2 == 0x80)) { + if (RB_UNLIKELY(out_script_safe && ch == 0xE2 && b2 == 0x80)) { unsigned char b3 = ptr[pos + 2]; if (b3 == 0xA8) { FLUSH_POS(3); @@ -453,7 +478,7 @@ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self) { rb_check_arity(argc, 0, 1); VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); - return cState_partial_generate(Vstate, self, generate_json_object); + return cState_partial_generate(Vstate, self, generate_json_object, Qfalse); } /* @@ -467,7 +492,7 @@ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self) static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) { rb_check_arity(argc, 0, 1); VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); - return cState_partial_generate(Vstate, self, generate_json_array); + return cState_partial_generate(Vstate, self, generate_json_array, Qfalse); } #ifdef RUBY_INTEGER_UNIFICATION @@ -480,7 +505,7 @@ static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self) { rb_check_arity(argc, 0, 1); VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); - return cState_partial_generate(Vstate, self, generate_json_integer); + return cState_partial_generate(Vstate, self, generate_json_integer, Qfalse); } #else @@ -493,7 +518,7 @@ static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self) { rb_check_arity(argc, 0, 1); VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); - return cState_partial_generate(Vstate, self, generate_json_fixnum); + return cState_partial_generate(Vstate, self, generate_json_fixnum, Qfalse); } /* @@ -505,7 +530,7 @@ static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self) { rb_check_arity(argc, 0, 1); VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); - return cState_partial_generate(Vstate, self, generate_json_bignum); + return cState_partial_generate(Vstate, self, generate_json_bignum, Qfalse); } #endif @@ -518,7 +543,7 @@ static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self) { rb_check_arity(argc, 0, 1); VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); - return cState_partial_generate(Vstate, self, generate_json_float); + return cState_partial_generate(Vstate, self, generate_json_float, Qfalse); } /* @@ -543,7 +568,7 @@ static VALUE mString_to_json(int argc, VALUE *argv, VALUE self) { rb_check_arity(argc, 0, 1); VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); - return cState_partial_generate(Vstate, self, generate_json_string); + return cState_partial_generate(Vstate, self, generate_json_string, Qfalse); } /* @@ -638,7 +663,7 @@ static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "01", &state); Check_Type(string, T_STRING); state = cState_from_state_s(cState, state); - return cState_partial_generate(state, string, generate_json_string); + return cState_partial_generate(state, string, generate_json_string, Qfalse); } static void State_mark(void *ptr) @@ -867,6 +892,17 @@ static inline int enc_utf8_compatible_p(int enc_idx) return 0; } +static VALUE encode_json_string_try(VALUE str) +{ + return rb_funcall(str, i_encode, 1, Encoding_UTF_8); +} + +static VALUE encode_json_string_rescue(VALUE str, VALUE exception) +{ + raise_generator_error_str(str, rb_funcall(exception, rb_intern("message"), 0)); + return Qundef; +} + static inline VALUE ensure_valid_encoding(VALUE str) { int encindex = RB_ENCODING_GET(str); @@ -886,7 +922,7 @@ static inline VALUE ensure_valid_encoding(VALUE str) } } - str = rb_funcall(str, i_encode, 1, Encoding_UTF_8); + str = rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str); } return str; } @@ -909,7 +945,7 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat } break; default: - rb_raise(rb_path2class("JSON::GeneratorError"), "source sequence is illegal/malformed utf-8"); + raise_generator_error(obj, "source sequence is illegal/malformed utf-8"); break; } fbuffer_append_char(buffer, '"'); @@ -957,10 +993,8 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data char allow_nan = state->allow_nan; VALUE tmp = rb_funcall(obj, i_to_s, 0); if (!allow_nan) { - if (isinf(value)) { - rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", tmp); - } else if (isnan(value)) { - rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", tmp); + if (isinf(value) || isnan(value)) { + raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", tmp); } } fbuffer_append_str(buffer, tmp); @@ -1008,7 +1042,7 @@ static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON default: general: if (state->strict) { - rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", CLASS_OF(obj)); + raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", CLASS_OF(obj)); } else if (rb_respond_to(obj, i_to_json)) { tmp = rb_funcall(obj, i_to_json, 1, vstate_get(data)); Check_Type(tmp, T_STRING); @@ -1036,21 +1070,19 @@ static VALUE generate_json_rescue(VALUE d, VALUE exc) struct generate_json_data *data = (struct generate_json_data *)d; fbuffer_free(data->buffer); - if (RBASIC_CLASS(exc) == rb_path2class("Encoding::UndefinedConversionError")) { - exc = rb_exc_new_str(eGeneratorError, rb_funcall(exc, rb_intern("message"), 0)); - } - rb_exc_raise(exc); return Qundef; } -static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func) +static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func, VALUE io) { GET_STATE(self); char stack_buffer[FBUFFER_STACK_SIZE]; - FBuffer buffer = {0}; + FBuffer buffer = { + .io = RTEST(io) ? io : Qfalse, + }; fbuffer_stack_init(&buffer, state->buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE); struct generate_json_data data = { @@ -1062,19 +1094,12 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func) }; rb_rescue(generate_json_try, (VALUE)&data, generate_json_rescue, (VALUE)&data); - return fbuffer_to_s(&buffer); + return fbuffer_finalize(&buffer); } -/* - * call-seq: generate(obj) - * - * Generates a valid JSON document from object +obj+ and returns the - * result. If no valid JSON document can be created this method raises a - * GeneratorError exception. - */ -static VALUE cState_generate(VALUE self, VALUE obj) +static VALUE cState_generate(VALUE self, VALUE obj, VALUE io) { - VALUE result = cState_partial_generate(self, obj, generate_json); + VALUE result = cState_partial_generate(self, obj, generate_json, io); GET_STATE(self); (void)state; return result; @@ -1502,14 +1527,16 @@ static VALUE cState_configure(VALUE self, VALUE opts) return self; } -static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts) +static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io) { JSON_Generator_State state = {0}; state_init(&state); configure_state(&state, opts); char stack_buffer[FBUFFER_STACK_SIZE]; - FBuffer buffer = {0}; + FBuffer buffer = { + .io = RTEST(io) ? io : Qfalse, + }; fbuffer_stack_init(&buffer, state.buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE); struct generate_json_data data = { @@ -1521,7 +1548,7 @@ static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts) }; rb_rescue(generate_json_try, (VALUE)&data, generate_json_rescue, (VALUE)&data); - return fbuffer_to_s(&buffer); + return fbuffer_finalize(&buffer); } /* @@ -1540,10 +1567,11 @@ void Init_generator(void) VALUE mExt = rb_define_module_under(mJSON, "Ext"); VALUE mGenerator = rb_define_module_under(mExt, "Generator"); + rb_global_variable(&eGeneratorError); eGeneratorError = rb_path2class("JSON::GeneratorError"); + + rb_global_variable(&eNestingError); 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); @@ -1583,9 +1611,9 @@ void Init_generator(void) rb_define_method(cState, "depth=", cState_depth_set, 1); rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0); rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1); - rb_define_method(cState, "generate", cState_generate, 1); + rb_define_private_method(cState, "_generate", cState_generate, 2); - rb_define_singleton_method(cState, "generate", cState_m_generate, 2); + rb_define_singleton_method(cState, "generate", cState_m_generate, 3); VALUE mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods"); diff --git a/ext/json/ext/parser/extconf.rb b/ext/json/ext/parser/extconf.rb index 4c1ac52a..7bbcc33f 100644 --- a/ext/json/ext/parser/extconf.rb +++ b/ext/json/ext/parser/extconf.rb @@ -3,7 +3,6 @@ have_func("rb_enc_interned_str", "ruby.h") # RUBY_VERSION >= 3.0 have_func("rb_hash_new_capa", "ruby.h") # RUBY_VERSION >= 3.2 -have_func("rb_gc_mark_locations", "ruby.h") # Missing on TruffleRuby have_func("rb_hash_bulk_insert", "ruby.h") # Missing on TruffleRuby have_func("rb_category_warn", "ruby.h") # Missing on TruffleRuby diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index b6252556..dff021bd 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -28,19 +28,6 @@ static const char deprecated_create_additions_warning[] = "and will be removed in 3.0, use JSON.unsafe_load or explicitly " "pass `create_additions: true`"; -#ifndef HAVE_RB_GC_MARK_LOCATIONS -// For TruffleRuby -void rb_gc_mark_locations(const VALUE *start, const VALUE *end) -{ - VALUE *value = start; - - while (value < end) { - rb_gc_mark(*value); - value++; - } -} -#endif - #ifndef HAVE_RB_HASH_BULK_INSERT // For TruffleRuby void rb_hash_bulk_insert(long count, const VALUE *pairs, VALUE hash) @@ -266,7 +253,10 @@ static inline void rvalue_stack_pop(rvalue_stack *stack, long count) static void rvalue_stack_mark(void *ptr) { rvalue_stack *stack = (rvalue_stack *)ptr; - rb_gc_mark_locations(stack->ptr, stack->ptr + stack->head); + long index; + for (index = 0; index < stack->head; index++) { + rb_gc_mark(stack->ptr[index]); + } } static void rvalue_stack_free(void *ptr) @@ -449,11 +439,11 @@ static void raise_parse_error(const char *format, const char *start) -#line 475 "parser.rl" +#line 465 "parser.rl" -#line 457 "parser.c" +#line 447 "parser.c" enum {JSON_object_start = 1}; enum {JSON_object_first_final = 32}; enum {JSON_object_error = 0}; @@ -461,7 +451,7 @@ enum {JSON_object_error = 0}; enum {JSON_object_en_main = 1}; -#line 515 "parser.rl" +#line 505 "parser.rl" #define PUSH(result) rvalue_stack_push(json->stack, result, &json->stack_handle, &json->stack) @@ -477,14 +467,14 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu long stack_head = json->stack->head; -#line 481 "parser.c" +#line 471 "parser.c" { cs = JSON_object_start; } -#line 530 "parser.rl" +#line 520 "parser.rl" -#line 488 "parser.c" +#line 478 "parser.c" { short _widec; if ( p == pe ) @@ -513,7 +503,7 @@ case 2: goto st2; goto st0; tr2: -#line 494 "parser.rl" +#line 484 "parser.rl" { char *np; json->parsing_name = true; @@ -529,7 +519,7 @@ case 2: if ( ++p == pe ) goto _test_eof3; case 3: -#line 533 "parser.c" +#line 523 "parser.c" switch( (*p) ) { case 13: goto st3; case 32: goto st3; @@ -596,7 +586,7 @@ case 8: goto st8; goto st0; tr11: -#line 483 "parser.rl" +#line 473 "parser.rl" { char *np = JSON_parse_value(json, p, pe, result, current_nesting); if (np == NULL) { @@ -610,20 +600,20 @@ case 8: if ( ++p == pe ) goto _test_eof9; case 9: -#line 614 "parser.c" +#line 604 "parser.c" _widec = (*p); if ( (*p) < 13 ) { if ( (*p) > 9 ) { if ( 10 <= (*p) && (*p) <= 10 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 492 "parser.rl" +#line 482 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) >= 9 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 492 "parser.rl" +#line 482 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) > 13 ) { @@ -631,26 +621,26 @@ case 9: if ( 32 <= (*p) && (*p) <= 32 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 492 "parser.rl" +#line 482 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) > 44 ) { if ( 47 <= (*p) && (*p) <= 47 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 492 "parser.rl" +#line 482 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 492 "parser.rl" +#line 482 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 492 "parser.rl" +#line 482 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -671,14 +661,14 @@ case 9: goto st10; goto st0; tr4: -#line 505 "parser.rl" +#line 495 "parser.rl" { p--; {p++; cs = 32; goto _out;} } goto st32; st32: if ( ++p == pe ) goto _test_eof32; case 32: -#line 682 "parser.c" +#line 672 "parser.c" goto st0; st10: if ( ++p == pe ) @@ -780,13 +770,13 @@ case 20: if ( 47 <= (*p) && (*p) <= 47 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 492 "parser.rl" +#line 482 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) >= 42 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 492 "parser.rl" +#line 482 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -805,20 +795,20 @@ case 21: if ( (*p) <= 41 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 492 "parser.rl" +#line 482 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) > 42 ) { if ( 43 <= (*p) ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 492 "parser.rl" +#line 482 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 492 "parser.rl" +#line 482 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -841,13 +831,13 @@ case 22: if ( 42 <= (*p) && (*p) <= 42 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 492 "parser.rl" +#line 482 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 492 "parser.rl" +#line 482 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) > 46 ) { @@ -855,19 +845,19 @@ case 22: if ( 48 <= (*p) ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 492 "parser.rl" +#line 482 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) >= 47 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 492 "parser.rl" +#line 482 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 492 "parser.rl" +#line 482 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -891,20 +881,20 @@ case 23: if ( (*p) <= 9 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 492 "parser.rl" +#line 482 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) > 10 ) { if ( 11 <= (*p) ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 492 "parser.rl" +#line 482 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 492 "parser.rl" +#line 482 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -1018,7 +1008,7 @@ case 31: _out: {} } -#line 531 "parser.rl" +#line 521 "parser.rl" if (cs >= JSON_object_first_final) { long count = json->stack->head - stack_head; @@ -1069,7 +1059,7 @@ case 31: } -#line 1073 "parser.c" +#line 1063 "parser.c" enum {JSON_value_start = 1}; enum {JSON_value_first_final = 29}; enum {JSON_value_error = 0}; @@ -1077,7 +1067,7 @@ enum {JSON_value_error = 0}; enum {JSON_value_en_main = 1}; -#line 664 "parser.rl" +#line 654 "parser.rl" static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) @@ -1085,14 +1075,14 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul int cs = EVIL; -#line 1089 "parser.c" +#line 1079 "parser.c" { cs = JSON_value_start; } -#line 671 "parser.rl" +#line 661 "parser.rl" -#line 1096 "parser.c" +#line 1086 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1126,7 +1116,7 @@ case 1: cs = 0; goto _out; tr2: -#line 609 "parser.rl" +#line 599 "parser.rl" { char *np = JSON_parse_string(json, p, pe, result); if (np == NULL) { @@ -1138,7 +1128,7 @@ cs = 0; } goto st29; tr3: -#line 619 "parser.rl" +#line 609 "parser.rl" { char *np; if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) { @@ -1158,7 +1148,7 @@ cs = 0; } goto st29; tr7: -#line 637 "parser.rl" +#line 627 "parser.rl" { char *np; json->in_array++; @@ -1168,7 +1158,7 @@ cs = 0; } goto st29; tr11: -#line 645 "parser.rl" +#line 635 "parser.rl" { char *np; np = JSON_parse_object(json, p, pe, result, current_nesting + 1); @@ -1176,7 +1166,7 @@ cs = 0; } goto st29; tr25: -#line 602 "parser.rl" +#line 592 "parser.rl" { if (json->allow_nan) { *result = CInfinity; @@ -1186,7 +1176,7 @@ cs = 0; } goto st29; tr27: -#line 595 "parser.rl" +#line 585 "parser.rl" { if (json->allow_nan) { *result = CNaN; @@ -1196,19 +1186,19 @@ cs = 0; } goto st29; tr31: -#line 589 "parser.rl" +#line 579 "parser.rl" { *result = Qfalse; } goto st29; tr34: -#line 586 "parser.rl" +#line 576 "parser.rl" { *result = Qnil; } goto st29; tr37: -#line 592 "parser.rl" +#line 582 "parser.rl" { *result = Qtrue; } @@ -1217,9 +1207,9 @@ cs = 0; if ( ++p == pe ) goto _test_eof29; case 29: -#line 651 "parser.rl" +#line 641 "parser.rl" { p--; {p++; cs = 29; goto _out;} } -#line 1223 "parser.c" +#line 1213 "parser.c" switch( (*p) ) { case 13: goto st29; case 32: goto st29; @@ -1460,7 +1450,7 @@ case 28: _out: {} } -#line 672 "parser.rl" +#line 662 "parser.rl" if (json->freeze) { OBJ_FREEZE(*result); @@ -1475,7 +1465,7 @@ case 28: } -#line 1479 "parser.c" +#line 1469 "parser.c" enum {JSON_integer_start = 1}; enum {JSON_integer_first_final = 3}; enum {JSON_integer_error = 0}; @@ -1483,7 +1473,7 @@ enum {JSON_integer_error = 0}; enum {JSON_integer_en_main = 1}; -#line 693 "parser.rl" +#line 683 "parser.rl" #define MAX_FAST_INTEGER_SIZE 18 @@ -1523,7 +1513,7 @@ static char *JSON_decode_integer(JSON_Parser *json, char *p, VALUE *result) } -#line 1527 "parser.c" +#line 1517 "parser.c" enum {JSON_float_start = 1}; enum {JSON_float_first_final = 6}; enum {JSON_float_error = 0}; @@ -1531,7 +1521,7 @@ enum {JSON_float_error = 0}; enum {JSON_float_en_main = 1}; -#line 745 "parser.rl" +#line 735 "parser.rl" static char *JSON_parse_number(JSON_Parser *json, char *p, char *pe, VALUE *result) @@ -1540,15 +1530,15 @@ static char *JSON_parse_number(JSON_Parser *json, char *p, char *pe, VALUE *resu bool is_float = false; -#line 1544 "parser.c" +#line 1534 "parser.c" { cs = JSON_float_start; } -#line 753 "parser.rl" +#line 743 "parser.rl" json->memo = p; -#line 1552 "parser.c" +#line 1542 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1588,24 +1578,24 @@ case 6: goto st0; goto tr7; tr7: -#line 737 "parser.rl" +#line 727 "parser.rl" { p--; {p++; cs = 7; goto _out;} } goto st7; st7: if ( ++p == pe ) goto _test_eof7; case 7: -#line 1599 "parser.c" +#line 1589 "parser.c" goto st0; tr8: -#line 738 "parser.rl" +#line 728 "parser.rl" { is_float = true; } goto st3; st3: if ( ++p == pe ) goto _test_eof3; case 3: -#line 1609 "parser.c" +#line 1599 "parser.c" if ( 48 <= (*p) && (*p) <= 57 ) goto st8; goto st0; @@ -1624,14 +1614,14 @@ case 8: goto st0; goto tr7; tr9: -#line 738 "parser.rl" +#line 728 "parser.rl" { is_float = true; } goto st4; st4: if ( ++p == pe ) goto _test_eof4; case 4: -#line 1635 "parser.c" +#line 1625 "parser.c" switch( (*p) ) { case 43: goto st5; case 45: goto st5; @@ -1688,7 +1678,7 @@ case 10: _out: {} } -#line 755 "parser.rl" +#line 745 "parser.rl" if (cs >= JSON_float_first_final) { if (!is_float) { @@ -1744,7 +1734,7 @@ case 10: -#line 1748 "parser.c" +#line 1738 "parser.c" enum {JSON_array_start = 1}; enum {JSON_array_first_final = 22}; enum {JSON_array_error = 0}; @@ -1752,7 +1742,7 @@ enum {JSON_array_error = 0}; enum {JSON_array_en_main = 1}; -#line 835 "parser.rl" +#line 825 "parser.rl" static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) @@ -1765,14 +1755,14 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul long stack_head = json->stack->head; -#line 1769 "parser.c" +#line 1759 "parser.c" { cs = JSON_array_start; } -#line 847 "parser.rl" +#line 837 "parser.rl" -#line 1776 "parser.c" +#line 1766 "parser.c" { short _widec; if ( p == pe ) @@ -1812,7 +1802,7 @@ case 2: goto st2; goto st0; tr2: -#line 815 "parser.rl" +#line 805 "parser.rl" { VALUE v = Qnil; char *np = JSON_parse_value(json, p, pe, &v, current_nesting); @@ -1827,12 +1817,12 @@ case 2: if ( ++p == pe ) goto _test_eof3; case 3: -#line 1831 "parser.c" +#line 1821 "parser.c" _widec = (*p); if ( 44 <= (*p) && (*p) <= 44 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 825 "parser.rl" +#line 815 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -1879,14 +1869,14 @@ case 7: goto st3; goto st7; tr4: -#line 827 "parser.rl" +#line 817 "parser.rl" { p--; {p++; cs = 22; goto _out;} } goto st22; st22: if ( ++p == pe ) goto _test_eof22; case 22: -#line 1890 "parser.c" +#line 1880 "parser.c" goto st0; st8: if ( ++p == pe ) @@ -1954,13 +1944,13 @@ case 13: if ( 10 <= (*p) && (*p) <= 10 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 825 "parser.rl" +#line 815 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) >= 9 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 825 "parser.rl" +#line 815 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) > 13 ) { @@ -1968,19 +1958,19 @@ case 13: if ( 47 <= (*p) && (*p) <= 47 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 825 "parser.rl" +#line 815 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) >= 32 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 825 "parser.rl" +#line 815 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 825 "parser.rl" +#line 815 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -2019,13 +2009,13 @@ case 14: if ( 47 <= (*p) && (*p) <= 47 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 825 "parser.rl" +#line 815 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) >= 42 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 825 "parser.rl" +#line 815 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -2044,20 +2034,20 @@ case 15: if ( (*p) <= 41 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 825 "parser.rl" +#line 815 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) > 42 ) { if ( 43 <= (*p) ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 825 "parser.rl" +#line 815 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 825 "parser.rl" +#line 815 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -2080,13 +2070,13 @@ case 16: if ( 42 <= (*p) && (*p) <= 42 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 825 "parser.rl" +#line 815 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 825 "parser.rl" +#line 815 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) > 46 ) { @@ -2094,19 +2084,19 @@ case 16: if ( 48 <= (*p) ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 825 "parser.rl" +#line 815 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) >= 47 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 825 "parser.rl" +#line 815 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 825 "parser.rl" +#line 815 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -2130,20 +2120,20 @@ case 17: if ( (*p) <= 9 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 825 "parser.rl" +#line 815 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) > 10 ) { if ( 11 <= (*p) ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 825 "parser.rl" +#line 815 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 825 "parser.rl" +#line 815 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -2215,7 +2205,7 @@ case 21: _out: {} } -#line 848 "parser.rl" +#line 838 "parser.rl" if(cs >= JSON_array_first_final) { long count = json->stack->head - stack_head; @@ -2409,7 +2399,7 @@ static VALUE json_string_unescape(JSON_Parser *json, char *string, char *stringE } -#line 2413 "parser.c" +#line 2403 "parser.c" enum {JSON_string_start = 1}; enum {JSON_string_first_final = 9}; enum {JSON_string_error = 0}; @@ -2417,7 +2407,7 @@ enum {JSON_string_error = 0}; enum {JSON_string_en_main = 1}; -#line 1071 "parser.rl" +#line 1061 "parser.rl" static int @@ -2438,15 +2428,15 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu VALUE match_string; -#line 2442 "parser.c" +#line 2432 "parser.c" { cs = JSON_string_start; } -#line 1091 "parser.rl" +#line 1081 "parser.rl" json->memo = p; -#line 2450 "parser.c" +#line 2440 "parser.c" { if ( p == pe ) goto _test_eof; @@ -2471,14 +2461,14 @@ case 2: goto st0; goto st2; tr2: -#line 1053 "parser.rl" +#line 1043 "parser.rl" { *result = json_string_fastpath(json, json->memo + 1, p, json->parsing_name, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names); {p = (( p + 1))-1;} p--; {p++; cs = 9; goto _out;} } -#line 1046 "parser.rl" +#line 1036 "parser.rl" { *result = json_string_unescape(json, json->memo + 1, p, json->parsing_name, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names); {p = (( p + 1))-1;} @@ -2487,7 +2477,7 @@ case 2: } goto st9; tr6: -#line 1046 "parser.rl" +#line 1036 "parser.rl" { *result = json_string_unescape(json, json->memo + 1, p, json->parsing_name, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names); {p = (( p + 1))-1;} @@ -2499,7 +2489,7 @@ case 2: if ( ++p == pe ) goto _test_eof9; case 9: -#line 2503 "parser.c" +#line 2493 "parser.c" goto st0; st3: if ( ++p == pe ) @@ -2587,7 +2577,7 @@ case 8: _out: {} } -#line 1093 "parser.rl" +#line 1083 "parser.rl" if (json->create_additions && RTEST(match_string = json->match_string)) { VALUE klass; @@ -2740,7 +2730,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } -#line 2744 "parser.c" +#line 2734 "parser.c" enum {JSON_start = 1}; enum {JSON_first_final = 10}; enum {JSON_error = 0}; @@ -2748,7 +2738,7 @@ enum {JSON_error = 0}; enum {JSON_en_main = 1}; -#line 1259 "parser.rl" +#line 1249 "parser.rl" /* @@ -2777,16 +2767,16 @@ static VALUE cParser_parse(VALUE self) json->stack = &stack; -#line 2781 "parser.c" +#line 2771 "parser.c" { cs = JSON_start; } -#line 1287 "parser.rl" +#line 1277 "parser.rl" p = json->source; pe = p + json->len; -#line 2790 "parser.c" +#line 2780 "parser.c" { if ( p == pe ) goto _test_eof; @@ -2820,7 +2810,7 @@ case 1: cs = 0; goto _out; tr2: -#line 1251 "parser.rl" +#line 1241 "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;} @@ -2830,7 +2820,7 @@ cs = 0; if ( ++p == pe ) goto _test_eof10; case 10: -#line 2834 "parser.c" +#line 2824 "parser.c" switch( (*p) ) { case 13: goto st10; case 32: goto st10; @@ -2919,7 +2909,7 @@ case 9: _out: {} } -#line 1290 "parser.rl" +#line 1280 "parser.rl" if (json->stack_handle) { rvalue_stack_eagerly_release(json->stack_handle); @@ -2955,16 +2945,16 @@ static VALUE cParser_m_parse(VALUE klass, VALUE source, VALUE opts) json->stack = &stack; -#line 2959 "parser.c" +#line 2949 "parser.c" { cs = JSON_start; } -#line 1325 "parser.rl" +#line 1315 "parser.rl" p = json->source; pe = p + json->len; -#line 2968 "parser.c" +#line 2958 "parser.c" { if ( p == pe ) goto _test_eof; @@ -2998,7 +2988,7 @@ case 1: cs = 0; goto _out; tr2: -#line 1251 "parser.rl" +#line 1241 "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;} @@ -3008,7 +2998,7 @@ cs = 0; if ( ++p == pe ) goto _test_eof10; case 10: -#line 3012 "parser.c" +#line 3002 "parser.c" switch( (*p) ) { case 13: goto st10; case 32: goto st10; @@ -3097,7 +3087,7 @@ case 9: _out: {} } -#line 1328 "parser.rl" +#line 1318 "parser.rl" if (json->stack_handle) { rvalue_stack_eagerly_release(json->stack_handle); @@ -3122,8 +3112,10 @@ static void JSON_mark(void *ptr) rb_gc_mark(json->match_string); rb_gc_mark(json->stack_handle); - const VALUE *name_cache_entries = &json->name_cache.entries[0]; - rb_gc_mark_locations(name_cache_entries, name_cache_entries + json->name_cache.length); + long index; + for (index = 0; index < json->name_cache.length; index++) { + rb_gc_mark(json->name_cache.entries[index]); + } } static void JSON_free(void *ptr) diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl index eab60b91..ac949345 100644 --- a/ext/json/ext/parser/parser.rl +++ b/ext/json/ext/parser/parser.rl @@ -26,19 +26,6 @@ static const char deprecated_create_additions_warning[] = "and will be removed in 3.0, use JSON.unsafe_load or explicitly " "pass `create_additions: true`"; -#ifndef HAVE_RB_GC_MARK_LOCATIONS -// For TruffleRuby -void rb_gc_mark_locations(const VALUE *start, const VALUE *end) -{ - VALUE *value = start; - - while (value < end) { - rb_gc_mark(*value); - value++; - } -} -#endif - #ifndef HAVE_RB_HASH_BULK_INSERT // For TruffleRuby void rb_hash_bulk_insert(long count, const VALUE *pairs, VALUE hash) @@ -264,7 +251,10 @@ static inline void rvalue_stack_pop(rvalue_stack *stack, long count) static void rvalue_stack_mark(void *ptr) { rvalue_stack *stack = (rvalue_stack *)ptr; - rb_gc_mark_locations(stack->ptr, stack->ptr + stack->head); + long index; + for (index = 0; index < stack->head; index++) { + rb_gc_mark(stack->ptr[index]); + } } static void rvalue_stack_free(void *ptr) @@ -1349,8 +1339,10 @@ static void JSON_mark(void *ptr) rb_gc_mark(json->match_string); rb_gc_mark(json->stack_handle); - const VALUE *name_cache_entries = &json->name_cache.entries[0]; - rb_gc_mark_locations(name_cache_entries, name_cache_entries + json->name_cache.length); + long index; + for (index = 0; index < json->name_cache.length; index++) { + rb_gc_mark(json->name_cache.entries[index]); + } } static void JSON_free(void *ptr) diff --git a/java/src/json/ext/ByteListDirectOutputStream.java b/java/src/json/ext/ByteListDirectOutputStream.java new file mode 100644 index 00000000..178cf11c --- /dev/null +++ b/java/src/json/ext/ByteListDirectOutputStream.java @@ -0,0 +1,16 @@ +package json.ext; + +import org.jcodings.Encoding; +import org.jruby.util.ByteList; + +import java.io.ByteArrayOutputStream; + +public class ByteListDirectOutputStream extends ByteArrayOutputStream { + ByteListDirectOutputStream(int size) { + super(size); + } + + public ByteList toByteListDirect(Encoding encoding) { + return new ByteList(buf, 0, count, encoding, false); + } +} diff --git a/java/src/json/ext/ByteListTranscoder.java b/java/src/json/ext/ByteListTranscoder.java index 6f6ab66c..78d8037c 100644 --- a/java/src/json/ext/ByteListTranscoder.java +++ b/java/src/json/ext/ByteListTranscoder.java @@ -9,13 +9,14 @@ import org.jruby.runtime.ThreadContext; import org.jruby.util.ByteList; +import java.io.IOException; +import java.io.OutputStream; + /** * A class specialized in transcoding a certain String format into another, * using UTF-8 ByteLists as both input and output. */ abstract class ByteListTranscoder { - protected final ThreadContext context; - protected ByteList src; protected int srcEnd; /** Position where the last read character started */ @@ -23,7 +24,6 @@ abstract class ByteListTranscoder { /** Position of the next character to read */ protected int pos; - private ByteList out; /** * When a character that can be copied straight into the output is found, * its index is stored on this variable, and copying is delayed until @@ -33,20 +33,15 @@ abstract class ByteListTranscoder { */ private int quoteStart = -1; - protected ByteListTranscoder(ThreadContext context) { - this.context = context; + protected void init(ByteList src) { + this.init(src, 0, src.length()); } - protected void init(ByteList src, ByteList out) { - this.init(src, 0, src.length(), out); - } - - protected void init(ByteList src, int start, int end, ByteList out) { + protected void init(ByteList src, int start, int end) { this.src = src; this.pos = start; this.charStart = start; this.srcEnd = end; - this.out = out; } /** @@ -67,52 +62,57 @@ private char next() { * Reads an UTF-8 character from the input and returns its code point, * while advancing the input position. * - *
Raises an {@link #invalidUtf8()} exception if an invalid byte + *
Raises an {@link #invalidUtf8(ThreadContext)} exception if an invalid byte * is found. */ - protected int readUtf8Char() { + protected int readUtf8Char(ThreadContext context) { charStart = pos; char head = next(); if (head <= 0x7f) { // 0b0xxxxxxx (ASCII) return head; } if (head <= 0xbf) { // 0b10xxxxxx - throw invalidUtf8(); // tail byte with no head + throw invalidUtf8(context); // tail byte with no head } if (head <= 0xdf) { // 0b110xxxxx - ensureMin(1); + ensureMin(context, 1); int cp = ((head & 0x1f) << 6) - | nextPart(); - if (cp < 0x0080) throw invalidUtf8(); + | nextPart(context); + if (cp < 0x0080) throw invalidUtf8(context); return cp; } if (head <= 0xef) { // 0b1110xxxx - ensureMin(2); + ensureMin(context, 2); int cp = ((head & 0x0f) << 12) - | (nextPart() << 6) - | nextPart(); - if (cp < 0x0800) throw invalidUtf8(); + | (nextPart(context) << 6) + | nextPart(context); + if (cp < 0x0800) throw invalidUtf8(context); return cp; } if (head <= 0xf7) { // 0b11110xxx - ensureMin(3); + ensureMin(context, 3); int cp = ((head & 0x07) << 18) - | (nextPart() << 12) - | (nextPart() << 6) - | nextPart(); - if (!Character.isValidCodePoint(cp)) throw invalidUtf8(); + | (nextPart(context) << 12) + | (nextPart(context) << 6) + | nextPart(context); + if (!Character.isValidCodePoint(cp)) throw invalidUtf8(context); return cp; } // 0b11111xxx? - throw invalidUtf8(); + throw invalidUtf8(context); + } + + protected int readASCIIChar() { + charStart = pos; + return next(); } /** * Throws a GeneratorError if the input list doesn't have at least this * many bytes left. */ - protected void ensureMin(int n) { - if (pos + n > srcEnd) throw incompleteUtf8(); + protected void ensureMin(ThreadContext context, int n) { + if (pos + n > srcEnd) throw incompleteUtf8(context); } /** @@ -121,10 +121,10 @@ protected void ensureMin(int n) { * *
Throws a GeneratorError if the byte is not a valid tail.
*/
- private int nextPart() {
+ private int nextPart(ThreadContext context) {
char c = next();
// tail bytes must be 0b10xxxxxx
- if ((c & 0xc0) != 0x80) throw invalidUtf8();
+ if ((c & 0xc0) != 0x80) throw invalidUtf8(context);
return c & 0x3f;
}
@@ -142,25 +142,21 @@ protected void quoteStart() {
* recently read character, or {@link #charStart} to quote
* until the character before it.
*/
- protected void quoteStop(int endPos) {
+ protected void quoteStop(int endPos) throws IOException {
if (quoteStart != -1) {
- out.append(src, quoteStart, endPos - quoteStart);
+ append(src.unsafeBytes(), src.begin() + quoteStart, endPos - quoteStart);
quoteStart = -1;
}
}
- protected void append(int b) {
- out.append(b);
- }
+ protected abstract void append(int b) throws IOException;
- protected void append(byte[] origin, int start, int length) {
- out.append(origin, start, length);
- }
+ protected abstract void append(byte[] origin, int start, int length) throws IOException;
- protected abstract RaiseException invalidUtf8();
+ protected abstract RaiseException invalidUtf8(ThreadContext context);
- protected RaiseException incompleteUtf8() {
- return invalidUtf8();
+ protected RaiseException incompleteUtf8(ThreadContext context) {
+ return invalidUtf8(context);
}
}
diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java
index f76dcb38..4ab92805 100644
--- a/java/src/json/ext/Generator.java
+++ b/java/src/json/ext/Generator.java
@@ -5,21 +5,43 @@
*/
package json.ext;
+import org.jcodings.Encoding;
+import org.jcodings.specific.ASCIIEncoding;
+import org.jcodings.specific.USASCIIEncoding;
+import org.jcodings.specific.UTF8Encoding;
import org.jruby.Ruby;
import org.jruby.RubyArray;
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.RubyString;
+import org.jruby.RubySymbol;
+import org.jruby.RubyException;
+import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import org.jruby.exceptions.RaiseException;
+import org.jruby.util.ConvertBytes;
+import org.jruby.util.IOOutputStream;
+import org.jruby.util.StringSupport;
+import org.jruby.util.TypeConverter;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigInteger;
+
+import static java.nio.charset.StandardCharsets.*;
public final class Generator {
+
+ private static final int IO_BUFFER_SIZE = 8192;
+
private Generator() {
throw new RuntimeException();
}
@@ -27,33 +49,47 @@ private Generator() {
/**
* Encodes the given object as a JSON string, using the given handler.
*/
- static Note that anything called indirectly (via {@link GENERIC_HANDLER})
+ * Note that anything called indirectly (via {@link #GENERIC_HANDLER})
* won't be part of the session.
*/
static class Session {
- private final ThreadContext context;
private GeneratorState state;
private IRubyObject possibleState;
private RuntimeInfo info;
private StringEncoder stringEncoder;
- private boolean tainted = false;
- private boolean untrusted = false;
-
- Session(ThreadContext context, GeneratorState state) {
- this.context = context;
+ Session(GeneratorState state) {
this.state = state;
}
- Session(ThreadContext context, IRubyObject possibleState) {
- this.context = context;
+ Session(IRubyObject possibleState) {
this.possibleState = possibleState == null || possibleState.isNil()
? null : possibleState;
}
- public ThreadContext getContext() {
- return context;
- }
-
- public Ruby getRuntime() {
- return context.getRuntime();
- }
-
- public GeneratorState getState() {
+ public GeneratorState getState(ThreadContext context) {
if (state == null) {
- state = GeneratorState.fromState(context, getInfo(), possibleState);
+ state = GeneratorState.fromState(context, getInfo(context), possibleState);
}
return state;
}
- public RuntimeInfo getInfo() {
- if (info == null) info = RuntimeInfo.forRuntime(getRuntime());
+ public RuntimeInfo getInfo(ThreadContext context) {
+ if (info == null) info = RuntimeInfo.forRuntime(context.runtime);
return info;
}
- public StringEncoder getStringEncoder() {
+ public StringEncoder getStringEncoder(ThreadContext context) {
if (stringEncoder == null) {
- stringEncoder = new StringEncoder(context, getState().asciiOnly(), getState().scriptSafe());
+ GeneratorState state = getState(context);
+ stringEncoder = new StringEncoder(state.asciiOnly(), state.scriptSafe());
}
return stringEncoder;
}
-
- public void infectBy(IRubyObject object) {
- if (object.isTaint()) tainted = true;
- if (object.isUntrusted()) untrusted = true;
- }
-
- public This method creates a JSON text from the result of a call to
* {@link #to_json_raw_object} of this String.
*/
- @JRubyMethod(rest=true)
- public static IRubyObject to_json_raw(ThreadContext context,
- IRubyObject vSelf, IRubyObject[] args) {
+ @JRubyMethod
+ public static IRubyObject to_json_raw(ThreadContext context, IRubyObject vSelf) {
RubyHash obj = toJsonRawObject(context, Utils.ensureString(vSelf));
- return Generator.generateJson(context, obj,
- Generator.HASH_HANDLER, args);
+ return Generator.generateJson(context, obj, Generator.HASH_HANDLER);
+ }
+
+ @JRubyMethod
+ public static IRubyObject to_json_raw(ThreadContext context, IRubyObject vSelf, IRubyObject arg0) {
+ RubyHash obj = toJsonRawObject(context, Utils.ensureString(vSelf));
+ return Generator.generateJson(context, obj, Generator.HASH_HANDLER, arg0);
}
/**
@@ -128,15 +145,14 @@ public static IRubyObject to_json_raw(ThreadContext context,
* method should be used if you want to convert raw strings to JSON
* instead of UTF-8 strings, e.g. binary data.
*/
- @JRubyMethod(rest=true)
- public static IRubyObject to_json_raw_object(ThreadContext context,
- IRubyObject vSelf, IRubyObject[] args) {
+ @JRubyMethod
+ public static IRubyObject to_json_raw_object(ThreadContext context, IRubyObject vSelf) {
return toJsonRawObject(context, Utils.ensureString(vSelf));
}
private static RubyHash toJsonRawObject(ThreadContext context,
RubyString self) {
- Ruby runtime = context.getRuntime();
+ Ruby runtime = context.runtime;
RubyHash result = RubyHash.newHash(runtime);
IRubyObject createId = RuntimeInfo.forRuntime(runtime)
@@ -154,11 +170,10 @@ private static RubyHash toJsonRawObject(ThreadContext context,
return result;
}
- @JRubyMethod(required=1, module=true)
- public static IRubyObject included(ThreadContext context,
- IRubyObject vSelf, IRubyObject module) {
- RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime());
- return module.callMethod(context, "extend", info.stringExtendModule.get());
+ @JRubyMethod(module=true)
+ public static IRubyObject included(ThreadContext context, IRubyObject extendModule, IRubyObject module) {
+ RuntimeInfo info = RuntimeInfo.forRuntime(context.runtime);
+ return module.callMethod(context, "extend", ((RubyModule) extendModule).getConstant("Extend"));
}
}
@@ -170,10 +185,10 @@ public static class StringExtend {
* array for the key "raw"). The Ruby String can be created by this
* module method.
*/
- @JRubyMethod(required=1)
+ @JRubyMethod
public static IRubyObject json_create(ThreadContext context,
IRubyObject vSelf, IRubyObject vHash) {
- Ruby runtime = context.getRuntime();
+ Ruby runtime = context.runtime;
RubyHash o = vHash.convertToHash();
IRubyObject rawData = o.fastARef(runtime.newString("raw"));
if (rawData == null) {
@@ -195,37 +210,50 @@ public static IRubyObject json_create(ThreadContext context,
}
public static class RbTrue {
- @JRubyMethod(rest=true)
- public static IRubyObject to_json(ThreadContext context,
- IRubyObject vSelf, IRubyObject[] args) {
- return Generator.generateJson(context, (RubyBoolean)vSelf,
- Generator.TRUE_HANDLER, args);
+ @JRubyMethod
+ public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf) {
+ return Generator.generateJson(context, (RubyBoolean)vSelf, Generator.TRUE_HANDLER);
+ }
+
+ @JRubyMethod
+ public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf, IRubyObject arg0) {
+ return Generator.generateJson(context, (RubyBoolean)vSelf, Generator.TRUE_HANDLER, arg0);
}
}
public static class RbFalse {
- @JRubyMethod(rest=true)
- public static IRubyObject to_json(ThreadContext context,
- IRubyObject vSelf, IRubyObject[] args) {
- return Generator.generateJson(context, (RubyBoolean)vSelf,
- Generator.FALSE_HANDLER, args);
+ @JRubyMethod
+ public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf) {
+ return Generator.generateJson(context, (RubyBoolean)vSelf, Generator.FALSE_HANDLER);
+ }
+
+ @JRubyMethod
+ public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf, IRubyObject arg0) {
+ return Generator.generateJson(context, (RubyBoolean)vSelf, Generator.FALSE_HANDLER, arg0);
}
}
public static class RbNil {
- @JRubyMethod(rest=true)
- public static IRubyObject to_json(ThreadContext context,
- IRubyObject vSelf, IRubyObject[] args) {
- return Generator.generateJson(context, vSelf,
- Generator.NIL_HANDLER, args);
+ @JRubyMethod
+ public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf) {
+ return Generator.generateJson(context, vSelf, Generator.NIL_HANDLER);
+ }
+
+ @JRubyMethod
+ public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf, IRubyObject arg0) {
+ return Generator.generateJson(context, vSelf, Generator.NIL_HANDLER, arg0);
}
}
public static class RbObject {
- @JRubyMethod(rest=true)
- public static IRubyObject to_json(ThreadContext context,
- IRubyObject self, IRubyObject[] args) {
- return RbString.to_json(context, self.asString(), args);
+ @JRubyMethod
+ public static IRubyObject to_json(ThreadContext context, IRubyObject self) {
+ return RbString.to_json(context, self.asString());
+ }
+
+ @JRubyMethod
+ public static IRubyObject to_json(ThreadContext context, IRubyObject self, IRubyObject arg0) {
+ return RbString.to_json(context, self.asString(), arg0);
}
}
}
diff --git a/java/src/json/ext/GeneratorService.java b/java/src/json/ext/GeneratorService.java
index e665ad14..7d3f86e5 100644
--- a/java/src/json/ext/GeneratorService.java
+++ b/java/src/json/ext/GeneratorService.java
@@ -23,7 +23,8 @@ public boolean basicLoad(Ruby runtime) throws IOException {
runtime.getLoadService().require("json/common");
RuntimeInfo info = RuntimeInfo.initRuntime(runtime);
- info.jsonModule = new WeakReference
* Instantiates a new
* This class does not perform the actual parsing, just acts as an interface
- * to Ruby code. When the {@link #parse()} method is invoked, a
+ * to Ruby code. When the {@link #parse(ThreadContext)} method is invoked, a
* Parser.ParserSession object is instantiated, which handles the process.
*
* @author mernen
@@ -71,11 +73,7 @@ public class Parser extends RubyObject {
private static final String CONST_INFINITY = "Infinity";
private static final String CONST_MINUS_INFINITY = "MinusInfinity";
- static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
- return new Parser(runtime, klazz);
- }
- };
+ static final ObjectAllocator ALLOCATOR = Parser::new;
/**
* Multiple-value return for internal parser methods.
@@ -113,42 +111,42 @@ public Parser(Ruby runtime, RubyClass metaClass) {
*
*
*
*
*
*
*
*
* This class does not perform the actual parsing, just acts as an interface
- * to Ruby code. When the {@link #parse()} method is invoked, a
+ * to Ruby code. When the {@link #parse(ThreadContext)} method is invoked, a
* Parser.ParserSession object is instantiated, which handles the process.
*
* @author mernen
@@ -69,11 +71,7 @@ public class Parser extends RubyObject {
private static final String CONST_INFINITY = "Infinity";
private static final String CONST_MINUS_INFINITY = "MinusInfinity";
- static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
- return new Parser(runtime, klazz);
- }
- };
+ static final ObjectAllocator ALLOCATOR = Parser::new;
/**
* Multiple-value return for internal parser methods.
@@ -111,42 +109,42 @@ public class Parser extends RubyObject {
*
*
*
*
*
*
*
*
* Object#to_json
): coerces the object
@@ -433,15 +488,15 @@ void generate(Session session, RubyString object, ByteList buffer) {
static final HandlerJSON::Generator::GeneratorMethods
)
*/
static void populate(RuntimeInfo info, RubyModule module) {
@@ -45,65 +44,79 @@ static void populate(RuntimeInfo info, RubyModule module) {
defineMethods(module, "String", RbString.class);
defineMethods(module, "TrueClass", RbTrue.class);
- info.stringExtendModule = new WeakReferenceopts
is a State
* object, it is just returned.
- * @param clazzParam The receiver of the method call
- * ({@link RubyClass} State
)
+ * @param context The current thread context
+ * @param klass The receiver of the method call ({@link RubyClass} State
)
* @param opts The object to use as a base for the new State
- * @param block The block passed to the method
* @return A GeneratorState
as determined above
*/
@JRubyMethod(meta=true)
- public static IRubyObject from_state(ThreadContext context,
- IRubyObject klass, IRubyObject opts) {
+ public static IRubyObject from_state(ThreadContext context, IRubyObject klass, IRubyObject opts) {
return fromState(context, opts);
}
@JRubyMethod(meta=true)
- public static IRubyObject generate(ThreadContext context, IRubyObject klass, IRubyObject obj, IRubyObject opts) {
- return fromState(context, opts).generate(context, obj);
+ public static IRubyObject generate(ThreadContext context, IRubyObject klass, IRubyObject obj, IRubyObject opts, IRubyObject io) {
+ return fromState(context, opts)._generate(context, obj, io);
}
static GeneratorState fromState(ThreadContext context, IRubyObject opts) {
- return fromState(context, RuntimeInfo.forRuntime(context.getRuntime()), opts);
+ return fromState(context, RuntimeInfo.forRuntime(context.runtime), opts);
}
static GeneratorState fromState(ThreadContext context, RuntimeInfo info,
@@ -155,9 +149,8 @@ static GeneratorState fromState(ThreadContext context, RuntimeInfo info,
if (klass.isInstance(opts)) return (GeneratorState)opts;
// if the given parameter is a Hash, pass it to the instantiator
- if (context.getRuntime().getHash().isInstance(opts)) {
- return (GeneratorState)klass.newInstance(context,
- new IRubyObject[] {opts}, Block.NULL_BLOCK);
+ if (context.runtime.getHash().isInstance(opts)) {
+ return (GeneratorState)klass.newInstance(context, opts, Block.NULL_BLOCK);
}
}
@@ -167,9 +160,9 @@ static GeneratorState fromState(ThreadContext context, RuntimeInfo info,
/**
* State#initialize(opts = {})
- *
+ * State
object, configured by opts
.
- *
+ * opts
can have the following keys:
*
*
@@ -194,15 +187,21 @@ static GeneratorState fromState(ThreadContext context, RuntimeInfo info,
*
true
if U+2028, U+2029 and forward slashes should be escaped
* in the json output to make it safe to include in a JavaScript tag (default: false
)
*/
- @JRubyMethod(optional=1, visibility=Visibility.PRIVATE)
- public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
- configure(context, args.length > 0 ? args[0] : null);
+ @JRubyMethod(visibility=Visibility.PRIVATE)
+ public IRubyObject initialize(ThreadContext context) {
+ _configure(context, null);
+ return this;
+ }
+
+ @JRubyMethod(visibility=Visibility.PRIVATE)
+ public IRubyObject initialize(ThreadContext context, IRubyObject arg0) {
+ _configure(context, arg0);
return this;
}
@JRubyMethod
public IRubyObject initialize_copy(ThreadContext context, IRubyObject vOrig) {
- Ruby runtime = context.getRuntime();
+ Ruby runtime = context.runtime;
if (!(vOrig instanceof GeneratorState)) {
throw runtime.newTypeError(vOrig, getType());
}
@@ -228,30 +227,27 @@ public IRubyObject initialize_copy(ThreadContext context, IRubyObject vOrig) {
* the result. If no valid JSON document can be created this method raises
* a GeneratorError exception.
*/
- @JRubyMethod
- public IRubyObject generate(ThreadContext context, IRubyObject obj) {
- RubyString result = Generator.generateJson(context, obj, this);
- RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime());
- if (result.getEncoding() != UTF8Encoding.INSTANCE) {
- if (result.isFrozen()) {
- result = result.strDup(context.getRuntime());
- }
- result.force_encoding(context, info.utf8.get());
+ @JRubyMethod(visibility = Visibility.PRIVATE)
+ public IRubyObject _generate(ThreadContext context, IRubyObject obj, IRubyObject io) {
+ IRubyObject result = Generator.generateJson(context, obj, this, io);
+ RuntimeInfo info = RuntimeInfo.forRuntime(context.runtime);
+ if (!(result instanceof RubyString)) {
+ return result;
}
- return result;
- }
- private static boolean matchClosingBrace(ByteList bl, int pos, int len,
- int brace) {
- for (int endPos = len - 1; endPos > pos; endPos--) {
- int b = bl.get(endPos);
- if (Character.isWhitespace(b)) continue;
- return b == brace;
+ RubyString resultString = result.convertToString();
+ if (resultString.getEncoding() != UTF8Encoding.INSTANCE) {
+ if (resultString.isFrozen()) {
+ resultString = resultString.strDup(context.runtime);
+ }
+ resultString.setEncoding(UTF8Encoding.INSTANCE);
+ resultString.clearCodeRange();
}
- return false;
+
+ return resultString;
}
- @JRubyMethod(name="[]", required=1)
+ @JRubyMethod(name="[]")
public IRubyObject op_aref(ThreadContext context, IRubyObject vName) {
String name = vName.asJavaString();
if (getMetaClass().isMethodBound(name, true)) {
@@ -262,16 +258,16 @@ public IRubyObject op_aref(ThreadContext context, IRubyObject vName) {
}
}
- @JRubyMethod(name="[]=", required=2)
+ @JRubyMethod(name="[]=")
public IRubyObject op_aset(ThreadContext context, IRubyObject vName, IRubyObject value) {
String name = vName.asJavaString();
String nameWriter = name + "=";
if (getMetaClass().isMethodBound(nameWriter, true)) {
- return send(context, context.getRuntime().newString(nameWriter), value, Block.NULL_BLOCK);
+ return send(context, context.runtime.newString(nameWriter), value, Block.NULL_BLOCK);
} else {
getInstanceVariables().setInstanceVariable("@" + name, value);
}
- return context.getRuntime().getNil();
+ return context.nil;
}
public ByteList getIndent() {
@@ -280,7 +276,7 @@ public ByteList getIndent() {
@JRubyMethod(name="indent")
public RubyString indent_get(ThreadContext context) {
- return context.getRuntime().newString(indent);
+ return context.runtime.newString(indent);
}
@JRubyMethod(name="indent=")
@@ -295,7 +291,7 @@ public ByteList getSpace() {
@JRubyMethod(name="space")
public RubyString space_get(ThreadContext context) {
- return context.getRuntime().newString(space);
+ return context.runtime.newString(space);
}
@JRubyMethod(name="space=")
@@ -310,7 +306,7 @@ public ByteList getSpaceBefore() {
@JRubyMethod(name="space_before")
public RubyString space_before_get(ThreadContext context) {
- return context.getRuntime().newString(spaceBefore);
+ return context.runtime.newString(spaceBefore);
}
@JRubyMethod(name="space_before=")
@@ -326,7 +322,7 @@ public ByteList getObjectNl() {
@JRubyMethod(name="object_nl")
public RubyString object_nl_get(ThreadContext context) {
- return context.getRuntime().newString(objectNl);
+ return context.runtime.newString(objectNl);
}
@JRubyMethod(name="object_nl=")
@@ -342,7 +338,7 @@ public ByteList getArrayNl() {
@JRubyMethod(name="array_nl")
public RubyString array_nl_get(ThreadContext context) {
- return context.getRuntime().newString(arrayNl);
+ return context.runtime.newString(arrayNl);
}
@JRubyMethod(name="array_nl=")
@@ -354,19 +350,12 @@ public IRubyObject array_nl_set(ThreadContext context,
@JRubyMethod(name="check_circular?")
public RubyBoolean check_circular_p(ThreadContext context) {
- return context.getRuntime().newBoolean(maxNesting != 0);
- }
-
- /**
- * Returns the maximum level of nesting configured for this state.
- */
- public int getMaxNesting() {
- return maxNesting;
+ return RubyBoolean.newBoolean(context, maxNesting != 0);
}
@JRubyMethod(name="max_nesting")
public RubyInteger max_nesting_get(ThreadContext context) {
- return context.getRuntime().newFixnum(maxNesting);
+ return context.runtime.newFixnum(maxNesting);
}
@JRubyMethod(name="max_nesting=")
@@ -384,7 +373,7 @@ public boolean scriptSafe() {
@JRubyMethod(name="script_safe", alias="escape_slash")
public RubyBoolean script_safe_get(ThreadContext context) {
- return context.getRuntime().newBoolean(scriptSafe);
+ return RubyBoolean.newBoolean(context, scriptSafe);
}
@JRubyMethod(name="script_safe=", alias="escape_slash=")
@@ -395,7 +384,7 @@ public IRubyObject script_safe_set(IRubyObject script_safe) {
@JRubyMethod(name="script_safe?", alias="escape_slash?")
public RubyBoolean script_safe_p(ThreadContext context) {
- return context.getRuntime().newBoolean(scriptSafe);
+ return RubyBoolean.newBoolean(context, scriptSafe);
}
/**
@@ -405,9 +394,9 @@ public boolean strict() {
return strict;
}
- @JRubyMethod(name="strict")
+ @JRubyMethod(name={"strict","strict?"})
public RubyBoolean strict_get(ThreadContext context) {
- return context.getRuntime().newBoolean(strict);
+ return RubyBoolean.newBoolean(context, strict);
}
@JRubyMethod(name="strict=")
@@ -422,7 +411,7 @@ public boolean allowNaN() {
@JRubyMethod(name="allow_nan?")
public RubyBoolean allow_nan_p(ThreadContext context) {
- return context.getRuntime().newBoolean(allowNaN);
+ return RubyBoolean.newBoolean(context, allowNaN);
}
public boolean asciiOnly() {
@@ -431,12 +420,12 @@ public boolean asciiOnly() {
@JRubyMethod(name="ascii_only?")
public RubyBoolean ascii_only_p(ThreadContext context) {
- return context.getRuntime().newBoolean(asciiOnly);
+ return RubyBoolean.newBoolean(context, asciiOnly);
}
@JRubyMethod(name="buffer_initial_length")
public RubyInteger buffer_initial_length_get(ThreadContext context) {
- return context.getRuntime().newFixnum(bufferInitialLength);
+ return context.runtime.newFixnum(bufferInitialLength);
}
@JRubyMethod(name="buffer_initial_length=")
@@ -452,7 +441,7 @@ public int getDepth() {
@JRubyMethod(name="depth")
public RubyInteger depth_get(ThreadContext context) {
- return context.getRuntime().newFixnum(depth);
+ return context.runtime.newFixnum(depth);
}
@JRubyMethod(name="depth=")
@@ -463,9 +452,8 @@ public IRubyObject depth_set(IRubyObject vDepth) {
private ByteList prepareByteList(ThreadContext context, IRubyObject value) {
RubyString str = value.convertToString();
- RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime());
- if (str.encoding(context) != info.utf8.get()) {
- str = (RubyString)str.encode(context, info.utf8.get());
+ if (str.getEncoding() != UTF8Encoding.INSTANCE) {
+ str = (RubyString)str.encode(context, context.runtime.getEncodingService().convertEncodingToRubyEncoding(UTF8Encoding.INSTANCE));
}
return str.getByteList().dup();
}
@@ -478,8 +466,8 @@ private ByteList prepareByteList(ThreadContext context, IRubyObject value) {
* @param vOpts The options hash
* @return The receiver
*/
- @JRubyMethod(alias = "merge")
- public IRubyObject configure(ThreadContext context, IRubyObject vOpts) {
+ @JRubyMethod(visibility=Visibility.PRIVATE)
+ public IRubyObject _configure(ThreadContext context, IRubyObject vOpts) {
OptionsReader opts = new OptionsReader(context, vOpts);
ByteList indent = opts.getString("indent");
@@ -521,7 +509,7 @@ public IRubyObject configure(ThreadContext context, IRubyObject vOpts) {
*/
@JRubyMethod(alias = "to_hash")
public RubyHash to_h(ThreadContext context) {
- Ruby runtime = context.getRuntime();
+ Ruby runtime = context.runtime;
RubyHash result = RubyHash.newHash(runtime);
result.op_aset(context, runtime.newSymbol("indent"), indent_get(context));
@@ -542,26 +530,24 @@ public RubyHash to_h(ThreadContext context) {
return result;
}
- public int increaseDepth() {
+ public int increaseDepth(ThreadContext context) {
depth++;
- checkMaxNesting();
+ checkMaxNesting(context);
return depth;
}
- public int decreaseDepth() {
- return --depth;
+ public void decreaseDepth() {
+ --depth;
}
/**
* Checks if the current depth is allowed as per this state's options.
- * @param context
- * @param depth The current depth
+ * @param context The current context
*/
- private void checkMaxNesting() {
+ private void checkMaxNesting(ThreadContext context) {
if (maxNesting != 0 && depth > maxNesting) {
depth--;
- throw Utils.newException(getRuntime().getCurrentContext(),
- Utils.M_NESTING_ERROR, "nesting of " + depth + " is too deep");
+ throw Utils.newException(context, Utils.M_NESTING_ERROR, "nesting of " + depth + " is too deep");
}
}
}
diff --git a/java/src/json/ext/OptionsReader.java b/java/src/json/ext/OptionsReader.java
index 70426d42..ff976c38 100644
--- a/java/src/json/ext/OptionsReader.java
+++ b/java/src/json/ext/OptionsReader.java
@@ -5,6 +5,7 @@
*/
package json.ext;
+import org.jcodings.specific.UTF8Encoding;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyHash;
@@ -22,7 +23,7 @@ final class OptionsReader {
OptionsReader(ThreadContext context, IRubyObject vOpts) {
this.context = context;
- this.runtime = context.getRuntime();
+ this.runtime = context.runtime;
if (vOpts == null || vOpts.isNil()) {
opts = null;
} else if (vOpts.respondsTo("to_hash")) {
@@ -41,7 +42,7 @@ private RuntimeInfo getRuntimeInfo() {
}
/**
- * Efficiently looks up items with a {@link RubySymbol Symbol} key
+ * Efficiently looks up items with a {@link org.jruby.RubySymbol Symbol} key
* @param key The Symbol name to look up for
* @return The item in the {@link RubyHash Hash}, or null
* if not found
@@ -69,7 +70,7 @@ int getInt(String key, int defaultValue) {
* @param key The Symbol name to look up for
* @return null
if the key is not in the Hash or if
* its value evaluates to false
- * @throws RaiseException TypeError
if the value does not
+ * @throws org.jruby.exceptions.RaiseException TypeError
if the value does not
* evaluate to false
and can't be
* converted to string
*/
@@ -83,9 +84,8 @@ RubyString getString(String key, RubyString defaultValue) {
if (value == null || !value.isTrue()) return defaultValue;
RubyString str = value.convertToString();
- RuntimeInfo info = getRuntimeInfo();
- if (str.encoding(context) != info.utf8.get()) {
- str = (RubyString)str.encode(context, info.utf8.get());
+ if (str.getEncoding() != UTF8Encoding.INSTANCE) {
+ str = (RubyString)str.encode(context, context.runtime.getEncodingService().convertEncodingToRubyEncoding(UTF8Encoding.INSTANCE));
}
return str;
}
diff --git a/java/src/json/ext/Parser.java b/java/src/json/ext/Parser.java
index 74037d37..47e66795 100644
--- a/java/src/json/ext/Parser.java
+++ b/java/src/json/ext/Parser.java
@@ -7,10 +7,12 @@
*/
package json.ext;
+import org.jcodings.Encoding;
+import org.jcodings.specific.ASCIIEncoding;
+import org.jcodings.specific.UTF8Encoding;
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyClass;
-import org.jruby.RubyEncoding;
import org.jruby.RubyFloat;
import org.jruby.RubyHash;
import org.jruby.RubyInteger;
@@ -20,13 +22,13 @@
import org.jruby.exceptions.JumpException;
import org.jruby.exceptions.RaiseException;
import org.jruby.runtime.Block;
+import org.jruby.runtime.Helpers;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import org.jruby.util.ConvertBytes;
-import org.jruby.util.ConvertDouble;
import java.util.function.BiFunction;
@@ -41,7 +43,7 @@
* This is performed for you when you include "json/ext"
.
*
* source
.
* It will be configured by the opts
Hash.
* opts
can have the following keys:
- *
+ *
*
*/
- @JRubyMethod(name = "new", required = 1, optional = 1, meta = true)
- public static IRubyObject newInstance(IRubyObject clazz, IRubyObject[] args, Block block) {
+ @JRubyMethod(name = "new", meta = true)
+ public static IRubyObject newInstance(IRubyObject clazz, IRubyObject arg0, Block block) {
Parser parser = (Parser)((RubyClass)clazz).allocate();
- parser.callInit(args, block);
+ parser.callInit(arg0, block);
+
+ return parser;
+ }
+
+ @JRubyMethod(name = "new", meta = true)
+ public static IRubyObject newInstance(IRubyObject clazz, IRubyObject arg0, IRubyObject arg1, Block block) {
+ Parser parser = (Parser)((RubyClass)clazz).allocate();
+
+ parser.callInit(arg0, arg1, block);
return parser;
}
@JRubyMethod(meta=true)
public static IRubyObject parse(ThreadContext context, IRubyObject clazz, IRubyObject source, IRubyObject opts) {
- IRubyObject[] args = new IRubyObject[] {source, opts};
Parser parser = (Parser)((RubyClass)clazz).allocate();
- parser.callInit(args, null);
+ parser.callInit(source, opts, null);
return parser.parse(context);
}
- @JRubyMethod(required = 1, optional = 1, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
+ @JRubyMethod(visibility = Visibility.PRIVATE)
+ public IRubyObject initialize(ThreadContext context, IRubyObject arg0) {
+ return initialize(context, arg0, null);
+ }
+
+ @JRubyMethod(visibility = Visibility.PRIVATE)
+ public IRubyObject initialize(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
+ Ruby runtime = context.runtime;
if (this.vSource != null) {
throw runtime.newTypeError("already initialized instance");
- }
+ }
- OptionsReader opts = new OptionsReader(context, args.length > 1 ? args[1] : null);
+ OptionsReader opts = new OptionsReader(context, arg1);
this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
this.allowNaN = opts.getBool("allow_nan", false);
this.allowTrailingComma = opts.getBool("allow_trailing_comma", false);
@@ -215,12 +226,9 @@ public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
}
if(symbolizeNames && createAdditions) {
- throw runtime.newArgumentError(
- "options :symbolize_names and :create_additions cannot be " +
- " used in conjunction"
- );
+ throw runtime.newArgumentError("options :symbolize_names and :create_additions cannot be used in conjunction");
}
- this.vSource = args[0].convertToString();
+ this.vSource = arg0.convertToString();
this.vSource = convertEncoding(context, vSource);
return this;
@@ -232,44 +240,17 @@ public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
* Returns the source string if no conversion is needed.
*/
private RubyString convertEncoding(ThreadContext context, RubyString source) {
- RubyEncoding encoding = (RubyEncoding)source.encoding(context);
- if (encoding == info.ascii8bit.get()) {
+ Encoding encoding = source.getEncoding();
+ if (encoding == ASCIIEncoding.INSTANCE) {
source = (RubyString) source.dup();
- source.force_encoding(context, info.utf8.get());
- } else {
- source = (RubyString) source.encode(context, info.utf8.get());
+ source.setEncoding(UTF8Encoding.INSTANCE);
+ source.clearCodeRange();
+ } else if (encoding != UTF8Encoding.INSTANCE) {
+ source = (RubyString) source.encode(context, context.runtime.getEncodingService().convertEncodingToRubyEncoding(UTF8Encoding.INSTANCE));
}
return source;
}
- /**
- * Checks the first four bytes of the given ByteList to infer its encoding,
- * using the principle demonstrated on section 3 of RFC 4627 (JSON).
- */
- private static String sniffByteList(ByteList bl) {
- if (bl.length() < 4) return null;
- if (bl.get(0) == 0 && bl.get(2) == 0) {
- return bl.get(1) == 0 ? "utf-32be" : "utf-16be";
- }
- if (bl.get(1) == 0 && bl.get(3) == 0) {
- return bl.get(2) == 0 ? "utf-32le" : "utf-16le";
- }
- return null;
- }
-
- /**
- * Assumes the given (binary) RubyString to be in the given encoding, then
- * converts it to UTF-8.
- */
- private RubyString reinterpretEncoding(ThreadContext context,
- RubyString str, String sniffedEncoding) {
- RubyEncoding actualEncoding = info.getEncoding(context, sniffedEncoding);
- RubyEncoding targetEncoding = info.utf8.get();
- RubyString dup = (RubyString)str.dup();
- dup.force_encoding(context, actualEncoding);
- return (RubyString)dup.encode_bang(context, targetEncoding);
- }
-
/**
* :max_nesting
* :max_nesting => false|nil|0
,
* it defaults to 100.
- *
+ * :allow_nan
* true
, allow NaN
,
* Infinity
and -Infinity
in defiance of RFC 4627
* to be parsed by the Parser. This option defaults to false
.
- *
+ * :allow_trailing_comma
* true
, allow arrays and objects with a trailing
* comma in defiance of RFC 4627 to be parsed by the Parser.
* This option defaults to false
.
- *
+ * :symbolize_names
* true
, returns symbols for the names (keys) in
* a JSON object. Otherwise strings are returned, which is also the default.
- *
+ * :create_additions
* false
, the Parser doesn't create additions
* even if a matching class and create_id
was found. This option
* defaults to true
.
- *
+ * :object_class
* new
without arguments, and return an object that respond to []=
.
- *
+ * :array_class
* new
without arguments, and return an object that respond to <<
.
- *
+ * :decimal_class
* Parser#parse()
*
@@ -278,7 +259,7 @@ private RubyString reinterpretEncoding(ThreadContext context,
*/
@JRubyMethod
public IRubyObject parse(ThreadContext context) {
- return new ParserSession(this, context, info).parse();
+ return new ParserSession(this, context, info).parse(context);
}
/**
@@ -288,15 +269,15 @@ public IRubyObject parse(ThreadContext context) {
* used to construct this Parser.
*/
@JRubyMethod(name = "source")
- public IRubyObject source_get() {
- return checkAndGetSource().dup();
+ public IRubyObject source_get(ThreadContext context) {
+ return checkAndGetSource(context).dup();
}
- public RubyString checkAndGetSource() {
+ public RubyString checkAndGetSource(ThreadContext context) {
if (vSource != null) {
return vSource;
} else {
- throw getRuntime().newTypeError("uninitialized instance");
+ throw context.runtime.newTypeError("uninitialized instance");
}
}
@@ -335,47 +316,35 @@ private IRubyObject createCustomDecimal(final ThreadContext context, final ByteL
@SuppressWarnings("fallthrough")
private static class ParserSession {
private final Parser parser;
- private final ThreadContext context;
private final RuntimeInfo info;
private final ByteList byteList;
private final ByteList view;
private final byte[] data;
private final StringDecoder decoder;
private int currentNesting = 0;
- private final DoubleConverter dc;
-
- // initialization value for all state variables.
- // no idea about the origins of this value, ask Flori ;)
- private static final int EVIL = 0x666;
private ParserSession(Parser parser, ThreadContext context, RuntimeInfo info) {
this.parser = parser;
- this.context = context;
this.info = info;
- this.byteList = parser.checkAndGetSource().getByteList();
+ this.byteList = parser.checkAndGetSource(context).getByteList();
this.data = byteList.unsafeBytes();
this.view = new ByteList(data, false);
- this.decoder = new StringDecoder(context);
- this.dc = new DoubleConverter();
+ this.decoder = new StringDecoder();
}
- private RaiseException unexpectedToken(int absStart, int absEnd) {
- RubyString msg = getRuntime().newString("unexpected token at '")
+ private RaiseException unexpectedToken(ThreadContext context, int absStart, int absEnd) {
+ RubyString msg = context.runtime.newString("unexpected token at '")
.cat(data, absStart, Math.min(absEnd - absStart, 32))
.cat((byte)'\'');
- return newException(Utils.M_PARSER_ERROR, msg);
- }
-
- private Ruby getRuntime() {
- return context.getRuntime();
+ return newException(context, Utils.M_PARSER_ERROR, msg);
}
-// line 397 "Parser.rl"
+// line 366 "Parser.rl"
-// line 379 "Parser.java"
+// line 348 "Parser.java"
private static byte[] init__JSON_value_actions_0()
{
return new byte [] {
@@ -489,22 +458,22 @@ private static byte[] init__JSON_value_from_state_actions_0()
static final int JSON_value_en_main = 1;
-// line 503 "Parser.rl"
+// line 472 "Parser.rl"
- void parseValue(ParserResult res, int p, int pe) {
- int cs = EVIL;
+ void parseValue(ThreadContext context, ParserResult res, int p, int pe) {
+ int cs;
IRubyObject result = null;
-// line 501 "Parser.java"
+// line 470 "Parser.java"
{
cs = JSON_value_start;
}
-// line 510 "Parser.rl"
+// line 479 "Parser.rl"
-// line 508 "Parser.java"
+// line 477 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -530,13 +499,13 @@ void parseValue(ParserResult res, int p, int pe) {
while ( _nacts-- > 0 ) {
switch ( _JSON_value_actions[_acts++] ) {
case 9:
-// line 488 "Parser.rl"
+// line 457 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 540 "Parser.java"
+// line 509 "Parser.java"
}
}
@@ -599,45 +568,45 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] )
switch ( _JSON_value_actions[_acts++] )
{
case 0:
-// line 405 "Parser.rl"
+// line 374 "Parser.rl"
{
- result = getRuntime().getNil();
+ result = context.nil;
}
break;
case 1:
-// line 408 "Parser.rl"
+// line 377 "Parser.rl"
{
- result = getRuntime().getFalse();
+ result = context.fals;
}
break;
case 2:
-// line 411 "Parser.rl"
+// line 380 "Parser.rl"
{
- result = getRuntime().getTrue();
+ result = context.tru;
}
break;
case 3:
-// line 414 "Parser.rl"
+// line 383 "Parser.rl"
{
if (parser.allowNaN) {
result = getConstant(CONST_NAN);
} else {
- throw unexpectedToken(p - 2, pe);
+ throw unexpectedToken(context, p - 2, pe);
}
}
break;
case 4:
-// line 421 "Parser.rl"
+// line 390 "Parser.rl"
{
if (parser.allowNaN) {
result = getConstant(CONST_INFINITY);
} else {
- throw unexpectedToken(p - 7, pe);
+ throw unexpectedToken(context, p - 7, pe);
}
}
break;
case 5:
-// line 428 "Parser.rl"
+// line 397 "Parser.rl"
{
if (pe > p + 8 &&
absSubSequence(p, p + 9).equals(JSON_MINUS_INFINITY)) {
@@ -648,15 +617,15 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] )
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
} else {
- throw unexpectedToken(p, pe);
+ throw unexpectedToken(context, p, pe);
}
}
- parseFloat(res, p, pe);
+ parseFloat(context, res, p, pe);
if (res.result != null) {
result = res.result;
{p = (( res.p))-1;}
}
- parseInteger(res, p, pe);
+ parseInteger(context, res, p, pe);
if (res.result != null) {
result = res.result;
{p = (( res.p))-1;}
@@ -666,9 +635,9 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] )
}
break;
case 6:
-// line 454 "Parser.rl"
+// line 423 "Parser.rl"
{
- parseString(res, p, pe);
+ parseString(context, res, p, pe);
if (res.result == null) {
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
@@ -679,10 +648,10 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] )
}
break;
case 7:
-// line 464 "Parser.rl"
+// line 433 "Parser.rl"
{
currentNesting++;
- parseArray(res, p, pe);
+ parseArray(context, res, p, pe);
currentNesting--;
if (res.result == null) {
p--;
@@ -694,10 +663,10 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] )
}
break;
case 8:
-// line 476 "Parser.rl"
+// line 445 "Parser.rl"
{
currentNesting++;
- parseObject(res, p, pe);
+ parseObject(context, res, p, pe);
currentNesting--;
if (res.result == null) {
p--;
@@ -708,7 +677,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] )
}
}
break;
-// line 712 "Parser.java"
+// line 681 "Parser.java"
}
}
}
@@ -728,7 +697,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] )
break; }
}
-// line 511 "Parser.rl"
+// line 480 "Parser.rl"
if (cs >= JSON_value_first_final && result != null) {
if (parser.freeze) {
@@ -741,7 +710,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] )
}
-// line 745 "Parser.java"
+// line 714 "Parser.java"
private static byte[] init__JSON_integer_actions_0()
{
return new byte [] {
@@ -840,33 +809,32 @@ private static byte[] init__JSON_integer_trans_actions_0()
static final int JSON_integer_en_main = 1;
-// line 533 "Parser.rl"
+// line 502 "Parser.rl"
- void parseInteger(ParserResult res, int p, int pe) {
+ void parseInteger(ThreadContext context, ParserResult res, int p, int pe) {
int new_p = parseIntegerInternal(p, pe);
if (new_p == -1) {
res.update(null, p);
return;
}
- RubyInteger number = createInteger(p, new_p);
+ RubyInteger number = createInteger(context, p, new_p);
res.update(number, new_p + 1);
- return;
}
int parseIntegerInternal(int p, int pe) {
- int cs = EVIL;
+ int cs;
-// line 862 "Parser.java"
+// line 830 "Parser.java"
{
cs = JSON_integer_start;
}
-// line 550 "Parser.rl"
+// line 518 "Parser.rl"
int memo = p;
-// line 870 "Parser.java"
+// line 838 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -947,13 +915,13 @@ else if ( data[p] > _JSON_integer_trans_keys[_mid+1] )
switch ( _JSON_integer_actions[_acts++] )
{
case 0:
-// line 527 "Parser.rl"
+// line 496 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 957 "Parser.java"
+// line 925 "Parser.java"
}
}
}
@@ -973,7 +941,7 @@ else if ( data[p] > _JSON_integer_trans_keys[_mid+1] )
break; }
}
-// line 552 "Parser.rl"
+// line 520 "Parser.rl"
if (cs < JSON_integer_first_final) {
return -1;
@@ -982,8 +950,8 @@ else if ( data[p] > _JSON_integer_trans_keys[_mid+1] )
return p;
}
- RubyInteger createInteger(int p, int new_p) {
- Ruby runtime = getRuntime();
+ RubyInteger createInteger(ThreadContext context, int p, int new_p) {
+ Ruby runtime = context.runtime;
ByteList num = absSubSequence(p, new_p);
return bytesToInum(runtime, num);
}
@@ -993,7 +961,7 @@ RubyInteger bytesToInum(Ruby runtime, ByteList num) {
}
-// line 997 "Parser.java"
+// line 965 "Parser.java"
private static byte[] init__JSON_float_actions_0()
{
return new byte [] {
@@ -1095,10 +1063,10 @@ private static byte[] init__JSON_float_trans_actions_0()
static final int JSON_float_en_main = 1;
-// line 585 "Parser.rl"
+// line 553 "Parser.rl"
- void parseFloat(ParserResult res, int p, int pe) {
+ void parseFloat(ThreadContext context, ParserResult res, int p, int pe) {
int new_p = parseFloatInternal(p, pe);
if (new_p == -1) {
res.update(null, p);
@@ -1111,18 +1079,18 @@ void parseFloat(ParserResult res, int p, int pe) {
}
int parseFloatInternal(int p, int pe) {
- int cs = EVIL;
+ int cs;
-// line 1118 "Parser.java"
+// line 1086 "Parser.java"
{
cs = JSON_float_start;
}
-// line 603 "Parser.rl"
+// line 571 "Parser.rl"
int memo = p;
-// line 1126 "Parser.java"
+// line 1094 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -1203,13 +1171,13 @@ else if ( data[p] > _JSON_float_trans_keys[_mid+1] )
switch ( _JSON_float_actions[_acts++] )
{
case 0:
-// line 576 "Parser.rl"
+// line 544 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 1213 "Parser.java"
+// line 1181 "Parser.java"
}
}
}
@@ -1229,7 +1197,7 @@ else if ( data[p] > _JSON_float_trans_keys[_mid+1] )
break; }
}
-// line 605 "Parser.rl"
+// line 573 "Parser.rl"
if (cs < JSON_float_first_final) {
return -1;
@@ -1239,7 +1207,7 @@ else if ( data[p] > _JSON_float_trans_keys[_mid+1] )
}
-// line 1243 "Parser.java"
+// line 1211 "Parser.java"
private static byte[] init__JSON_string_actions_0()
{
return new byte [] {
@@ -1341,23 +1309,23 @@ private static byte[] init__JSON_string_trans_actions_0()
static final int JSON_string_en_main = 1;
-// line 644 "Parser.rl"
+// line 612 "Parser.rl"
- void parseString(ParserResult res, int p, int pe) {
- int cs = EVIL;
+ void parseString(ThreadContext context, ParserResult res, int p, int pe) {
+ int cs;
IRubyObject result = null;
-// line 1353 "Parser.java"
+// line 1321 "Parser.java"
{
cs = JSON_string_start;
}
-// line 651 "Parser.rl"
+// line 619 "Parser.rl"
int memo = p;
-// line 1361 "Parser.java"
+// line 1329 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -1438,12 +1406,12 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] )
switch ( _JSON_string_actions[_acts++] )
{
case 0:
-// line 619 "Parser.rl"
+// line 587 "Parser.rl"
{
int offset = byteList.begin();
- ByteList decoded = decoder.decode(byteList, memo + 1 - offset,
+ ByteList decoded = decoder.decode(context, byteList, memo + 1 - offset,
p - offset);
- result = getRuntime().newString(decoded);
+ result = context.runtime.newString(decoded);
if (result == null) {
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
@@ -1453,13 +1421,13 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] )
}
break;
case 1:
-// line 632 "Parser.rl"
+// line 600 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 1463 "Parser.java"
+// line 1431 "Parser.java"
}
}
}
@@ -1479,29 +1447,21 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] )
break; }
}
-// line 653 "Parser.rl"
+// line 621 "Parser.rl"
if (parser.createAdditions) {
RubyHash matchString = parser.match_string;
if (matchString != null) {
final IRubyObject[] memoArray = { result, null };
try {
- matchString.visitAll(new RubyHash.Visitor() {
- @Override
- public void visit(IRubyObject pattern, IRubyObject klass) {
- if (pattern.callMethod(context, "===", memoArray[0]).isTrue()) {
- memoArray[1] = klass;
- throw JumpException.SPECIAL_JUMP;
- }
- }
- });
+ matchString.visitAll(context, MATCH_VISITOR, memoArray);
} catch (JumpException e) { }
if (memoArray[1] != null) {
RubyClass klass = (RubyClass) memoArray[1];
if (klass.respondsTo("json_creatable?") &&
klass.callMethod(context, "json_creatable?").isTrue()) {
if (parser.deprecatedCreateAdditions) {
- klass.getRuntime().getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`");
+ context.runtime.getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`");
}
result = klass.callMethod(context, "json_create", result);
}
@@ -1512,10 +1472,11 @@ public void visit(IRubyObject pattern, IRubyObject klass) {
if (cs >= JSON_string_first_final && result != null) {
if (result instanceof RubyString) {
RubyString string = (RubyString)result;
- string.force_encoding(context, info.utf8.get());
+ string.setEncoding(UTF8Encoding.INSTANCE);
+ string.clearCodeRange();
if (parser.freeze) {
string.setFrozen(true);
- string = getRuntime().freezeAndDedupString(string);
+ string = context.runtime.freezeAndDedupString(string);
}
res.update(string, p + 1);
} else {
@@ -1527,7 +1488,7 @@ public void visit(IRubyObject pattern, IRubyObject klass) {
}
-// line 1531 "Parser.java"
+// line 1492 "Parser.java"
private static byte[] init__JSON_array_actions_0()
{
return new byte [] {
@@ -1694,34 +1655,34 @@ private static byte[] init__JSON_array_trans_actions_0()
static final int JSON_array_en_main = 1;
-// line 738 "Parser.rl"
+// line 699 "Parser.rl"
- void parseArray(ParserResult res, int p, int pe) {
- int cs = EVIL;
+ void parseArray(ThreadContext context, ParserResult res, int p, int pe) {
+ int cs;
if (parser.maxNesting > 0 && currentNesting > parser.maxNesting) {
- throw newException(Utils.M_NESTING_ERROR,
+ throw newException(context, Utils.M_NESTING_ERROR,
"nesting of " + currentNesting + " is too deep");
}
IRubyObject result;
- if (parser.arrayClass == getRuntime().getArray()) {
- result = RubyArray.newArray(getRuntime());
+ if (parser.arrayClass == context.runtime.getArray()) {
+ result = RubyArray.newArray(context.runtime);
} else {
result = parser.arrayClass.newInstance(context,
IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
}
-// line 1718 "Parser.java"
+// line 1679 "Parser.java"
{
cs = JSON_array_start;
}
-// line 757 "Parser.rl"
+// line 718 "Parser.rl"
-// line 1725 "Parser.java"
+// line 1686 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -1764,7 +1725,7 @@ else if ( _widec > _JSON_array_cond_keys[_mid+1] )
case 0: {
_widec = 65536 + (data[p] - 0);
if (
-// line 705 "Parser.rl"
+// line 666 "Parser.rl"
parser.allowTrailingComma ) _widec += 65536;
break;
}
@@ -1834,14 +1795,14 @@ else if ( _widec > _JSON_array_trans_keys[_mid+1] )
switch ( _JSON_array_actions[_acts++] )
{
case 0:
-// line 707 "Parser.rl"
+// line 668 "Parser.rl"
{
- parseValue(res, p, pe);
+ parseValue(context, res, p, pe);
if (res.result == null) {
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
} else {
- if (parser.arrayClass == getRuntime().getArray()) {
+ if (parser.arrayClass == context.runtime.getArray()) {
((RubyArray)result).append(res.result);
} else {
result.callMethod(context, "<<", res.result);
@@ -1851,13 +1812,13 @@ else if ( _widec > _JSON_array_trans_keys[_mid+1] )
}
break;
case 1:
-// line 722 "Parser.rl"
+// line 683 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 1861 "Parser.java"
+// line 1822 "Parser.java"
}
}
}
@@ -1877,17 +1838,17 @@ else if ( _widec > _JSON_array_trans_keys[_mid+1] )
break; }
}
-// line 758 "Parser.rl"
+// line 719 "Parser.rl"
if (cs >= JSON_array_first_final) {
res.update(result, p + 1);
} else {
- throw unexpectedToken(p, pe);
+ throw unexpectedToken(context, p, pe);
}
}
-// line 1891 "Parser.java"
+// line 1852 "Parser.java"
private static byte[] init__JSON_object_actions_0()
{
return new byte [] {
@@ -2064,24 +2025,24 @@ private static byte[] init__JSON_object_trans_actions_0()
static final int JSON_object_en_main = 1;
-// line 819 "Parser.rl"
+// line 780 "Parser.rl"
- void parseObject(ParserResult res, int p, int pe) {
- int cs = EVIL;
+ void parseObject(ThreadContext context, ParserResult res, int p, int pe) {
+ int cs;
IRubyObject lastName = null;
boolean objectDefault = true;
if (parser.maxNesting > 0 && currentNesting > parser.maxNesting) {
- throw newException(Utils.M_NESTING_ERROR,
+ throw newException(context, Utils.M_NESTING_ERROR,
"nesting of " + currentNesting + " is too deep");
}
// this is guaranteed to be a RubyHash due to the earlier
// allocator test at OptionsReader#getClass
IRubyObject result;
- if (parser.objectClass == getRuntime().getHash()) {
- result = RubyHash.newHash(getRuntime());
+ if (parser.objectClass == context.runtime.getHash()) {
+ result = RubyHash.newHash(context.runtime);
} else {
objectDefault = false;
result = parser.objectClass.newInstance(context,
@@ -2089,14 +2050,14 @@ void parseObject(ParserResult res, int p, int pe) {
}
-// line 2093 "Parser.java"
+// line 2054 "Parser.java"
{
cs = JSON_object_start;
}
-// line 843 "Parser.rl"
+// line 804 "Parser.rl"
-// line 2100 "Parser.java"
+// line 2061 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -2139,7 +2100,7 @@ else if ( _widec > _JSON_object_cond_keys[_mid+1] )
case 0: {
_widec = 65536 + (data[p] - 0);
if (
-// line 772 "Parser.rl"
+// line 733 "Parser.rl"
parser.allowTrailingComma ) _widec += 65536;
break;
}
@@ -2209,26 +2170,26 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] )
switch ( _JSON_object_actions[_acts++] )
{
case 0:
-// line 774 "Parser.rl"
+// line 735 "Parser.rl"
{
- parseValue(res, p, pe);
+ parseValue(context, res, p, pe);
if (res.result == null) {
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
} else {
- if (parser.objectClass == getRuntime().getHash()) {
+ if (parser.objectClass == context.runtime.getHash()) {
((RubyHash)result).op_aset(context, lastName, res.result);
} else {
- result.callMethod(context, "[]=", new IRubyObject[] { lastName, res.result });
+ Helpers.invoke(context, result, "[]=", lastName, res.result);
}
{p = (( res.p))-1;}
}
}
break;
case 1:
-// line 789 "Parser.rl"
+// line 750 "Parser.rl"
{
- parseString(res, p, pe);
+ parseString(context, res, p, pe);
if (res.result == null) {
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
@@ -2244,13 +2205,13 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] )
}
break;
case 2:
-// line 805 "Parser.rl"
+// line 766 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 2254 "Parser.java"
+// line 2215 "Parser.java"
}
}
}
@@ -2270,7 +2231,7 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] )
break; }
}
-// line 844 "Parser.rl"
+// line 805 "Parser.rl"
if (cs < JSON_object_first_final) {
res.update(null, p + 1);
@@ -2295,7 +2256,7 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] )
if (klass.respondsTo("json_creatable?") &&
klass.callMethod(context, "json_creatable?").isTrue()) {
if (parser.deprecatedCreateAdditions) {
- klass.getRuntime().getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`");
+ context.runtime.getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`");
}
returnedResult = klass.callMethod(context, "json_create", result);
@@ -2306,7 +2267,7 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] )
}
-// line 2310 "Parser.java"
+// line 2271 "Parser.java"
private static byte[] init__JSON_actions_0()
{
return new byte [] {
@@ -2409,26 +2370,26 @@ private static byte[] init__JSON_trans_actions_0()
static final int JSON_en_main = 1;
-// line 898 "Parser.rl"
+// line 859 "Parser.rl"
- public IRubyObject parseImplemetation() {
- int cs = EVIL;
+ public IRubyObject parseImplementation(ThreadContext context) {
+ int cs;
int p, pe;
IRubyObject result = null;
ParserResult res = new ParserResult();
-// line 2423 "Parser.java"
+// line 2384 "Parser.java"
{
cs = JSON_start;
}
-// line 907 "Parser.rl"
+// line 868 "Parser.rl"
p = byteList.begin();
pe = p + byteList.length();
-// line 2432 "Parser.java"
+// line 2393 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -2509,9 +2470,9 @@ else if ( data[p] > _JSON_trans_keys[_mid+1] )
switch ( _JSON_actions[_acts++] )
{
case 0:
-// line 884 "Parser.rl"
+// line 845 "Parser.rl"
{
- parseValue(res, p, pe);
+ parseValue(context, res, p, pe);
if (res.result == null) {
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
@@ -2521,7 +2482,7 @@ else if ( data[p] > _JSON_trans_keys[_mid+1] )
}
}
break;
-// line 2525 "Parser.java"
+// line 2486 "Parser.java"
}
}
}
@@ -2541,23 +2502,23 @@ else if ( data[p] > _JSON_trans_keys[_mid+1] )
break; }
}
-// line 910 "Parser.rl"
+// line 871 "Parser.rl"
if (cs >= JSON_first_final && p == pe) {
return result;
} else {
- throw unexpectedToken(p, pe);
+ throw unexpectedToken(context, p, pe);
}
}
- public IRubyObject parse() {
- return parseImplemetation();
+ public IRubyObject parse(ThreadContext context) {
+ return parseImplementation(context);
}
/**
* Updates the "view" bytelist with the new offsets and returns it.
- * @param start
- * @param end
+ * @param absStart
+ * @param absEnd
*/
private ByteList absSubSequence(int absStart, int absEnd) {
view.setBegin(absStart);
@@ -2573,18 +2534,22 @@ private IRubyObject getConstant(String name) {
return parser.info.jsonModule.get().getConstant(name);
}
- private RaiseException newException(String className, String message) {
+ private RaiseException newException(ThreadContext context, String className, String message) {
return Utils.newException(context, className, message);
}
- private RaiseException newException(String className, RubyString message) {
+ private RaiseException newException(ThreadContext context, String className, RubyString message) {
return Utils.newException(context, className, message);
}
- private RaiseException newException(String className,
- String messageBegin, ByteList messageEnd) {
- return newException(className,
- getRuntime().newString(messageBegin).cat(messageEnd));
- }
+ RubyHash.VisitorWithStateinclude "json/ext"
.
*
* source
.
* It will be configured by the opts
Hash.
* opts
can have the following keys:
- *
+ *
*
*/
- @JRubyMethod(name = "new", required = 1, optional = 1, meta = true)
- public static IRubyObject newInstance(IRubyObject clazz, IRubyObject[] args, Block block) {
+ @JRubyMethod(name = "new", meta = true)
+ public static IRubyObject newInstance(IRubyObject clazz, IRubyObject arg0, Block block) {
Parser parser = (Parser)((RubyClass)clazz).allocate();
- parser.callInit(args, block);
+ parser.callInit(arg0, block);
+
+ return parser;
+ }
+
+ @JRubyMethod(name = "new", meta = true)
+ public static IRubyObject newInstance(IRubyObject clazz, IRubyObject arg0, IRubyObject arg1, Block block) {
+ Parser parser = (Parser)((RubyClass)clazz).allocate();
+
+ parser.callInit(arg0, arg1, block);
return parser;
}
@JRubyMethod(meta=true)
public static IRubyObject parse(ThreadContext context, IRubyObject clazz, IRubyObject source, IRubyObject opts) {
- IRubyObject[] args = new IRubyObject[] {source, opts};
Parser parser = (Parser)((RubyClass)clazz).allocate();
- parser.callInit(args, null);
+ parser.callInit(source, opts, null);
return parser.parse(context);
}
- @JRubyMethod(required = 1, optional = 1, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
+ @JRubyMethod(visibility = Visibility.PRIVATE)
+ public IRubyObject initialize(ThreadContext context, IRubyObject arg0) {
+ return initialize(context, arg0, null);
+ }
+
+ @JRubyMethod(visibility = Visibility.PRIVATE)
+ public IRubyObject initialize(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
+ Ruby runtime = context.runtime;
if (this.vSource != null) {
throw runtime.newTypeError("already initialized instance");
- }
+ }
- OptionsReader opts = new OptionsReader(context, args.length > 1 ? args[1] : null);
+ OptionsReader opts = new OptionsReader(context, arg1);
this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
this.allowNaN = opts.getBool("allow_nan", false);
this.allowTrailingComma = opts.getBool("allow_trailing_comma", false);
@@ -213,12 +224,9 @@ public class Parser extends RubyObject {
}
if(symbolizeNames && createAdditions) {
- throw runtime.newArgumentError(
- "options :symbolize_names and :create_additions cannot be " +
- " used in conjunction"
- );
+ throw runtime.newArgumentError("options :symbolize_names and :create_additions cannot be used in conjunction");
}
- this.vSource = args[0].convertToString();
+ this.vSource = arg0.convertToString();
this.vSource = convertEncoding(context, vSource);
return this;
@@ -230,44 +238,17 @@ public class Parser extends RubyObject {
* Returns the source string if no conversion is needed.
*/
private RubyString convertEncoding(ThreadContext context, RubyString source) {
- RubyEncoding encoding = (RubyEncoding)source.encoding(context);
- if (encoding == info.ascii8bit.get()) {
+ Encoding encoding = source.getEncoding();
+ if (encoding == ASCIIEncoding.INSTANCE) {
source = (RubyString) source.dup();
- source.force_encoding(context, info.utf8.get());
- } else {
- source = (RubyString) source.encode(context, info.utf8.get());
+ source.setEncoding(UTF8Encoding.INSTANCE);
+ source.clearCodeRange();
+ } else if (encoding != UTF8Encoding.INSTANCE) {
+ source = (RubyString) source.encode(context, context.runtime.getEncodingService().convertEncodingToRubyEncoding(UTF8Encoding.INSTANCE));
}
return source;
}
- /**
- * Checks the first four bytes of the given ByteList to infer its encoding,
- * using the principle demonstrated on section 3 of RFC 4627 (JSON).
- */
- private static String sniffByteList(ByteList bl) {
- if (bl.length() < 4) return null;
- if (bl.get(0) == 0 && bl.get(2) == 0) {
- return bl.get(1) == 0 ? "utf-32be" : "utf-16be";
- }
- if (bl.get(1) == 0 && bl.get(3) == 0) {
- return bl.get(2) == 0 ? "utf-32le" : "utf-16le";
- }
- return null;
- }
-
- /**
- * Assumes the given (binary) RubyString to be in the given encoding, then
- * converts it to UTF-8.
- */
- private RubyString reinterpretEncoding(ThreadContext context,
- RubyString str, String sniffedEncoding) {
- RubyEncoding actualEncoding = info.getEncoding(context, sniffedEncoding);
- RubyEncoding targetEncoding = info.utf8.get();
- RubyString dup = (RubyString)str.dup();
- dup.force_encoding(context, actualEncoding);
- return (RubyString)dup.encode_bang(context, targetEncoding);
- }
-
/**
* :max_nesting
* :max_nesting => false|nil|0
,
* it defaults to 100.
- *
+ * :allow_nan
* true
, allow NaN
,
* Infinity
and -Infinity
in defiance of RFC 4627
* to be parsed by the Parser. This option defaults to false
.
- *
+ * :allow_trailing_comma
* true
, allow arrays and objects with a trailing
* comma in defiance of RFC 4627 to be parsed by the Parser.
* This option defaults to false
.
- *
+ * :symbolize_names
* true
, returns symbols for the names (keys) in
* a JSON object. Otherwise strings are returned, which is also the default.
- *
+ * :create_additions
* false
, the Parser doesn't create additions
* even if a matching class and create_id
was found. This option
* defaults to true
.
- *
+ * :object_class
* new
without arguments, and return an object that respond to []=
.
- *
+ * :array_class
* new
without arguments, and return an object that respond to <<
.
- *
+ * :decimal_class
* Parser#parse()
*
@@ -276,7 +257,7 @@ public class Parser extends RubyObject {
*/
@JRubyMethod
public IRubyObject parse(ThreadContext context) {
- return new ParserSession(this, context, info).parse();
+ return new ParserSession(this, context, info).parse(context);
}
/**
@@ -286,15 +267,15 @@ public class Parser extends RubyObject {
* used to construct this Parser.
*/
@JRubyMethod(name = "source")
- public IRubyObject source_get() {
- return checkAndGetSource().dup();
+ public IRubyObject source_get(ThreadContext context) {
+ return checkAndGetSource(context).dup();
}
- public RubyString checkAndGetSource() {
+ public RubyString checkAndGetSource(ThreadContext context) {
if (vSource != null) {
return vSource;
} else {
- throw getRuntime().newTypeError("uninitialized instance");
+ throw context.runtime.newTypeError("uninitialized instance");
}
}
@@ -333,39 +314,27 @@ public class Parser extends RubyObject {
@SuppressWarnings("fallthrough")
private static class ParserSession {
private final Parser parser;
- private final ThreadContext context;
private final RuntimeInfo info;
private final ByteList byteList;
private final ByteList view;
private final byte[] data;
private final StringDecoder decoder;
private int currentNesting = 0;
- private final DoubleConverter dc;
-
- // initialization value for all state variables.
- // no idea about the origins of this value, ask Flori ;)
- private static final int EVIL = 0x666;
private ParserSession(Parser parser, ThreadContext context, RuntimeInfo info) {
this.parser = parser;
- this.context = context;
this.info = info;
- this.byteList = parser.checkAndGetSource().getByteList();
+ this.byteList = parser.checkAndGetSource(context).getByteList();
this.data = byteList.unsafeBytes();
this.view = new ByteList(data, false);
- this.decoder = new StringDecoder(context);
- this.dc = new DoubleConverter();
+ this.decoder = new StringDecoder();
}
- private RaiseException unexpectedToken(int absStart, int absEnd) {
- RubyString msg = getRuntime().newString("unexpected token at '")
+ private RaiseException unexpectedToken(ThreadContext context, int absStart, int absEnd) {
+ RubyString msg = context.runtime.newString("unexpected token at '")
.cat(data, absStart, Math.min(absEnd - absStart, 32))
.cat((byte)'\'');
- return newException(Utils.M_PARSER_ERROR, msg);
- }
-
- private Ruby getRuntime() {
- return context.getRuntime();
+ return newException(context, Utils.M_PARSER_ERROR, msg);
}
%%{
@@ -403,26 +372,26 @@ public class Parser extends RubyObject {
write data;
action parse_null {
- result = getRuntime().getNil();
+ result = context.nil;
}
action parse_false {
- result = getRuntime().getFalse();
+ result = context.fals;
}
action parse_true {
- result = getRuntime().getTrue();
+ result = context.tru;
}
action parse_nan {
if (parser.allowNaN) {
result = getConstant(CONST_NAN);
} else {
- throw unexpectedToken(p - 2, pe);
+ throw unexpectedToken(context, p - 2, pe);
}
}
action parse_infinity {
if (parser.allowNaN) {
result = getConstant(CONST_INFINITY);
} else {
- throw unexpectedToken(p - 7, pe);
+ throw unexpectedToken(context, p - 7, pe);
}
}
action parse_number {
@@ -435,15 +404,15 @@ public class Parser extends RubyObject {
fhold;
fbreak;
} else {
- throw unexpectedToken(p, pe);
+ throw unexpectedToken(context, p, pe);
}
}
- parseFloat(res, fpc, pe);
+ parseFloat(context, res, fpc, pe);
if (res.result != null) {
result = res.result;
fexec res.p;
}
- parseInteger(res, fpc, pe);
+ parseInteger(context, res, fpc, pe);
if (res.result != null) {
result = res.result;
fexec res.p;
@@ -452,7 +421,7 @@ public class Parser extends RubyObject {
fbreak;
}
action parse_string {
- parseString(res, fpc, pe);
+ parseString(context, res, fpc, pe);
if (res.result == null) {
fhold;
fbreak;
@@ -463,7 +432,7 @@ public class Parser extends RubyObject {
}
action parse_array {
currentNesting++;
- parseArray(res, fpc, pe);
+ parseArray(context, res, fpc, pe);
currentNesting--;
if (res.result == null) {
fhold;
@@ -475,7 +444,7 @@ public class Parser extends RubyObject {
}
action parse_object {
currentNesting++;
- parseObject(res, fpc, pe);
+ parseObject(context, res, fpc, pe);
currentNesting--;
if (res.result == null) {
fhold;
@@ -502,8 +471,8 @@ public class Parser extends RubyObject {
) %*exit;
}%%
- void parseValue(ParserResult res, int p, int pe) {
- int cs = EVIL;
+ void parseValue(ThreadContext context, ParserResult res, int p, int pe) {
+ int cs;
IRubyObject result = null;
%% write init;
@@ -532,19 +501,18 @@ public class Parser extends RubyObject {
main := '-'? ( '0' | [1-9][0-9]* ) ( ^[0-9]? @exit );
}%%
- void parseInteger(ParserResult res, int p, int pe) {
+ void parseInteger(ThreadContext context, ParserResult res, int p, int pe) {
int new_p = parseIntegerInternal(p, pe);
if (new_p == -1) {
res.update(null, p);
return;
}
- RubyInteger number = createInteger(p, new_p);
+ RubyInteger number = createInteger(context, p, new_p);
res.update(number, new_p + 1);
- return;
}
int parseIntegerInternal(int p, int pe) {
- int cs = EVIL;
+ int cs;
%% write init;
int memo = p;
@@ -557,8 +525,8 @@ public class Parser extends RubyObject {
return p;
}
- RubyInteger createInteger(int p, int new_p) {
- Ruby runtime = getRuntime();
+ RubyInteger createInteger(ThreadContext context, int p, int new_p) {
+ Ruby runtime = context.runtime;
ByteList num = absSubSequence(p, new_p);
return bytesToInum(runtime, num);
}
@@ -584,7 +552,7 @@ public class Parser extends RubyObject {
( ^[0-9Ee.\-]? @exit );
}%%
- void parseFloat(ParserResult res, int p, int pe) {
+ void parseFloat(ThreadContext context, ParserResult res, int p, int pe) {
int new_p = parseFloatInternal(p, pe);
if (new_p == -1) {
res.update(null, p);
@@ -597,7 +565,7 @@ public class Parser extends RubyObject {
}
int parseFloatInternal(int p, int pe) {
- int cs = EVIL;
+ int cs;
%% write init;
int memo = p;
@@ -618,9 +586,9 @@ public class Parser extends RubyObject {
action parse_string {
int offset = byteList.begin();
- ByteList decoded = decoder.decode(byteList, memo + 1 - offset,
+ ByteList decoded = decoder.decode(context, byteList, memo + 1 - offset,
p - offset);
- result = getRuntime().newString(decoded);
+ result = context.runtime.newString(decoded);
if (result == null) {
fhold;
fbreak;
@@ -643,8 +611,8 @@ public class Parser extends RubyObject {
) '"' @exit;
}%%
- void parseString(ParserResult res, int p, int pe) {
- int cs = EVIL;
+ void parseString(ThreadContext context, ParserResult res, int p, int pe) {
+ int cs;
IRubyObject result = null;
%% write init;
@@ -656,22 +624,14 @@ public class Parser extends RubyObject {
if (matchString != null) {
final IRubyObject[] memoArray = { result, null };
try {
- matchString.visitAll(new RubyHash.Visitor() {
- @Override
- public void visit(IRubyObject pattern, IRubyObject klass) {
- if (pattern.callMethod(context, "===", memoArray[0]).isTrue()) {
- memoArray[1] = klass;
- throw JumpException.SPECIAL_JUMP;
- }
- }
- });
+ matchString.visitAll(context, MATCH_VISITOR, memoArray);
} catch (JumpException e) { }
if (memoArray[1] != null) {
RubyClass klass = (RubyClass) memoArray[1];
if (klass.respondsTo("json_creatable?") &&
klass.callMethod(context, "json_creatable?").isTrue()) {
if (parser.deprecatedCreateAdditions) {
- klass.getRuntime().getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`");
+ context.runtime.getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`");
}
result = klass.callMethod(context, "json_create", result);
}
@@ -682,10 +642,11 @@ public class Parser extends RubyObject {
if (cs >= JSON_string_first_final && result != null) {
if (result instanceof RubyString) {
RubyString string = (RubyString)result;
- string.force_encoding(context, info.utf8.get());
+ string.setEncoding(UTF8Encoding.INSTANCE);
+ string.clearCodeRange();
if (parser.freeze) {
string.setFrozen(true);
- string = getRuntime().freezeAndDedupString(string);
+ string = context.runtime.freezeAndDedupString(string);
}
res.update(string, p + 1);
} else {
@@ -705,12 +666,12 @@ public class Parser extends RubyObject {
action allow_trailing_comma { parser.allowTrailingComma }
action parse_value {
- parseValue(res, fpc, pe);
+ parseValue(context, res, fpc, pe);
if (res.result == null) {
fhold;
fbreak;
} else {
- if (parser.arrayClass == getRuntime().getArray()) {
+ if (parser.arrayClass == context.runtime.getArray()) {
((RubyArray)result).append(res.result);
} else {
result.callMethod(context, "<<", res.result);
@@ -737,17 +698,17 @@ public class Parser extends RubyObject {
end_array @exit;
}%%
- void parseArray(ParserResult res, int p, int pe) {
- int cs = EVIL;
+ void parseArray(ThreadContext context, ParserResult res, int p, int pe) {
+ int cs;
if (parser.maxNesting > 0 && currentNesting > parser.maxNesting) {
- throw newException(Utils.M_NESTING_ERROR,
+ throw newException(context, Utils.M_NESTING_ERROR,
"nesting of " + currentNesting + " is too deep");
}
IRubyObject result;
- if (parser.arrayClass == getRuntime().getArray()) {
- result = RubyArray.newArray(getRuntime());
+ if (parser.arrayClass == context.runtime.getArray()) {
+ result = RubyArray.newArray(context.runtime);
} else {
result = parser.arrayClass.newInstance(context,
IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
@@ -759,7 +720,7 @@ public class Parser extends RubyObject {
if (cs >= JSON_array_first_final) {
res.update(result, p + 1);
} else {
- throw unexpectedToken(p, pe);
+ throw unexpectedToken(context, p, pe);
}
}
@@ -772,22 +733,22 @@ public class Parser extends RubyObject {
action allow_trailing_comma { parser.allowTrailingComma }
action parse_value {
- parseValue(res, fpc, pe);
+ parseValue(context, res, fpc, pe);
if (res.result == null) {
fhold;
fbreak;
} else {
- if (parser.objectClass == getRuntime().getHash()) {
+ if (parser.objectClass == context.runtime.getHash()) {
((RubyHash)result).op_aset(context, lastName, res.result);
} else {
- result.callMethod(context, "[]=", new IRubyObject[] { lastName, res.result });
+ Helpers.invoke(context, result, "[]=", lastName, res.result);
}
fexec res.p;
}
}
action parse_name {
- parseString(res, fpc, pe);
+ parseString(context, res, fpc, pe);
if (res.result == null) {
fhold;
fbreak;
@@ -818,21 +779,21 @@ public class Parser extends RubyObject {
) @exit;
}%%
- void parseObject(ParserResult res, int p, int pe) {
- int cs = EVIL;
+ void parseObject(ThreadContext context, ParserResult res, int p, int pe) {
+ int cs;
IRubyObject lastName = null;
boolean objectDefault = true;
if (parser.maxNesting > 0 && currentNesting > parser.maxNesting) {
- throw newException(Utils.M_NESTING_ERROR,
+ throw newException(context, Utils.M_NESTING_ERROR,
"nesting of " + currentNesting + " is too deep");
}
// this is guaranteed to be a RubyHash due to the earlier
// allocator test at OptionsReader#getClass
IRubyObject result;
- if (parser.objectClass == getRuntime().getHash()) {
- result = RubyHash.newHash(getRuntime());
+ if (parser.objectClass == context.runtime.getHash()) {
+ result = RubyHash.newHash(context.runtime);
} else {
objectDefault = false;
result = parser.objectClass.newInstance(context,
@@ -865,7 +826,7 @@ public class Parser extends RubyObject {
if (klass.respondsTo("json_creatable?") &&
klass.callMethod(context, "json_creatable?").isTrue()) {
if (parser.deprecatedCreateAdditions) {
- klass.getRuntime().getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`");
+ context.runtime.getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`");
}
returnedResult = klass.callMethod(context, "json_create", result);
@@ -882,7 +843,7 @@ public class Parser extends RubyObject {
write data;
action parse_value {
- parseValue(res, fpc, pe);
+ parseValue(context, res, fpc, pe);
if (res.result == null) {
fhold;
fbreak;
@@ -897,8 +858,8 @@ public class Parser extends RubyObject {
ignore*;
}%%
- public IRubyObject parseImplemetation() {
- int cs = EVIL;
+ public IRubyObject parseImplementation(ThreadContext context) {
+ int cs;
int p, pe;
IRubyObject result = null;
ParserResult res = new ParserResult();
@@ -911,18 +872,18 @@ public class Parser extends RubyObject {
if (cs >= JSON_first_final && p == pe) {
return result;
} else {
- throw unexpectedToken(p, pe);
+ throw unexpectedToken(context, p, pe);
}
}
- public IRubyObject parse() {
- return parseImplemetation();
+ public IRubyObject parse(ThreadContext context) {
+ return parseImplementation(context);
}
/**
* Updates the "view" bytelist with the new offsets and returns it.
- * @param start
- * @param end
+ * @param absStart
+ * @param absEnd
*/
private ByteList absSubSequence(int absStart, int absEnd) {
view.setBegin(absStart);
@@ -938,18 +899,22 @@ public class Parser extends RubyObject {
return parser.info.jsonModule.get().getConstant(name);
}
- private RaiseException newException(String className, String message) {
+ private RaiseException newException(ThreadContext context, String className, String message) {
return Utils.newException(context, className, message);
}
- private RaiseException newException(String className, RubyString message) {
+ private RaiseException newException(ThreadContext context, String className, RubyString message) {
return Utils.newException(context, className, message);
}
- private RaiseException newException(String className,
- String messageBegin, ByteList messageEnd) {
- return newException(className,
- getRuntime().newString(messageBegin).cat(messageEnd));
- }
+ RubyHash.VisitorWithState