diff options
22 files changed, 355 insertions, 68 deletions
diff --git a/editors/proto.vim b/editors/proto.vim index 7cd1dbf2..23085a28 100644 --- a/editors/proto.vim +++ b/editors/proto.vim @@ -54,7 +54,7 @@ syn keyword pbTodo contained TODO FIXME XXX syn cluster pbCommentGrp contains=pbTodo syn keyword pbSyntax syntax import option -syn keyword pbStructure package message group +syn keyword pbStructure package message group oneof syn keyword pbRepeat optional required repeated syn keyword pbDefault default syn keyword pbExtend extend extensions to max diff --git a/java/src/main/java/com/google/protobuf/BoundedByteString.java b/java/src/main/java/com/google/protobuf/BoundedByteString.java index 2828e9c7..8cb6f463 100644 --- a/java/src/main/java/com/google/protobuf/BoundedByteString.java +++ b/java/src/main/java/com/google/protobuf/BoundedByteString.java @@ -30,6 +30,9 @@ package com.google.protobuf; +import java.io.InvalidObjectException; +import java.io.IOException; +import java.io.ObjectInputStream; import java.util.NoSuchElementException; /** @@ -123,6 +126,20 @@ class BoundedByteString extends LiteralByteString { } // ================================================================= + // Serializable + + private static final long serialVersionUID = 1L; + + Object writeReplace() { + return new LiteralByteString(toByteArray()); + } + + private void readObject(ObjectInputStream in) throws IOException { + throw new InvalidObjectException( + "BoundedByteStream instances are not to be serialized directly"); + } + + // ================================================================= // ByteIterator @Override diff --git a/java/src/main/java/com/google/protobuf/ByteString.java b/java/src/main/java/com/google/protobuf/ByteString.java index 7da56127..cff1ee51 100644 --- a/java/src/main/java/com/google/protobuf/ByteString.java +++ b/java/src/main/java/com/google/protobuf/ByteString.java @@ -34,6 +34,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -57,7 +58,7 @@ import java.util.NoSuchElementException; * @author carlanton@google.com Carl Haverl * @author martinrb@google.com Martin Buchholz */ -public abstract class ByteString implements Iterable<Byte> { +public abstract class ByteString implements Iterable<Byte>, Serializable { /** * When two strings to be concatenated have a combined length shorter than @@ -502,9 +503,9 @@ public abstract class ByteString implements Iterable<Byte> { /** * Internal (package private) implementation of - * @link{#copyTo(byte[],int,int,int}. + * {@link #copyTo(byte[],int,int,int)}. * It assumes that all error checking has already been performed and that - * @code{numberToCopy > 0}. + * {@code numberToCopy > 0}. */ protected abstract void copyToInternal(byte[] target, int sourceOffset, int targetOffset, int numberToCopy); @@ -699,7 +700,7 @@ public abstract class ByteString implements Iterable<Byte> { * The {@link InputStream} returned by this method is guaranteed to be * completely non-blocking. The method {@link InputStream#available()} * returns the number of bytes remaining in the stream. The methods - * {@link InputStream#read(byte[]), {@link InputStream#read(byte[],int,int)} + * {@link InputStream#read(byte[])}, {@link InputStream#read(byte[],int,int)} * and {@link InputStream#skip(long)} will read/skip as many bytes as are * available. * <p> diff --git a/java/src/main/java/com/google/protobuf/Internal.java b/java/src/main/java/com/google/protobuf/Internal.java index 5ef39418..5a0de6d1 100644 --- a/java/src/main/java/com/google/protobuf/Internal.java +++ b/java/src/main/java/com/google/protobuf/Internal.java @@ -236,7 +236,7 @@ public class Internal { } /** - * Helper method for implementing {@link MessageLite#hashCode()} for longs. + * Helper method for implementing {@link Message#hashCode()} for longs. * @see Long#hashCode() */ public static int hashLong(long n) { @@ -244,7 +244,7 @@ public class Internal { } /** - * Helper method for implementing {@link MessageLite#hashCode()} for + * Helper method for implementing {@link Message#hashCode()} for * booleans. * @see Boolean#hashCode() */ @@ -253,7 +253,7 @@ public class Internal { } /** - * Helper method for implementing {@link MessageLite#hashCode()} for enums. + * Helper method for implementing {@link Message#hashCode()} for enums. * <p> * This is needed because {@link java.lang.Enum#hashCode()} is final, but we * need to use the field number as the hash code to ensure compatibility @@ -264,7 +264,7 @@ public class Internal { } /** - * Helper method for implementing {@link MessageLite#hashCode()} for + * Helper method for implementing {@link Message#hashCode()} for * enum lists. */ public static int hashEnumList(List<? extends EnumLite> list) { @@ -276,7 +276,7 @@ public class Internal { } /** - * Helper method for implementing {@link MessageLite#equals()} for bytes field. + * Helper method for implementing {@link Message#equals(Object)} for bytes field. */ public static boolean equals(List<byte[]> a, List<byte[]> b) { if (a.size() != b.size()) return false; @@ -289,7 +289,7 @@ public class Internal { } /** - * Helper method for implementing {@link MessageLite#hashCode()} for bytes field. + * Helper method for implementing {@link Message#hashCode()} for bytes field. */ public static int hashCode(List<byte[]> list) { int hash = 1; @@ -300,7 +300,7 @@ public class Internal { } /** - * Helper method for implementing {@link MessageLite#hashCode()} for bytes field. + * Helper method for implementing {@link Message#hashCode()} for bytes field. */ public static int hashCode(byte[] bytes) { // The hash code for a byte array should be the same as the hash code for a @@ -311,7 +311,7 @@ public class Internal { } /** - * Helper method for implementing {@link MessageLite#equals()} for bytes + * Helper method for implementing {@link Message#equals(Object)} for bytes * field. */ public static boolean equalsByteBuffer(ByteBuffer a, ByteBuffer b) { @@ -324,7 +324,7 @@ public class Internal { } /** - * Helper method for implementing {@link MessageLite#equals()} for bytes + * Helper method for implementing {@link Message#equals(Object)} for bytes * field. */ public static boolean equalsByteBuffer( @@ -341,7 +341,7 @@ public class Internal { } /** - * Helper method for implementing {@link MessageLite#hashCode()} for bytes + * Helper method for implementing {@link Message#hashCode()} for bytes * field. */ public static int hashCodeByteBuffer(List<ByteBuffer> list) { @@ -355,7 +355,7 @@ public class Internal { private static final int DEFAULT_BUFFER_SIZE = 4096; /** - * Helper method for implementing {@link MessageLite#hashCode()} for bytes + * Helper method for implementing {@link Message#hashCode()} for bytes * field. */ public static int hashCodeByteBuffer(ByteBuffer bytes) { diff --git a/java/src/main/java/com/google/protobuf/LiteralByteString.java b/java/src/main/java/com/google/protobuf/LiteralByteString.java index 127c574d..767b9f35 100644 --- a/java/src/main/java/com/google/protobuf/LiteralByteString.java +++ b/java/src/main/java/com/google/protobuf/LiteralByteString.java @@ -51,6 +51,8 @@ import java.util.NoSuchElementException; */ class LiteralByteString extends ByteString { + private static final long serialVersionUID = 1L; + protected final byte[] bytes; /** diff --git a/java/src/main/java/com/google/protobuf/RopeByteString.java b/java/src/main/java/com/google/protobuf/RopeByteString.java index d1655b80..fa23c9dd 100644 --- a/java/src/main/java/com/google/protobuf/RopeByteString.java +++ b/java/src/main/java/com/google/protobuf/RopeByteString.java @@ -30,8 +30,10 @@ package com.google.protobuf; +import java.io.InvalidObjectException; import java.io.IOException; import java.io.InputStream; +import java.io.ObjectInputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.io.ByteArrayInputStream; @@ -772,6 +774,20 @@ class RopeByteString extends ByteString { } // ================================================================= + // Serializable + + private static final long serialVersionUID = 1L; + + Object writeReplace() { + return new LiteralByteString(toByteArray()); + } + + private void readObject(ObjectInputStream in) throws IOException { + throw new InvalidObjectException( + "RopeByteStream instances are not to be serialized directly"); + } + + // ================================================================= // ByteIterator @Override diff --git a/java/src/main/java/com/google/protobuf/TextFormat.java b/java/src/main/java/com/google/protobuf/TextFormat.java index 63e62fc6..4f6756ed 100644 --- a/java/src/main/java/com/google/protobuf/TextFormat.java +++ b/java/src/main/java/com/google/protobuf/TextFormat.java @@ -411,7 +411,8 @@ public final class TextFormat { generator.print("\""); generator.print(escapeNonAscii ? escapeText((String) value) : - escapeDoubleQuotesAndBackslashes((String) value)); + escapeDoubleQuotesAndBackslashes((String) value) + .replace("\n", "\\n")); generator.print("\""); break; diff --git a/java/src/test/java/com/google/protobuf/BoundedByteStringTest.java b/java/src/test/java/com/google/protobuf/BoundedByteStringTest.java index 2b71cfbc..6c9596ca 100644 --- a/java/src/test/java/com/google/protobuf/BoundedByteStringTest.java +++ b/java/src/test/java/com/google/protobuf/BoundedByteStringTest.java @@ -30,8 +30,14 @@ package com.google.protobuf; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.UnsupportedEncodingException; + /** * This class tests {@link BoundedByteString}, which extends {@link LiteralByteString}, * by inheriting the tests from {@link LiteralByteStringTest}. The only method which @@ -65,4 +71,17 @@ public class BoundedByteStringTest extends LiteralByteStringTest { assertEquals(classUnderTest + " unicode bytes must match", testString.substring(2, testString.length() - 6), roundTripString); } + + public void testJavaSerialization() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(out); + oos.writeObject(stringUnderTest); + oos.close(); + byte[] pickled = out.toByteArray(); + InputStream in = new ByteArrayInputStream(pickled); + ObjectInputStream ois = new ObjectInputStream(in); + Object o = ois.readObject(); + assertTrue("Didn't get a ByteString back", o instanceof ByteString); + assertEquals("Should get an equal ByteString back", stringUnderTest, o); + } } diff --git a/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java b/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java index 475f7ffb..ff39ca3f 100644 --- a/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java +++ b/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java @@ -32,9 +32,12 @@ package com.google.protobuf; import junit.framework.TestCase; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; @@ -393,4 +396,17 @@ public class LiteralByteStringTest extends TestCase { assertSame("empty concatenated with " + classUnderTest + " must give " + classUnderTest, ByteString.EMPTY.concat(stringUnderTest), stringUnderTest); } + + public void testJavaSerialization() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(out); + oos.writeObject(stringUnderTest); + oos.close(); + byte[] pickled = out.toByteArray(); + InputStream in = new ByteArrayInputStream(pickled); + ObjectInputStream ois = new ObjectInputStream(in); + Object o = ois.readObject(); + assertTrue("Didn't get a ByteString back", o instanceof ByteString); + assertEquals("Should get an equal ByteString back", stringUnderTest, o); + } } diff --git a/java/src/test/java/com/google/protobuf/RopeByteStringTest.java b/java/src/test/java/com/google/protobuf/RopeByteStringTest.java index 9676f527..b3970196 100644 --- a/java/src/test/java/com/google/protobuf/RopeByteStringTest.java +++ b/java/src/test/java/com/google/protobuf/RopeByteStringTest.java @@ -30,6 +30,11 @@ package com.google.protobuf; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.UnsupportedEncodingException; import java.util.Arrays; import java.util.Iterator; @@ -112,4 +117,17 @@ public class RopeByteStringTest extends LiteralByteStringTest { assertEquals(classUnderTest + " string must must have same hashCode as the flat string", flatString.hashCode(), unicode.hashCode()); } + + public void testJavaSerialization() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(out); + oos.writeObject(stringUnderTest); + oos.close(); + byte[] pickled = out.toByteArray(); + InputStream in = new ByteArrayInputStream(pickled); + ObjectInputStream ois = new ObjectInputStream(in); + Object o = ois.readObject(); + assertTrue("Didn't get a ByteString back", o instanceof ByteString); + assertEquals("Should get an equal ByteString back", stringUnderTest, o); + } } diff --git a/java/src/test/java/com/google/protobuf/TextFormatTest.java b/java/src/test/java/com/google/protobuf/TextFormatTest.java index 82f9582f..eba06ca0 100644 --- a/java/src/test/java/com/google/protobuf/TextFormatTest.java +++ b/java/src/test/java/com/google/protobuf/TextFormatTest.java @@ -864,15 +864,15 @@ public class TextFormatTest extends TestCase { assertEquals(message.getOptionalString(), builder.getOptionalString()); } - public void testPrintToUnicodeStringWithNewlines() { + public void testPrintToUnicodeStringWithNewlines() throws Exception { // No newlines at start and end - assertEquals("optional_string: \"test newlines\n\nin\nstring\"\n", + assertEquals("optional_string: \"test newlines\\n\\nin\\nstring\"\n", TextFormat.printToUnicodeString(TestAllTypes.newBuilder() .setOptionalString("test newlines\n\nin\nstring") .build())); // Newlines at start and end - assertEquals("optional_string: \"\ntest\nnewlines\n\nin\nstring\n\"\n", + assertEquals("optional_string: \"\\ntest\\nnewlines\\n\\nin\\nstring\\n\"\n", TextFormat.printToUnicodeString(TestAllTypes.newBuilder() .setOptionalString("\ntest\nnewlines\n\nin\nstring\n") .build())); @@ -882,14 +882,22 @@ public class TextFormatTest extends TestCase { TextFormat.printToUnicodeString(TestAllTypes.newBuilder() .setOptionalString("") .build())); - assertEquals("optional_string: \"\n\"\n", + assertEquals("optional_string: \"\\n\"\n", TextFormat.printToUnicodeString(TestAllTypes.newBuilder() .setOptionalString("\n") .build())); - assertEquals("optional_string: \"\n\n\"\n", + assertEquals("optional_string: \"\\n\\n\"\n", TextFormat.printToUnicodeString(TestAllTypes.newBuilder() .setOptionalString("\n\n") .build())); + + // Test escaping roundtrip + TestAllTypes message = TestAllTypes.newBuilder() + .setOptionalString("\ntest\nnewlines\n\nin\nstring\n") + .build(); + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TextFormat.merge(TextFormat.printToUnicodeString(message), builder); + assertEquals(message.getOptionalString(), builder.getOptionalString()); } public void testPrintToUnicodeString_unknown() { diff --git a/src/google/protobuf/compiler/cpp/cpp_extension.h b/src/google/protobuf/compiler/cpp/cpp_extension.h index d354c16a..1c1caf1f 100644 --- a/src/google/protobuf/compiler/cpp/cpp_extension.h +++ b/src/google/protobuf/compiler/cpp/cpp_extension.h @@ -57,7 +57,7 @@ namespace cpp { class ExtensionGenerator { public: // See generator.cc for the meaning of dllexport_decl. - explicit ExtensionGenerator(const FieldDescriptor* desycriptor, + explicit ExtensionGenerator(const FieldDescriptor* descriptor, const Options& options); ~ExtensionGenerator(); diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc index b42f32b8..212bc3e9 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message.cc @@ -561,7 +561,7 @@ GenerateFieldAccessorDefinitions(io::Printer* printer) { } else { printer->Print(vars, "inline bool $classname$::has_$name$() const {\n" - " return $name$_ != NULL;\n" + " return !_is_default_instance_ && $name$_ != NULL;\n" "}\n"); } } @@ -1051,6 +1051,22 @@ GenerateClassDefinition(io::Printer* printer) { printer->Print(cached_size_decl.c_str()); need_to_emit_cached_size = false; } + } else { + // Without field presence, we need another way to disambiguate the default + // instance, because the default instance's submessage fields (if any) store + // pointers to the default instances of the submessages even when they + // aren't present. Alternatives to this approach might be to (i) use a + // tagged pointer on all message fields, setting a tag bit for "not really + // present, just default instance"; or (ii) comparing |this| against the + // return value from GeneratedMessageFactory::GetPrototype() in all + // has_$field$() calls. However, both of these options are much more + // expensive (in code size and CPU overhead) than just checking a field in + // the message. Long-term, the best solution would be to rearchitect the + // default instance design not to store pointers to submessage default + // instances, and have reflection get those some other way; but that change + // would have too much impact on proto2. + printer->Print( + "bool _is_default_instance_;\n"); } // Field members: @@ -1323,11 +1339,21 @@ GenerateDescriptorInitializer(io::Printer* printer, int index) { if (UseUnknownFieldSet(descriptor_->file())) { printer->Print(vars, " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" - "$classname$, _internal_metadata_));\n"); + "$classname$, _internal_metadata_),\n"); + } else { + printer->Print(vars, + " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" + "$classname$, _arena_),\n"); + } + + // is_default_instance_ offset. + if (HasFieldPresence(descriptor_->file())) { + printer->Print(vars, + " -1);\n"); } else { printer->Print(vars, " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" - "$classname$, _arena_));\n"); + "$classname$, _is_default_instance_));\n"); } // Handle nested types. @@ -1612,6 +1638,11 @@ GenerateSharedConstructorCode(io::Printer* printer) { "classname", classname_); printer->Indent(); + if (!HasFieldPresence(descriptor_->file())) { + printer->Print( + " _is_default_instance_ = false;\n"); + } + printer->Print(StrCat( uses_string_ ? "::google::protobuf::internal::GetEmptyString();\n" : "", "_cached_size_ = 0;\n").c_str()); @@ -1809,6 +1840,11 @@ GenerateStructors(io::Printer* printer) { "void $classname$::InitAsDefaultInstance() {\n", "classname", classname_); + if (!HasFieldPresence(descriptor_->file())) { + printer->Print( + " _is_default_instance_ = true;\n"); + } + // The default instance needs all of its embedded message pointers // cross-linked to other default instances. We can't do this initialization // in the constructor because some other default instances may not have been diff --git a/src/google/protobuf/compiler/parser.cc b/src/google/protobuf/compiler/parser.cc index 4b3efe48..fe697acf 100644 --- a/src/google/protobuf/compiler/parser.cc +++ b/src/google/protobuf/compiler/parser.cc @@ -468,6 +468,10 @@ bool Parser::Parse(io::Tokenizer* input, FileDescriptorProto* file) { // Store the syntax into the file. if (file != NULL) file->set_syntax(syntax_identifier_); } else if (!stop_after_syntax_identifier_) { + GOOGLE_LOG(WARNING) << "No syntax specified for the proto file. " + << "Please use 'syntax = \"proto2\";' or " + << "'syntax = \"proto3\";' to specify a syntax " + << "version. (Defaulted to proto2 syntax.)"; syntax_identifier_ = "proto2"; } diff --git a/src/google/protobuf/compiler/parser_unittest.cc b/src/google/protobuf/compiler/parser_unittest.cc index 00112999..45f95df1 100644 --- a/src/google/protobuf/compiler/parser_unittest.cc +++ b/src/google/protobuf/compiler/parser_unittest.cc @@ -216,6 +216,15 @@ TEST_F(ParserTest, StopAfterSyntaxIdentifierWithErrors) { EXPECT_EQ("1:9: Expected syntax identifier.\n", error_collector_.text_); } +TEST_F(ParserTest, WarnIfSyntaxIdentifierOmmitted) { + SetupParser("message A {}"); + FileDescriptorProto file; + CaptureTestStderr(); + EXPECT_TRUE(parser_->Parse(input_.get(), &file)); + EXPECT_TRUE( + GetCapturedTestStderr().find("No syntax specified") != string::npos); +} + // =================================================================== typedef ParserTest ParseMessageTest; diff --git a/src/google/protobuf/compiler/plugin.pb.cc b/src/google/protobuf/compiler/plugin.pb.cc index f33d716c..5118de15 100644 --- a/src/google/protobuf/compiler/plugin.pb.cc +++ b/src/google/protobuf/compiler/plugin.pb.cc @@ -56,7 +56,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto() { -1, -1, sizeof(CodeGeneratorRequest), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(CodeGeneratorRequest, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(CodeGeneratorRequest, _internal_metadata_), + -1); CodeGeneratorResponse_descriptor_ = file->message_type(1); static const int CodeGeneratorResponse_offsets_[2] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(CodeGeneratorResponse, error_), @@ -71,7 +72,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto() { -1, -1, sizeof(CodeGeneratorResponse), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(CodeGeneratorResponse, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(CodeGeneratorResponse, _internal_metadata_), + -1); CodeGeneratorResponse_File_descriptor_ = CodeGeneratorResponse_descriptor_->nested_type(0); static const int CodeGeneratorResponse_File_offsets_[3] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(CodeGeneratorResponse_File, name_), @@ -87,7 +89,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fcompiler_2fplugin_2eproto() { -1, -1, sizeof(CodeGeneratorResponse_File), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(CodeGeneratorResponse_File, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(CodeGeneratorResponse_File, _internal_metadata_), + -1); } namespace { diff --git a/src/google/protobuf/descriptor.pb.cc b/src/google/protobuf/descriptor.pb.cc index df214d44..97121fa9 100644 --- a/src/google/protobuf/descriptor.pb.cc +++ b/src/google/protobuf/descriptor.pb.cc @@ -111,7 +111,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, -1, sizeof(FileDescriptorSet), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorSet, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorSet, _internal_metadata_), + -1); FileDescriptorProto_descriptor_ = file->message_type(1); static const int FileDescriptorProto_offsets_[12] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, name_), @@ -136,7 +137,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, -1, sizeof(FileDescriptorProto), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, _internal_metadata_), + -1); DescriptorProto_descriptor_ = file->message_type(2); static const int DescriptorProto_offsets_[8] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, name_), @@ -157,7 +159,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, -1, sizeof(DescriptorProto), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, _internal_metadata_), + -1); DescriptorProto_ExtensionRange_descriptor_ = DescriptorProto_descriptor_->nested_type(0); static const int DescriptorProto_ExtensionRange_offsets_[2] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto_ExtensionRange, start_), @@ -172,7 +175,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, -1, sizeof(DescriptorProto_ExtensionRange), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto_ExtensionRange, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto_ExtensionRange, _internal_metadata_), + -1); FieldDescriptorProto_descriptor_ = file->message_type(3); static const int FieldDescriptorProto_offsets_[9] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, name_), @@ -194,7 +198,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, -1, sizeof(FieldDescriptorProto), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, _internal_metadata_), + -1); FieldDescriptorProto_Type_descriptor_ = FieldDescriptorProto_descriptor_->enum_type(0); FieldDescriptorProto_Label_descriptor_ = FieldDescriptorProto_descriptor_->enum_type(1); OneofDescriptorProto_descriptor_ = file->message_type(4); @@ -210,7 +215,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, -1, sizeof(OneofDescriptorProto), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(OneofDescriptorProto, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(OneofDescriptorProto, _internal_metadata_), + -1); EnumDescriptorProto_descriptor_ = file->message_type(5); static const int EnumDescriptorProto_offsets_[3] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumDescriptorProto, name_), @@ -226,7 +232,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, -1, sizeof(EnumDescriptorProto), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumDescriptorProto, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumDescriptorProto, _internal_metadata_), + -1); EnumValueDescriptorProto_descriptor_ = file->message_type(6); static const int EnumValueDescriptorProto_offsets_[3] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumValueDescriptorProto, name_), @@ -242,7 +249,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, -1, sizeof(EnumValueDescriptorProto), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumValueDescriptorProto, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumValueDescriptorProto, _internal_metadata_), + -1); ServiceDescriptorProto_descriptor_ = file->message_type(7); static const int ServiceDescriptorProto_offsets_[3] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceDescriptorProto, name_), @@ -258,7 +266,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, -1, sizeof(ServiceDescriptorProto), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceDescriptorProto, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceDescriptorProto, _internal_metadata_), + -1); MethodDescriptorProto_descriptor_ = file->message_type(8); static const int MethodDescriptorProto_offsets_[6] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodDescriptorProto, name_), @@ -277,7 +286,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, -1, sizeof(MethodDescriptorProto), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodDescriptorProto, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodDescriptorProto, _internal_metadata_), + -1); FileOptions_descriptor_ = file->message_type(9); static const int FileOptions_offsets_[13] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, java_package_), @@ -303,7 +313,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, _extensions_), sizeof(FileOptions), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, _internal_metadata_), + -1); FileOptions_OptimizeMode_descriptor_ = FileOptions_descriptor_->enum_type(0); MessageOptions_descriptor_ = file->message_type(10); static const int MessageOptions_offsets_[5] = { @@ -322,7 +333,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MessageOptions, _extensions_), sizeof(MessageOptions), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MessageOptions, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MessageOptions, _internal_metadata_), + -1); FieldOptions_descriptor_ = file->message_type(11); static const int FieldOptions_offsets_[6] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, ctype_), @@ -341,7 +353,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, _extensions_), sizeof(FieldOptions), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, _internal_metadata_), + -1); FieldOptions_CType_descriptor_ = FieldOptions_descriptor_->enum_type(0); EnumOptions_descriptor_ = file->message_type(12); static const int EnumOptions_offsets_[3] = { @@ -358,7 +371,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumOptions, _extensions_), sizeof(EnumOptions), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumOptions, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumOptions, _internal_metadata_), + -1); EnumValueOptions_descriptor_ = file->message_type(13); static const int EnumValueOptions_offsets_[2] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumValueOptions, deprecated_), @@ -373,7 +387,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumValueOptions, _extensions_), sizeof(EnumValueOptions), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumValueOptions, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumValueOptions, _internal_metadata_), + -1); ServiceOptions_descriptor_ = file->message_type(14); static const int ServiceOptions_offsets_[2] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceOptions, deprecated_), @@ -388,7 +403,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceOptions, _extensions_), sizeof(ServiceOptions), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceOptions, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceOptions, _internal_metadata_), + -1); MethodOptions_descriptor_ = file->message_type(15); static const int MethodOptions_offsets_[2] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodOptions, deprecated_), @@ -403,7 +419,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodOptions, _extensions_), sizeof(MethodOptions), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodOptions, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodOptions, _internal_metadata_), + -1); UninterpretedOption_descriptor_ = file->message_type(16); static const int UninterpretedOption_offsets_[7] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(UninterpretedOption, name_), @@ -423,7 +440,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, -1, sizeof(UninterpretedOption), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(UninterpretedOption, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(UninterpretedOption, _internal_metadata_), + -1); UninterpretedOption_NamePart_descriptor_ = UninterpretedOption_descriptor_->nested_type(0); static const int UninterpretedOption_NamePart_offsets_[2] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(UninterpretedOption_NamePart, name_part_), @@ -438,7 +456,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, -1, sizeof(UninterpretedOption_NamePart), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(UninterpretedOption_NamePart, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(UninterpretedOption_NamePart, _internal_metadata_), + -1); SourceCodeInfo_descriptor_ = file->message_type(17); static const int SourceCodeInfo_offsets_[1] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SourceCodeInfo, location_), @@ -452,7 +471,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, -1, sizeof(SourceCodeInfo), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SourceCodeInfo, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SourceCodeInfo, _internal_metadata_), + -1); SourceCodeInfo_Location_descriptor_ = SourceCodeInfo_descriptor_->nested_type(0); static const int SourceCodeInfo_Location_offsets_[4] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SourceCodeInfo_Location, path_), @@ -469,7 +489,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { -1, -1, sizeof(SourceCodeInfo_Location), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SourceCodeInfo_Location, _internal_metadata_)); + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SourceCodeInfo_Location, _internal_metadata_), + -1); } namespace { diff --git a/src/google/protobuf/dynamic_message.cc b/src/google/protobuf/dynamic_message.cc index 565afaab..318ce6f9 100644 --- a/src/google/protobuf/dynamic_message.cc +++ b/src/google/protobuf/dynamic_message.cc @@ -218,6 +218,7 @@ class DynamicMessage : public Message { int oneof_case_offset; int unknown_fields_offset; int extensions_offset; + int is_default_instance_offset; // Not owned by the TypeInfo. DynamicMessageFactory* factory; // The factory that created this object. @@ -316,6 +317,11 @@ void DynamicMessage::SharedCtor() { uint32(0); } + if (type_info_->is_default_instance_offset != -1) { + *reinterpret_cast<bool*>( + OffsetToPointer(type_info_->is_default_instance_offset)) = false; + } + new(OffsetToPointer(type_info_->unknown_fields_offset)) UnknownFieldSet; if (type_info_->extensions_offset != -1) { @@ -532,6 +538,14 @@ void DynamicMessage::CrossLinkPrototypes() { factory->GetPrototypeNoLock(field->message_type()); } } + + // Set as the default instance -- this affects field-presence semantics for + // proto3. + if (type_info_->is_default_instance_offset != -1) { + void* is_default_instance_ptr = + OffsetToPointer(type_info_->is_default_instance_offset); + *reinterpret_cast<bool*>(is_default_instance_ptr) = true; + } } Message* DynamicMessage::New() const { @@ -641,11 +655,24 @@ const Message* DynamicMessageFactory::GetPrototypeNoLock( size = AlignOffset(size); // Next the has_bits, which is an array of uint32s. - type_info->has_bits_offset = size; - int has_bits_array_size = - DivideRoundingUp(type->field_count(), bitsizeof(uint32)); - size += has_bits_array_size * sizeof(uint32); - size = AlignOffset(size); + if (type->file()->syntax() == FileDescriptor::SYNTAX_PROTO3) { + type_info->has_bits_offset = -1; + } else { + type_info->has_bits_offset = size; + int has_bits_array_size = + DivideRoundingUp(type->field_count(), bitsizeof(uint32)); + size += has_bits_array_size * sizeof(uint32); + size = AlignOffset(size); + } + + // The is_default_instance member, if any. + if (type->file()->syntax() == FileDescriptor::SYNTAX_PROTO3) { + type_info->is_default_instance_offset = size; + size += sizeof(bool); + size = AlignOffset(size); + } else { + type_info->is_default_instance_offset = -1; + } // The oneof_case, if any. It is an array of uint32s. if (type->oneof_decl_count() > 0) { @@ -731,7 +758,8 @@ const Message* DynamicMessageFactory::GetPrototypeNoLock( type_info->pool, this, type_info->size, - -1 /* arena_offset */)); + -1 /* arena_offset */, + type_info->is_default_instance_offset)); } else { type_info->reflection.reset( new GeneratedMessageReflection( @@ -744,7 +772,8 @@ const Message* DynamicMessageFactory::GetPrototypeNoLock( type_info->pool, this, type_info->size, - -1 /* arena_offset */)); + -1 /* arena_offset */, + type_info->is_default_instance_offset)); } // Cross link prototypes. prototype->CrossLinkPrototypes(); diff --git a/src/google/protobuf/dynamic_message_unittest.cc b/src/google/protobuf/dynamic_message_unittest.cc index 6353ecbf..522a092a 100644 --- a/src/google/protobuf/dynamic_message_unittest.cc +++ b/src/google/protobuf/dynamic_message_unittest.cc @@ -46,6 +46,7 @@ #include <google/protobuf/descriptor.pb.h> #include <google/protobuf/test_util.h> #include <google/protobuf/unittest.pb.h> +#include <google/protobuf/unittest_no_field_presence.pb.h> #include <google/protobuf/testing/googletest.h> #include <gtest/gtest.h> @@ -65,6 +66,8 @@ class DynamicMessageTest : public testing::Test { const Message* packed_prototype_; const Descriptor* oneof_descriptor_; const Message* oneof_prototype_; + const Descriptor* proto3_descriptor_; + const Message* proto3_prototype_; DynamicMessageTest(): factory_(&pool_) {} @@ -76,16 +79,20 @@ class DynamicMessageTest : public testing::Test { FileDescriptorProto unittest_file; FileDescriptorProto unittest_import_file; FileDescriptorProto unittest_import_public_file; + FileDescriptorProto unittest_no_field_presence_file; unittest::TestAllTypes::descriptor()->file()->CopyTo(&unittest_file); unittest_import::ImportMessage::descriptor()->file()->CopyTo( &unittest_import_file); unittest_import::PublicImportMessage::descriptor()->file()->CopyTo( &unittest_import_public_file); + proto2_nofieldpresence_unittest::TestAllTypes::descriptor()-> + file()->CopyTo(&unittest_no_field_presence_file); ASSERT_TRUE(pool_.BuildFile(unittest_import_public_file) != NULL); ASSERT_TRUE(pool_.BuildFile(unittest_import_file) != NULL); ASSERT_TRUE(pool_.BuildFile(unittest_file) != NULL); + ASSERT_TRUE(pool_.BuildFile(unittest_no_field_presence_file) != NULL); descriptor_ = pool_.FindMessageTypeByName("protobuf_unittest.TestAllTypes"); ASSERT_TRUE(descriptor_ != NULL); @@ -105,6 +112,12 @@ class DynamicMessageTest : public testing::Test { pool_.FindMessageTypeByName("protobuf_unittest.TestOneof2"); ASSERT_TRUE(oneof_descriptor_ != NULL); oneof_prototype_ = factory_.GetPrototype(oneof_descriptor_); + + proto3_descriptor_ = + pool_.FindMessageTypeByName( + "proto2_nofieldpresence_unittest.TestAllTypes"); + ASSERT_TRUE(proto3_descriptor_ != NULL); + proto3_prototype_ = factory_.GetPrototype(proto3_descriptor_); } }; @@ -233,6 +246,40 @@ TEST_F(DynamicMessageTest, Arena) { // Return without freeing: should not leak. } +TEST_F(DynamicMessageTest, Proto3) { + Message* message = proto3_prototype_->New(); + const Reflection* refl = message->GetReflection(); + const Descriptor* desc = message->GetDescriptor(); + + // Just test a single primtive and single message field here to make sure we + // are getting the no-field-presence semantics elsewhere. DynamicMessage uses + // GeneratedMessageReflection under the hood, so the rest should be fine as + // long as GMR recognizes that we're using a proto3 message. + const FieldDescriptor* optional_int32 = + desc->FindFieldByName("optional_int32"); + const FieldDescriptor* optional_msg = + desc->FindFieldByName("optional_nested_message"); + EXPECT_TRUE(optional_int32 != NULL); + EXPECT_TRUE(optional_msg != NULL); + + EXPECT_EQ(false, refl->HasField(*message, optional_int32)); + refl->SetInt32(message, optional_int32, 42); + EXPECT_EQ(true, refl->HasField(*message, optional_int32)); + refl->SetInt32(message, optional_int32, 0); + EXPECT_EQ(false, refl->HasField(*message, optional_int32)); + + EXPECT_EQ(false, refl->HasField(*message, optional_msg)); + refl->MutableMessage(message, optional_msg); + EXPECT_EQ(true, refl->HasField(*message, optional_msg)); + delete refl->ReleaseMessage(message, optional_msg); + EXPECT_EQ(false, refl->HasField(*message, optional_msg)); + + // Also ensure that the default instance handles field presence properly. + EXPECT_EQ(false, refl->HasField(*proto3_prototype_, optional_msg)); + + delete message; +} + } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/generated_message_reflection.cc b/src/google/protobuf/generated_message_reflection.cc index a20e362c..b4e98acd 100644 --- a/src/google/protobuf/generated_message_reflection.cc +++ b/src/google/protobuf/generated_message_reflection.cc @@ -193,7 +193,8 @@ GeneratedMessageReflection::GeneratedMessageReflection( const DescriptorPool* descriptor_pool, MessageFactory* factory, int object_size, - int arena_offset) + int arena_offset, + int is_default_instance_offset) : descriptor_ (descriptor), default_instance_ (default_instance), offsets_ (offsets), @@ -201,6 +202,7 @@ GeneratedMessageReflection::GeneratedMessageReflection( unknown_fields_offset_(unknown_fields_offset), extensions_offset_(extensions_offset), arena_offset_ (arena_offset), + is_default_instance_offset_(is_default_instance_offset), object_size_ (object_size), descriptor_pool_ ((descriptor_pool == NULL) ? DescriptorPool::generated_pool() : @@ -220,7 +222,8 @@ GeneratedMessageReflection::GeneratedMessageReflection( const DescriptorPool* descriptor_pool, MessageFactory* factory, int object_size, - int arena_offset) + int arena_offset, + int is_default_instance_offset) : descriptor_ (descriptor), default_instance_ (default_instance), default_oneof_instance_ (default_oneof_instance), @@ -230,6 +233,7 @@ GeneratedMessageReflection::GeneratedMessageReflection( unknown_fields_offset_(unknown_fields_offset), extensions_offset_(extensions_offset), arena_offset_ (arena_offset), + is_default_instance_offset_(is_default_instance_offset), object_size_ (object_size), descriptor_pool_ ((descriptor_pool == NULL) ? DescriptorPool::generated_pool() : @@ -1829,6 +1833,17 @@ GeneratedMessageReflection::MutableInternalMetadataWithArena( return reinterpret_cast<InternalMetadataWithArena*>(ptr); } +inline bool +GeneratedMessageReflection::GetIsDefaultInstance( + const Message& message) const { + if (is_default_instance_offset_ == kHasNoDefaultInstanceField) { + return false; + } + const void* ptr = reinterpret_cast<const uint8*>(&message) + + is_default_instance_offset_; + return *reinterpret_cast<const bool*>(ptr); +} + // Simple accessors for manipulating has_bits_. inline bool GeneratedMessageReflection::HasBit( const Message& message, const FieldDescriptor* field) const { @@ -1836,7 +1851,8 @@ inline bool GeneratedMessageReflection::HasBit( // proto3: no has-bits. All fields present except messages, which are // present only if their message-field pointer is non-NULL. if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { - return GetRaw<const Message*>(message, field) != NULL; + return !GetIsDefaultInstance(message) && + GetRaw<const Message*>(message, field) != NULL; } else { // Non-message field (and non-oneof, since that was handled in HasField() // before calling us), and singular (again, checked in HasField). So, this @@ -2082,7 +2098,8 @@ GeneratedMessageReflection::NewGeneratedMessageReflection( const void* default_oneof_instance, int oneof_case_offset, int object_size, - int arena_offset) { + int arena_offset, + int is_default_instance_offset) { return new GeneratedMessageReflection(descriptor, default_instance, offsets, @@ -2094,7 +2111,8 @@ GeneratedMessageReflection::NewGeneratedMessageReflection( DescriptorPool::generated_pool(), MessageFactory::generated_factory(), object_size, - arena_offset); + arena_offset, + is_default_instance_offset); } GeneratedMessageReflection* @@ -2106,7 +2124,8 @@ GeneratedMessageReflection::NewGeneratedMessageReflection( int unknown_fields_offset, int extensions_offset, int object_size, - int arena_offset) { + int arena_offset, + int is_default_instance_offset) { return new GeneratedMessageReflection(descriptor, default_instance, offsets, @@ -2116,7 +2135,8 @@ GeneratedMessageReflection::NewGeneratedMessageReflection( DescriptorPool::generated_pool(), MessageFactory::generated_factory(), object_size, - arena_offset); + arena_offset, + is_default_instance_offset); } } // namespace internal diff --git a/src/google/protobuf/generated_message_reflection.h b/src/google/protobuf/generated_message_reflection.h index 306809da..4dddf6c7 100644 --- a/src/google/protobuf/generated_message_reflection.h +++ b/src/google/protobuf/generated_message_reflection.h @@ -136,7 +136,8 @@ class LIBPROTOBUF_EXPORT GeneratedMessageReflection : public Reflection { const DescriptorPool* pool, MessageFactory* factory, int object_size, - int arena_offset); + int arena_offset, + int is_default_instance_offset = -1); // Similar with the construction above. Call this construction if the // message has oneof definition. @@ -173,7 +174,8 @@ class LIBPROTOBUF_EXPORT GeneratedMessageReflection : public Reflection { const DescriptorPool* pool, MessageFactory* factory, int object_size, - int arena_offset); + int arena_offset, + int is_default_instance_offset = -1); ~GeneratedMessageReflection(); // Shorter-to-call helpers for the above two constructions that work if the @@ -190,7 +192,8 @@ class LIBPROTOBUF_EXPORT GeneratedMessageReflection : public Reflection { const void* default_oneof_instance, int oneof_case_offset, int object_size, - int arena_offset); + int arena_offset, + int is_default_instance_offset = -1); static GeneratedMessageReflection* NewGeneratedMessageReflection( const Descriptor* descriptor, @@ -200,7 +203,8 @@ class LIBPROTOBUF_EXPORT GeneratedMessageReflection : public Reflection { int unknown_fields_offset, int extensions_offset, int object_size, - int arena_offset); + int arena_offset, + int is_default_instance_offset = -1); // implements Reflection ------------------------------------------- @@ -414,8 +418,11 @@ class LIBPROTOBUF_EXPORT GeneratedMessageReflection : public Reflection { int unknown_fields_offset_; int extensions_offset_; int arena_offset_; + int is_default_instance_offset_; int object_size_; + static const int kHasNoDefaultInstanceField = -1; + const DescriptorPool* descriptor_pool_; MessageFactory* message_factory_; @@ -446,6 +453,8 @@ class LIBPROTOBUF_EXPORT GeneratedMessageReflection : public Reflection { inline internal::InternalMetadataWithArena* MutableInternalMetadataWithArena(Message* message) const; + inline bool GetIsDefaultInstance(const Message& message) const; + inline bool HasBit(const Message& message, const FieldDescriptor* field) const; inline void SetBit(Message* message, diff --git a/src/google/protobuf/no_field_presence_test.cc b/src/google/protobuf/no_field_presence_test.cc index f248327c..4b7b31d9 100644 --- a/src/google/protobuf/no_field_presence_test.cc +++ b/src/google/protobuf/no_field_presence_test.cc @@ -269,6 +269,10 @@ TEST(NoFieldPresenceTest, MessageFieldPresenceTest) { EXPECT_EQ(true, message.has_optional_lazy_message()); message.clear_optional_lazy_message(); EXPECT_EQ(false, message.has_optional_lazy_message()); + + // Test field presence of a message field on the default instance. + EXPECT_EQ(false, proto2_nofieldpresence_unittest::TestAllTypes:: + default_instance().has_optional_nested_message()); } TEST(NoFieldPresenceTest, ReflectionHasFieldTest) { @@ -287,6 +291,13 @@ TEST(NoFieldPresenceTest, ReflectionHasFieldTest) { EXPECT_EQ(false, r->HasField(message, field)); } + // Test field presence of a message field on the default instance. + const google::protobuf::FieldDescriptor* msg_field = + desc->FindFieldByName("optional_nested_message"); + EXPECT_EQ(false, r->HasField( + proto2_nofieldpresence_unittest::TestAllTypes:: + default_instance(), msg_field)); + // Fill all fields, expect everything to report true (check oneofs below). FillValues(&message); for (int i = 0; i < desc->field_count(); i++) { |