1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/
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 #ifndef _MSC_VER
32 #include <unistd.h>
33 #endif
34 #include <climits>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <fstream>
38 #include <iostream>
39 #include <sstream>
40 #include <stdlib.h>
41 #include <unordered_set>
42 #include <vector>
43
44 #include <google/protobuf/compiler/objectivec/objectivec_helpers.h>
45 #include <google/protobuf/compiler/objectivec/objectivec_nsobject_methods.h>
46 #include <google/protobuf/descriptor.pb.h>
47 #include <google/protobuf/io/coded_stream.h>
48 #include <google/protobuf/io/printer.h>
49 #include <google/protobuf/io/zero_copy_stream_impl.h>
50 #include <google/protobuf/io/io_win32.h>
51 #include <google/protobuf/stubs/port.h>
52 #include <google/protobuf/stubs/strutil.h>
53
54 // NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
55 // error cases, so it seems to be ok to use as a back door for errors.
56
57 namespace google {
58 namespace protobuf {
59 namespace compiler {
60 namespace objectivec {
61
62 // <io.h> is transitively included in this file. Import the functions explicitly
63 // in this port namespace to avoid ambiguous definition.
64 namespace posix {
65 #ifdef _WIN32
66 using ::google::protobuf::io::win32::open;
67 #else
68 using ::open;
69 #endif
70 } // namespace port
71
Options()72 Options::Options() {
73 // Default is the value of the env for the package prefixes.
74 const char* file_path = getenv("GPB_OBJC_EXPECTED_PACKAGE_PREFIXES");
75 if (file_path) {
76 expected_prefixes_path = file_path;
77 }
78 const char* suppressions = getenv("GPB_OBJC_EXPECTED_PACKAGE_PREFIXES_SUPPRESSIONS");
79 if (suppressions) {
80 SplitStringUsing(suppressions, ";", &expected_prefixes_suppressions);
81 }
82 }
83
84 namespace {
85
MakeWordsMap(const char * const words[],size_t num_words)86 std::unordered_set<string> MakeWordsMap(const char* const words[], size_t num_words) {
87 std::unordered_set<string> result;
88 for (int i = 0; i < num_words; i++) {
89 result.insert(words[i]);
90 }
91 return result;
92 }
93
94 const char* const kUpperSegmentsList[] = {"url", "http", "https"};
95
96 std::unordered_set<string> kUpperSegments =
97 MakeWordsMap(kUpperSegmentsList, GOOGLE_ARRAYSIZE(kUpperSegmentsList));
98
ascii_isnewline(char c)99 bool ascii_isnewline(char c) {
100 return c == '\n' || c == '\r';
101 }
102
103 // Internal helper for name handing.
104 // Do not expose this outside of helpers, stick to having functions for specific
105 // cases (ClassName(), FieldName()), so there is always consistent suffix rules.
UnderscoresToCamelCase(const string & input,bool first_capitalized)106 string UnderscoresToCamelCase(const string& input, bool first_capitalized) {
107 std::vector<string> values;
108 string current;
109
110 bool last_char_was_number = false;
111 bool last_char_was_lower = false;
112 bool last_char_was_upper = false;
113 for (int i = 0; i < input.size(); i++) {
114 char c = input[i];
115 if (ascii_isdigit(c)) {
116 if (!last_char_was_number) {
117 values.push_back(current);
118 current = "";
119 }
120 current += c;
121 last_char_was_number = last_char_was_lower = last_char_was_upper = false;
122 last_char_was_number = true;
123 } else if (ascii_islower(c)) {
124 // lowercase letter can follow a lowercase or uppercase letter
125 if (!last_char_was_lower && !last_char_was_upper) {
126 values.push_back(current);
127 current = "";
128 }
129 current += c; // already lower
130 last_char_was_number = last_char_was_lower = last_char_was_upper = false;
131 last_char_was_lower = true;
132 } else if (ascii_isupper(c)) {
133 if (!last_char_was_upper) {
134 values.push_back(current);
135 current = "";
136 }
137 current += ascii_tolower(c);
138 last_char_was_number = last_char_was_lower = last_char_was_upper = false;
139 last_char_was_upper = true;
140 } else {
141 last_char_was_number = last_char_was_lower = last_char_was_upper = false;
142 }
143 }
144 values.push_back(current);
145
146 string result;
147 bool first_segment_forces_upper = false;
148 for (std::vector<string>::iterator i = values.begin(); i != values.end(); ++i) {
149 string value = *i;
150 bool all_upper = (kUpperSegments.count(value) > 0);
151 if (all_upper && (result.length() == 0)) {
152 first_segment_forces_upper = true;
153 }
154 for (int j = 0; j < value.length(); j++) {
155 if (j == 0 || all_upper) {
156 value[j] = ascii_toupper(value[j]);
157 } else {
158 // Nothing, already in lower.
159 }
160 }
161 result += value;
162 }
163 if ((result.length() != 0) &&
164 !first_capitalized &&
165 !first_segment_forces_upper) {
166 result[0] = ascii_tolower(result[0]);
167 }
168 return result;
169 }
170
171 const char* const kReservedWordList[] = {
172 // Note NSObject Methods:
173 // These are brought in from objectivec_nsobject_methods.h that is generated
174 // using method_dump.sh. See kNSObjectMethods below.
175
176 // Objective C "keywords" that aren't in C
177 // From
178 // http://stackoverflow.com/questions/1873630/reserved-keywords-in-objective-c
179 // with some others added on.
180 "id", "_cmd", "super", "in", "out", "inout", "bycopy", "byref", "oneway",
181 "self", "instancetype", "nullable", "nonnull", "nil", "Nil",
182 "YES", "NO", "weak",
183
184 // C/C++ keywords (Incl C++ 0x11)
185 // From http://en.cppreference.com/w/cpp/keywords
186 "and", "and_eq", "alignas", "alignof", "asm", "auto", "bitand", "bitor",
187 "bool", "break", "case", "catch", "char", "char16_t", "char32_t", "class",
188 "compl", "const", "constexpr", "const_cast", "continue", "decltype",
189 "default", "delete", "double", "dynamic_cast", "else", "enum", "explicit",
190 "export", "extern ", "false", "float", "for", "friend", "goto", "if",
191 "inline", "int", "long", "mutable", "namespace", "new", "noexcept", "not",
192 "not_eq", "nullptr", "operator", "or", "or_eq", "private", "protected",
193 "public", "register", "reinterpret_cast", "return", "short", "signed",
194 "sizeof", "static", "static_assert", "static_cast", "struct", "switch",
195 "template", "this", "thread_local", "throw", "true", "try", "typedef",
196 "typeid", "typename", "union", "unsigned", "using", "virtual", "void",
197 "volatile", "wchar_t", "while", "xor", "xor_eq",
198
199 // C99 keywords
200 // From
201 // http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fkeyw.htm
202 "restrict",
203
204 // GCC/Clang extension
205 "typeof",
206
207 // Not a keyword, but will break you
208 "NULL",
209
210 // Objective-C Runtime typedefs
211 // From <obc/runtime.h>
212 "Category", "Ivar", "Method", "Protocol",
213
214 // GPBMessage Methods
215 // Only need to add instance methods that may conflict with
216 // method declared in protos. The main cases are methods
217 // that take no arguments, or setFoo:/hasFoo: type methods.
218 "clear", "data", "delimitedData", "descriptor", "extensionRegistry",
219 "extensionsCurrentlySet", "initialized", "isInitialized", "serializedSize",
220 "sortedExtensionsInUse", "unknownFields",
221
222 // MacTypes.h names
223 "Fixed", "Fract", "Size", "LogicalAddress", "PhysicalAddress", "ByteCount",
224 "ByteOffset", "Duration", "AbsoluteTime", "OptionBits", "ItemCount",
225 "PBVersion", "ScriptCode", "LangCode", "RegionCode", "OSType",
226 "ProcessSerialNumber", "Point", "Rect", "FixedPoint", "FixedRect", "Style",
227 "StyleParameter", "StyleField", "TimeScale", "TimeBase", "TimeRecord",
228 };
229
230 // returns true is input starts with __ or _[A-Z] which are reserved identifiers
231 // in C/ C++. All calls should go through UnderscoresToCamelCase before getting here
232 // but this verifies and allows for future expansion if we decide to redefine what a
233 // reserved C identifier is (for example the GNU list
234 // https://www.gnu.org/software/libc/manual/html_node/Reserved-Names.html )
IsReservedCIdentifier(const string & input)235 bool IsReservedCIdentifier(const string& input) {
236 if (input.length() > 2) {
237 if (input.at(0) == '_') {
238 if (isupper(input.at(1)) || input.at(1) == '_') {
239 return true;
240 }
241 }
242 }
243 return false;
244 }
245
SanitizeNameForObjC(const string & prefix,const string & input,const string & extension,string * out_suffix_added)246 string SanitizeNameForObjC(const string& prefix,
247 const string& input,
248 const string& extension,
249 string* out_suffix_added) {
250 static const std::unordered_set<string> kReservedWords =
251 MakeWordsMap(kReservedWordList, GOOGLE_ARRAYSIZE(kReservedWordList));
252 static const std::unordered_set<string> kNSObjectMethods =
253 MakeWordsMap(kNSObjectMethodsList, GOOGLE_ARRAYSIZE(kNSObjectMethodsList));
254 string sanitized;
255 // We add the prefix in the cases where the string is missing a prefix.
256 // We define "missing a prefix" as where 'input':
257 // a) Doesn't start with the prefix or
258 // b) Isn't equivalent to the prefix or
259 // c) Has the prefix, but the letter after the prefix is lowercase
260 if (HasPrefixString(input, prefix)) {
261 if (input.length() == prefix.length() || !ascii_isupper(input[prefix.length()])) {
262 sanitized = prefix + input;
263 } else {
264 sanitized = input;
265 }
266 } else {
267 sanitized = prefix + input;
268 }
269 if (IsReservedCIdentifier(sanitized) ||
270 (kReservedWords.count(sanitized) > 0) ||
271 (kNSObjectMethods.count(sanitized) > 0)) {
272 if (out_suffix_added) *out_suffix_added = extension;
273 return sanitized + extension;
274 }
275 if (out_suffix_added) out_suffix_added->clear();
276 return sanitized;
277 }
278
NameFromFieldDescriptor(const FieldDescriptor * field)279 string NameFromFieldDescriptor(const FieldDescriptor* field) {
280 if (field->type() == FieldDescriptor::TYPE_GROUP) {
281 return field->message_type()->name();
282 } else {
283 return field->name();
284 }
285 }
286
PathSplit(const string & path,string * directory,string * basename)287 void PathSplit(const string& path, string* directory, string* basename) {
288 string::size_type last_slash = path.rfind('/');
289 if (last_slash == string::npos) {
290 if (directory) {
291 *directory = "";
292 }
293 if (basename) {
294 *basename = path;
295 }
296 } else {
297 if (directory) {
298 *directory = path.substr(0, last_slash);
299 }
300 if (basename) {
301 *basename = path.substr(last_slash + 1);
302 }
303 }
304 }
305
IsSpecialName(const string & name,const string * special_names,size_t count)306 bool IsSpecialName(const string& name, const string* special_names,
307 size_t count) {
308 for (size_t i = 0; i < count; ++i) {
309 size_t length = special_names[i].length();
310 if (name.compare(0, length, special_names[i]) == 0) {
311 if (name.length() > length) {
312 // If name is longer than the retained_name[i] that it matches
313 // the next character must be not lower case (newton vs newTon vs
314 // new_ton).
315 return !ascii_islower(name[length]);
316 } else {
317 return true;
318 }
319 }
320 }
321 return false;
322 }
323
GetZeroEnumNameForFlagType(const FlagType flag_type)324 string GetZeroEnumNameForFlagType(const FlagType flag_type) {
325 switch(flag_type) {
326 case FLAGTYPE_DESCRIPTOR_INITIALIZATION:
327 return "GPBDescriptorInitializationFlag_None";
328 case FLAGTYPE_EXTENSION:
329 return "GPBExtensionNone";
330 case FLAGTYPE_FIELD:
331 return "GPBFieldNone";
332 default:
333 GOOGLE_LOG(FATAL) << "Can't get here.";
334 return "0";
335 }
336 }
337
GetEnumNameForFlagType(const FlagType flag_type)338 string GetEnumNameForFlagType(const FlagType flag_type) {
339 switch(flag_type) {
340 case FLAGTYPE_DESCRIPTOR_INITIALIZATION:
341 return "GPBDescriptorInitializationFlags";
342 case FLAGTYPE_EXTENSION:
343 return "GPBExtensionOptions";
344 case FLAGTYPE_FIELD:
345 return "GPBFieldFlags";
346 default:
347 GOOGLE_LOG(FATAL) << "Can't get here.";
348 return string();
349 }
350 }
351
352 } // namespace
353
354 // Escape C++ trigraphs by escaping question marks to \?
EscapeTrigraphs(const string & to_escape)355 string EscapeTrigraphs(const string& to_escape) {
356 return StringReplace(to_escape, "?", "\\?", true);
357 }
358
StripProto(const string & filename)359 string StripProto(const string& filename) {
360 if (HasSuffixString(filename, ".protodevel")) {
361 return StripSuffixString(filename, ".protodevel");
362 } else {
363 return StripSuffixString(filename, ".proto");
364 }
365 }
366
TrimWhitespace(StringPiece * input)367 void TrimWhitespace(StringPiece* input) {
368 while (!input->empty() && ascii_isspace(*input->data())) {
369 input->remove_prefix(1);
370 }
371 while (!input->empty() && ascii_isspace((*input)[input->length() - 1])) {
372 input->remove_suffix(1);
373 }
374 }
375
376
IsRetainedName(const string & name)377 bool IsRetainedName(const string& name) {
378 // List of prefixes from
379 // http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html
380 static const string retained_names[] = {"new", "alloc", "copy",
381 "mutableCopy"};
382 return IsSpecialName(name, retained_names,
383 sizeof(retained_names) / sizeof(retained_names[0]));
384 }
385
IsInitName(const string & name)386 bool IsInitName(const string& name) {
387 static const string init_names[] = {"init"};
388 return IsSpecialName(name, init_names,
389 sizeof(init_names) / sizeof(init_names[0]));
390 }
391
BaseFileName(const FileDescriptor * file)392 string BaseFileName(const FileDescriptor* file) {
393 string basename;
394 PathSplit(file->name(), NULL, &basename);
395 return basename;
396 }
397
FileClassPrefix(const FileDescriptor * file)398 string FileClassPrefix(const FileDescriptor* file) {
399 // Default is empty string, no need to check has_objc_class_prefix.
400 string result = file->options().objc_class_prefix();
401 return result;
402 }
403
FilePath(const FileDescriptor * file)404 string FilePath(const FileDescriptor* file) {
405 string output;
406 string basename;
407 string directory;
408 PathSplit(file->name(), &directory, &basename);
409 if (directory.length() > 0) {
410 output = directory + "/";
411 }
412 basename = StripProto(basename);
413
414 // CamelCase to be more ObjC friendly.
415 basename = UnderscoresToCamelCase(basename, true);
416
417 output += basename;
418 return output;
419 }
420
FilePathBasename(const FileDescriptor * file)421 string FilePathBasename(const FileDescriptor* file) {
422 string output;
423 string basename;
424 string directory;
425 PathSplit(file->name(), &directory, &basename);
426 basename = StripProto(basename);
427
428 // CamelCase to be more ObjC friendly.
429 output = UnderscoresToCamelCase(basename, true);
430
431 return output;
432 }
433
FileClassName(const FileDescriptor * file)434 string FileClassName(const FileDescriptor* file) {
435 const string prefix = FileClassPrefix(file);
436 const string name = UnderscoresToCamelCase(StripProto(BaseFileName(file)), true) + "Root";
437 // There aren't really any reserved words that end in "Root", but playing
438 // it safe and checking.
439 return SanitizeNameForObjC(prefix, name, "_RootClass", NULL);
440 }
441
ClassNameWorker(const Descriptor * descriptor)442 string ClassNameWorker(const Descriptor* descriptor) {
443 string name;
444 if (descriptor->containing_type() != NULL) {
445 name = ClassNameWorker(descriptor->containing_type());
446 name += "_";
447 }
448 return name + descriptor->name();
449 }
450
ClassNameWorker(const EnumDescriptor * descriptor)451 string ClassNameWorker(const EnumDescriptor* descriptor) {
452 string name;
453 if (descriptor->containing_type() != NULL) {
454 name = ClassNameWorker(descriptor->containing_type());
455 name += "_";
456 }
457 return name + descriptor->name();
458 }
459
ClassName(const Descriptor * descriptor)460 string ClassName(const Descriptor* descriptor) {
461 return ClassName(descriptor, NULL);
462 }
463
ClassName(const Descriptor * descriptor,string * out_suffix_added)464 string ClassName(const Descriptor* descriptor, string* out_suffix_added) {
465 // 1. Message names are used as is (style calls for CamelCase, trust it).
466 // 2. Check for reserved word at the very end and then suffix things.
467 const string prefix = FileClassPrefix(descriptor->file());
468 const string name = ClassNameWorker(descriptor);
469 return SanitizeNameForObjC(prefix, name, "_Class", out_suffix_added);
470 }
471
EnumName(const EnumDescriptor * descriptor)472 string EnumName(const EnumDescriptor* descriptor) {
473 // 1. Enum names are used as is (style calls for CamelCase, trust it).
474 // 2. Check for reserved word at the every end and then suffix things.
475 // message Fixed {
476 // message Size {...}
477 // enum Mumble {...}
478 // ...
479 // }
480 // yields Fixed_Class, Fixed_Size.
481 const string prefix = FileClassPrefix(descriptor->file());
482 const string name = ClassNameWorker(descriptor);
483 return SanitizeNameForObjC(prefix, name, "_Enum", NULL);
484 }
485
EnumValueName(const EnumValueDescriptor * descriptor)486 string EnumValueName(const EnumValueDescriptor* descriptor) {
487 // Because of the Switch enum compatibility, the name on the enum has to have
488 // the suffix handing, so it slightly diverges from how nested classes work.
489 // enum Fixed {
490 // FOO = 1
491 // }
492 // yields Fixed_Enum and Fixed_Enum_Foo (not Fixed_Foo).
493 const string class_name = EnumName(descriptor->type());
494 const string value_str = UnderscoresToCamelCase(descriptor->name(), true);
495 const string name = class_name + "_" + value_str;
496 // There aren't really any reserved words with an underscore and a leading
497 // capital letter, but playing it safe and checking.
498 return SanitizeNameForObjC("", name, "_Value", NULL);
499 }
500
EnumValueShortName(const EnumValueDescriptor * descriptor)501 string EnumValueShortName(const EnumValueDescriptor* descriptor) {
502 // Enum value names (EnumValueName above) are the enum name turned into
503 // a class name and then the value name is CamelCased and concatenated; the
504 // whole thing then gets sanitized for reserved words.
505 // The "short name" is intended to be the final leaf, the value name; but
506 // you can't simply send that off to sanitize as that could result in it
507 // getting modified when the full name didn't. For example enum
508 // "StorageModes" has a value "retain". So the full name is
509 // "StorageModes_Retain", but if we sanitize "retain" it would become
510 // "RetainValue".
511 // So the right way to get the short name is to take the full enum name
512 // and then strip off the enum name (leaving the value name and anything
513 // done by sanitize).
514 const string class_name = EnumName(descriptor->type());
515 const string long_name_prefix = class_name + "_";
516 const string long_name = EnumValueName(descriptor);
517 return StripPrefixString(long_name, long_name_prefix);
518 }
519
UnCamelCaseEnumShortName(const string & name)520 string UnCamelCaseEnumShortName(const string& name) {
521 string result;
522 for (int i = 0; i < name.size(); i++) {
523 char c = name[i];
524 if (i > 0 && ascii_isupper(c)) {
525 result += '_';
526 }
527 result += ascii_toupper(c);
528 }
529 return result;
530 }
531
ExtensionMethodName(const FieldDescriptor * descriptor)532 string ExtensionMethodName(const FieldDescriptor* descriptor) {
533 const string name = NameFromFieldDescriptor(descriptor);
534 const string result = UnderscoresToCamelCase(name, false);
535 return SanitizeNameForObjC("", result, "_Extension", NULL);
536 }
537
FieldName(const FieldDescriptor * field)538 string FieldName(const FieldDescriptor* field) {
539 const string name = NameFromFieldDescriptor(field);
540 string result = UnderscoresToCamelCase(name, false);
541 if (field->is_repeated() && !field->is_map()) {
542 // Add "Array" before do check for reserved worlds.
543 result += "Array";
544 } else {
545 // If it wasn't repeated, but ends in "Array", force on the _p suffix.
546 if (HasSuffixString(result, "Array")) {
547 result += "_p";
548 }
549 }
550 return SanitizeNameForObjC("", result, "_p", NULL);
551 }
552
FieldNameCapitalized(const FieldDescriptor * field)553 string FieldNameCapitalized(const FieldDescriptor* field) {
554 // Want the same suffix handling, so upcase the first letter of the other
555 // name.
556 string result = FieldName(field);
557 if (result.length() > 0) {
558 result[0] = ascii_toupper(result[0]);
559 }
560 return result;
561 }
562
OneofEnumName(const OneofDescriptor * descriptor)563 string OneofEnumName(const OneofDescriptor* descriptor) {
564 const Descriptor* fieldDescriptor = descriptor->containing_type();
565 string name = ClassName(fieldDescriptor);
566 name += "_" + UnderscoresToCamelCase(descriptor->name(), true) + "_OneOfCase";
567 // No sanitize needed because the OS never has names that end in _OneOfCase.
568 return name;
569 }
570
OneofName(const OneofDescriptor * descriptor)571 string OneofName(const OneofDescriptor* descriptor) {
572 string name = UnderscoresToCamelCase(descriptor->name(), false);
573 // No sanitize needed because it gets OneOfCase added and that shouldn't
574 // ever conflict.
575 return name;
576 }
577
OneofNameCapitalized(const OneofDescriptor * descriptor)578 string OneofNameCapitalized(const OneofDescriptor* descriptor) {
579 // Use the common handling and then up-case the first letter.
580 string result = OneofName(descriptor);
581 if (result.length() > 0) {
582 result[0] = ascii_toupper(result[0]);
583 }
584 return result;
585 }
586
UnCamelCaseFieldName(const string & name,const FieldDescriptor * field)587 string UnCamelCaseFieldName(const string& name, const FieldDescriptor* field) {
588 string worker(name);
589 if (HasSuffixString(worker, "_p")) {
590 worker = StripSuffixString(worker, "_p");
591 }
592 if (field->is_repeated() && HasSuffixString(worker, "Array")) {
593 worker = StripSuffixString(worker, "Array");
594 }
595 if (field->type() == FieldDescriptor::TYPE_GROUP) {
596 if (worker.length() > 0) {
597 if (ascii_islower(worker[0])) {
598 worker[0] = ascii_toupper(worker[0]);
599 }
600 }
601 return worker;
602 } else {
603 string result;
604 for (int i = 0; i < worker.size(); i++) {
605 char c = worker[i];
606 if (ascii_isupper(c)) {
607 if (i > 0) {
608 result += '_';
609 }
610 result += ascii_tolower(c);
611 } else {
612 result += c;
613 }
614 }
615 return result;
616 }
617 }
618
GetCapitalizedType(const FieldDescriptor * field)619 string GetCapitalizedType(const FieldDescriptor* field) {
620 switch (field->type()) {
621 case FieldDescriptor::TYPE_INT32:
622 return "Int32";
623 case FieldDescriptor::TYPE_UINT32:
624 return "UInt32";
625 case FieldDescriptor::TYPE_SINT32:
626 return "SInt32";
627 case FieldDescriptor::TYPE_FIXED32:
628 return "Fixed32";
629 case FieldDescriptor::TYPE_SFIXED32:
630 return "SFixed32";
631 case FieldDescriptor::TYPE_INT64:
632 return "Int64";
633 case FieldDescriptor::TYPE_UINT64:
634 return "UInt64";
635 case FieldDescriptor::TYPE_SINT64:
636 return "SInt64";
637 case FieldDescriptor::TYPE_FIXED64:
638 return "Fixed64";
639 case FieldDescriptor::TYPE_SFIXED64:
640 return "SFixed64";
641 case FieldDescriptor::TYPE_FLOAT:
642 return "Float";
643 case FieldDescriptor::TYPE_DOUBLE:
644 return "Double";
645 case FieldDescriptor::TYPE_BOOL:
646 return "Bool";
647 case FieldDescriptor::TYPE_STRING:
648 return "String";
649 case FieldDescriptor::TYPE_BYTES:
650 return "Bytes";
651 case FieldDescriptor::TYPE_ENUM:
652 return "Enum";
653 case FieldDescriptor::TYPE_GROUP:
654 return "Group";
655 case FieldDescriptor::TYPE_MESSAGE:
656 return "Message";
657 }
658
659 // Some compilers report reaching end of function even though all cases of
660 // the enum are handed in the switch.
661 GOOGLE_LOG(FATAL) << "Can't get here.";
662 return string();
663 }
664
GetObjectiveCType(FieldDescriptor::Type field_type)665 ObjectiveCType GetObjectiveCType(FieldDescriptor::Type field_type) {
666 switch (field_type) {
667 case FieldDescriptor::TYPE_INT32:
668 case FieldDescriptor::TYPE_SINT32:
669 case FieldDescriptor::TYPE_SFIXED32:
670 return OBJECTIVECTYPE_INT32;
671
672 case FieldDescriptor::TYPE_UINT32:
673 case FieldDescriptor::TYPE_FIXED32:
674 return OBJECTIVECTYPE_UINT32;
675
676 case FieldDescriptor::TYPE_INT64:
677 case FieldDescriptor::TYPE_SINT64:
678 case FieldDescriptor::TYPE_SFIXED64:
679 return OBJECTIVECTYPE_INT64;
680
681 case FieldDescriptor::TYPE_UINT64:
682 case FieldDescriptor::TYPE_FIXED64:
683 return OBJECTIVECTYPE_UINT64;
684
685 case FieldDescriptor::TYPE_FLOAT:
686 return OBJECTIVECTYPE_FLOAT;
687
688 case FieldDescriptor::TYPE_DOUBLE:
689 return OBJECTIVECTYPE_DOUBLE;
690
691 case FieldDescriptor::TYPE_BOOL:
692 return OBJECTIVECTYPE_BOOLEAN;
693
694 case FieldDescriptor::TYPE_STRING:
695 return OBJECTIVECTYPE_STRING;
696
697 case FieldDescriptor::TYPE_BYTES:
698 return OBJECTIVECTYPE_DATA;
699
700 case FieldDescriptor::TYPE_ENUM:
701 return OBJECTIVECTYPE_ENUM;
702
703 case FieldDescriptor::TYPE_GROUP:
704 case FieldDescriptor::TYPE_MESSAGE:
705 return OBJECTIVECTYPE_MESSAGE;
706 }
707
708 // Some compilers report reaching end of function even though all cases of
709 // the enum are handed in the switch.
710 GOOGLE_LOG(FATAL) << "Can't get here.";
711 return OBJECTIVECTYPE_INT32;
712 }
713
IsPrimitiveType(const FieldDescriptor * field)714 bool IsPrimitiveType(const FieldDescriptor* field) {
715 ObjectiveCType type = GetObjectiveCType(field);
716 switch (type) {
717 case OBJECTIVECTYPE_INT32:
718 case OBJECTIVECTYPE_UINT32:
719 case OBJECTIVECTYPE_INT64:
720 case OBJECTIVECTYPE_UINT64:
721 case OBJECTIVECTYPE_FLOAT:
722 case OBJECTIVECTYPE_DOUBLE:
723 case OBJECTIVECTYPE_BOOLEAN:
724 case OBJECTIVECTYPE_ENUM:
725 return true;
726 break;
727 default:
728 return false;
729 }
730 }
731
IsReferenceType(const FieldDescriptor * field)732 bool IsReferenceType(const FieldDescriptor* field) {
733 return !IsPrimitiveType(field);
734 }
735
HandleExtremeFloatingPoint(string val,bool add_float_suffix)736 static string HandleExtremeFloatingPoint(string val, bool add_float_suffix) {
737 if (val == "nan") {
738 return "NAN";
739 } else if (val == "inf") {
740 return "INFINITY";
741 } else if (val == "-inf") {
742 return "-INFINITY";
743 } else {
744 // float strings with ., e or E need to have f appended
745 if (add_float_suffix &&
746 (val.find(".") != string::npos || val.find("e") != string::npos ||
747 val.find("E") != string::npos)) {
748 val += "f";
749 }
750 return val;
751 }
752 }
753
GPBGenericValueFieldName(const FieldDescriptor * field)754 string GPBGenericValueFieldName(const FieldDescriptor* field) {
755 // Returns the field within the GPBGenericValue union to use for the given
756 // field.
757 if (field->is_repeated()) {
758 return "valueMessage";
759 }
760 switch (field->cpp_type()) {
761 case FieldDescriptor::CPPTYPE_INT32:
762 return "valueInt32";
763 case FieldDescriptor::CPPTYPE_UINT32:
764 return "valueUInt32";
765 case FieldDescriptor::CPPTYPE_INT64:
766 return "valueInt64";
767 case FieldDescriptor::CPPTYPE_UINT64:
768 return "valueUInt64";
769 case FieldDescriptor::CPPTYPE_FLOAT:
770 return "valueFloat";
771 case FieldDescriptor::CPPTYPE_DOUBLE:
772 return "valueDouble";
773 case FieldDescriptor::CPPTYPE_BOOL:
774 return "valueBool";
775 case FieldDescriptor::CPPTYPE_STRING:
776 if (field->type() == FieldDescriptor::TYPE_BYTES) {
777 return "valueData";
778 } else {
779 return "valueString";
780 }
781 case FieldDescriptor::CPPTYPE_ENUM:
782 return "valueEnum";
783 case FieldDescriptor::CPPTYPE_MESSAGE:
784 return "valueMessage";
785 }
786
787 // Some compilers report reaching end of function even though all cases of
788 // the enum are handed in the switch.
789 GOOGLE_LOG(FATAL) << "Can't get here.";
790 return string();
791 }
792
793
DefaultValue(const FieldDescriptor * field)794 string DefaultValue(const FieldDescriptor* field) {
795 // Repeated fields don't have defaults.
796 if (field->is_repeated()) {
797 return "nil";
798 }
799
800 // Switch on cpp_type since we need to know which default_value_* method
801 // of FieldDescriptor to call.
802 switch (field->cpp_type()) {
803 case FieldDescriptor::CPPTYPE_INT32:
804 // gcc and llvm reject the decimal form of kint32min and kint64min.
805 if (field->default_value_int32() == INT_MIN) {
806 return "-0x80000000";
807 }
808 return StrCat(field->default_value_int32());
809 case FieldDescriptor::CPPTYPE_UINT32:
810 return StrCat(field->default_value_uint32()) + "U";
811 case FieldDescriptor::CPPTYPE_INT64:
812 // gcc and llvm reject the decimal form of kint32min and kint64min.
813 if (field->default_value_int64() == LLONG_MIN) {
814 return "-0x8000000000000000LL";
815 }
816 return StrCat(field->default_value_int64()) + "LL";
817 case FieldDescriptor::CPPTYPE_UINT64:
818 return StrCat(field->default_value_uint64()) + "ULL";
819 case FieldDescriptor::CPPTYPE_DOUBLE:
820 return HandleExtremeFloatingPoint(
821 SimpleDtoa(field->default_value_double()), false);
822 case FieldDescriptor::CPPTYPE_FLOAT:
823 return HandleExtremeFloatingPoint(
824 SimpleFtoa(field->default_value_float()), true);
825 case FieldDescriptor::CPPTYPE_BOOL:
826 return field->default_value_bool() ? "YES" : "NO";
827 case FieldDescriptor::CPPTYPE_STRING: {
828 const bool has_default_value = field->has_default_value();
829 const string& default_string = field->default_value_string();
830 if (!has_default_value || default_string.length() == 0) {
831 // If the field is defined as being the empty string,
832 // then we will just assign to nil, as the empty string is the
833 // default for both strings and data.
834 return "nil";
835 }
836 if (field->type() == FieldDescriptor::TYPE_BYTES) {
837 // We want constant fields in our data structures so we can
838 // declare them as static. To achieve this we cheat and stuff
839 // a escaped c string (prefixed with a length) into the data
840 // field, and cast it to an (NSData*) so it will compile.
841 // The runtime library knows how to handle it.
842
843 // Must convert to a standard byte order for packing length into
844 // a cstring.
845 uint32 length = ghtonl(default_string.length());
846 string bytes((const char*)&length, sizeof(length));
847 bytes.append(default_string);
848 return "(NSData*)\"" + EscapeTrigraphs(CEscape(bytes)) + "\"";
849 } else {
850 return "@\"" + EscapeTrigraphs(CEscape(default_string)) + "\"";
851 }
852 }
853 case FieldDescriptor::CPPTYPE_ENUM:
854 return EnumValueName(field->default_value_enum());
855 case FieldDescriptor::CPPTYPE_MESSAGE:
856 return "nil";
857 }
858
859 // Some compilers report reaching end of function even though all cases of
860 // the enum are handed in the switch.
861 GOOGLE_LOG(FATAL) << "Can't get here.";
862 return string();
863 }
864
HasNonZeroDefaultValue(const FieldDescriptor * field)865 bool HasNonZeroDefaultValue(const FieldDescriptor* field) {
866 // Repeated fields don't have defaults.
867 if (field->is_repeated()) {
868 return false;
869 }
870
871 // As much as checking field->has_default_value() seems useful, it isn't
872 // because of enums. proto2 syntax allows the first item in an enum (the
873 // default) to be non zero. So checking field->has_default_value() would
874 // result in missing this non zero default. See MessageWithOneBasedEnum in
875 // objectivec/Tests/unittest_objc.proto for a test Message to confirm this.
876
877 // Some proto file set the default to the zero value, so make sure the value
878 // isn't the zero case.
879 switch (field->cpp_type()) {
880 case FieldDescriptor::CPPTYPE_INT32:
881 return field->default_value_int32() != 0;
882 case FieldDescriptor::CPPTYPE_UINT32:
883 return field->default_value_uint32() != 0U;
884 case FieldDescriptor::CPPTYPE_INT64:
885 return field->default_value_int64() != 0LL;
886 case FieldDescriptor::CPPTYPE_UINT64:
887 return field->default_value_uint64() != 0ULL;
888 case FieldDescriptor::CPPTYPE_DOUBLE:
889 return field->default_value_double() != 0.0;
890 case FieldDescriptor::CPPTYPE_FLOAT:
891 return field->default_value_float() != 0.0f;
892 case FieldDescriptor::CPPTYPE_BOOL:
893 return field->default_value_bool();
894 case FieldDescriptor::CPPTYPE_STRING: {
895 const string& default_string = field->default_value_string();
896 return default_string.length() != 0;
897 }
898 case FieldDescriptor::CPPTYPE_ENUM:
899 return field->default_value_enum()->number() != 0;
900 case FieldDescriptor::CPPTYPE_MESSAGE:
901 return false;
902 }
903
904 // Some compilers report reaching end of function even though all cases of
905 // the enum are handed in the switch.
906 GOOGLE_LOG(FATAL) << "Can't get here.";
907 return false;
908 }
909
BuildFlagsString(const FlagType flag_type,const std::vector<string> & strings)910 string BuildFlagsString(const FlagType flag_type,
911 const std::vector<string>& strings) {
912 if (strings.size() == 0) {
913 return GetZeroEnumNameForFlagType(flag_type);
914 } else if (strings.size() == 1) {
915 return strings[0];
916 }
917 string string("(" + GetEnumNameForFlagType(flag_type) + ")(");
918 for (size_t i = 0; i != strings.size(); ++i) {
919 if (i > 0) {
920 string.append(" | ");
921 }
922 string.append(strings[i]);
923 }
924 string.append(")");
925 return string;
926 }
927
BuildCommentsString(const SourceLocation & location,bool prefer_single_line)928 string BuildCommentsString(const SourceLocation& location,
929 bool prefer_single_line) {
930 const string& comments = location.leading_comments.empty()
931 ? location.trailing_comments
932 : location.leading_comments;
933 std::vector<string> lines;
934 SplitStringAllowEmpty(comments, "\n", &lines);
935 while (!lines.empty() && lines.back().empty()) {
936 lines.pop_back();
937 }
938 // If there are no comments, just return an empty string.
939 if (lines.size() == 0) {
940 return "";
941 }
942
943 string prefix;
944 string suffix;
945 string final_comments;
946 string epilogue;
947
948 bool add_leading_space = false;
949
950 if (prefer_single_line && lines.size() == 1) {
951 prefix = "/** ";
952 suffix = " */\n";
953 } else {
954 prefix = "* ";
955 suffix = "\n";
956 final_comments += "/**\n";
957 epilogue = " **/\n";
958 add_leading_space = true;
959 }
960
961 for (int i = 0; i < lines.size(); i++) {
962 string line = StripPrefixString(lines[i], " ");
963 // HeaderDoc and appledoc use '\' and '@' for markers; escape them.
964 line = StringReplace(line, "\\", "\\\\", true);
965 line = StringReplace(line, "@", "\\@", true);
966 // Decouple / from * to not have inline comments inside comments.
967 line = StringReplace(line, "/*", "/\\*", true);
968 line = StringReplace(line, "*/", "*\\/", true);
969 line = prefix + line;
970 StripWhitespace(&line);
971 // If not a one line, need to add the first space before *, as
972 // StripWhitespace would have removed it.
973 line = (add_leading_space ? " " : "") + line;
974 final_comments += line + suffix;
975 }
976 final_comments += epilogue;
977 return final_comments;
978 }
979
980 // Making these a generator option for folks that don't use CocoaPods, but do
981 // want to put the library in a framework is an interesting question. The
982 // problem is it means changing sources shipped with the library to actually
983 // use a different value; so it isn't as simple as a option.
984 const char* const ProtobufLibraryFrameworkName = "Protobuf";
985
ProtobufFrameworkImportSymbol(const string & framework_name)986 string ProtobufFrameworkImportSymbol(const string& framework_name) {
987 // GPB_USE_[framework_name]_FRAMEWORK_IMPORTS
988 string result = string("GPB_USE_");
989 result += ToUpper(framework_name);
990 result += "_FRAMEWORK_IMPORTS";
991 return result;
992 }
993
IsProtobufLibraryBundledProtoFile(const FileDescriptor * file)994 bool IsProtobufLibraryBundledProtoFile(const FileDescriptor* file) {
995 // We don't check the name prefix or proto package because some files
996 // (descriptor.proto), aren't shipped generated by the library, so this
997 // seems to be the safest way to only catch the ones shipped.
998 const string name = file->name();
999 if (name == "google/protobuf/any.proto" ||
1000 name == "google/protobuf/api.proto" ||
1001 name == "google/protobuf/duration.proto" ||
1002 name == "google/protobuf/empty.proto" ||
1003 name == "google/protobuf/field_mask.proto" ||
1004 name == "google/protobuf/source_context.proto" ||
1005 name == "google/protobuf/struct.proto" ||
1006 name == "google/protobuf/timestamp.proto" ||
1007 name == "google/protobuf/type.proto" ||
1008 name == "google/protobuf/wrappers.proto") {
1009 return true;
1010 }
1011 return false;
1012 }
1013
ReadLine(StringPiece * input,StringPiece * line)1014 bool ReadLine(StringPiece* input, StringPiece* line) {
1015 for (int len = 0; len < input->size(); ++len) {
1016 if (ascii_isnewline((*input)[len])) {
1017 *line = StringPiece(input->data(), len);
1018 ++len; // advance over the newline
1019 *input = StringPiece(input->data() + len, input->size() - len);
1020 return true;
1021 }
1022 }
1023 return false; // Ran out of input with no newline.
1024 }
1025
RemoveComment(StringPiece * input)1026 void RemoveComment(StringPiece* input) {
1027 int offset = input->find('#');
1028 if (offset != StringPiece::npos) {
1029 input->remove_suffix(input->length() - offset);
1030 }
1031 }
1032
1033 namespace {
1034
1035 class ExpectedPrefixesCollector : public LineConsumer {
1036 public:
ExpectedPrefixesCollector(std::map<string,string> * inout_package_to_prefix_map)1037 ExpectedPrefixesCollector(std::map<string, string>* inout_package_to_prefix_map)
1038 : prefix_map_(inout_package_to_prefix_map) {}
1039
1040 virtual bool ConsumeLine(const StringPiece& line, string* out_error);
1041
1042 private:
1043 std::map<string, string>* prefix_map_;
1044 };
1045
ConsumeLine(const StringPiece & line,string * out_error)1046 bool ExpectedPrefixesCollector::ConsumeLine(
1047 const StringPiece& line, string* out_error) {
1048 int offset = line.find('=');
1049 if (offset == StringPiece::npos) {
1050 *out_error = string("Expected prefixes file line without equal sign: '") +
1051 string(line) + "'.";
1052 return false;
1053 }
1054 StringPiece package = line.substr(0, offset);
1055 StringPiece prefix = line.substr(offset + 1);
1056 TrimWhitespace(&package);
1057 TrimWhitespace(&prefix);
1058 // Don't really worry about error checking the package/prefix for
1059 // being valid. Assume the file is validated when it is created/edited.
1060 (*prefix_map_)[string(package)] = string(prefix);
1061 return true;
1062 }
1063
LoadExpectedPackagePrefixes(const Options & generation_options,std::map<string,string> * prefix_map,string * out_error)1064 bool LoadExpectedPackagePrefixes(const Options &generation_options,
1065 std::map<string, string>* prefix_map,
1066 string* out_error) {
1067 if (generation_options.expected_prefixes_path.empty()) {
1068 return true;
1069 }
1070
1071 ExpectedPrefixesCollector collector(prefix_map);
1072 return ParseSimpleFile(
1073 generation_options.expected_prefixes_path, &collector, out_error);
1074 }
1075
ValidateObjCClassPrefix(const FileDescriptor * file,const string & expected_prefixes_path,const std::map<string,string> & expected_package_prefixes,string * out_error)1076 bool ValidateObjCClassPrefix(
1077 const FileDescriptor* file,
1078 const string& expected_prefixes_path,
1079 const std::map<string, string>& expected_package_prefixes,
1080 string* out_error) {
1081 const string prefix = file->options().objc_class_prefix();
1082 const string package = file->package();
1083
1084 // NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
1085 // error cases, so it seems to be ok to use as a back door for warnings.
1086
1087 // Check: Error - See if there was an expected prefix for the package and
1088 // report if it doesn't match (wrong or missing).
1089 std::map<string, string>::const_iterator package_match =
1090 expected_package_prefixes.find(package);
1091 if (package_match != expected_package_prefixes.end()) {
1092 // There was an entry, and...
1093 if (package_match->second == prefix) {
1094 // ...it matches. All good, out of here!
1095 return true;
1096 } else {
1097 // ...it didn't match!
1098 *out_error = "error: Expected 'option objc_class_prefix = \"" +
1099 package_match->second + "\";' for package '" + package +
1100 "' in '" + file->name() + "'";
1101 if (prefix.length()) {
1102 *out_error += "; but found '" + prefix + "' instead";
1103 }
1104 *out_error += ".";
1105 return false;
1106 }
1107 }
1108
1109 // If there was no prefix option, we're done at this point.
1110 if (prefix.empty()) {
1111 // No prefix, nothing left to check.
1112 return true;
1113 }
1114
1115 // Check: Warning - Make sure the prefix is is a reasonable value according
1116 // to Apple's rules (the checks above implicitly whitelist anything that
1117 // doesn't meet these rules).
1118 if (!ascii_isupper(prefix[0])) {
1119 std::cerr << std::endl
1120 << "protoc:0: warning: Invalid 'option objc_class_prefix = \""
1121 << prefix << "\";' in '" << file->name() << "';"
1122 << " it should start with a capital letter." << std::endl;
1123 std::cerr.flush();
1124 }
1125 if (prefix.length() < 3) {
1126 // Apple reserves 2 character prefixes for themselves. They do use some
1127 // 3 character prefixes, but they haven't updated the rules/docs.
1128 std::cerr << std::endl
1129 << "protoc:0: warning: Invalid 'option objc_class_prefix = \""
1130 << prefix << "\";' in '" << file->name() << "';"
1131 << " Apple recommends they should be at least 3 characters long."
1132 << std::endl;
1133 std::cerr.flush();
1134 }
1135
1136 // Look for any other package that uses the same prefix.
1137 string other_package_for_prefix;
1138 for (std::map<string, string>::const_iterator i = expected_package_prefixes.begin();
1139 i != expected_package_prefixes.end(); ++i) {
1140 if (i->second == prefix) {
1141 other_package_for_prefix = i->first;
1142 break;
1143 }
1144 }
1145
1146 // Check: Warning - If the file does not have a package, check whether
1147 // the prefix declared is being used by another package or not.
1148 if (package.empty()) {
1149 // The file does not have a package and ...
1150 if (other_package_for_prefix.empty()) {
1151 // ... no other package has declared that prefix.
1152 std::cerr << std::endl
1153 << "protoc:0: warning: File '" << file->name() << "' has no "
1154 << "package. Consider adding a new package to the proto and adding '"
1155 << "new.package = " << prefix << "' to the expected prefixes file ("
1156 << expected_prefixes_path << ")." << std::endl;
1157 std::cerr.flush();
1158 } else {
1159 // ... another package has declared the same prefix.
1160 std::cerr << std::endl
1161 << "protoc:0: warning: File '" << file->name() << "' has no package "
1162 << "and package '" << other_package_for_prefix << "' already uses '"
1163 << prefix << "' as its prefix. Consider either adding a new package "
1164 << "to the proto, or reusing one of the packages already using this "
1165 << "prefix in the expected prefixes file ("
1166 << expected_prefixes_path << ")." << std::endl;
1167 std::cerr.flush();
1168 }
1169 return true;
1170 }
1171
1172 // Check: Error - Make sure the prefix wasn't expected for a different
1173 // package (overlap is allowed, but it has to be listed as an expected
1174 // overlap).
1175 if (!other_package_for_prefix.empty()) {
1176 *out_error =
1177 "error: Found 'option objc_class_prefix = \"" + prefix +
1178 "\";' in '" + file->name() +
1179 "'; that prefix is already used for 'package " +
1180 other_package_for_prefix + ";'. It can only be reused by listing " +
1181 "it in the expected file (" +
1182 expected_prefixes_path + ").";
1183 return false; // Only report first usage of the prefix.
1184 }
1185
1186 // Check: Warning - If the given package/prefix pair wasn't expected, issue a
1187 // warning issue a warning suggesting it gets added to the file.
1188 if (!expected_package_prefixes.empty()) {
1189 std::cerr << std::endl
1190 << "protoc:0: warning: Found unexpected 'option objc_class_prefix = \""
1191 << prefix << "\";' in '" << file->name() << "';"
1192 << " consider adding it to the expected prefixes file ("
1193 << expected_prefixes_path << ")." << std::endl;
1194 std::cerr.flush();
1195 }
1196
1197 return true;
1198 }
1199
1200 } // namespace
1201
ValidateObjCClassPrefixes(const std::vector<const FileDescriptor * > & files,const Options & generation_options,string * out_error)1202 bool ValidateObjCClassPrefixes(const std::vector<const FileDescriptor*>& files,
1203 const Options& generation_options,
1204 string* out_error) {
1205 // Load the expected package prefixes, if available, to validate against.
1206 std::map<string, string> expected_package_prefixes;
1207 if (!LoadExpectedPackagePrefixes(generation_options,
1208 &expected_package_prefixes,
1209 out_error)) {
1210 return false;
1211 }
1212
1213 for (int i = 0; i < files.size(); i++) {
1214 bool should_skip =
1215 (std::find(generation_options.expected_prefixes_suppressions.begin(),
1216 generation_options.expected_prefixes_suppressions.end(),
1217 files[i]->name())
1218 != generation_options.expected_prefixes_suppressions.end());
1219 if (should_skip) {
1220 continue;
1221 }
1222
1223 bool is_valid =
1224 ValidateObjCClassPrefix(files[i],
1225 generation_options.expected_prefixes_path,
1226 expected_package_prefixes,
1227 out_error);
1228 if (!is_valid) {
1229 return false;
1230 }
1231 }
1232 return true;
1233 }
1234
TextFormatDecodeData()1235 TextFormatDecodeData::TextFormatDecodeData() { }
1236
~TextFormatDecodeData()1237 TextFormatDecodeData::~TextFormatDecodeData() { }
1238
AddString(int32 key,const string & input_for_decode,const string & desired_output)1239 void TextFormatDecodeData::AddString(int32 key,
1240 const string& input_for_decode,
1241 const string& desired_output) {
1242 for (std::vector<DataEntry>::const_iterator i = entries_.begin();
1243 i != entries_.end(); ++i) {
1244 if (i->first == key) {
1245 std::cerr << "error: duplicate key (" << key
1246 << ") making TextFormat data, input: \"" << input_for_decode
1247 << "\", desired: \"" << desired_output << "\"." << std::endl;
1248 std::cerr.flush();
1249 abort();
1250 }
1251 }
1252
1253 const string& data = TextFormatDecodeData::DecodeDataForString(
1254 input_for_decode, desired_output);
1255 entries_.push_back(DataEntry(key, data));
1256 }
1257
Data() const1258 string TextFormatDecodeData::Data() const {
1259 std::ostringstream data_stringstream;
1260
1261 if (num_entries() > 0) {
1262 io::OstreamOutputStream data_outputstream(&data_stringstream);
1263 io::CodedOutputStream output_stream(&data_outputstream);
1264
1265 output_stream.WriteVarint32(num_entries());
1266 for (std::vector<DataEntry>::const_iterator i = entries_.begin();
1267 i != entries_.end(); ++i) {
1268 output_stream.WriteVarint32(i->first);
1269 output_stream.WriteString(i->second);
1270 }
1271 }
1272
1273 data_stringstream.flush();
1274 return data_stringstream.str();
1275 }
1276
1277 namespace {
1278
1279 // Helper to build up the decode data for a string.
1280 class DecodeDataBuilder {
1281 public:
DecodeDataBuilder()1282 DecodeDataBuilder() { Reset(); }
1283
1284 bool AddCharacter(const char desired, const char input);
AddUnderscore()1285 void AddUnderscore() {
1286 Push();
1287 need_underscore_ = true;
1288 }
Finish()1289 string Finish() {
1290 Push();
1291 return decode_data_;
1292 }
1293
1294 private:
1295 static const uint8 kAddUnderscore = 0x80;
1296
1297 static const uint8 kOpAsIs = 0x00;
1298 static const uint8 kOpFirstUpper = 0x40;
1299 static const uint8 kOpFirstLower = 0x20;
1300 static const uint8 kOpAllUpper = 0x60;
1301
1302 static const int kMaxSegmentLen = 0x1f;
1303
AddChar(const char desired)1304 void AddChar(const char desired) {
1305 ++segment_len_;
1306 is_all_upper_ &= ascii_isupper(desired);
1307 }
1308
Push()1309 void Push() {
1310 uint8 op = (op_ | segment_len_);
1311 if (need_underscore_) op |= kAddUnderscore;
1312 if (op != 0) {
1313 decode_data_ += (char)op;
1314 }
1315 Reset();
1316 }
1317
AddFirst(const char desired,const char input)1318 bool AddFirst(const char desired, const char input) {
1319 if (desired == input) {
1320 op_ = kOpAsIs;
1321 } else if (desired == ascii_toupper(input)) {
1322 op_ = kOpFirstUpper;
1323 } else if (desired == ascii_tolower(input)) {
1324 op_ = kOpFirstLower;
1325 } else {
1326 // Can't be transformed to match.
1327 return false;
1328 }
1329 AddChar(desired);
1330 return true;
1331 }
1332
Reset()1333 void Reset() {
1334 need_underscore_ = false;
1335 op_ = 0;
1336 segment_len_ = 0;
1337 is_all_upper_ = true;
1338 }
1339
1340 bool need_underscore_;
1341 bool is_all_upper_;
1342 uint8 op_;
1343 int segment_len_;
1344
1345 string decode_data_;
1346 };
1347
AddCharacter(const char desired,const char input)1348 bool DecodeDataBuilder::AddCharacter(const char desired, const char input) {
1349 // If we've hit the max size, push to start a new segment.
1350 if (segment_len_ == kMaxSegmentLen) {
1351 Push();
1352 }
1353 if (segment_len_ == 0) {
1354 return AddFirst(desired, input);
1355 }
1356
1357 // Desired and input match...
1358 if (desired == input) {
1359 // If we aren't transforming it, or we're upper casing it and it is
1360 // supposed to be uppercase; just add it to the segment.
1361 if ((op_ != kOpAllUpper) || ascii_isupper(desired)) {
1362 AddChar(desired);
1363 return true;
1364 }
1365
1366 // Add the current segment, and start the next one.
1367 Push();
1368 return AddFirst(desired, input);
1369 }
1370
1371 // If we need to uppercase, and everything so far has been uppercase,
1372 // promote op to AllUpper.
1373 if ((desired == ascii_toupper(input)) && is_all_upper_) {
1374 op_ = kOpAllUpper;
1375 AddChar(desired);
1376 return true;
1377 }
1378
1379 // Give up, push and start a new segment.
1380 Push();
1381 return AddFirst(desired, input);
1382 }
1383
1384 // If decode data can't be generated, a directive for the raw string
1385 // is used instead.
DirectDecodeString(const string & str)1386 string DirectDecodeString(const string& str) {
1387 string result;
1388 result += (char)'\0'; // Marker for full string.
1389 result += str;
1390 result += (char)'\0'; // End of string.
1391 return result;
1392 }
1393
1394 } // namespace
1395
1396 // static
DecodeDataForString(const string & input_for_decode,const string & desired_output)1397 string TextFormatDecodeData::DecodeDataForString(const string& input_for_decode,
1398 const string& desired_output) {
1399 if ((input_for_decode.size() == 0) || (desired_output.size() == 0)) {
1400 std::cerr << "error: got empty string for making TextFormat data, input: \""
1401 << input_for_decode << "\", desired: \"" << desired_output << "\"."
1402 << std::endl;
1403 std::cerr.flush();
1404 abort();
1405 }
1406 if ((input_for_decode.find('\0') != string::npos) ||
1407 (desired_output.find('\0') != string::npos)) {
1408 std::cerr << "error: got a null char in a string for making TextFormat data,"
1409 << " input: \"" << CEscape(input_for_decode) << "\", desired: \""
1410 << CEscape(desired_output) << "\"." << std::endl;
1411 std::cerr.flush();
1412 abort();
1413 }
1414
1415 DecodeDataBuilder builder;
1416
1417 // Walk the output building it from the input.
1418 int x = 0;
1419 for (int y = 0; y < desired_output.size(); y++) {
1420 const char d = desired_output[y];
1421 if (d == '_') {
1422 builder.AddUnderscore();
1423 continue;
1424 }
1425
1426 if (x >= input_for_decode.size()) {
1427 // Out of input, no way to encode it, just return a full decode.
1428 return DirectDecodeString(desired_output);
1429 }
1430 if (builder.AddCharacter(d, input_for_decode[x])) {
1431 ++x; // Consumed one input
1432 } else {
1433 // Couldn't transform for the next character, just return a full decode.
1434 return DirectDecodeString(desired_output);
1435 }
1436 }
1437
1438 if (x != input_for_decode.size()) {
1439 // Extra input (suffix from name sanitizing?), just return a full decode.
1440 return DirectDecodeString(desired_output);
1441 }
1442
1443 // Add the end marker.
1444 return builder.Finish() + (char)'\0';
1445 }
1446
1447 namespace {
1448
1449 class Parser {
1450 public:
Parser(LineConsumer * line_consumer)1451 Parser(LineConsumer* line_consumer)
1452 : line_consumer_(line_consumer), line_(0) {}
1453
1454 // Parses a check of input, returning success/failure.
1455 bool ParseChunk(StringPiece chunk);
1456
1457 // Should be called to finish parsing (after all input has been provided via
1458 // ParseChunk()). Returns success/failure.
1459 bool Finish();
1460
last_line() const1461 int last_line() const { return line_; }
error_str() const1462 string error_str() const { return error_str_; }
1463
1464 private:
1465 bool ParseLoop();
1466
1467 LineConsumer* line_consumer_;
1468 int line_;
1469 string error_str_;
1470 StringPiece p_;
1471 string leftover_;
1472 };
1473
ParseChunk(StringPiece chunk)1474 bool Parser::ParseChunk(StringPiece chunk) {
1475 if (!leftover_.empty()) {
1476 leftover_ += string(chunk);
1477 p_ = StringPiece(leftover_);
1478 } else {
1479 p_ = chunk;
1480 }
1481 bool result = ParseLoop();
1482 if (p_.empty()) {
1483 leftover_.clear();
1484 } else {
1485 leftover_ = string(p_);
1486 }
1487 return result;
1488 }
1489
Finish()1490 bool Parser::Finish() {
1491 if (leftover_.empty()) {
1492 return true;
1493 }
1494 // Force a newline onto the end to finish parsing.
1495 leftover_ += "\n";
1496 p_ = StringPiece(leftover_);
1497 if (!ParseLoop()) {
1498 return false;
1499 }
1500 return p_.empty(); // Everything used?
1501 }
1502
ParseLoop()1503 bool Parser::ParseLoop() {
1504 StringPiece line;
1505 while (ReadLine(&p_, &line)) {
1506 ++line_;
1507 RemoveComment(&line);
1508 TrimWhitespace(&line);
1509 if (line.size() == 0) {
1510 continue; // Blank line.
1511 }
1512 if (!line_consumer_->ConsumeLine(line, &error_str_)) {
1513 return false;
1514 }
1515 }
1516 return true;
1517 }
1518
1519 } // namespace
1520
LineConsumer()1521 LineConsumer::LineConsumer() {}
1522
~LineConsumer()1523 LineConsumer::~LineConsumer() {}
1524
ParseSimpleFile(const string & path,LineConsumer * line_consumer,string * out_error)1525 bool ParseSimpleFile(
1526 const string& path, LineConsumer* line_consumer, string* out_error) {
1527 int fd;
1528 do {
1529 fd = posix::open(path.c_str(), O_RDONLY);
1530 } while (fd < 0 && errno == EINTR);
1531 if (fd < 0) {
1532 *out_error =
1533 string("error: Unable to open \"") + path + "\", " + strerror(errno);
1534 return false;
1535 }
1536 io::FileInputStream file_stream(fd);
1537 file_stream.SetCloseOnDelete(true);
1538
1539 Parser parser(line_consumer);
1540 const void* buf;
1541 int buf_len;
1542 while (file_stream.Next(&buf, &buf_len)) {
1543 if (buf_len == 0) {
1544 continue;
1545 }
1546
1547 if (!parser.ParseChunk(StringPiece(static_cast<const char*>(buf), buf_len))) {
1548 *out_error =
1549 string("error: ") + path +
1550 " Line " + StrCat(parser.last_line()) + ", " + parser.error_str();
1551 return false;
1552 }
1553 }
1554 return parser.Finish();
1555 }
1556
ImportWriter(const string & generate_for_named_framework,const string & named_framework_to_proto_path_mappings_path,bool include_wkt_imports)1557 ImportWriter::ImportWriter(
1558 const string& generate_for_named_framework,
1559 const string& named_framework_to_proto_path_mappings_path,
1560 bool include_wkt_imports)
1561 : generate_for_named_framework_(generate_for_named_framework),
1562 named_framework_to_proto_path_mappings_path_(
1563 named_framework_to_proto_path_mappings_path),
1564 include_wkt_imports_(include_wkt_imports),
1565 need_to_parse_mapping_file_(true) {
1566 }
1567
~ImportWriter()1568 ImportWriter::~ImportWriter() {}
1569
AddFile(const FileDescriptor * file,const string & header_extension)1570 void ImportWriter::AddFile(const FileDescriptor* file,
1571 const string& header_extension) {
1572 const string file_path(FilePath(file));
1573
1574 if (IsProtobufLibraryBundledProtoFile(file)) {
1575 // The imports of the WKTs are only needed within the library itself,
1576 // in other cases, they get skipped because the generated code already
1577 // import GPBProtocolBuffers.h and hence proves them.
1578 if (include_wkt_imports_) {
1579 protobuf_framework_imports_.push_back(
1580 FilePathBasename(file) + header_extension);
1581 protobuf_non_framework_imports_.push_back(file_path + header_extension);
1582 }
1583 return;
1584 }
1585
1586 // Lazy parse any mappings.
1587 if (need_to_parse_mapping_file_) {
1588 ParseFrameworkMappings();
1589 }
1590
1591 std::map<string, string>::iterator proto_lookup =
1592 proto_file_to_framework_name_.find(file->name());
1593 if (proto_lookup != proto_file_to_framework_name_.end()) {
1594 other_framework_imports_.push_back(
1595 proto_lookup->second + "/" +
1596 FilePathBasename(file) + header_extension);
1597 return;
1598 }
1599
1600 if (!generate_for_named_framework_.empty()) {
1601 other_framework_imports_.push_back(
1602 generate_for_named_framework_ + "/" +
1603 FilePathBasename(file) + header_extension);
1604 return;
1605 }
1606
1607 other_imports_.push_back(file_path + header_extension);
1608 }
1609
Print(io::Printer * printer) const1610 void ImportWriter::Print(io::Printer* printer) const {
1611 assert(protobuf_non_framework_imports_.size() ==
1612 protobuf_framework_imports_.size());
1613
1614 bool add_blank_line = false;
1615
1616 if (protobuf_framework_imports_.size() > 0) {
1617 const string framework_name(ProtobufLibraryFrameworkName);
1618 const string cpp_symbol(ProtobufFrameworkImportSymbol(framework_name));
1619
1620 printer->Print(
1621 "#if $cpp_symbol$\n",
1622 "cpp_symbol", cpp_symbol);
1623 for (std::vector<string>::const_iterator iter = protobuf_framework_imports_.begin();
1624 iter != protobuf_framework_imports_.end(); ++iter) {
1625 printer->Print(
1626 " #import <$framework_name$/$header$>\n",
1627 "framework_name", framework_name,
1628 "header", *iter);
1629 }
1630 printer->Print(
1631 "#else\n");
1632 for (std::vector<string>::const_iterator iter = protobuf_non_framework_imports_.begin();
1633 iter != protobuf_non_framework_imports_.end(); ++iter) {
1634 printer->Print(
1635 " #import \"$header$\"\n",
1636 "header", *iter);
1637 }
1638 printer->Print(
1639 "#endif\n");
1640
1641 add_blank_line = true;
1642 }
1643
1644 if (other_framework_imports_.size() > 0) {
1645 if (add_blank_line) {
1646 printer->Print("\n");
1647 }
1648
1649 for (std::vector<string>::const_iterator iter = other_framework_imports_.begin();
1650 iter != other_framework_imports_.end(); ++iter) {
1651 printer->Print(
1652 "#import <$header$>\n",
1653 "header", *iter);
1654 }
1655
1656 add_blank_line = true;
1657 }
1658
1659 if (other_imports_.size() > 0) {
1660 if (add_blank_line) {
1661 printer->Print("\n");
1662 }
1663
1664 for (std::vector<string>::const_iterator iter = other_imports_.begin();
1665 iter != other_imports_.end(); ++iter) {
1666 printer->Print(
1667 "#import \"$header$\"\n",
1668 "header", *iter);
1669 }
1670 }
1671 }
1672
ParseFrameworkMappings()1673 void ImportWriter::ParseFrameworkMappings() {
1674 need_to_parse_mapping_file_ = false;
1675 if (named_framework_to_proto_path_mappings_path_.empty()) {
1676 return; // Nothing to do.
1677 }
1678
1679 ProtoFrameworkCollector collector(&proto_file_to_framework_name_);
1680 string parse_error;
1681 if (!ParseSimpleFile(named_framework_to_proto_path_mappings_path_,
1682 &collector, &parse_error)) {
1683 std::cerr << "error parsing " << named_framework_to_proto_path_mappings_path_
1684 << " : " << parse_error << std::endl;
1685 std::cerr.flush();
1686 }
1687 }
1688
ConsumeLine(const StringPiece & line,string * out_error)1689 bool ImportWriter::ProtoFrameworkCollector::ConsumeLine(
1690 const StringPiece& line, string* out_error) {
1691 int offset = line.find(':');
1692 if (offset == StringPiece::npos) {
1693 *out_error =
1694 string("Framework/proto file mapping line without colon sign: '") +
1695 string(line) + "'.";
1696 return false;
1697 }
1698 StringPiece framework_name = line.substr(0, offset);
1699 StringPiece proto_file_list = line.substr(offset + 1);
1700 TrimWhitespace(&framework_name);
1701
1702 int start = 0;
1703 while (start < proto_file_list.length()) {
1704 offset = proto_file_list.find(',', start);
1705 if (offset == StringPiece::npos) {
1706 offset = proto_file_list.length();
1707 }
1708
1709 StringPiece proto_file = proto_file_list.substr(start, offset - start);
1710 TrimWhitespace(&proto_file);
1711 if (proto_file.size() != 0) {
1712 std::map<string, string>::iterator existing_entry =
1713 map_->find(string(proto_file));
1714 if (existing_entry != map_->end()) {
1715 std::cerr << "warning: duplicate proto file reference, replacing "
1716 "framework entry for '"
1717 << string(proto_file) << "' with '" << string(framework_name)
1718 << "' (was '" << existing_entry->second << "')." << std::endl;
1719 std::cerr.flush();
1720 }
1721
1722 if (proto_file.find(' ') != StringPiece::npos) {
1723 std::cerr << "note: framework mapping file had a proto file with a "
1724 "space in, hopefully that isn't a missing comma: '"
1725 << string(proto_file) << "'" << std::endl;
1726 std::cerr.flush();
1727 }
1728
1729 (*map_)[string(proto_file)] = string(framework_name);
1730 }
1731
1732 start = offset + 1;
1733 }
1734
1735 return true;
1736 }
1737
1738
1739 } // namespace objectivec
1740 } // namespace compiler
1741 } // namespace protobuf
1742 } // namespace google
1743