Merge "Parse unknown enum values like full proto2."
diff --git a/java/pom.xml b/java/pom.xml
index ec890c3..59083c2 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -190,6 +190,12 @@
<arg value="../src/google/protobuf/unittest_extension_packed_nano.proto" />
</exec>
<exec executable="../src/protoc">
+ <arg value="--javanano_out=store_unknown_fields=true:target/generated-test-sources" />
+ <arg value="--proto_path=../src" />
+ <arg value="--proto_path=src/test/java" />
+ <arg value="../src/google/protobuf/unittest_unknown_enum_values_nano.proto" />
+ </exec>
+ <exec executable="../src/protoc">
<arg value="--javanano_out=java_nano_generate_has=true,generate_equals=true:target/generated-test-sources" />
<arg value="--proto_path=../src" />
<arg value="--proto_path=src/test/java" />
diff --git a/java/src/test/java/com/google/protobuf/NanoTest.java b/java/src/test/java/com/google/protobuf/NanoTest.java
index 9f0b3c1..42b1793 100644
--- a/java/src/test/java/com/google/protobuf/NanoTest.java
+++ b/java/src/test/java/com/google/protobuf/NanoTest.java
@@ -61,6 +61,8 @@
import com.google.protobuf.nano.UnittestRecursiveNano.RecursiveMessageNano;
import com.google.protobuf.nano.UnittestSimpleNano.SimpleMessageNano;
import com.google.protobuf.nano.UnittestSingleNano.SingleMessageNano;
+import com.google.protobuf.nano.UnknownEnumValuesNanoOuterClass.StandardVersion;
+import com.google.protobuf.nano.UnknownEnumValuesNanoOuterClass.VersionWithoutVal3;
import com.google.protobuf.nano.testext.Extensions;
import com.google.protobuf.nano.testext.Extensions.AnotherMessage;
import com.google.protobuf.nano.testext.Extensions.MessageWithGroup;
@@ -3875,6 +3877,67 @@
assertTrue("bar was not deserialized correctly", Arrays.equals(new byte[] { 7 }, bar));
}
+ public void testUnknownOptionalEnumValue() throws Exception {
+ StandardVersion standardMsg = new StandardVersion();
+ standardMsg.optionalField = StandardVersion.VAL_3;
+ byte[] standardMsgBytes = MessageNano.toByteArray(standardMsg);
+
+ // When parsing and reading, the unknown value appears as UNKNOWN.
+ VersionWithoutVal3 withoutVal3Msg = new VersionWithoutVal3();
+ MessageNano.mergeFrom(withoutVal3Msg, standardMsgBytes);
+ assertEquals(VersionWithoutVal3.UNKNOWN, withoutVal3Msg.optionalField);
+
+ // When serializing and reparsing as the standard version, the old, unknown value wins out (even
+ // if a new one is set).
+ withoutVal3Msg.optionalField = VersionWithoutVal3.VAL_2;
+ byte[] withoutVal3MsgBytes = MessageNano.toByteArray(withoutVal3Msg);
+ StandardVersion standardMsgFromWithoutVal3Msg = new StandardVersion();
+ MessageNano.mergeFrom(standardMsgFromWithoutVal3Msg, withoutVal3MsgBytes);
+ assertEquals(StandardVersion.VAL_3, standardMsgFromWithoutVal3Msg.optionalField);
+ }
+
+ public void testUnknownRepeatedEnumValue() throws Exception {
+ StandardVersion standardMsg = new StandardVersion();
+ standardMsg.repeatedField = new int[] { StandardVersion.VAL_3, StandardVersion.VAL_2 };
+ byte[] standardMsgBytes = MessageNano.toByteArray(standardMsg);
+
+ // When parsing and reading, the unknown value is stripped out.
+ VersionWithoutVal3 withoutVal3Msg = new VersionWithoutVal3();
+ MessageNano.mergeFrom(withoutVal3Msg, standardMsgBytes);
+ assertTrue(Arrays.equals(new int[] { VersionWithoutVal3.VAL_2 }, withoutVal3Msg.repeatedField));
+
+ // When serializing and reparsing as the standard version, the old, unknown value reappears, but
+ // at the end of all entries (including any newly added ones).
+ withoutVal3Msg.repeatedField = new int[] { VersionWithoutVal3.VAL_2, VersionWithoutVal3.VAL_1 };
+ byte[] withoutVal3MsgBytes = MessageNano.toByteArray(withoutVal3Msg);
+ StandardVersion standardMsgFromWithoutVal3Msg = new StandardVersion();
+ MessageNano.mergeFrom(standardMsgFromWithoutVal3Msg, withoutVal3MsgBytes);
+ assertTrue(Arrays.equals(
+ new int[] { StandardVersion.VAL_2, StandardVersion.VAL_1, StandardVersion.VAL_3 },
+ standardMsgFromWithoutVal3Msg.repeatedField));
+ }
+
+ public void testUnknownRepeatedPackedEnumValue() throws Exception {
+ StandardVersion standardMsg = new StandardVersion();
+ standardMsg.packedField = new int[] { StandardVersion.VAL_3, StandardVersion.VAL_2 };
+ byte[] standardMsgBytes = MessageNano.toByteArray(standardMsg);
+
+ // When parsing and reading, the unknown value is stripped out.
+ VersionWithoutVal3 withoutVal3Msg = new VersionWithoutVal3();
+ MessageNano.mergeFrom(withoutVal3Msg, standardMsgBytes);
+ assertTrue(Arrays.equals(new int[] { VersionWithoutVal3.VAL_2 }, withoutVal3Msg.packedField));
+
+ // When serializing and reparsing as the standard version, the old, unknown value reappears, but
+ // at the end of all entries (including any newly added ones).
+ withoutVal3Msg.packedField = new int[] { VersionWithoutVal3.VAL_2, VersionWithoutVal3.VAL_1 };
+ byte[] withoutVal3MsgBytes = MessageNano.toByteArray(withoutVal3Msg);
+ StandardVersion standardMsgFromWithoutVal3Msg = new StandardVersion();
+ MessageNano.mergeFrom(standardMsgFromWithoutVal3Msg, withoutVal3MsgBytes);
+ assertTrue(Arrays.equals(
+ new int[] { StandardVersion.VAL_2, StandardVersion.VAL_1, StandardVersion.VAL_3 },
+ standardMsgFromWithoutVal3Msg.packedField));
+ }
+
private void assertHasWireData(MessageNano message, boolean expected) {
byte[] bytes = MessageNano.toByteArray(message);
int wireLength = bytes.length;
diff --git a/src/google/protobuf/compiler/javanano/javanano_enum_field.cc b/src/google/protobuf/compiler/javanano/javanano_enum_field.cc
index 7666db3..39a4eeb 100644
--- a/src/google/protobuf/compiler/javanano/javanano_enum_field.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_enum_field.cc
@@ -144,6 +144,9 @@
void EnumFieldGenerator::
GenerateMergingCode(io::Printer* printer) const {
+ if (params_.store_unknown_fields()) {
+ printer->Print("int initialPos = input.getPosition();\n");
+ }
printer->Print(variables_,
"int value = input.readInt32();\n"
"switch (value) {\n");
@@ -155,11 +158,20 @@
" has$capitalized_name$ = true;\n");
}
printer->Print(
- " break;\n"
- "}\n");
- // No default case: in case of invalid value from the wire, preserve old
- // field value. Also we are not storing the invalid value into the unknown
- // fields, because there is no way to get the value out.
+ " break;\n");
+ if (params_.store_unknown_fields()) {
+ // If storing unknown fields, store invalid values there.
+ // This is consistent with full protobuf, but note that if a client writes
+ // a new value to this field, both will be serialized on the wire, and
+ // other clients which are aware of unknown fields will see the previous
+ // value, not the new one.
+ printer->Print(
+ " default:\n"
+ " input.rewindToPosition(initialPos);\n"
+ " storeUnknownField(input, tag);\n"
+ " break;\n");
+ }
+ printer->Print("}\n");
}
void EnumFieldGenerator::
@@ -300,6 +312,9 @@
void AccessorEnumFieldGenerator::
GenerateMergingCode(io::Printer* printer) const {
+ if (params_.store_unknown_fields()) {
+ printer->Print("int initialPos = input.getPosition();\n");
+ }
printer->Print(variables_,
"int value = input.readInt32();\n"
"switch (value) {\n");
@@ -307,11 +322,20 @@
printer->Print(variables_,
" $name$_ = value;\n"
" $set_has$;\n"
- " break;\n"
- "}\n");
- // No default case: in case of invalid value from the wire, preserve old
- // field value. Also we are not storing the invalid value into the unknown
- // fields, because there is no way to get the value out.
+ " break;\n");
+ if (params_.store_unknown_fields()) {
+ // If storing unknown fields, store invalid values there.
+ // This is consistent with full protobuf, but note that if a client writes
+ // a new value to this field, both will be serialized on the wire, and
+ // other clients which are aware of unknown fields will see the previous
+ // value, not the new one.
+ printer->Print(
+ " default:\n"
+ " input.rewindToPosition(initialPos);\n"
+ " storeUnknownField(input, tag);\n"
+ " break;\n");
+ }
+ printer->Print("}\n");
}
void AccessorEnumFieldGenerator::
@@ -381,7 +405,11 @@
"for (int i = 0; i < length; i++) {\n"
" if (i != 0) { // tag for first value already consumed.\n"
" input.readTag();\n"
- " }\n"
+ " }\n");
+ if (params_.store_unknown_fields()) {
+ printer->Print(" int initialPos = input.getPosition();\n");
+ }
+ printer->Print(
" int value = input.readInt32();\n"
" switch (value) {\n");
printer->Indent();
@@ -389,7 +417,19 @@
printer->Outdent();
printer->Print(variables_,
" validValues[validCount++] = value;\n"
- " break;\n"
+ " break;\n");
+ if (params_.store_unknown_fields()) {
+ // If storing unknown fields, store invalid values there.
+ // This is consistent with full protobuf. Note that this can lead to very
+ // strange behaviors if a value is serialized and reread, e.g. changes in
+ // value ordering.
+ printer->Print(
+ " default:\n"
+ " input.rewindToPosition(initialPos);\n"
+ " storeUnknownField(input, tag);\n"
+ " break;\n");
+ }
+ printer->Print(variables_,
" }\n"
"}\n"
"if (validCount != 0) {\n"
@@ -432,7 +472,11 @@
" if (i != 0) {\n"
" java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n"
" }\n"
- " while (input.getBytesUntilLimit() > 0) {\n"
+ " while (input.getBytesUntilLimit() > 0) {\n");
+ if (params_.store_unknown_fields()) {
+ printer->Print(" int initialPos = input.getPosition();\n");
+ }
+ printer->Print(variables_,
" int value = input.readInt32();\n"
" switch (value) {\n");
printer->Indent();
@@ -442,7 +486,19 @@
printer->Outdent();
printer->Print(variables_,
" newArray[i++] = value;\n"
- " break;\n"
+ " break;\n");
+ if (params_.store_unknown_fields()) {
+ // If storing unknown fields, store invalid values there.
+ // This is consistent with full protobuf. Note that this can lead to very
+ // strange behaviors if a value is serialized and reread, e.g. changes in
+ // value ordering.
+ printer->Print(variables_,
+ " default:\n"
+ " input.rewindToPosition(initialPos);\n"
+ " storeUnknownField(input, $non_packed_tag$);\n"
+ " break;\n");
+ }
+ printer->Print(variables_,
" }\n"
" }\n"
" this.$name$ = newArray;\n"
diff --git a/src/google/protobuf/unittest_unknown_enum_values_nano.proto b/src/google/protobuf/unittest_unknown_enum_values_nano.proto
new file mode 100644
index 0000000..59d91c4
--- /dev/null
+++ b/src/google/protobuf/unittest_unknown_enum_values_nano.proto
@@ -0,0 +1,58 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2016 Google Inc. All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package protobuf_unittest;
+
+option java_package = "com.google.protobuf.nano";
+option java_outer_classname = "UnknownEnumValuesNanoOuterClass";
+
+message StandardVersion {
+ enum Enum {
+ UNKNOWN = 0;
+ VAL_1 = 1;
+ VAL_2 = 2;
+ VAL_3 = 3;
+ }
+ optional Enum optional_field = 1;
+ repeated Enum repeated_field = 2;
+ repeated Enum packed_field = 3 [packed = true];
+}
+
+message VersionWithoutVal3 {
+ enum Enum {
+ UNKNOWN = 0;
+ VAL_1 = 1;
+ VAL_2 = 2;
+ // VAL_3 is unknown in this version.
+ }
+ optional Enum optional_field = 1;
+ repeated Enum repeated_field = 2;
+ repeated Enum packed_field = 3 [packed = true];
+}