Fix unordered field numbers inside oneof causing fields to be ignored (#617)
Previous fix in commit ee1aff9 was incorrect.
The descriptor array should be kept ordered and generator is now fixed
to do so.
diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py
index 578deb7..b95fb41 100755
--- a/generator/nanopb_generator.py
+++ b/generator/nanopb_generator.py
@@ -1068,9 +1068,6 @@
def tags(self):
return ''.join([f.tags() for f in self.fields])
- def fieldlist(self):
- return ' \\\n'.join(field.fieldlist() for field in self.fields)
-
def data_size(self, dependencies):
return max(f.data_size(dependencies) for f in self.fields)
@@ -1305,10 +1302,14 @@
while any(field.name == Field.macro_a_param for field in self.all_fields()):
Field.macro_a_param += '_'
+ # Field descriptor array must be sorted by tag number, pb_common.c relies on it.
+ sorted_fields = list(self.all_fields())
+ sorted_fields.sort(key = lambda x: x.tag)
+
result = '#define %s_FIELDLIST(%s, %s) \\\n' % (self.name,
Field.macro_x_param,
Field.macro_a_param)
- result += ' \\\n'.join(field.fieldlist() for field in sorted(self.fields))
+ result += ' \\\n'.join(x.fieldlist() for x in sorted_fields)
result += '\n'
has_callbacks = bool([f for f in self.fields if f.has_callbacks()])
@@ -1326,13 +1327,12 @@
else:
result += '#define %s_DEFAULT NULL\n' % self.name
- for field in sorted(self.fields):
+ for field in sorted_fields:
if field.pbtype in ['MESSAGE', 'MSG_W_CB']:
- result += "#define %s_%s_MSGTYPE %s\n" % (self.name, field.name, field.ctype)
- elif field.rules == 'ONEOF':
- for member in field.fields:
- if member.pbtype in ['MESSAGE', 'MSG_W_CB']:
- result += "#define %s_%s_%s_MSGTYPE %s\n" % (self.name, member.union_name, member.name, member.ctype)
+ if field.rules == 'ONEOF':
+ result += "#define %s_%s_%s_MSGTYPE %s\n" % (self.name, field.union_name, field.name, field.ctype)
+ else:
+ result += "#define %s_%s_MSGTYPE %s\n" % (self.name, field.name, field.ctype)
return result
diff --git a/tests/common_unittests/common_unittests.c b/tests/common_unittests/common_unittests.c
index b6ed46d..8452b35 100644
--- a/tests/common_unittests/common_unittests.c
+++ b/tests/common_unittests/common_unittests.c
@@ -88,12 +88,11 @@
TEST(pb_field_iter_next(&iter) && iter.tag == 60 && iter.pData == &msg.oneof.oneof_msg1 && iter.pSize == &msg.which_oneof )
TEST(pb_field_iter_next(&iter) && iter.tag == 61 && iter.pData == &msg.oneof.oneof_msg2 && iter.pSize == &msg.which_oneof )
+ TEST(pb_field_iter_next(&iter) && iter.tag == 62 && iter.pData == &msg.opt_non_zero_based_enum && iter.pSize == &msg.has_opt_non_zero_based_enum)
TEST(pb_field_iter_next(&iter) && iter.tag == 63 && iter.pData == &msg.oneof.static_msg && iter.pSize == &msg.which_oneof )
TEST(iter.required_field_index == 19)
TEST(iter.submessage_index == 8)
- TEST(pb_field_iter_next(&iter) && iter.tag == 62 && iter.pData == &msg.opt_non_zero_based_enum && iter.pSize == &msg.has_opt_non_zero_based_enum)
-
TEST(pb_field_iter_next(&iter) && iter.tag == 95 && iter.pData == &msg.rep_farray2 && iter.pSize == &iter.array_size && iter.array_size == 3)
TEST(iter.required_field_index == 19)
TEST(iter.submessage_index == 9)
diff --git a/tests/regression/issue_617/SConscript b/tests/regression/issue_617/SConscript
new file mode 100644
index 0000000..80ca106
--- /dev/null
+++ b/tests/regression/issue_617/SConscript
@@ -0,0 +1,10 @@
+# Regression test for #617:
+# Unordered field numbers inside oneof can cause fields to be ignored
+
+Import("env")
+
+env.NanopbProto(["oneof.proto", "oneof.options"])
+
+test = env.Program(["test_oneof.c", "oneof.pb.c", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"])
+env.RunTest(test)
+
diff --git a/tests/regression/issue_617/oneof.options b/tests/regression/issue_617/oneof.options
new file mode 100644
index 0000000..cdbbd51
--- /dev/null
+++ b/tests/regression/issue_617/oneof.options
@@ -0,0 +1,4 @@
+* long_names : false
+
+NoDecode.name max_length : 32
+
diff --git a/tests/regression/issue_617/oneof.proto b/tests/regression/issue_617/oneof.proto
new file mode 100644
index 0000000..914d5f3
--- /dev/null
+++ b/tests/regression/issue_617/oneof.proto
@@ -0,0 +1,41 @@
+syntax = "proto3";
+
+message DecodesOK
+{
+ uint32 a = 1;
+ uint32 b = 2;
+}
+
+message NoDecode
+{
+ string name = 1;
+}
+
+message TestMessage
+{
+ enum MessageType
+ {
+ A = 0;
+ B = 1;
+ C = 2;
+ }
+
+ MessageType messageType = 1;
+ uint32 x = 2;
+ uint32 y = 3;
+ uint32 ip1 = 4;
+
+ oneof payload
+ {
+ DecodesOK pla5 = 5;
+ DecodesOK pla6 = 6;
+ DecodesOK pla7 = 7;
+ DecodesOK pla8 = 8;
+ DecodesOK pla9 = 9;
+ DecodesOK pla10 = 10;
+ NoDecode plb11 = 12;
+ }
+
+ uint32 ip2 = 11;
+}
+
diff --git a/tests/regression/issue_617/test_oneof.c b/tests/regression/issue_617/test_oneof.c
new file mode 100644
index 0000000..7a89fad
--- /dev/null
+++ b/tests/regression/issue_617/test_oneof.c
@@ -0,0 +1,22 @@
+#include <pb_decode.h>
+#include <unittests.h>
+#include "oneof.pb.h"
+
+int main()
+{
+ const uint8_t input_data[] = {
+ 0x08, 0x01, 0x10, 0x0F, 0x18, 0xAC, 0x02, 0x20,
+ 0xF1, 0x82, 0xA0, 0x85, 0x0C, 0x62, 0x00, 0x58,
+ 0xF1, 0x82, 0xA0, 0x85, 0x0C
+ };
+
+ int status = 0;
+ TestMessage msg = TestMessage_init_zero;
+ pb_istream_t stream = pb_istream_from_buffer(input_data, sizeof(input_data));
+
+ TEST(pb_decode(&stream, TestMessage_fields, &msg));
+ TEST(msg.which_payload == TestMessage_plb11_tag);
+ TEST(msg.payload.plb11.name[0] == 0);
+
+ return status;
+}