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 <vector>
37 #include <google/protobuf/stubs/hash.h>
38
39 #include <google/protobuf/compiler/cpp/cpp_helpers.h>
40 #include <google/protobuf/stubs/common.h>
41 #include <google/protobuf/stubs/strutil.h>
42 #include <google/protobuf/stubs/substitute.h>
43
44
45 namespace google {
46 namespace protobuf {
47 namespace compiler {
48 namespace cpp {
49
50 namespace {
51
DotsToUnderscores(const string & name)52 string DotsToUnderscores(const string& name) {
53 return StringReplace(name, ".", "_", true);
54 }
55
DotsToColons(const string & name)56 string DotsToColons(const string& name) {
57 return StringReplace(name, ".", "::", true);
58 }
59
60 const char* const kKeywordList[] = {
61 "and", "and_eq", "asm", "auto", "bitand", "bitor", "bool", "break", "case",
62 "catch", "char", "class", "compl", "const", "const_cast", "continue",
63 "default", "delete", "do", "double", "dynamic_cast", "else", "enum",
64 "explicit", "extern", "false", "float", "for", "friend", "goto", "if",
65 "inline", "int", "long", "mutable", "namespace", "new", "not", "not_eq",
66 "operator", "or", "or_eq", "private", "protected", "public", "register",
67 "reinterpret_cast", "return", "short", "signed", "sizeof", "static",
68 "static_cast", "struct", "switch", "template", "this", "throw", "true", "try",
69 "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual",
70 "void", "volatile", "wchar_t", "while", "xor", "xor_eq"
71 };
72
MakeKeywordsMap()73 hash_set<string> MakeKeywordsMap() {
74 hash_set<string> result;
75 for (int i = 0; i < GOOGLE_ARRAYSIZE(kKeywordList); i++) {
76 result.insert(kKeywordList[i]);
77 }
78 return result;
79 }
80
81 hash_set<string> kKeywords = MakeKeywordsMap();
82
UnderscoresToCamelCase(const string & input,bool cap_next_letter)83 string UnderscoresToCamelCase(const string& input, bool cap_next_letter) {
84 string result;
85 // Note: I distrust ctype.h due to locales.
86 for (int i = 0; i < input.size(); i++) {
87 if ('a' <= input[i] && input[i] <= 'z') {
88 if (cap_next_letter) {
89 result += input[i] + ('A' - 'a');
90 } else {
91 result += input[i];
92 }
93 cap_next_letter = false;
94 } else if ('A' <= input[i] && input[i] <= 'Z') {
95 // Capital letters are left as-is.
96 result += input[i];
97 cap_next_letter = false;
98 } else if ('0' <= input[i] && input[i] <= '9') {
99 result += input[i];
100 cap_next_letter = true;
101 } else {
102 cap_next_letter = true;
103 }
104 }
105 return result;
106 }
107
108 } // namespace
109
110 const char kThickSeparator[] =
111 "// ===================================================================\n";
112 const char kThinSeparator[] =
113 "// -------------------------------------------------------------------\n";
114
ClassName(const Descriptor * descriptor,bool qualified)115 string ClassName(const Descriptor* descriptor, bool qualified) {
116
117 // Find "outer", the descriptor of the top-level message in which
118 // "descriptor" is embedded.
119 const Descriptor* outer = descriptor;
120 while (outer->containing_type() != NULL) outer = outer->containing_type();
121
122 const string& outer_name = outer->full_name();
123 string inner_name = descriptor->full_name().substr(outer_name.size());
124
125 if (qualified) {
126 return "::" + DotsToColons(outer_name) + DotsToUnderscores(inner_name);
127 } else {
128 return outer->name() + DotsToUnderscores(inner_name);
129 }
130 }
131
ClassName(const EnumDescriptor * enum_descriptor,bool qualified)132 string ClassName(const EnumDescriptor* enum_descriptor, bool qualified) {
133 if (enum_descriptor->containing_type() == NULL) {
134 if (qualified) {
135 return DotsToColons(enum_descriptor->full_name());
136 } else {
137 return enum_descriptor->name();
138 }
139 } else {
140 string result = ClassName(enum_descriptor->containing_type(), qualified);
141 result += '_';
142 result += enum_descriptor->name();
143 return result;
144 }
145 }
146
147
SuperClassName(const Descriptor * descriptor)148 string SuperClassName(const Descriptor* descriptor) {
149 return HasDescriptorMethods(descriptor->file()) ?
150 "::google::protobuf::Message" : "::google::protobuf::MessageLite";
151 }
152
FieldName(const FieldDescriptor * field)153 string FieldName(const FieldDescriptor* field) {
154 string result = field->name();
155 LowerString(&result);
156 if (kKeywords.count(result) > 0) {
157 result.append("_");
158 }
159 return result;
160 }
161
FieldConstantName(const FieldDescriptor * field)162 string FieldConstantName(const FieldDescriptor *field) {
163 string field_name = UnderscoresToCamelCase(field->name(), true);
164 string result = "k" + field_name + "FieldNumber";
165
166 if (!field->is_extension() &&
167 field->containing_type()->FindFieldByCamelcaseName(
168 field->camelcase_name()) != field) {
169 // This field's camelcase name is not unique. As a hack, add the field
170 // number to the constant name. This makes the constant rather useless,
171 // but what can we do?
172 result += "_" + SimpleItoa(field->number());
173 }
174
175 return result;
176 }
177
FieldMessageTypeName(const FieldDescriptor * field)178 string FieldMessageTypeName(const FieldDescriptor* field) {
179 // Note: The Google-internal version of Protocol Buffers uses this function
180 // as a hook point for hacks to support legacy code.
181 return ClassName(field->message_type(), true);
182 }
183
StripProto(const string & filename)184 string StripProto(const string& filename) {
185 if (HasSuffixString(filename, ".protodevel")) {
186 return StripSuffixString(filename, ".protodevel");
187 } else {
188 return StripSuffixString(filename, ".proto");
189 }
190 }
191
PrimitiveTypeName(FieldDescriptor::CppType type)192 const char* PrimitiveTypeName(FieldDescriptor::CppType type) {
193 switch (type) {
194 case FieldDescriptor::CPPTYPE_INT32 : return "::google::protobuf::int32";
195 case FieldDescriptor::CPPTYPE_INT64 : return "::google::protobuf::int64";
196 case FieldDescriptor::CPPTYPE_UINT32 : return "::google::protobuf::uint32";
197 case FieldDescriptor::CPPTYPE_UINT64 : return "::google::protobuf::uint64";
198 case FieldDescriptor::CPPTYPE_DOUBLE : return "double";
199 case FieldDescriptor::CPPTYPE_FLOAT : return "float";
200 case FieldDescriptor::CPPTYPE_BOOL : return "bool";
201 case FieldDescriptor::CPPTYPE_ENUM : return "int";
202 case FieldDescriptor::CPPTYPE_STRING : return "::std::string";
203 case FieldDescriptor::CPPTYPE_MESSAGE: return NULL;
204
205 // No default because we want the compiler to complain if any new
206 // CppTypes are added.
207 }
208
209 GOOGLE_LOG(FATAL) << "Can't get here.";
210 return NULL;
211 }
212
DeclaredTypeMethodName(FieldDescriptor::Type type)213 const char* DeclaredTypeMethodName(FieldDescriptor::Type type) {
214 switch (type) {
215 case FieldDescriptor::TYPE_INT32 : return "Int32";
216 case FieldDescriptor::TYPE_INT64 : return "Int64";
217 case FieldDescriptor::TYPE_UINT32 : return "UInt32";
218 case FieldDescriptor::TYPE_UINT64 : return "UInt64";
219 case FieldDescriptor::TYPE_SINT32 : return "SInt32";
220 case FieldDescriptor::TYPE_SINT64 : return "SInt64";
221 case FieldDescriptor::TYPE_FIXED32 : return "Fixed32";
222 case FieldDescriptor::TYPE_FIXED64 : return "Fixed64";
223 case FieldDescriptor::TYPE_SFIXED32: return "SFixed32";
224 case FieldDescriptor::TYPE_SFIXED64: return "SFixed64";
225 case FieldDescriptor::TYPE_FLOAT : return "Float";
226 case FieldDescriptor::TYPE_DOUBLE : return "Double";
227
228 case FieldDescriptor::TYPE_BOOL : return "Bool";
229 case FieldDescriptor::TYPE_ENUM : return "Enum";
230
231 case FieldDescriptor::TYPE_STRING : return "String";
232 case FieldDescriptor::TYPE_BYTES : return "Bytes";
233 case FieldDescriptor::TYPE_GROUP : return "Group";
234 case FieldDescriptor::TYPE_MESSAGE : return "Message";
235
236 // No default because we want the compiler to complain if any new
237 // types are added.
238 }
239 GOOGLE_LOG(FATAL) << "Can't get here.";
240 return "";
241 }
242
DefaultValue(const FieldDescriptor * field)243 string DefaultValue(const FieldDescriptor* field) {
244 switch (field->cpp_type()) {
245 case FieldDescriptor::CPPTYPE_INT32:
246 return SimpleItoa(field->default_value_int32());
247 case FieldDescriptor::CPPTYPE_UINT32:
248 return SimpleItoa(field->default_value_uint32()) + "u";
249 case FieldDescriptor::CPPTYPE_INT64:
250 return "GOOGLE_LONGLONG(" + SimpleItoa(field->default_value_int64()) + ")";
251 case FieldDescriptor::CPPTYPE_UINT64:
252 return "GOOGLE_ULONGLONG(" + SimpleItoa(field->default_value_uint64())+ ")";
253 case FieldDescriptor::CPPTYPE_DOUBLE: {
254 double value = field->default_value_double();
255 if (value == numeric_limits<double>::infinity()) {
256 return "::google::protobuf::internal::Infinity()";
257 } else if (value == -numeric_limits<double>::infinity()) {
258 return "-::google::protobuf::internal::Infinity()";
259 } else if (value != value) {
260 return "::google::protobuf::internal::NaN()";
261 } else {
262 return SimpleDtoa(value);
263 }
264 }
265 case FieldDescriptor::CPPTYPE_FLOAT:
266 {
267 float value = field->default_value_float();
268 if (value == numeric_limits<float>::infinity()) {
269 return "static_cast<float>(::google::protobuf::internal::Infinity())";
270 } else if (value == -numeric_limits<float>::infinity()) {
271 return "static_cast<float>(-::google::protobuf::internal::Infinity())";
272 } else if (value != value) {
273 return "static_cast<float>(::google::protobuf::internal::NaN())";
274 } else {
275 string float_value = SimpleFtoa(value);
276 // If floating point value contains a period (.) or an exponent
277 // (either E or e), then append suffix 'f' to make it a float
278 // literal.
279 if (float_value.find_first_of(".eE") != string::npos) {
280 float_value.push_back('f');
281 }
282 return float_value;
283 }
284 }
285 case FieldDescriptor::CPPTYPE_BOOL:
286 return field->default_value_bool() ? "true" : "false";
287 case FieldDescriptor::CPPTYPE_ENUM:
288 // Lazy: Generate a static_cast because we don't have a helper function
289 // that constructs the full name of an enum value.
290 return strings::Substitute(
291 "static_cast< $0 >($1)",
292 ClassName(field->enum_type(), true),
293 field->default_value_enum()->number());
294 case FieldDescriptor::CPPTYPE_STRING:
295 return "\"" + CEscape(field->default_value_string()) + "\"";
296 case FieldDescriptor::CPPTYPE_MESSAGE:
297 return FieldMessageTypeName(field) + "::default_instance()";
298 }
299 // Can't actually get here; make compiler happy. (We could add a default
300 // case above but then we wouldn't get the nice compiler warning when a
301 // new type is added.)
302 GOOGLE_LOG(FATAL) << "Can't get here.";
303 return "";
304 }
305
306 // Convert a file name into a valid identifier.
FilenameIdentifier(const string & filename)307 string FilenameIdentifier(const string& filename) {
308 string result;
309 for (int i = 0; i < filename.size(); i++) {
310 if (ascii_isalnum(filename[i])) {
311 result.push_back(filename[i]);
312 } else {
313 // Not alphanumeric. To avoid any possibility of name conflicts we
314 // use the hex code for the character.
315 result.push_back('_');
316 char buffer[kFastToBufferSize];
317 result.append(FastHexToBuffer(static_cast<uint8>(filename[i]), buffer));
318 }
319 }
320 return result;
321 }
322
323 // Return the name of the AddDescriptors() function for a given file.
GlobalAddDescriptorsName(const string & filename)324 string GlobalAddDescriptorsName(const string& filename) {
325 return "protobuf_AddDesc_" + FilenameIdentifier(filename);
326 }
327
328 // Return the name of the AssignDescriptors() function for a given file.
GlobalAssignDescriptorsName(const string & filename)329 string GlobalAssignDescriptorsName(const string& filename) {
330 return "protobuf_AssignDesc_" + FilenameIdentifier(filename);
331 }
332
333 // Return the name of the ShutdownFile() function for a given file.
GlobalShutdownFileName(const string & filename)334 string GlobalShutdownFileName(const string& filename) {
335 return "protobuf_ShutdownFile_" + FilenameIdentifier(filename);
336 }
337
338 } // namespace cpp
339 } // namespace compiler
340 } // namespace protobuf
341 } // namespace google
342