From 220409f0c186e80da9f4d99cb0d99d8c9e9d0fa2 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 13 Jan 2025 14:04:15 +0100 Subject: [PATCH] [ruby/json] Refactor JSON::Ext::Parser to split configuration and parsing state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ref: https://github.com/ruby/json/pull/718 The existing `Parser` interface is pretty bad, as it forces to instantiate a new instance for each document. Instead it's preferable to only take the config and do all the initialization needed, and then keep the parsing state on the stack on in ephemeral memory. This refactor makes the `JSON::Coder` pull request much easier to implement in a performant way. https://github.com/ruby/json/commit/c8d5236a92 Co-Authored-By: Étienne Barrié --- ext/json/lib/json/common.rb | 11 +- ext/json/lib/json/ext.rb | 29 +- ext/json/parser/parser.c | 661 +++++++++++------------------- ext/json/parser/parser.rl | 309 +++++++------- test/json/json_ext_parser_test.rb | 8 +- 5 files changed, 419 insertions(+), 599 deletions(-) diff --git a/ext/json/lib/json/common.rb b/ext/json/lib/json/common.rb index 89f11a0c31a15b..3c85ef06553a5c 100644 --- a/ext/json/lib/json/common.rb +++ b/ext/json/lib/json/common.rb @@ -232,12 +232,13 @@ def parse(source, opts = nil) # - Option +max_nesting+, if not provided, defaults to +false+, # which disables checking for nesting depth. # - Option +allow_nan+, if not provided, defaults to +true+. - def parse!(source, opts = {}) - opts = { + def parse!(source, opts = nil) + options = { :max_nesting => false, :allow_nan => true - }.merge(opts) - Parser.new(source, **(opts||{})).parse + } + options.merge!(opts) if opts + Parser.new(source, options).parse end # :call-seq: @@ -258,7 +259,7 @@ def load_file(filespec, opts = nil) # JSON.parse!(File.read(path, opts)) # # See method #parse! - def load_file!(filespec, opts = {}) + def load_file!(filespec, opts = nil) parse!(File.read(filespec, encoding: Encoding::UTF_8), opts) end diff --git a/ext/json/lib/json/ext.rb b/ext/json/lib/json/ext.rb index 2082cae68f0159..f9fca0a163f9fa 100644 --- a/ext/json/lib/json/ext.rb +++ b/ext/json/lib/json/ext.rb @@ -6,15 +6,36 @@ module JSON # This module holds all the modules/classes that implement JSON's # functionality as C extensions. module Ext + class Parser + class << self + def parse(...) + new(...).parse + end + end + + def initialize(source, opts = nil) + @source = source + @config = Config.new(opts) + end + + def source + @source.dup + end + + def parse + @config.parse(@source) + end + end + + require 'json/ext/parser' + Ext::Parser::Config = Ext::ParserConfig + JSON.parser = Ext::Parser + if RUBY_ENGINE == 'truffleruby' - require 'json/ext/parser' require 'json/truffle_ruby/generator' - JSON.parser = Parser JSON.generator = ::JSON::TruffleRuby::Generator else - require 'json/ext/parser' require 'json/ext/generator' - JSON.parser = Parser JSON.generator = Generator end end diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index 83ed9f2508e2ba..2906cfd1e8fe1c 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -3,7 +3,7 @@ #include "ruby.h" #include "../fbuffer/fbuffer.h" -static VALUE mJSON, mExt, cParser, eNestingError, Encoding_UTF_8; +static VALUE mJSON, eNestingError, Encoding_UTF_8; static VALUE CNaN, CInfinity, CMinusInfinity; static ID i_json_creatable_p, i_json_create, i_create_id, @@ -374,17 +374,11 @@ static int convert_UTF32_to_UTF8(char *buf, uint32_t ch) } typedef struct JSON_ParserStruct { - VALUE Vsource; - char *source; - long len; - char *memo; VALUE create_id; VALUE object_class; VALUE array_class; VALUE decimal_class; VALUE match_string; - FBuffer fbuffer; - int in_array; int max_nesting; bool allow_nan; bool allow_trailing_comma; @@ -393,16 +387,22 @@ typedef struct JSON_ParserStruct { bool freeze; bool create_additions; bool deprecated_create_additions; - rvalue_cache name_cache; - rvalue_stack *stack; - VALUE stack_handle; } JSON_Parser; -#define GET_PARSER \ - GET_PARSER_INIT; \ - if (!json->Vsource) rb_raise(rb_eTypeError, "uninitialized instance") +typedef struct JSON_ParserStateStruct { + JSON_Parser *json; + VALUE Vsource; + VALUE stack_handle; + char *source; + long len; + char *memo; + FBuffer fbuffer; + rvalue_stack *stack; + rvalue_cache name_cache; + int in_array; +} JSON_ParserState; -#define GET_PARSER_INIT \ +#define GET_PARSER \ JSON_Parser *json; \ TypedData_Get_Struct(self, JSON_Parser, &JSON_Parser_type, json) @@ -410,12 +410,11 @@ typedef struct JSON_ParserStruct { #define EVIL 0x666 static const rb_data_type_t JSON_Parser_type; -static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result); -static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting); -static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting); -static char *JSON_parse_number(JSON_Parser *json, char *p, char *pe, VALUE *result); -static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting); - +static char *JSON_parse_string(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result); +static char *JSON_parse_object(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting); +static char *JSON_parse_value(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting); +static char *JSON_parse_number(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result); +static char *JSON_parse_array(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting); #ifndef HAVE_STRNLEN static size_t strnlen(const char *s, size_t maxlen) @@ -447,11 +446,11 @@ static void raise_parse_error(const char *format, const char *start) -#line 473 "parser.rl" +#line 472 "parser.rl" -#line 455 "parser.c" +#line 454 "parser.c" enum {JSON_object_start = 1}; enum {JSON_object_first_final = 32}; enum {JSON_object_error = 0}; @@ -459,12 +458,12 @@ enum {JSON_object_error = 0}; enum {JSON_object_en_main = 1}; -#line 513 "parser.rl" +#line 512 "parser.rl" -#define PUSH(result) rvalue_stack_push(json->stack, result, &json->stack_handle, &json->stack) +#define PUSH(result) rvalue_stack_push(state->stack, result, &state->stack_handle, &state->stack) -static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) +static char *JSON_parse_object(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) { int cs = EVIL; @@ -472,17 +471,17 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); } - long stack_head = json->stack->head; + long stack_head = state->stack->head; -#line 479 "parser.c" +#line 478 "parser.c" { cs = JSON_object_start; } -#line 528 "parser.rl" +#line 527 "parser.rl" -#line 486 "parser.c" +#line 485 "parser.c" { short _widec; if ( p == pe ) @@ -511,11 +510,11 @@ case 2: goto st2; goto st0; tr2: -#line 492 "parser.rl" +#line 491 "parser.rl" { char *np; json->parsing_name = true; - np = JSON_parse_string(json, p, pe, result); + np = JSON_parse_string(state, json, p, pe, result); json->parsing_name = false; if (np == NULL) { p--; {p++; cs = 3; goto _out;} } else { PUSH(*result); @@ -527,7 +526,7 @@ case 2: if ( ++p == pe ) goto _test_eof3; case 3: -#line 531 "parser.c" +#line 530 "parser.c" switch( (*p) ) { case 13: goto st3; case 32: goto st3; @@ -594,9 +593,9 @@ case 8: goto st8; goto st0; tr11: -#line 481 "parser.rl" +#line 480 "parser.rl" { - char *np = JSON_parse_value(json, p, pe, result, current_nesting); + char *np = JSON_parse_value(state, json, p, pe, result, current_nesting); if (np == NULL) { p--; {p++; cs = 9; goto _out;} } else { @@ -608,20 +607,20 @@ case 8: if ( ++p == pe ) goto _test_eof9; case 9: -#line 612 "parser.c" +#line 611 "parser.c" _widec = (*p); if ( (*p) < 13 ) { if ( (*p) > 9 ) { if ( 10 <= (*p) && (*p) <= 10 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 490 "parser.rl" +#line 489 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) >= 9 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 490 "parser.rl" +#line 489 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) > 13 ) { @@ -629,26 +628,26 @@ case 9: if ( 32 <= (*p) && (*p) <= 32 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 490 "parser.rl" +#line 489 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) > 44 ) { if ( 47 <= (*p) && (*p) <= 47 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 490 "parser.rl" +#line 489 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 490 "parser.rl" +#line 489 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 490 "parser.rl" +#line 489 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -669,14 +668,14 @@ case 9: goto st10; goto st0; tr4: -#line 503 "parser.rl" +#line 502 "parser.rl" { p--; {p++; cs = 32; goto _out;} } goto st32; st32: if ( ++p == pe ) goto _test_eof32; case 32: -#line 680 "parser.c" +#line 679 "parser.c" goto st0; st10: if ( ++p == pe ) @@ -778,13 +777,13 @@ case 20: if ( 47 <= (*p) && (*p) <= 47 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 490 "parser.rl" +#line 489 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) >= 42 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 490 "parser.rl" +#line 489 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -803,20 +802,20 @@ case 21: if ( (*p) <= 41 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 490 "parser.rl" +#line 489 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) > 42 ) { if ( 43 <= (*p) ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 490 "parser.rl" +#line 489 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 490 "parser.rl" +#line 489 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -839,13 +838,13 @@ case 22: if ( 42 <= (*p) && (*p) <= 42 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 490 "parser.rl" +#line 489 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 490 "parser.rl" +#line 489 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) > 46 ) { @@ -853,19 +852,19 @@ case 22: if ( 48 <= (*p) ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 490 "parser.rl" +#line 489 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) >= 47 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 490 "parser.rl" +#line 489 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 490 "parser.rl" +#line 489 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -889,20 +888,20 @@ case 23: if ( (*p) <= 9 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 490 "parser.rl" +#line 489 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) > 10 ) { if ( 11 <= (*p) ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 490 "parser.rl" +#line 489 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 490 "parser.rl" +#line 489 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -1016,15 +1015,15 @@ case 31: _out: {} } -#line 529 "parser.rl" +#line 528 "parser.rl" if (cs >= JSON_object_first_final) { - long count = json->stack->head - stack_head; + long count = state->stack->head - stack_head; if (RB_UNLIKELY(json->object_class)) { VALUE object = rb_class_new_instance(0, 0, json->object_class); long index = 0; - VALUE *items = rvalue_stack_peek(json->stack, count); + VALUE *items = rvalue_stack_peek(state->stack, count); while (index < count) { VALUE name = items[index++]; VALUE value = items[index++]; @@ -1038,10 +1037,10 @@ case 31: #else hash = rb_hash_new(); #endif - rb_hash_bulk_insert(count, rvalue_stack_peek(json->stack, count), hash); + rb_hash_bulk_insert(count, rvalue_stack_peek(state->stack, count), hash); *result = hash; } - rvalue_stack_pop(json->stack, count); + rvalue_stack_pop(state->stack, count); if (RB_UNLIKELY(json->create_additions)) { VALUE klassname; @@ -1067,7 +1066,7 @@ case 31: } -#line 1071 "parser.c" +#line 1070 "parser.c" enum {JSON_value_start = 1}; enum {JSON_value_first_final = 29}; enum {JSON_value_error = 0}; @@ -1075,22 +1074,22 @@ enum {JSON_value_error = 0}; enum {JSON_value_en_main = 1}; -#line 662 "parser.rl" +#line 661 "parser.rl" -static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) +static char *JSON_parse_value(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) { int cs = EVIL; -#line 1087 "parser.c" +#line 1086 "parser.c" { cs = JSON_value_start; } -#line 669 "parser.rl" +#line 668 "parser.rl" -#line 1094 "parser.c" +#line 1093 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1124,9 +1123,9 @@ case 1: cs = 0; goto _out; tr2: -#line 607 "parser.rl" +#line 606 "parser.rl" { - char *np = JSON_parse_string(json, p, pe, result); + char *np = JSON_parse_string(state, json, p, pe, result); if (np == NULL) { p--; {p++; cs = 29; goto _out;} @@ -1136,7 +1135,7 @@ cs = 0; } goto st29; tr3: -#line 617 "parser.rl" +#line 616 "parser.rl" { char *np; if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) { @@ -1148,7 +1147,7 @@ cs = 0; raise_parse_error("unexpected token at '%s'", p); } } - np = JSON_parse_number(json, p, pe, result); + np = JSON_parse_number(state, json, p, pe, result); if (np != NULL) { {p = (( np))-1;} } @@ -1156,25 +1155,25 @@ cs = 0; } goto st29; tr7: -#line 635 "parser.rl" +#line 634 "parser.rl" { char *np; - json->in_array++; - np = JSON_parse_array(json, p, pe, result, current_nesting + 1); - json->in_array--; + state->in_array++; + np = JSON_parse_array(state, json, p, pe, result, current_nesting + 1); + state->in_array--; if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;} } goto st29; tr11: -#line 643 "parser.rl" +#line 642 "parser.rl" { char *np; - np = JSON_parse_object(json, p, pe, result, current_nesting + 1); + np = JSON_parse_object(state, json, p, pe, result, current_nesting + 1); if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;} } goto st29; tr25: -#line 600 "parser.rl" +#line 599 "parser.rl" { if (json->allow_nan) { *result = CInfinity; @@ -1184,7 +1183,7 @@ cs = 0; } goto st29; tr27: -#line 593 "parser.rl" +#line 592 "parser.rl" { if (json->allow_nan) { *result = CNaN; @@ -1194,19 +1193,19 @@ cs = 0; } goto st29; tr31: -#line 587 "parser.rl" +#line 586 "parser.rl" { *result = Qfalse; } goto st29; tr34: -#line 584 "parser.rl" +#line 583 "parser.rl" { *result = Qnil; } goto st29; tr37: -#line 590 "parser.rl" +#line 589 "parser.rl" { *result = Qtrue; } @@ -1215,9 +1214,9 @@ cs = 0; if ( ++p == pe ) goto _test_eof29; case 29: -#line 649 "parser.rl" +#line 648 "parser.rl" { p--; {p++; cs = 29; goto _out;} } -#line 1221 "parser.c" +#line 1220 "parser.c" switch( (*p) ) { case 13: goto st29; case 32: goto st29; @@ -1458,7 +1457,7 @@ case 28: _out: {} } -#line 670 "parser.rl" +#line 669 "parser.rl" if (json->freeze) { OBJ_FREEZE(*result); @@ -1473,7 +1472,7 @@ case 28: } -#line 1477 "parser.c" +#line 1476 "parser.c" enum {JSON_integer_start = 1}; enum {JSON_integer_first_final = 3}; enum {JSON_integer_error = 0}; @@ -1481,7 +1480,7 @@ enum {JSON_integer_error = 0}; enum {JSON_integer_en_main = 1}; -#line 691 "parser.rl" +#line 690 "parser.rl" #define MAX_FAST_INTEGER_SIZE 18 @@ -1506,22 +1505,22 @@ static inline VALUE fast_parse_integer(char *p, char *pe) return LL2NUM(memo); } -static char *JSON_decode_integer(JSON_Parser *json, char *p, VALUE *result) +static char *JSON_decode_integer(JSON_ParserState *state, JSON_Parser *json, char *p, VALUE *result) { - long len = p - json->memo; + long len = p - state->memo; if (RB_LIKELY(len < MAX_FAST_INTEGER_SIZE)) { - *result = fast_parse_integer(json->memo, p); + *result = fast_parse_integer(state->memo, p); } else { - fbuffer_clear(&json->fbuffer); - fbuffer_append(&json->fbuffer, json->memo, len); - fbuffer_append_char(&json->fbuffer, '\0'); - *result = rb_cstr2inum(FBUFFER_PTR(&json->fbuffer), 10); + fbuffer_clear(&state->fbuffer); + fbuffer_append(&state->fbuffer, state->memo, len); + fbuffer_append_char(&state->fbuffer, '\0'); + *result = rb_cstr2inum(FBUFFER_PTR(&state->fbuffer), 10); } return p + 1; } -#line 1525 "parser.c" +#line 1524 "parser.c" enum {JSON_float_start = 1}; enum {JSON_float_first_final = 6}; enum {JSON_float_error = 0}; @@ -1529,24 +1528,24 @@ enum {JSON_float_error = 0}; enum {JSON_float_en_main = 1}; -#line 743 "parser.rl" +#line 742 "parser.rl" -static char *JSON_parse_number(JSON_Parser *json, char *p, char *pe, VALUE *result) +static char *JSON_parse_number(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result) { int cs = EVIL; bool is_float = false; -#line 1542 "parser.c" +#line 1541 "parser.c" { cs = JSON_float_start; } -#line 751 "parser.rl" - json->memo = p; +#line 750 "parser.rl" + state->memo = p; -#line 1550 "parser.c" +#line 1549 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1586,24 +1585,24 @@ case 6: goto st0; goto tr7; tr7: -#line 735 "parser.rl" +#line 734 "parser.rl" { p--; {p++; cs = 7; goto _out;} } goto st7; st7: if ( ++p == pe ) goto _test_eof7; case 7: -#line 1597 "parser.c" +#line 1596 "parser.c" goto st0; tr8: -#line 736 "parser.rl" +#line 735 "parser.rl" { is_float = true; } goto st3; st3: if ( ++p == pe ) goto _test_eof3; case 3: -#line 1607 "parser.c" +#line 1606 "parser.c" if ( 48 <= (*p) && (*p) <= 57 ) goto st8; goto st0; @@ -1622,14 +1621,14 @@ case 8: goto st0; goto tr7; tr9: -#line 736 "parser.rl" +#line 735 "parser.rl" { is_float = true; } goto st4; st4: if ( ++p == pe ) goto _test_eof4; case 4: -#line 1633 "parser.c" +#line 1632 "parser.c" switch( (*p) ) { case 43: goto st5; case 45: goto st5; @@ -1686,11 +1685,11 @@ case 10: _out: {} } -#line 753 "parser.rl" +#line 752 "parser.rl" if (cs >= JSON_float_first_final) { if (!is_float) { - return JSON_decode_integer(json, p, result); + return JSON_decode_integer(state, json, p, result); } VALUE mod = Qnil; ID method_id = 0; @@ -1722,16 +1721,16 @@ case 10: } } - long len = p - json->memo; - fbuffer_clear(&json->fbuffer); - fbuffer_append(&json->fbuffer, json->memo, len); - fbuffer_append_char(&json->fbuffer, '\0'); + long len = p - state->memo; + fbuffer_clear(&state->fbuffer); + fbuffer_append(&state->fbuffer, state->memo, len); + fbuffer_append_char(&state->fbuffer, '\0'); if (method_id) { - VALUE text = rb_str_new2(FBUFFER_PTR(&json->fbuffer)); + VALUE text = rb_str_new2(FBUFFER_PTR(&state->fbuffer)); *result = rb_funcallv(mod, method_id, 1, &text); } else { - *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(&json->fbuffer), 1)); + *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(&state->fbuffer), 1)); } return p + 1; @@ -1742,7 +1741,7 @@ case 10: -#line 1746 "parser.c" +#line 1745 "parser.c" enum {JSON_array_start = 1}; enum {JSON_array_first_final = 22}; enum {JSON_array_error = 0}; @@ -1750,27 +1749,27 @@ enum {JSON_array_error = 0}; enum {JSON_array_en_main = 1}; -#line 833 "parser.rl" +#line 832 "parser.rl" -static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) +static char *JSON_parse_array(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) { int cs = EVIL; if (json->max_nesting && current_nesting > json->max_nesting) { rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); } - long stack_head = json->stack->head; + long stack_head = state->stack->head; -#line 1767 "parser.c" +#line 1766 "parser.c" { cs = JSON_array_start; } -#line 845 "parser.rl" +#line 844 "parser.rl" -#line 1774 "parser.c" +#line 1773 "parser.c" { short _widec; if ( p == pe ) @@ -1810,10 +1809,10 @@ case 2: goto st2; goto st0; tr2: -#line 813 "parser.rl" +#line 812 "parser.rl" { VALUE v = Qnil; - char *np = JSON_parse_value(json, p, pe, &v, current_nesting); + char *np = JSON_parse_value(state, json, p, pe, &v, current_nesting); if (np == NULL) { p--; {p++; cs = 3; goto _out;} } else { @@ -1825,12 +1824,12 @@ case 2: if ( ++p == pe ) goto _test_eof3; case 3: -#line 1829 "parser.c" +#line 1828 "parser.c" _widec = (*p); if ( 44 <= (*p) && (*p) <= 44 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 823 "parser.rl" +#line 822 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -1877,14 +1876,14 @@ case 7: goto st3; goto st7; tr4: -#line 825 "parser.rl" +#line 824 "parser.rl" { p--; {p++; cs = 22; goto _out;} } goto st22; st22: if ( ++p == pe ) goto _test_eof22; case 22: -#line 1888 "parser.c" +#line 1887 "parser.c" goto st0; st8: if ( ++p == pe ) @@ -1952,13 +1951,13 @@ case 13: if ( 10 <= (*p) && (*p) <= 10 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 823 "parser.rl" +#line 822 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) >= 9 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 823 "parser.rl" +#line 822 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) > 13 ) { @@ -1966,19 +1965,19 @@ case 13: if ( 47 <= (*p) && (*p) <= 47 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 823 "parser.rl" +#line 822 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) >= 32 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 823 "parser.rl" +#line 822 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 823 "parser.rl" +#line 822 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -2017,13 +2016,13 @@ case 14: if ( 47 <= (*p) && (*p) <= 47 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 823 "parser.rl" +#line 822 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) >= 42 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 823 "parser.rl" +#line 822 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -2042,20 +2041,20 @@ case 15: if ( (*p) <= 41 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 823 "parser.rl" +#line 822 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) > 42 ) { if ( 43 <= (*p) ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 823 "parser.rl" +#line 822 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 823 "parser.rl" +#line 822 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -2078,13 +2077,13 @@ case 16: if ( 42 <= (*p) && (*p) <= 42 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 823 "parser.rl" +#line 822 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 823 "parser.rl" +#line 822 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) > 46 ) { @@ -2092,19 +2091,19 @@ case 16: if ( 48 <= (*p) ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 823 "parser.rl" +#line 822 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) >= 47 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 823 "parser.rl" +#line 822 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 823 "parser.rl" +#line 822 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -2128,20 +2127,20 @@ case 17: if ( (*p) <= 9 ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 823 "parser.rl" +#line 822 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else if ( (*p) > 10 ) { if ( 11 <= (*p) ) { _widec = (short)(128 + ((*p) - -128)); if ( -#line 823 "parser.rl" +#line 822 "parser.rl" json->allow_trailing_comma ) _widec += 256; } } else { _widec = (short)(128 + ((*p) - -128)); if ( -#line 823 "parser.rl" +#line 822 "parser.rl" json->allow_trailing_comma ) _widec += 256; } switch( _widec ) { @@ -2213,24 +2212,24 @@ case 21: _out: {} } -#line 846 "parser.rl" +#line 845 "parser.rl" if(cs >= JSON_array_first_final) { - long count = json->stack->head - stack_head; + long count = state->stack->head - stack_head; if (RB_UNLIKELY(json->array_class)) { VALUE array = rb_class_new_instance(0, 0, json->array_class); - VALUE *items = rvalue_stack_peek(json->stack, count); + VALUE *items = rvalue_stack_peek(state->stack, count); long index; for (index = 0; index < count; index++) { rb_funcall(array, i_leftshift, 1, items[index]); } *result = array; } else { - VALUE array = rb_ary_new_from_values(count, rvalue_stack_peek(json->stack, count)); + VALUE array = rb_ary_new_from_values(count, rvalue_stack_peek(state->stack, count)); *result = array; } - rvalue_stack_pop(json->stack, count); + rvalue_stack_pop(state->stack, count); return p + 1; } else { @@ -2265,16 +2264,16 @@ static inline VALUE build_string(const char *start, const char *end, bool intern return result; } -static VALUE json_string_fastpath(JSON_Parser *json, char *string, char *stringEnd, bool is_name, bool intern, bool symbolize) +static VALUE json_string_fastpath(JSON_ParserState *state, char *string, char *stringEnd, bool is_name, bool intern, bool symbolize) { size_t bufferSize = stringEnd - string; - if (is_name && json->in_array) { + if (is_name && state->in_array) { VALUE cached_key; if (RB_UNLIKELY(symbolize)) { - cached_key = rsymbol_cache_fetch(&json->name_cache, string, bufferSize); + cached_key = rsymbol_cache_fetch(&state->name_cache, string, bufferSize); } else { - cached_key = rstring_cache_fetch(&json->name_cache, string, bufferSize); + cached_key = rstring_cache_fetch(&state->name_cache, string, bufferSize); } if (RB_LIKELY(cached_key)) { @@ -2285,19 +2284,19 @@ static VALUE json_string_fastpath(JSON_Parser *json, char *string, char *stringE return build_string(string, stringEnd, intern, symbolize); } -static VALUE json_string_unescape(JSON_Parser *json, char *string, char *stringEnd, bool is_name, bool intern, bool symbolize) +static VALUE json_string_unescape(JSON_ParserState *state, char *string, char *stringEnd, bool is_name, bool intern, bool symbolize) { size_t bufferSize = stringEnd - string; char *p = string, *pe = string, *unescape, *bufferStart, *buffer; int unescape_len; char buf[4]; - if (is_name && json->in_array) { + if (is_name && state->in_array) { VALUE cached_key; if (RB_UNLIKELY(symbolize)) { - cached_key = rsymbol_cache_fetch(&json->name_cache, string, bufferSize); + cached_key = rsymbol_cache_fetch(&state->name_cache, string, bufferSize); } else { - cached_key = rstring_cache_fetch(&json->name_cache, string, bufferSize); + cached_key = rstring_cache_fetch(&state->name_cache, string, bufferSize); } if (RB_LIKELY(cached_key)) { @@ -2407,7 +2406,7 @@ static VALUE json_string_unescape(JSON_Parser *json, char *string, char *stringE } -#line 2411 "parser.c" +#line 2410 "parser.c" enum {JSON_string_start = 1}; enum {JSON_string_first_final = 9}; enum {JSON_string_error = 0}; @@ -2415,7 +2414,7 @@ enum {JSON_string_error = 0}; enum {JSON_string_en_main = 1}; -#line 1069 "parser.rl" +#line 1068 "parser.rl" static int @@ -2430,21 +2429,21 @@ match_i(VALUE regexp, VALUE klass, VALUE memo) return ST_CONTINUE; } -static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result) +static char *JSON_parse_string(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result) { int cs = EVIL; VALUE match_string; -#line 2440 "parser.c" +#line 2439 "parser.c" { cs = JSON_string_start; } -#line 1089 "parser.rl" - json->memo = p; +#line 1088 "parser.rl" + state->memo = p; -#line 2448 "parser.c" +#line 2447 "parser.c" { if ( p == pe ) goto _test_eof; @@ -2469,25 +2468,25 @@ case 2: goto st0; goto st2; tr2: -#line 1051 "parser.rl" +#line 1050 "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); + *result = json_string_fastpath(state, state->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 1044 "parser.rl" +#line 1043 "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); + *result = json_string_unescape(state, state->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;} } goto st9; tr6: -#line 1044 "parser.rl" +#line 1043 "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); + *result = json_string_unescape(state, state->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;} @@ -2497,7 +2496,7 @@ case 2: if ( ++p == pe ) goto _test_eof9; case 9: -#line 2501 "parser.c" +#line 2500 "parser.c" goto st0; st3: if ( ++p == pe ) @@ -2585,7 +2584,7 @@ case 8: _out: {} } -#line 1091 "parser.rl" +#line 1090 "parser.rl" if (json->create_additions && RTEST(match_string = json->match_string)) { VALUE klass; @@ -2660,13 +2659,8 @@ static int configure_parser_i(VALUE key, VALUE val, VALUE data) return ST_CONTINUE; } -static void parser_init(JSON_Parser *json, VALUE source, VALUE opts) +static void parser_init(JSON_Parser *json, VALUE opts) { - if (json->Vsource) { - rb_raise(rb_eTypeError, "already initialized instance"); - } - - json->fbuffer.initial_length = FBUFFER_INITIAL_LENGTH_DEFAULT; json->max_nesting = 100; if (!NIL_P(opts)) { @@ -2688,17 +2682,12 @@ static void parser_init(JSON_Parser *json, VALUE source, VALUE opts) } } - source = convert_encoding(StringValue(source)); - StringValue(source); - json->len = RSTRING_LEN(source); - json->source = RSTRING_PTR(source); - json->Vsource = source; } /* - * call-seq: new(source, opts => {}) + * call-seq: new(opts => {}) * - * Creates a new JSON::Ext::Parser instance for the string _source_. + * Creates a new JSON::Ext::ParserConfig instance. * * It will be configured by the _opts_ hash. _opts_ can have the following * keys: @@ -2727,18 +2716,16 @@ static void parser_init(JSON_Parser *json, VALUE source, VALUE opts) * (Float) when parsing decimal numbers. This class must accept a single * string argument in its constructor. */ -static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) +static VALUE cParserConfig_initialize(VALUE self, VALUE opts) { - GET_PARSER_INIT; - - rb_check_arity(argc, 1, 2); + GET_PARSER; - parser_init(json, argv[0], argc == 2 ? argv[1] : Qnil); + parser_init(json, opts); return self; } -#line 2742 "parser.c" +#line 2729 "parser.c" enum {JSON_start = 1}; enum {JSON_first_final = 10}; enum {JSON_error = 0}; @@ -2746,45 +2733,28 @@ enum {JSON_error = 0}; enum {JSON_en_main = 1}; -#line 1257 "parser.rl" +#line 1244 "parser.rl" -/* - * call-seq: parse() - * - * Parses the current JSON text _source_ and returns the complete data - * structure as a result. - * It raises JSON::ParserError if fail to parse. - */ -static VALUE cParser_parse(VALUE self) +static VALUE cParser_parse_safe(VALUE vstate) { + JSON_ParserState *state = (JSON_ParserState *)vstate; + VALUE result = Qnil; char *p, *pe; int cs = EVIL; - VALUE result = Qnil; - GET_PARSER; - - char stack_buffer[FBUFFER_STACK_SIZE]; - fbuffer_stack_init(&json->fbuffer, FBUFFER_INITIAL_LENGTH_DEFAULT, stack_buffer, FBUFFER_STACK_SIZE); - - VALUE rvalue_stack_buffer[RVALUE_STACK_INITIAL_CAPA]; - rvalue_stack stack = { - .type = RVALUE_STACK_STACK_ALLOCATED, - .ptr = rvalue_stack_buffer, - .capa = RVALUE_STACK_INITIAL_CAPA, - }; - json->stack = &stack; + JSON_Parser *json = state->json; -#line 2779 "parser.c" +#line 2749 "parser.c" { cs = JSON_start; } -#line 1285 "parser.rl" - p = json->source; - pe = p + json->len; +#line 1255 "parser.rl" + p = state->source; + pe = p + state->len; -#line 2788 "parser.c" +#line 2758 "parser.c" { if ( p == pe ) goto _test_eof; @@ -2818,9 +2788,9 @@ case 1: cs = 0; goto _out; tr2: -#line 1249 "parser.rl" +#line 1236 "parser.rl" { - char *np = JSON_parse_value(json, p, pe, &result, 0); + char *np = JSON_parse_value(state, json, p, pe, &result, 0); if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;} } goto st10; @@ -2828,7 +2798,7 @@ cs = 0; if ( ++p == pe ) goto _test_eof10; case 10: -#line 2832 "parser.c" +#line 2802 "parser.c" switch( (*p) ) { case 13: goto st10; case 32: goto st10; @@ -2917,10 +2887,10 @@ case 9: _out: {} } -#line 1288 "parser.rl" +#line 1258 "parser.rl" - if (json->stack_handle) { - rvalue_stack_eagerly_release(json->stack_handle); + if (state->stack_handle) { + rvalue_stack_eagerly_release(state->stack_handle); } if (cs >= JSON_first_final && p == pe) { @@ -2931,18 +2901,10 @@ case 9: } } -static VALUE cParser_m_parse(VALUE klass, VALUE source, VALUE opts) +static VALUE cParser_parse(JSON_Parser *json, VALUE Vsource) { - char *p, *pe; - int cs = EVIL; - VALUE result = Qnil; - - JSON_Parser _parser = {0}; - JSON_Parser *json = &_parser; - parser_init(json, source, opts); - - char stack_buffer[FBUFFER_STACK_SIZE]; - fbuffer_stack_init(&json->fbuffer, FBUFFER_INITIAL_LENGTH_DEFAULT, stack_buffer, FBUFFER_STACK_SIZE); + Vsource = convert_encoding(StringValue(Vsource)); + StringValue(Vsource); VALUE rvalue_stack_buffer[RVALUE_STACK_INITIAL_CAPA]; rvalue_stack stack = { @@ -2950,193 +2912,74 @@ static VALUE cParser_m_parse(VALUE klass, VALUE source, VALUE opts) .ptr = rvalue_stack_buffer, .capa = RVALUE_STACK_INITIAL_CAPA, }; - json->stack = &stack; + JSON_ParserState _state = { + .json = json, + .len = RSTRING_LEN(Vsource), + .source = RSTRING_PTR(Vsource), + .Vsource = Vsource, + .stack = &stack, + }; + JSON_ParserState *state = &_state; -#line 2957 "parser.c" - { - cs = JSON_start; - } + char stack_buffer[FBUFFER_STACK_SIZE]; + fbuffer_stack_init(&state->fbuffer, FBUFFER_INITIAL_LENGTH_DEFAULT, stack_buffer, FBUFFER_STACK_SIZE); -#line 1323 "parser.rl" - p = json->source; - pe = p + json->len; + int interupted; + VALUE result = rb_protect(cParser_parse_safe, (VALUE)state, &interupted); -#line 2966 "parser.c" - { - if ( p == pe ) - goto _test_eof; - switch ( cs ) - { -st1: - if ( ++p == pe ) - goto _test_eof1; -case 1: - switch( (*p) ) { - case 13: goto st1; - case 32: goto st1; - case 34: goto tr2; - case 45: goto tr2; - case 47: goto st6; - case 73: goto tr2; - case 78: goto tr2; - case 91: goto tr2; - case 102: goto tr2; - case 110: goto tr2; - case 116: goto tr2; - case 123: goto tr2; - } - if ( (*p) > 10 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto tr2; - } else if ( (*p) >= 9 ) - goto st1; - goto st0; -st0: -cs = 0; - goto _out; -tr2: -#line 1249 "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;} + fbuffer_free(&state->fbuffer); + if (interupted) { + rb_jump_tag(interupted); } - goto st10; -st10: - if ( ++p == pe ) - goto _test_eof10; -case 10: -#line 3010 "parser.c" - switch( (*p) ) { - case 13: goto st10; - case 32: goto st10; - case 47: goto st2; - } - if ( 9 <= (*p) && (*p) <= 10 ) - goto st10; - goto st0; -st2: - if ( ++p == pe ) - goto _test_eof2; -case 2: - switch( (*p) ) { - case 42: goto st3; - case 47: goto st5; - } - goto st0; -st3: - if ( ++p == pe ) - goto _test_eof3; -case 3: - if ( (*p) == 42 ) - goto st4; - goto st3; -st4: - if ( ++p == pe ) - goto _test_eof4; -case 4: - switch( (*p) ) { - case 42: goto st4; - case 47: goto st10; - } - goto st3; -st5: - if ( ++p == pe ) - goto _test_eof5; -case 5: - if ( (*p) == 10 ) - goto st10; - goto st5; -st6: - if ( ++p == pe ) - goto _test_eof6; -case 6: - switch( (*p) ) { - case 42: goto st7; - case 47: goto st9; - } - goto st0; -st7: - if ( ++p == pe ) - goto _test_eof7; -case 7: - if ( (*p) == 42 ) - goto st8; - goto st7; -st8: - if ( ++p == pe ) - goto _test_eof8; -case 8: - switch( (*p) ) { - case 42: goto st8; - case 47: goto st1; - } - goto st7; -st9: - if ( ++p == pe ) - goto _test_eof9; -case 9: - if ( (*p) == 10 ) - goto st1; - goto st9; - } - _test_eof1: cs = 1; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - _test_eof: {} - _out: {} - } + return result; +} + +/* + * call-seq: parse(source) + * + * Parses the current JSON text _source_ and returns the complete data + * structure as a result. + * It raises JSON::ParserError if fail to parse. + */ +static VALUE cParserConfig_parse(VALUE self, VALUE Vsource) +{ + GET_PARSER; + return cParser_parse(json, Vsource); +} -#line 1326 "parser.rl" +static VALUE cParser_m_parse(VALUE klass, VALUE Vsource, VALUE opts) +{ + Vsource = convert_encoding(StringValue(Vsource)); + StringValue(Vsource); - if (json->stack_handle) { - rvalue_stack_eagerly_release(json->stack_handle); - } + JSON_Parser _parser = {0}; + JSON_Parser *json = &_parser; + parser_init(json, opts); - if (cs >= JSON_first_final && p == pe) { - return result; - } else { - raise_parse_error("unexpected token at '%s'", p); - return Qnil; - } + return cParser_parse(json, Vsource); } static void JSON_mark(void *ptr) { JSON_Parser *json = ptr; - rb_gc_mark(json->Vsource); rb_gc_mark(json->create_id); rb_gc_mark(json->object_class); rb_gc_mark(json->array_class); rb_gc_mark(json->decimal_class); rb_gc_mark(json->match_string); - rb_gc_mark(json->stack_handle); - - 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) { JSON_Parser *json = ptr; - fbuffer_free(&json->fbuffer); ruby_xfree(json); } static size_t JSON_memsize(const void *ptr) { - const JSON_Parser *json = ptr; - return sizeof(*json) + FBUFFER_CAPA(&json->fbuffer); + return sizeof(JSON_Parser); } static const rb_data_type_t JSON_Parser_type = { @@ -3149,21 +2992,7 @@ static const rb_data_type_t JSON_Parser_type = { static VALUE cJSON_parser_s_allocate(VALUE klass) { JSON_Parser *json; - VALUE obj = TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json); - fbuffer_stack_init(&json->fbuffer, 0, NULL, 0); - return obj; -} - -/* - * call-seq: source() - * - * Returns a copy of the current _source_ string, that was used to construct - * this Parser. - */ -static VALUE cParser_source(VALUE self) -{ - GET_PARSER; - return rb_str_dup(json->Vsource); + return TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json); } void Init_parser(void) @@ -3175,15 +3004,15 @@ void Init_parser(void) #undef rb_intern rb_require("json/common"); mJSON = rb_define_module("JSON"); - mExt = rb_define_module_under(mJSON, "Ext"); - cParser = rb_define_class_under(mExt, "Parser", rb_cObject); + VALUE mExt = rb_define_module_under(mJSON, "Ext"); + VALUE cParserConfig = rb_define_class_under(mExt, "ParserConfig", rb_cObject); eNestingError = rb_path2class("JSON::NestingError"); rb_gc_register_mark_object(eNestingError); - rb_define_alloc_func(cParser, cJSON_parser_s_allocate); - rb_define_method(cParser, "initialize", cParser_initialize, -1); - rb_define_method(cParser, "parse", cParser_parse, 0); - rb_define_method(cParser, "source", cParser_source, 0); + rb_define_alloc_func(cParserConfig, cJSON_parser_s_allocate); + rb_define_method(cParserConfig, "initialize", cParserConfig_initialize, 1); + rb_define_method(cParserConfig, "parse", cParserConfig_parse, 1); + VALUE cParser = rb_define_class_under(mExt, "Parser", rb_cObject); rb_define_singleton_method(cParser, "parse", cParser_m_parse, 2); CNaN = rb_const_get(mJSON, rb_intern("NaN")); diff --git a/ext/json/parser/parser.rl b/ext/json/parser/parser.rl index 9856a738088493..50226a7259380b 100644 --- a/ext/json/parser/parser.rl +++ b/ext/json/parser/parser.rl @@ -1,7 +1,7 @@ #include "ruby.h" #include "../fbuffer/fbuffer.h" -static VALUE mJSON, mExt, cParser, eNestingError, Encoding_UTF_8; +static VALUE mJSON, eNestingError, Encoding_UTF_8; static VALUE CNaN, CInfinity, CMinusInfinity; static ID i_json_creatable_p, i_json_create, i_create_id, @@ -372,17 +372,11 @@ static int convert_UTF32_to_UTF8(char *buf, uint32_t ch) } typedef struct JSON_ParserStruct { - VALUE Vsource; - char *source; - long len; - char *memo; VALUE create_id; VALUE object_class; VALUE array_class; VALUE decimal_class; VALUE match_string; - FBuffer fbuffer; - int in_array; int max_nesting; bool allow_nan; bool allow_trailing_comma; @@ -391,16 +385,22 @@ typedef struct JSON_ParserStruct { bool freeze; bool create_additions; bool deprecated_create_additions; - rvalue_cache name_cache; - rvalue_stack *stack; - VALUE stack_handle; } JSON_Parser; -#define GET_PARSER \ - GET_PARSER_INIT; \ - if (!json->Vsource) rb_raise(rb_eTypeError, "uninitialized instance") +typedef struct JSON_ParserStateStruct { + JSON_Parser *json; + VALUE Vsource; + VALUE stack_handle; + char *source; + long len; + char *memo; + FBuffer fbuffer; + rvalue_stack *stack; + rvalue_cache name_cache; + int in_array; +} JSON_ParserState; -#define GET_PARSER_INIT \ +#define GET_PARSER \ JSON_Parser *json; \ TypedData_Get_Struct(self, JSON_Parser, &JSON_Parser_type, json) @@ -408,12 +408,11 @@ typedef struct JSON_ParserStruct { #define EVIL 0x666 static const rb_data_type_t JSON_Parser_type; -static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result); -static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting); -static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting); -static char *JSON_parse_number(JSON_Parser *json, char *p, char *pe, VALUE *result); -static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting); - +static char *JSON_parse_string(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result); +static char *JSON_parse_object(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting); +static char *JSON_parse_value(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting); +static char *JSON_parse_number(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result); +static char *JSON_parse_array(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting); #ifndef HAVE_STRNLEN static size_t strnlen(const char *s, size_t maxlen) @@ -479,7 +478,7 @@ static void raise_parse_error(const char *format, const char *start) write data; action parse_value { - char *np = JSON_parse_value(json, fpc, pe, result, current_nesting); + char *np = JSON_parse_value(state, json, fpc, pe, result, current_nesting); if (np == NULL) { fhold; fbreak; } else { @@ -492,7 +491,7 @@ static void raise_parse_error(const char *format, const char *start) action parse_name { char *np; json->parsing_name = true; - np = JSON_parse_string(json, fpc, pe, result); + np = JSON_parse_string(state, json, fpc, pe, result); json->parsing_name = false; if (np == NULL) { fhold; fbreak; } else { PUSH(*result); @@ -512,9 +511,9 @@ static void raise_parse_error(const char *format, const char *start) ) @exit; }%% -#define PUSH(result) rvalue_stack_push(json->stack, result, &json->stack_handle, &json->stack) +#define PUSH(result) rvalue_stack_push(state->stack, result, &state->stack_handle, &state->stack) -static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) +static char *JSON_parse_object(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) { int cs = EVIL; @@ -522,18 +521,18 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); } - long stack_head = json->stack->head; + long stack_head = state->stack->head; %% write init; %% write exec; if (cs >= JSON_object_first_final) { - long count = json->stack->head - stack_head; + long count = state->stack->head - stack_head; if (RB_UNLIKELY(json->object_class)) { VALUE object = rb_class_new_instance(0, 0, json->object_class); long index = 0; - VALUE *items = rvalue_stack_peek(json->stack, count); + VALUE *items = rvalue_stack_peek(state->stack, count); while (index < count) { VALUE name = items[index++]; VALUE value = items[index++]; @@ -547,10 +546,10 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu #else hash = rb_hash_new(); #endif - rb_hash_bulk_insert(count, rvalue_stack_peek(json->stack, count), hash); + rb_hash_bulk_insert(count, rvalue_stack_peek(state->stack, count), hash); *result = hash; } - rvalue_stack_pop(json->stack, count); + rvalue_stack_pop(state->stack, count); if (RB_UNLIKELY(json->create_additions)) { VALUE klassname; @@ -605,7 +604,7 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu } } action parse_string { - char *np = JSON_parse_string(json, fpc, pe, result); + char *np = JSON_parse_string(state, json, fpc, pe, result); if (np == NULL) { fhold; fbreak; @@ -625,7 +624,7 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu raise_parse_error("unexpected token at '%s'", p); } } - np = JSON_parse_number(json, fpc, pe, result); + np = JSON_parse_number(state, json, fpc, pe, result); if (np != NULL) { fexec np; } @@ -634,15 +633,15 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu action parse_array { char *np; - json->in_array++; - np = JSON_parse_array(json, fpc, pe, result, current_nesting + 1); - json->in_array--; + state->in_array++; + np = JSON_parse_array(state, json, fpc, pe, result, current_nesting + 1); + state->in_array--; if (np == NULL) { fhold; fbreak; } else fexec np; } action parse_object { char *np; - np = JSON_parse_object(json, fpc, pe, result, current_nesting + 1); + np = JSON_parse_object(state, json, fpc, pe, result, current_nesting + 1); if (np == NULL) { fhold; fbreak; } else fexec np; } @@ -661,7 +660,7 @@ main := ignore* ( ) ignore* %*exit; }%% -static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) +static char *JSON_parse_value(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) { int cs = EVIL; @@ -712,16 +711,16 @@ static inline VALUE fast_parse_integer(char *p, char *pe) return LL2NUM(memo); } -static char *JSON_decode_integer(JSON_Parser *json, char *p, VALUE *result) +static char *JSON_decode_integer(JSON_ParserState *state, JSON_Parser *json, char *p, VALUE *result) { - long len = p - json->memo; + long len = p - state->memo; if (RB_LIKELY(len < MAX_FAST_INTEGER_SIZE)) { - *result = fast_parse_integer(json->memo, p); + *result = fast_parse_integer(state->memo, p); } else { - fbuffer_clear(&json->fbuffer); - fbuffer_append(&json->fbuffer, json->memo, len); - fbuffer_append_char(&json->fbuffer, '\0'); - *result = rb_cstr2inum(FBUFFER_PTR(&json->fbuffer), 10); + fbuffer_clear(&state->fbuffer); + fbuffer_append(&state->fbuffer, state->memo, len); + fbuffer_append_char(&state->fbuffer, '\0'); + *result = rb_cstr2inum(FBUFFER_PTR(&state->fbuffer), 10); } return p + 1; } @@ -742,18 +741,18 @@ static char *JSON_decode_integer(JSON_Parser *json, char *p, VALUE *result) ) (^[0-9Ee.\-]? @exit )); }%% -static char *JSON_parse_number(JSON_Parser *json, char *p, char *pe, VALUE *result) +static char *JSON_parse_number(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result) { int cs = EVIL; bool is_float = false; %% write init; - json->memo = p; + state->memo = p; %% write exec; if (cs >= JSON_float_first_final) { if (!is_float) { - return JSON_decode_integer(json, p, result); + return JSON_decode_integer(state, json, p, result); } VALUE mod = Qnil; ID method_id = 0; @@ -785,16 +784,16 @@ static char *JSON_parse_number(JSON_Parser *json, char *p, char *pe, VALUE *resu } } - long len = p - json->memo; - fbuffer_clear(&json->fbuffer); - fbuffer_append(&json->fbuffer, json->memo, len); - fbuffer_append_char(&json->fbuffer, '\0'); + long len = p - state->memo; + fbuffer_clear(&state->fbuffer); + fbuffer_append(&state->fbuffer, state->memo, len); + fbuffer_append_char(&state->fbuffer, '\0'); if (method_id) { - VALUE text = rb_str_new2(FBUFFER_PTR(&json->fbuffer)); + VALUE text = rb_str_new2(FBUFFER_PTR(&state->fbuffer)); *result = rb_funcallv(mod, method_id, 1, &text); } else { - *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(&json->fbuffer), 1)); + *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(&state->fbuffer), 1)); } return p + 1; @@ -812,7 +811,7 @@ static char *JSON_parse_number(JSON_Parser *json, char *p, char *pe, VALUE *resu action parse_value { VALUE v = Qnil; - char *np = JSON_parse_value(json, fpc, pe, &v, current_nesting); + char *np = JSON_parse_value(state, json, fpc, pe, &v, current_nesting); if (np == NULL) { fhold; fbreak; } else { @@ -832,34 +831,34 @@ static char *JSON_parse_number(JSON_Parser *json, char *p, char *pe, VALUE *resu end_array @exit; }%% -static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) +static char *JSON_parse_array(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) { int cs = EVIL; if (json->max_nesting && current_nesting > json->max_nesting) { rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); } - long stack_head = json->stack->head; + long stack_head = state->stack->head; %% write init; %% write exec; if(cs >= JSON_array_first_final) { - long count = json->stack->head - stack_head; + long count = state->stack->head - stack_head; if (RB_UNLIKELY(json->array_class)) { VALUE array = rb_class_new_instance(0, 0, json->array_class); - VALUE *items = rvalue_stack_peek(json->stack, count); + VALUE *items = rvalue_stack_peek(state->stack, count); long index; for (index = 0; index < count; index++) { rb_funcall(array, i_leftshift, 1, items[index]); } *result = array; } else { - VALUE array = rb_ary_new_from_values(count, rvalue_stack_peek(json->stack, count)); + VALUE array = rb_ary_new_from_values(count, rvalue_stack_peek(state->stack, count)); *result = array; } - rvalue_stack_pop(json->stack, count); + rvalue_stack_pop(state->stack, count); return p + 1; } else { @@ -894,16 +893,16 @@ static inline VALUE build_string(const char *start, const char *end, bool intern return result; } -static VALUE json_string_fastpath(JSON_Parser *json, char *string, char *stringEnd, bool is_name, bool intern, bool symbolize) +static VALUE json_string_fastpath(JSON_ParserState *state, char *string, char *stringEnd, bool is_name, bool intern, bool symbolize) { size_t bufferSize = stringEnd - string; - if (is_name && json->in_array) { + if (is_name && state->in_array) { VALUE cached_key; if (RB_UNLIKELY(symbolize)) { - cached_key = rsymbol_cache_fetch(&json->name_cache, string, bufferSize); + cached_key = rsymbol_cache_fetch(&state->name_cache, string, bufferSize); } else { - cached_key = rstring_cache_fetch(&json->name_cache, string, bufferSize); + cached_key = rstring_cache_fetch(&state->name_cache, string, bufferSize); } if (RB_LIKELY(cached_key)) { @@ -914,19 +913,19 @@ static VALUE json_string_fastpath(JSON_Parser *json, char *string, char *stringE return build_string(string, stringEnd, intern, symbolize); } -static VALUE json_string_unescape(JSON_Parser *json, char *string, char *stringEnd, bool is_name, bool intern, bool symbolize) +static VALUE json_string_unescape(JSON_ParserState *state, char *string, char *stringEnd, bool is_name, bool intern, bool symbolize) { size_t bufferSize = stringEnd - string; char *p = string, *pe = string, *unescape, *bufferStart, *buffer; int unescape_len; char buf[4]; - if (is_name && json->in_array) { + if (is_name && state->in_array) { VALUE cached_key; if (RB_UNLIKELY(symbolize)) { - cached_key = rsymbol_cache_fetch(&json->name_cache, string, bufferSize); + cached_key = rsymbol_cache_fetch(&state->name_cache, string, bufferSize); } else { - cached_key = rstring_cache_fetch(&json->name_cache, string, bufferSize); + cached_key = rstring_cache_fetch(&state->name_cache, string, bufferSize); } if (RB_LIKELY(cached_key)) { @@ -1042,14 +1041,14 @@ static VALUE json_string_unescape(JSON_Parser *json, char *string, char *stringE write data; action parse_complex_string { - *result = json_string_unescape(json, json->memo + 1, p, json->parsing_name, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names); + *result = json_string_unescape(state, state->memo + 1, p, json->parsing_name, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names); fexec p + 1; fhold; fbreak; } action parse_simple_string { - *result = json_string_fastpath(json, json->memo + 1, p, json->parsing_name, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names); + *result = json_string_fastpath(state, state->memo + 1, p, json->parsing_name, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names); fexec p + 1; fhold; fbreak; @@ -1080,13 +1079,13 @@ match_i(VALUE regexp, VALUE klass, VALUE memo) return ST_CONTINUE; } -static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result) +static char *JSON_parse_string(JSON_ParserState *state, JSON_Parser *json, char *p, char *pe, VALUE *result) { int cs = EVIL; VALUE match_string; %% write init; - json->memo = p; + state->memo = p; %% write exec; if (json->create_additions && RTEST(match_string = json->match_string)) { @@ -1162,13 +1161,8 @@ static int configure_parser_i(VALUE key, VALUE val, VALUE data) return ST_CONTINUE; } -static void parser_init(JSON_Parser *json, VALUE source, VALUE opts) +static void parser_init(JSON_Parser *json, VALUE opts) { - if (json->Vsource) { - rb_raise(rb_eTypeError, "already initialized instance"); - } - - json->fbuffer.initial_length = FBUFFER_INITIAL_LENGTH_DEFAULT; json->max_nesting = 100; if (!NIL_P(opts)) { @@ -1190,17 +1184,12 @@ static void parser_init(JSON_Parser *json, VALUE source, VALUE opts) } } - source = convert_encoding(StringValue(source)); - StringValue(source); - json->len = RSTRING_LEN(source); - json->source = RSTRING_PTR(source); - json->Vsource = source; } /* - * call-seq: new(source, opts => {}) + * call-seq: new(opts => {}) * - * Creates a new JSON::Ext::Parser instance for the string _source_. + * Creates a new JSON::Ext::ParserConfig instance. * * It will be configured by the _opts_ hash. _opts_ can have the following * keys: @@ -1229,13 +1218,11 @@ static void parser_init(JSON_Parser *json, VALUE source, VALUE opts) * (Float) when parsing decimal numbers. This class must accept a single * string argument in its constructor. */ -static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) +static VALUE cParserConfig_initialize(VALUE self, VALUE opts) { - GET_PARSER_INIT; - - rb_check_arity(argc, 1, 2); + GET_PARSER; - parser_init(json, argv[0], argc == 2 ? argv[1] : Qnil); + parser_init(json, opts); return self; } @@ -1247,7 +1234,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) include JSON_common; action parse_value { - char *np = JSON_parse_value(json, fpc, pe, &result, 0); + char *np = JSON_parse_value(state, json, fpc, pe, &result, 0); if (np == NULL) { fhold; fbreak; } else fexec np; } @@ -1256,38 +1243,21 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) ) ignore*; }%% -/* - * call-seq: parse() - * - * Parses the current JSON text _source_ and returns the complete data - * structure as a result. - * It raises JSON::ParserError if fail to parse. - */ -static VALUE cParser_parse(VALUE self) +static VALUE cParser_parse_safe(VALUE vstate) { + JSON_ParserState *state = (JSON_ParserState *)vstate; + VALUE result = Qnil; char *p, *pe; int cs = EVIL; - VALUE result = Qnil; - GET_PARSER; - - char stack_buffer[FBUFFER_STACK_SIZE]; - fbuffer_stack_init(&json->fbuffer, FBUFFER_INITIAL_LENGTH_DEFAULT, stack_buffer, FBUFFER_STACK_SIZE); - - VALUE rvalue_stack_buffer[RVALUE_STACK_INITIAL_CAPA]; - rvalue_stack stack = { - .type = RVALUE_STACK_STACK_ALLOCATED, - .ptr = rvalue_stack_buffer, - .capa = RVALUE_STACK_INITIAL_CAPA, - }; - json->stack = &stack; + JSON_Parser *json = state->json; %% write init; - p = json->source; - pe = p + json->len; + p = state->source; + pe = p + state->len; %% write exec; - if (json->stack_handle) { - rvalue_stack_eagerly_release(json->stack_handle); + if (state->stack_handle) { + rvalue_stack_eagerly_release(state->stack_handle); } if (cs >= JSON_first_final && p == pe) { @@ -1298,18 +1268,10 @@ static VALUE cParser_parse(VALUE self) } } -static VALUE cParser_m_parse(VALUE klass, VALUE source, VALUE opts) +static VALUE cParser_parse(JSON_Parser *json, VALUE Vsource) { - char *p, *pe; - int cs = EVIL; - VALUE result = Qnil; - - JSON_Parser _parser = {0}; - JSON_Parser *json = &_parser; - parser_init(json, source, opts); - - char stack_buffer[FBUFFER_STACK_SIZE]; - fbuffer_stack_init(&json->fbuffer, FBUFFER_INITIAL_LENGTH_DEFAULT, stack_buffer, FBUFFER_STACK_SIZE); + Vsource = convert_encoding(StringValue(Vsource)); + StringValue(Vsource); VALUE rvalue_stack_buffer[RVALUE_STACK_INITIAL_CAPA]; rvalue_stack stack = { @@ -1317,53 +1279,74 @@ static VALUE cParser_m_parse(VALUE klass, VALUE source, VALUE opts) .ptr = rvalue_stack_buffer, .capa = RVALUE_STACK_INITIAL_CAPA, }; - json->stack = &stack; - %% write init; - p = json->source; - pe = p + json->len; - %% write exec; + JSON_ParserState _state = { + .json = json, + .len = RSTRING_LEN(Vsource), + .source = RSTRING_PTR(Vsource), + .Vsource = Vsource, + .stack = &stack, + }; + JSON_ParserState *state = &_state; - if (json->stack_handle) { - rvalue_stack_eagerly_release(json->stack_handle); - } + char stack_buffer[FBUFFER_STACK_SIZE]; + fbuffer_stack_init(&state->fbuffer, FBUFFER_INITIAL_LENGTH_DEFAULT, stack_buffer, FBUFFER_STACK_SIZE); - if (cs >= JSON_first_final && p == pe) { - return result; - } else { - raise_parse_error("unexpected token at '%s'", p); - return Qnil; + int interupted; + VALUE result = rb_protect(cParser_parse_safe, (VALUE)state, &interupted); + + fbuffer_free(&state->fbuffer); + if (interupted) { + rb_jump_tag(interupted); } + + return result; +} + +/* + * call-seq: parse(source) + * + * Parses the current JSON text _source_ and returns the complete data + * structure as a result. + * It raises JSON::ParserError if fail to parse. + */ +static VALUE cParserConfig_parse(VALUE self, VALUE Vsource) +{ + GET_PARSER; + return cParser_parse(json, Vsource); +} + +static VALUE cParser_m_parse(VALUE klass, VALUE Vsource, VALUE opts) +{ + Vsource = convert_encoding(StringValue(Vsource)); + StringValue(Vsource); + + JSON_Parser _parser = {0}; + JSON_Parser *json = &_parser; + parser_init(json, opts); + + return cParser_parse(json, Vsource); } static void JSON_mark(void *ptr) { JSON_Parser *json = ptr; - rb_gc_mark(json->Vsource); rb_gc_mark(json->create_id); rb_gc_mark(json->object_class); rb_gc_mark(json->array_class); rb_gc_mark(json->decimal_class); rb_gc_mark(json->match_string); - rb_gc_mark(json->stack_handle); - - 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) { JSON_Parser *json = ptr; - fbuffer_free(&json->fbuffer); ruby_xfree(json); } static size_t JSON_memsize(const void *ptr) { - const JSON_Parser *json = ptr; - return sizeof(*json) + FBUFFER_CAPA(&json->fbuffer); + return sizeof(JSON_Parser); } static const rb_data_type_t JSON_Parser_type = { @@ -1376,21 +1359,7 @@ static const rb_data_type_t JSON_Parser_type = { static VALUE cJSON_parser_s_allocate(VALUE klass) { JSON_Parser *json; - VALUE obj = TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json); - fbuffer_stack_init(&json->fbuffer, 0, NULL, 0); - return obj; -} - -/* - * call-seq: source() - * - * Returns a copy of the current _source_ string, that was used to construct - * this Parser. - */ -static VALUE cParser_source(VALUE self) -{ - GET_PARSER; - return rb_str_dup(json->Vsource); + return TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json); } void Init_parser(void) @@ -1402,15 +1371,15 @@ void Init_parser(void) #undef rb_intern rb_require("json/common"); mJSON = rb_define_module("JSON"); - mExt = rb_define_module_under(mJSON, "Ext"); - cParser = rb_define_class_under(mExt, "Parser", rb_cObject); + VALUE mExt = rb_define_module_under(mJSON, "Ext"); + VALUE cParserConfig = rb_define_class_under(mExt, "ParserConfig", rb_cObject); eNestingError = rb_path2class("JSON::NestingError"); rb_gc_register_mark_object(eNestingError); - rb_define_alloc_func(cParser, cJSON_parser_s_allocate); - rb_define_method(cParser, "initialize", cParser_initialize, -1); - rb_define_method(cParser, "parse", cParser_parse, 0); - rb_define_method(cParser, "source", cParser_source, 0); + rb_define_alloc_func(cParserConfig, cJSON_parser_s_allocate); + rb_define_method(cParserConfig, "initialize", cParserConfig_initialize, 1); + rb_define_method(cParserConfig, "parse", cParserConfig_parse, 1); + VALUE cParser = rb_define_class_under(mExt, "Parser", rb_cObject); rb_define_singleton_method(cParser, "parse", cParser_m_parse, 2); CNaN = rb_const_get(mJSON, rb_intern("NaN")); diff --git a/test/json/json_ext_parser_test.rb b/test/json/json_ext_parser_test.rb index da615049893148..8aa626257eff46 100644 --- a/test/json/json_ext_parser_test.rb +++ b/test/json/json_ext_parser_test.rb @@ -6,11 +6,11 @@ class JSONExtParserTest < Test::Unit::TestCase def test_allocate parser = JSON::Ext::Parser.new("{}") - assert_raise(TypeError, '[ruby-core:35079]') do - parser.__send__(:initialize, "{}") - end + parser.__send__(:initialize, "{}") + assert_equal "{}", parser.source + parser = JSON::Ext::Parser.allocate - assert_raise(TypeError, '[ruby-core:35079]') { parser.source } + assert_nil parser.source end def test_error_messages