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, ¶ms);
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