From 2b3a6d2cdc2c59abb2ae06bf490b21729a8ad95b Mon Sep 17 00:00:00 2001 From: Thomas Van Lenten Date: Tue, 4 Oct 2022 12:00:48 -0400 Subject: [PATCH] [ObjC] First pass at bringing back helpers.cc There are still some things not related to naming in naming.h, but they are current exposed for gRPC to use. --- src/file_lists.cmake | 1 + .../protobuf/compiler/objectivec/BUILD.bazel | 1 + .../protobuf/compiler/objectivec/enum.cc | 1 + .../compiler/objectivec/enum_field.cc | 1 + .../protobuf/compiler/objectivec/extension.cc | 1 + .../protobuf/compiler/objectivec/field.cc | 1 + .../protobuf/compiler/objectivec/file.cc | 1 + .../protobuf/compiler/objectivec/generator.cc | 1 + .../protobuf/compiler/objectivec/helpers.cc | 885 ++++++++++++++++++ .../protobuf/compiler/objectivec/helpers.h | 168 +++- .../compiler/objectivec/helpers_unittest.cc | 1 + .../protobuf/compiler/objectivec/map_field.cc | 1 + .../protobuf/compiler/objectivec/message.cc | 1 + .../compiler/objectivec/message_field.cc | 1 + .../protobuf/compiler/objectivec/names.cc | 752 +-------------- .../protobuf/compiler/objectivec/names.h | 194 +--- .../protobuf/compiler/objectivec/oneof.cc | 1 + .../compiler/objectivec/primitive_field.cc | 1 + 18 files changed, 1092 insertions(+), 921 deletions(-) create mode 100644 src/google/protobuf/compiler/objectivec/helpers.cc diff --git a/src/file_lists.cmake b/src/file_lists.cmake index c701b6619b..3b372c48ba 100644 --- a/src/file_lists.cmake +++ b/src/file_lists.cmake @@ -356,6 +356,7 @@ set(libprotoc_srcs ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/field.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/file.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/generator.cc + ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/helpers.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/map_field.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/message.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/message_field.cc diff --git a/src/google/protobuf/compiler/objectivec/BUILD.bazel b/src/google/protobuf/compiler/objectivec/BUILD.bazel index 465888c916..6bd65b3b9c 100644 --- a/src/google/protobuf/compiler/objectivec/BUILD.bazel +++ b/src/google/protobuf/compiler/objectivec/BUILD.bazel @@ -44,6 +44,7 @@ cc_library( "field.cc", "file.cc", "generator.cc", + "helpers.cc", "map_field.cc", "message.cc", "message_field.cc", diff --git a/src/google/protobuf/compiler/objectivec/enum.cc b/src/google/protobuf/compiler/objectivec/enum.cc index 32a2549c6b..656b80f8d8 100644 --- a/src/google/protobuf/compiler/objectivec/enum.cc +++ b/src/google/protobuf/compiler/objectivec/enum.cc @@ -36,6 +36,7 @@ #include "absl/strings/escaping.h" #include "absl/strings/str_cat.h" +#include "google/protobuf/compiler/objectivec/names.h" #include "google/protobuf/compiler/objectivec/helpers.h" #include "google/protobuf/io/printer.h" diff --git a/src/google/protobuf/compiler/objectivec/enum_field.cc b/src/google/protobuf/compiler/objectivec/enum_field.cc index 58d4ed9f40..72d0b581f0 100644 --- a/src/google/protobuf/compiler/objectivec/enum_field.cc +++ b/src/google/protobuf/compiler/objectivec/enum_field.cc @@ -32,6 +32,7 @@ #include #include "google/protobuf/compiler/objectivec/enum_field.h" +#include "google/protobuf/compiler/objectivec/names.h" #include "google/protobuf/compiler/objectivec/helpers.h" #include "google/protobuf/io/printer.h" diff --git a/src/google/protobuf/compiler/objectivec/extension.cc b/src/google/protobuf/compiler/objectivec/extension.cc index fce45e59cd..f13bff0755 100644 --- a/src/google/protobuf/compiler/objectivec/extension.cc +++ b/src/google/protobuf/compiler/objectivec/extension.cc @@ -33,6 +33,7 @@ #include #include "absl/strings/str_cat.h" +#include "google/protobuf/compiler/objectivec/names.h" #include "google/protobuf/compiler/objectivec/helpers.h" #include "google/protobuf/descriptor.pb.h" #include "google/protobuf/io/printer.h" diff --git a/src/google/protobuf/compiler/objectivec/field.cc b/src/google/protobuf/compiler/objectivec/field.cc index 8c192945b3..ee050549c3 100644 --- a/src/google/protobuf/compiler/objectivec/field.cc +++ b/src/google/protobuf/compiler/objectivec/field.cc @@ -34,6 +34,7 @@ #include "absl/strings/str_cat.h" #include "google/protobuf/compiler/objectivec/enum_field.h" +#include "google/protobuf/compiler/objectivec/names.h" #include "google/protobuf/compiler/objectivec/helpers.h" #include "google/protobuf/compiler/objectivec/map_field.h" #include "google/protobuf/compiler/objectivec/message_field.h" diff --git a/src/google/protobuf/compiler/objectivec/file.cc b/src/google/protobuf/compiler/objectivec/file.cc index d09ed7ead6..e202123e35 100644 --- a/src/google/protobuf/compiler/objectivec/file.cc +++ b/src/google/protobuf/compiler/objectivec/file.cc @@ -38,6 +38,7 @@ #include "absl/strings/str_cat.h" #include "google/protobuf/compiler/objectivec/enum.h" #include "google/protobuf/compiler/objectivec/extension.h" +#include "google/protobuf/compiler/objectivec/names.h" #include "google/protobuf/compiler/objectivec/helpers.h" #include "google/protobuf/compiler/objectivec/message.h" #include "google/protobuf/io/printer.h" diff --git a/src/google/protobuf/compiler/objectivec/generator.cc b/src/google/protobuf/compiler/objectivec/generator.cc index e0e0e29def..8267d2c827 100644 --- a/src/google/protobuf/compiler/objectivec/generator.cc +++ b/src/google/protobuf/compiler/objectivec/generator.cc @@ -39,6 +39,7 @@ #include "absl/strings/str_split.h" #include "absl/strings/strip.h" #include "google/protobuf/compiler/objectivec/file.h" +#include "google/protobuf/compiler/objectivec/names.h" #include "google/protobuf/compiler/objectivec/helpers.h" #include "google/protobuf/io/printer.h" #include "google/protobuf/io/zero_copy_stream.h" diff --git a/src/google/protobuf/compiler/objectivec/helpers.cc b/src/google/protobuf/compiler/objectivec/helpers.cc new file mode 100644 index 0000000000..abe227f970 --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/helpers.cc @@ -0,0 +1,885 @@ +// 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/code_generator.h" +#include "google/protobuf/stubs/strutil.h" +#include "absl/strings/ascii.h" +#include "absl/strings/escaping.h" +#include "absl/strings/str_replace.h" +#include "absl/strings/str_split.h" +#include "google/protobuf/compiler/objectivec/helpers.h" +#include "google/protobuf/compiler/objectivec/names.h" +#include "google/protobuf/io/printer.h" +#include "google/protobuf/io/zero_copy_stream_impl.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 && (val.find('.') != std::string::npos || + val.find('e') != std::string::npos || + val.find('E') != std::string::npos)) { + 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; +} + +bool IsPrimitiveType(const FieldDescriptor* field) { + ObjectiveCType type = GetObjectiveCType(field); + switch (type) { + case OBJECTIVECTYPE_INT32: + case OBJECTIVECTYPE_UINT32: + case OBJECTIVECTYPE_INT64: + case OBJECTIVECTYPE_UINT64: + case OBJECTIVECTYPE_FLOAT: + case OBJECTIVECTYPE_DOUBLE: + case OBJECTIVECTYPE_BOOLEAN: + case OBJECTIVECTYPE_ENUM: + return true; + break; + default: + return false; + } +} + +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( + SimpleDtoa(field->default_value_double()), false); + case FieldDescriptor::CPPTYPE_FLOAT: + return HandleExtremeFloatingPoint( + 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(); +} + +bool HasNonZeroDefaultValue(const FieldDescriptor* field) { + // Repeated fields don't have defaults. + if (field->is_repeated()) { + return false; + } + + // As much as checking field->has_default_value() seems useful, it isn't + // because of enums. proto2 syntax allows the first item in an enum (the + // default) to be non zero. So checking field->has_default_value() would + // result in missing this non zero default. See MessageWithOneBasedEnum in + // objectivec/Tests/unittest_objc.proto for a test Message to confirm this. + + // Some proto file set the default to the zero value, so make sure the value + // isn't the zero case. + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + return field->default_value_int32() != 0; + case FieldDescriptor::CPPTYPE_UINT32: + return field->default_value_uint32() != 0U; + case FieldDescriptor::CPPTYPE_INT64: + return field->default_value_int64() != 0LL; + case FieldDescriptor::CPPTYPE_UINT64: + return field->default_value_uint64() != 0ULL; + case FieldDescriptor::CPPTYPE_DOUBLE: + return field->default_value_double() != 0.0; + case FieldDescriptor::CPPTYPE_FLOAT: + return field->default_value_float() != 0.0f; + case FieldDescriptor::CPPTYPE_BOOL: + return field->default_value_bool(); + case FieldDescriptor::CPPTYPE_STRING: { + const std::string& default_string = field->default_value_string(); + return default_string.length() != 0; + } + case FieldDescriptor::CPPTYPE_ENUM: + return field->default_value_enum()->number() != 0; + case FieldDescriptor::CPPTYPE_MESSAGE: + return false; + } + + // 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 false; +} + +std::string BuildFlagsString(const FlagType flag_type, + const std::vector& 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 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; +} + +TextFormatDecodeData::TextFormatDecodeData() { } + +TextFormatDecodeData::~TextFormatDecodeData() { } + +void TextFormatDecodeData::AddString(int32_t key, + const std::string& input_for_decode, + const std::string& desired_output) { + for (std::vector::const_iterator i = entries_.begin(); + i != entries_.end(); ++i) { + if (i->first == key) { + std::cerr << "error: duplicate key (" << key + << ") making TextFormat data, input: \"" << input_for_decode + << "\", desired: \"" << desired_output << "\"." << std::endl; + std::cerr.flush(); + abort(); + } + } + + const std::string& data = TextFormatDecodeData::DecodeDataForString( + input_for_decode, desired_output); + entries_.push_back(DataEntry(key, data)); +} + +std::string TextFormatDecodeData::Data() const { + std::ostringstream data_stringstream; + + if (num_entries() > 0) { + io::OstreamOutputStream data_outputstream(&data_stringstream); + io::CodedOutputStream output_stream(&data_outputstream); + + output_stream.WriteVarint32(num_entries()); + for (std::vector::const_iterator i = entries_.begin(); + i != entries_.end(); ++i) { + output_stream.WriteVarint32(i->first); + output_stream.WriteString(i->second); + } + } + + data_stringstream.flush(); + return data_stringstream.str(); +} + +namespace { + +// Helper to build up the decode data for a string. +class DecodeDataBuilder { + public: + DecodeDataBuilder() { Reset(); } + + bool AddCharacter(const char desired, const char input); + void AddUnderscore() { + Push(); + need_underscore_ = true; + } + std::string Finish() { + Push(); + return decode_data_; + } + + private: + static constexpr uint8_t kAddUnderscore = 0x80; + + static constexpr uint8_t kOpAsIs = 0x00; + static constexpr uint8_t kOpFirstUpper = 0x40; + static constexpr uint8_t kOpFirstLower = 0x20; + static constexpr uint8_t kOpAllUpper = 0x60; + + static constexpr int kMaxSegmentLen = 0x1f; + + void AddChar(const char desired) { + ++segment_len_; + is_all_upper_ &= absl::ascii_isupper(desired); + } + + void Push() { + uint8_t op = (op_ | segment_len_); + if (need_underscore_) op |= kAddUnderscore; + if (op != 0) { + decode_data_ += (char)op; + } + Reset(); + } + + bool AddFirst(const char desired, const char input) { + if (desired == input) { + op_ = kOpAsIs; + } else if (desired == absl::ascii_toupper(input)) { + op_ = kOpFirstUpper; + } else if (desired == absl::ascii_tolower(input)) { + op_ = kOpFirstLower; + } else { + // Can't be transformed to match. + return false; + } + AddChar(desired); + return true; + } + + void Reset() { + need_underscore_ = false; + op_ = 0; + segment_len_ = 0; + is_all_upper_ = true; + } + + bool need_underscore_; + bool is_all_upper_; + uint8_t op_; + int segment_len_; + + std::string decode_data_; +}; + +bool DecodeDataBuilder::AddCharacter(const char desired, const char input) { + // If we've hit the max size, push to start a new segment. + if (segment_len_ == kMaxSegmentLen) { + Push(); + } + if (segment_len_ == 0) { + return AddFirst(desired, input); + } + + // Desired and input match... + if (desired == input) { + // If we aren't transforming it, or we're upper casing it and it is + // supposed to be uppercase; just add it to the segment. + if ((op_ != kOpAllUpper) || absl::ascii_isupper(desired)) { + AddChar(desired); + return true; + } + + // Add the current segment, and start the next one. + Push(); + return AddFirst(desired, input); + } + + // If we need to uppercase, and everything so far has been uppercase, + // promote op to AllUpper. + if ((desired == absl::ascii_toupper(input)) && is_all_upper_) { + op_ = kOpAllUpper; + AddChar(desired); + return true; + } + + // Give up, push and start a new segment. + Push(); + return AddFirst(desired, input); +} + +// If decode data can't be generated, a directive for the raw string +// is used instead. +std::string DirectDecodeString(const std::string& str) { + std::string result; + result += (char)'\0'; // Marker for full string. + result += str; + result += (char)'\0'; // End of string. + return result; +} + +} // namespace + +// static +std::string TextFormatDecodeData::DecodeDataForString( + const std::string& input_for_decode, const std::string& desired_output) { + if (input_for_decode.empty() || desired_output.empty()) { + std::cerr << "error: got empty string for making TextFormat data, input: \"" + << input_for_decode << "\", desired: \"" << desired_output << "\"." + << std::endl; + std::cerr.flush(); + abort(); + } + if ((input_for_decode.find('\0') != std::string::npos) || + (desired_output.find('\0') != std::string::npos)) { + std::cerr << "error: got a null char in a string for making TextFormat data," + << " input: \"" << absl::CEscape(input_for_decode) << "\", desired: \"" + << absl::CEscape(desired_output) << "\"." << std::endl; + std::cerr.flush(); + abort(); + } + + DecodeDataBuilder builder; + + // Walk the output building it from the input. + int x = 0; + for (int y = 0; y < desired_output.size(); y++) { + const char d = desired_output[y]; + if (d == '_') { + builder.AddUnderscore(); + continue; + } + + if (x >= input_for_decode.size()) { + // Out of input, no way to encode it, just return a full decode. + return DirectDecodeString(desired_output); + } + if (builder.AddCharacter(d, input_for_decode[x])) { + ++x; // Consumed one input + } else { + // Couldn't transform for the next character, just return a full decode. + return DirectDecodeString(desired_output); + } + } + + if (x != input_for_decode.size()) { + // Extra input (suffix from name sanitizing?), just return a full decode. + return DirectDecodeString(desired_output); + } + + // Add the end marker. + return builder.Finish() + (char)'\0'; +} + +namespace { + +class ProtoFrameworkCollector : public LineConsumer { + public: + ProtoFrameworkCollector(std::map* inout_proto_file_to_framework_name) + : map_(inout_proto_file_to_framework_name) {} + + virtual bool ConsumeLine(const absl::string_view& line, std::string* out_error) override; + + private: + std::map* map_; +}; + +bool ProtoFrameworkCollector::ConsumeLine( + const absl::string_view& line, std::string* out_error) { + int offset = line.find(':'); + if (offset == absl::string_view::npos) { + *out_error = + std::string("Framework/proto file mapping line without colon sign: '") + + std::string(line) + "'."; + return false; + } + absl::string_view framework_name = line.substr(0, offset); + absl::string_view proto_file_list = line.substr(offset + 1); + TrimWhitespace(&framework_name); + + int start = 0; + while (start < proto_file_list.length()) { + offset = proto_file_list.find(',', start); + if (offset == absl::string_view::npos) { + offset = proto_file_list.length(); + } + + absl::string_view proto_file = proto_file_list.substr(start, offset - start); + TrimWhitespace(&proto_file); + if (!proto_file.empty()) { + std::map::iterator existing_entry = + map_->find(std::string(proto_file)); + if (existing_entry != map_->end()) { + std::cerr << "warning: duplicate proto file reference, replacing " + "framework entry for '" + << std::string(proto_file) << "' with '" << std::string(framework_name) + << "' (was '" << existing_entry->second << "')." << std::endl; + std::cerr.flush(); + } + + if (proto_file.find(' ') != absl::string_view::npos) { + std::cerr << "note: framework mapping file had a proto file with a " + "space in, hopefully that isn't a missing comma: '" + << std::string(proto_file) << "'" << std::endl; + std::cerr.flush(); + } + + (*map_)[std::string(proto_file)] = std::string(framework_name); + } + + start = offset + 1; + } + + return true; +} + +} + +ImportWriter::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) + : generate_for_named_framework_(generate_for_named_framework), + named_framework_to_proto_path_mappings_path_( + named_framework_to_proto_path_mappings_path), + runtime_import_prefix_(runtime_import_prefix), + include_wkt_imports_(include_wkt_imports), + need_to_parse_mapping_file_(true) {} + +ImportWriter::~ImportWriter() {} + +void ImportWriter::AddFile(const FileDescriptor* file, + const std::string& header_extension) { + if (IsProtobufLibraryBundledProtoFile(file)) { + // The imports of the WKTs are only needed within the library itself, + // in other cases, they get skipped because the generated code already + // import GPBProtocolBuffers.h and hence proves them. + if (include_wkt_imports_) { + const std::string header_name = + "GPB" + FilePathBasename(file) + header_extension; + protobuf_imports_.push_back(header_name); + } + return; + } + + // Lazy parse any mappings. + if (need_to_parse_mapping_file_) { + ParseFrameworkMappings(); + } + + std::map::iterator proto_lookup = + proto_file_to_framework_name_.find(file->name()); + if (proto_lookup != proto_file_to_framework_name_.end()) { + other_framework_imports_.push_back( + proto_lookup->second + "/" + + FilePathBasename(file) + header_extension); + return; + } + + if (!generate_for_named_framework_.empty()) { + other_framework_imports_.push_back( + generate_for_named_framework_ + "/" + + FilePathBasename(file) + header_extension); + return; + } + + other_imports_.push_back(FilePath(file) + header_extension); +} + +void ImportWriter::Print(io::Printer* printer) const { + bool add_blank_line = false; + + if (!protobuf_imports_.empty()) { + PrintRuntimeImports(printer, protobuf_imports_, runtime_import_prefix_); + add_blank_line = true; + } + + if (!other_framework_imports_.empty()) { + if (add_blank_line) { + printer->Print("\n"); + } + + for (std::vector::const_iterator iter = + other_framework_imports_.begin(); + iter != other_framework_imports_.end(); ++iter) { + printer->Print( + "#import <$header$>\n", + "header", *iter); + } + + add_blank_line = true; + } + + if (!other_imports_.empty()) { + if (add_blank_line) { + printer->Print("\n"); + } + + for (std::vector::const_iterator iter = other_imports_.begin(); + iter != other_imports_.end(); ++iter) { + printer->Print( + "#import \"$header$\"\n", + "header", *iter); + } + } +} + +void ImportWriter::PrintRuntimeImports( + io::Printer* printer, const std::vector& header_to_import, + const std::string& runtime_import_prefix, bool default_cpp_symbol) { + // Given an override, use that. + if (!runtime_import_prefix.empty()) { + for (const auto& header : header_to_import) { + printer->Print( + " #import \"$import_prefix$/$header$\"\n", + "import_prefix", runtime_import_prefix, + "header", header); + } + return; + } + + const std::string framework_name(ProtobufLibraryFrameworkName); + const std::string cpp_symbol(ProtobufFrameworkImportSymbol(framework_name)); + + if (default_cpp_symbol) { + printer->Print( + "// This CPP symbol can be defined to use imports that match up to the framework\n" + "// imports needed when using CocoaPods.\n" + "#if !defined($cpp_symbol$)\n" + " #define $cpp_symbol$ 0\n" + "#endif\n" + "\n", + "cpp_symbol", cpp_symbol); + } + + printer->Print( + "#if $cpp_symbol$\n", + "cpp_symbol", cpp_symbol); + for (const auto& header : header_to_import) { + printer->Print( + " #import <$framework_name$/$header$>\n", + "framework_name", framework_name, + "header", header); + } + printer->Print( + "#else\n"); + for (const auto& header : header_to_import) { + printer->Print( + " #import \"$header$\"\n", + "header", header); + } + printer->Print( + "#endif\n"); +} + +void ImportWriter::ParseFrameworkMappings() { + need_to_parse_mapping_file_ = false; + if (named_framework_to_proto_path_mappings_path_.empty()) { + return; // Nothing to do. + } + + ProtoFrameworkCollector collector(&proto_file_to_framework_name_); + std::string parse_error; + if (!ParseSimpleFile(named_framework_to_proto_path_mappings_path_, + &collector, &parse_error)) { + std::cerr << "error parsing " << named_framework_to_proto_path_mappings_path_ + << " : " << parse_error << std::endl; + std::cerr.flush(); + } +} + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/objectivec/helpers.h b/src/google/protobuf/compiler/objectivec/helpers.h index 9c61dfa57b..b644513405 100644 --- a/src/google/protobuf/compiler/objectivec/helpers.h +++ b/src/google/protobuf/compiler/objectivec/helpers.h @@ -33,25 +33,177 @@ #ifndef GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_HELPERS_H__ #define GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_HELPERS_H__ -#include "google/protobuf/compiler/objectivec/names.h" +#include +#include -// clang-format off -#include "google/protobuf/port_def.inc" -// clang-format on +#include "google/protobuf/descriptor.h" +#include "google/protobuf/descriptor.pb.h" namespace google { namespace protobuf { namespace compiler { namespace objectivec { -// TODO(thomasvl) Move internal helpers in names.h back to here. Currently -// the dependencies are too interwoven to easily split up. +inline bool HasPreservingUnknownEnumSemantics(const FileDescriptor* file) { + return file->syntax() == FileDescriptor::SYNTAX_PROTO3; +} + +inline bool IsMapEntryMessage(const Descriptor* descriptor) { + return descriptor->options().map_entry(); +} + +// Escape C++ trigraphs by escaping question marks to "\?". +std::string EscapeTrigraphs(absl::string_view to_escape); + +enum ObjectiveCType { + OBJECTIVECTYPE_INT32, + OBJECTIVECTYPE_UINT32, + OBJECTIVECTYPE_INT64, + OBJECTIVECTYPE_UINT64, + OBJECTIVECTYPE_FLOAT, + OBJECTIVECTYPE_DOUBLE, + OBJECTIVECTYPE_BOOLEAN, + OBJECTIVECTYPE_STRING, + OBJECTIVECTYPE_DATA, + OBJECTIVECTYPE_ENUM, + OBJECTIVECTYPE_MESSAGE +}; + +enum FlagType { + FLAGTYPE_DESCRIPTOR_INITIALIZATION, + FLAGTYPE_EXTENSION, + FLAGTYPE_FIELD +}; + +std::string GetCapitalizedType(const FieldDescriptor* field); + +ObjectiveCType GetObjectiveCType(FieldDescriptor::Type field_type); + +inline ObjectiveCType GetObjectiveCType(const FieldDescriptor* field) { + return GetObjectiveCType(field->type()); +} + +bool IsPrimitiveType(const FieldDescriptor* field); +inline bool IsReferenceType(const FieldDescriptor* field) { + return !IsPrimitiveType(field); +} + +std::string GPBGenericValueFieldName(const FieldDescriptor* field); +std::string DefaultValue(const FieldDescriptor* field); +bool HasNonZeroDefaultValue(const FieldDescriptor* field); + +std::string BuildFlagsString(const FlagType type, const std::vector& strings); + +// Returns a symbol that can be used in C code to refer to an Objective C +// class without initializing the class. +std::string ObjCClass(const std::string& class_name); + +// Declares an Objective C class without initializing the class so that it can +// be refrerred to by ObjCClass. +std::string ObjCClassDeclaration(const std::string& class_name); + +// Builds HeaderDoc/appledoc style comments out of the comments in the .proto +// file. +std::string BuildCommentsString(const SourceLocation& location, + bool prefer_single_line); + +template +std::string GetOptionalDeprecatedAttribute(const TDescriptor* descriptor, + const FileDescriptor* file = NULL, + bool preSpace = true, + bool postNewline = false) { + bool isDeprecated = descriptor->options().deprecated(); + // The file is only passed when checking Messages & Enums, so those types + // get tagged. At the moment, it doesn't seem to make sense to tag every + // field or enum value with when the file is deprecated. + bool isFileLevelDeprecation = false; + if (!isDeprecated && file) { + isFileLevelDeprecation = file->options().deprecated(); + isDeprecated = isFileLevelDeprecation; + } + if (isDeprecated) { + std::string message; + const FileDescriptor* sourceFile = descriptor->file(); + if (isFileLevelDeprecation) { + message = sourceFile->name() + " is deprecated."; + } else { + message = descriptor->full_name() + " is deprecated (see " + + sourceFile->name() + ")."; + } + + std::string result = std::string("GPB_DEPRECATED_MSG(\"") + message + "\")"; + if (preSpace) { + result.insert(0, " "); + } + if (postNewline) { + result.append("\n"); + } + return result; + } else { + return ""; + } +} + +// Generate decode data needed for ObjC's GPBDecodeTextFormatName() to transform +// the input into the expected output. +class TextFormatDecodeData { + public: + TextFormatDecodeData(); + ~TextFormatDecodeData(); + + TextFormatDecodeData(const TextFormatDecodeData&) = delete; + TextFormatDecodeData& operator=(const TextFormatDecodeData&) = delete; + + void AddString(int32_t key, const std::string& input_for_decode, + const std::string& desired_output); + size_t num_entries() const { return entries_.size(); } + std::string Data() const; + + static std::string DecodeDataForString(const std::string& input_for_decode, + const std::string& desired_output); + + private: + typedef std::pair DataEntry; + std::vector entries_; +}; + +// Helper class for parsing framework import mappings and generating +// import statements. +class ImportWriter { + public: + 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); + ~ImportWriter(); + + void AddFile(const FileDescriptor* file, const std::string& header_extension); + void Print(io::Printer* printer) const; + + static void PrintRuntimeImports(io::Printer* printer, + const std::vector& header_to_import, + const std::string& runtime_import_prefix, + bool default_cpp_symbol = false); + + private: + void ParseFrameworkMappings(); + + const std::string generate_for_named_framework_; + const std::string named_framework_to_proto_path_mappings_path_; + const std::string runtime_import_prefix_; + const bool include_wkt_imports_; + std::map proto_file_to_framework_name_; + bool need_to_parse_mapping_file_; + + std::vector protobuf_imports_; + std::vector other_framework_imports_; + std::vector other_imports_; +}; + } // namespace objectivec } // namespace compiler } // namespace protobuf } // namespace google -#include "google/protobuf/port_undef.inc" - #endif // GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_HELPERS_H__ diff --git a/src/google/protobuf/compiler/objectivec/helpers_unittest.cc b/src/google/protobuf/compiler/objectivec/helpers_unittest.cc index d73efb34aa..5b49459cb7 100644 --- a/src/google/protobuf/compiler/objectivec/helpers_unittest.cc +++ b/src/google/protobuf/compiler/objectivec/helpers_unittest.cc @@ -28,6 +28,7 @@ // (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/names.h" #include "google/protobuf/compiler/objectivec/helpers.h" #include "google/protobuf/io/zero_copy_stream_impl_lite.h" #include diff --git a/src/google/protobuf/compiler/objectivec/map_field.cc b/src/google/protobuf/compiler/objectivec/map_field.cc index bbc590cd05..e11794a77e 100644 --- a/src/google/protobuf/compiler/objectivec/map_field.cc +++ b/src/google/protobuf/compiler/objectivec/map_field.cc @@ -32,6 +32,7 @@ #include #include "google/protobuf/compiler/objectivec/map_field.h" +#include "google/protobuf/compiler/objectivec/names.h" #include "google/protobuf/compiler/objectivec/helpers.h" #include "google/protobuf/io/printer.h" diff --git a/src/google/protobuf/compiler/objectivec/message.cc b/src/google/protobuf/compiler/objectivec/message.cc index 530b7cd88b..9c50d952d1 100644 --- a/src/google/protobuf/compiler/objectivec/message.cc +++ b/src/google/protobuf/compiler/objectivec/message.cc @@ -38,6 +38,7 @@ #include "absl/strings/str_cat.h" #include "google/protobuf/compiler/objectivec/enum.h" #include "google/protobuf/compiler/objectivec/extension.h" +#include "google/protobuf/compiler/objectivec/names.h" #include "google/protobuf/compiler/objectivec/helpers.h" #include "google/protobuf/descriptor.pb.h" #include "google/protobuf/io/printer.h" diff --git a/src/google/protobuf/compiler/objectivec/message_field.cc b/src/google/protobuf/compiler/objectivec/message_field.cc index 675b479b28..b9dd6c02fb 100644 --- a/src/google/protobuf/compiler/objectivec/message_field.cc +++ b/src/google/protobuf/compiler/objectivec/message_field.cc @@ -32,6 +32,7 @@ #include #include "google/protobuf/compiler/objectivec/message_field.h" +#include "google/protobuf/compiler/objectivec/names.h" #include "google/protobuf/compiler/objectivec/helpers.h" #include "google/protobuf/io/printer.h" diff --git a/src/google/protobuf/compiler/objectivec/names.cc b/src/google/protobuf/compiler/objectivec/names.cc index a72da60e05..7ccb713caa 100644 --- a/src/google/protobuf/compiler/objectivec/names.cc +++ b/src/google/protobuf/compiler/objectivec/names.cc @@ -43,21 +43,13 @@ #include #include "google/protobuf/compiler/code_generator.h" -#include "google/protobuf/stubs/strutil.h" -#include "absl/strings/ascii.h" -#include "absl/strings/escaping.h" #include "absl/strings/str_replace.h" #include "absl/strings/str_split.h" -#include "absl/strings/strip.h" #include "google/protobuf/compiler/objectivec/names.h" #include "google/protobuf/compiler/objectivec/nsobject_methods.h" #include "google/protobuf/descriptor.pb.h" -#include "google/protobuf/io/coded_stream.h" #include "google/protobuf/io/io_win32.h" -#include "google/protobuf/io/printer.h" #include "google/protobuf/io/zero_copy_stream_impl.h" -#include "google/protobuf/port.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. @@ -267,24 +259,6 @@ void SetForcedPackagePrefix(const std::string& prefix) { g_prefix_mode.set_forced_package_prefix(prefix); } -Options::Options() { - // While there are generator options, also support env variables to help with - // build systems where it isn't as easy to hook in for add the generation - // options when invoking protoc. - const char* file_path = getenv("GPB_OBJC_EXPECTED_PACKAGE_PREFIXES"); - if (file_path) { - expected_prefixes_path = file_path; - } - const char* suppressions = getenv("GPB_OBJC_EXPECTED_PACKAGE_PREFIXES_SUPPRESSIONS"); - if (suppressions) { - expected_prefixes_suppressions = - absl::StrSplit(suppressions, ";", absl::SkipEmpty()); - } - prefixes_must_be_registered = - BoolFromEnvVar("GPB_OBJC_PREFIXES_MUST_BE_REGISTERED", false); - require_prefixes = BoolFromEnvVar("GPB_OBJC_REQUIRE_PREFIXES", false); -} - namespace { std::unordered_set MakeWordsMap(const char* const words[], @@ -534,34 +508,6 @@ bool IsSpecialNamePrefix(const std::string& name, return false; } -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(); - } -} - void MaybeUnQuote(absl::string_view* input) { if ((input->length() >= 2) && ((*input->data() == '\'' || *input->data() == '"')) && @@ -573,11 +519,6 @@ void MaybeUnQuote(absl::string_view* input) { } // namespace -// Escape C++ trigraphs by escaping question marks to \? -std::string EscapeTrigraphs(absl::string_view to_escape) { - return absl::StrReplaceAll(to_escape, {{"?", "\\?"}}); -} - void TrimWhitespace(absl::string_view* input) { while (!input->empty() && absl::ascii_isspace(*input->data())) { input->remove_prefix(1); @@ -869,14 +810,6 @@ std::string OneofNameCapitalized(const OneofDescriptor* descriptor) { return result; } -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 UnCamelCaseFieldName(const std::string& name, const FieldDescriptor* field) { absl::string_view worker(name); if (absl::EndsWith(worker, "_p")) { @@ -911,369 +844,6 @@ std::string UnCamelCaseFieldName(const std::string& name, const FieldDescriptor* } } -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; -} - -bool IsPrimitiveType(const FieldDescriptor* field) { - ObjectiveCType type = GetObjectiveCType(field); - switch (type) { - case OBJECTIVECTYPE_INT32: - case OBJECTIVECTYPE_UINT32: - case OBJECTIVECTYPE_INT64: - case OBJECTIVECTYPE_UINT64: - case OBJECTIVECTYPE_FLOAT: - case OBJECTIVECTYPE_DOUBLE: - case OBJECTIVECTYPE_BOOLEAN: - case OBJECTIVECTYPE_ENUM: - return true; - break; - default: - return false; - } -} - -bool IsReferenceType(const FieldDescriptor* field) { - return !IsPrimitiveType(field); -} - -static 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 && (val.find('.') != std::string::npos || - val.find('e') != std::string::npos || - val.find('E') != std::string::npos)) { - val += "f"; - } - return val; - } -} - -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( - SimpleDtoa(field->default_value_double()), false); - case FieldDescriptor::CPPTYPE_FLOAT: - return HandleExtremeFloatingPoint( - 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(); -} - -bool HasNonZeroDefaultValue(const FieldDescriptor* field) { - // Repeated fields don't have defaults. - if (field->is_repeated()) { - return false; - } - - // As much as checking field->has_default_value() seems useful, it isn't - // because of enums. proto2 syntax allows the first item in an enum (the - // default) to be non zero. So checking field->has_default_value() would - // result in missing this non zero default. See MessageWithOneBasedEnum in - // objectivec/Tests/unittest_objc.proto for a test Message to confirm this. - - // Some proto file set the default to the zero value, so make sure the value - // isn't the zero case. - switch (field->cpp_type()) { - case FieldDescriptor::CPPTYPE_INT32: - return field->default_value_int32() != 0; - case FieldDescriptor::CPPTYPE_UINT32: - return field->default_value_uint32() != 0U; - case FieldDescriptor::CPPTYPE_INT64: - return field->default_value_int64() != 0LL; - case FieldDescriptor::CPPTYPE_UINT64: - return field->default_value_uint64() != 0ULL; - case FieldDescriptor::CPPTYPE_DOUBLE: - return field->default_value_double() != 0.0; - case FieldDescriptor::CPPTYPE_FLOAT: - return field->default_value_float() != 0.0f; - case FieldDescriptor::CPPTYPE_BOOL: - return field->default_value_bool(); - case FieldDescriptor::CPPTYPE_STRING: { - const std::string& default_string = field->default_value_string(); - return default_string.length() != 0; - } - case FieldDescriptor::CPPTYPE_ENUM: - return field->default_value_enum()->number() != 0; - case FieldDescriptor::CPPTYPE_MESSAGE: - return false; - } - - // 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 false; -} - -std::string BuildFlagsString(const FlagType flag_type, - const std::vector& 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 BuildCommentsString(const SourceLocation& location, - bool prefer_single_line) { - const std::string& comments = location.leading_comments.empty() - ? location.trailing_comments - : location.leading_comments; - std::vector 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; -} - // Making these a generator option for folks that don't use CocoaPods, but do // want to put the library in a framework is an interesting question. The // problem is it means changing sources shipped with the library to actually @@ -1506,6 +1076,24 @@ bool ValidateObjCClassPrefix( } // namespace +Options::Options() { + // While there are generator options, also support env variables to help with + // build systems where it isn't as easy to hook in for add the generation + // options when invoking protoc. + const char* file_path = getenv("GPB_OBJC_EXPECTED_PACKAGE_PREFIXES"); + if (file_path) { + expected_prefixes_path = file_path; + } + const char* suppressions = getenv("GPB_OBJC_EXPECTED_PACKAGE_PREFIXES_SUPPRESSIONS"); + if (suppressions) { + expected_prefixes_suppressions = + absl::StrSplit(suppressions, ";", absl::SkipEmpty()); + } + prefixes_must_be_registered = + BoolFromEnvVar("GPB_OBJC_PREFIXES_MUST_BE_REGISTERED", false); + require_prefixes = BoolFromEnvVar("GPB_OBJC_REQUIRE_PREFIXES", false); +} + bool ValidateObjCClassPrefixes(const std::vector& files, std::string* out_error) { // Options's ctor load from the environment. @@ -1554,47 +1142,6 @@ bool ValidateObjCClassPrefixes(const std::vector& files, return true; } -TextFormatDecodeData::TextFormatDecodeData() { } - -TextFormatDecodeData::~TextFormatDecodeData() { } - -void TextFormatDecodeData::AddString(int32_t key, - const std::string& input_for_decode, - const std::string& desired_output) { - for (std::vector::const_iterator i = entries_.begin(); - i != entries_.end(); ++i) { - if (i->first == key) { - std::cerr << "error: duplicate key (" << key - << ") making TextFormat data, input: \"" << input_for_decode - << "\", desired: \"" << desired_output << "\"." << std::endl; - std::cerr.flush(); - abort(); - } - } - - const std::string& data = TextFormatDecodeData::DecodeDataForString( - input_for_decode, desired_output); - entries_.push_back(DataEntry(key, data)); -} - -std::string TextFormatDecodeData::Data() const { - std::ostringstream data_stringstream; - - if (num_entries() > 0) { - io::OstreamOutputStream data_outputstream(&data_stringstream); - io::CodedOutputStream output_stream(&data_outputstream); - - output_stream.WriteVarint32(num_entries()); - for (std::vector::const_iterator i = entries_.begin(); - i != entries_.end(); ++i) { - output_stream.WriteVarint32(i->first); - output_stream.WriteString(i->second); - } - } - - data_stringstream.flush(); - return data_stringstream.str(); -} namespace { @@ -1703,69 +1250,8 @@ bool DecodeDataBuilder::AddCharacter(const char desired, const char input) { return AddFirst(desired, input); } -// If decode data can't be generated, a directive for the raw string -// is used instead. -std::string DirectDecodeString(const std::string& str) { - std::string result; - result += (char)'\0'; // Marker for full string. - result += str; - result += (char)'\0'; // End of string. - return result; -} - } // namespace -// static -std::string TextFormatDecodeData::DecodeDataForString( - const std::string& input_for_decode, const std::string& desired_output) { - if (input_for_decode.empty() || desired_output.empty()) { - std::cerr << "error: got empty string for making TextFormat data, input: \"" - << input_for_decode << "\", desired: \"" << desired_output << "\"." - << std::endl; - std::cerr.flush(); - abort(); - } - if ((input_for_decode.find('\0') != std::string::npos) || - (desired_output.find('\0') != std::string::npos)) { - std::cerr << "error: got a null char in a string for making TextFormat data," - << " input: \"" << absl::CEscape(input_for_decode) << "\", desired: \"" - << absl::CEscape(desired_output) << "\"." << std::endl; - std::cerr.flush(); - abort(); - } - - DecodeDataBuilder builder; - - // Walk the output building it from the input. - int x = 0; - for (int y = 0; y < desired_output.size(); y++) { - const char d = desired_output[y]; - if (d == '_') { - builder.AddUnderscore(); - continue; - } - - if (x >= input_for_decode.size()) { - // Out of input, no way to encode it, just return a full decode. - return DirectDecodeString(desired_output); - } - if (builder.AddCharacter(d, input_for_decode[x])) { - ++x; // Consumed one input - } else { - // Couldn't transform for the next character, just return a full decode. - return DirectDecodeString(desired_output); - } - } - - if (x != input_for_decode.size()) { - // Extra input (suffix from name sanitizing?), just return a full decode. - return DirectDecodeString(desired_output); - } - - // Add the end marker. - return builder.Finish() + (char)'\0'; -} - namespace { class Parser { @@ -1887,208 +1373,6 @@ bool ParseSimpleStream(io::ZeroCopyInputStream& input_stream, return true; } -ImportWriter::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) - : generate_for_named_framework_(generate_for_named_framework), - named_framework_to_proto_path_mappings_path_( - named_framework_to_proto_path_mappings_path), - runtime_import_prefix_(runtime_import_prefix), - include_wkt_imports_(include_wkt_imports), - need_to_parse_mapping_file_(true) {} - -ImportWriter::~ImportWriter() {} - -void ImportWriter::AddFile(const FileDescriptor* file, - const std::string& header_extension) { - if (IsProtobufLibraryBundledProtoFile(file)) { - // The imports of the WKTs are only needed within the library itself, - // in other cases, they get skipped because the generated code already - // import GPBProtocolBuffers.h and hence proves them. - if (include_wkt_imports_) { - const std::string header_name = - "GPB" + FilePathBasename(file) + header_extension; - protobuf_imports_.push_back(header_name); - } - return; - } - - // Lazy parse any mappings. - if (need_to_parse_mapping_file_) { - ParseFrameworkMappings(); - } - - std::map::iterator proto_lookup = - proto_file_to_framework_name_.find(file->name()); - if (proto_lookup != proto_file_to_framework_name_.end()) { - other_framework_imports_.push_back( - proto_lookup->second + "/" + - FilePathBasename(file) + header_extension); - return; - } - - if (!generate_for_named_framework_.empty()) { - other_framework_imports_.push_back( - generate_for_named_framework_ + "/" + - FilePathBasename(file) + header_extension); - return; - } - - other_imports_.push_back(FilePath(file) + header_extension); -} - -void ImportWriter::Print(io::Printer* printer) const { - bool add_blank_line = false; - - if (!protobuf_imports_.empty()) { - PrintRuntimeImports(printer, protobuf_imports_, runtime_import_prefix_); - add_blank_line = true; - } - - if (!other_framework_imports_.empty()) { - if (add_blank_line) { - printer->Print("\n"); - } - - for (std::vector::const_iterator iter = - other_framework_imports_.begin(); - iter != other_framework_imports_.end(); ++iter) { - printer->Print( - "#import <$header$>\n", - "header", *iter); - } - - add_blank_line = true; - } - - if (!other_imports_.empty()) { - if (add_blank_line) { - printer->Print("\n"); - } - - for (std::vector::const_iterator iter = other_imports_.begin(); - iter != other_imports_.end(); ++iter) { - printer->Print( - "#import \"$header$\"\n", - "header", *iter); - } - } -} - -void ImportWriter::PrintRuntimeImports( - io::Printer* printer, const std::vector& header_to_import, - const std::string& runtime_import_prefix, bool default_cpp_symbol) { - // Given an override, use that. - if (!runtime_import_prefix.empty()) { - for (const auto& header : header_to_import) { - printer->Print( - " #import \"$import_prefix$/$header$\"\n", - "import_prefix", runtime_import_prefix, - "header", header); - } - return; - } - - const std::string framework_name(ProtobufLibraryFrameworkName); - const std::string cpp_symbol(ProtobufFrameworkImportSymbol(framework_name)); - - if (default_cpp_symbol) { - printer->Print( - "// This CPP symbol can be defined to use imports that match up to the framework\n" - "// imports needed when using CocoaPods.\n" - "#if !defined($cpp_symbol$)\n" - " #define $cpp_symbol$ 0\n" - "#endif\n" - "\n", - "cpp_symbol", cpp_symbol); - } - - printer->Print( - "#if $cpp_symbol$\n", - "cpp_symbol", cpp_symbol); - for (const auto& header : header_to_import) { - printer->Print( - " #import <$framework_name$/$header$>\n", - "framework_name", framework_name, - "header", header); - } - printer->Print( - "#else\n"); - for (const auto& header : header_to_import) { - printer->Print( - " #import \"$header$\"\n", - "header", header); - } - printer->Print( - "#endif\n"); -} - -void ImportWriter::ParseFrameworkMappings() { - need_to_parse_mapping_file_ = false; - if (named_framework_to_proto_path_mappings_path_.empty()) { - return; // Nothing to do. - } - - ProtoFrameworkCollector collector(&proto_file_to_framework_name_); - std::string parse_error; - if (!ParseSimpleFile(named_framework_to_proto_path_mappings_path_, - &collector, &parse_error)) { - std::cerr << "error parsing " << named_framework_to_proto_path_mappings_path_ - << " : " << parse_error << std::endl; - std::cerr.flush(); - } -} - -bool ImportWriter::ProtoFrameworkCollector::ConsumeLine( - const absl::string_view& line, std::string* out_error) { - int offset = line.find(':'); - if (offset == absl::string_view::npos) { - *out_error = - std::string("Framework/proto file mapping line without colon sign: '") + - std::string(line) + "'."; - return false; - } - absl::string_view framework_name = line.substr(0, offset); - absl::string_view proto_file_list = line.substr(offset + 1); - TrimWhitespace(&framework_name); - - int start = 0; - while (start < proto_file_list.length()) { - offset = proto_file_list.find(',', start); - if (offset == absl::string_view::npos) { - offset = proto_file_list.length(); - } - - absl::string_view proto_file = proto_file_list.substr(start, offset - start); - TrimWhitespace(&proto_file); - if (!proto_file.empty()) { - std::map::iterator existing_entry = - map_->find(std::string(proto_file)); - if (existing_entry != map_->end()) { - std::cerr << "warning: duplicate proto file reference, replacing " - "framework entry for '" - << std::string(proto_file) << "' with '" << std::string(framework_name) - << "' (was '" << existing_entry->second << "')." << std::endl; - std::cerr.flush(); - } - - if (proto_file.find(' ') != absl::string_view::npos) { - std::cerr << "note: framework mapping file had a proto file with a " - "space in, hopefully that isn't a missing comma: '" - << std::string(proto_file) << "'" << std::endl; - std::cerr.flush(); - } - - (*map_)[std::string(proto_file)] = std::string(framework_name); - } - - start = offset + 1; - } - - return true; -} - } // namespace objectivec } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/objectivec/names.h b/src/google/protobuf/compiler/objectivec/names.h index 83fb24c37a..103cf1de52 100644 --- a/src/google/protobuf/compiler/objectivec/names.h +++ b/src/google/protobuf/compiler/objectivec/names.h @@ -37,7 +37,6 @@ #include #include "google/protobuf/descriptor.h" -#include "google/protobuf/descriptor.pb.h" #include "google/protobuf/io/zero_copy_stream.h" // clang-format off @@ -70,19 +69,6 @@ void PROTOC_EXPORT SetProtoPackagePrefixExceptionList( std::string PROTOC_EXPORT GetForcedPackagePrefix(); void PROTOC_EXPORT SetForcedPackagePrefix(const std::string& prefix); -// Generator Prefix Validation Options (see generator.cc for a -// description of each): -struct Options { - Options(); - std::string expected_prefixes_path; - std::vector expected_prefixes_suppressions; - bool prefixes_must_be_registered; - bool require_prefixes; -}; - -// Escape C++ trigraphs by escaping question marks to "\?". -std::string PROTOC_EXPORT EscapeTrigraphs(absl::string_view to_escape); - // Remove white space from either end of a absl::string_view. void PROTOC_EXPORT TrimWhitespace(absl::string_view* input); @@ -144,108 +130,10 @@ std::string PROTOC_EXPORT OneofEnumName(const OneofDescriptor* descriptor); std::string PROTOC_EXPORT OneofName(const OneofDescriptor* descriptor); std::string PROTOC_EXPORT OneofNameCapitalized(const OneofDescriptor* descriptor); -// Returns a symbol that can be used in C code to refer to an Objective C -// class without initializing the class. -std::string PROTOC_EXPORT ObjCClass(const std::string& class_name); - -// Declares an Objective C class without initializing the class so that it can -// be refrerred to by ObjCClass. -std::string PROTOC_EXPORT ObjCClassDeclaration(const std::string& class_name); - -inline bool HasPreservingUnknownEnumSemantics(const FileDescriptor* file) { - return file->syntax() == FileDescriptor::SYNTAX_PROTO3; -} - -inline bool IsMapEntryMessage(const Descriptor* descriptor) { - return descriptor->options().map_entry(); -} - // Reverse of the above. std::string PROTOC_EXPORT UnCamelCaseFieldName(const std::string& name, const FieldDescriptor* field); -enum ObjectiveCType { - OBJECTIVECTYPE_INT32, - OBJECTIVECTYPE_UINT32, - OBJECTIVECTYPE_INT64, - OBJECTIVECTYPE_UINT64, - OBJECTIVECTYPE_FLOAT, - OBJECTIVECTYPE_DOUBLE, - OBJECTIVECTYPE_BOOLEAN, - OBJECTIVECTYPE_STRING, - OBJECTIVECTYPE_DATA, - OBJECTIVECTYPE_ENUM, - OBJECTIVECTYPE_MESSAGE -}; - -enum FlagType { - FLAGTYPE_DESCRIPTOR_INITIALIZATION, - FLAGTYPE_EXTENSION, - FLAGTYPE_FIELD -}; - -template -std::string GetOptionalDeprecatedAttribute(const TDescriptor* descriptor, - const FileDescriptor* file = NULL, - bool preSpace = true, - bool postNewline = false) { - bool isDeprecated = descriptor->options().deprecated(); - // The file is only passed when checking Messages & Enums, so those types - // get tagged. At the moment, it doesn't seem to make sense to tag every - // field or enum value with when the file is deprecated. - bool isFileLevelDeprecation = false; - if (!isDeprecated && file) { - isFileLevelDeprecation = file->options().deprecated(); - isDeprecated = isFileLevelDeprecation; - } - if (isDeprecated) { - std::string message; - const FileDescriptor* sourceFile = descriptor->file(); - if (isFileLevelDeprecation) { - message = sourceFile->name() + " is deprecated."; - } else { - message = descriptor->full_name() + " is deprecated (see " + - sourceFile->name() + ")."; - } - - std::string result = std::string("GPB_DEPRECATED_MSG(\"") + message + "\")"; - if (preSpace) { - result.insert(0, " "); - } - if (postNewline) { - result.append("\n"); - } - return result; - } else { - return ""; - } -} - -std::string PROTOC_EXPORT GetCapitalizedType(const FieldDescriptor* field); - -ObjectiveCType PROTOC_EXPORT -GetObjectiveCType(FieldDescriptor::Type field_type); - -inline ObjectiveCType GetObjectiveCType(const FieldDescriptor* field) { - return GetObjectiveCType(field->type()); -} - -bool PROTOC_EXPORT IsPrimitiveType(const FieldDescriptor* field); -bool PROTOC_EXPORT IsReferenceType(const FieldDescriptor* field); - -std::string PROTOC_EXPORT -GPBGenericValueFieldName(const FieldDescriptor* field); -std::string PROTOC_EXPORT DefaultValue(const FieldDescriptor* field); -bool PROTOC_EXPORT HasNonZeroDefaultValue(const FieldDescriptor* field); - -std::string PROTOC_EXPORT -BuildFlagsString(const FlagType type, const std::vector& strings); - -// Builds HeaderDoc/appledoc style comments out of the comments in the .proto -// file. -std::string PROTOC_EXPORT BuildCommentsString(const SourceLocation& location, - bool prefer_single_line); - // The name the commonly used by the library when built as a framework. // This lines up to the name used in the CocoaPod. extern PROTOC_EXPORT const char* const ProtobufLibraryFrameworkName; @@ -254,10 +142,25 @@ extern PROTOC_EXPORT const char* const ProtobufLibraryFrameworkName; std::string PROTOC_EXPORT ProtobufFrameworkImportSymbol(const std::string& framework_name); +// --------------------------------------------------------------------------- + +// These aren't really "naming" related, but can be useful for something +// building on top of ObjC Protos to be able to share the knowledge/enforcement. + // Checks if the file is one of the proto's bundled with the library. bool PROTOC_EXPORT IsProtobufLibraryBundledProtoFile(const FileDescriptor* file); +// Generator Prefix Validation Options (see generator.cc for a +// description of each): +struct Options { + Options(); + std::string expected_prefixes_path; + std::vector expected_prefixes_suppressions; + bool prefixes_must_be_registered; + bool require_prefixes; +}; + // Checks the prefix for the given files and outputs any warnings as needed. If // there are flat out errors, then out_error is filled in with the first error // and the result is false. @@ -269,29 +172,6 @@ bool PROTOC_EXPORT ValidateObjCClassPrefixes( bool PROTOC_EXPORT ValidateObjCClassPrefixes( const std::vector& files, std::string* out_error); -// Generate decode data needed for ObjC's GPBDecodeTextFormatName() to transform -// the input into the expected output. -class PROTOC_EXPORT TextFormatDecodeData { - public: - TextFormatDecodeData(); - ~TextFormatDecodeData(); - - TextFormatDecodeData(const TextFormatDecodeData&) = delete; - TextFormatDecodeData& operator=(const TextFormatDecodeData&) = delete; - - void AddString(int32_t key, const std::string& input_for_decode, - const std::string& desired_output); - size_t num_entries() const { return entries_.size(); } - std::string Data() const; - - static std::string DecodeDataForString(const std::string& input_for_decode, - const std::string& desired_output); - - private: - typedef std::pair DataEntry; - std::vector entries_; -}; - // Helper for parsing simple files. class PROTOC_EXPORT LineConsumer { public: @@ -309,50 +189,6 @@ bool PROTOC_EXPORT ParseSimpleStream(io::ZeroCopyInputStream& input_stream, LineConsumer* line_consumer, std::string* out_error); -// Helper class for parsing framework import mappings and generating -// import statements. -class PROTOC_EXPORT ImportWriter { - public: - 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); - ~ImportWriter(); - - void AddFile(const FileDescriptor* file, const std::string& header_extension); - void Print(io::Printer* printer) const; - - static void PrintRuntimeImports(io::Printer* printer, - const std::vector& header_to_import, - const std::string& runtime_import_prefix, - bool default_cpp_symbol = false); - - private: - class ProtoFrameworkCollector : public LineConsumer { - public: - ProtoFrameworkCollector(std::map* inout_proto_file_to_framework_name) - : map_(inout_proto_file_to_framework_name) {} - - virtual bool ConsumeLine(const absl::string_view& line, std::string* out_error) override; - - private: - std::map* map_; - }; - - void ParseFrameworkMappings(); - - const std::string generate_for_named_framework_; - const std::string named_framework_to_proto_path_mappings_path_; - const std::string runtime_import_prefix_; - const bool include_wkt_imports_; - std::map proto_file_to_framework_name_; - bool need_to_parse_mapping_file_; - - std::vector protobuf_imports_; - std::vector other_framework_imports_; - std::vector other_imports_; -}; - } // namespace objectivec } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/objectivec/oneof.cc b/src/google/protobuf/compiler/objectivec/oneof.cc index 22024988c2..db4922f2d5 100644 --- a/src/google/protobuf/compiler/objectivec/oneof.cc +++ b/src/google/protobuf/compiler/objectivec/oneof.cc @@ -34,6 +34,7 @@ #include #include "absl/strings/str_cat.h" +#include "google/protobuf/compiler/objectivec/names.h" #include "google/protobuf/compiler/objectivec/helpers.h" #include "google/protobuf/io/printer.h" diff --git a/src/google/protobuf/compiler/objectivec/primitive_field.cc b/src/google/protobuf/compiler/objectivec/primitive_field.cc index b64dd42e10..739d1ce334 100644 --- a/src/google/protobuf/compiler/objectivec/primitive_field.cc +++ b/src/google/protobuf/compiler/objectivec/primitive_field.cc @@ -34,6 +34,7 @@ #include #include "absl/strings/str_cat.h" +#include "google/protobuf/compiler/objectivec/names.h" #include "google/protobuf/compiler/objectivec/helpers.h" #include "google/protobuf/io/printer.h"