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