1 /*
2  * Copyright (c) 2009-2021, Google LLC
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of Google LLC nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "protos_generator/gen_utils.h"
29 
30 #include <algorithm>
31 #include <string>
32 
33 #include "absl/log/absl_check.h"
34 #include "absl/strings/str_cat.h"
35 // begin:google_only
36 // #include "absl/strings/str_replace.h"
37 // end:google_only
38 #include "absl/strings/str_split.h"
39 #include "upbc/keywords.h"
40 
41 namespace protos_generator {
42 
43 namespace protobuf = ::google::protobuf;
44 
DotsToColons(const std::string & name)45 std::string DotsToColons(const std::string& name) {
46   return absl::StrReplaceAll(name, {{".", "::"}});
47 }
48 
Namespace(const std::string & package)49 std::string Namespace(const std::string& package) {
50   if (package.empty()) return "";
51   return "::" + DotsToColons(package);
52 }
53 
54 // Return the qualified C++ name for a file level symbol.
QualifiedFileLevelSymbol(const protobuf::FileDescriptor * file,const std::string & name)55 std::string QualifiedFileLevelSymbol(const protobuf::FileDescriptor* file,
56                                      const std::string& name) {
57   if (file->package().empty()) {
58     return absl::StrCat("::", name);
59   }
60   // Append ::protos postfix to package name.
61   return absl::StrCat(Namespace(file->package()), "::protos::", name);
62 }
63 
ClassName(const protobuf::Descriptor * descriptor)64 std::string ClassName(const protobuf::Descriptor* descriptor) {
65   const protobuf::Descriptor* parent = descriptor->containing_type();
66   std::string res;
67   // Classes in global namespace without package names are prefixed
68   // by protos_ to avoid collision with C compiler structs defined in
69   // proto.upb.h.
70   if ((parent && parent->file()->package().empty()) ||
71       descriptor->file()->package().empty()) {
72     res = std::string(kNoPackageNamePrefix);
73   }
74   if (parent) res += ClassName(parent) + "_";
75   absl::StrAppend(&res, descriptor->name());
76   return ::upbc::ResolveKeywordConflict(res);
77 }
78 
QualifiedClassName(const protobuf::Descriptor * descriptor)79 std::string QualifiedClassName(const protobuf::Descriptor* descriptor) {
80   return QualifiedFileLevelSymbol(descriptor->file(), ClassName(descriptor));
81 }
82 
QualifiedInternalClassName(const protobuf::Descriptor * descriptor)83 std::string QualifiedInternalClassName(const protobuf::Descriptor* descriptor) {
84   return QualifiedFileLevelSymbol(
85       descriptor->file(), absl::StrCat("internal::", ClassName(descriptor)));
86 }
87 
CppSourceFilename(const google::protobuf::FileDescriptor * file)88 std::string CppSourceFilename(const google::protobuf::FileDescriptor* file) {
89   return StripExtension(file->name()) + ".upb.proto.cc";
90 }
91 
UpbCFilename(const google::protobuf::FileDescriptor * file)92 std::string UpbCFilename(const google::protobuf::FileDescriptor* file) {
93   return StripExtension(file->name()) + ".upb.h";
94 }
95 
ForwardingHeaderFilename(const google::protobuf::FileDescriptor * file)96 std::string ForwardingHeaderFilename(const google::protobuf::FileDescriptor* file) {
97   return StripExtension(file->name()) + ".upb.fwd.h";
98 }
99 
CppHeaderFilename(const google::protobuf::FileDescriptor * file)100 std::string CppHeaderFilename(const google::protobuf::FileDescriptor* file) {
101   return StripExtension(file->name()) + ".upb.proto.h";
102 }
103 
NamespaceFromPackageName(absl::string_view package_name)104 std::string NamespaceFromPackageName(absl::string_view package_name) {
105   return absl::StrCat(absl::StrReplaceAll(package_name, {{".", "::"}}),
106                       "::protos");
107 }
108 
WriteStartNamespace(const protobuf::FileDescriptor * file,Output & output)109 void WriteStartNamespace(const protobuf::FileDescriptor* file, Output& output) {
110   // Skip namespace generation if package name is not specified.
111   if (file->package().empty()) {
112     return;
113   }
114 
115   output("namespace $0 {\n\n", NamespaceFromPackageName(file->package()));
116 }
117 
WriteEndNamespace(const protobuf::FileDescriptor * file,Output & output)118 void WriteEndNamespace(const protobuf::FileDescriptor* file, Output& output) {
119   if (file->package().empty()) {
120     return;
121   }
122   output("} //  namespace $0\n\n", NamespaceFromPackageName(file->package()));
123 }
124 
CppTypeInternal(const protobuf::FieldDescriptor * field,bool is_const,bool is_type_parameter)125 std::string CppTypeInternal(const protobuf::FieldDescriptor* field,
126                             bool is_const, bool is_type_parameter) {
127   std::string maybe_const = is_const ? "const " : "";
128   switch (field->cpp_type()) {
129     case protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
130       if (is_type_parameter) {
131         return absl::StrCat(maybe_const,
132                             QualifiedClassName(field->message_type()));
133       } else {
134         return absl::StrCat(maybe_const,
135                             QualifiedClassName(field->message_type()), "*");
136       }
137     }
138     case protobuf::FieldDescriptor::CPPTYPE_BOOL:
139       return "bool";
140     case protobuf::FieldDescriptor::CPPTYPE_FLOAT:
141       return "float";
142     case protobuf::FieldDescriptor::CPPTYPE_INT32:
143     case protobuf::FieldDescriptor::CPPTYPE_ENUM:
144       return "int32_t";
145     case protobuf::FieldDescriptor::CPPTYPE_UINT32:
146       return "uint32_t";
147     case protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
148       return "double";
149     case protobuf::FieldDescriptor::CPPTYPE_INT64:
150       return "int64_t";
151     case protobuf::FieldDescriptor::CPPTYPE_UINT64:
152       return "uint64_t";
153     case protobuf::FieldDescriptor::CPPTYPE_STRING:
154       return "absl::string_view";
155     default:
156       ABSL_LOG(FATAL) << "Unexpected type: " << field->cpp_type();
157   }
158 }
159 
CppConstType(const protobuf::FieldDescriptor * field)160 std::string CppConstType(const protobuf::FieldDescriptor* field) {
161   return CppTypeInternal(field, /* is_const= */ true,
162                          /* is_type_parameter= */ false);
163 }
164 
CppTypeParameterName(const protobuf::FieldDescriptor * field)165 std::string CppTypeParameterName(const protobuf::FieldDescriptor* field) {
166   return CppTypeInternal(field, /* is_const= */ false,
167                          /* is_type_parameter= */ true);
168 }
169 
MessageBaseType(const protobuf::FieldDescriptor * field,bool is_const)170 std::string MessageBaseType(const protobuf::FieldDescriptor* field,
171                             bool is_const) {
172   ABSL_DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
173   std::string maybe_const = is_const ? "const " : "";
174   return maybe_const + QualifiedClassName(field->message_type());
175 }
176 
MessagePtrConstType(const protobuf::FieldDescriptor * field,bool is_const)177 std::string MessagePtrConstType(const protobuf::FieldDescriptor* field,
178                                 bool is_const) {
179   ABSL_DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
180   std::string maybe_const = is_const ? "const " : "";
181   return "::protos::Ptr<" + maybe_const +
182          QualifiedClassName(field->message_type()) + ">";
183 }
184 
MessageCProxyType(const protobuf::FieldDescriptor * field,bool is_const)185 std::string MessageCProxyType(const protobuf::FieldDescriptor* field,
186                               bool is_const) {
187   ABSL_DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
188   std::string maybe_const = is_const ? "const " : "";
189   return maybe_const + QualifiedInternalClassName(field->message_type()) +
190          "CProxy";
191 }
192 
MessageProxyType(const protobuf::FieldDescriptor * field,bool is_const)193 std::string MessageProxyType(const protobuf::FieldDescriptor* field,
194                              bool is_const) {
195   ABSL_DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
196   std::string maybe_const = is_const ? "const " : "";
197   return maybe_const + QualifiedInternalClassName(field->message_type()) +
198          "Proxy";
199 }
200 
AddEnums(const protobuf::Descriptor * message,std::vector<const protobuf::EnumDescriptor * > * enums)201 void AddEnums(const protobuf::Descriptor* message,
202               std::vector<const protobuf::EnumDescriptor*>* enums) {
203   enums->reserve(enums->size() + message->enum_type_count());
204   for (int i = 0; i < message->enum_type_count(); i++) {
205     enums->push_back(message->enum_type(i));
206   }
207   for (int i = 0; i < message->nested_type_count(); i++) {
208     AddEnums(message->nested_type(i), enums);
209   }
210 }
211 
SortedEnums(const protobuf::FileDescriptor * file)212 std::vector<const protobuf::EnumDescriptor*> SortedEnums(
213     const protobuf::FileDescriptor* file) {
214   std::vector<const protobuf::EnumDescriptor*> enums;
215   enums.reserve(file->enum_type_count());
216   for (int i = 0; i < file->enum_type_count(); i++) {
217     enums.push_back(file->enum_type(i));
218   }
219   for (int i = 0; i < file->message_type_count(); i++) {
220     AddEnums(file->message_type(i), &enums);
221   }
222   return enums;
223 }
224 
AddMessages(const protobuf::Descriptor * message,std::vector<const protobuf::Descriptor * > * messages)225 void AddMessages(const protobuf::Descriptor* message,
226                  std::vector<const protobuf::Descriptor*>* messages) {
227   messages->push_back(message);
228   for (int i = 0; i < message->nested_type_count(); i++) {
229     AddMessages(message->nested_type(i), messages);
230   }
231 }
232 
SortedMessages(const protobuf::FileDescriptor * file)233 std::vector<const protobuf::Descriptor*> SortedMessages(
234     const protobuf::FileDescriptor* file) {
235   std::vector<const protobuf::Descriptor*> messages;
236   for (int i = 0; i < file->message_type_count(); i++) {
237     AddMessages(file->message_type(i), &messages);
238   }
239   return messages;
240 }
241 
AddExtensionsFromMessage(const protobuf::Descriptor * message,std::vector<const protobuf::FieldDescriptor * > * exts)242 void AddExtensionsFromMessage(
243     const protobuf::Descriptor* message,
244     std::vector<const protobuf::FieldDescriptor*>* exts) {
245   for (int i = 0; i < message->extension_count(); i++) {
246     exts->push_back(message->extension(i));
247   }
248   for (int i = 0; i < message->nested_type_count(); i++) {
249     AddExtensionsFromMessage(message->nested_type(i), exts);
250   }
251 }
252 
SortedExtensions(const protobuf::FileDescriptor * file)253 std::vector<const protobuf::FieldDescriptor*> SortedExtensions(
254     const protobuf::FileDescriptor* file) {
255   std::vector<const protobuf::FieldDescriptor*> ret;
256   for (int i = 0; i < file->extension_count(); i++) {
257     ret.push_back(file->extension(i));
258   }
259   for (int i = 0; i < file->message_type_count(); i++) {
260     AddExtensionsFromMessage(file->message_type(i), &ret);
261   }
262   return ret;
263 }
264 
FieldNumberOrder(const protobuf::Descriptor * message)265 std::vector<const protobuf::FieldDescriptor*> FieldNumberOrder(
266     const protobuf::Descriptor* message) {
267   std::vector<const protobuf::FieldDescriptor*> fields;
268   fields.reserve(message->field_count());
269   for (int i = 0; i < message->field_count(); i++) {
270     fields.push_back(message->field(i));
271   }
272   std::sort(fields.begin(), fields.end(),
273             [](const protobuf::FieldDescriptor* a,
274                const protobuf::FieldDescriptor* b) {
275               return a->number() < b->number();
276             });
277   return fields;
278 }
279 
ToCamelCase(const std::string & input,bool lower_first)280 std::string ToCamelCase(const std::string& input, bool lower_first) {
281   bool capitalize_next = !lower_first;
282   std::string result;
283   result.reserve(input.size());
284 
285   for (char character : input) {
286     if (character == '_') {
287       capitalize_next = true;
288     } else if (capitalize_next) {
289       result.push_back(absl::ascii_toupper(character));
290       capitalize_next = false;
291     } else {
292       result.push_back(character);
293     }
294   }
295 
296   // Lower-case the first letter.
297   if (lower_first && !result.empty()) {
298     result[0] = absl::ascii_tolower(result[0]);
299   }
300 
301   return result;
302 }
303 
304 }  // namespace protos_generator
305