41 #include <unordered_set>
44 #include <google/protobuf/compiler/code_generator.h>
45 #include <google/protobuf/compiler/objectivec/objectivec_helpers.h>
46 #include <google/protobuf/compiler/objectivec/objectivec_nsobject_methods.h>
47 #include <google/protobuf/descriptor.pb.h>
48 #include <google/protobuf/io/coded_stream.h>
49 #include <google/protobuf/io/printer.h>
50 #include <google/protobuf/io/zero_copy_stream_impl.h>
51 #include <google/protobuf/io/io_win32.h>
52 #include <google/protobuf/port.h>
53 #include <google/protobuf/stubs/common.h>
54 #include <google/protobuf/stubs/strutil.h>
62 namespace objectivec {
76 class SimpleLineCollector :
public LineConsumer {
78 SimpleLineCollector(std::unordered_set<std::string>* inout_set)
81 virtual bool ConsumeLine(
const StringPiece&
line,
std::string* out_error)
override {
87 std::unordered_set<std::string>*
set_;
90 class PrefixModeStorage {
103 bool is_package_exempted(
const std::string& package);
111 PrefixModeStorage::PrefixModeStorage() {
115 const char* use_package_cstr =
getenv(
"GPB_OBJC_USE_PACKAGE_AS_PREFIX");
119 const char* exception_path =
getenv(
"GPB_OBJC_PACKAGE_PREFIX_EXCEPTIONS_PATH");
120 if (exception_path) {
125 bool PrefixModeStorage::is_package_exempted(
const std::string& package) {
130 if (error_str.empty()) {
131 error_str =
std::string(
"protoc:0: warning: Failed to parse")
135 std::cerr << error_str << std::endl;
150 PrefixModeStorage g_prefix_mode;
155 return g_prefix_mode.use_package_name();
159 g_prefix_mode.set_use_package_name(on_or_off);
163 return g_prefix_mode.exception_path();
167 g_prefix_mode.set_exception_path(file_path);
172 const char* file_path =
getenv(
"GPB_OBJC_EXPECTED_PACKAGE_PREFIXES");
176 const char* suppressions =
getenv(
"GPB_OBJC_EXPECTED_PACKAGE_PREFIXES_SUPPRESSIONS");
179 Split(suppressions,
";",
true);
187 std::unordered_set<std::string> MakeWordsMap(
const char*
const words[],
189 std::unordered_set<std::string>
result;
190 for (
int i = 0;
i < num_words;
i++) {
196 const char*
const kUpperSegmentsList[] = {
"url",
"http",
"https"};
198 std::unordered_set<std::string> kUpperSegments =
201 bool ascii_isnewline(
char c) {
202 return c ==
'\n' ||
c ==
'\r';
209 bool first_capitalized) {
210 std::vector<std::string>
values;
213 bool last_char_was_number =
false;
214 bool last_char_was_lower =
false;
215 bool last_char_was_upper =
false;
216 for (
int i = 0;
i <
input.size();
i++) {
219 if (!last_char_was_number) {
220 values.push_back(current);
224 last_char_was_number = last_char_was_lower = last_char_was_upper =
false;
225 last_char_was_number =
true;
228 if (!last_char_was_lower && !last_char_was_upper) {
229 values.push_back(current);
233 last_char_was_number = last_char_was_lower = last_char_was_upper =
false;
234 last_char_was_lower =
true;
236 if (!last_char_was_upper) {
237 values.push_back(current);
241 last_char_was_number = last_char_was_lower = last_char_was_upper =
false;
242 last_char_was_upper =
true;
244 last_char_was_number = last_char_was_lower = last_char_was_upper =
false;
247 values.push_back(current);
250 bool first_segment_forces_upper =
false;
254 bool all_upper = (kUpperSegments.count(
value) > 0);
255 if (all_upper && (
result.length() == 0)) {
256 first_segment_forces_upper =
true;
258 for (
int j = 0;
j <
value.length();
j++) {
259 if (j == 0 || all_upper) {
267 if ((
result.length() != 0) &&
268 !first_capitalized &&
269 !first_segment_forces_upper) {
275 const char*
const kReservedWordList[] = {
284 "id",
"_cmd",
"super",
"in",
"out",
"inout",
"bycopy",
"byref",
"oneway",
285 "self",
"instancetype",
"nullable",
"nonnull",
"nil",
"Nil",
290 "and",
"and_eq",
"alignas",
"alignof",
"asm",
"auto",
"bitand",
"bitor",
291 "bool",
"break",
"case",
"catch",
"char",
"char16_t",
"char32_t",
"class",
292 "compl",
"const",
"constexpr",
"const_cast",
"continue",
"decltype",
293 "default",
"delete",
"double",
"dynamic_cast",
"else",
"enum",
"explicit",
294 "export",
"extern ",
"false",
"float",
"for",
"friend",
"goto",
"if",
295 "inline",
"int",
"long",
"mutable",
"namespace",
"new",
"noexcept",
"not",
296 "not_eq",
"nullptr",
"operator",
"or",
"or_eq",
"private",
"protected",
297 "public",
"register",
"reinterpret_cast",
"return",
"short",
"signed",
298 "sizeof",
"static",
"static_assert",
"static_cast",
"struct",
"switch",
299 "template",
"this",
"thread_local",
"throw",
"true",
"try",
"typedef",
300 "typeid",
"typename",
"union",
"unsigned",
"using",
"virtual",
"void",
301 "volatile",
"wchar_t",
"while",
"xor",
"xor_eq",
316 "stdin",
"stdout",
"stderr",
320 "Category",
"Ivar",
"Method",
"Protocol",
326 "clear",
"data",
"delimitedData",
"descriptor",
"extensionRegistry",
327 "extensionsCurrentlySet",
"initialized",
"isInitialized",
"serializedSize",
328 "sortedExtensionsInUse",
"unknownFields",
331 "Fixed",
"Fract",
"Size",
"LogicalAddress",
"PhysicalAddress",
"ByteCount",
332 "ByteOffset",
"Duration",
"AbsoluteTime",
"OptionBits",
"ItemCount",
333 "PBVersion",
"ScriptCode",
"LangCode",
"RegionCode",
"OSType",
334 "ProcessSerialNumber",
"Point",
"Rect",
"FixedPoint",
"FixedRect",
"Style",
335 "StyleParameter",
"StyleField",
"TimeScale",
"TimeBase",
"TimeRecord",
344 if (
input.length() > 2) {
345 if (
input.at(0) ==
'_') {
346 if (isupper(
input.at(1)) ||
input.at(1) ==
'_') {
358 static const std::unordered_set<std::string> kReservedWords =
360 static const std::unordered_set<std::string> kNSObjectMethods =
377 if (IsReservedCIdentifier(sanitized) ||
378 (kReservedWords.count(sanitized) > 0) ||
379 (kNSObjectMethods.count(sanitized) > 0)) {
380 if (out_suffix_added) *out_suffix_added =
extension;
383 if (out_suffix_added) out_suffix_added->clear();
389 return field->message_type()->name();
391 return field->name();
397 std::string::size_type last_slash =
path.rfind(
'/');
398 if (last_slash == std::string::npos) {
410 *basename =
path.substr(last_slash + 1);
417 for (
size_t i = 0;
i <
count; ++
i) {
418 size_t length = special_names[
i].length();
419 if (
name.compare(0,
length, special_names[i]) == 0) {
436 return "GPBDescriptorInitializationFlag_None";
438 return "GPBExtensionNone";
440 return "GPBFieldNone";
450 return "GPBDescriptorInitializationFlags";
452 return "GPBExtensionOptions";
454 return "GPBFieldFlags";
461 void MaybeUnQuote(StringPiece*
input) {
462 if ((
input->length() >= 2) &&
463 ((*
input->data() ==
'\'' || *
input->data() ==
'"')) &&
465 input->remove_prefix(1);
466 input->remove_suffix(1);
479 input->remove_prefix(1);
482 input->remove_suffix(1);
489 static const std::string retained_names[] = {
"new",
"alloc",
"copy",
491 return IsSpecialName(
name, retained_names,
492 sizeof(retained_names) /
sizeof(retained_names[0]));
497 return IsSpecialName(
name, init_names,
498 sizeof(init_names) /
sizeof(init_names[0]));
503 PathSplit(
file->
name(), NULL, &basename);
509 if (
file->options().has_objc_class_prefix()) {
510 return file->options().objc_class_prefix();
514 if (!g_prefix_mode.use_package_name() ||
file->package().empty()) {
519 if (g_prefix_mode.is_package_exempted(
file->package())) {
527 const std::vector<std::string> segments =
Split(
file->package(),
".",
true);
528 for (
const auto& segment : segments) {
580 return SanitizeNameForObjC(
prefix,
name,
"_RootClass", NULL);
611 return SanitizeNameForObjC(
prefix,
name,
"_Class", out_suffix_added);
625 return SanitizeNameForObjC(
prefix,
name,
"_Enum", NULL);
641 return SanitizeNameForObjC(
"",
name,
"_Value", NULL);
665 for (
int i = 0;
i <
name.size();
i++) {
678 return SanitizeNameForObjC(
"",
result,
"_Extension", NULL);
684 if (
field->is_repeated() && !
field->is_map()) {
693 return SanitizeNameForObjC(
"",
result,
"_p", NULL);
700 if (
result.length() > 0) {
724 if (
result.length() > 0) {
747 if (
worker.length() > 0) {
755 for (
int i = 0;
i <
worker.size();
i++) {
771 switch (
field->type()) {
888 bool add_float_suffix) {
891 }
else if (val ==
"inf") {
893 }
else if (val ==
"-inf") {
897 if (add_float_suffix && (val.find(
".") != std::string::npos ||
898 val.find(
"e") != std::string::npos ||
899 val.find(
"E") != std::string::npos)) {
909 if (
field->is_repeated()) {
910 return "valueMessage";
912 switch (
field->cpp_type()) {
916 return "valueUInt32";
920 return "valueUInt64";
924 return "valueDouble";
931 return "valueString";
936 return "valueMessage";
948 if (
field->is_repeated()) {
954 switch (
field->cpp_type()) {
957 if (
field->default_value_int32() == INT_MIN) {
958 return "-0x80000000";
962 return StrCat(
field->default_value_uint32()) +
"U";
965 if (
field->default_value_int64() == LLONG_MIN) {
966 return "-0x8000000000000000LL";
968 return StrCat(
field->default_value_int64()) +
"LL";
970 return StrCat(
field->default_value_uint64()) +
"ULL";
978 return field->default_value_bool() ?
"YES" :
"NO";
980 const bool has_default_value =
field->has_default_value();
1019 if (
field->is_repeated()) {
1031 switch (
field->cpp_type()) {
1033 return field->default_value_int32() != 0;
1035 return field->default_value_uint32() != 0U;
1037 return field->default_value_int64() != 0
LL;
1039 return field->default_value_uint64() != 0
ULL;
1041 return field->default_value_double() != 0.0;
1043 return field->default_value_float() != 0.0f;
1045 return field->default_value_bool();
1051 return field->default_value_enum()->number() != 0;
1063 const std::vector<std::string>& strings) {
1064 if (strings.empty()) {
1065 return GetZeroEnumNameForFlagType(flag_type);
1066 }
else if (strings.size() == 1) {
1070 for (
size_t i = 0;
i != strings.size(); ++
i) {
1072 string.append(
" | ");
1074 string.append(strings[
i]);
1081 bool prefer_single_line) {
1085 std::vector<std::string>
lines;
1087 while (!
lines.empty() &&
lines.back().empty()) {
1091 if (
lines.empty()) {
1100 bool add_leading_space =
false;
1102 if (prefer_single_line &&
lines.size() == 1) {
1108 final_comments +=
"/**\n";
1109 epilogue =
" **/\n";
1110 add_leading_space =
true;
1113 for (
int i = 0;
i <
lines.size();
i++) {
1125 line = (add_leading_space ?
" " :
"") +
line;
1128 final_comments += epilogue;
1129 return final_comments;
1142 result +=
"_FRAMEWORK_IMPORTS";
1151 if (
name ==
"google/protobuf/any.proto" ||
1152 name ==
"google/protobuf/api.proto" ||
1153 name ==
"google/protobuf/duration.proto" ||
1154 name ==
"google/protobuf/empty.proto" ||
1155 name ==
"google/protobuf/field_mask.proto" ||
1156 name ==
"google/protobuf/source_context.proto" ||
1157 name ==
"google/protobuf/struct.proto" ||
1158 name ==
"google/protobuf/timestamp.proto" ||
1159 name ==
"google/protobuf/type.proto" ||
1160 name ==
"google/protobuf/wrappers.proto") {
1168 if (ascii_isnewline((*
input)[
len])) {
1187 class ExpectedPrefixesCollector :
public LineConsumer {
1189 ExpectedPrefixesCollector(std::map<std::string, std::string>* inout_package_to_prefix_map)
1192 virtual bool ConsumeLine(
const StringPiece&
line,
std::string* out_error)
override;
1198 bool ExpectedPrefixesCollector::ConsumeLine(
1202 *out_error =
std::string(
"Expected prefixes file line without equal sign: '") +
1206 StringPiece
package = line.substr(0, offset);
1217 bool LoadExpectedPackagePrefixes(
const Options& generation_options,
1218 std::map<std::string, std::string>* prefix_map,
1220 if (generation_options.expected_prefixes_path.empty()) {
1224 ExpectedPrefixesCollector collector(prefix_map);
1226 generation_options.expected_prefixes_path, &collector, out_error);
1229 bool ValidateObjCClassPrefix(
1231 const std::map<std::string, std::string>& expected_package_prefixes,
1232 bool prefixes_must_be_registered,
bool require_prefixes,
1238 bool has_prefix =
file->options().has_objc_class_prefix();
1239 bool have_expected_prefix_file = !expected_prefixes_path.empty();
1249 std::map<std::string, std::string>::const_iterator package_match =
1250 expected_package_prefixes.find(package);
1251 if (package_match != expected_package_prefixes.end()) {
1253 if (has_prefix && package_match->second ==
prefix) {
1258 *out_error =
"error: Expected 'option objc_class_prefix = \"" +
1259 package_match->second +
"\";' for package '" +
package +
1260 "' in '" + file->name() + "'";
1262 *out_error +=
"; but found '" +
prefix +
"' instead";
1271 if (require_prefixes) {
1273 "error: '" +
file->
name() +
"' does not have a required 'option" +
1274 " objc_class_prefix'.";
1281 if (!
prefix.empty() && have_expected_prefix_file) {
1284 for (std::map<std::string, std::string>::const_iterator i =
1285 expected_package_prefixes.begin();
1286 i != expected_package_prefixes.end(); ++i) {
1288 other_package_for_prefix =
i->first;
1296 if (package.empty()) {
1298 if (other_package_for_prefix.empty()) {
1301 <<
"protoc:0: warning: File '" <<
file->
name() <<
"' has no "
1302 <<
"package. Consider adding a new package to the proto and adding '"
1303 <<
"new.package = " <<
prefix <<
"' to the expected prefixes file ("
1304 << expected_prefixes_path <<
")." << std::endl;
1309 <<
"protoc:0: warning: File '" <<
file->
name() <<
"' has no package "
1310 <<
"and package '" << other_package_for_prefix <<
"' already uses '"
1311 <<
prefix <<
"' as its prefix. Consider either adding a new package "
1312 <<
"to the proto, or reusing one of the packages already using this "
1313 <<
"prefix in the expected prefixes file ("
1314 << expected_prefixes_path <<
")." << std::endl;
1323 if (!other_package_for_prefix.empty()) {
1325 "error: Found 'option objc_class_prefix = \"" +
prefix +
1327 "'; that prefix is already used for 'package " +
1328 other_package_for_prefix +
";'. It can only be reused by listing " +
1329 "it in the expected file (" +
1330 expected_prefixes_path +
").";
1340 <<
"protoc:0: warning: Invalid 'option objc_class_prefix = \""
1342 <<
" it should start with a capital letter." << std::endl;
1349 <<
"protoc:0: warning: Invalid 'option objc_class_prefix = \""
1351 <<
" Apple recommends they should be at least 3 characters long."
1358 if (have_expected_prefix_file) {
1359 if (prefixes_must_be_registered) {
1361 "error: '" +
file->
name() +
"' has 'option objc_class_prefix = \"" +
1362 prefix +
"\";', but it is not registered; add it to the expected " +
1363 "prefixes file (" + expected_prefixes_path +
") for the package '" +
1369 <<
"protoc:0: warning: Found unexpected 'option objc_class_prefix = \""
1371 <<
" consider adding it to the expected prefixes file ("
1372 << expected_prefixes_path <<
")." << std::endl;
1382 const Options& generation_options,
1391 std::map<std::string, std::string> expected_package_prefixes;
1392 if (!LoadExpectedPackagePrefixes(generation_options,
1393 &expected_package_prefixes,
1398 for (
int i = 0;
i <
files.size();
i++) {
1409 ValidateObjCClassPrefix(
files[
i],
1411 expected_package_prefixes,
1429 for (std::vector<DataEntry>::const_iterator
i = entries_.begin();
1430 i != entries_.end(); ++
i) {
1431 if (
i->first ==
key) {
1432 std::cerr <<
"error: duplicate key (" <<
key
1433 <<
") making TextFormat data, input: \"" << input_for_decode
1434 <<
"\", desired: \"" << desired_output <<
"\"." << std::endl;
1441 input_for_decode, desired_output);
1446 std::ostringstream data_stringstream;
1448 if (num_entries() > 0) {
1452 output_stream.WriteVarint32(num_entries());
1453 for (std::vector<DataEntry>::const_iterator i = entries_.begin();
1454 i != entries_.end(); ++i) {
1455 output_stream.WriteVarint32(
i->first);
1456 output_stream.WriteString(
i->second);
1460 data_stringstream.flush();
1461 return data_stringstream.str();
1467 class DecodeDataBuilder {
1469 DecodeDataBuilder() {
Reset(); }
1471 bool AddCharacter(
const char desired,
const char input);
1472 void AddUnderscore() {
1491 void AddChar(
const char desired) {
1505 bool AddFirst(
const char desired,
const char input) {
1506 if (desired ==
input) {
1535 bool DecodeDataBuilder::AddCharacter(
const char desired,
const char input) {
1541 return AddFirst(desired,
input);
1545 if (desired ==
input) {
1555 return AddFirst(desired,
input);
1568 return AddFirst(desired,
input);
1586 if (input_for_decode.empty() || desired_output.empty()) {
1587 std::cerr <<
"error: got empty string for making TextFormat data, input: \""
1588 << input_for_decode <<
"\", desired: \"" << desired_output <<
"\"."
1593 if ((input_for_decode.find(
'\0') != std::string::npos) ||
1594 (desired_output.find(
'\0') != std::string::npos)) {
1595 std::cerr <<
"error: got a null char in a string for making TextFormat data,"
1596 <<
" input: \"" <<
CEscape(input_for_decode) <<
"\", desired: \""
1597 <<
CEscape(desired_output) <<
"\"." << std::endl;
1606 for (
int y = 0;
y < desired_output.size();
y++) {
1607 const char d = desired_output[
y];
1613 if (
x >= input_for_decode.size()) {
1615 return DirectDecodeString(desired_output);
1617 if (
builder.AddCharacter(d, input_for_decode[
x])) {
1621 return DirectDecodeString(desired_output);
1625 if (
x != input_for_decode.size()) {
1627 return DirectDecodeString(desired_output);
1631 return builder.Finish() + (char)
'\0';
1650 int last_line()
const {
return line_; }
1658 bool Parser::ParseChunk(StringPiece chunk,
std::string* out_error) {
1659 StringPiece full_chunk;
1673 if (out_error->empty()) {
1674 *out_error =
"ConsumeLine failed without setting an error.";
1681 if (full_chunk.empty()) {
1691 if (!
leftover_.empty() && !ParseChunk(
"\n", out_error)) {
1696 *out_error =
"ParseSimple Internal error: finished with pending data.";
1717 }
while (fd < 0 && errno == EINTR);
1719 *out_error =
std::string(
"error: Unable to open \"") +
path +
"\", " +
1734 Parser
parser(line_consumer);
1737 while (input_stream.
Next(&
buf, &buf_len)) {
1744 *out_error = FullErrorString(stream_name,
parser.last_line(), local_error);
1748 if (!
parser.Finish(&local_error)) {
1749 *out_error = FullErrorString(stream_name,
parser.last_line(), local_error);
1757 const std::string& named_framework_to_proto_path_mappings_path,
1758 const std::string& runtime_import_prefix,
bool include_wkt_imports)
1759 : generate_for_named_framework_(generate_for_named_framework),
1760 named_framework_to_proto_path_mappings_path_(
1761 named_framework_to_proto_path_mappings_path),
1762 runtime_import_prefix_(runtime_import_prefix),
1763 include_wkt_imports_(include_wkt_imports),
1764 need_to_parse_mapping_file_(
true) {}
1791 proto_lookup->second +
"/" +
1807 bool add_blank_line =
false;
1811 add_blank_line =
true;
1815 if (add_blank_line) {
1816 printer->
Print(
"\n");
1819 for (std::vector<std::string>::const_iterator
iter =
1823 "#import <$header$>\n",
1827 add_blank_line =
true;
1831 if (add_blank_line) {
1832 printer->
Print(
"\n");
1838 "#import \"$header$\"\n",
1845 io::Printer* printer,
const std::vector<std::string>& header_to_import,
1846 const std::string& runtime_import_prefix,
bool default_cpp_symbol) {
1848 if (!runtime_import_prefix.empty()) {
1849 for (
const auto&
header : header_to_import) {
1851 " #import \"$import_prefix$/$header$\"\n",
1852 "import_prefix", runtime_import_prefix,
1861 if (default_cpp_symbol) {
1863 "// This CPP symbol can be defined to use imports that match up to the framework\n"
1864 "// imports needed when using CocoaPods.\n"
1865 "#if !defined($cpp_symbol$)\n"
1866 " #define $cpp_symbol$ 0\n"
1869 "cpp_symbol", cpp_symbol);
1873 "#if $cpp_symbol$\n",
1874 "cpp_symbol", cpp_symbol);
1875 for (
const auto&
header : header_to_import) {
1877 " #import <$framework_name$/$header$>\n",
1878 "framework_name", framework_name,
1883 for (
const auto&
header : header_to_import) {
1885 " #import \"$header$\"\n",
1913 std::string(
"Framework/proto file mapping line without colon sign: '") +
1930 if (!proto_file.
empty()) {
1933 if (existing_entry !=
map_->end()) {
1934 std::cerr <<
"warning: duplicate proto file reference, replacing "
1935 "framework entry for '"
1937 <<
"' (was '" << existing_entry->second <<
"')." << std::endl;
1942 std::cerr <<
"note: framework mapping file had a proto file with a "
1943 "space in, hopefully that isn't a missing comma: '"