3131#include " google/protobuf/compiler/java/internal_helpers.h"
3232#include " google/protobuf/compiler/java/lite/generator_factory.h"
3333#include " google/protobuf/compiler/java/name_resolver.h"
34+ #include " google/protobuf/compiler/java/names.h"
3435#include " google/protobuf/compiler/java/options.h"
3536#include " google/protobuf/compiler/java/shared_code_generator.h"
3637#include " google/protobuf/compiler/retention.h"
@@ -116,8 +117,9 @@ void CollectPublicDependencies(
116117
117118// Finds all extensions for custom options in the given file descriptor with the
118119// builder pool which resolves Java features instead of the generated pool.
119- void CollectExtensions (const FileDescriptor& file,
120- FieldDescriptorSet* extensions) {
120+ void CollectExtensions (const FileDescriptor& file, const Options& options,
121+ FieldDescriptorSet* extensions,
122+ FieldDescriptorSet* optional_extensions) {
121123 FileDescriptorProto file_proto = StripSourceRetentionOptions (file);
122124 std::string file_data;
123125 file_proto.SerializeToString (&file_data);
@@ -139,15 +141,27 @@ void CollectExtensions(const FileDescriptor& file,
139141 // Unknown extensions are ok and expected in the case of option imports.
140142 CollectExtensions (*dynamic_file_proto, extensions);
141143
142- // TODO Check against dependencies to remove option dependencies
143- // polluting the pool. These will be handled as optional dependencies.
144+ if (options.strip_nonfunctional_codegen ) {
145+ // Skip feature extensions, which are a visible (but non-functional)
146+ // deviation between editions and legacy syntax.
147+ absl::erase_if (*extensions, [](const FieldDescriptor* field) {
148+ return field->containing_type ()->full_name () == " google.protobuf.FeatureSet" ;
149+ });
150+ }
151+
152+ // Check against dependencies to handle option dependencies polluting pool.
144153 absl::flat_hash_set<const FileDescriptor*> dependencies;
145154 dependencies.insert (&file);
146155 for (int i = 0 ; i < file.dependency_count (); i++) {
147156 CollectPublicDependencies (file.dependency (i), &dependencies);
148157 }
158+ for (auto * extension : *extensions) {
159+ if (!dependencies.contains (extension->file ())) {
160+ optional_extensions->insert (extension);
161+ }
162+ }
149163 absl::erase_if (*extensions, [&](const FieldDescriptor* fieldDescriptor) {
150- return !dependencies. contains (fieldDescriptor-> file () );
164+ return optional_extensions-> contains (fieldDescriptor );
151165 });
152166}
153167
@@ -325,11 +339,14 @@ void FileGenerator::Generate(io::Printer* printer) {
325339 printer->Print (" @com.google.protobuf.Internal.ProtoNonnullApi\n " );
326340 }
327341 printer->Print (
328- " $deprecation$public final class $classname$ {\n "
342+ " $deprecation$public final class $classname$ $extends$ {\n "
329343 " private $ctor$() {}\n " ,
330344 " deprecation" ,
331345 file_->options ().deprecated () ? " @java.lang.Deprecated " : " " ,
332- " classname" , classname_, " ctor" , classname_);
346+ " classname" , classname_, " ctor" , classname_, " extends" ,
347+ HasDescriptorMethods (file_, context_->EnforceLite ())
348+ ? " extends com.google.protobuf.GeneratedFile "
349+ : " " );
333350 printer->Annotate (" classname" , file_->name ());
334351 printer->Indent ();
335352
@@ -507,15 +524,8 @@ void FileGenerator::GenerateDescriptorInitializationCodeForImmutable(
507524 // of the FileDescriptor based on the builder-pool, then we can use
508525 // reflections to find all extension fields
509526 FieldDescriptorSet extensions;
510- CollectExtensions (*file_, &extensions);
511-
512- if (options_.strip_nonfunctional_codegen ) {
513- // Skip feature extensions, which are a visible (but non-functional)
514- // deviation between editions and legacy syntax.
515- absl::erase_if (extensions, [](const FieldDescriptor* field) {
516- return field->containing_type ()->full_name () == " google.protobuf.FeatureSet" ;
517- });
518- }
527+ FieldDescriptorSet optional_extensions;
528+ CollectExtensions (*file_, options_, &extensions, &optional_extensions);
519529
520530 // Force descriptor initialization of all dependencies.
521531 for (int i = 0 ; i < file_->dependency_count (); i++) {
@@ -527,7 +537,7 @@ void FileGenerator::GenerateDescriptorInitializationCodeForImmutable(
527537 }
528538 }
529539
530- if (!extensions.empty ()) {
540+ if (!extensions.empty () || !optional_extensions. empty () ) {
531541 // Must construct an ExtensionRegistry containing all existing extensions
532542 // and use it to parse the descriptor data again to recognize extensions.
533543 printer->Print (
@@ -544,6 +554,25 @@ void FileGenerator::GenerateDescriptorInitializationCodeForImmutable(
544554 " private static void _clinit_autosplit_dinit_$method_num$(\n "
545555 " com.google.protobuf.ExtensionRegistry registry) {\n " );
546556 }
557+ for (const FieldDescriptor* field : optional_extensions) {
558+ std::unique_ptr<ExtensionGenerator> generator (
559+ generator_factory_->NewExtensionGenerator (field));
560+ printer->Emit ({{" scope" , field->extension_scope () != nullptr
561+ ? name_resolver_->GetImmutableClassName (
562+ field->extension_scope ())
563+ : name_resolver_->GetImmutableClassName (
564+ field->file ())},
565+ {" name" , UnderscoresToCamelCaseCheckReserved (field)}},
566+ R"java(
567+ addOptionalExtension(registry, "$scope$", "$name$");
568+ )java" );
569+ bytecode_estimate += 8 ;
570+ MaybeRestartJavaMethod (
571+ printer, &bytecode_estimate, &method_num,
572+ " _clinit_autosplit_dinit_$method_num$(registry);\n " ,
573+ " private static void _clinit_autosplit_dinit_$method_num$(\n "
574+ " com.google.protobuf.ExtensionRegistry registry) {\n " );
575+ }
547576 printer->Print (
548577 " com.google.protobuf.Descriptors.FileDescriptor\n "
549578 " .internalUpdateFileDescriptor(descriptor, registry);\n " );
0 commit comments