diff --git a/tests/conformance_upb.c b/tests/conformance_upb.c index 8285242d3e..1d87060095 100644 --- a/tests/conformance_upb.c +++ b/tests/conformance_upb.c @@ -15,6 +15,7 @@ #include "upb/decode.h" #include "upb/encode.h" #include "upb/reflection.h" +#include "upb/json_decode.h" #include "upb/json_encode.h" #include "upb/text_encode.h" @@ -85,9 +86,11 @@ void serialize_text(const upb_msg *msg, const upb_msgdef *m, const ctx *c) { size_t len2; int opts = 0; char *data; + if (!conformance_ConformanceRequest_print_unknown_fields(c->request)) { opts |= UPB_TXTENC_SKIPUNKNOWN; } + len = upb_text_encode(msg, m, c->symtab, opts, NULL, 0); data = upb_arena_malloc(c->arena, len + 1); len2 = upb_text_encode(msg, m, c->symtab, opts, data, len + 1); @@ -96,6 +99,33 @@ void serialize_text(const upb_msg *msg, const upb_msgdef *m, const ctx *c) { c->response, upb_strview_make(data, len)); } +bool parse_json(upb_msg *msg, const upb_msgdef *m, const ctx* c) { + upb_strview json = + conformance_ConformanceRequest_json_payload(c->request); + upb_status status; + int opts = 0; + + if (conformance_ConformanceRequest_test_category(c->request) == + conformance_JSON_IGNORE_UNKNOWN_PARSING_TEST) { + opts |= UPB_JSONDEC_IGNOREUNKNOWN; + } + + upb_status_clear(&status); + if (upb_json_decode(json.data, json.size, msg, m, c->symtab, opts, c->arena, + &status)) { + return true; + } else { + const char *inerr = upb_status_errmsg(&status); + size_t len = strlen(inerr); + char *err = upb_arena_malloc(c->arena, len + 1); + memcpy(err, inerr, strlen(inerr)); + err[len] = '\0'; + conformance_ConformanceResponse_set_parse_error(c->response, + upb_strview_makez(err)); + return false; + } +} + void serialize_json(const upb_msg *msg, const upb_msgdef *m, const ctx *c) { size_t len; size_t len2; @@ -104,16 +134,16 @@ void serialize_json(const upb_msg *msg, const upb_msgdef *m, const ctx *c) { upb_status status; upb_status_clear(&status); - if (!conformance_ConformanceRequest_print_unknown_fields(c->request)) { - opts |= UPB_TXTENC_SKIPUNKNOWN; - } - len = upb_json_encode(msg, m, c->symtab, opts, NULL, 0, &status); if (len == -1) { - static const char msg[] = "Error serializing."; - conformance_ConformanceResponse_set_serialize_error( - c->response, upb_strview_make(msg, strlen(msg))); + const char *inerr = upb_status_errmsg(&status); + size_t len = strlen(inerr); + char *err = upb_arena_malloc(c->arena, len + 1); + memcpy(err, inerr, strlen(inerr)); + err[len] = '\0'; + conformance_ConformanceResponse_set_serialize_error(c->response, + upb_strview_makez(err)); return; } @@ -128,6 +158,8 @@ bool parse_input(upb_msg *msg, const upb_msgdef *m, const ctx* c) { switch (conformance_ConformanceRequest_payload_case(c->request)) { case conformance_ConformanceRequest_payload_protobuf_payload: return parse_proto(msg, m, c); + case conformance_ConformanceRequest_payload_json_payload: + return parse_json(msg, m, c); case conformance_ConformanceRequest_payload_NOT_SET: fprintf(stderr, "conformance_upb: Request didn't have payload.\n"); return false; diff --git a/upb/def.c b/upb/def.c index 96307b0477..fb65689525 100644 --- a/upb/def.c +++ b/upb/def.c @@ -468,6 +468,10 @@ const char *upb_fielddef_name(const upb_fielddef *f) { return shortdefname(f->full_name); } +const char *upb_fielddef_jsonname(const upb_fielddef *f) { + return f->json_name; +} + uint32_t upb_fielddef_selectorbase(const upb_fielddef *f) { return f->selector_base; } @@ -1382,7 +1386,7 @@ static bool create_fielddef( return false; } - if (!upb_inttable_insert2(&m->itof, field_number, v, alloc)) { + if (upb_inttable_lookup(&m->itof, field_number, NULL)) { upb_status_seterrf(ctx->status, "duplicate field number (%u)", field_number); return false; diff --git a/upb/json_decode.c b/upb/json_decode.c index 9248151042..e879231f9f 100644 --- a/upb/json_decode.c +++ b/upb/json_decode.c @@ -2,6 +2,8 @@ #include "upb/json_decode.h" #include +#include +#include #include #include #include @@ -44,7 +46,7 @@ UPB_NORETURN static void jsondec_err(jsondec *d, const char *msg) { longjmp(d->err, 1); } -static void jsondec_errf(jsondec *d, const char *fmt, ...) { +UPB_NORETURN static void jsondec_errf(jsondec *d, const char *fmt, ...) { va_list argp; va_start(argp, fmt); upb_status_vseterrf(d->status, fmt, argp); @@ -78,9 +80,11 @@ static bool jsondec_tryparsech(jsondec *d, char ch) { } static void jsondec_parselit(jsondec *d, const char *lit) { - if (d->end - d->ptr < strlen(lit) || memcmp(d->ptr, lit, strlen(lit)) != 0) { + size_t len = strlen(lit); + if (d->end - d->ptr < len || memcmp(d->ptr, lit, len) != 0) { jsondec_errf(d, "Expected: '%s'", lit); } + d->ptr += len; } static void jsondec_wsch(jsondec *d, char ch) { @@ -126,7 +130,7 @@ static int jsondec_rawpeek(jsondec *d) { case 'n': return JD_NULL; default: - jsondec_err(d, "Unexpected character"); + jsondec_errf(d, "Unexpected character: '%c'", *d->ptr); } } @@ -149,6 +153,7 @@ static void jsondec_push(jsondec *d) { if (--d->depth < 0) { jsondec_err(d, "Recursion limit exceeded"); } + d->is_first = true; } static bool jsondec_seqnext(jsondec *d, char end_ch) { @@ -252,7 +257,15 @@ parse: /* Currently the min/max-val conformance tests fail if we check this. Does * this mean the conformance tests are wrong or strtod() is wrong, or * something else? Investigate further. */ - /* CHK(errno == 0); */ + /* + if (errno == ERANGE) { + jsondec_err(d, "Number out of range"); + } + */ + + if (val > DBL_MAX || val < -DBL_MAX) { + jsondec_err(d, "Number out of range"); + } return val; } @@ -351,10 +364,11 @@ static size_t jsondec_unicode(jsondec *d, char* out) { } static void jsondec_resize(jsondec *d, char **buf, char **end, char **buf_end) { - size_t size = 8; + size_t oldsize = *buf_end - *buf; size_t len = *end - *buf; - while (size < *buf_end - *buf) size++; - *buf = upb_arena_realloc(d->arena, *buf, *buf_end - *buf, size); + size_t size = UPB_MAX(8, 2 * oldsize); + + *buf = upb_arena_realloc(d->arena, *buf, len, size); *end = *buf + len; *buf_end = *buf + size; } @@ -371,18 +385,21 @@ static upb_strview jsondec_string(jsondec *d) { } while (d->ptr < d->end) { + char ch = *d->ptr++; + if (end == buf_end) { jsondec_resize(d, &buf, &end, &buf_end); } - switch (*d->ptr) { + switch (ch) { case '"': { upb_strview ret = {buf, end - buf}; return ret; } case '\\': - if (++d->ptr == d->end) goto eof; + if (d->ptr == d->end) goto eof; if (*d->ptr == 'u') { + d->ptr++; if (buf_end - end < 4) { // Allow space for maximum-sized code point (4 bytes). jsondec_resize(d, &buf, &end, &buf_end); @@ -391,11 +408,12 @@ static upb_strview jsondec_string(jsondec *d) { } else { *end++ = jsondec_escape(d); } + break; default: if ((unsigned char)*d->ptr < 0x20) { jsondec_err(d, "Invalid char in JSON string"); } - *end++ = *d->ptr; + *end++ = ch; break; } } @@ -429,7 +447,7 @@ static void jsondec_skipval(jsondec *d) { jsondec_false(d); break; case JD_NULL: - jsondec_number(d); + jsondec_null(d); break; case JD_STRING: jsondec_string(d); @@ -570,6 +588,7 @@ static const char *jsondec_buftouint64(jsondec *d, const char *ptr, } u64 *= 10; u64 += ch; + ptr++; } *val = u64; @@ -626,8 +645,9 @@ static upb_msgval jsondec_int(jsondec *d, const upb_fielddef *f) { jsondec_err(d, "JSON number is out of range."); } val.int64_val = dbl; /* must be guarded, overflow here is UB */ - if (val.uint64_val != dbl) { - jsondec_err(d, "JSON number was not integral."); + if (val.int64_val != dbl) { + jsondec_errf(d, "JSON number was not integral (%d != %" PRId64 ")", dbl, + val.int64_val); } break; } @@ -640,7 +660,7 @@ static upb_msgval jsondec_int(jsondec *d, const upb_fielddef *f) { jsondec_err(d, "Expected number or string"); } - if (upb_fielddef_type(f) == UPB_TYPE_UINT32) { + if (upb_fielddef_type(f) == UPB_TYPE_INT32) { if (val.int64_val > INT32_MAX || val.int64_val < INT32_MIN) { jsondec_err(d, "Integer out of range."); } @@ -662,7 +682,8 @@ static upb_msgval jsondec_uint(jsondec *d, const upb_fielddef *f) { } val.uint64_val = dbl; /* must be guarded, overflow here is UB */ if (val.uint64_val != dbl) { - jsondec_err(d, "JSON number was not integral."); + jsondec_errf(d, "JSON number was not integral (%d != %" PRIu64 ")", dbl, + val.uint64_val); } break; } @@ -703,6 +724,7 @@ static upb_msgval jsondec_double(jsondec *d, const upb_fielddef *f) { } else if (jsondec_streql(str, "-Infinity")) { val.double_val = -UPB_INFINITY; } else { + val.double_val = strtod(str.data, NULL); } break; default: @@ -710,6 +732,10 @@ static upb_msgval jsondec_double(jsondec *d, const upb_fielddef *f) { } if (upb_fielddef_type(f) == UPB_TYPE_FLOAT) { + if (val.double_val != UPB_INFINITY && val.double_val != -UPB_INFINITY && + (val.double_val > FLT_MAX || val.double_val < -FLT_MAX)) { + jsondec_err(d, "Float out of range"); + } val.float_val = val.double_val; } @@ -758,9 +784,11 @@ static upb_msgval jsondec_bool(jsondec *d, const upb_fielddef *f) { switch (jsondec_peek(d)) { case JD_TRUE: val.bool_val = true; + jsondec_true(d); break; case JD_FALSE: val.bool_val = false; + jsondec_false(d); break; default: jsondec_err(d, "Expected true or false"); @@ -897,14 +925,16 @@ static int jsondec_tsdigits(jsondec *d, const char **ptr, size_t digits, uint64_t val; const char *p = *ptr; const char *end = p + digits; + size_t after_len = after ? strlen(after) : 0; + assert(digits <= 9); /* int can't overflow. */ if (jsondec_buftouint64(d, p, end, &val) != end || - (after && memcmp(end, after, strlen(after)) != 0)) { + (after_len && memcmp(end, after, after_len) != 0)) { jsondec_err(d, "Malformed timestamp"); } - *ptr = end + strlen(after); + *ptr = end + after_len; return val; } @@ -929,7 +959,7 @@ static int jsondec_nanos(jsondec *d, const char **ptr, const char *end) { // jsondec_epochdays(1970, 1, 1) == 1970-01-01 == 0 static int jsondec_epochdays(int y, int m, int d) { unsigned year_base = 4800; /* Before minimum year, divisible by 100 & 400 */ - unsigned epoch = 719528; /* Days between year_base and 1970 (Unix epoch) */ + unsigned epoch = 2472632; /* Days between year_base and 1970 (Unix epoch) */ unsigned carry = (unsigned)m - 3 > m; unsigned m_adj = m - 3 + (carry ? 12 : 0); /* Month, counting from March */ unsigned y_adj = y + year_base - carry; /* Year, positive and March-based */ @@ -941,7 +971,7 @@ static int jsondec_epochdays(int y, int m, int d) { } static int64_t jsondec_unixtime(int y, int m, int d, int h, int min, int s) { - return jsondec_epochdays(y, m, d) * 86400 + h * 3600 + min * 60 + s; + return (int64_t)jsondec_epochdays(y, m, d) * 86400 + h * 3600 + min * 60 + s; } static void jsondec_timestamp(jsondec *d, upb_msg *msg, const upb_msgdef *m) { @@ -1096,12 +1126,13 @@ static void jsondec_wellknownvalue(jsondec *d, upb_msg *msg, f = upb_msgdef_itof(m, 4); jsondec_true(d); break; - /* Note: these cases return, because upb_msg_mutable() is enough. */ case JD_NULL: /* NullValue null_value = 1; */ + val.int32_val = 0; + f = upb_msgdef_itof(m, 1); jsondec_null(d); - upb_msg_mutable(msg, upb_msgdef_itof(m, 1), d->arena); - return; + break; + /* Note: these cases return, because upb_msg_mutable() is enough. */ case JD_OBJECT: { /* Struct struct_value = 5; */ f = upb_msgdef_itof(m, 5); @@ -1133,6 +1164,7 @@ static upb_strview jsondec_mask(jsondec *d, const char *buf, const char *end) { ret.size = end - ptr; while (ptr < end) { ret.size += (*ptr >= 'A' && *ptr <= 'Z'); + ptr++; } out = upb_arena_malloc(d->arena, ret.size); @@ -1239,7 +1271,10 @@ static void jsondec_any(jsondec *d, upb_msg *msg, const upb_msgdef *m) { jsondec_entrysep(d); if (jsondec_streql(name, "@type")) { any_m = jsondec_typeurl(d, msg, m); - if (pre_type_data) pre_type_end = start; + if (pre_type_data) { + pre_type_end = start; + while (*pre_type_end != ',') pre_type_end--; + } } else { if (!pre_type_data) pre_type_data = start; jsondec_skipval(d); @@ -1253,10 +1288,14 @@ static void jsondec_any(jsondec *d, upb_msg *msg, const upb_msgdef *m) { any_msg = upb_msg_new(any_m, d->arena); if (pre_type_data) { + size_t len = pre_type_end - pre_type_data + 1; + char *tmp = upb_arena_malloc(d->arena, len); + memcpy(tmp, pre_type_data, len - 1); + tmp[len - 1] = '}'; const char *saved_ptr = d->ptr; const char *saved_end = d->end; - d->ptr = pre_type_data; - d->end = pre_type_end; + d->ptr = tmp; + d->end = tmp + len; d->is_first = true; while (jsondec_objnext(d)) { jsondec_anyfield(d, any_msg, any_m); @@ -1276,6 +1315,12 @@ static void jsondec_any(jsondec *d, upb_msg *msg, const upb_msgdef *m) { upb_msg_set(msg, value_f, encoded, d->arena); } +static void jsondec_wrapper(jsondec *d, upb_msg *msg, const upb_msgdef *m) { + const upb_fielddef *value_f = upb_msgdef_itof(m, 1); + upb_msgval val = jsondec_value(d, value_f); + upb_msg_set(msg, value_f, val, d->arena); +} + static void jsondec_wellknown(jsondec *d, upb_msg *msg, const upb_msgdef *m) { switch (upb_msgdef_wellknowntype(m)) { case UPB_WELLKNOWN_ANY: @@ -1308,7 +1353,7 @@ static void jsondec_wellknown(jsondec *d, upb_msg *msg, const upb_msgdef *m) { case UPB_WELLKNOWN_STRINGVALUE: case UPB_WELLKNOWN_BYTESVALUE: case UPB_WELLKNOWN_BOOLVALUE: - jsondec_value(d, upb_msgdef_itof(m, 1)); + jsondec_wrapper(d, msg, m); break; default: UPB_UNREACHABLE(); diff --git a/upb/json_encode.c b/upb/json_encode.c index 0069e4a317..caa0c8a234 100644 --- a/upb/json_encode.c +++ b/upb/json_encode.c @@ -29,13 +29,23 @@ static void jsonenc_msg(jsonenc *e, const upb_msg *msg, const upb_msgdef *m); static void jsonenc_scalar(jsonenc *e, upb_msgval val, const upb_fielddef *f); static void jsonenc_msgfield(jsonenc *e, const upb_msg *msg, const upb_msgdef *m); +static void jsonenc_msgfields(jsonenc *e, const upb_msg *msg, + const upb_msgdef *m); static void jsonenc_value(jsonenc *e, const upb_msg *msg, const upb_msgdef *m); -static void jsonenc_err(jsonenc *e, const char *msg) { +UPB_NORETURN static void jsonenc_err(jsonenc *e, const char *msg) { upb_status_seterrmsg(e->status, msg); longjmp(e->err, 1); } +static upb_arena *jsonenc_arena(jsonenc *e) { + /* Create lazily, since it's only needed for Any */ + if (!e->arena) { + e->arena = upb_arena_new(); + } + return e->arena; +} + static void jsonenc_putbytes(jsonenc *e, const void *data, size_t len) { size_t have = e->end - e->ptr; if (UPB_LIKELY(have >= len)) { @@ -70,19 +80,19 @@ static void jsonenc_printf(jsonenc *e, const char *fmt, ...) { } static void jsonenc_nanos(jsonenc *e, int32_t nanos) { - const char zeros[3] = "000"; + int digits = 9; if (nanos == 0) return; if (nanos < 0 || nanos >= 1000000000) { jsonenc_err(e, "error formatting timestamp as JSON: invalid nanos"); } - jsonenc_printf(e, "%09" PRId32, nanos); - - /* Remove trailing zeros, 3 at a time. */ - while ((e->ptr - e->buf) >= 3 && memcmp(e->ptr, zeros, 3) == 0) { - e->ptr -= 3; + while (nanos % 1000 == 0) { + nanos /= 1000; + digits -= 3; } + + jsonenc_printf(e, ".%0.*" PRId32, digits, nanos); } static void jsonenc_timestamp(jsonenc *e, const upb_msg *msg, @@ -107,7 +117,7 @@ static void jsonenc_timestamp(jsonenc *e, const upb_msg *msg, * Fliegel, H. F., and Van Flandern, T. C., "A Machine Algorithm for * Processing Calendar Dates," Communications of the Association of * Computing Machines, vol. 11 (1968), p. 657. */ - L = (seconds / 86400) + 2440588; + L = (seconds / 86400) + 68569 + 2440588; N = 4 * L / 146097; L = L - (146097 * N + 3) / 4; I = 4000 * (L + 1) / 1461001; @@ -138,6 +148,10 @@ static void jsonenc_duration(jsonenc *e, const upb_msg *msg, const upb_msgdef *m jsonenc_err(e, "bad duration"); } + if (nanos < 0) { + nanos = -nanos; + } + jsonenc_printf(e, "\"%" PRId64, seconds); jsonenc_nanos(e, nanos); jsonenc_putstr(e, "s\""); @@ -158,8 +172,8 @@ static void jsonenc_bytes(jsonenc *e, upb_strview str) { /* This is the regular base64, not the "web-safe" version. */ static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - const char *ptr = str.data; - const char *end = ptr + str.size; + const unsigned char *ptr = (unsigned char*)str.data; + const unsigned char *end = ptr + str.size; char buf[4]; jsonenc_putstr(e, "\""); @@ -182,6 +196,7 @@ static void jsonenc_bytes(jsonenc *e, upb_strview str) { jsonenc_putbytes(e, buf, 4); break; case 1: + fprintf(stderr, "Base64 encode: %d\n", (int)ptr[0]); buf[0] = base64[ptr[0] >> 2]; buf[1] = base64[((ptr[0] & 0x3) << 4)]; buf[2] = '='; @@ -212,10 +227,10 @@ static void jsonenc_stringbody(jsonenc *e, upb_strview str) { jsonenc_putstr(e, "\\\""); break; case '\f': - jsonenc_putstr(e, "\f'"); + jsonenc_putstr(e, "\\f"); break; case '\b': - jsonenc_putstr(e, "\b'"); + jsonenc_putstr(e, "\\b"); break; case '\\': jsonenc_putstr(e, "\\\\"); @@ -255,7 +270,7 @@ static void jsonenc_double(jsonenc *e, const char *fmt, double val) { static void jsonenc_wrapper(jsonenc *e, const upb_msg *msg, const upb_msgdef *m) { const upb_fielddef *val_f = upb_msgdef_itof(m, 1); - upb_msgval val = upb_msg_get(m, val_f); + upb_msgval val = upb_msg_get(msg, val_f); jsonenc_scalar(e, val, val_f); } @@ -263,13 +278,14 @@ static const upb_msgdef *jsonenc_getanymsg(jsonenc *e, upb_strview type_url) { /* Find last '/', if any. */ const char *end = type_url.data + type_url.size; const char *ptr = end; + const upb_msgdef *ret; - if (!e->ext_pool || type_url.size == 0) return NULL; + if (!e->ext_pool || type_url.size == 0) goto badurl; while (true) { if (--ptr == type_url.data) { /* Type URL must contain at least one '/', with host before. */ - return NULL; + goto badurl; } if (*ptr == '/') { ptr++; @@ -277,19 +293,29 @@ static const upb_msgdef *jsonenc_getanymsg(jsonenc *e, upb_strview type_url) { } } - return upb_symtab_lookupmsg2(e->ext_pool, ptr, end - ptr); + ret = upb_symtab_lookupmsg2(e->ext_pool, ptr, end - ptr); + + if (!ret) { + jsonenc_err(e, "Couldn't find Any type"); + } + + return ret; + +badurl: + jsonenc_err(e, "Bad type URL"); } static void jsonenc_any(jsonenc *e, const upb_msg *msg, const upb_msgdef *m) { const upb_fielddef *type_url_f = upb_msgdef_itof(m, 1); - const upb_fielddef *value_f = upb_msgdef_itof(m, 1); + const upb_fielddef *value_f = upb_msgdef_itof(m, 2); upb_strview type_url = upb_msg_get(msg, type_url_f).str_val; upb_strview value = upb_msg_get(msg, value_f).str_val; const upb_msgdef *any_m = jsonenc_getanymsg(e, type_url); const upb_msglayout *any_layout = upb_msgdef_layout(any_m); - upb_msg *any = upb_msg_new(any_m, e->arena); + upb_arena *arena = jsonenc_arena(e); + upb_msg *any = upb_msg_new(any_m, arena); - if (!upb_decode(value.data, value.size, any, any_layout, e->arena)) { + if (!upb_decode(value.data, value.size, any, any_layout, arena)) { jsonenc_err(e, "Error decoding message in Any"); } @@ -297,9 +323,9 @@ static void jsonenc_any(jsonenc *e, const upb_msg *msg, const upb_msgdef *m) { jsonenc_string(e, type_url); jsonenc_putstr(e, ", "); - if (upb_msgdef_wellknowntype(m) == UPB_WELLKNOWN_UNSPECIFIED) { + if (upb_msgdef_wellknowntype(any_m) == UPB_WELLKNOWN_UNSPECIFIED) { /* Regular messages: {"@type": "...", "foo": 1, "bar": 2} */ - jsonenc_msg(e, any, any_m); + jsonenc_msgfields(e, any, any_m); } else { /* Well-known type: {"@type": "...", "value": } */ jsonenc_putstr(e, "value: "); @@ -468,7 +494,7 @@ static void jsonenc_msgfield(jsonenc *e, const upb_msg *msg, jsonenc_listvalue(e, msg, m); break; case UPB_WELLKNOWN_STRUCT: - jsonenc_listvalue(e, msg, m); + jsonenc_struct(e, msg, m); break; } } @@ -532,6 +558,7 @@ static void jsonenc_mapkey(jsonenc *e, upb_msgval val, const upb_fielddef *f) { break; case UPB_TYPE_STRING: jsonenc_stringbody(e, val.str_val); + break; default: UPB_UNREACHABLE(); } @@ -595,13 +622,12 @@ static void jsonenc_fieldval(jsonenc *e, const upb_fielddef *f, } } -static void jsonenc_msg(jsonenc *e, const upb_msg *msg, const upb_msgdef *m) { +static void jsonenc_msgfields(jsonenc *e, const upb_msg *msg, + const upb_msgdef *m) { upb_msgval val; const upb_fielddef *f; bool first = true; - jsonenc_putstr(e, "{"); - if (e->options & UPB_JSONENC_EMITDEFAULTS) { /* Iterate over all fields. */ upb_msg_field_iter i; @@ -617,7 +643,11 @@ static void jsonenc_msg(jsonenc *e, const upb_msg *msg, const upb_msgdef *m) { jsonenc_fieldval(e, f, val, &first); } } +} +static void jsonenc_msg(jsonenc *e, const upb_msg *msg, const upb_msgdef *m) { + jsonenc_putstr(e, "{"); + jsonenc_msgfields(e, msg, m); jsonenc_putstr(e, "}"); } @@ -644,9 +674,11 @@ size_t upb_json_encode(const upb_msg *msg, const upb_msgdef *m, e.options = options; e.ext_pool = ext_pool; e.status = status; + e.arena = NULL; if (setjmp(e.err)) return -1; jsonenc_msg(&e, msg, m); + if (e.arena) upb_arena_free(e.arena); return jsonenc_nullz(&e, size); } diff --git a/upb/reflection.c b/upb/reflection.c index 42ee0990da..b31a917469 100644 --- a/upb/reflection.c +++ b/upb/reflection.c @@ -67,6 +67,7 @@ static uint32_t *oneofcase(const upb_msg *msg, } static upb_msgval _upb_msg_getraw(const upb_msg *msg, const upb_fielddef *f) { + assert(f); const upb_msglayout_field *field = upb_fielddef_layout(f); const char *mem = PTR_AT(msg, field->offset, char); upb_msgval val = {0};