• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 // Author: kenton@google.com (Kenton Varda)
9 //  Based on original Protocol Buffers design by
10 //  Sanjay Ghemawat, Jeff Dean, and others.
11 
12 #include "google/protobuf/compiler/cpp/generator.h"
13 
14 #include <cstdlib>
15 #include <cstring>
16 #include <memory>
17 #include <string>
18 #include <utility>
19 #include <vector>
20 
21 #include "absl/container/flat_hash_map.h"
22 #include "absl/log/absl_check.h"
23 #include "absl/memory/memory.h"
24 #include "absl/status/status.h"
25 #include "absl/strings/match.h"
26 #include "absl/strings/numbers.h"
27 #include "absl/strings/str_cat.h"
28 #include "absl/strings/str_split.h"
29 #include "absl/strings/string_view.h"
30 #include "google/protobuf/compiler/code_generator.h"
31 #include "google/protobuf/compiler/cpp/file.h"
32 #include "google/protobuf/compiler/cpp/helpers.h"
33 #include "google/protobuf/compiler/cpp/options.h"
34 #include "google/protobuf/cpp_features.pb.h"
35 #include "google/protobuf/descriptor.h"
36 #include "google/protobuf/descriptor.pb.h"
37 #include "google/protobuf/descriptor_visitor.h"
38 #include "google/protobuf/io/printer.h"
39 
40 
41 namespace google {
42 namespace protobuf {
43 namespace compiler {
44 namespace cpp {
45 namespace {
46 
NumberedCcFileName(absl::string_view basename,int number)47 std::string NumberedCcFileName(absl::string_view basename, int number) {
48   return absl::StrCat(basename, ".out/", number, ".cc");
49 }
50 
CommonVars(const Options & options)51 absl::flat_hash_map<absl::string_view, std::string> CommonVars(
52     const Options& options) {
53   bool is_oss = options.opensource_runtime;
54   return {
55       {"proto_ns", std::string(ProtobufNamespace(options))},
56       {"pb", absl::StrCat("::", ProtobufNamespace(options))},
57       {"pbi", absl::StrCat("::", ProtobufNamespace(options), "::internal")},
58 
59       {"string", "std::string"},
60       {"int8", "::int8_t"},
61       {"int32", "::int32_t"},
62       {"int64", "::int64_t"},
63       {"uint8", "::uint8_t"},
64       {"uint32", "::uint32_t"},
65       {"uint64", "::uint64_t"},
66 
67       {"hrule_thick", kThickSeparator},
68       {"hrule_thin", kThinSeparator},
69 
70       // Warning: there is some clever naming/splitting here to avoid extract
71       // script rewrites.  The names of these variables must not be things that
72       // the extract script will rewrite.  That's why we use "CHK" (for example)
73       // instead of "ABSL_CHECK".
74       //
75       // These values are things the extract script would rewrite if we did not
76       // split them.  It might not strictly matter since we don't generate
77       // google3 code in open-source.  But it's good to prevent surprising
78       // things from happening.
79       {"GOOGLE_PROTOBUF", is_oss ? "GOOGLE_PROTOBUF"
80                                  : "GOOGLE3_PROTOBU"
81                                    "F"},
82       {"CHK",
83        "ABSL_CHEC"
84        "K"},
85       {"DCHK",
86        "ABSL_DCHEC"
87        "K"},
88   };
89 }
90 
91 
92 }  // namespace
93 
Generate(const FileDescriptor * file,const std::string & parameter,GeneratorContext * generator_context,std::string * error) const94 bool CppGenerator::Generate(const FileDescriptor* file,
95                             const std::string& parameter,
96                             GeneratorContext* generator_context,
97                             std::string* error) const {
98   std::vector<std::pair<std::string, std::string>> options;
99   ParseGeneratorParameter(parameter, &options);
100 
101   // -----------------------------------------------------------------
102   // parse generator options
103 
104   // If the dllexport_decl option is passed to the compiler, we need to write
105   // it in front of every symbol that should be exported if this .proto is
106   // compiled into a Windows DLL.  E.g., if the user invokes the protocol
107   // compiler as:
108   //   protoc --cpp_out=dllexport_decl=FOO_EXPORT:outdir foo.proto
109   // then we'll define classes like this:
110   //   class FOO_EXPORT Foo {
111   //     ...
112   //   }
113   // FOO_EXPORT is a macro which should expand to __declspec(dllexport) or
114   // __declspec(dllimport) depending on what is being compiled.
115   //
116   // If the proto_h option is passed to the compiler, we will generate all
117   // classes and enums so that they can be forward-declared from files that
118   // need them from imports.
119   //
120   // If the lite option is passed to the compiler, we will generate the
121   // current files and all transitive dependencies using the LITE runtime.
122   Options file_options;
123 
124   file_options.opensource_runtime = opensource_runtime_;
125   file_options.runtime_include_base = runtime_include_base_;
126 
127   for (const auto& option : options) {
128     const auto& key = option.first;
129     const auto& value = option.second;
130 
131     if (key == "dllexport_decl") {
132       file_options.dllexport_decl = value;
133     } else if (key == "safe_boundary_check") {
134       file_options.safe_boundary_check = true;
135     } else if (key == "annotate_headers") {
136       file_options.annotate_headers = true;
137     } else if (key == "annotation_pragma_name") {
138       file_options.annotation_pragma_name = value;
139     } else if (key == "annotation_guard_name") {
140       file_options.annotation_guard_name = value;
141     } else if (key == "speed") {
142       file_options.enforce_mode = EnforceOptimizeMode::kSpeed;
143     } else if (key == "code_size") {
144       file_options.enforce_mode = EnforceOptimizeMode::kCodeSize;
145     } else if (key == "lite") {
146       file_options.enforce_mode = EnforceOptimizeMode::kLiteRuntime;
147     } else if (key == "lite_implicit_weak_fields") {
148       file_options.enforce_mode = EnforceOptimizeMode::kLiteRuntime;
149       file_options.lite_implicit_weak_fields = true;
150       int num_cc_files;
151       if (!value.empty() && absl::SimpleAtoi(value, &num_cc_files)) {
152         file_options.num_cc_files = num_cc_files;
153       }
154     } else if (key == "descriptor_implicit_weak_messages") {
155       file_options.descriptor_implicit_weak_messages = true;
156     } else if (key == "proto_h") {
157       file_options.proto_h = true;
158     } else if (key == "proto_static_reflection_h") {
159     } else if (key == "annotate_accessor") {
160       file_options.annotate_accessor = true;
161     } else if (key == "protos_for_field_listener_events") {
162       for (absl::string_view proto : absl::StrSplit(value, ':')) {
163         if (proto == file->name()) {
164           file_options.field_listener_options.inject_field_listener_events =
165               true;
166           break;
167         }
168       }
169     } else if (key == "inject_field_listener_events") {
170       file_options.field_listener_options.inject_field_listener_events = true;
171     } else if (key == "forbidden_field_listener_events") {
172       std::size_t pos = 0;
173       do {
174         std::size_t next_pos = value.find_first_of("+", pos);
175         if (next_pos == std::string::npos) {
176           next_pos = value.size();
177         }
178         if (next_pos > pos)
179           file_options.field_listener_options.forbidden_field_listener_events
180               .emplace(value.substr(pos, next_pos - pos));
181         pos = next_pos + 1;
182       } while (pos < value.size());
183     } else if (key == "force_eagerly_verified_lazy") {
184       file_options.force_eagerly_verified_lazy = true;
185     } else if (key == "experimental_strip_nonfunctional_codegen") {
186       file_options.strip_nonfunctional_codegen = true;
187     } else {
188       *error = absl::StrCat("Unknown generator option: ", key);
189       return false;
190     }
191   }
192 
193   // The safe_boundary_check option controls behavior for Google-internal
194   // protobuf APIs.
195   if (file_options.safe_boundary_check && file_options.opensource_runtime) {
196     *error =
197         "The safe_boundary_check option is not supported outside of Google.";
198     return false;
199   }
200 
201   // -----------------------------------------------------------------
202 
203 
204   std::string basename = StripProto(file->name());
205 
206   auto generate_reserved_static_reflection_header = [&basename,
207                                                      &generator_context]() {
208     auto output = absl::WrapUnique(generator_context->Open(
209         absl::StrCat(basename, ".proto.static_reflection.h")));
210     io::Printer(output.get()).Emit(R"cc(
211       // Reserved for future use.
212     )cc");
213   };
214   // Suppress maybe unused warning.
215   (void)generate_reserved_static_reflection_header;
216 
217   if (MaybeBootstrap(file_options, generator_context, file_options.bootstrap,
218                      &basename)) {
219     return true;
220   }
221 
222   absl::Status validation_result = ValidateFeatures(file);
223   if (!validation_result.ok()) {
224     *error = std::string(validation_result.message());
225     return false;
226   }
227 
228 
229   FileGenerator file_generator(file, file_options);
230 
231   // Generate header(s).
232   if (file_options.proto_h) {
233     auto output = absl::WrapUnique(
234         generator_context->Open(absl::StrCat(basename, ".proto.h")));
235 
236     GeneratedCodeInfo annotations;
237     io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
238         &annotations);
239     io::Printer::Options options;
240     if (file_options.annotate_headers) {
241       options.annotation_collector = &annotation_collector;
242     }
243 
244     io::Printer p(output.get(), options);
245     auto v = p.WithVars(CommonVars(file_options));
246 
247     std::string info_path = absl::StrCat(basename, ".proto.h.meta");
248     file_generator.GenerateProtoHeader(
249         &p, file_options.annotate_headers ? info_path : "");
250 
251     if (file_options.annotate_headers) {
252       auto info_output = absl::WrapUnique(generator_context->Open(info_path));
253       annotations.SerializeToZeroCopyStream(info_output.get());
254     }
255   }
256 
257   {
258     auto output = absl::WrapUnique(
259         generator_context->Open(absl::StrCat(basename, ".pb.h")));
260 
261     GeneratedCodeInfo annotations;
262     io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
263         &annotations);
264     io::Printer::Options options;
265     if (file_options.annotate_headers) {
266       options.annotation_collector = &annotation_collector;
267     }
268 
269     io::Printer p(output.get(), options);
270     auto v = p.WithVars(CommonVars(file_options));
271 
272     std::string info_path = absl::StrCat(basename, ".pb.h.meta");
273     file_generator.GeneratePBHeader(
274         &p, file_options.annotate_headers ? info_path : "");
275 
276     if (file_options.annotate_headers) {
277       auto info_output = absl::WrapUnique(generator_context->Open(info_path));
278       annotations.SerializeToZeroCopyStream(info_output.get());
279     }
280   }
281 
282   // Generate cc file(s).
283   if (UsingImplicitWeakFields(file, file_options)) {
284     {
285       // This is the global .cc file, containing
286       // enum/services/tables/reflection
287       auto output = absl::WrapUnique(
288           generator_context->Open(absl::StrCat(basename, ".pb.cc")));
289       io::Printer p(output.get());
290       auto v = p.WithVars(CommonVars(file_options));
291 
292       file_generator.GenerateGlobalSource(&p);
293     }
294 
295     int num_cc_files =
296         file_generator.NumMessages() + file_generator.NumExtensions();
297 
298     // If we're using implicit weak fields then we allow the user to
299     // optionally specify how many files to generate, not counting the global
300     // pb.cc file. If we have more files than messages, then some files will
301     // be generated as empty placeholders.
302     if (file_options.num_cc_files > 0) {
303       ABSL_CHECK_LE(num_cc_files, file_options.num_cc_files)
304           << "There must be at least as many numbered .cc files as messages "
305              "and extensions.";
306       num_cc_files = file_options.num_cc_files;
307     }
308 
309     int cc_file_number = 0;
310     for (int i = 0; i < file_generator.NumMessages(); ++i) {
311       auto output = absl::WrapUnique(generator_context->Open(
312           NumberedCcFileName(basename, cc_file_number++)));
313       io::Printer p(output.get());
314       auto v = p.WithVars(CommonVars(file_options));
315 
316       file_generator.GenerateSourceForMessage(i, &p);
317     }
318 
319     for (int i = 0; i < file_generator.NumExtensions(); ++i) {
320       auto output = absl::WrapUnique(generator_context->Open(
321           NumberedCcFileName(basename, cc_file_number++)));
322       io::Printer p(output.get());
323       auto v = p.WithVars(CommonVars(file_options));
324 
325       file_generator.GenerateSourceForExtension(i, &p);
326     }
327 
328     // Create empty placeholder files if necessary to match the expected number
329     // of files.
330     while (cc_file_number < num_cc_files) {
331       (void)absl::WrapUnique(generator_context->Open(
332           NumberedCcFileName(basename, cc_file_number++)));
333     }
334   } else {
335     auto output = absl::WrapUnique(
336         generator_context->Open(absl::StrCat(basename, ".pb.cc")));
337     io::Printer p(output.get());
338     auto v = p.WithVars(CommonVars(file_options));
339 
340     file_generator.GenerateSource(&p);
341   }
342 
343   return true;
344 }
345 
IsEnumMapType(const FieldDescriptor & field)346 static bool IsEnumMapType(const FieldDescriptor& field) {
347   if (!field.is_map()) return false;
348   for (int i = 0; i < field.message_type()->field_count(); ++i) {
349     if (field.message_type()->field(i)->type() == FieldDescriptor::TYPE_ENUM) {
350       return true;
351     }
352   }
353   return false;
354 }
355 
ValidateFeatures(const FileDescriptor * file) const356 absl::Status CppGenerator::ValidateFeatures(const FileDescriptor* file) const {
357   absl::Status status = absl::OkStatus();
358   google::protobuf::internal::VisitDescriptors(*file, [&](const FieldDescriptor& field) {
359     const FeatureSet& resolved_features = GetResolvedSourceFeatures(field);
360     const pb::CppFeatures& unresolved_features =
361         GetUnresolvedSourceFeatures(field, pb::cpp);
362     if (field.enum_type() != nullptr &&
363         resolved_features.GetExtension(::pb::cpp).legacy_closed_enum() &&
364         resolved_features.field_presence() == FeatureSet::IMPLICIT) {
365       status = absl::FailedPreconditionError(
366           absl::StrCat("Field ", field.full_name(),
367                        " has a closed enum type with implicit presence."));
368     }
369 
370     if (field.containing_type() == nullptr ||
371         !field.containing_type()->options().map_entry()) {
372       // Skip validation of explicit features on generated map fields.  These
373       // will be blindly propagated from the original map field, and may violate
374       // a lot of these conditions.  Note: we do still validate the
375       // user-specified map field.
376       if (unresolved_features.has_legacy_closed_enum() &&
377           field.cpp_type() != FieldDescriptor::CPPTYPE_ENUM &&
378           !IsEnumMapType(field)) {
379         status = absl::FailedPreconditionError(
380             absl::StrCat("Field ", field.full_name(),
381                          " specifies the legacy_closed_enum feature but has "
382                          "non-enum type."));
383       }
384     }
385 
386     if (unresolved_features.has_string_type()) {
387       if (field.cpp_type() != FieldDescriptor::CPPTYPE_STRING) {
388         status = absl::FailedPreconditionError(absl::StrCat(
389             "Field ", field.full_name(),
390             " specifies string_type, but is not a string nor bytes field."));
391       } else if (unresolved_features.string_type() == pb::CppFeatures::CORD &&
392                  field.is_extension()) {
393         status = absl::FailedPreconditionError(
394             absl::StrCat("Extension ", field.full_name(),
395                          " specifies string_type=CORD which is not supported "
396                          "for extensions."));
397       } else if (field.options().has_ctype()) {
398         // NOTE: this is just a sanity check. This case should never happen
399         // because descriptor builder makes string_type override ctype.
400         const FieldOptions::CType ctype = field.options().ctype();
401         const pb::CppFeatures::StringType string_type =
402             unresolved_features.string_type();
403         if ((ctype == FieldOptions::STRING &&
404              string_type != pb::CppFeatures::STRING) ||
405             (ctype == FieldOptions::CORD &&
406              string_type != pb::CppFeatures::CORD)) {
407           status = absl::FailedPreconditionError(
408               absl::StrCat(field.full_name(),
409                            " specifies inconsistent string_type and ctype."));
410         }
411       }
412     }
413 
414     if (field.options().has_ctype()) {
415       if (field.cpp_type() != FieldDescriptor::CPPTYPE_STRING) {
416         status = absl::FailedPreconditionError(absl::StrCat(
417             "Field ", field.full_name(),
418             " specifies ctype, but is not a string nor bytes field."));
419       }
420       if (field.options().ctype() == FieldOptions::CORD) {
421         if (field.is_extension()) {
422           status = absl::FailedPreconditionError(absl::StrCat(
423               "Extension ", field.full_name(),
424               " specifies Cord type which is not supported for extensions."));
425         }
426       }
427     }
428 
429     if (field.cpp_type() == FieldDescriptor::CPPTYPE_STRING &&
430         field.cpp_string_type() == FieldDescriptor::CppStringType::kCord &&
431         field.is_extension()) {
432       status = absl::FailedPreconditionError(absl::StrCat(
433           "Extension ", field.full_name(),
434           " specifies Cord type which is not supported for extensions."));
435     }
436   });
437   return status;
438 }
439 
440 }  // namespace cpp
441 }  // namespace compiler
442 }  // namespace protobuf
443 }  // namespace google
444