diff mbox

[3/3] qapi: Drop support for qobject_from_jsonf("%"PRId64)

Message ID 1479874588-1969-4-git-send-email-eblake@redhat.com
State New
Headers show

Commit Message

Eric Blake Nov. 23, 2016, 4:16 a.m. UTC
The qobject_from_jsonf() function implements a pseudo-printf
language for creating a QObject; however, it is hard-coded to
only parse a subset of formats understood by printf().  In
particular, any use of a 64-bit integer works only if the
system's definition of PRId64 matches what the parser expects;
which works on glibc (%lld) and mingw (%I64d), but not on
Mac OS (%qd).  Rather than enhance the parser, we have already
converted almost all clients to use an alternative method;
convert or eliminate the remaining uses in the testsuite, and
rip out this code from the parser.

Ripping it all out means that we will now uniformly get
failures on all platforms that try to use dynamic JSON with
64-bit numbers. Ultimately, I plan for later patches to rip
out dynamic JSON altogether, but that is more invasive and
therefore not appropriate for the 2.8 release, while this
patch fixes an actual testsuite failure of check-qjson on
Mac OS.

Reported by: G 3 <programmingkidx@gmail.com>
Signed-off-by: Eric Blake <eblake@redhat.com>
---
 qobject/json-lexer.c               | 28 ----------------------------
 qobject/json-parser.c              |  5 -----
 tests/check-qjson.c                | 10 ----------
 tests/test-qobject-input-visitor.c |  5 +++--
 4 files changed, 3 insertions(+), 45 deletions(-)

Comments

Paolo Bonzini Nov. 23, 2016, 9:25 a.m. UTC | #1
> The qobject_from_jsonf() function implements a pseudo-printf
> language for creating a QObject; however, it is hard-coded to
> only parse a subset of formats understood by printf().  In
> particular, any use of a 64-bit integer works only if the
> system's definition of PRId64 matches what the parser expects;
> which works on glibc (%lld) and mingw (%I64d), but not on
> Mac OS (%qd).  Rather than enhance the parser, we have already
> converted almost all clients to use an alternative method;
> convert or eliminate the remaining uses in the testsuite, and
> rip out this code from the parser.
> 
> Ripping it all out means that we will now uniformly get
> failures on all platforms that try to use dynamic JSON with
> 64-bit numbers. Ultimately, I plan for later patches to rip
> out dynamic JSON altogether, but that is more invasive and
> therefore not appropriate for the 2.8 release, while this
> patch fixes an actual testsuite failure of check-qjson on
> Mac OS.
> 
> Reported by: G 3 <programmingkidx@gmail.com>
> Signed-off-by: Eric Blake <eblake@redhat.com>

This is throwing out the baby with the bathwater.  %lld works
just fine for long long.  Throwing away %I64d is fine though...

> @@ -964,7 +964,6 @@ static void vararg_number(void)
>      QInt *qint;
>      QFloat *qfloat;
>      int value = 0x2342;
> -    int64_t value64 = 0x2342342343LL;
>      double valuef = 2.323423423;
> 
>      obj = qobject_from_jsonf("%d", value);
> @@ -976,15 +975,6 @@ static void vararg_number(void)
> 
>      QDECREF(qint);
> 
> -    obj = qobject_from_jsonf("%" PRId64, value64);
> -    g_assert(obj != NULL);
> -    g_assert(qobject_type(obj) == QTYPE_QINT);
> -
> -    qint = qobject_to_qint(obj);
> -    g_assert(qint_get_int(qint) == value64);
> -
> -    QDECREF(qint);
> -
>      obj = qobject_from_jsonf("%f", valuef);
>      g_assert(obj != NULL);
>      g_assert(qobject_type(obj) == QTYPE_QFLOAT);

if you change the test to use %lld and long long instead of int64_t.

> diff --git a/tests/test-qobject-input-visitor.c
> b/tests/test-qobject-input-visitor.c
> index 26c5012..945404a 100644
> --- a/tests/test-qobject-input-visitor.c
> +++ b/tests/test-qobject-input-visitor.c
> @@ -83,10 +83,11 @@ static Visitor
> *visitor_input_test_init_raw(TestInputVisitorData *data,
>  static void test_visitor_in_int(TestInputVisitorData *data,
>                                  const void *unused)
>  {
> -    int64_t res = 0, value = -42;
> +    int64_t res = 0;
> +    int value = -42;
>      Visitor *v;
> 
> -    v = visitor_input_test_init(data, "%" PRId64, value);
> +    v = visitor_input_test_init(data, "%d", value);
> 
>      visit_type_int(v, NULL, &res, &error_abort);
>      g_assert_cmpint(res, ==, value);
> --
> 2.7.4
> 
> 

This part is fine I guess.

Paolo
Eric Blake Nov. 23, 2016, 10:54 a.m. UTC | #2
On 11/23/2016 03:25 AM, Paolo Bonzini wrote:
>> The qobject_from_jsonf() function implements a pseudo-printf
>> language for creating a QObject; however, it is hard-coded to
>> only parse a subset of formats understood by printf().  In
>> particular, any use of a 64-bit integer works only if the
>> system's definition of PRId64 matches what the parser expects;
>> which works on glibc (%lld) and mingw (%I64d), but not on
>> Mac OS (%qd).  Rather than enhance the parser, we have already
>> converted almost all clients to use an alternative method;
>> convert or eliminate the remaining uses in the testsuite, and
>> rip out this code from the parser.
>>
>> Ripping it all out means that we will now uniformly get
>> failures on all platforms that try to use dynamic JSON with
>> 64-bit numbers. Ultimately, I plan for later patches to rip
>> out dynamic JSON altogether, but that is more invasive and
>> therefore not appropriate for the 2.8 release, while this
>> patch fixes an actual testsuite failure of check-qjson on
>> Mac OS.
>>
>> Reported by: G 3 <programmingkidx@gmail.com>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
> 
> This is throwing out the baby with the bathwater.  %lld works
> just fine for long long.  Throwing away %I64d is fine though...

On 64-bit systems where int64_t is 'long' rather than 'long long',
PRId64 is %ld, not %lld.  And on 32-bit MacOS, PRId64 is %qd, which is
NOT covered by the existing JSON parser.  If you write:

int64_t foo;
qobject_from_jsonf("%lld", foo)

then gcc will complain on all platforms where %lld is not the right
string to match against int64_t, thanks to -Wformat.  We could instead
write:

int64_t foo;
qobject_from_jsonf("%lld", (long long) foo);

and have that work everywhere, but then you have to explicitly cast.
And our current qdict_get_int() code returns int64_t, not long long, so
it's hard to convince people to write:

long long foo;
foo = qdict_get_int(...);
qobject_from_jsonf("%lld", foo);

since 'long long' is more typing than 'int64_t'. I suppose we could do
that, but my next question is why bother.  As I've proven in these three
patches, there were VERY FEW clients that were trying to use dynamic
JSON on a 64-bit value in the first place.  Ripping out ALL 64-bit
support _proves_ that we can't mess it up in the future, vs. leaving
%lld there and getting it right for some versions of glibc but still
failing on other platforms when someone uses PRId64 instead of an
explicit %lld.

My other argument is that I _do_ intend to rip out ALL of the dynamic
JSON support, at which point we no longer have %d, let along %lld.
Until you see that followup series and decide whether it was too
invasive for 2.9, it's hard to say that we are throwing out anything
useful in this short-term fix for 2.8.  So I guess that gives me a
reason to hurry up and finish my work on that series to post it today
before I take a long holiday weekend.

>> +++ b/tests/test-qobject-input-visitor.c
>> @@ -83,10 +83,11 @@ static Visitor
>> *visitor_input_test_init_raw(TestInputVisitorData *data,
>>  static void test_visitor_in_int(TestInputVisitorData *data,
>>                                  const void *unused)
>>  {
>> -    int64_t res = 0, value = -42;
>> +    int64_t res = 0;
>> +    int value = -42;
>>      Visitor *v;
>>
>> -    v = visitor_input_test_init(data, "%" PRId64, value);
>> +    v = visitor_input_test_init(data, "%d", value);
>>
>>      visit_type_int(v, NULL, &res, &error_abort);
>>      g_assert_cmpint(res, ==, value);
>> --
>> 2.7.4
>>
>>
> 
> This part is fine I guess.

If desired, I can respin this patch to _just_ the changes under tests/,
as that is all the more that is needed to fix MacOS runtime for 2.8, and
leave the ripping out of %lld for 2.9 for the same time when I rip out
all other % support.  Personally, I found it easier to prove that
nothing was relying on 64-bit parsing by ripping it out completely, so
that glibc platforms would also flag improper uses at runtime, rather
than hoping that I had caught everything by mere grep.  But I guess that
even though 'make check' now passes (and so it appears that we no longer
have any runtime dependency on 64-bit values), going with the more
conservative approach of just fixing the two tests that relied on 64-bit
values but leaving existing support in will avoid problems on glibc and
mingw (but not MacOS) if we still managed to miss something not covered
by 'make check'.

On the bright side, mere grep shows that we really only have 3 remaining
clients of qobject_from_jsonf() in the main code body, none of which use
64-bit ints; and that the only clients of qobject_from_jsonv() are in
the testsuite; so the fact that 'make check' passes is a pretty good
indication that I did not manage to miss anything that still wants to
use dynamic JSON with a 64-bit int.
Markus Armbruster Nov. 23, 2016, 2:17 p.m. UTC | #3
Eric Blake <eblake@redhat.com> writes:

> On 11/23/2016 03:25 AM, Paolo Bonzini wrote:
>>> The qobject_from_jsonf() function implements a pseudo-printf
>>> language for creating a QObject; however, it is hard-coded to
>>> only parse a subset of formats understood by printf().  In
>>> particular, any use of a 64-bit integer works only if the
>>> system's definition of PRId64 matches what the parser expects;
>>> which works on glibc (%lld) and mingw (%I64d), but not on
>>> Mac OS (%qd).  Rather than enhance the parser, we have already
>>> converted almost all clients to use an alternative method;
>>> convert or eliminate the remaining uses in the testsuite, and
>>> rip out this code from the parser.
>>>
>>> Ripping it all out means that we will now uniformly get
>>> failures on all platforms that try to use dynamic JSON with
>>> 64-bit numbers. Ultimately, I plan for later patches to rip
>>> out dynamic JSON altogether, but that is more invasive and
>>> therefore not appropriate for the 2.8 release, while this
>>> patch fixes an actual testsuite failure of check-qjson on
>>> Mac OS.
>>>
>>> Reported by: G 3 <programmingkidx@gmail.com>
>>> Signed-off-by: Eric Blake <eblake@redhat.com>

The first two patches are bug fixes, and as such they should be
considered for 2.8.

This patch doesn't fix anything, and it might conceivably break
something.  Too late for 2.8.

>> This is throwing out the baby with the bathwater.  %lld works
>> just fine for long long.  Throwing away %I64d is fine though...
>
> On 64-bit systems where int64_t is 'long' rather than 'long long',
> PRId64 is %ld, not %lld.  And on 32-bit MacOS, PRId64 is %qd, which is
> NOT covered by the existing JSON parser.  If you write:
>
> int64_t foo;
> qobject_from_jsonf("%lld", foo)
>
> then gcc will complain on all platforms where %lld is not the right
> string to match against int64_t, thanks to -Wformat.  We could instead
> write:
>
> int64_t foo;
> qobject_from_jsonf("%lld", (long long) foo);
>
> and have that work everywhere, but then you have to explicitly cast.
> And our current qdict_get_int() code returns int64_t, not long long, so
> it's hard to convince people to write:
>
> long long foo;
> foo = qdict_get_int(...);
> qobject_from_jsonf("%lld", foo);
>
> since 'long long' is more typing than 'int64_t'. I suppose we could do
> that, but my next question is why bother.  As I've proven in these three
> patches, there were VERY FEW clients that were trying to use dynamic
> JSON on a 64-bit value in the first place.  Ripping out ALL 64-bit
> support _proves_ that we can't mess it up in the future, vs. leaving
> %lld there and getting it right for some versions of glibc but still
> failing on other platforms when someone uses PRId64 instead of an
> explicit %lld.
>
> My other argument is that I _do_ intend to rip out ALL of the dynamic
> JSON support, at which point we no longer have %d, let along %lld.
> Until you see that followup series and decide whether it was too
> invasive for 2.9, it's hard to say that we are throwing out anything
> useful in this short-term fix for 2.8.  So I guess that gives me a
> reason to hurry up and finish my work on that series to post it today
> before I take a long holiday weekend.

If we rip out _jsonf() in 2.9, then ripping out currently unused parts
of it in 2.8 during hard freeze is needless churn at a rather
inconvenient time.

If we decice not to rip it out, it may well have to be reverted.

I don't think there's a need to hurry, as this patch isn't appropriate
for 2.8 anyway, so there's no reason to quickly decide what to do with
the followup series now.

>>> +++ b/tests/test-qobject-input-visitor.c
>>> @@ -83,10 +83,11 @@ static Visitor
>>> *visitor_input_test_init_raw(TestInputVisitorData *data,
>>>  static void test_visitor_in_int(TestInputVisitorData *data,
>>>                                  const void *unused)
>>>  {
>>> -    int64_t res = 0, value = -42;
>>> +    int64_t res = 0;
>>> +    int value = -42;
>>>      Visitor *v;
>>>
>>> -    v = visitor_input_test_init(data, "%" PRId64, value);
>>> +    v = visitor_input_test_init(data, "%d", value);
>>>
>>>      visit_type_int(v, NULL, &res, &error_abort);
>>>      g_assert_cmpint(res, ==, value);
>>> --
>>> 2.7.4
>>>
>>>
>> 
>> This part is fine I guess.
>
> If desired, I can respin this patch to _just_ the changes under tests/,
> as that is all the more that is needed to fix MacOS runtime for 2.8, and
> leave the ripping out of %lld for 2.9 for the same time when I rip out
> all other % support.  Personally, I found it easier to prove that
> nothing was relying on 64-bit parsing by ripping it out completely, so
> that glibc platforms would also flag improper uses at runtime, rather
> than hoping that I had caught everything by mere grep.  But I guess that
> even though 'make check' now passes (and so it appears that we no longer
> have any runtime dependency on 64-bit values), going with the more
> conservative approach of just fixing the two tests that relied on 64-bit
> values but leaving existing support in will avoid problems on glibc and
> mingw (but not MacOS) if we still managed to miss something not covered
> by 'make check'.

It's a valuable sanity check, but that doesn't mean we should commit it
for 2.8.

> On the bright side, mere grep shows that we really only have 3 remaining
> clients of qobject_from_jsonf() in the main code body, none of which use
> 64-bit ints; and that the only clients of qobject_from_jsonv() are in
> the testsuite; so the fact that 'make check' passes is a pretty good
> indication that I did not manage to miss anything that still wants to
> use dynamic JSON with a 64-bit int.

I'd be willing to buy that, but not this late in hard freeze.
Eric Blake Nov. 23, 2016, 2:24 p.m. UTC | #4
On 11/23/2016 08:17 AM, Markus Armbruster wrote:

> 
> The first two patches are bug fixes, and as such they should be
> considered for 2.8.
> 
> This patch doesn't fix anything, and it might conceivably break
> something.  Too late for 2.8.

Ah, but it DOES fix check-qjson on Mac OS.

As mentioned to Paolo, I'm splitting this into two parts for the v2
series (the first part to fix testsuite failures on Mac OS which is
still 2.8 material, the second to rip out %I64d which becomes more of
2.9 material).

>> My other argument is that I _do_ intend to rip out ALL of the dynamic
>> JSON support, at which point we no longer have %d, let along %lld.
>> Until you see that followup series and decide whether it was too
>> invasive for 2.9, it's hard to say that we are throwing out anything
>> useful in this short-term fix for 2.8.  So I guess that gives me a
>> reason to hurry up and finish my work on that series to post it today
>> before I take a long holiday weekend.
> 
> If we rip out _jsonf() in 2.9, then ripping out currently unused parts
> of it in 2.8 during hard freeze is needless churn at a rather
> inconvenient time.
> 
> If we decice not to rip it out, it may well have to be reverted.
> 
> I don't think there's a need to hurry, as this patch isn't appropriate
> for 2.8 anyway, so there's no reason to quickly decide what to do with
> the followup series now.

Fair enough.

v2 coming soon.
Markus Armbruster Nov. 23, 2016, 4:56 p.m. UTC | #5
Eric Blake <eblake@redhat.com> writes:

> On 11/23/2016 08:17 AM, Markus Armbruster wrote:
>
>> 
>> The first two patches are bug fixes, and as such they should be
>> considered for 2.8.
>> 
>> This patch doesn't fix anything, and it might conceivably break
>> something.  Too late for 2.8.
>
> Ah, but it DOES fix check-qjson on Mac OS.

PATCH 1+2 do, don't they?

> As mentioned to Paolo, I'm splitting this into two parts for the v2
> series (the first part to fix testsuite failures on Mac OS which is
> still 2.8 material, the second to rip out %I64d which becomes more of
> 2.9 material).
>
>>> My other argument is that I _do_ intend to rip out ALL of the dynamic
>>> JSON support, at which point we no longer have %d, let along %lld.
>>> Until you see that followup series and decide whether it was too
>>> invasive for 2.9, it's hard to say that we are throwing out anything
>>> useful in this short-term fix for 2.8.  So I guess that gives me a
>>> reason to hurry up and finish my work on that series to post it today
>>> before I take a long holiday weekend.
>> 
>> If we rip out _jsonf() in 2.9, then ripping out currently unused parts
>> of it in 2.8 during hard freeze is needless churn at a rather
>> inconvenient time.
>> 
>> If we decice not to rip it out, it may well have to be reverted.
>> 
>> I don't think there's a need to hurry, as this patch isn't appropriate
>> for 2.8 anyway, so there's no reason to quickly decide what to do with
>> the followup series now.
>
> Fair enough.
>
> v2 coming soon.

Thanks!
Eric Blake Nov. 23, 2016, 4:59 p.m. UTC | #6
On 11/23/2016 10:56 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> On 11/23/2016 08:17 AM, Markus Armbruster wrote:
>>
>>>
>>> The first two patches are bug fixes, and as such they should be
>>> considered for 2.8.
>>>
>>> This patch doesn't fix anything, and it might conceivably break
>>> something.  Too late for 2.8.
>>
>> Ah, but it DOES fix check-qjson on Mac OS.
> 
> PATCH 1+2 do, don't they?

No, patch 1 fixes emission of QMP events, patch 2 fixes check-qga. Also
reported broken by G 3 was check-qjson, and my audit revealed that
test-qobject-input-visitor is also affected.
diff mbox

Patch

diff --git a/qobject/json-lexer.c b/qobject/json-lexer.c
index af4a75e..b175da7 100644
--- a/qobject/json-lexer.c
+++ b/qobject/json-lexer.c
@@ -59,11 +59,6 @@  enum json_lexer_state {
     IN_NEG_NONZERO_NUMBER,
     IN_KEYWORD,
     IN_ESCAPE,
-    IN_ESCAPE_L,
-    IN_ESCAPE_LL,
-    IN_ESCAPE_I,
-    IN_ESCAPE_I6,
-    IN_ESCAPE_I64,
     IN_WHITESPACE,
     IN_START,
 };
@@ -225,35 +220,12 @@  static const uint8_t json_lexer[][256] =  {
     },

     /* escape */
-    [IN_ESCAPE_LL] = {
-        ['d'] = JSON_ESCAPE,
-    },
-
-    [IN_ESCAPE_L] = {
-        ['d'] = JSON_ESCAPE,
-        ['l'] = IN_ESCAPE_LL,
-    },
-
-    [IN_ESCAPE_I64] = {
-        ['d'] = JSON_ESCAPE,
-    },
-
-    [IN_ESCAPE_I6] = {
-        ['4'] = IN_ESCAPE_I64,
-    },
-
-    [IN_ESCAPE_I] = {
-        ['6'] = IN_ESCAPE_I6,
-    },
-
     [IN_ESCAPE] = {
         ['d'] = JSON_ESCAPE,
         ['i'] = JSON_ESCAPE,
         ['p'] = JSON_ESCAPE,
         ['s'] = JSON_ESCAPE,
         ['f'] = JSON_ESCAPE,
-        ['l'] = IN_ESCAPE_L,
-        ['I'] = IN_ESCAPE_I,
     },

     /* top level rule */
diff --git a/qobject/json-parser.c b/qobject/json-parser.c
index c18e48a..492d141 100644
--- a/qobject/json-parser.c
+++ b/qobject/json-parser.c
@@ -467,11 +467,6 @@  static QObject *parse_escape(JSONParserContext *ctxt, va_list *ap)
         return QOBJECT(qbool_from_bool(va_arg(*ap, int)));
     } else if (!strcmp(token->str, "%d")) {
         return QOBJECT(qint_from_int(va_arg(*ap, int)));
-    } else if (!strcmp(token->str, "%ld")) {
-        return QOBJECT(qint_from_int(va_arg(*ap, long)));
-    } else if (!strcmp(token->str, "%lld") ||
-               !strcmp(token->str, "%I64d")) {
-        return QOBJECT(qint_from_int(va_arg(*ap, long long)));
     } else if (!strcmp(token->str, "%s")) {
         return QOBJECT(qstring_from_str(va_arg(*ap, const char *)));
     } else if (!strcmp(token->str, "%f")) {
diff --git a/tests/check-qjson.c b/tests/check-qjson.c
index 8595574..7149a92 100644
--- a/tests/check-qjson.c
+++ b/tests/check-qjson.c
@@ -964,7 +964,6 @@  static void vararg_number(void)
     QInt *qint;
     QFloat *qfloat;
     int value = 0x2342;
-    int64_t value64 = 0x2342342343LL;
     double valuef = 2.323423423;

     obj = qobject_from_jsonf("%d", value);
@@ -976,15 +975,6 @@  static void vararg_number(void)

     QDECREF(qint);

-    obj = qobject_from_jsonf("%" PRId64, value64);
-    g_assert(obj != NULL);
-    g_assert(qobject_type(obj) == QTYPE_QINT);
-
-    qint = qobject_to_qint(obj);
-    g_assert(qint_get_int(qint) == value64);
-
-    QDECREF(qint);
-
     obj = qobject_from_jsonf("%f", valuef);
     g_assert(obj != NULL);
     g_assert(qobject_type(obj) == QTYPE_QFLOAT);
diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c
index 26c5012..945404a 100644
--- a/tests/test-qobject-input-visitor.c
+++ b/tests/test-qobject-input-visitor.c
@@ -83,10 +83,11 @@  static Visitor *visitor_input_test_init_raw(TestInputVisitorData *data,
 static void test_visitor_in_int(TestInputVisitorData *data,
                                 const void *unused)
 {
-    int64_t res = 0, value = -42;
+    int64_t res = 0;
+    int value = -42;
     Visitor *v;

-    v = visitor_input_test_init(data, "%" PRId64, value);
+    v = visitor_input_test_init(data, "%d", value);

     visit_type_int(v, NULL, &res, &error_abort);
     g_assert_cmpint(res, ==, value);