1 | // Protocol Buffers - Google's data interchange format |
2 | // Copyright 2008 Google Inc. All rights reserved. |
3 | // https://developers.google.com/protocol-buffers/ |
4 | // |
5 | // Redistribution and use in source and binary forms, with or without |
6 | // modification, are permitted provided that the following conditions are |
7 | // met: |
8 | // |
9 | // * Redistributions of source code must retain the above copyright |
10 | // notice, this list of conditions and the following disclaimer. |
11 | // * Redistributions in binary form must reproduce the above |
12 | // copyright notice, this list of conditions and the following disclaimer |
13 | // in the documentation and/or other materials provided with the |
14 | // distribution. |
15 | // * Neither the name of Google Inc. nor the names of its |
16 | // contributors may be used to endorse or promote products derived from |
17 | // this software without specific prior written permission. |
18 | // |
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 | |
31 | // Author: kenton@google.com (Kenton Varda) |
32 | // Based on original Protocol Buffers design by |
33 | // Sanjay Ghemawat, Jeff Dean, and others. |
34 | |
35 | #include <google/protobuf/compiler/java/file.h> |
36 | |
37 | #include <memory> |
38 | #include <set> |
39 | |
40 | #include <google/protobuf/compiler/code_generator.h> |
41 | #include <google/protobuf/io/printer.h> |
42 | #include <google/protobuf/io/zero_copy_stream.h> |
43 | #include <google/protobuf/dynamic_message.h> |
44 | #include <google/protobuf/stubs/strutil.h> |
45 | #include <google/protobuf/compiler/java/context.h> |
46 | #include <google/protobuf/compiler/java/enum.h> |
47 | #include <google/protobuf/compiler/java/enum_lite.h> |
48 | #include <google/protobuf/compiler/java/extension.h> |
49 | #include <google/protobuf/compiler/java/generator_factory.h> |
50 | #include <google/protobuf/compiler/java/helpers.h> |
51 | #include <google/protobuf/compiler/java/message.h> |
52 | #include <google/protobuf/compiler/java/name_resolver.h> |
53 | #include <google/protobuf/compiler/java/service.h> |
54 | #include <google/protobuf/compiler/java/shared_code_generator.h> |
55 | #include <google/protobuf/descriptor.pb.h> |
56 | |
57 | // Must be last. |
58 | #include <google/protobuf/port_def.inc> |
59 | |
60 | namespace google { |
61 | namespace protobuf { |
62 | namespace compiler { |
63 | namespace java { |
64 | |
65 | namespace { |
66 | |
67 | struct FieldDescriptorCompare { |
68 | bool operator()(const FieldDescriptor* f1, const FieldDescriptor* f2) const { |
69 | if (f1 == NULL) { |
70 | return false; |
71 | } |
72 | if (f2 == NULL) { |
73 | return true; |
74 | } |
75 | return f1->full_name() < f2->full_name(); |
76 | } |
77 | }; |
78 | |
79 | typedef std::set<const FieldDescriptor*, FieldDescriptorCompare> |
80 | FieldDescriptorSet; |
81 | |
82 | // Recursively searches the given message to collect extensions. |
83 | // Returns true if all the extensions can be recognized. The extensions will be |
84 | // appended in to the extensions parameter. |
85 | // Returns false when there are unknown fields, in which case the data in the |
86 | // extensions output parameter is not reliable and should be discarded. |
87 | bool CollectExtensions(const Message& message, FieldDescriptorSet* extensions) { |
88 | const Reflection* reflection = message.GetReflection(); |
89 | |
90 | // There are unknown fields that could be extensions, thus this call fails. |
91 | if (reflection->GetUnknownFields(message).field_count() > 0) return false; |
92 | |
93 | std::vector<const FieldDescriptor*> fields; |
94 | reflection->ListFields(message, output: &fields); |
95 | |
96 | for (int i = 0; i < fields.size(); i++) { |
97 | if (fields[i]->is_extension()) { |
98 | extensions->insert(x: fields[i]); |
99 | } |
100 | |
101 | if (GetJavaType(field: fields[i]) == JAVATYPE_MESSAGE) { |
102 | if (fields[i]->is_repeated()) { |
103 | int size = reflection->FieldSize(message, field: fields[i]); |
104 | for (int j = 0; j < size; j++) { |
105 | const Message& sub_message = |
106 | reflection->GetRepeatedMessage(message, field: fields[i], index: j); |
107 | if (!CollectExtensions(message: sub_message, extensions)) return false; |
108 | } |
109 | } else { |
110 | const Message& sub_message = reflection->GetMessage(message, field: fields[i]); |
111 | if (!CollectExtensions(message: sub_message, extensions)) return false; |
112 | } |
113 | } |
114 | } |
115 | |
116 | return true; |
117 | } |
118 | |
119 | // Finds all extensions in the given message and its sub-messages. If the |
120 | // message contains unknown fields (which could be extensions), then those |
121 | // extensions are defined in alternate_pool. |
122 | // The message will be converted to a DynamicMessage backed by alternate_pool |
123 | // in order to handle this case. |
124 | void CollectExtensions(const FileDescriptorProto& file_proto, |
125 | const DescriptorPool& alternate_pool, |
126 | FieldDescriptorSet* extensions, |
127 | const std::string& file_data) { |
128 | if (!CollectExtensions(message: file_proto, extensions)) { |
129 | // There are unknown fields in the file_proto, which are probably |
130 | // extensions. We need to parse the data into a dynamic message based on the |
131 | // builder-pool to find out all extensions. |
132 | const Descriptor* file_proto_desc = alternate_pool.FindMessageTypeByName( |
133 | name: file_proto.GetDescriptor()->full_name()); |
134 | GOOGLE_CHECK(file_proto_desc) |
135 | << "Find unknown fields in FileDescriptorProto when building " |
136 | << file_proto.name() |
137 | << ". It's likely that those fields are custom options, however, " |
138 | "descriptor.proto is not in the transitive dependencies. " |
139 | "This normally should not happen. Please report a bug." ; |
140 | DynamicMessageFactory factory; |
141 | std::unique_ptr<Message> dynamic_file_proto( |
142 | factory.GetPrototype(type: file_proto_desc)->New()); |
143 | GOOGLE_CHECK(dynamic_file_proto.get() != NULL); |
144 | GOOGLE_CHECK(dynamic_file_proto->ParseFromString(file_data)); |
145 | |
146 | // Collect the extensions again from the dynamic message. There should be no |
147 | // more unknown fields this time, i.e. all the custom options should be |
148 | // parsed as extensions now. |
149 | extensions->clear(); |
150 | GOOGLE_CHECK(CollectExtensions(*dynamic_file_proto, extensions)) |
151 | << "Find unknown fields in FileDescriptorProto when building " |
152 | << file_proto.name() |
153 | << ". It's likely that those fields are custom options, however, " |
154 | "those options cannot be recognized in the builder pool. " |
155 | "This normally should not happen. Please report a bug." ; |
156 | } |
157 | } |
158 | |
159 | // Our static initialization methods can become very, very large. |
160 | // So large that if we aren't careful we end up blowing the JVM's |
161 | // 64K bytes of bytecode/method. Fortunately, since these static |
162 | // methods are executed only once near the beginning of a program, |
163 | // there's usually plenty of stack space available and we can |
164 | // extend our methods by simply chaining them to another method |
165 | // with a tail call. This inserts the sequence call-next-method, |
166 | // end this one, begin-next-method as needed. |
167 | void MaybeRestartJavaMethod(io::Printer* printer, int* bytecode_estimate, |
168 | int* method_num, const char* chain_statement, |
169 | const char* method_decl) { |
170 | // The goal here is to stay under 64K bytes of jvm bytecode/method, |
171 | // since otherwise we hit a hardcoded limit in the jvm and javac will |
172 | // then fail with the error "code too large". This limit lets our |
173 | // estimates be off by a factor of two and still we're okay. |
174 | static const int bytesPerMethod = kMaxStaticSize; |
175 | |
176 | if ((*bytecode_estimate) > bytesPerMethod) { |
177 | ++(*method_num); |
178 | printer->Print(text: chain_statement, args: "method_num" , args: StrCat(a: *method_num)); |
179 | printer->Outdent(); |
180 | printer->Print(text: "}\n" ); |
181 | printer->Print(text: method_decl, args: "method_num" , args: StrCat(a: *method_num)); |
182 | printer->Indent(); |
183 | *bytecode_estimate = 0; |
184 | } |
185 | } |
186 | } // namespace |
187 | |
188 | FileGenerator::FileGenerator(const FileDescriptor* file, const Options& options, |
189 | bool immutable_api) |
190 | : file_(file), |
191 | java_package_(FileJavaPackage(file, immutable: immutable_api)), |
192 | message_generators_(file->message_type_count()), |
193 | extension_generators_(file->extension_count()), |
194 | context_(new Context(file, options)), |
195 | name_resolver_(context_->GetNameResolver()), |
196 | options_(options), |
197 | immutable_api_(immutable_api) { |
198 | classname_ = name_resolver_->GetFileClassName(file, immutable: immutable_api); |
199 | generator_factory_.reset(p: new ImmutableGeneratorFactory(context_.get())); |
200 | for (int i = 0; i < file_->message_type_count(); ++i) { |
201 | message_generators_[i].reset( |
202 | p: generator_factory_->NewMessageGenerator(descriptor: file_->message_type(index: i))); |
203 | } |
204 | for (int i = 0; i < file_->extension_count(); ++i) { |
205 | extension_generators_[i].reset( |
206 | p: generator_factory_->NewExtensionGenerator(descriptor: file_->extension(index: i))); |
207 | } |
208 | } |
209 | |
210 | FileGenerator::~FileGenerator() {} |
211 | |
212 | bool FileGenerator::Validate(std::string* error) { |
213 | // Check that no class name matches the file's class name. This is a common |
214 | // problem that leads to Java compile errors that can be hard to understand. |
215 | // It's especially bad when using the java_multiple_files, since we would |
216 | // end up overwriting the outer class with one of the inner ones. |
217 | if (name_resolver_->HasConflictingClassName(file: file_, classname: classname_, |
218 | equality_mode: NameEquality::EXACT_EQUAL)) { |
219 | error->assign(str: file_->name()); |
220 | error->append( |
221 | s: ": Cannot generate Java output because the file's outer class name, " |
222 | "\"" ); |
223 | error->append(str: classname_); |
224 | error->append( |
225 | s: "\", matches the name of one of the types declared inside it. " |
226 | "Please either rename the type or use the java_outer_classname " |
227 | "option to specify a different outer class name for the .proto file." ); |
228 | return false; |
229 | } |
230 | // Similar to the check above, but ignore the case this time. This is not a |
231 | // problem on Linux, but will lead to Java compile errors on Windows / Mac |
232 | // because filenames are case-insensitive on those platforms. |
233 | if (name_resolver_->HasConflictingClassName( |
234 | file: file_, classname: classname_, equality_mode: NameEquality::EQUAL_IGNORE_CASE)) { |
235 | GOOGLE_LOG(WARNING) |
236 | << file_->name() << ": The file's outer class name, \"" << classname_ |
237 | << "\", matches the name of one of the types declared inside it when " |
238 | << "case is ignored. This can cause compilation issues on Windows / " |
239 | << "MacOS. Please either rename the type or use the " |
240 | << "java_outer_classname option to specify a different outer class " |
241 | << "name for the .proto file to be safe." ; |
242 | } |
243 | |
244 | // Print a warning if optimize_for = LITE_RUNTIME is used. |
245 | if (file_->options().optimize_for() == FileOptions::LITE_RUNTIME && |
246 | !options_.enforce_lite) { |
247 | GOOGLE_LOG(WARNING) |
248 | << "The optimize_for = LITE_RUNTIME option is no longer supported by " |
249 | << "protobuf Java code generator and is ignored--protoc will always " |
250 | << "generate full runtime code for Java. To use Java Lite runtime, " |
251 | << "users should use the Java Lite plugin instead. See:\n" |
252 | << " " |
253 | "https://github.com/protocolbuffers/protobuf/blob/main/java/" |
254 | "lite.md" ; |
255 | } |
256 | return true; |
257 | } |
258 | |
259 | void FileGenerator::Generate(io::Printer* printer) { |
260 | // We don't import anything because we refer to all classes by their |
261 | // fully-qualified names in the generated source. |
262 | printer->Print( |
263 | text: "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" |
264 | "// source: $filename$\n" |
265 | "\n" , |
266 | args: "filename" , args: file_->name()); |
267 | if (!java_package_.empty()) { |
268 | printer->Print( |
269 | text: "package $package$;\n" |
270 | "\n" , |
271 | args: "package" , args: java_package_); |
272 | } |
273 | PrintGeneratedAnnotation( |
274 | printer, delimiter: '$', annotation_file: options_.annotate_code ? classname_ + ".java.pb.meta" : "" ); |
275 | |
276 | printer->Print( |
277 | text: "$deprecation$public final class $classname$ {\n" |
278 | " private $ctor$() {}\n" , |
279 | args: "deprecation" , |
280 | args: file_->options().deprecated() ? "@java.lang.Deprecated " : "" , |
281 | args: "classname" , args: classname_, args: "ctor" , args: classname_); |
282 | printer->Annotate(varname: "classname" , file_name: file_->name()); |
283 | printer->Indent(); |
284 | |
285 | // ----------------------------------------------------------------- |
286 | |
287 | printer->Print( |
288 | text: "public static void registerAllExtensions(\n" |
289 | " com.google.protobuf.ExtensionRegistryLite registry) {\n" ); |
290 | |
291 | printer->Indent(); |
292 | |
293 | for (int i = 0; i < file_->extension_count(); i++) { |
294 | extension_generators_[i]->GenerateRegistrationCode(printer); |
295 | } |
296 | |
297 | for (int i = 0; i < file_->message_type_count(); i++) { |
298 | message_generators_[i]->GenerateExtensionRegistrationCode(printer); |
299 | } |
300 | |
301 | printer->Outdent(); |
302 | printer->Print(text: "}\n" ); |
303 | if (HasDescriptorMethods(file_, enforce_lite: context_->EnforceLite())) { |
304 | // Overload registerAllExtensions for the non-lite usage to |
305 | // redundantly maintain the original signature (this is |
306 | // redundant because ExtensionRegistryLite now invokes |
307 | // ExtensionRegistry in the non-lite usage). Intent is |
308 | // to remove this in the future. |
309 | printer->Print( |
310 | text: "\n" |
311 | "public static void registerAllExtensions(\n" |
312 | " com.google.protobuf.ExtensionRegistry registry) {\n" |
313 | " registerAllExtensions(\n" |
314 | " (com.google.protobuf.ExtensionRegistryLite) registry);\n" |
315 | "}\n" ); |
316 | } |
317 | |
318 | // ----------------------------------------------------------------- |
319 | |
320 | if (!MultipleJavaFiles(descriptor: file_, immutable: immutable_api_)) { |
321 | for (int i = 0; i < file_->enum_type_count(); i++) { |
322 | if (HasDescriptorMethods(file_, enforce_lite: context_->EnforceLite())) { |
323 | EnumGenerator(file_->enum_type(index: i), immutable_api_, context_.get()) |
324 | .Generate(printer); |
325 | } else { |
326 | EnumLiteGenerator(file_->enum_type(index: i), immutable_api_, context_.get()) |
327 | .Generate(printer); |
328 | } |
329 | } |
330 | for (int i = 0; i < file_->message_type_count(); i++) { |
331 | message_generators_[i]->GenerateInterface(printer); |
332 | message_generators_[i]->Generate(printer); |
333 | } |
334 | if (HasGenericServices(file: file_, enforce_lite: context_->EnforceLite())) { |
335 | for (int i = 0; i < file_->service_count(); i++) { |
336 | std::unique_ptr<ServiceGenerator> generator( |
337 | generator_factory_->NewServiceGenerator(descriptor: file_->service(index: i))); |
338 | generator->Generate(printer); |
339 | } |
340 | } |
341 | } |
342 | |
343 | // Extensions must be generated in the outer class since they are values, |
344 | // not classes. |
345 | for (int i = 0; i < file_->extension_count(); i++) { |
346 | extension_generators_[i]->Generate(printer); |
347 | } |
348 | |
349 | // Static variables. We'd like them to be final if possible, but due to |
350 | // the JVM's 64k size limit on static blocks, we have to initialize some |
351 | // of them in methods; thus they cannot be final. |
352 | int static_block_bytecode_estimate = 0; |
353 | for (int i = 0; i < file_->message_type_count(); i++) { |
354 | message_generators_[i]->GenerateStaticVariables( |
355 | printer, bytecode_estimate: &static_block_bytecode_estimate); |
356 | } |
357 | |
358 | printer->Print(text: "\n" ); |
359 | |
360 | if (HasDescriptorMethods(file_, enforce_lite: context_->EnforceLite())) { |
361 | if (immutable_api_) { |
362 | GenerateDescriptorInitializationCodeForImmutable(printer); |
363 | } else { |
364 | GenerateDescriptorInitializationCodeForMutable(printer); |
365 | } |
366 | } else { |
367 | printer->Print(text: "static {\n" ); |
368 | printer->Indent(); |
369 | int bytecode_estimate = 0; |
370 | int method_num = 0; |
371 | |
372 | for (int i = 0; i < file_->message_type_count(); i++) { |
373 | bytecode_estimate += |
374 | message_generators_[i]->GenerateStaticVariableInitializers(printer); |
375 | MaybeRestartJavaMethod( |
376 | printer, bytecode_estimate: &bytecode_estimate, method_num: &method_num, |
377 | chain_statement: "_clinit_autosplit_$method_num$();\n" , |
378 | method_decl: "private static void _clinit_autosplit_$method_num$() {\n" ); |
379 | } |
380 | |
381 | printer->Outdent(); |
382 | printer->Print(text: "}\n" ); |
383 | } |
384 | |
385 | printer->Print( |
386 | text: "\n" |
387 | "// @@protoc_insertion_point(outer_class_scope)\n" ); |
388 | |
389 | printer->Outdent(); |
390 | printer->Print(text: "}\n" ); |
391 | } |
392 | |
393 | void FileGenerator::GenerateDescriptorInitializationCodeForImmutable( |
394 | io::Printer* printer) { |
395 | printer->Print( |
396 | text: "public static com.google.protobuf.Descriptors.FileDescriptor\n" |
397 | " getDescriptor() {\n" |
398 | " return descriptor;\n" |
399 | "}\n" |
400 | "private static $final$ com.google.protobuf.Descriptors.FileDescriptor\n" |
401 | " descriptor;\n" |
402 | "static {\n" , |
403 | // TODO(dweis): Mark this as final. |
404 | args: "final" , args: "" ); |
405 | printer->Indent(); |
406 | |
407 | SharedCodeGenerator shared_code_generator(file_, options_); |
408 | shared_code_generator.GenerateDescriptors(printer); |
409 | |
410 | int bytecode_estimate = 0; |
411 | int method_num = 0; |
412 | |
413 | for (int i = 0; i < file_->message_type_count(); i++) { |
414 | bytecode_estimate += |
415 | message_generators_[i]->GenerateStaticVariableInitializers(printer); |
416 | MaybeRestartJavaMethod( |
417 | printer, bytecode_estimate: &bytecode_estimate, method_num: &method_num, |
418 | chain_statement: "_clinit_autosplit_dinit_$method_num$();\n" , |
419 | method_decl: "private static void _clinit_autosplit_dinit_$method_num$() {\n" ); |
420 | } |
421 | for (int i = 0; i < file_->extension_count(); i++) { |
422 | bytecode_estimate += |
423 | extension_generators_[i]->GenerateNonNestedInitializationCode(printer); |
424 | MaybeRestartJavaMethod( |
425 | printer, bytecode_estimate: &bytecode_estimate, method_num: &method_num, |
426 | chain_statement: "_clinit_autosplit_dinit_$method_num$();\n" , |
427 | method_decl: "private static void _clinit_autosplit_dinit_$method_num$() {\n" ); |
428 | } |
429 | |
430 | // Proto compiler builds a DescriptorPool, which holds all the descriptors to |
431 | // generate, when processing the ".proto" files. We call this DescriptorPool |
432 | // the parsed pool (a.k.a. file_->pool()). |
433 | // |
434 | // Note that when users try to extend the (.*)DescriptorProto in their |
435 | // ".proto" files, it does not affect the pre-built FileDescriptorProto class |
436 | // in proto compiler. When we put the descriptor data in the file_proto, those |
437 | // extensions become unknown fields. |
438 | // |
439 | // Now we need to find out all the extension value to the (.*)DescriptorProto |
440 | // in the file_proto message, and prepare an ExtensionRegistry to return. |
441 | // |
442 | // To find those extensions, we need to parse the data into a dynamic message |
443 | // of the FileDescriptor based on the builder-pool, then we can use |
444 | // reflections to find all extension fields |
445 | FileDescriptorProto file_proto; |
446 | file_->CopyTo(proto: &file_proto); |
447 | std::string file_data; |
448 | file_proto.SerializeToString(output: &file_data); |
449 | FieldDescriptorSet extensions; |
450 | CollectExtensions(file_proto, alternate_pool: *file_->pool(), extensions: &extensions, file_data); |
451 | |
452 | if (extensions.size() > 0) { |
453 | // Must construct an ExtensionRegistry containing all existing extensions |
454 | // and use it to parse the descriptor data again to recognize extensions. |
455 | printer->Print( |
456 | text: "com.google.protobuf.ExtensionRegistry registry =\n" |
457 | " com.google.protobuf.ExtensionRegistry.newInstance();\n" ); |
458 | FieldDescriptorSet::iterator it; |
459 | for (it = extensions.begin(); it != extensions.end(); it++) { |
460 | std::unique_ptr<ExtensionGenerator> generator( |
461 | generator_factory_->NewExtensionGenerator(descriptor: *it)); |
462 | bytecode_estimate += generator->GenerateRegistrationCode(printer); |
463 | MaybeRestartJavaMethod( |
464 | printer, bytecode_estimate: &bytecode_estimate, method_num: &method_num, |
465 | chain_statement: "_clinit_autosplit_dinit_$method_num$(registry);\n" , |
466 | method_decl: "private static void _clinit_autosplit_dinit_$method_num$(\n" |
467 | " com.google.protobuf.ExtensionRegistry registry) {\n" ); |
468 | } |
469 | printer->Print( |
470 | text: "com.google.protobuf.Descriptors.FileDescriptor\n" |
471 | " .internalUpdateFileDescriptor(descriptor, registry);\n" ); |
472 | } |
473 | |
474 | // Force descriptor initialization of all dependencies. |
475 | for (int i = 0; i < file_->dependency_count(); i++) { |
476 | if (ShouldIncludeDependency(descriptor: file_->dependency(index: i), immutable_api_: true)) { |
477 | std::string dependency = |
478 | name_resolver_->GetImmutableClassName(descriptor: file_->dependency(index: i)); |
479 | printer->Print(text: "$dependency$.getDescriptor();\n" , args: "dependency" , |
480 | args: dependency); |
481 | } |
482 | } |
483 | |
484 | printer->Outdent(); |
485 | printer->Print(text: "}\n" ); |
486 | } |
487 | |
488 | void FileGenerator::GenerateDescriptorInitializationCodeForMutable( |
489 | io::Printer* printer) { |
490 | printer->Print( |
491 | text: "public static com.google.protobuf.Descriptors.FileDescriptor\n" |
492 | " getDescriptor() {\n" |
493 | " return descriptor;\n" |
494 | "}\n" |
495 | "private static final com.google.protobuf.Descriptors.FileDescriptor\n" |
496 | " descriptor;\n" |
497 | "static {\n" ); |
498 | printer->Indent(); |
499 | |
500 | printer->Print( |
501 | text: "descriptor = $immutable_package$.$descriptor_classname$.descriptor;\n" , |
502 | args: "immutable_package" , args: FileJavaPackage(file: file_, immutable: true), args: "descriptor_classname" , |
503 | args: name_resolver_->GetDescriptorClassName(file: file_)); |
504 | |
505 | for (int i = 0; i < file_->message_type_count(); i++) { |
506 | message_generators_[i]->GenerateStaticVariableInitializers(printer); |
507 | } |
508 | for (int i = 0; i < file_->extension_count(); i++) { |
509 | extension_generators_[i]->GenerateNonNestedInitializationCode(printer); |
510 | } |
511 | |
512 | // Check if custom options exist. If any, try to load immutable classes since |
513 | // custom options are only represented with immutable messages. |
514 | FileDescriptorProto file_proto; |
515 | file_->CopyTo(proto: &file_proto); |
516 | std::string file_data; |
517 | file_proto.SerializeToString(output: &file_data); |
518 | FieldDescriptorSet extensions; |
519 | CollectExtensions(file_proto, alternate_pool: *file_->pool(), extensions: &extensions, file_data); |
520 | |
521 | if (extensions.size() > 0) { |
522 | // Try to load immutable messages' outer class. Its initialization code |
523 | // will take care of interpreting custom options. |
524 | printer->Print( |
525 | text: "try {\n" |
526 | // Note that we have to load the immutable class dynamically here as |
527 | // we want the mutable code to be independent from the immutable code |
528 | // at compile time. It is required to implement dual-compile for |
529 | // mutable and immutable API in blaze. |
530 | " java.lang.Class<?> immutableClass = java.lang.Class.forName(\n" |
531 | " \"$immutable_classname$\");\n" |
532 | "} catch (java.lang.ClassNotFoundException e) {\n" , |
533 | args: "immutable_classname" , args: name_resolver_->GetImmutableClassName(descriptor: file_)); |
534 | printer->Indent(); |
535 | |
536 | // The immutable class can not be found. We try our best to collect all |
537 | // custom option extensions to interpret the custom options. |
538 | printer->Print( |
539 | text: "com.google.protobuf.ExtensionRegistry registry =\n" |
540 | " com.google.protobuf.ExtensionRegistry.newInstance();\n" |
541 | "com.google.protobuf.MessageLite defaultExtensionInstance = null;\n" ); |
542 | FieldDescriptorSet::iterator it; |
543 | for (it = extensions.begin(); it != extensions.end(); it++) { |
544 | const FieldDescriptor* field = *it; |
545 | std::string scope; |
546 | if (field->extension_scope() != NULL) { |
547 | scope = name_resolver_->GetMutableClassName(descriptor: field->extension_scope()) + |
548 | ".getDescriptor()" ; |
549 | } else { |
550 | scope = FileJavaPackage(file: field->file(), immutable: true) + "." + |
551 | name_resolver_->GetDescriptorClassName(file: field->file()) + |
552 | ".descriptor" ; |
553 | } |
554 | if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { |
555 | printer->Print( |
556 | text: "defaultExtensionInstance = com.google.protobuf.Internal\n" |
557 | " .getDefaultInstance(\"$class$\");\n" |
558 | "if (defaultExtensionInstance != null) {\n" |
559 | " registry.add(\n" |
560 | " $scope$.getExtensions().get($index$),\n" |
561 | " (com.google.protobuf.Message) defaultExtensionInstance);\n" |
562 | "}\n" , |
563 | args: "scope" , args: scope, args: "index" , args: StrCat(a: field->index()), args: "class" , |
564 | args: name_resolver_->GetImmutableClassName(descriptor: field->message_type())); |
565 | } else { |
566 | printer->Print(text: "registry.add($scope$.getExtensions().get($index$));\n" , |
567 | args: "scope" , args: scope, args: "index" , args: StrCat(a: field->index())); |
568 | } |
569 | } |
570 | printer->Print( |
571 | text: "com.google.protobuf.Descriptors.FileDescriptor\n" |
572 | " .internalUpdateFileDescriptor(descriptor, registry);\n" ); |
573 | |
574 | printer->Outdent(); |
575 | printer->Print(text: "}\n" ); |
576 | } |
577 | |
578 | // Force descriptor initialization of all dependencies. |
579 | for (int i = 0; i < file_->dependency_count(); i++) { |
580 | if (ShouldIncludeDependency(descriptor: file_->dependency(index: i), immutable_api_: false)) { |
581 | std::string dependency = |
582 | name_resolver_->GetMutableClassName(descriptor: file_->dependency(index: i)); |
583 | printer->Print(text: "$dependency$.getDescriptor();\n" , args: "dependency" , |
584 | args: dependency); |
585 | } |
586 | } |
587 | |
588 | printer->Outdent(); |
589 | printer->Print(text: "}\n" ); |
590 | } |
591 | |
592 | template <typename GeneratorClass, typename DescriptorClass> |
593 | static void GenerateSibling( |
594 | const std::string& package_dir, const std::string& java_package, |
595 | const DescriptorClass* descriptor, GeneratorContext* context, |
596 | std::vector<std::string>* file_list, bool annotate_code, |
597 | std::vector<std::string>* annotation_list, const std::string& name_suffix, |
598 | GeneratorClass* generator, |
599 | void (GeneratorClass::*pfn)(io::Printer* printer)) { |
600 | std::string filename = |
601 | package_dir + descriptor->name() + name_suffix + ".java" ; |
602 | file_list->push_back(x: filename); |
603 | std::string info_full_path = filename + ".pb.meta" ; |
604 | GeneratedCodeInfo annotations; |
605 | io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector( |
606 | &annotations); |
607 | |
608 | std::unique_ptr<io::ZeroCopyOutputStream> output(context->Open(filename)); |
609 | io::Printer printer(output.get(), '$', |
610 | annotate_code ? &annotation_collector : NULL); |
611 | |
612 | printer.Print( |
613 | "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" |
614 | "// source: $filename$\n" |
615 | "\n" , |
616 | "filename" , descriptor->file()->name()); |
617 | if (!java_package.empty()) { |
618 | printer.Print( |
619 | text: "package $package$;\n" |
620 | "\n" , |
621 | args: "package" , args: java_package); |
622 | } |
623 | |
624 | (generator->*pfn)(&printer); |
625 | |
626 | if (annotate_code) { |
627 | std::unique_ptr<io::ZeroCopyOutputStream> info_output( |
628 | context->Open(filename: info_full_path)); |
629 | annotations.SerializeToZeroCopyStream(output: info_output.get()); |
630 | annotation_list->push_back(x: info_full_path); |
631 | } |
632 | } |
633 | |
634 | void FileGenerator::GenerateSiblings( |
635 | const std::string& package_dir, GeneratorContext* context, |
636 | std::vector<std::string>* file_list, |
637 | std::vector<std::string>* annotation_list) { |
638 | if (MultipleJavaFiles(descriptor: file_, immutable: immutable_api_)) { |
639 | for (int i = 0; i < file_->enum_type_count(); i++) { |
640 | if (HasDescriptorMethods(file_, enforce_lite: context_->EnforceLite())) { |
641 | EnumGenerator generator(file_->enum_type(index: i), immutable_api_, |
642 | context_.get()); |
643 | GenerateSibling<EnumGenerator>( |
644 | package_dir, java_package: java_package_, descriptor: file_->enum_type(index: i), context, file_list, |
645 | annotate_code: options_.annotate_code, annotation_list, name_suffix: "" , generator: &generator, |
646 | pfn: &EnumGenerator::Generate); |
647 | } else { |
648 | EnumLiteGenerator generator(file_->enum_type(index: i), immutable_api_, |
649 | context_.get()); |
650 | GenerateSibling<EnumLiteGenerator>( |
651 | package_dir, java_package: java_package_, descriptor: file_->enum_type(index: i), context, file_list, |
652 | annotate_code: options_.annotate_code, annotation_list, name_suffix: "" , generator: &generator, |
653 | pfn: &EnumLiteGenerator::Generate); |
654 | } |
655 | } |
656 | for (int i = 0; i < file_->message_type_count(); i++) { |
657 | if (immutable_api_) { |
658 | GenerateSibling<MessageGenerator>( |
659 | package_dir, java_package: java_package_, descriptor: file_->message_type(index: i), context, |
660 | file_list, annotate_code: options_.annotate_code, annotation_list, name_suffix: "OrBuilder" , |
661 | generator: message_generators_[i].get(), pfn: &MessageGenerator::GenerateInterface); |
662 | } |
663 | GenerateSibling<MessageGenerator>( |
664 | package_dir, java_package: java_package_, descriptor: file_->message_type(index: i), context, |
665 | file_list, annotate_code: options_.annotate_code, annotation_list, name_suffix: "" , |
666 | generator: message_generators_[i].get(), pfn: &MessageGenerator::Generate); |
667 | } |
668 | if (HasGenericServices(file: file_, enforce_lite: context_->EnforceLite())) { |
669 | for (int i = 0; i < file_->service_count(); i++) { |
670 | std::unique_ptr<ServiceGenerator> generator( |
671 | generator_factory_->NewServiceGenerator(descriptor: file_->service(index: i))); |
672 | GenerateSibling<ServiceGenerator>( |
673 | package_dir, java_package: java_package_, descriptor: file_->service(index: i), context, file_list, |
674 | annotate_code: options_.annotate_code, annotation_list, name_suffix: "" , generator: generator.get(), |
675 | pfn: &ServiceGenerator::Generate); |
676 | } |
677 | } |
678 | } |
679 | } |
680 | |
681 | std::string FileGenerator::GetKotlinClassname() { |
682 | return name_resolver_->GetFileClassName(file: file_, immutable: immutable_api_, kotlin: true); |
683 | } |
684 | |
685 | void FileGenerator::GenerateKotlinSiblings( |
686 | const std::string& package_dir, GeneratorContext* context, |
687 | std::vector<std::string>* file_list, |
688 | std::vector<std::string>* annotation_list) { |
689 | for (int i = 0; i < file_->message_type_count(); i++) { |
690 | const Descriptor* descriptor = file_->message_type(index: i); |
691 | MessageGenerator* generator = message_generators_[i].get(); |
692 | auto open_file = [context](const std::string& filename) { |
693 | return std::unique_ptr<io::ZeroCopyOutputStream>(context->Open(filename)); |
694 | }; |
695 | std::string filename = package_dir + descriptor->name() + "Kt.kt" ; |
696 | file_list->push_back(x: filename); |
697 | std::string info_full_path = filename + ".pb.meta" ; |
698 | GeneratedCodeInfo annotations; |
699 | io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector( |
700 | &annotations); |
701 | auto output = open_file(filename); |
702 | io::Printer printer( |
703 | output.get(), '$', |
704 | options_.annotate_code ? &annotation_collector : nullptr); |
705 | |
706 | printer.Print( |
707 | text: "//Generated by the protocol buffer compiler. DO NOT EDIT!\n" |
708 | "// source: $filename$\n" |
709 | "\n" , |
710 | args: "filename" , args: descriptor->file()->name()); |
711 | if (!java_package_.empty()) { |
712 | printer.Print( |
713 | text: "package $package$;\n" |
714 | "\n" , |
715 | args: "package" , args: java_package_); |
716 | } |
717 | |
718 | generator->GenerateKotlinMembers(printer: &printer); |
719 | generator->GenerateTopLevelKotlinMembers(printer: &printer); |
720 | |
721 | if (options_.annotate_code) { |
722 | auto info_output = open_file(info_full_path); |
723 | annotations.SerializeToZeroCopyStream(output: info_output.get()); |
724 | annotation_list->push_back(x: info_full_path); |
725 | } |
726 | } |
727 | } |
728 | |
729 | bool FileGenerator::ShouldIncludeDependency(const FileDescriptor* descriptor, |
730 | bool immutable_api) { |
731 | return true; |
732 | } |
733 | |
734 | } // namespace java |
735 | } // namespace compiler |
736 | } // namespace protobuf |
737 | } // namespace google |
738 | |
739 | #include <google/protobuf/port_undef.inc> |
740 | |