--- JSON-XS/XS.xs 2013/10/29 15:55:49 1.122 +++ JSON-XS/XS.xs 2016/02/26 21:46:45 1.127 @@ -81,16 +81,18 @@ #ifdef USE_ITHREADS # define JSON_SLOW 1 # define JSON_STASH (json_stash ? json_stash : gv_stashpv ("JSON::XS", 1)) +# define BOOL_STASH (bool_stash ? bool_stash : gv_stashpv ("Types::Serialiser::Boolean", 1)) #else # define JSON_SLOW 0 # define JSON_STASH json_stash +# define BOOL_STASH bool_stash #endif // the amount of HEs to allocate on the stack, when sorting keys #define STACK_HES 64 -static HV *json_stash, *types_boolean_stash; // JSON::XS:: -static SV *types_true, *types_false, *sv_json; +static HV *json_stash, *bool_stash; // JSON::XS::, Types::Serialiser::Boolean:: +static SV *bool_true, *bool_false, *sv_json; enum { INCR_M_WS = 0, // initial whitespace skipping, must be 0 @@ -301,6 +303,47 @@ return neg ? -accum : accum; } + +// target of scalar reference is bool? -1 == nope, 0 == false, 1 == true +static int +ref_bool_type (SV *sv) +{ + svtype svt = SvTYPE (sv); + + if (svt < SVt_PVAV) + { + STRLEN len = 0; + char *pv = svt ? SvPV (sv, len) : 0; + + if (len == 1) + if (*pv == '1') + return 1; + else if (*pv == '0') + return 0; + + } + + return -1; +} + +// returns whether scalar is not a reference in the sense of allow_nonref +static int +json_nonref (SV *scalar) +{ + if (!SvROK (scalar)) + return 1; + + scalar = SvRV (scalar); + + if (SvSTASH (scalar) == bool_stash) + return 1; + + if (!SvOBJECT (scalar) && ref_bool_type (scalar) >= 0) + return 1; + + return 0; +} + ///////////////////////////////////////////////////////////////////////////// // encoder @@ -703,12 +746,9 @@ if (expect_false (SvOBJECT (sv))) { - HV *boolean_stash = !JSON_SLOW || types_boolean_stash - ? types_boolean_stash - : gv_stashpv ("Types::Serialiser::Boolean", 1); HV *stash = SvSTASH (sv); - if (stash == boolean_stash) + if (stash == bool_stash) { if (SvIV (sv)) encode_str (enc, "true", 4, 0); @@ -720,7 +760,9 @@ int count; dSP; - ENTER; SAVETMPS; PUSHMARK (SP); + ENTER; SAVETMPS; + SAVESTACK_POS (); + PUSHMARK (SP); EXTEND (SP, 2); // we re-bless the reference to get overload and other niceties right PUSHs (sv_bless (sv_2mortal (newRV_inc (sv)), stash)); @@ -732,7 +774,7 @@ // catch this surprisingly common error if (SvROK (TOPs) && SvRV (TOPs) == sv) - croak ("%s::TO_JSON method returned same object as was passed instead of a new one", HvNAME (SvSTASH (sv))); + croak ("%s::FREEZE method returned same object as was passed instead of a new one", HvNAME (SvSTASH (sv))); encode_ch (enc, '('); encode_ch (enc, '"'); @@ -751,15 +793,14 @@ encode_ch (enc, ']'); - PUTBACK; - FREETMPS; LEAVE; } else if ((enc->json.flags & F_CONV_BLESSED) && (method = gv_fetchmethod_autoload (stash, "TO_JSON", 0))) { dSP; - ENTER; SAVETMPS; PUSHMARK (SP); + ENTER; SAVETMPS; + PUSHMARK (SP); // we re-bless the reference to get overload and other niceties right XPUSHs (sv_bless (sv_2mortal (newRV_inc (sv)), stash)); @@ -791,12 +832,11 @@ encode_av (enc, (AV *)sv); else if (svt < SVt_PVAV) { - STRLEN len = 0; - char *pv = svt ? SvPV (sv, len) : 0; + int bool_type = ref_bool_type (sv); - if (len == 1 && *pv == '1') + if (bool_type == 1) encode_str (enc, "true", 4, 0); - else if (len == 1 && *pv == '0') + else if (bool_type == 0) encode_str (enc, "false", 5, 0); else if (enc->json.flags & F_ALLOW_UNKNOWN) encode_str (enc, "null", 4, 0); @@ -889,7 +929,7 @@ { enc_t enc; - if (!(json->flags & F_ALLOW_NONREF) && !SvROK (scalar)) + if (!(json->flags & F_ALLOW_NONREF) && json_nonref (scalar)) croak ("hash- or arrayref expected (not a simple scalar, use allow_nonref to allow this)"); enc.json = *json; @@ -1106,6 +1146,8 @@ utf8 = 1; } + else if (ch == '\t' && dec->json.flags & F_RELAXED) + *cur++ = ch; else { --dec_cur; @@ -1437,7 +1479,7 @@ he = hv_iternext (hv); hv_iterinit (hv); - // the next line creates a mortal sv each time its called. + // the next line creates a mortal sv each time it's called. // might want to optimise this for common cases. cb = hv_fetch_ent (dec->json.cb_sk_object, hv_iterkeysv (he), 0, 0); @@ -1446,7 +1488,9 @@ dSP; int count; - ENTER; SAVETMPS; PUSHMARK (SP); + ENTER; SAVETMPS; + SAVESTACK_POS (); + PUSHMARK (SP); XPUSHs (HeVAL (he)); sv_2mortal (sv); @@ -1469,7 +1513,9 @@ dSP; int count; - ENTER; SAVETMPS; PUSHMARK (SP); + ENTER; SAVETMPS; + SAVESTACK_POS (); + PUSHMARK (SP); XPUSHs (sv_2mortal (sv)); PUTBACK; count = call_sv (dec->json.cb_object, G_ARRAY); SPAGAIN; @@ -1546,7 +1592,8 @@ dSP; - ENTER; SAVETMPS; PUSHMARK (SP); + ENTER; SAVETMPS; + PUSHMARK (SP); EXTEND (SP, len + 2); // we re-bless the reference to get overload and other niceties right PUSHs (tag); @@ -1598,9 +1645,9 @@ { dec->cur += 4; #if JSON_SLOW - types_true = get_bool ("Types::Serialiser::true"); + bool_true = get_bool ("Types::Serialiser::true"); #endif - return newSVsv (types_true); + return newSVsv (bool_true); } else ERR ("'true' expected"); @@ -1612,9 +1659,9 @@ { dec->cur += 5; #if JSON_SLOW - types_false = get_bool ("Types::Serialiser::false"); + bool_false = get_bool ("Types::Serialiser::false"); #endif - return newSVsv (types_false); + return newSVsv (bool_false); } else ERR ("'false' expected"); @@ -1651,9 +1698,12 @@ * makes perl ignore the magic in subsequent accesses. * also make a copy of non-PV values, to get them into a clean * state (SvPV should do that, but it's buggy, see below). + * + * SvIsCOW_shared_hash works around a bug in perl (possibly 5.16), + * as reported by Reini Urban. */ /*SvGETMAGIC (string);*/ - if (SvMAGICAL (string) || !SvPOK (string)) + if (SvMAGICAL (string) || !SvPOK (string) || SvIsCOW_shared_hash (string)) string = sv_2mortal (newSVsv (string)); SvUPGRADE (string, SVt_PV); @@ -1738,7 +1788,7 @@ sv = sv_2mortal (sv); - if (!(dec.json.flags & F_ALLOW_NONREF) && !SvROK (sv)) + if (!(dec.json.flags & F_ALLOW_NONREF) && json_nonref (sv)) croak ("JSON text must be an object or array (but found number, string, true, false or null, use allow_nonref to allow this)"); return sv; @@ -1918,11 +1968,10 @@ : i >= 'A' && i <= 'F' ? i - 'A' + 10 : -1; - json_stash = gv_stashpv ("JSON::XS" , 1); - types_boolean_stash = gv_stashpv ("Types::Serialiser::Boolean", 1); - - types_true = get_bool ("Types::Serialiser::true"); - types_false = get_bool ("Types::Serialiser::false"); + json_stash = gv_stashpv ("JSON::XS" , 1); + bool_stash = gv_stashpv ("Types::Serialiser::Boolean", 1); + bool_true = get_bool ("Types::Serialiser::true"); + bool_false = get_bool ("Types::Serialiser::false"); sv_json = newSVpv ("JSON", 0); SvREADONLY_on (sv_json); @@ -1934,8 +1983,8 @@ void CLONE (...) CODE: - json_stash = 0; - types_boolean_stash = 0; + json_stash = 0; + bool_stash = 0; void new (char *klass) PPCODE: