41 #include <unordered_set>
60 namespace objectivec {
66 using ::google::protobuf::io::win32::open;
74 const char* file_path = getenv(
"GPB_OBJC_EXPECTED_PACKAGE_PREFIXES");
78 const char* suppressions = getenv(
"GPB_OBJC_EXPECTED_PACKAGE_PREFIXES_SUPPRESSIONS");
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++) {
94 const char*
const kUpperSegmentsList[] = {
"url",
"http",
"https"};
96 std::unordered_set<string> kUpperSegments =
99 bool ascii_isnewline(
char c) {
100 return c ==
'\n' || c ==
'\r';
107 std::vector<string>
values;
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++) {
116 if (!last_char_was_number) {
117 values.push_back(current);
121 last_char_was_number = last_char_was_lower = last_char_was_upper =
false;
122 last_char_was_number =
true;
125 if (!last_char_was_lower && !last_char_was_upper) {
126 values.push_back(current);
130 last_char_was_number = last_char_was_lower = last_char_was_upper =
false;
131 last_char_was_lower =
true;
133 if (!last_char_was_upper) {
134 values.push_back(current);
138 last_char_was_number = last_char_was_lower = last_char_was_upper =
false;
139 last_char_was_upper =
true;
141 last_char_was_number = last_char_was_lower = last_char_was_upper =
false;
144 values.push_back(current);
147 bool first_segment_forces_upper =
false;
148 for (std::vector<string>::iterator
i =
values.begin();
i !=
values.end(); ++
i) {
150 bool all_upper = (kUpperSegments.count(
value) > 0);
151 if (all_upper && (result.length() == 0)) {
152 first_segment_forces_upper =
true;
154 for (
int j = 0; j <
value.length(); j++) {
155 if (j == 0 || all_upper) {
163 if ((result.length() != 0) &&
164 !first_capitalized &&
165 !first_segment_forces_upper) {
171 const char*
const kReservedWordList[] = {
180 "id",
"_cmd",
"super",
"in",
"out",
"inout",
"bycopy",
"byref",
"oneway",
181 "self",
"instancetype",
"nullable",
"nonnull",
"nil",
"Nil",
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",
212 "Category",
"Ivar",
"Method",
"Protocol",
218 "clear",
"data",
"delimitedData",
"descriptor",
"extensionRegistry",
219 "extensionsCurrentlySet",
"initialized",
"isInitialized",
"serializedSize",
220 "sortedExtensionsInUse",
"unknownFields",
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",
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) ==
'_') {
246 string SanitizeNameForObjC(
const string&
prefix,
249 string* out_suffix_added) {
250 static const std::unordered_set<string> kReservedWords =
252 static const std::unordered_set<string> kNSObjectMethods =
269 if (IsReservedCIdentifier(sanitized) ||
270 (kReservedWords.count(sanitized) > 0) ||
271 (kNSObjectMethods.count(sanitized) > 0)) {
272 if (out_suffix_added) *out_suffix_added =
extension;
275 if (out_suffix_added) out_suffix_added->clear();
281 return field->message_type()->name();
283 return field->name();
287 void PathSplit(
const string&
path,
string* directory,
string* basename) {
288 string::size_type last_slash =
path.rfind(
'/');
289 if (last_slash == string::npos) {
298 *directory =
path.substr(0, last_slash);
301 *basename =
path.substr(last_slash + 1);
306 bool IsSpecialName(
const string&
name,
const string* special_names,
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) {
324 string GetZeroEnumNameForFlagType(
const FlagType flag_type) {
327 return "GPBDescriptorInitializationFlag_None";
329 return "GPBExtensionNone";
331 return "GPBFieldNone";
338 string GetEnumNameForFlagType(
const FlagType flag_type) {
341 return "GPBDescriptorInitializationFlags";
343 return "GPBExtensionOptions";
345 return "GPBFieldFlags";
369 input->remove_prefix(1);
372 input->remove_suffix(1);
380 static const string retained_names[] = {
"new",
"alloc",
"copy",
382 return IsSpecialName(
name, retained_names,
383 sizeof(retained_names) /
sizeof(retained_names[0]));
387 static const string init_names[] = {
"init"};
388 return IsSpecialName(
name, init_names,
389 sizeof(init_names) /
sizeof(init_names[0]));
394 PathSplit(file->
name(),
NULL, &basename);
408 PathSplit(file->
name(), &directory, &basename);
409 if (directory.length() > 0) {
425 PathSplit(file->
name(), &directory, &basename);
469 return SanitizeNameForObjC(
prefix,
name,
"_Class", out_suffix_added);
495 const string name = class_name +
"_" + value_str;
498 return SanitizeNameForObjC(
"",
name,
"_Value",
NULL);
515 const string long_name_prefix = class_name +
"_";
522 for (
int i = 0;
i <
name.size();
i++) {
535 return SanitizeNameForObjC(
"", result,
"_Extension",
NULL);
539 const string name = NameFromFieldDescriptor(
field);
541 if (
field->is_repeated() && !
field->is_map()) {
550 return SanitizeNameForObjC(
"", result,
"_p",
NULL);
557 if (result.length() > 0) {
581 if (result.length() > 0) {
596 if (
worker.length() > 0) {
604 for (
int i = 0;
i <
worker.size();
i++) {
620 switch (
field->type()) {
739 }
else if (
val ==
"inf") {
741 }
else if (
val ==
"-inf") {
745 if (add_float_suffix &&
746 (
val.find(
".") != string::npos ||
val.find(
"e") != string::npos ||
747 val.find(
"E") != string::npos)) {
757 if (
field->is_repeated()) {
758 return "valueMessage";
760 switch (
field->cpp_type()) {
764 return "valueUInt32";
768 return "valueUInt64";
772 return "valueDouble";
779 return "valueString";
784 return "valueMessage";
796 if (
field->is_repeated()) {
802 switch (
field->cpp_type()) {
805 if (
field->default_value_int32() == INT_MIN) {
806 return "-0x80000000";
810 return StrCat(
field->default_value_uint32()) +
"U";
813 if (
field->default_value_int64() == LLONG_MIN) {
814 return "-0x8000000000000000LL";
816 return StrCat(
field->default_value_int64()) +
"LL";
818 return StrCat(
field->default_value_uint64()) +
"ULL";
826 return field->default_value_bool() ?
"YES" :
"NO";
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) {
847 bytes.append(default_string);
867 if (
field->is_repeated()) {
879 switch (
field->cpp_type()) {
881 return field->default_value_int32() != 0;
883 return field->default_value_uint32() != 0U;
885 return field->default_value_int64() != 0
LL;
887 return field->default_value_uint64() != 0
ULL;
889 return field->default_value_double() != 0.0;
891 return field->default_value_float() != 0.0f;
893 return field->default_value_bool();
895 const string& default_string =
field->default_value_string();
896 return default_string.length() != 0;
899 return field->default_value_enum()->number() != 0;
911 const std::vector<string>&
strings) {
913 return GetZeroEnumNameForFlagType(flag_type);
914 }
else if (
strings.size() == 1) {
917 string string(
"(" + GetEnumNameForFlagType(flag_type) +
")(");
918 for (
size_t i = 0;
i !=
strings.size(); ++
i) {
920 string.append(
" | ");
929 bool prefer_single_line) {
930 const string& comments =
location.leading_comments.empty()
933 std::vector<string> lines;
935 while (!lines.empty() && lines.back().empty()) {
939 if (lines.size() == 0) {
945 string final_comments;
948 bool add_leading_space =
false;
950 if (prefer_single_line && lines.size() == 1) {
956 final_comments +=
"/**\n";
958 add_leading_space =
true;
961 for (
int i = 0;
i < lines.size();
i++) {
973 line = (add_leading_space ?
" " :
"") + line;
974 final_comments += line + suffix;
976 final_comments += epilogue;
977 return final_comments;
988 string result =
string(
"GPB_USE_");
989 result +=
ToUpper(framework_name);
990 result +=
"_FRAMEWORK_IMPORTS";
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") {
1016 if (ascii_isnewline((*
input)[
len])) {
1035 class ExpectedPrefixesCollector :
public LineConsumer {
1037 ExpectedPrefixesCollector(std::map<string, string>* inout_package_to_prefix_map)
1040 virtual bool ConsumeLine(
const StringPiece& line,
string* out_error);
1046 bool ExpectedPrefixesCollector::ConsumeLine(
1047 const StringPiece& line,
string* out_error) {
1048 int offset = line.find(
'=');
1050 *out_error =
string(
"Expected prefixes file line without equal sign: '") +
1054 StringPiece
package = line.substr(0, offset);
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()) {
1071 ExpectedPrefixesCollector collector(prefix_map);
1073 generation_options.expected_prefixes_path, &collector, out_error);
1076 bool ValidateObjCClassPrefix(
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();
1089 std::map<string, string>::const_iterator package_match =
1090 expected_package_prefixes.find(package);
1091 if (package_match != expected_package_prefixes.end()) {
1093 if (package_match->second ==
prefix) {
1098 *out_error =
"error: Expected 'option objc_class_prefix = \"" +
1099 package_match->second +
"\";' for package '" +
package +
1100 "' in '" + file->name() + "'";
1102 *out_error +=
"; but found '" +
prefix +
"' instead";
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;
1125 if (
prefix.length() < 3) {
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."
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) {
1141 other_package_for_prefix =
i->first;
1150 if (other_package_for_prefix.empty()) {
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;
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;
1175 if (!other_package_for_prefix.empty()) {
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 +
").";
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;
1203 const Options& generation_options,
1204 string* out_error) {
1206 std::map<string, string> expected_package_prefixes;
1207 if (!LoadExpectedPackagePrefixes(generation_options,
1208 &expected_package_prefixes,
1213 for (
int i = 0;
i < files.size();
i++) {
1224 ValidateObjCClassPrefix(files[
i],
1226 expected_package_prefixes,
1240 const string& input_for_decode,
1241 const string& desired_output) {
1242 for (std::vector<DataEntry>::const_iterator
i =
entries_.begin();
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;
1254 input_for_decode, desired_output);
1259 std::ostringstream data_stringstream;
1266 for (std::vector<DataEntry>::const_iterator
i =
entries_.begin();
1273 data_stringstream.flush();
1274 return data_stringstream.str();
1280 class DecodeDataBuilder {
1282 DecodeDataBuilder() {
Reset(); }
1284 bool AddCharacter(
const char desired,
const char input);
1285 void AddUnderscore() {
1304 void AddChar(
const char desired) {
1318 bool AddFirst(
const char desired,
const char input) {
1319 if (desired ==
input) {
1348 bool DecodeDataBuilder::AddCharacter(
const char desired,
const char input) {
1354 return AddFirst(desired,
input);
1358 if (desired ==
input) {
1368 return AddFirst(desired,
input);
1381 return AddFirst(desired,
input);
1386 string DirectDecodeString(
const string&
str) {
1388 result += (char)
'\0';
1390 result += (char)
'\0';
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 <<
"\"."
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;
1415 DecodeDataBuilder builder;
1419 for (
int y = 0;
y < desired_output.size();
y++) {
1420 const char d = desired_output[
y];
1422 builder.AddUnderscore();
1426 if (
x >= input_for_decode.size()) {
1428 return DirectDecodeString(desired_output);
1430 if (builder.AddCharacter(
d, input_for_decode[
x])) {
1434 return DirectDecodeString(desired_output);
1438 if (
x != input_for_decode.size()) {
1440 return DirectDecodeString(desired_output);
1444 return builder.Finish() + (char)
'\0';
1461 int last_line()
const {
return line_; }
1462 string error_str()
const {
return error_str_; }
1474 bool Parser::ParseChunk(StringPiece chunk) {
1481 bool result = ParseLoop();
1503 bool Parser::ParseLoop() {
1509 if (line.size() == 0) {
1529 fd = posix::open(
path.c_str(), O_RDONLY);
1539 Parser
parser(line_consumer);
1542 while (file_stream.
Next(&
buf, &buf_len)) {
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) {
1571 const string& header_extension) {
1572 const string file_path(
FilePath(file));
1591 std::map<string, string>::iterator proto_lookup =
1595 proto_lookup->second +
"/" +
1614 bool add_blank_line =
false;
1621 "#if $cpp_symbol$\n",
1622 "cpp_symbol", cpp_symbol);
1626 " #import <$framework_name$/$header$>\n",
1627 "framework_name", framework_name,
1635 " #import \"$header$\"\n",
1641 add_blank_line =
true;
1645 if (add_blank_line) {
1646 printer->
Print(
"\n");
1652 "#import <$header$>\n",
1656 add_blank_line =
true;
1660 if (add_blank_line) {
1661 printer->
Print(
"\n");
1664 for (std::vector<string>::const_iterator iter =
other_imports_.begin();
1667 "#import \"$header$\"\n",
1682 &collector, &parse_error)) {
1684 <<
" : " << parse_error << std::endl;
1694 string(
"Framework/proto file mapping line without colon sign: '") +
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;
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;