1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // http://code.google.com/p/protobuf/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 // * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 // Author: kenton@google.com (Kenton Varda)
32 // Based on original Protocol Buffers design by
33 // Sanjay Ghemawat, Jeff Dean, and others.
34
35 #include <limits>
36 #include <map>
37 #include <vector>
38 #include <google/protobuf/stubs/hash.h>
39
40 #include <google/protobuf/compiler/cpp/cpp_helpers.h>
41 #include <google/protobuf/io/printer.h>
42 #include <google/protobuf/stubs/common.h>
43 #include <google/protobuf/stubs/strutil.h>
44 #include <google/protobuf/stubs/substitute.h>
45
46
47 namespace google {
48 namespace protobuf {
49 namespace compiler {
50 namespace cpp {
51
52 namespace {
53
DotsToUnderscores(const string & name)54 string DotsToUnderscores(const string& name) {
55 return StringReplace(name, ".", "_", true);
56 }
57
DotsToColons(const string & name)58 string DotsToColons(const string& name) {
59 return StringReplace(name, ".", "::", true);
60 }
61
62 const char* const kKeywordList[] = {
63 "and", "and_eq", "asm", "auto", "bitand", "bitor", "bool", "break", "case",
64 "catch", "char", "class", "compl", "const", "const_cast", "continue",
65 "default", "delete", "do", "double", "dynamic_cast", "else", "enum",
66 "explicit", "extern", "false", "float", "for", "friend", "goto", "if",
67 "inline", "int", "long", "mutable", "namespace", "new", "not", "not_eq",
68 "operator", "or", "or_eq", "private", "protected", "public", "register",
69 "reinterpret_cast", "return", "short", "signed", "sizeof", "static",
70 "static_cast", "struct", "switch", "template", "this", "throw", "true", "try",
71 "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual",
72 "void", "volatile", "wchar_t", "while", "xor", "xor_eq"
73 };
74
MakeKeywordsMap()75 hash_set<string> MakeKeywordsMap() {
76 hash_set<string> result;
77 for (int i = 0; i < GOOGLE_ARRAYSIZE(kKeywordList); i++) {
78 result.insert(kKeywordList[i]);
79 }
80 return result;
81 }
82
83 hash_set<string> kKeywords = MakeKeywordsMap();
84
UnderscoresToCamelCase(const string & input,bool cap_next_letter)85 string UnderscoresToCamelCase(const string& input, bool cap_next_letter) {
86 string result;
87 // Note: I distrust ctype.h due to locales.
88 for (int i = 0; i < input.size(); i++) {
89 if ('a' <= input[i] && input[i] <= 'z') {
90 if (cap_next_letter) {
91 result += input[i] + ('A' - 'a');
92 } else {
93 result += input[i];
94 }
95 cap_next_letter = false;
96 } else if ('A' <= input[i] && input[i] <= 'Z') {
97 // Capital letters are left as-is.
98 result += input[i];
99 cap_next_letter = false;
100 } else if ('0' <= input[i] && input[i] <= '9') {
101 result += input[i];
102 cap_next_letter = true;
103 } else {
104 cap_next_letter = true;
105 }
106 }
107 return result;
108 }
109
110 // Returns whether the provided descriptor has an extension. This includes its
111 // nested types.
HasExtension(const Descriptor * descriptor)112 bool HasExtension(const Descriptor* descriptor) {
113 if (descriptor->extension_count() > 0) {
114 return true;
115 }
116 for (int i = 0; i < descriptor->nested_type_count(); ++i) {
117 if (HasExtension(descriptor->nested_type(i))) {
118 return true;
119 }
120 }
121 return false;
122 }
123
124 } // namespace
125
126 const char kThickSeparator[] =
127 "// ===================================================================\n";
128 const char kThinSeparator[] =
129 "// -------------------------------------------------------------------\n";
130
ClassName(const Descriptor * descriptor,bool qualified)131 string ClassName(const Descriptor* descriptor, bool qualified) {
132
133 // Find "outer", the descriptor of the top-level message in which
134 // "descriptor" is embedded.
135 const Descriptor* outer = descriptor;
136 while (outer->containing_type() != NULL) outer = outer->containing_type();
137
138 const string& outer_name = outer->full_name();
139 string inner_name = descriptor->full_name().substr(outer_name.size());
140
141 if (qualified) {
142 return "::" + DotsToColons(outer_name) + DotsToUnderscores(inner_name);
143 } else {
144 return outer->name() + DotsToUnderscores(inner_name);
145 }
146 }
147
ClassName(const EnumDescriptor * enum_descriptor,bool qualified)148 string ClassName(const EnumDescriptor* enum_descriptor, bool qualified) {
149 if (enum_descriptor->containing_type() == NULL) {
150 if (qualified) {
151 return "::" + DotsToColons(enum_descriptor->full_name());
152 } else {
153 return enum_descriptor->name();
154 }
155 } else {
156 string result = ClassName(enum_descriptor->containing_type(), qualified);
157 result += '_';
158 result += enum_descriptor->name();
159 return result;
160 }
161 }
162
163
SuperClassName(const Descriptor * descriptor)164 string SuperClassName(const Descriptor* descriptor) {
165 return HasDescriptorMethods(descriptor->file()) ?
166 "::google::protobuf::Message" : "::google::protobuf::MessageLite";
167 }
168
FieldName(const FieldDescriptor * field)169 string FieldName(const FieldDescriptor* field) {
170 string result = field->name();
171 LowerString(&result);
172 if (kKeywords.count(result) > 0) {
173 result.append("_");
174 }
175 return result;
176 }
177
FieldConstantName(const FieldDescriptor * field)178 string FieldConstantName(const FieldDescriptor *field) {
179 string field_name = UnderscoresToCamelCase(field->name(), true);
180 string result = "k" + field_name + "FieldNumber";
181
182 if (!field->is_extension() &&
183 field->containing_type()->FindFieldByCamelcaseName(
184 field->camelcase_name()) != field) {
185 // This field's camelcase name is not unique. As a hack, add the field
186 // number to the constant name. This makes the constant rather useless,
187 // but what can we do?
188 result += "_" + SimpleItoa(field->number());
189 }
190
191 return result;
192 }
193
FieldMessageTypeName(const FieldDescriptor * field)194 string FieldMessageTypeName(const FieldDescriptor* field) {
195 // Note: The Google-internal version of Protocol Buffers uses this function
196 // as a hook point for hacks to support legacy code.
197 return ClassName(field->message_type(), true);
198 }
199
StripProto(const string & filename)200 string StripProto(const string& filename) {
201 if (HasSuffixString(filename, ".protodevel")) {
202 return StripSuffixString(filename, ".protodevel");
203 } else {
204 return StripSuffixString(filename, ".proto");
205 }
206 }
207
PrimitiveTypeName(FieldDescriptor::CppType type)208 const char* PrimitiveTypeName(FieldDescriptor::CppType type) {
209 switch (type) {
210 case FieldDescriptor::CPPTYPE_INT32 : return "::google::protobuf::int32";
211 case FieldDescriptor::CPPTYPE_INT64 : return "::google::protobuf::int64";
212 case FieldDescriptor::CPPTYPE_UINT32 : return "::google::protobuf::uint32";
213 case FieldDescriptor::CPPTYPE_UINT64 : return "::google::protobuf::uint64";
214 case FieldDescriptor::CPPTYPE_DOUBLE : return "double";
215 case FieldDescriptor::CPPTYPE_FLOAT : return "float";
216 case FieldDescriptor::CPPTYPE_BOOL : return "bool";
217 case FieldDescriptor::CPPTYPE_ENUM : return "int";
218 case FieldDescriptor::CPPTYPE_STRING : return "::std::string";
219 case FieldDescriptor::CPPTYPE_MESSAGE: return NULL;
220
221 // No default because we want the compiler to complain if any new
222 // CppTypes are added.
223 }
224
225 GOOGLE_LOG(FATAL) << "Can't get here.";
226 return NULL;
227 }
228
DeclaredTypeMethodName(FieldDescriptor::Type type)229 const char* DeclaredTypeMethodName(FieldDescriptor::Type type) {
230 switch (type) {
231 case FieldDescriptor::TYPE_INT32 : return "Int32";
232 case FieldDescriptor::TYPE_INT64 : return "Int64";
233 case FieldDescriptor::TYPE_UINT32 : return "UInt32";
234 case FieldDescriptor::TYPE_UINT64 : return "UInt64";
235 case FieldDescriptor::TYPE_SINT32 : return "SInt32";
236 case FieldDescriptor::TYPE_SINT64 : return "SInt64";
237 case FieldDescriptor::TYPE_FIXED32 : return "Fixed32";
238 case FieldDescriptor::TYPE_FIXED64 : return "Fixed64";
239 case FieldDescriptor::TYPE_SFIXED32: return "SFixed32";
240 case FieldDescriptor::TYPE_SFIXED64: return "SFixed64";
241 case FieldDescriptor::TYPE_FLOAT : return "Float";
242 case FieldDescriptor::TYPE_DOUBLE : return "Double";
243
244 case FieldDescriptor::TYPE_BOOL : return "Bool";
245 case FieldDescriptor::TYPE_ENUM : return "Enum";
246
247 case FieldDescriptor::TYPE_STRING : return "String";
248 case FieldDescriptor::TYPE_BYTES : return "Bytes";
249 case FieldDescriptor::TYPE_GROUP : return "Group";
250 case FieldDescriptor::TYPE_MESSAGE : return "Message";
251
252 // No default because we want the compiler to complain if any new
253 // types are added.
254 }
255 GOOGLE_LOG(FATAL) << "Can't get here.";
256 return "";
257 }
258
DefaultValue(const FieldDescriptor * field)259 string DefaultValue(const FieldDescriptor* field) {
260 switch (field->cpp_type()) {
261 case FieldDescriptor::CPPTYPE_INT32:
262 // gcc rejects the decimal form of kint32min and kint64min.
263 if (field->default_value_int32() == kint32min) {
264 // Make sure we are in a 2's complement system.
265 GOOGLE_COMPILE_ASSERT(
266 (uint32)kint32min == (uint32)0 - (uint32)0x80000000,
267 kint32min_value_error);
268 return "-0x80000000";
269 }
270 return SimpleItoa(field->default_value_int32());
271 case FieldDescriptor::CPPTYPE_UINT32:
272 return SimpleItoa(field->default_value_uint32()) + "u";
273 case FieldDescriptor::CPPTYPE_INT64:
274 // See the comments for CPPTYPE_INT32.
275 if (field->default_value_int64() == kint64min) {
276 // Make sure we are in a 2's complement system.
277 GOOGLE_COMPILE_ASSERT(
278 (uint64)kint64min ==
279 (uint64)0 - (uint64)GOOGLE_LONGLONG(0x8000000000000000),
280 kint64min_value_error);
281 return "GOOGLE_LONGLONG(-0x8000000000000000)";
282 }
283 return "GOOGLE_LONGLONG(" + SimpleItoa(field->default_value_int64()) + ")";
284 case FieldDescriptor::CPPTYPE_UINT64:
285 return "GOOGLE_ULONGLONG(" + SimpleItoa(field->default_value_uint64())+ ")";
286 case FieldDescriptor::CPPTYPE_DOUBLE: {
287 double value = field->default_value_double();
288 if (value == numeric_limits<double>::infinity()) {
289 return "::google::protobuf::internal::Infinity()";
290 } else if (value == -numeric_limits<double>::infinity()) {
291 return "-::google::protobuf::internal::Infinity()";
292 } else if (value != value) {
293 return "::google::protobuf::internal::NaN()";
294 } else {
295 return SimpleDtoa(value);
296 }
297 }
298 case FieldDescriptor::CPPTYPE_FLOAT:
299 {
300 float value = field->default_value_float();
301 if (value == numeric_limits<float>::infinity()) {
302 return "static_cast<float>(::google::protobuf::internal::Infinity())";
303 } else if (value == -numeric_limits<float>::infinity()) {
304 return "static_cast<float>(-::google::protobuf::internal::Infinity())";
305 } else if (value != value) {
306 return "static_cast<float>(::google::protobuf::internal::NaN())";
307 } else {
308 string float_value = SimpleFtoa(value);
309 // If floating point value contains a period (.) or an exponent
310 // (either E or e), then append suffix 'f' to make it a float
311 // literal.
312 if (float_value.find_first_of(".eE") != string::npos) {
313 float_value.push_back('f');
314 }
315 return float_value;
316 }
317 }
318 case FieldDescriptor::CPPTYPE_BOOL:
319 return field->default_value_bool() ? "true" : "false";
320 case FieldDescriptor::CPPTYPE_ENUM:
321 // Lazy: Generate a static_cast because we don't have a helper function
322 // that constructs the full name of an enum value.
323 return strings::Substitute(
324 "static_cast< $0 >($1)",
325 ClassName(field->enum_type(), true),
326 field->default_value_enum()->number());
327 case FieldDescriptor::CPPTYPE_STRING:
328 return "\"" + EscapeTrigraphs(
329 CEscape(field->default_value_string())) +
330 "\"";
331 case FieldDescriptor::CPPTYPE_MESSAGE:
332 return FieldMessageTypeName(field) + "::default_instance()";
333 }
334 // Can't actually get here; make compiler happy. (We could add a default
335 // case above but then we wouldn't get the nice compiler warning when a
336 // new type is added.)
337 GOOGLE_LOG(FATAL) << "Can't get here.";
338 return "";
339 }
340
341 // Convert a file name into a valid identifier.
FilenameIdentifier(const string & filename)342 string FilenameIdentifier(const string& filename) {
343 string result;
344 for (int i = 0; i < filename.size(); i++) {
345 if (ascii_isalnum(filename[i])) {
346 result.push_back(filename[i]);
347 } else {
348 // Not alphanumeric. To avoid any possibility of name conflicts we
349 // use the hex code for the character.
350 result.push_back('_');
351 char buffer[kFastToBufferSize];
352 result.append(FastHexToBuffer(static_cast<uint8>(filename[i]), buffer));
353 }
354 }
355 return result;
356 }
357
358 // Return the name of the AddDescriptors() function for a given file.
GlobalAddDescriptorsName(const string & filename)359 string GlobalAddDescriptorsName(const string& filename) {
360 return "protobuf_AddDesc_" + FilenameIdentifier(filename);
361 }
362
363 // Return the name of the AssignDescriptors() function for a given file.
GlobalAssignDescriptorsName(const string & filename)364 string GlobalAssignDescriptorsName(const string& filename) {
365 return "protobuf_AssignDesc_" + FilenameIdentifier(filename);
366 }
367
368 // Return the name of the ShutdownFile() function for a given file.
GlobalShutdownFileName(const string & filename)369 string GlobalShutdownFileName(const string& filename) {
370 return "protobuf_ShutdownFile_" + FilenameIdentifier(filename);
371 }
372
373 // Escape C++ trigraphs by escaping question marks to \?
EscapeTrigraphs(const string & to_escape)374 string EscapeTrigraphs(const string& to_escape) {
375 return StringReplace(to_escape, "?", "\\?", true);
376 }
377
StaticInitializersForced(const FileDescriptor * file)378 bool StaticInitializersForced(const FileDescriptor* file) {
379 if (HasDescriptorMethods(file) || file->extension_count() > 0) {
380 return true;
381 }
382 for (int i = 0; i < file->message_type_count(); ++i) {
383 if (HasExtension(file->message_type(i))) {
384 return true;
385 }
386 }
387 return false;
388 }
389
PrintHandlingOptionalStaticInitializers(const FileDescriptor * file,io::Printer * printer,const char * with_static_init,const char * without_static_init,const char * var1,const string & val1,const char * var2,const string & val2)390 void PrintHandlingOptionalStaticInitializers(
391 const FileDescriptor* file, io::Printer* printer,
392 const char* with_static_init, const char* without_static_init,
393 const char* var1, const string& val1,
394 const char* var2, const string& val2) {
395 map<string, string> vars;
396 if (var1) {
397 vars[var1] = val1;
398 }
399 if (var2) {
400 vars[var2] = val2;
401 }
402 PrintHandlingOptionalStaticInitializers(
403 vars, file, printer, with_static_init, without_static_init);
404 }
405
PrintHandlingOptionalStaticInitializers(const map<string,string> & vars,const FileDescriptor * file,io::Printer * printer,const char * with_static_init,const char * without_static_init)406 void PrintHandlingOptionalStaticInitializers(
407 const map<string, string>& vars, const FileDescriptor* file,
408 io::Printer* printer, const char* with_static_init,
409 const char* without_static_init) {
410 if (StaticInitializersForced(file)) {
411 printer->Print(vars, with_static_init);
412 } else {
413 printer->Print(vars, (string(
414 "#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER\n") +
415 without_static_init +
416 "#else\n" +
417 with_static_init +
418 "#endif\n").c_str());
419 }
420 }
421
422
HasEnumDefinitions(const Descriptor * message_type)423 static bool HasEnumDefinitions(const Descriptor* message_type) {
424 if (message_type->enum_type_count() > 0) return true;
425 for (int i = 0; i < message_type->nested_type_count(); ++i) {
426 if (HasEnumDefinitions(message_type->nested_type(i))) return true;
427 }
428 return false;
429 }
430
HasEnumDefinitions(const FileDescriptor * file)431 bool HasEnumDefinitions(const FileDescriptor* file) {
432 if (file->enum_type_count() > 0) return true;
433 for (int i = 0; i < file->message_type_count(); ++i) {
434 if (HasEnumDefinitions(file->message_type(i))) return true;
435 }
436 return false;
437 }
438
439 } // namespace cpp
440 } // namespace compiler
441 } // namespace protobuf
442 } // namespace google
443