| // Protocol Buffers - Google's data interchange format |
| // Copyright 2008 Google Inc. All rights reserved. |
| // https://developers.google.com/protocol-buffers/ |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "google/protobuf/compiler/objectivec/helpers.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include "google/protobuf/compiler/code_generator.h" |
| #include "absl/strings/ascii.h" |
| #include "absl/strings/escaping.h" |
| #include "absl/strings/match.h" |
| #include "absl/strings/str_replace.h" |
| #include "absl/strings/str_split.h" |
| #include "google/protobuf/compiler/objectivec/names.h" |
| #include "google/protobuf/io/strtod.h" |
| #include "google/protobuf/stubs/common.h" |
| |
| // NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some |
| // error cases, so it seems to be ok to use as a back door for errors. |
| |
| namespace google { |
| namespace protobuf { |
| namespace compiler { |
| namespace objectivec { |
| |
| std::string EscapeTrigraphs(absl::string_view to_escape) { |
| return absl::StrReplaceAll(to_escape, {{"?", "\\?"}}); |
| } |
| |
| namespace { |
| |
| std::string GetZeroEnumNameForFlagType(const FlagType flag_type) { |
| switch (flag_type) { |
| case FLAGTYPE_DESCRIPTOR_INITIALIZATION: |
| return "GPBDescriptorInitializationFlag_None"; |
| case FLAGTYPE_EXTENSION: |
| return "GPBExtensionNone"; |
| case FLAGTYPE_FIELD: |
| return "GPBFieldNone"; |
| default: |
| GOOGLE_LOG(FATAL) << "Can't get here."; |
| return "0"; |
| } |
| } |
| |
| std::string GetEnumNameForFlagType(const FlagType flag_type) { |
| switch (flag_type) { |
| case FLAGTYPE_DESCRIPTOR_INITIALIZATION: |
| return "GPBDescriptorInitializationFlags"; |
| case FLAGTYPE_EXTENSION: |
| return "GPBExtensionOptions"; |
| case FLAGTYPE_FIELD: |
| return "GPBFieldFlags"; |
| default: |
| GOOGLE_LOG(FATAL) << "Can't get here."; |
| return std::string(); |
| } |
| } |
| |
| std::string HandleExtremeFloatingPoint(std::string val, bool add_float_suffix) { |
| if (val == "nan") { |
| return "NAN"; |
| } else if (val == "inf") { |
| return "INFINITY"; |
| } else if (val == "-inf") { |
| return "-INFINITY"; |
| } else { |
| // float strings with ., e or E need to have f appended |
| if (add_float_suffix && |
| (absl::StrContains(val, '.') || absl::StrContains(val, 'e') || |
| absl::StrContains(val, 'E'))) { |
| val += "f"; |
| } |
| return val; |
| } |
| } |
| |
| } // namespace |
| |
| std::string GetCapitalizedType(const FieldDescriptor* field) { |
| switch (field->type()) { |
| case FieldDescriptor::TYPE_INT32: |
| return "Int32"; |
| case FieldDescriptor::TYPE_UINT32: |
| return "UInt32"; |
| case FieldDescriptor::TYPE_SINT32: |
| return "SInt32"; |
| case FieldDescriptor::TYPE_FIXED32: |
| return "Fixed32"; |
| case FieldDescriptor::TYPE_SFIXED32: |
| return "SFixed32"; |
| case FieldDescriptor::TYPE_INT64: |
| return "Int64"; |
| case FieldDescriptor::TYPE_UINT64: |
| return "UInt64"; |
| case FieldDescriptor::TYPE_SINT64: |
| return "SInt64"; |
| case FieldDescriptor::TYPE_FIXED64: |
| return "Fixed64"; |
| case FieldDescriptor::TYPE_SFIXED64: |
| return "SFixed64"; |
| case FieldDescriptor::TYPE_FLOAT: |
| return "Float"; |
| case FieldDescriptor::TYPE_DOUBLE: |
| return "Double"; |
| case FieldDescriptor::TYPE_BOOL: |
| return "Bool"; |
| case FieldDescriptor::TYPE_STRING: |
| return "String"; |
| case FieldDescriptor::TYPE_BYTES: |
| return "Bytes"; |
| case FieldDescriptor::TYPE_ENUM: |
| return "Enum"; |
| case FieldDescriptor::TYPE_GROUP: |
| return "Group"; |
| case FieldDescriptor::TYPE_MESSAGE: |
| return "Message"; |
| } |
| |
| // Some compilers report reaching end of function even though all cases of |
| // the enum are handed in the switch. |
| GOOGLE_LOG(FATAL) << "Can't get here."; |
| return std::string(); |
| } |
| |
| ObjectiveCType GetObjectiveCType(FieldDescriptor::Type field_type) { |
| switch (field_type) { |
| case FieldDescriptor::TYPE_INT32: |
| case FieldDescriptor::TYPE_SINT32: |
| case FieldDescriptor::TYPE_SFIXED32: |
| return OBJECTIVECTYPE_INT32; |
| |
| case FieldDescriptor::TYPE_UINT32: |
| case FieldDescriptor::TYPE_FIXED32: |
| return OBJECTIVECTYPE_UINT32; |
| |
| case FieldDescriptor::TYPE_INT64: |
| case FieldDescriptor::TYPE_SINT64: |
| case FieldDescriptor::TYPE_SFIXED64: |
| return OBJECTIVECTYPE_INT64; |
| |
| case FieldDescriptor::TYPE_UINT64: |
| case FieldDescriptor::TYPE_FIXED64: |
| return OBJECTIVECTYPE_UINT64; |
| |
| case FieldDescriptor::TYPE_FLOAT: |
| return OBJECTIVECTYPE_FLOAT; |
| |
| case FieldDescriptor::TYPE_DOUBLE: |
| return OBJECTIVECTYPE_DOUBLE; |
| |
| case FieldDescriptor::TYPE_BOOL: |
| return OBJECTIVECTYPE_BOOLEAN; |
| |
| case FieldDescriptor::TYPE_STRING: |
| return OBJECTIVECTYPE_STRING; |
| |
| case FieldDescriptor::TYPE_BYTES: |
| return OBJECTIVECTYPE_DATA; |
| |
| case FieldDescriptor::TYPE_ENUM: |
| return OBJECTIVECTYPE_ENUM; |
| |
| case FieldDescriptor::TYPE_GROUP: |
| case FieldDescriptor::TYPE_MESSAGE: |
| return OBJECTIVECTYPE_MESSAGE; |
| } |
| |
| // Some compilers report reaching end of function even though all cases of |
| // the enum are handed in the switch. |
| GOOGLE_LOG(FATAL) << "Can't get here."; |
| return OBJECTIVECTYPE_INT32; |
| } |
| |
| std::string GPBGenericValueFieldName(const FieldDescriptor* field) { |
| // Returns the field within the GPBGenericValue union to use for the given |
| // field. |
| if (field->is_repeated()) { |
| return "valueMessage"; |
| } |
| switch (field->cpp_type()) { |
| case FieldDescriptor::CPPTYPE_INT32: |
| return "valueInt32"; |
| case FieldDescriptor::CPPTYPE_UINT32: |
| return "valueUInt32"; |
| case FieldDescriptor::CPPTYPE_INT64: |
| return "valueInt64"; |
| case FieldDescriptor::CPPTYPE_UINT64: |
| return "valueUInt64"; |
| case FieldDescriptor::CPPTYPE_FLOAT: |
| return "valueFloat"; |
| case FieldDescriptor::CPPTYPE_DOUBLE: |
| return "valueDouble"; |
| case FieldDescriptor::CPPTYPE_BOOL: |
| return "valueBool"; |
| case FieldDescriptor::CPPTYPE_STRING: |
| if (field->type() == FieldDescriptor::TYPE_BYTES) { |
| return "valueData"; |
| } else { |
| return "valueString"; |
| } |
| case FieldDescriptor::CPPTYPE_ENUM: |
| return "valueEnum"; |
| case FieldDescriptor::CPPTYPE_MESSAGE: |
| return "valueMessage"; |
| } |
| |
| // Some compilers report reaching end of function even though all cases of |
| // the enum are handed in the switch. |
| GOOGLE_LOG(FATAL) << "Can't get here."; |
| return std::string(); |
| } |
| |
| std::string DefaultValue(const FieldDescriptor* field) { |
| // Repeated fields don't have defaults. |
| if (field->is_repeated()) { |
| return "nil"; |
| } |
| |
| // Switch on cpp_type since we need to know which default_value_* method |
| // of FieldDescriptor to call. |
| switch (field->cpp_type()) { |
| case FieldDescriptor::CPPTYPE_INT32: |
| // gcc and llvm reject the decimal form of kint32min and kint64min. |
| if (field->default_value_int32() == INT_MIN) { |
| return "-0x80000000"; |
| } |
| return absl::StrCat(field->default_value_int32()); |
| case FieldDescriptor::CPPTYPE_UINT32: |
| return absl::StrCat(field->default_value_uint32()) + "U"; |
| case FieldDescriptor::CPPTYPE_INT64: |
| // gcc and llvm reject the decimal form of kint32min and kint64min. |
| if (field->default_value_int64() == LLONG_MIN) { |
| return "-0x8000000000000000LL"; |
| } |
| return absl::StrCat(field->default_value_int64()) + "LL"; |
| case FieldDescriptor::CPPTYPE_UINT64: |
| return absl::StrCat(field->default_value_uint64()) + "ULL"; |
| case FieldDescriptor::CPPTYPE_DOUBLE: |
| return HandleExtremeFloatingPoint( |
| io::SimpleDtoa(field->default_value_double()), false); |
| case FieldDescriptor::CPPTYPE_FLOAT: |
| return HandleExtremeFloatingPoint( |
| io::SimpleFtoa(field->default_value_float()), true); |
| case FieldDescriptor::CPPTYPE_BOOL: |
| return field->default_value_bool() ? "YES" : "NO"; |
| case FieldDescriptor::CPPTYPE_STRING: { |
| const bool has_default_value = field->has_default_value(); |
| const std::string& default_string = field->default_value_string(); |
| if (!has_default_value || default_string.length() == 0) { |
| // If the field is defined as being the empty string, |
| // then we will just assign to nil, as the empty string is the |
| // default for both strings and data. |
| return "nil"; |
| } |
| if (field->type() == FieldDescriptor::TYPE_BYTES) { |
| // We want constant fields in our data structures so we can |
| // declare them as static. To achieve this we cheat and stuff |
| // a escaped c string (prefixed with a length) into the data |
| // field, and cast it to an (NSData*) so it will compile. |
| // The runtime library knows how to handle it. |
| |
| // Must convert to a standard byte order for packing length into |
| // a cstring. |
| uint32_t length = ghtonl(default_string.length()); |
| std::string bytes((const char*)&length, sizeof(length)); |
| bytes.append(default_string); |
| return "(NSData*)\"" + EscapeTrigraphs(absl::CEscape(bytes)) + "\""; |
| } else { |
| return "@\"" + EscapeTrigraphs(absl::CEscape(default_string)) + "\""; |
| } |
| } |
| case FieldDescriptor::CPPTYPE_ENUM: |
| return EnumValueName(field->default_value_enum()); |
| case FieldDescriptor::CPPTYPE_MESSAGE: |
| return "nil"; |
| } |
| |
| // Some compilers report reaching end of function even though all cases of |
| // the enum are handed in the switch. |
| GOOGLE_LOG(FATAL) << "Can't get here."; |
| return std::string(); |
| } |
| |
| std::string BuildFlagsString(const FlagType flag_type, |
| const std::vector<std::string>& strings) { |
| if (strings.empty()) { |
| return GetZeroEnumNameForFlagType(flag_type); |
| } else if (strings.size() == 1) { |
| return strings[0]; |
| } |
| std::string string("(" + GetEnumNameForFlagType(flag_type) + ")("); |
| for (size_t i = 0; i != strings.size(); ++i) { |
| if (i > 0) { |
| string.append(" | "); |
| } |
| string.append(strings[i]); |
| } |
| string.append(")"); |
| return string; |
| } |
| |
| std::string ObjCClass(const std::string& class_name) { |
| return std::string("GPBObjCClass(") + class_name + ")"; |
| } |
| |
| std::string ObjCClassDeclaration(const std::string& class_name) { |
| return std::string("GPBObjCClassDeclaration(") + class_name + ");"; |
| } |
| |
| std::string BuildCommentsString(const SourceLocation& location, |
| bool prefer_single_line) { |
| const std::string& comments = location.leading_comments.empty() |
| ? location.trailing_comments |
| : location.leading_comments; |
| std::vector<std::string> lines; |
| lines = absl::StrSplit(comments, '\n', absl::AllowEmpty()); |
| while (!lines.empty() && lines.back().empty()) { |
| lines.pop_back(); |
| } |
| // If there are no comments, just return an empty string. |
| if (lines.empty()) { |
| return ""; |
| } |
| |
| std::string prefix; |
| std::string suffix; |
| std::string final_comments; |
| std::string epilogue; |
| |
| bool add_leading_space = false; |
| |
| if (prefer_single_line && lines.size() == 1) { |
| prefix = "/** "; |
| suffix = " */\n"; |
| } else { |
| prefix = "* "; |
| suffix = "\n"; |
| final_comments += "/**\n"; |
| epilogue = " **/\n"; |
| add_leading_space = true; |
| } |
| |
| for (size_t i = 0; i < lines.size(); i++) { |
| std::string line = absl::StrReplaceAll( |
| absl::StripPrefix(lines[i], " "), |
| {// HeaderDoc and appledoc use '\' and '@' for markers; escape them. |
| {"\\", "\\\\"}, |
| {"@", "\\@"}, |
| // Decouple / from * to not have inline comments inside comments. |
| {"/*", "/\\*"}, |
| {"*/", "*\\/"}}); |
| line = prefix + line; |
| absl::StripAsciiWhitespace(&line); |
| // If not a one line, need to add the first space before *, as |
| // absl::StripAsciiWhitespace would have removed it. |
| line = (add_leading_space ? " " : "") + line; |
| final_comments += line + suffix; |
| } |
| final_comments += epilogue; |
| return final_comments; |
| } |
| |
| } // namespace objectivec |
| } // namespace compiler |
| } // namespace protobuf |
| } // namespace google |