Index: third_party/protobuf/src/google/protobuf/compiler/java/java_file.cc |
=================================================================== |
--- third_party/protobuf/src/google/protobuf/compiler/java/java_file.cc (revision 216642) |
+++ third_party/protobuf/src/google/protobuf/compiler/java/java_file.cc (working copy) |
@@ -42,6 +42,7 @@ |
#include <google/protobuf/io/printer.h> |
#include <google/protobuf/io/zero_copy_stream.h> |
#include <google/protobuf/descriptor.pb.h> |
+#include <google/protobuf/dynamic_message.h> |
#include <google/protobuf/stubs/strutil.h> |
namespace google { |
@@ -51,18 +52,24 @@ |
namespace { |
-// Recursively searches the given message to see if it contains any extensions. |
-bool UsesExtensions(const Message& message) { |
+ |
+// Recursively searches the given message to collect extensions. |
+// Returns true if all the extensions can be recognized. The extensions will be |
+// appended in to the extensions parameter. |
+// Returns false when there are unknown fields, in which case the data in the |
+// extensions output parameter is not reliable and should be discarded. |
+bool CollectExtensions(const Message& message, |
+ vector<const FieldDescriptor*>* extensions) { |
const Reflection* reflection = message.GetReflection(); |
- // We conservatively assume that unknown fields are extensions. |
- if (reflection->GetUnknownFields(message).field_count() > 0) return true; |
+ // There are unknown fields that could be extensions, thus this call fails. |
+ if (reflection->GetUnknownFields(message).field_count() > 0) return false; |
vector<const FieldDescriptor*> fields; |
reflection->ListFields(message, &fields); |
for (int i = 0; i < fields.size(); i++) { |
- if (fields[i]->is_extension()) return true; |
+ if (fields[i]->is_extension()) extensions->push_back(fields[i]); |
if (GetJavaType(fields[i]) == JAVATYPE_MESSAGE) { |
if (fields[i]->is_repeated()) { |
@@ -70,19 +77,59 @@ |
for (int j = 0; j < size; j++) { |
const Message& sub_message = |
reflection->GetRepeatedMessage(message, fields[i], j); |
- if (UsesExtensions(sub_message)) return true; |
+ if (!CollectExtensions(sub_message, extensions)) return false; |
} |
} else { |
const Message& sub_message = reflection->GetMessage(message, fields[i]); |
- if (UsesExtensions(sub_message)) return true; |
+ if (!CollectExtensions(sub_message, extensions)) return false; |
} |
} |
} |
- return false; |
+ return true; |
} |
+// Finds all extensions in the given message and its sub-messages. If the |
+// message contains unknown fields (which could be extensions), then those |
+// extensions are defined in alternate_pool. |
+// The message will be converted to a DynamicMessage backed by alternate_pool |
+// in order to handle this case. |
+void CollectExtensions(const FileDescriptorProto& file_proto, |
+ const DescriptorPool& alternate_pool, |
+ vector<const FieldDescriptor*>* extensions, |
+ const string& file_data) { |
+ if (!CollectExtensions(file_proto, extensions)) { |
+ // There are unknown fields in the file_proto, which are probably |
+ // extensions. We need to parse the data into a dynamic message based on the |
+ // builder-pool to find out all extensions. |
+ const Descriptor* file_proto_desc = alternate_pool.FindMessageTypeByName( |
+ file_proto.GetDescriptor()->full_name()); |
+ GOOGLE_CHECK(file_proto_desc) |
+ << "Find unknown fields in FileDescriptorProto when building " |
+ << file_proto.name() |
+ << ". It's likely that those fields are custom options, however, " |
+ "descriptor.proto is not in the transitive dependencies. " |
+ "This normally should not happen. Please report a bug."; |
+ DynamicMessageFactory factory; |
+ scoped_ptr<Message> dynamic_file_proto( |
+ factory.GetPrototype(file_proto_desc)->New()); |
+ GOOGLE_CHECK(dynamic_file_proto.get() != NULL); |
+ GOOGLE_CHECK(dynamic_file_proto->ParseFromString(file_data)); |
+ // Collect the extensions again from the dynamic message. There should be no |
+ // more unknown fields this time, i.e. all the custom options should be |
+ // parsed as extensions now. |
+ extensions->clear(); |
+ GOOGLE_CHECK(CollectExtensions(*dynamic_file_proto, extensions)) |
+ << "Find unknown fields in FileDescriptorProto when building " |
+ << file_proto.name() |
+ << ". It's likely that those fields are custom options, however, " |
+ "those options cannot be recognized in the builder pool. " |
+ "This normally should not happen. Please report a bug."; |
+ } |
+} |
+ |
+ |
} // namespace |
FileGenerator::FileGenerator(const FileDescriptor* file) |
@@ -306,19 +353,32 @@ |
.GenerateNonNestedInitializationCode(printer); |
} |
- if (UsesExtensions(file_proto)) { |
- // Must construct an ExtensionRegistry containing all possible extensions |
+ // Proto compiler builds a DescriptorPool, which holds all the descriptors to |
+ // generate, when processing the ".proto" files. We call this DescriptorPool |
+ // the parsed pool (a.k.a. file_->pool()). |
+ // |
+ // Note that when users try to extend the (.*)DescriptorProto in their |
+ // ".proto" files, it does not affect the pre-built FileDescriptorProto class |
+ // in proto compiler. When we put the descriptor data in the file_proto, those |
+ // extensions become unknown fields. |
+ // |
+ // Now we need to find out all the extension value to the (.*)DescriptorProto |
+ // in the file_proto message, and prepare an ExtensionRegistry to return. |
+ // |
+ // To find those extensions, we need to parse the data into a dynamic message |
+ // of the FileDescriptor based on the builder-pool, then we can use |
+ // reflections to find all extension fields |
+ vector<const FieldDescriptor*> extensions; |
+ CollectExtensions(file_proto, *file_->pool(), &extensions, file_data); |
+ |
+ if (extensions.size() > 0) { |
+ // Must construct an ExtensionRegistry containing all existing extensions |
// and return it. |
printer->Print( |
"com.google.protobuf.ExtensionRegistry registry =\n" |
- " com.google.protobuf.ExtensionRegistry.newInstance();\n" |
- "registerAllExtensions(registry);\n"); |
- for (int i = 0; i < file_->dependency_count(); i++) { |
- if (ShouldIncludeDependency(file_->dependency(i))) { |
- printer->Print( |
- "$dependency$.registerAllExtensions(registry);\n", |
- "dependency", ClassName(file_->dependency(i))); |
- } |
+ " com.google.protobuf.ExtensionRegistry.newInstance();\n"); |
+ for (int i = 0; i < extensions.size(); i++) { |
+ ExtensionGenerator(extensions[i]).GenerateRegistrationCode(printer); |
} |
printer->Print( |
"return registry;\n"); |
@@ -375,7 +435,9 @@ |
printer.Print( |
"// Generated by the protocol buffer compiler. DO NOT EDIT!\n" |
- "\n"); |
+ "// source: $filename$\n" |
+ "\n", |
+ "filename", descriptor->file()->name()); |
if (!java_package.empty()) { |
printer.Print( |
"package $package$;\n" |