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