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
47namespace google {
48namespace protobuf {
49namespace compiler {
50namespace cpp {
51
52namespace {
53
54string DotsToUnderscores(const string& name) {
55  return StringReplace(name, ".", "_", true);
56}
57
58string DotsToColons(const string& name) {
59  return StringReplace(name, ".", "::", true);
60}
61
62const 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
75hash_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
83hash_set<string> kKeywords = MakeKeywordsMap();
84
85string 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.
112bool 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
126const char kThickSeparator[] =
127  "// ===================================================================\n";
128const char kThinSeparator[] =
129  "// -------------------------------------------------------------------\n";
130
131string 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
148string 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
164string SuperClassName(const Descriptor* descriptor) {
165  return HasDescriptorMethods(descriptor->file()) ?
166      "::google::protobuf::Message" : "::google::protobuf::MessageLite";
167}
168
169string 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
178string 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
194string 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
200string StripProto(const string& filename) {
201  if (HasSuffixString(filename, ".protodevel")) {
202    return StripSuffixString(filename, ".protodevel");
203  } else {
204    return StripSuffixString(filename, ".proto");
205  }
206}
207
208const 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
229const 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
259string 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.
342string 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.
359string GlobalAddDescriptorsName(const string& filename) {
360  return "protobuf_AddDesc_" + FilenameIdentifier(filename);
361}
362
363// Return the name of the AssignDescriptors() function for a given file.
364string GlobalAssignDescriptorsName(const string& filename) {
365  return "protobuf_AssignDesc_" + FilenameIdentifier(filename);
366}
367
368// Return the name of the ShutdownFile() function for a given file.
369string GlobalShutdownFileName(const string& filename) {
370  return "protobuf_ShutdownFile_" + FilenameIdentifier(filename);
371}
372
373// Escape C++ trigraphs by escaping question marks to \?
374string EscapeTrigraphs(const string& to_escape) {
375  return StringReplace(to_escape, "?", "\\?", true);
376}
377
378bool 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
390void 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
406void 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
423static 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
431bool 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