diff options
Diffstat (limited to 'src/google/protobuf/compiler/java/java_file.cc')
-rw-r--r-- | src/google/protobuf/compiler/java/java_file.cc | 432 |
1 files changed, 269 insertions, 163 deletions
diff --git a/src/google/protobuf/compiler/java/java_file.cc b/src/google/protobuf/compiler/java/java_file.cc index 7ea127c..5b165b1 100644 --- a/src/google/protobuf/compiler/java/java_file.cc +++ b/src/google/protobuf/compiler/java/java_file.cc @@ -33,15 +33,23 @@ // Sanjay Ghemawat, Jeff Dean, and others. #include <google/protobuf/compiler/java/java_file.h> + +#include <memory> + +#include <google/protobuf/compiler/java/java_context.h> #include <google/protobuf/compiler/java/java_enum.h> -#include <google/protobuf/compiler/java/java_service.h> #include <google/protobuf/compiler/java/java_extension.h> +#include <google/protobuf/compiler/java/java_generator_factory.h> #include <google/protobuf/compiler/java/java_helpers.h> #include <google/protobuf/compiler/java/java_message.h> +#include <google/protobuf/compiler/java/java_name_resolver.h> +#include <google/protobuf/compiler/java/java_service.h> +#include <google/protobuf/compiler/java/java_shared_code_generator.h> #include <google/protobuf/compiler/code_generator.h> #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 +59,24 @@ namespace java { 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,25 +84,83 @@ bool UsesExtensions(const Message& message) { 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) - : file_(file), - java_package_(FileJavaPackage(file)), - classname_(FileClassName(file)) {} +FileGenerator::FileGenerator(const FileDescriptor* file, bool immutable_api) + : file_(file), + java_package_(FileJavaPackage(file, immutable_api)), + message_generators_( + new scoped_ptr<MessageGenerator>[file->message_type_count()]), + extension_generators_( + new scoped_ptr<ExtensionGenerator>[file->extension_count()]), + context_(new Context(file)), + name_resolver_(context_->GetNameResolver()), + immutable_api_(immutable_api) { + classname_ = name_resolver_->GetFileClassName(file, immutable_api); + generator_factory_.reset( + new ImmutableGeneratorFactory(context_.get())); + for (int i = 0; i < file_->message_type_count(); ++i) { + message_generators_[i].reset( + generator_factory_->NewMessageGenerator(file_->message_type(i))); + } + for (int i = 0; i < file_->extension_count(); ++i) { + extension_generators_[i].reset( + generator_factory_->NewExtensionGenerator(file_->extension(i))); + } +} FileGenerator::~FileGenerator() {} @@ -97,25 +169,7 @@ bool FileGenerator::Validate(string* error) { // problem that leads to Java compile errors that can be hard to understand. // It's especially bad when using the java_multiple_files, since we would // end up overwriting the outer class with one of the inner ones. - - bool found_conflict = false; - for (int i = 0; i < file_->enum_type_count() && !found_conflict; i++) { - if (file_->enum_type(i)->name() == classname_) { - found_conflict = true; - } - } - for (int i = 0; i < file_->message_type_count() && !found_conflict; i++) { - if (file_->message_type(i)->name() == classname_) { - found_conflict = true; - } - } - for (int i = 0; i < file_->service_count() && !found_conflict; i++) { - if (file_->service(i)->name() == classname_) { - found_conflict = true; - } - } - - if (found_conflict) { + if (name_resolver_->HasConflictingClassName(file_, classname_)) { error->assign(file_->name()); error->append( ": Cannot generate Java output because the file's outer class name, \""); @@ -126,7 +180,29 @@ bool FileGenerator::Validate(string* error) { "option to specify a different outer class name for the .proto file."); return false; } - + // If java_outer_classname option is not set and the default outer class name + // conflicts with a type defined in the message, we will append a suffix to + // avoid the conflict. This allows proto1 API protos to be dual-compiled into + // proto2 API without code change. When this happens we'd like to issue an + // warning to let the user know that the outer class name has been changed. + // Although we only do this automatic naming fix for immutable API, mutable + // outer class name will also be affected as it's contructed from immutable + // outer class name with an additional "Mutable" prefix. Since the naming + // change in mutable API is not caused by a naming conflict, we generate the + // warning for immutable API only. + if (immutable_api_ && !file_->options().has_java_outer_classname()) { + string default_classname = + name_resolver_->GetFileDefaultImmutableClassName(file_); + if (default_classname != classname_) { + GOOGLE_LOG(WARNING) << file_->name() << ": The default outer class name, \"" + << default_classname << "\", conflicts with a type " + << "declared in the proto file and an alternative outer " + << "class name is used: \"" << classname_ << "\". To avoid " + << "this warning, please use the java_outer_classname " + << "option to specify a different outer class name for " + << "the .proto file."; + } + } return true; } @@ -160,12 +236,11 @@ void FileGenerator::Generate(io::Printer* printer) { printer->Indent(); for (int i = 0; i < file_->extension_count(); i++) { - ExtensionGenerator(file_->extension(i)).GenerateRegistrationCode(printer); + extension_generators_[i]->GenerateRegistrationCode(printer); } for (int i = 0; i < file_->message_type_count(); i++) { - MessageGenerator(file_->message_type(i)) - .GenerateExtensionRegistrationCode(printer); + message_generators_[i]->GenerateExtensionRegistrationCode(printer); } printer->Outdent(); @@ -174,16 +249,20 @@ void FileGenerator::Generate(io::Printer* printer) { // ----------------------------------------------------------------- - if (!file_->options().java_multiple_files()) { + if (!MultipleJavaFiles(file_, immutable_api_)) { for (int i = 0; i < file_->enum_type_count(); i++) { - EnumGenerator(file_->enum_type(i)).Generate(printer); + EnumGenerator(file_->enum_type(i), immutable_api_, context_.get()) + .Generate(printer); } for (int i = 0; i < file_->message_type_count(); i++) { - MessageGenerator(file_->message_type(i)).Generate(printer); + message_generators_[i]->GenerateInterface(printer); + message_generators_[i]->Generate(printer); } if (HasGenericServices(file_)) { for (int i = 0; i < file_->service_count(); i++) { - ServiceGenerator(file_->service(i)).Generate(printer); + scoped_ptr<ServiceGenerator> generator( + generator_factory_->NewServiceGenerator(file_->service(i))); + generator->Generate(printer); } } } @@ -191,34 +270,29 @@ void FileGenerator::Generate(io::Printer* printer) { // Extensions must be generated in the outer class since they are values, // not classes. for (int i = 0; i < file_->extension_count(); i++) { - ExtensionGenerator(file_->extension(i)).Generate(printer); + extension_generators_[i]->Generate(printer); } // Static variables. for (int i = 0; i < file_->message_type_count(); i++) { - // TODO(kenton): Reuse MessageGenerator objects? - MessageGenerator(file_->message_type(i)).GenerateStaticVariables(printer); + message_generators_[i]->GenerateStaticVariables(printer); } printer->Print("\n"); if (HasDescriptorMethods(file_)) { - GenerateEmbeddedDescriptor(printer); + if (immutable_api_) { + GenerateDescriptorInitializationCodeForImmutable(printer); + } else { + GenerateDescriptorInitializationCodeForMutable(printer); + } } else { printer->Print( "static {\n"); printer->Indent(); for (int i = 0; i < file_->message_type_count(); i++) { - // TODO(kenton): Reuse MessageGenerator objects? - MessageGenerator(file_->message_type(i)) - .GenerateStaticVariableInitializers(printer); - } - - for (int i = 0; i < file_->extension_count(); i++) { - // TODO(kenton): Reuse ExtensionGenerator objects? - ExtensionGenerator(file_->extension(i)) - .GenerateInitializationCode(printer); + message_generators_[i]->GenerateStaticVariableInitializers(printer); } printer->Outdent(); @@ -226,13 +300,6 @@ void FileGenerator::Generate(io::Printer* printer) { "}\n"); } - // Dummy function we can use to force the static initialization block to - // run. Needed by inner classes. Cannot be private due to - // java_multiple_files option. - printer->Print( - "\n" - "public static void internalForceInit() {}\n"); - printer->Print( "\n" "// @@protoc_insertion_point(outer_class_scope)\n"); @@ -241,23 +308,8 @@ void FileGenerator::Generate(io::Printer* printer) { printer->Print("}\n"); } -void FileGenerator::GenerateEmbeddedDescriptor(io::Printer* printer) { - // Embed the descriptor. We simply serialize the entire FileDescriptorProto - // and embed it as a string literal, which is parsed and built into real - // descriptors at initialization time. We unfortunately have to put it in - // a string literal, not a byte array, because apparently using a literal - // byte array causes the Java compiler to generate *instructions* to - // initialize each and every byte of the array, e.g. as if you typed: - // b[0] = 123; b[1] = 456; b[2] = 789; - // This makes huge bytecode files and can easily hit the compiler's internal - // code size limits (error "code to large"). String literals are apparently - // embedded raw, which is what we want. - FileDescriptorProto file_proto; - file_->CopyTo(&file_proto); - - string file_data; - file_proto.SerializeToString(&file_data); - +void FileGenerator::GenerateDescriptorInitializationCodeForImmutable( + io::Printer* printer) { printer->Print( "public static com.google.protobuf.Descriptors.FileDescriptor\n" " getDescriptor() {\n" @@ -265,104 +317,135 @@ void FileGenerator::GenerateEmbeddedDescriptor(io::Printer* printer) { "}\n" "private static com.google.protobuf.Descriptors.FileDescriptor\n" " descriptor;\n" - "static {\n" - " java.lang.String[] descriptorData = {\n"); - printer->Indent(); + "static {\n"); printer->Indent(); - // Only write 40 bytes per line. - static const int kBytesPerLine = 40; - for (int i = 0; i < file_data.size(); i += kBytesPerLine) { - if (i > 0) { - // Every 400 lines, start a new string literal, in order to avoid the - // 64k length limit. - if (i % 400 == 0) { - printer->Print(",\n"); - } else { - printer->Print(" +\n"); - } - } - printer->Print("\"$data$\"", - "data", CEscape(file_data.substr(i, kBytesPerLine))); - } - - printer->Outdent(); - printer->Print("\n};\n"); - - // ----------------------------------------------------------------- - // Create the InternalDescriptorAssigner. - - printer->Print( - "com.google.protobuf.Descriptors.FileDescriptor." - "InternalDescriptorAssigner assigner =\n" - " new com.google.protobuf.Descriptors.FileDescriptor." - "InternalDescriptorAssigner() {\n" - " public com.google.protobuf.ExtensionRegistry assignDescriptors(\n" - " com.google.protobuf.Descriptors.FileDescriptor root) {\n" - " descriptor = root;\n"); - - printer->Indent(); - printer->Indent(); - printer->Indent(); + SharedCodeGenerator shared_code_generator(file_); + shared_code_generator.GenerateDescriptors(printer); for (int i = 0; i < file_->message_type_count(); i++) { - // TODO(kenton): Reuse MessageGenerator objects? - MessageGenerator(file_->message_type(i)) - .GenerateStaticVariableInitializers(printer); + message_generators_[i]->GenerateStaticVariableInitializers(printer); } - for (int i = 0; i < file_->extension_count(); i++) { - // TODO(kenton): Reuse ExtensionGenerator objects? - ExtensionGenerator(file_->extension(i)) - .GenerateInitializationCode(printer); + extension_generators_[i]->GenerateNonNestedInitializationCode(printer); } - if (UsesExtensions(file_proto)) { - // Must construct an ExtensionRegistry containing all possible extensions - // and return it. + // 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 + FileDescriptorProto file_proto; + file_->CopyTo(&file_proto); + string file_data; + file_proto.SerializeToString(&file_data); + 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 use it to parse the descriptor data again to recognize extensions. 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++) { - 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++) { + scoped_ptr<ExtensionGenerator> generator( + generator_factory_->NewExtensionGenerator(extensions[i])); + generator->GenerateRegistrationCode(printer); } printer->Print( - "return registry;\n"); - } else { - printer->Print( - "return null;\n"); + "com.google.protobuf.Descriptors.FileDescriptor\n" + " .internalUpdateFileDescriptor(descriptor, registry);\n"); } - printer->Outdent(); - printer->Outdent(); - printer->Outdent(); + // Force descriptor initialization of all dependencies. + for (int i = 0; i < file_->dependency_count(); i++) { + if (ShouldIncludeDependency(file_->dependency(i), true)) { + string dependency = + name_resolver_->GetImmutableClassName(file_->dependency(i)); + printer->Print( + "$dependency$.getDescriptor();\n", + "dependency", dependency); + } + } + printer->Outdent(); printer->Print( - " }\n" - " };\n"); + "}\n"); +} - // ----------------------------------------------------------------- - // Invoke internalBuildGeneratedFileFrom() to build the file. +void FileGenerator::GenerateDescriptorInitializationCodeForMutable(io::Printer* printer) { + printer->Print( + "public static com.google.protobuf.Descriptors.FileDescriptor\n" + " getDescriptor() {\n" + " return descriptor;\n" + "}\n" + "private static com.google.protobuf.Descriptors.FileDescriptor\n" + " descriptor;\n" + "static {\n"); + printer->Indent(); printer->Print( - "com.google.protobuf.Descriptors.FileDescriptor\n" - " .internalBuildGeneratedFileFrom(descriptorData,\n" - " new com.google.protobuf.Descriptors.FileDescriptor[] {\n"); + "descriptor = $immutable_package$.$descriptor_classname$.descriptor;\n", + "immutable_package", FileJavaPackage(file_, true), + "descriptor_classname", name_resolver_->GetDescriptorClassName(file_)); + + for (int i = 0; i < file_->message_type_count(); i++) { + message_generators_[i]->GenerateStaticVariableInitializers(printer); + } + for (int i = 0; i < file_->extension_count(); i++) { + extension_generators_[i]->GenerateNonNestedInitializationCode(printer); + } + + // Check if custom options exist. If any, try to load immutable classes since + // custom options are only represented with immutable messages. + FileDescriptorProto file_proto; + file_->CopyTo(&file_proto); + string file_data; + file_proto.SerializeToString(&file_data); + vector<const FieldDescriptor*> extensions; + CollectExtensions(file_proto, *file_->pool(), &extensions, file_data); + + if (extensions.size() > 0) { + // Try to load immutable messages' outer class. Its initialization code + // will take care of interpreting custom options. + printer->Print( + "try {\n" + // Note that we have to load the immutable class dynamically here as + // we want the mutable code to be independent from the immutable code + // at compile time. It is required to implement dual-compile for + // mutable and immutable API in blaze. + " java.lang.Class immutableClass = java.lang.Class.forName(\n" + " \"$immutable_classname$\");\n" + "} catch (java.lang.ClassNotFoundException e) {\n" + // The immutable class can not be found. Custom options are left + // as unknown fields. + // TODO(xiaofeng): inform the user with a warning? + "}\n", + "immutable_classname", name_resolver_->GetImmutableClassName(file_)); + } + // Force descriptor initialization of all dependencies. for (int i = 0; i < file_->dependency_count(); i++) { - if (ShouldIncludeDependency(file_->dependency(i))) { + if (ShouldIncludeDependency(file_->dependency(i), false)) { + string dependency = name_resolver_->GetMutableClassName( + file_->dependency(i)); printer->Print( - " $dependency$.getDescriptor(),\n", - "dependency", ClassName(file_->dependency(i))); + "$dependency$.getDescriptor();\n", + "dependency", dependency); } } - printer->Print( - " }, assigner);\n"); - printer->Outdent(); printer->Print( "}\n"); @@ -372,18 +455,22 @@ template<typename GeneratorClass, typename DescriptorClass> static void GenerateSibling(const string& package_dir, const string& java_package, const DescriptorClass* descriptor, - OutputDirectory* output_directory, - vector<string>* file_list) { - string filename = package_dir + descriptor->name() + ".java"; + GeneratorContext* context, + vector<string>* file_list, + const string& name_suffix, + GeneratorClass* generator, + void (GeneratorClass::*pfn)(io::Printer* printer)) { + string filename = package_dir + descriptor->name() + name_suffix + ".java"; file_list->push_back(filename); - scoped_ptr<io::ZeroCopyOutputStream> output( - output_directory->Open(filename)); + scoped_ptr<io::ZeroCopyOutputStream> output(context->Open(filename)); io::Printer printer(output.get(), '$'); 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" @@ -391,34 +478,53 @@ static void GenerateSibling(const string& package_dir, "package", java_package); } - GeneratorClass(descriptor).Generate(&printer); + (generator->*pfn)(&printer); } void FileGenerator::GenerateSiblings(const string& package_dir, - OutputDirectory* output_directory, + GeneratorContext* context, vector<string>* file_list) { - if (file_->options().java_multiple_files()) { + if (MultipleJavaFiles(file_, immutable_api_)) { for (int i = 0; i < file_->enum_type_count(); i++) { + EnumGenerator generator(file_->enum_type(i), immutable_api_, + context_.get()); GenerateSibling<EnumGenerator>(package_dir, java_package_, file_->enum_type(i), - output_directory, file_list); + context, file_list, "", + &generator, + &EnumGenerator::Generate); } for (int i = 0; i < file_->message_type_count(); i++) { + if (immutable_api_) { + GenerateSibling<MessageGenerator>(package_dir, java_package_, + file_->message_type(i), + context, file_list, + "OrBuilder", + message_generators_[i].get(), + &MessageGenerator::GenerateInterface); + } GenerateSibling<MessageGenerator>(package_dir, java_package_, file_->message_type(i), - output_directory, file_list); + context, file_list, "", + message_generators_[i].get(), + &MessageGenerator::Generate); } if (HasGenericServices(file_)) { for (int i = 0; i < file_->service_count(); i++) { + scoped_ptr<ServiceGenerator> generator( + generator_factory_->NewServiceGenerator(file_->service(i))); GenerateSibling<ServiceGenerator>(package_dir, java_package_, file_->service(i), - output_directory, file_list); + context, file_list, "", + generator.get(), + &ServiceGenerator::Generate); } } } } -bool FileGenerator::ShouldIncludeDependency(const FileDescriptor* descriptor) { +bool FileGenerator::ShouldIncludeDependency( + const FileDescriptor* descriptor, bool immutable_api) { return true; } |