1 // Copyright (c) 2009-2021, Google LLC
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //     * Redistributions of source code must retain the above copyright
7 //       notice, this list of conditions and the following disclaimer.
8 //     * Redistributions in binary form must reproduce the above copyright
9 //       notice, this list of conditions and the following disclaimer in the
10 //       documentation and/or other materials provided with the distribution.
11 //     * Neither the name of Google LLC nor the
12 //       names of its contributors may be used to endorse or promote products
13 //       derived from this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 // ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
19 // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 
26 #include <memory>
27 
28 #include "google/protobuf/descriptor.pb.h"
29 #include "google/protobuf/compiler/code_generator.h"
30 #include "google/protobuf/compiler/plugin.h"
31 #include "google/protobuf/descriptor.h"
32 #include "protos_generator/gen_enums.h"
33 #include "protos_generator/gen_extensions.h"
34 #include "protos_generator/gen_messages.h"
35 #include "protos_generator/gen_utils.h"
36 #include "protos_generator/output.h"
37 #include "upbc/file_layout.h"
38 
39 namespace protos_generator {
40 namespace {
41 
42 namespace protoc = ::google::protobuf::compiler;
43 namespace protobuf = ::google::protobuf;
44 using FileDescriptor = ::google::protobuf::FileDescriptor;
45 
46 void WriteSource(const protobuf::FileDescriptor* file, Output& output,
47                  bool fasttable_enabled);
48 void WriteHeader(const protobuf::FileDescriptor* file, Output& output);
49 void WriteForwardingHeader(const protobuf::FileDescriptor* file,
50                            Output& output);
51 void WriteMessageImplementations(const protobuf::FileDescriptor* file,
52                                  Output& output);
53 void WriteTypedefForwardingHeader(
54     const protobuf::FileDescriptor* file,
55     const std::vector<const protobuf::Descriptor*>& file_messages,
56     Output& output);
57 void WriteHeaderMessageForwardDecls(
58     const protobuf::FileDescriptor* file,
59     const std::vector<const protobuf::Descriptor*>& file_messages,
60     const std::vector<const protobuf::FieldDescriptor*>& file_exts,
61     Output& output);
62 
63 class Generator : public protoc::CodeGenerator {
64  public:
~Generator()65   ~Generator() override {}
66   bool Generate(const protobuf::FileDescriptor* file,
67                 const std::string& parameter, protoc::GeneratorContext* context,
68                 std::string* error) const override;
GetSupportedFeatures() const69   uint64_t GetSupportedFeatures() const override {
70     return FEATURE_PROTO3_OPTIONAL;
71   }
72 };
73 
Generate(const protobuf::FileDescriptor * file,const std::string & parameter,protoc::GeneratorContext * context,std::string * error) const74 bool Generator::Generate(const protobuf::FileDescriptor* file,
75                          const std::string& parameter,
76                          protoc::GeneratorContext* context,
77                          std::string* error) const {
78   bool fasttable_enabled = false;
79   std::vector<std::pair<std::string, std::string>> params;
80   google::protobuf::compiler::ParseGeneratorParameter(parameter, &params);
81 
82   for (const auto& pair : params) {
83     if (pair.first == "fasttable") {
84       fasttable_enabled = true;
85     } else {
86       *error = "Unknown parameter: " + pair.first;
87       return false;
88     }
89   }
90 
91   // Write model.upb.fwd.h
92   Output forwarding_header_output(
93       context->Open(ForwardingHeaderFilename(file)));
94   WriteForwardingHeader(file, forwarding_header_output);
95   // Write model.upb.proto.h
96   Output header_output(context->Open(CppHeaderFilename(file)));
97   WriteHeader(file, header_output);
98   // Write model.upb.proto.cc
99   Output cc_output(context->Open(CppSourceFilename(file)));
100   WriteSource(file, cc_output, fasttable_enabled);
101   return true;
102 }
103 
104 // The forwarding header defines Access/Proxy/CProxy for message classes
105 // used to include when referencing dependencies to prevent transitive
106 // dependency headers from being included.
WriteForwardingHeader(const protobuf::FileDescriptor * file,Output & output)107 void WriteForwardingHeader(const protobuf::FileDescriptor* file,
108                            Output& output) {
109   EmitFileWarning(file, output);
110   output(
111       R"cc(
112 #ifndef $0_UPB_FWD_H_
113 #define $0_UPB_FWD_H_
114       )cc",
115       ToPreproc(file->name()));
116   output("\n");
117   const std::vector<const protobuf::Descriptor*> this_file_messages =
118       SortedMessages(file);
119   WriteTypedefForwardingHeader(file, this_file_messages, output);
120   output("#endif  /* $0_UPB_FWD_H_ */\n", ToPreproc(file->name()));
121 }
122 
WriteHeader(const protobuf::FileDescriptor * file,Output & output)123 void WriteHeader(const protobuf::FileDescriptor* file, Output& output) {
124   EmitFileWarning(file, output);
125   output(
126       R"cc(
127 #ifndef $0_UPB_PROTO_H_
128 #define $0_UPB_PROTO_H_
129 
130 #include "protos/protos.h"
131 #include "protos/protos_internal.h"
132 #include "upb/upb.hpp"
133 
134 #include "absl/strings/string_view.h"
135 #include "absl/status/statusor.h"
136 #include "upb/message/internal.h"
137 #include "upb/message/copy.h"
138       )cc",
139       ToPreproc(file->name()));
140 
141   // Import headers for proto public dependencies.
142   for (int i = 0; i < file->public_dependency_count(); i++) {
143     if (i == 0) {
144       output("// Public Imports.\n");
145     }
146     output("#include \"$0\"\n", CppHeaderFilename(file->public_dependency(i)));
147     if (i == file->public_dependency_count() - 1) {
148       output("\n");
149     }
150   }
151 
152   output("#include \"upb/port/def.inc\"\n");
153 
154   const std::vector<const protobuf::Descriptor*> this_file_messages =
155       SortedMessages(file);
156   const std::vector<const protobuf::FieldDescriptor*> this_file_exts =
157       SortedExtensions(file);
158 
159   if (!this_file_messages.empty()) {
160     output("\n");
161   }
162 
163   WriteHeaderMessageForwardDecls(file, this_file_messages, this_file_exts,
164                                  output);
165   WriteStartNamespace(file, output);
166 
167   std::vector<const protobuf::EnumDescriptor*> this_file_enums =
168       SortedEnums(file);
169 
170   // Write Class and Enums.
171   WriteEnumDeclarations(this_file_enums, output);
172   output("\n");
173 
174   for (auto message : this_file_messages) {
175     WriteMessageClassDeclarations(message, this_file_exts, this_file_enums,
176                                   output);
177   }
178   output("\n");
179 
180   WriteExtensionIdentifiersHeader(this_file_exts, output);
181   output("\n");
182 
183   WriteEndNamespace(file, output);
184 
185   output("\n#include \"upb/port/undef.inc\"\n\n");
186   // End of "C" section.
187 
188   output("#endif  /* $0_UPB_PROTO_H_ */\n", ToPreproc(file->name()));
189 }
190 
191 // Writes a .upb.cc source file.
WriteSource(const protobuf::FileDescriptor * file,Output & output,bool fasttable_enabled)192 void WriteSource(const protobuf::FileDescriptor* file, Output& output,
193                  bool fasttable_enabled) {
194   EmitFileWarning(file, output);
195 
196   output(
197       R"cc(
198 #include <stddef.h>
199 #include "absl/strings/string_view.h"
200 #include "upb/message/copy.h"
201 #include "upb/message/internal.h"
202 #include "protos/protos.h"
203 #include "$0"
204       )cc",
205       CppHeaderFilename(file));
206 
207   for (int i = 0; i < file->dependency_count(); i++) {
208     output("#include \"$0\"\n", CppHeaderFilename(file->dependency(i)));
209   }
210   output("#include \"upb/port/def.inc\"\n");
211 
212   WriteStartNamespace(file, output);
213   WriteMessageImplementations(file, output);
214   const std::vector<const protobuf::FieldDescriptor*> this_file_exts =
215       SortedExtensions(file);
216   WriteExtensionIdentifiers(this_file_exts, output);
217   WriteEndNamespace(file, output);
218 
219   output("#include \"upb/port/undef.inc\"\n\n");
220 }
221 
WriteMessageImplementations(const protobuf::FileDescriptor * file,Output & output)222 void WriteMessageImplementations(const protobuf::FileDescriptor* file,
223                                  Output& output) {
224   const std::vector<const protobuf::FieldDescriptor*> file_exts =
225       SortedExtensions(file);
226   const std::vector<const protobuf::Descriptor*> this_file_messages =
227       SortedMessages(file);
228   for (auto message : this_file_messages) {
229     WriteMessageImplementation(message, file_exts, output);
230   }
231 }
232 
WriteTypedefForwardingHeader(const protobuf::FileDescriptor * file,const std::vector<const protobuf::Descriptor * > & file_messages,Output & output)233 void WriteTypedefForwardingHeader(
234     const protobuf::FileDescriptor* file,
235     const std::vector<const protobuf::Descriptor*>& file_messages,
236     Output& output) {
237   WriteStartNamespace(file, output);
238 
239   // Forward-declare types defined in this file.
240   for (auto message : file_messages) {
241     output(
242         R"cc(
243           class $0;
244           namespace internal {
245           class $0Access;
246           class $0Proxy;
247           class $0CProxy;
248           }  // namespace internal
249         )cc",
250         ClassName(message));
251   }
252   output("\n");
253   WriteEndNamespace(file, output);
254 }
255 
256 /// Writes includes for upb C minitables and fwd.h for transitive typedefs.
WriteHeaderMessageForwardDecls(const protobuf::FileDescriptor * file,const std::vector<const protobuf::Descriptor * > & file_messages,const std::vector<const protobuf::FieldDescriptor * > & file_exts,Output & output)257 void WriteHeaderMessageForwardDecls(
258     const protobuf::FileDescriptor* file,
259     const std::vector<const protobuf::Descriptor*>& file_messages,
260     const std::vector<const protobuf::FieldDescriptor*>& file_exts,
261     Output& output) {
262   // Import forward-declaration of types defined in this file.
263   output("#include \"$0\"\n", UpbCFilename(file));
264   output("#include \"$0\"\n", ForwardingHeaderFilename(file));
265   // Forward-declare types not in this file, but used as submessages.
266   // Order by full name for consistent ordering.
267   std::map<std::string, const protobuf::Descriptor*> forward_messages;
268 
269   for (auto* message : file_messages) {
270     for (int i = 0; i < message->field_count(); i++) {
271       const protobuf::FieldDescriptor* field = message->field(i);
272       if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE &&
273           field->file() != field->message_type()->file()) {
274         forward_messages[field->message_type()->full_name()] =
275             field->message_type();
276       }
277     }
278   }
279   for (auto* ext : file_exts) {
280     if (ext->file() != ext->containing_type()->file()) {
281       forward_messages[ext->containing_type()->full_name()] =
282           ext->containing_type();
283       if (ext->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
284         forward_messages[ext->message_type()->full_name()] =
285             ext->message_type();
286       }
287     }
288   }
289   std::map<std::string, const protobuf::FileDescriptor*> files_to_import;
290   for (const auto& pair : forward_messages) {
291     files_to_import[ForwardingHeaderFilename(pair.second->file())] = file;
292   }
293   for (const auto& pair : files_to_import) {
294     output("#include \"$0\"\n", UpbCFilename(pair.second));
295     output("#include \"$0\"\n", pair.first);
296   }
297   output("\n");
298 }
299 
300 }  // namespace
301 }  // namespace protos_generator
302 
main(int argc,char ** argv)303 int main(int argc, char** argv) {
304   protos_generator::Generator generator_cc;
305   return google::protobuf::compiler::PluginMain(argc, argv, &generator_cc);
306 }
307