boolean encoding is inconsistent
karenetheridge opened this issue · comments
perl -MCpanel::JSON::XS -wle'print Cpanel::JSON::XS->new->allow_nonref(1)->encode(!0)'
true
perl -MCpanel::JSON::XS -wle'print Cpanel::JSON::XS->new->allow_nonref(1)->encode([!0])'
[1]
This is with version 4.19.
13:37 < tinita> I think the problem is with !0 itself. as soon as you save it in an arrayref, its special status gets lost
13:38 < tinita> perl -wE'use Devel::Peek;Dump !0;Dump [!0]'
13:38 <@kraih> lol
so I guess this means it's not actually fixable. I hope I'm wrong though.
SV_yes and SV_no should not be interpreted as JSON booleans (unless via Cpanel::JSON::XS::Type with the boolean type). The user cannot easily tell if something they have is one of these special constants, they might accidentally encode one of them when trying to encode the number 1 (for example), and as demonstrated here they can de-special themselves. They are simply not reliably distinct from 0 and 1 in implementation or interface.
as demonstrated here they can de-special themselves
I don't agree with this part, as that's the same problem we have with the difference between the string "6"
and the number 6
, depending on how the value has been used be earlier code.
It is similar, and it's not good that that happens either. But in this case, we can avoid the problem and everything still works intuitively.
Not really, !0 is always SV_YES, even if as arrayref.
perl -Dx -e'print [!0]' => SV = SV_YES
perl -Dx -e'print !1' => SV = SV_NO
It's the anonlist in perl before the XS encode which changes the [yes] to [1].
With -Dts
(-e:1) pushmark
=> ** \PVMG(""\0) *
(-e:1) const(SV_YES)
=> ** \PVMG(""\0) * SV_YES
(-e:1) anonlist
=> ** \PVMG(""\0) \AV()
(-e:1) method_named
=> ** \PVMG(""\0) \AV() CV(encode)
(-e:1) entersub
=> * PV("[1]"\0) [UTF8 "[1]"]
So it's av_make
in anonlist which turns the SV_YES into 1, esp. sv_setsv_flags
does not support the special immortals, like YES, NO, ... Upstream bug, sorry
BTW: Trivially fixable there, but it's already too late.
My concern is even if that were reliable, it's very common to already be using these values and expecting a 1 on encode, because it's generally indistinguishable from 1 in perl code. The boolean object is the only consistent and obvious way to represent something to be encoded to a boolean without Cpanel::JSON::XS::Type, and people are already used to it.