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