diff options
Diffstat (limited to 'src')
41 files changed, 5184 insertions, 2 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 1e4516fb..02b8cd72 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -135,6 +135,7 @@ nobase_include_HEADERS = \ google/protobuf/compiler/java/java_generator.h \ google/protobuf/compiler/java/java_names.h \ google/protobuf/compiler/javanano/javanano_generator.h \ + google/protobuf/compiler/objectivec/objectivec_generator.h \ google/protobuf/compiler/python/python_generator.h \ google/protobuf/compiler/ruby/ruby_generator.h @@ -297,6 +298,29 @@ libprotoc_la_SOURCES = \ google/protobuf/compiler/javanano/javanano_params.h \ google/protobuf/compiler/javanano/javanano_primitive_field.cc \ google/protobuf/compiler/javanano/javanano_primitive_field.h \ + google/protobuf/compiler/objectivec/objectivec_enum.cc \ + google/protobuf/compiler/objectivec/objectivec_enum.h \ + google/protobuf/compiler/objectivec/objectivec_enum_field.cc \ + google/protobuf/compiler/objectivec/objectivec_enum_field.h \ + google/protobuf/compiler/objectivec/objectivec_extension.cc \ + google/protobuf/compiler/objectivec/objectivec_extension.h \ + google/protobuf/compiler/objectivec/objectivec_field.cc \ + google/protobuf/compiler/objectivec/objectivec_field.h \ + google/protobuf/compiler/objectivec/objectivec_file.cc \ + google/protobuf/compiler/objectivec/objectivec_file.h \ + google/protobuf/compiler/objectivec/objectivec_generator.cc \ + google/protobuf/compiler/objectivec/objectivec_helpers.cc \ + google/protobuf/compiler/objectivec/objectivec_helpers.h \ + google/protobuf/compiler/objectivec/objectivec_map_field.cc \ + google/protobuf/compiler/objectivec/objectivec_map_field.h \ + google/protobuf/compiler/objectivec/objectivec_message.cc \ + google/protobuf/compiler/objectivec/objectivec_message.h \ + google/protobuf/compiler/objectivec/objectivec_message_field.cc \ + google/protobuf/compiler/objectivec/objectivec_message_field.h \ + google/protobuf/compiler/objectivec/objectivec_oneof.cc \ + google/protobuf/compiler/objectivec/objectivec_oneof.h \ + google/protobuf/compiler/objectivec/objectivec_primitive_field.cc \ + google/protobuf/compiler/objectivec/objectivec_primitive_field.h \ google/protobuf/compiler/python/python_generator.cc \ google/protobuf/compiler/ruby/ruby_generator.cc @@ -501,6 +525,7 @@ protobuf_test_SOURCES = \ google/protobuf/compiler/cpp/cpp_plugin_unittest.cc \ google/protobuf/compiler/java/java_plugin_unittest.cc \ google/protobuf/compiler/java/java_doc_comment_unittest.cc \ + google/protobuf/compiler/objectivec/objectivec_helpers_unittest.cc \ google/protobuf/compiler/python/python_plugin_unittest.cc \ google/protobuf/compiler/ruby/ruby_generator_unittest.cc \ $(COMMON_TEST_SOURCES) diff --git a/src/google/protobuf/any.proto b/src/google/protobuf/any.proto index 3aba15b3..0bb375ce 100644 --- a/src/google/protobuf/any.proto +++ b/src/google/protobuf/any.proto @@ -35,6 +35,7 @@ option java_generate_equals_and_hash = true; option java_multiple_files = true; option java_outer_classname = "AnyProto"; option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; // `Any` contains an arbitrary serialized message along with a URL diff --git a/src/google/protobuf/api.proto b/src/google/protobuf/api.proto index 00857f55..f368c24d 100644 --- a/src/google/protobuf/api.proto +++ b/src/google/protobuf/api.proto @@ -37,6 +37,7 @@ import "google/protobuf/type.proto"; option java_multiple_files = true; option java_outer_classname = "ApiProto"; option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; // Api is a light-weight descriptor for a protocol buffer service. diff --git a/src/google/protobuf/compiler/main.cc b/src/google/protobuf/compiler/main.cc index 931b8fa3..cff80164 100644 --- a/src/google/protobuf/compiler/main.cc +++ b/src/google/protobuf/compiler/main.cc @@ -36,6 +36,7 @@ #include <google/protobuf/compiler/java/java_generator.h> #include <google/protobuf/compiler/javanano/javanano_generator.h> #include <google/protobuf/compiler/ruby/ruby_generator.h> +#include <google/protobuf/compiler/objectivec/objectivec_generator.h> int main(int argc, char* argv[]) { @@ -68,5 +69,10 @@ int main(int argc, char* argv[]) { cli.RegisterGenerator("--ruby_out", &rb_generator, "Generate Ruby source file."); + // Objective C + google::protobuf::compiler::objectivec::ObjectiveCGenerator objc_generator; + cli.RegisterGenerator("--objc_out", &objc_generator, + "Generate Objective C header and source."); + return cli.Run(argc, argv); } diff --git a/src/google/protobuf/compiler/objectivec/objectivec_enum.cc b/src/google/protobuf/compiler/objectivec/objectivec_enum.cc new file mode 100644 index 00000000..b7c720d2 --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_enum.cc @@ -0,0 +1,199 @@ +// 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 <map> +#include <string> + +#include <google/protobuf/compiler/objectivec/objectivec_enum.h> +#include <google/protobuf/compiler/objectivec/objectivec_helpers.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/stubs/strutil.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor) + : descriptor_(descriptor), + name_(EnumName(descriptor_)) { + for (int i = 0; i < descriptor_->value_count(); i++) { + const EnumValueDescriptor* value = descriptor_->value(i); + const EnumValueDescriptor* canonical_value = + descriptor_->FindValueByNumber(value->number()); + + if (value == canonical_value) { + base_values_.push_back(value); + } + all_values_.push_back(value); + } +} + +EnumGenerator::~EnumGenerator() {} + +void EnumGenerator::GenerateHeader(io::Printer* printer) { + string enum_comments; + SourceLocation location; + if (descriptor_->GetSourceLocation(&location)) { + enum_comments = BuildCommentsString(location); + } else { + enum_comments = ""; + } + + printer->Print( + "#pragma mark - Enum $name$\n" + "\n", + "name", name_); + + printer->Print("$comments$typedef GPB_ENUM($name$) {\n", + "comments", enum_comments, + "name", name_); + printer->Indent(); + + if (HasPreservingUnknownEnumSemantics(descriptor_->file())) { + // Include the unknown value. + printer->Print( + "$name$_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue,\n", + "name", name_); + } + + for (int i = 0; i < all_values_.size(); i++) { + SourceLocation location; + if (all_values_[i]->GetSourceLocation(&location)) { + string comments = BuildCommentsString(location).c_str(); + if (comments.length() > 0) { + if (i > 0) { + printer->Print("\n"); + } + printer->Print(comments.c_str()); + } + } + + printer->Print( + "$name$ = $value$,\n", + "name", EnumValueName(all_values_[i]), + "value", SimpleItoa(all_values_[i]->number())); + } + printer->Outdent(); + printer->Print( + "};\n" + "\n" + "GPBEnumDescriptor *$name$_EnumDescriptor(void);\n" + "\n" + "BOOL $name$_IsValidValue(int32_t value);\n" + "\n", + "name", name_); +} + +void EnumGenerator::GenerateSource(io::Printer* printer) { + printer->Print( + "#pragma mark - Enum $name$\n" + "\n", + "name", name_); + + printer->Print( + "GPBEnumDescriptor *$name$_EnumDescriptor(void) {\n" + " static GPBEnumDescriptor *descriptor = NULL;\n" + " if (!descriptor) {\n" + " static GPBMessageEnumValueDescription values[] = {\n", + "name", name_); + printer->Indent(); + printer->Indent(); + printer->Indent(); + + // Note: For the TextFormat decode info, we can't use the enum value as + // the key because protocol buffer enums have 'allow_alias', which lets + // a value be used more than once. Instead, the index into the list of + // enum value descriptions is used. Note: start with -1 so the first one + // will be zero. + TextFormatDecodeData text_format_decode_data; + int enum_value_description_key = -1; + + for (int i = 0; i < all_values_.size(); i++) { + ++enum_value_description_key; + string short_name(EnumValueShortName(all_values_[i])); + printer->Print("{ .name = \"$short_name$\", .number = $name$ },\n", + "short_name", short_name, + "name", EnumValueName(all_values_[i])); + if (UnCamelCaseEnumShortName(short_name) != all_values_[i]->name()) { + text_format_decode_data.AddString(enum_value_description_key, short_name, + all_values_[i]->name()); + } + } + printer->Outdent(); + printer->Outdent(); + printer->Outdent(); + printer->Print(" };\n"); + if (text_format_decode_data.num_entries() == 0) { + printer->Print( + " descriptor = [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol($name$)\n" + " values:values\n" + " valueCount:sizeof(values) / sizeof(GPBMessageEnumValueDescription)\n" + " enumVerifier:$name$_IsValidValue];\n", + "name", name_); + } else { + printer->Print( + " static const char *extraTextFormatInfo = \"$extraTextFormatInfo$\";\n" + " descriptor = [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol($name$)\n" + " values:values\n" + " valueCount:sizeof(values) / sizeof(GPBMessageEnumValueDescription)\n" + " enumVerifier:$name$_IsValidValue\n" + " extraTextFormatInfo:extraTextFormatInfo];\n", + "name", name_, + "extraTextFormatInfo", CEscape(text_format_decode_data.Data())); + } + printer->Print( + " }\n" + " return descriptor;\n" + "}\n\n"); + + printer->Print( + "BOOL $name$_IsValidValue(int32_t value__) {\n" + " switch (value__) {\n", + "name", name_); + + for (int i = 0; i < base_values_.size(); i++) { + printer->Print( + " case $name$:\n", + "name", EnumValueName(base_values_[i])); + } + + printer->Print( + " return YES;\n" + " default:\n" + " return NO;\n" + " }\n" + "}\n\n"); +} +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/objectivec/objectivec_enum.h b/src/google/protobuf/compiler/objectivec/objectivec_enum.h new file mode 100644 index 00000000..2dc5547b --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_enum.h @@ -0,0 +1,73 @@ +// 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. + +#ifndef GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_ENUM_H__ +#define GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_ENUM_H__ + +#include <string> +#include <set> +#include <vector> +#include <google/protobuf/descriptor.h> + +namespace google { +namespace protobuf { +namespace io { +class Printer; // printer.h +} +} + +namespace protobuf { +namespace compiler { +namespace objectivec { + +class EnumGenerator { + public: + EnumGenerator(const EnumDescriptor* descriptor); + ~EnumGenerator(); + + void GenerateHeader(io::Printer* printer); + void GenerateSource(io::Printer* printer); + + const string& name() const { return name_; } + + private: + const EnumDescriptor* descriptor_; + vector<const EnumValueDescriptor*> base_values_; + vector<const EnumValueDescriptor*> all_values_; + const string name_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumGenerator); +}; + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_ENUM_H__ diff --git a/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc b/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc new file mode 100644 index 00000000..739282b2 --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc @@ -0,0 +1,129 @@ +// 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 <map> +#include <string> + +#include <google/protobuf/compiler/objectivec/objectivec_enum_field.h> +#include <google/protobuf/stubs/common.h> +#include <google/protobuf/compiler/objectivec/objectivec_helpers.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/wire_format.h> +#include <google/protobuf/stubs/strutil.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +namespace { +void SetEnumVariables(const FieldDescriptor* descriptor, + map<string, string>* variables) { + string type = EnumName(descriptor->enum_type()); + (*variables)["storage_type"] = type; + // TODO(thomasvl): Make inclusion of descriptor compile time and output + // both of these. Note: Extensions currently have to have the EnumDescription. + (*variables)["enum_verifier"] = type + "_IsValidValue"; + (*variables)["enum_desc_func"] = type + "_EnumDescriptor"; + + const Descriptor* msg_descriptor = descriptor->containing_type(); + (*variables)["owning_message_class"] = ClassName(msg_descriptor); +} +} // namespace + +EnumFieldGenerator::EnumFieldGenerator(const FieldDescriptor* descriptor) + : SingleFieldGenerator(descriptor) { + SetEnumVariables(descriptor, &variables_); +} + +EnumFieldGenerator::~EnumFieldGenerator() {} + +void EnumFieldGenerator::GenerateFieldDescriptionTypeSpecific( + io::Printer* printer) const { + // TODO(thomasvl): Output the CPP check to use descFunc or validator based + // on final compile. + printer->Print( + variables_, + " .typeSpecific.enumDescFunc = $enum_desc_func$,\n"); +} + +void EnumFieldGenerator::GenerateCFunctionDeclarations( + io::Printer* printer) const { + if (!HasPreservingUnknownEnumSemantics(descriptor_->file())) return; + + printer->Print( + variables_, + "int32_t $owning_message_class$_$capitalized_name$_RawValue($owning_message_class$ *message);\n" + "void Set$owning_message_class$_$capitalized_name$_RawValue($owning_message_class$ *message, int32_t value);\n" + "\n"); +} + +void EnumFieldGenerator::GenerateCFunctionImplementations( + io::Printer* printer) const { + if (!HasPreservingUnknownEnumSemantics(descriptor_->file())) return; + + printer->Print( + variables_, + "int32_t $owning_message_class$_$capitalized_name$_RawValue($owning_message_class$ *message) {\n" + " GPBDescriptor *descriptor = [$owning_message_class$ descriptor];\n" + " GPBFieldDescriptor *field = [descriptor fieldWithNumber:$field_number_name$];\n" + " return GPBGetInt32IvarWithField(message, field);\n" + "}\n" + "\n" + "void Set$owning_message_class$_$capitalized_name$_RawValue($owning_message_class$ *message, int32_t value) {\n" + " GPBDescriptor *descriptor = [$owning_message_class$ descriptor];\n" + " GPBFieldDescriptor *field = [descriptor fieldWithNumber:$field_number_name$];\n" + " GPBSetInt32IvarWithFieldInternal(message, field, value, descriptor.file.syntax);\n" + "}\n" + "\n"); +} + +RepeatedEnumFieldGenerator::RepeatedEnumFieldGenerator( + const FieldDescriptor* descriptor) + : RepeatedFieldGenerator(descriptor) { + SetEnumVariables(descriptor, &variables_); + variables_["array_storage_type"] = "GPBEnumArray"; +} + +RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {} + +void RepeatedEnumFieldGenerator::GenerateFieldDescriptionTypeSpecific( + io::Printer* printer) const { + // TODO(thomasvl): Output the CPP check to use descFunc or validator based + // on final compile. + printer->Print( + variables_, + " .typeSpecific.enumDescFunc = $enum_desc_func$,\n"); +} + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/objectivec/objectivec_enum_field.h b/src/google/protobuf/compiler/objectivec/objectivec_enum_field.h new file mode 100644 index 00000000..2d5822bb --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_enum_field.h @@ -0,0 +1,77 @@ +// 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. + +#ifndef GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_ENUM_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_ENUM_FIELD_H__ + +#include <map> +#include <string> +#include <google/protobuf/compiler/objectivec/objectivec_field.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +class EnumFieldGenerator : public SingleFieldGenerator { + friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field); + + public: + virtual void GenerateFieldDescriptionTypeSpecific(io::Printer* printer) const; + virtual void GenerateCFunctionDeclarations(io::Printer* printer) const; + virtual void GenerateCFunctionImplementations(io::Printer* printer) const; + + protected: + EnumFieldGenerator(const FieldDescriptor* descriptor); + virtual ~EnumFieldGenerator(); + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumFieldGenerator); +}; + +class RepeatedEnumFieldGenerator : public RepeatedFieldGenerator { + friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field); + + public: + virtual void GenerateFieldDescriptionTypeSpecific(io::Printer* printer) const; + + protected: + RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor); + virtual ~RepeatedEnumFieldGenerator(); + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedEnumFieldGenerator); +}; + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_ENUM_FIELD_H__ diff --git a/src/google/protobuf/compiler/objectivec/objectivec_extension.cc b/src/google/protobuf/compiler/objectivec/objectivec_extension.cc new file mode 100644 index 00000000..b19c1b45 --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_extension.cc @@ -0,0 +1,166 @@ +// 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 <iostream> + +#include <google/protobuf/compiler/objectivec/objectivec_extension.h> +#include <google/protobuf/compiler/objectivec/objectivec_helpers.h> +#include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/stubs/strutil.h> +#include <google/protobuf/io/printer.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +ExtensionGenerator::ExtensionGenerator(const string& root_class_name, + const FieldDescriptor* descriptor) + : method_name_(ExtensionMethodName(descriptor)), + root_class_and_method_name_(root_class_name + "_" + method_name_), + descriptor_(descriptor) { + // Extensions can be filtered via the method they are accessed off the + // file's Root with. + if (FilterClass(root_class_and_method_name_)) { + filter_reason_ = + string("Extension |") + root_class_and_method_name_ + "| was not whitelisted."; + } else { + // Extensions that add a Message field also require that field be allowed + // by the filter, or they aren't usable. + ObjectiveCType objc_type = GetObjectiveCType(descriptor_); + if (objc_type == OBJECTIVECTYPE_MESSAGE) { + const string message_class_name(ClassName(descriptor_->message_type())); + if (FilterClass(message_class_name)) { + filter_reason_ = string("Extension |") + root_class_and_method_name_ + + "| needs message |" + message_class_name + + "|, which was not whitelisted."; + } + } + } + if (descriptor->is_map()) { + // NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some + // error case, so it seem to be ok to use as a back door for errors. + cerr << "error: Extension is a map<>!" + << " That used to be blocked by the compiler." << endl; + cerr.flush(); + abort(); + } +} + +ExtensionGenerator::~ExtensionGenerator() {} + +void ExtensionGenerator::GenerateMembersHeader(io::Printer* printer) { + WriteClassNameToClassList(root_class_and_method_name_); + if (IsFiltered()) { + printer->Print("// $filter_reason$\n\n", "filter_reason", filter_reason_); + return; + } + map<string, string> vars; + vars["method_name"] = method_name_; + SourceLocation location; + if (descriptor_->GetSourceLocation(&location)) { + vars["comments"] = BuildCommentsString(location); + } else { + vars["comments"] = ""; + } + printer->Print(vars, + "$comments$" + "+ (GPBExtensionField*)$method_name$;\n"); +} + +void ExtensionGenerator::GenerateStaticVariablesInitialization( + io::Printer* printer, bool* out_generated, bool root) { + if (IsFiltered()) { + return; + } + *out_generated = true; + map<string, string> vars; + vars["root_class_and_method_name"] = root_class_and_method_name_; + vars["extended_type"] = ClassName(descriptor_->containing_type()); + vars["number"] = SimpleItoa(descriptor_->number()); + + std::vector<string> options; + if (descriptor_->is_repeated()) options.push_back("GPBExtensionRepeated"); + if (descriptor_->options().packed()) options.push_back("GPBExtensionPacked"); + if (descriptor_->containing_type()->options().message_set_wire_format()) + options.push_back("GPBExtensionSetWireFormat"); + + vars["options"] = BuildFlagsString(options); + + ObjectiveCType objc_type = GetObjectiveCType(descriptor_); + string singular_type; + if (objc_type == OBJECTIVECTYPE_MESSAGE) { + vars["type"] = string("GPBStringifySymbol(") + + ClassName(descriptor_->message_type()) + ")"; + } else { + vars["type"] = "NULL"; + } + + vars["default_name"] = GPBValueFieldName(descriptor_); + if (descriptor_->is_repeated()) { + vars["default"] = "nil"; + } else { + vars["default"] = DefaultValue(descriptor_); + } + string type = GetCapitalizedType(descriptor_); + vars["extension_type"] = string("GPBType") + type; + + if (objc_type == OBJECTIVECTYPE_ENUM) { + vars["enum_desc_func_name"] = + EnumName(descriptor_->enum_type()) + "_EnumDescriptor"; + } else { + vars["enum_desc_func_name"] = "NULL"; + } + + printer->Print(vars, + "{\n" + " .singletonName = GPBStringifySymbol($root_class_and_method_name$),\n" + " .type = $extension_type$,\n" + " .extendedClass = GPBStringifySymbol($extended_type$),\n" + " .fieldNumber = $number$,\n" + " .defaultValue.$default_name$ = $default$,\n" + " .messageOrGroupClassName = $type$,\n" + " .options = $options$,\n" + " .enumDescriptorFunc = $enum_desc_func_name$,\n" + "},\n"); +} + +void ExtensionGenerator::GenerateRegistrationSource(io::Printer* printer) { + if (IsFiltered()) { + return; + } + printer->Print( + "[registry addExtension:$root_class_and_method_name$];\n", + "root_class_and_method_name", root_class_and_method_name_); +} +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/objectivec/objectivec_extension.h b/src/google/protobuf/compiler/objectivec/objectivec_extension.h new file mode 100644 index 00000000..d17f5be9 --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_extension.h @@ -0,0 +1,73 @@ +// 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. + +#ifndef GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_EXTENSION_H__ +#define GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_EXTENSION_H__ + +#include <google/protobuf/stubs/common.h> + +namespace google { +namespace protobuf { +class FieldDescriptor; // descriptor.h +namespace io { +class Printer; // printer.h +} +} + +namespace protobuf { +namespace compiler { +namespace objectivec { + +class ExtensionGenerator { + public: + explicit ExtensionGenerator(const string& root_class_name, + const FieldDescriptor* descriptor); + ~ExtensionGenerator(); + + void GenerateMembersHeader(io::Printer* printer); + void GenerateStaticVariablesInitialization(io::Printer* printer, + bool* out_generated, bool root); + void GenerateRegistrationSource(io::Printer* printer); + + bool IsFiltered() const { return filter_reason_.length() > 0; } + + private: + string method_name_; + string root_class_and_method_name_; + string filter_reason_; + const FieldDescriptor* descriptor_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionGenerator); +}; +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_MESSAGE_H__ diff --git a/src/google/protobuf/compiler/objectivec/objectivec_field.cc b/src/google/protobuf/compiler/objectivec/objectivec_field.cc new file mode 100644 index 00000000..b071ce5d --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_field.cc @@ -0,0 +1,474 @@ +// 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/objectivec_field.h> +#include <google/protobuf/compiler/objectivec/objectivec_helpers.h> +#include <google/protobuf/compiler/objectivec/objectivec_enum_field.h> +#include <google/protobuf/compiler/objectivec/objectivec_map_field.h> +#include <google/protobuf/compiler/objectivec/objectivec_message_field.h> +#include <google/protobuf/compiler/objectivec/objectivec_primitive_field.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/wire_format.h> +#include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/strutil.h> + +#ifndef htonl +#include <netinet/in.h> +#endif + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +namespace { +void SetCommonFieldVariables(const FieldDescriptor* descriptor, + map<string, string>* variables) { + string camel_case_name = FieldName(descriptor); + string raw_field_name; + if (descriptor->type() == FieldDescriptor::TYPE_GROUP) { + raw_field_name = descriptor->message_type()->name(); + } else { + raw_field_name = descriptor->name(); + } + // The logic here has to match -[GGPBFieldDescriptor textFormatName]. + const string un_camel_case_name( + UnCamelCaseFieldName(camel_case_name, descriptor)); + const bool needs_custom_name = (raw_field_name != un_camel_case_name); + + SourceLocation location; + if (descriptor->GetSourceLocation(&location)) { + (*variables)["comments"] = BuildCommentsString(location); + } else { + (*variables)["comments"] = "\n"; + } + const string& classname = ClassName(descriptor->containing_type()); + (*variables)["classname"] = classname; + (*variables)["name"] = camel_case_name; + const string& capitalized_name = FieldNameCapitalized(descriptor); + (*variables)["capitalized_name"] = capitalized_name; + (*variables)["raw_field_name"] = raw_field_name; + (*variables)["field_number_name"] = + classname + "_FieldNumber_" + capitalized_name; + (*variables)["field_number"] = SimpleItoa(descriptor->number()); + (*variables)["has_index"] = SimpleItoa(descriptor->index()); + (*variables)["field_type"] = GetCapitalizedType(descriptor); + std::vector<string> field_flags; + if (descriptor->is_repeated()) field_flags.push_back("GPBFieldRepeated"); + if (descriptor->is_required()) field_flags.push_back("GPBFieldRequired"); + if (descriptor->is_optional()) field_flags.push_back("GPBFieldOptional"); + if (descriptor->options().packed()) field_flags.push_back("GPBFieldPacked"); + + // ObjC custom flags. + if (descriptor->has_default_value()) + field_flags.push_back("GPBFieldHasDefaultValue"); + if (needs_custom_name) field_flags.push_back("GPBFieldTextFormatNameCustom"); + if (descriptor->type() == FieldDescriptor::TYPE_ENUM) { + // TODO(thomasvl): Output the CPP check to use descFunc or validator based + // on final compile. + field_flags.push_back("GPBFieldHasEnumDescriptor"); + } + + (*variables)["fieldflags"] = BuildFlagsString(field_flags); + + (*variables)["default"] = DefaultValue(descriptor); + (*variables)["default_name"] = GPBValueFieldName(descriptor); + + (*variables)["typeSpecific_name"] = "className"; + (*variables)["typeSpecific_value"] = "NULL"; + + string field_options = descriptor->options().SerializeAsString(); + // Must convert to a standard byte order for packing length into + // a cstring. + uint32_t length = htonl(field_options.length()); + if (length > 0) { + string bytes((const char*)&length, sizeof(length)); + bytes.append(field_options); + string options_str = "\"" + CEscape(bytes) + "\""; + (*variables)["fieldoptions"] = "\"" + CEscape(bytes) + "\""; + } else { + (*variables)["fieldoptions"] = ""; + } + + // Clear some common things so they can be set just when needed. + (*variables)["storage_attribute"] = ""; +} + +// A field generator that writes nothing. +class EmptyFieldGenerator : public FieldGenerator { + public: + EmptyFieldGenerator(const FieldDescriptor* descriptor, const string& reason) + : FieldGenerator(descriptor), reason_(reason) {} + virtual ~EmptyFieldGenerator() {} + + virtual void GenerateFieldStorageDeclaration(io::Printer* printer) const {} + virtual void GeneratePropertyDeclaration(io::Printer* printer) const { + string name = FieldName(descriptor_); + string type; + switch (GetObjectiveCType(descriptor_)) { + case OBJECTIVECTYPE_MESSAGE: + type = ClassName(descriptor_->message_type()) + " *"; + break; + + case OBJECTIVECTYPE_ENUM: + type = EnumName(descriptor_->enum_type()) + " "; + break; + + default: + type = string(descriptor_->type_name()) + " "; + break; + } + printer->Print("// Field |$type$$name$| $reason$\n\n", "type", type, "name", + name, "reason", reason_); + } + + virtual void GenerateFieldNumberConstant(io::Printer* printer) const {} + virtual void GeneratePropertyImplementation(io::Printer* printer) const {} + virtual void GenerateFieldDescription(io::Printer* printer) const {} + + virtual bool WantsHasProperty(void) const { return false; } + + private: + string reason_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EmptyFieldGenerator); +}; + +} // namespace + +FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field) { + FieldGenerator* result = NULL; + if (field->is_repeated()) { + switch (GetObjectiveCType(field)) { + case OBJECTIVECTYPE_MESSAGE: { + string type = ClassName(field->message_type()); + if (FilterClass(type)) { + string reason = + "Filtered by |" + type + "| not being whitelisted."; + result = new EmptyFieldGenerator(field, reason); + } else if (field->is_map()) { + result = new MapFieldGenerator(field); + } else { + result = new RepeatedMessageFieldGenerator(field); + } + break; + } + case OBJECTIVECTYPE_ENUM: + result = new RepeatedEnumFieldGenerator(field); + break; + default: + result = new RepeatedPrimitiveFieldGenerator(field); + break; + } + } else { + switch (GetObjectiveCType(field)) { + case OBJECTIVECTYPE_MESSAGE: { + string type = ClassName(field->message_type()); + if (FilterClass(type)) { + string reason = + "Filtered by |" + type + "| not being whitelisted."; + result = new EmptyFieldGenerator(field, reason); + } else { + result = new MessageFieldGenerator(field); + } + break; + } + case OBJECTIVECTYPE_ENUM: + result = new EnumFieldGenerator(field); + break; + default: + if (IsReferenceType(field)) { + result = new PrimitiveObjFieldGenerator(field); + } else { + result = new PrimitiveFieldGenerator(field); + } + break; + } + } + result->FinishInitialization(); + return result; +} + + +FieldGenerator::FieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetCommonFieldVariables(descriptor, &variables_); +} + +FieldGenerator::~FieldGenerator() {} + +void FieldGenerator::GenerateFieldNumberConstant(io::Printer* printer) const { + printer->Print( + variables_, + "$field_number_name$ = $field_number$,\n"); +} + +void FieldGenerator::GenerateCFunctionDeclarations( + io::Printer* printer) const { + // Nothing +} + +void FieldGenerator::GenerateCFunctionImplementations( + io::Printer* printer) const { + // Nothing +} + +void FieldGenerator::GenerateFieldDescription( + io::Printer* printer) const { + printer->Print( + variables_, + "{\n" + " .name = \"$name$\",\n" + " .number = $field_number_name$,\n" + " .hasIndex = $has_index$,\n" + " .flags = $fieldflags$,\n" + " .type = GPBType$field_type$,\n" + " .offset = offsetof($classname$_Storage, $name$),\n" + " .defaultValue.$default_name$ = $default$,\n"); + + // " .typeSpecific.value* = [something]," + GenerateFieldDescriptionTypeSpecific(printer); + + const string& field_options(variables_.at("fieldoptions")); + if (field_options.empty()) { + printer->Print(" .fieldOptions = NULL,\n"); + } else { + // Can't use PrintRaw() here to get the #if/#else/#endif lines completely + // outdented because the need for indent captured on the previous + // printing of a \n and there is no way to get the current indent level + // to call the right number of Outdent()/Indents() to maintain state. + printer->Print( + variables_, + "#if GPBOBJC_INCLUDE_FIELD_OPTIONS\n" + " .fieldOptions = $fieldoptions$,\n" + "#else\n" + " .fieldOptions = NULL,\n" + "#endif // GPBOBJC_INCLUDE_FIELD_OPTIONS\n"); + } + + printer->Print("},\n"); +} + +void FieldGenerator::GenerateFieldDescriptionTypeSpecific( + io::Printer* printer) const { + printer->Print( + variables_, + " .typeSpecific.$typeSpecific_name$ = $typeSpecific_value$,\n"); +} + +void FieldGenerator::SetOneofIndexBase(int index_base) { + if (descriptor_->containing_oneof() != NULL) { + int index = descriptor_->containing_oneof()->index() + index_base; + // Flip the sign to mark it as a oneof. + variables_["has_index"] = SimpleItoa(-index);; + } +} + +void FieldGenerator::FinishInitialization(void) { + // Nothing +} + +SingleFieldGenerator::SingleFieldGenerator( + const FieldDescriptor* descriptor) + : FieldGenerator(descriptor) { + // Nothing +} + +SingleFieldGenerator::~SingleFieldGenerator() {} + +void SingleFieldGenerator::GenerateFieldStorageDeclaration( + io::Printer* printer) const { + printer->Print(variables_, "$storage_type$ $name$;\n"); +} + +void SingleFieldGenerator::GeneratePropertyDeclaration( + io::Printer* printer) const { + printer->Print(variables_, "$comments$"); + if (WantsHasProperty()) { + printer->Print( + variables_, + "@property(nonatomic, readwrite) BOOL has$capitalized_name$;\n"); + } + printer->Print( + variables_, + "@property(nonatomic, readwrite) $storage_type$ $name$;\n" + "\n"); +} + +void SingleFieldGenerator::GeneratePropertyImplementation( + io::Printer* printer) const { + if (WantsHasProperty()) { + printer->Print(variables_, "@dynamic has$capitalized_name$, $name$;\n"); + } else { + printer->Print(variables_, "@dynamic $name$;\n"); + } +} + +bool SingleFieldGenerator::WantsHasProperty(void) const { + if (descriptor_->containing_oneof() != NULL) { + // If in a oneof, it uses the oneofcase instead of a has bit. + return false; + } + if (HasFieldPresence(descriptor_->file())) { + // In proto1/proto2, every field has a has_$name$() method. + return true; + } + return false; +} + +ObjCObjFieldGenerator::ObjCObjFieldGenerator( + const FieldDescriptor* descriptor) + : SingleFieldGenerator(descriptor) { + variables_["property_storage_attribute"] = "strong"; + if (IsRetainedName(variables_["name"])) { + variables_["storage_attribute"] = " NS_RETURNS_NOT_RETAINED"; + } +} + +ObjCObjFieldGenerator::~ObjCObjFieldGenerator() {} + +void ObjCObjFieldGenerator::GenerateFieldStorageDeclaration( + io::Printer* printer) const { + printer->Print(variables_, "$storage_type$ *$name$;\n"); +} + +void ObjCObjFieldGenerator::GeneratePropertyDeclaration( + io::Printer* printer) const { + + // Differs from SingleFieldGenerator::GeneratePropertyDeclaration() in that + // it uses pointers and deals with Objective C's rules around storage name + // conventions (init*, new*, etc.) + + printer->Print(variables_, "$comments$"); + if (WantsHasProperty()) { + printer->Print( + variables_, + "@property(nonatomic, readwrite) BOOL has$capitalized_name$;\n"); + } + printer->Print( + variables_, + "@property(nonatomic, readwrite, $property_storage_attribute$) $storage_type$ *$name$$storage_attribute$;\n"); + if (IsInitName(variables_.at("name"))) { + // If property name starts with init we need to annotate it to get past ARC. + // http://stackoverflow.com/questions/18723226/how-do-i-annotate-an-objective-c-property-with-an-objc-method-family/18723227#18723227 + printer->Print(variables_, + "- ($storage_type$ *)$name$ GPB_METHOD_FAMILY_NONE;\n"); + } + printer->Print("\n"); +} + +RepeatedFieldGenerator::RepeatedFieldGenerator( + const FieldDescriptor* descriptor) + : ObjCObjFieldGenerator(descriptor) { + // Repeated fields don't use the has index. + variables_["has_index"] = "GPBNoHasBit"; +} + +RepeatedFieldGenerator::~RepeatedFieldGenerator() {} + +void RepeatedFieldGenerator::FinishInitialization(void) { + FieldGenerator::FinishInitialization(); + variables_["array_comment"] = + "// |" + variables_["name"] + "| contains |" + variables_["storage_type"] + "|\n"; +} + +void RepeatedFieldGenerator::GenerateFieldStorageDeclaration( + io::Printer* printer) const { + printer->Print(variables_, "$array_storage_type$ *$name$;\n"); +} + +void RepeatedFieldGenerator::GeneratePropertyImplementation( + io::Printer* printer) const { + printer->Print(variables_, "@dynamic $name$;\n"); +} + +void RepeatedFieldGenerator::GeneratePropertyDeclaration( + io::Printer* printer) const { + + // Repeated fields don't need the has* properties, but this has the same + // logic as ObjCObjFieldGenerator::GeneratePropertyDeclaration() for dealing + // with needing Objective C's rules around storage name conventions (init*, + // new*, etc.) + + printer->Print( + variables_, + "$comments$" + "$array_comment$" + "@property(nonatomic, readwrite, strong) $array_storage_type$ *$name$$storage_attribute$;\n"); + if (IsInitName(variables_.at("name"))) { + // If property name starts with init we need to annotate it to get past ARC. + // http://stackoverflow.com/questions/18723226/how-do-i-annotate-an-objective-c-property-with-an-objc-method-family/18723227#18723227 + printer->Print(variables_, + "- ($array_storage_type$ *)$name$ GPB_METHOD_FAMILY_NONE;\n"); + } + printer->Print("\n"); +} + +bool RepeatedFieldGenerator::WantsHasProperty(void) const { + // Consumer check the array size/existance rather than a has bit. + return false; +} + +FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor) + : descriptor_(descriptor), + field_generators_( + new scoped_ptr<FieldGenerator>[descriptor->field_count()]), + extension_generators_( + new scoped_ptr<FieldGenerator>[descriptor->extension_count()]) { + // Construct all the FieldGenerators. + for (int i = 0; i < descriptor->field_count(); i++) { + field_generators_[i].reset(FieldGenerator::Make(descriptor->field(i))); + } + for (int i = 0; i < descriptor->extension_count(); i++) { + extension_generators_[i].reset(FieldGenerator::Make(descriptor->extension(i))); + } +} + +FieldGeneratorMap::~FieldGeneratorMap() {} + +const FieldGenerator& FieldGeneratorMap::get( + const FieldDescriptor* field) const { + GOOGLE_CHECK_EQ(field->containing_type(), descriptor_); + return *field_generators_[field->index()]; +} + +const FieldGenerator& FieldGeneratorMap::get_extension(int index) const { + return *extension_generators_[index]; +} + +void FieldGeneratorMap::SetOneofIndexBase(int index_base) { + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_[i]->SetOneofIndexBase(index_base); + } +} + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/objectivec/objectivec_field.h b/src/google/protobuf/compiler/objectivec/objectivec_field.h new file mode 100644 index 00000000..c65e73b2 --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_field.h @@ -0,0 +1,166 @@ +// 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. + +#ifndef GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_FIELD_H__ + +#include <map> +#include <string> +#include <google/protobuf/compiler/objectivec/objectivec_helpers.h> +#include <google/protobuf/stubs/common.h> +#include <google/protobuf/descriptor.h> + +namespace google { +namespace protobuf { + +namespace io { +class Printer; // printer.h +} // namespace io + +namespace compiler { +namespace objectivec { + +class FieldGenerator { + public: + static FieldGenerator* Make(const FieldDescriptor* field); + + virtual ~FieldGenerator(); + + virtual void GenerateFieldStorageDeclaration(io::Printer* printer) const = 0; + virtual void GeneratePropertyDeclaration(io::Printer* printer) const = 0; + + virtual void GeneratePropertyImplementation(io::Printer* printer) const = 0; + + virtual void GenerateFieldDescription(io::Printer* printer) const; + virtual void GenerateFieldDescriptionTypeSpecific(io::Printer* printer) const; + virtual void GenerateFieldNumberConstant(io::Printer* printer) const; + + virtual void GenerateCFunctionDeclarations(io::Printer* printer) const; + virtual void GenerateCFunctionImplementations(io::Printer* printer) const; + + void SetOneofIndexBase(int index_base); + + string variable(const char* key) const { + return variables_.find(key)->second; + } + + bool needs_textformat_name_support() const { + const string& field_flags = variable("fieldflags"); + return field_flags.find("GPBFieldTextFormatNameCustom") != string::npos; + } + string generated_objc_name() const { return variable("name"); } + string raw_field_name() const { return variable("raw_field_name"); } + + protected: + FieldGenerator(const FieldDescriptor* descriptor); + + virtual void FinishInitialization(void); + virtual bool WantsHasProperty(void) const = 0; + + const FieldDescriptor* descriptor_; + map<string, string> variables_; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGenerator); +}; + +class SingleFieldGenerator : public FieldGenerator { + public: + virtual ~SingleFieldGenerator(); + + virtual void GenerateFieldStorageDeclaration(io::Printer* printer) const; + virtual void GeneratePropertyDeclaration(io::Printer* printer) const; + + virtual void GeneratePropertyImplementation(io::Printer* printer) const; + + protected: + SingleFieldGenerator(const FieldDescriptor* descriptor); + virtual bool WantsHasProperty(void) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SingleFieldGenerator); +}; + +// Subclass with common support for when the field ends up as an ObjC Object. +class ObjCObjFieldGenerator : public SingleFieldGenerator { + public: + virtual ~ObjCObjFieldGenerator(); + + virtual void GenerateFieldStorageDeclaration(io::Printer* printer) const; + virtual void GeneratePropertyDeclaration(io::Printer* printer) const; + + protected: + ObjCObjFieldGenerator(const FieldDescriptor* descriptor); + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ObjCObjFieldGenerator); +}; + +class RepeatedFieldGenerator : public ObjCObjFieldGenerator { + public: + virtual ~RepeatedFieldGenerator(); + + virtual void GenerateFieldStorageDeclaration(io::Printer* printer) const; + virtual void GeneratePropertyDeclaration(io::Printer* printer) const; + + virtual void GeneratePropertyImplementation(io::Printer* printer) const; + + protected: + RepeatedFieldGenerator(const FieldDescriptor* descriptor); + virtual void FinishInitialization(void); + virtual bool WantsHasProperty(void) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedFieldGenerator); +}; + +// Convenience class which constructs FieldGenerators for a Descriptor. +class FieldGeneratorMap { + public: + FieldGeneratorMap(const Descriptor* descriptor); + ~FieldGeneratorMap(); + + const FieldGenerator& get(const FieldDescriptor* field) const; + const FieldGenerator& get_extension(int index) const; + + void SetOneofIndexBase(int index_base); + + private: + const Descriptor* descriptor_; + scoped_array<scoped_ptr<FieldGenerator> > field_generators_; + scoped_array<scoped_ptr<FieldGenerator> > extension_generators_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGeneratorMap); +}; +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_FIELD_H__ diff --git a/src/google/protobuf/compiler/objectivec/objectivec_file.cc b/src/google/protobuf/compiler/objectivec/objectivec_file.cc new file mode 100644 index 00000000..f1e8c0a0 --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_file.cc @@ -0,0 +1,392 @@ +// 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/objectivec_file.h> +#include <google/protobuf/compiler/objectivec/objectivec_enum.h> +#include <google/protobuf/compiler/objectivec/objectivec_extension.h> +#include <google/protobuf/compiler/objectivec/objectivec_message.h> +#include <google/protobuf/compiler/code_generator.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/io/zero_copy_stream_impl.h> +#include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/stubs/stl_util.h> +#include <google/protobuf/stubs/strutil.h> +#include <sstream> + +// This is also found in GPBBootstrap.h, and needs to be kept in sync. It +// is the version check done to ensure generated code works with the current +// runtime being used. +const int32_t GOOGLE_PROTOBUF_OBJC_GEN_VERSION = 30000; + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { +FileGenerator::FileGenerator(const FileDescriptor *file) + : file_(file), + root_class_name_(FileClassName(file)), + is_filtered_(true), + all_extensions_filtered_(true) { + for (int i = 0; i < file_->enum_type_count(); i++) { + EnumGenerator *generator = new EnumGenerator(file_->enum_type(i)); + // The enums are exposed via C functions, so they will dead strip if + // not used. + is_filtered_ &= false; + enum_generators_.push_back(generator); + } + for (int i = 0; i < file_->message_type_count(); i++) { + MessageGenerator *generator = + new MessageGenerator(root_class_name_, file_->message_type(i)); + is_filtered_ &= generator->IsFiltered(); + is_filtered_ &= generator->IsSubContentFiltered(); + message_generators_.push_back(generator); + } + for (int i = 0; i < file_->extension_count(); i++) { + ExtensionGenerator *generator = + new ExtensionGenerator(root_class_name_, file_->extension(i)); + is_filtered_ &= generator->IsFiltered(); + all_extensions_filtered_ &= generator->IsFiltered(); + extension_generators_.push_back(generator); + } + // If there is nothing in the file we filter it. +} + +FileGenerator::~FileGenerator() { + STLDeleteContainerPointers(dependency_generators_.begin(), + dependency_generators_.end()); + STLDeleteContainerPointers(enum_generators_.begin(), enum_generators_.end()); + STLDeleteContainerPointers(message_generators_.begin(), + message_generators_.end()); + STLDeleteContainerPointers(extension_generators_.begin(), + extension_generators_.end()); +} + +void FileGenerator::GenerateHeader(io::Printer *printer) { + printer->Print( + "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "// source: $filename$\n" + "\n", + "filename", file_->name()); + + printer->Print("#import \"GPBProtocolBuffers.h\"\n\n"); + + // Add some verification that the generated code matches the source the + // code is being compiled with. + printer->Print( + "#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != $protoc_gen_objc_version$\n" + "#error This file was generated by a different version of protoc-gen-objc which is incompatible with your Protocol Buffer sources.\n" + "#endif\n" + "\n", + "protoc_gen_objc_version", + SimpleItoa(GOOGLE_PROTOBUF_OBJC_GEN_VERSION)); + + if (!IsFiltered()) { + const vector<FileGenerator *> &dependency_generators = + DependencyGenerators(); + if (dependency_generators.size() > 0) { + for (vector<FileGenerator *>::const_iterator iter = + dependency_generators.begin(); + iter != dependency_generators.end(); ++iter) { + printer->Print("#import \"$header$.pbobjc.h\"\n", + "header", (*iter)->Path()); + } + printer->Print("\n"); + } + } + + printer->Print("CF_EXTERN_C_BEGIN\n\n"); + + if (!IsFiltered()) { + set<string> dependencies; + DetermineDependencies(&dependencies); + for (set<string>::const_iterator i(dependencies.begin()); + i != dependencies.end(); ++i) { + printer->Print("$value$;\n", "value", *i); + } + + if (dependencies.begin() != dependencies.end()) { + printer->Print("\n"); + } + } + + // need to write out all enums first + for (vector<EnumGenerator *>::iterator iter = enum_generators_.begin(); + iter != enum_generators_.end(); ++iter) { + (*iter)->GenerateHeader(printer); + } + + for (vector<MessageGenerator *>::iterator iter = message_generators_.begin(); + iter != message_generators_.end(); ++iter) { + (*iter)->GenerateEnumHeader(printer); + } + + // For extensions to chain together, the Root gets created even if there + // are no extensions. So if the entire file isn't filtered away, output it. + if (!IsFiltered()) { + printer->Print( + "\n" + "#pragma mark - $root_class_name$\n" + "\n" + "@interface $root_class_name$ : GPBRootObject\n" + "@end\n\n", + "root_class_name", root_class_name_); + } + + if (extension_generators_.size() > 0) { + // The dynamic methods block is only needed if there are extensions. If + // they are all filtered, output the @interface as a comment so there is + // something left in the header for anyone that looks. + const char *root_line_prefix = ""; + if (AreAllExtensionsFiltered()) { + root_line_prefix = "// "; + } + printer->Print( + "$root_line_prefix$@interface $root_class_name$ (DynamicMethods)\n", + "root_class_name", root_class_name_, + "root_line_prefix", root_line_prefix); + + for (vector<ExtensionGenerator *>::iterator iter = + extension_generators_.begin(); + iter != extension_generators_.end(); ++iter) { + (*iter)->GenerateMembersHeader(printer); + } + + printer->Print("$root_line_prefix$@end\n\n", + "root_line_prefix", root_line_prefix); + } // extension_generators_.size() > 0 + + for (vector<MessageGenerator *>::iterator iter = message_generators_.begin(); + iter != message_generators_.end(); ++iter) { + (*iter)->GenerateMessageHeader(printer); + } + + printer->Print("CF_EXTERN_C_END\n"); +} + +void DetermineDependenciesWorker(set<string> *dependencies, + set<string> *seen_files, + const string &classname, + const FileDescriptor *file) { + if (seen_files->find(file->name()) != seen_files->end()) { + // don't infinitely recurse + return; + } + + seen_files->insert(file->name()); + + for (int i = 0; i < file->dependency_count(); i++) { + DetermineDependenciesWorker(dependencies, seen_files, classname, + file->dependency(i)); + } + for (int i = 0; i < file->message_type_count(); i++) { + MessageGenerator(classname, file->message_type(i)) + .DetermineDependencies(dependencies); + } +} + +void FileGenerator::DetermineDependencies(set<string> *dependencies) { + set<string> seen_files; + DetermineDependenciesWorker(dependencies, &seen_files, root_class_name_, + file_); +} + +void FileGenerator::GenerateSource(io::Printer *printer) { + printer->Print( + "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "// source: $filename$\n" + "\n", + "filename", file_->name()); + + if (IsFiltered()) { + printer->Print( + "// File empty because all messages, extensions and enum have been filtered.\n" + "\n" + "\n" + "// Dummy symbol that will be stripped but will avoid linker warnings about\n" + "// no symbols in the .o form compiling this file.\n" + "static int $root_class_name$_dummy __attribute__((unused,used)) = 0;\n", + "root_class_name", root_class_name_); + return; + } + + printer->Print("#import \"GPBProtocolBuffers_RuntimeSupport.h\"\n\n"); + + string header_file = Path() + ".pbobjc.h"; + + printer->Print( + "#import \"$header_file$\"\n" + "\n" + "#pragma mark - $root_class_name$\n" + "\n" + "@implementation $root_class_name$\n\n", + "header_file", header_file, + "root_class_name", root_class_name_); + + bool generated_extensions = false; + if (file_->extension_count() + file_->message_type_count() + + file_->dependency_count() > + 0) { + ostringstream extensions_stringstream; + + if (file_->extension_count() + file_->message_type_count() > 0) { + io::OstreamOutputStream extensions_outputstream(&extensions_stringstream); + io::Printer extensions_printer(&extensions_outputstream, '$'); + extensions_printer.Print( + "static GPBExtensionDescription descriptions[] = {\n"); + extensions_printer.Indent(); + for (vector<ExtensionGenerator *>::iterator iter = + extension_generators_.begin(); + iter != extension_generators_.end(); ++iter) { + (*iter)->GenerateStaticVariablesInitialization( + &extensions_printer, &generated_extensions, true); + } + for (vector<MessageGenerator *>::iterator iter = + message_generators_.begin(); + iter != message_generators_.end(); ++iter) { + (*iter)->GenerateStaticVariablesInitialization(&extensions_printer, + &generated_extensions); + } + extensions_printer.Outdent(); + extensions_printer.Print("};\n"); + if (generated_extensions) { + extensions_printer.Print( + "for (size_t i = 0; i < sizeof(descriptions) / sizeof(descriptions[0]); ++i) {\n" + " GPBExtensionField *extension = [[GPBExtensionField alloc] initWithDescription:&descriptions[i]];\n" + " [registry addExtension:extension];\n" + " [self globallyRegisterExtension:extension];\n" + " [extension release];\n" + "}\n"); + } else { + extensions_printer.Print("#pragma unused (descriptions)\n"); + } + const vector<FileGenerator *> &dependency_generators = + DependencyGenerators(); + if (dependency_generators.size()) { + for (vector<FileGenerator *>::const_iterator iter = + dependency_generators.begin(); + iter != dependency_generators.end(); ++iter) { + if (!(*iter)->IsFiltered()) { + extensions_printer.Print( + "[registry addExtensions:[$dependency$ extensionRegistry]];\n", + "dependency", (*iter)->RootClassName()); + generated_extensions = true; + } + } + } else if (!generated_extensions) { + extensions_printer.Print("#pragma unused (registry)\n"); + } + } + + if (generated_extensions) { + printer->Print( + "+ (GPBExtensionRegistry*)extensionRegistry {\n" + " // This is called by +initialize so there is no need to worry\n" + " // about thread safety and initialization of registry.\n" + " static GPBExtensionRegistry* registry = nil;\n" + " if (!registry) {\n" + " registry = [[GPBExtensionRegistry alloc] init];\n"); + + printer->Indent(); + printer->Indent(); + + extensions_stringstream.flush(); + printer->Print(extensions_stringstream.str().c_str()); + printer->Outdent(); + printer->Outdent(); + + printer->Print( + " }\n" + " return registry;\n" + "}\n" + "\n" + "+ (void)load {\n" + " @autoreleasepool {\n" + " [self extensionRegistry]; // Construct extension registry.\n" + " }\n" + "}\n\n"); + } + } + + printer->Print("@end\n\n"); + + + string syntax; + switch (file_->syntax()) { + case FileDescriptor::SYNTAX_UNKNOWN: + syntax = "GPBFileSyntaxUnknown"; + break; + case FileDescriptor::SYNTAX_PROTO2: + syntax = "GPBFileSyntaxProto2"; + break; + case FileDescriptor::SYNTAX_PROTO3: + syntax = "GPBFileSyntaxProto3"; + break; + } + printer->Print( + "static GPBFileDescriptor *$root_class_name$_FileDescriptor(void) {\n" + " // This is called by +initialize so there is no need to worry\n" + " // about thread safety of the singleton.\n" + " static GPBFileDescriptor *descriptor = NULL;\n" + " if (!descriptor) {\n" + " descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n" + " syntax:$syntax$];\n" + " }\n" + " return descriptor;\n" + "}\n" + "\n", + "root_class_name", root_class_name_, + "package", file_->package(), + "syntax", syntax); + + for (vector<EnumGenerator *>::iterator iter = enum_generators_.begin(); + iter != enum_generators_.end(); ++iter) { + (*iter)->GenerateSource(printer); + } + for (vector<MessageGenerator *>::iterator iter = message_generators_.begin(); + iter != message_generators_.end(); ++iter) { + (*iter)->GenerateSource(printer); + } +} + +const string FileGenerator::Path() const { return FilePath(file_); } + +const vector<FileGenerator *> &FileGenerator::DependencyGenerators() { + if (file_->dependency_count() != dependency_generators_.size()) { + for (int i = 0; i < file_->dependency_count(); i++) { + FileGenerator *generator = new FileGenerator(file_->dependency(i)); + dependency_generators_.push_back(generator); + } + } + return dependency_generators_; +} +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/objectivec/objectivec_file.h b/src/google/protobuf/compiler/objectivec/objectivec_file.h new file mode 100644 index 00000000..fbd08eae --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_file.h @@ -0,0 +1,94 @@ +// 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. + +#ifndef GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_FILE_H__ +#define GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_FILE_H__ + +#include <string> +#include <set> +#include <vector> +#include <google/protobuf/compiler/objectivec/objectivec_helpers.h> +#include <google/protobuf/stubs/common.h> + +namespace google { +namespace protobuf { +class FileDescriptor; // descriptor.h +namespace io { +class Printer; // printer.h +} +} + +namespace protobuf { +namespace compiler { +namespace objectivec { + +class EnumGenerator; +class ExtensionGenerator; +class MessageGenerator; + +class FileGenerator { + public: + explicit FileGenerator(const FileDescriptor* file); + ~FileGenerator(); + + void GenerateSource(io::Printer* printer); + void GenerateHeader(io::Printer* printer); + + const string& RootClassName() const { return root_class_name_; } + const string Path() const; + + bool IsFiltered() const { return is_filtered_; } + bool AreAllExtensionsFiltered() const { return all_extensions_filtered_; } + + private: + const FileDescriptor* file_; + string root_class_name_; + + // Access this field through the DependencyGenerators accessor call below. + // Do not reference it directly. + vector<FileGenerator*> dependency_generators_; + + vector<EnumGenerator*> enum_generators_; + vector<MessageGenerator*> message_generators_; + vector<ExtensionGenerator*> extension_generators_; + bool is_filtered_; + bool all_extensions_filtered_; + + void DetermineDependencies(set<string>* dependencies); + + const vector<FileGenerator*>& DependencyGenerators(); + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileGenerator); +}; +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_FILE_H__ diff --git a/src/google/protobuf/compiler/objectivec/objectivec_generator.cc b/src/google/protobuf/compiler/objectivec/objectivec_generator.cc new file mode 100644 index 00000000..1eb31a79 --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_generator.cc @@ -0,0 +1,95 @@ +// 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 <iostream> +#include <google/protobuf/compiler/objectivec/objectivec_generator.h> +#include <google/protobuf/compiler/objectivec/objectivec_file.h> +#include <google/protobuf/compiler/objectivec/objectivec_helpers.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/io/zero_copy_stream.h> +#include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/stubs/strutil.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +ObjectiveCGenerator::ObjectiveCGenerator() {} + +ObjectiveCGenerator::~ObjectiveCGenerator() {} + +bool ObjectiveCGenerator::Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const { + // ObjC doesn't have any options at the moment, error if passed one. + vector<pair<string, string> > options; + ParseGeneratorParameter(parameter, &options); + for (int i = 0; i < options.size(); i++) { + *error = "error:: Unknown generator option: " + options[i].first; + return false; + } + + if (!InitializeClassWhitelist(error)) { + return false; + } + + FileGenerator file_generator(file); + + string filepath = FilePath(file); + + // Generate header. + { + scoped_ptr<io::ZeroCopyOutputStream> output( + output_directory->Open(filepath + ".pbobjc.h")); + io::Printer printer(output.get(), '$'); + file_generator.GenerateHeader(&printer); + } + + // Generate m file. + { + scoped_ptr<io::ZeroCopyOutputStream> output( + output_directory->Open(filepath + ".pbobjc.m")); + io::Printer printer(output.get(), '$'); + file_generator.GenerateSource(&printer); + } + + if (!WriteClassList(error)) { + return false; + } + + return true; +} + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/objectivec/objectivec_generator.h b/src/google/protobuf/compiler/objectivec/objectivec_generator.h new file mode 100644 index 00000000..24286ac9 --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_generator.h @@ -0,0 +1,60 @@ +// 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. + +// Generates ObjectiveC code for a given .proto file. + +#ifndef GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_GENERATOR_H__ +#define GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_GENERATOR_H__ + +#include <string> +#include <google/protobuf/compiler/code_generator.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +class LIBPROTOC_EXPORT ObjectiveCGenerator : public CodeGenerator { + public: + ObjectiveCGenerator(); + ~ObjectiveCGenerator(); + + // implements CodeGenerator ---------------------------------------- + bool Generate(const FileDescriptor* file, const string& parameter, + OutputDirectory* output_directory, string* error) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ObjectiveCGenerator); +}; +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_GENERATOR_H__ diff --git a/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc b/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc new file mode 100644 index 00000000..a4eba09b --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc @@ -0,0 +1,1131 @@ +// 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 <arpa/inet.h> +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <unistd.h> + +#include <fstream> +#include <iostream> +#include <sstream> +#include <vector> + +#include <google/protobuf/stubs/hash.h> +#include <google/protobuf/compiler/objectivec/objectivec_helpers.h> +#include <google/protobuf/io/coded_stream.h> +#include <google/protobuf/io/zero_copy_stream_impl.h> +#include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/stubs/strutil.h> + +#ifndef htonl +#include <netinet/in.h> +#endif + +#ifndef O_EXLOCK +#include <sys/file.h> +#endif + +// NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some +// error case, so it seem to be ok to use as a back door for errors. + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +namespace { + +hash_set<string> gClassWhitelist; +stringstream gClassListStream; + +// islower()/isupper()/tolower()/toupper() change based on locale. + +bool IsLower(const char c) { + return ('a' <= c && c <= 'z'); +} + +bool IsUpper(const char c) { + return ('A' <= c && c <= 'Z'); +} + +char ToLower(char c) { + if ('A' <= c && c <= 'Z') { + c += 'a' - 'A'; + } + return c; +} + +// toupper() changes based on locale. We don't want this! +char ToUpper(char c) { + if ('a' <= c && c <= 'z') { + c += 'A' - 'a'; + } + return c; +} + +string TrimString(const string& s) { + string::size_type start = s.find_first_not_of(" \n\r\t"); + if (start == string::npos) { + return ""; + } + string::size_type end = s.find_last_not_of(" \n\r\t") + 1; + return s.substr(start, end - start); +} + +hash_set<string> MakeWordsMap(const char* const words[], size_t num_words) { + hash_set<string> result; + for (int i = 0; i < num_words; i++) { + result.insert(words[i]); + } + return result; +} + +const char* const kUpperSegmentsList[] = {"url", "http", "https"}; + +hash_set<string> kUpperSegments = + MakeWordsMap(kUpperSegmentsList, GOOGLE_ARRAYSIZE(kUpperSegmentsList)); + +// Internal helper for name handing. +// Do not expose this outside of helpers, stick to having functions for specific +// cases (ClassName(), FieldName()), so there is always consistent suffix rules. +string UnderscoresToCamelCase(const string& input, bool first_capitalized) { + vector<string> values; + string current; + + bool last_char_was_number = false; + bool last_char_was_lower = false; + bool last_char_was_upper = false; + for (int i = 0; i < input.size(); i++) { + char c = input[i]; + if (c >= '0' && c <= '9') { + if (!last_char_was_number) { + values.push_back(current); + current = ""; + } + current += c; + last_char_was_number = last_char_was_lower = last_char_was_upper = false; + last_char_was_number = true; + } else if (IsLower(c)) { + // lowercase letter can follow a lowercase or uppercase letter + if (!last_char_was_lower && !last_char_was_upper) { + values.push_back(current); + current = ""; + } + current += c; // already lower + last_char_was_number = last_char_was_lower = last_char_was_upper = false; + last_char_was_lower = true; + } else if (IsUpper(c)) { + if (!last_char_was_upper) { + values.push_back(current); + current = ""; + } + current += ToLower(c); + last_char_was_number = last_char_was_lower = last_char_was_upper = false; + last_char_was_upper = true; + } else { + last_char_was_number = last_char_was_lower = last_char_was_upper = false; + } + } + values.push_back(current); + + for (vector<string>::iterator i = values.begin(); i != values.end(); ++i) { + string value = *i; + bool all_upper = (kUpperSegments.count(value) > 0); + for (int j = 0; j < value.length(); j++) { + if (j == 0 || all_upper) { + value[j] = ToUpper(value[j]); + } else { + // Nothing, already in lower. + } + } + *i = value; + } + string result; + for (vector<string>::iterator i = values.begin(); i != values.end(); ++i) { + result += *i; + } + if ((result.length() != 0) && !first_capitalized) { + result[0] = ToLower(result[0]); + } + return result; +} + +const char* const kReservedWordList[] = { + // Objective C "keywords" that aren't in C + // From + // http://stackoverflow.com/questions/1873630/reserved-keywords-in-objective-c + "id", "_cmd", "super", "in", "out", "inout", "bycopy", "byref", "oneway", + "self", + + // C/C++ keywords (Incl C++ 0x11) + // From http://en.cppreference.com/w/cpp/keywords + "and", "and_eq", "alignas", "alignof", "asm", "auto", "bitand", "bitor", + "bool", "break", "case", "catch", "char", "char16_t", "char32_t", "class", + "compl", "const", "constexpr", "const_cast", "continue", "decltype", + "default", "delete", "double", "dynamic_cast", "else", "enum", "explicit", + "export", "extern ", "false", "float", "for", "friend", "goto", "if", + "inline", "int", "long", "mutable", "namespace", "new", "noexcept", "not", + "not_eq", "nullptr", "operator", "or", "or_eq", "private", "protected", + "public", "register", "reinterpret_cast", "return", "short", "signed", + "sizeof", "static", "static_assert", "static_cast", "struct", "switch", + "template", "this", "thread_local", "throw", "true", "try", "typedef", + "typeid", "typename", "union", "unsigned", "using", "virtual", "void", + "volatile", "wchar_t", "while", "xor", "xor_eq", + + // C99 keywords + // From + // http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fkeyw.htm + "restrict", + + // Objective-C Runtime typedefs + // From <obc/runtime.h> + "Category", "Ivar", "Method", "Protocol", + + // NSObject Methods + // new is covered by C++ keywords. + "description", "debugDescription", "finalize", "hash", "dealloc", "init", + "class", "superclass", "retain", "release", "autorelease", "retainCount", + "zone", "isProxy", "copy", "mutableCopy", "classForCoder", + + // GPBMessage Methods + // Only need to add instance methods that may conflict with + // method declared in protos. The main cases are methods + // that take no arguments, or setFoo:/hasFoo: type methods. + // These are currently in the same order as in GPBMessage.h. + "unknownFields", "extensionRegistry", "isInitialized", + "data", "delimitedData", "serializedSize", + "descriptor", "extensionsCurrentlySet", "clear", "sortedExtensionsInUse", + + // MacTypes.h names + "Fixed", "Fract", "Size", "LogicalAddress", "PhysicalAddress", "ByteCount", + "ByteOffset", "Duration", "AbsoluteTime", "OptionBits", "ItemCount", + "PBVersion", "ScriptCode", "LangCode", "RegionCode", "OSType", + "ProcessSerialNumber", "Point", "Rect", "FixedPoint", "FixedRect", "Style", + "StyleParameter", "StyleField", "TimeScale", "TimeBase", "TimeRecord", +}; + +hash_set<string> kReservedWords = + MakeWordsMap(kReservedWordList, GOOGLE_ARRAYSIZE(kReservedWordList)); + +string SanitizeNameForObjC(const string& input, const string& extension) { + if (kReservedWords.count(input) > 0) { + return input + extension; + } + return input; +} + +string NameFromFieldDescriptor(const FieldDescriptor* field) { + if (field->type() == FieldDescriptor::TYPE_GROUP) { + return field->message_type()->name(); + } else { + return field->name(); + } +} + +// Escape C++ trigraphs by escaping question marks to \? +string EscapeTrigraphs(const string& to_escape) { + return StringReplace(to_escape, "?", "\\?", true); +} + +void PathSplit(const string& path, string* directory, string* basename) { + string::size_type last_slash = path.rfind('/'); + if (last_slash == string::npos) { + if (directory) { + *directory = ""; + } + if (basename) { + *basename = path; + } + } else { + if (directory) { + *directory = path.substr(0, last_slash); + } + if (basename) { + *basename = path.substr(last_slash + 1); + } + } +} + +bool IsSpecialName(const string& name, const string* special_names, + size_t count) { + for (size_t i = 0; i < count; ++i) { + size_t length = special_names[i].length(); + if (name.compare(0, length, special_names[i]) == 0) { + if (name.length() > length) { + // If name is longer than the retained_name[i] that it matches + // the next character must be not lower case (newton vs newTon vs + // new_ton). + return !IsLower(name[length]); + } else { + return true; + } + } + } + return false; +} + +} // namespace + +string StripProto(const string& filename) { + if (HasSuffixString(filename, ".protodevel")) { + return StripSuffixString(filename, ".protodevel"); + } else { + return StripSuffixString(filename, ".proto"); + } +} + +bool IsRetainedName(const string& name) { + // List of prefixes from + // http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html + static const string retained_names[] = {"new", "alloc", "copy", + "mutableCopy"}; + return IsSpecialName(name, retained_names, + sizeof(retained_names) / sizeof(retained_names[0])); +} + +bool IsInitName(const string& name) { + static const string init_names[] = {"init"}; + return IsSpecialName(name, init_names, + sizeof(init_names) / sizeof(init_names[0])); +} + +string BaseFileName(const FileDescriptor* file) { + string basename; + PathSplit(file->name(), NULL, &basename); + return basename; +} + +string FileName(const FileDescriptor* file) { + string path = FilePath(file); + string basename; + PathSplit(path, NULL, &basename); + return basename; +} + +string FilePath(const FileDescriptor* file) { + string output; + string basename; + string directory; + PathSplit(file->name(), &directory, &basename); + if (directory.length() > 0) { + output = directory + "/"; + } + basename = StripProto(basename); + + // CamelCase to be more ObjC friendly. + basename = UnderscoresToCamelCase(basename, true); + + output += basename; + return output; +} + +string FileClassPrefix(const FileDescriptor* file) { + // Default is empty string, no need to check has_objc_class_prefix. + return file->options().objc_class_prefix(); +} + +string FileClassName(const FileDescriptor* file) { + string name = FileClassPrefix(file); + name += UnderscoresToCamelCase(StripProto(BaseFileName(file)), true); + name += "Root"; + // There aren't really any reserved words that end in "Root", but playing + // it safe and checking. + return SanitizeNameForObjC(name, "_RootClass"); +} + +string ClassNameWorker(const Descriptor* descriptor) { + string name; + if (descriptor->containing_type() != NULL) { + name = ClassNameWorker(descriptor->containing_type()); + name += "_"; + } + return name + descriptor->name(); +} + +string ClassNameWorker(const EnumDescriptor* descriptor) { + string name; + if (descriptor->containing_type() != NULL) { + name = ClassNameWorker(descriptor->containing_type()); + name += "_"; + } + return name + descriptor->name(); +} + +string ClassName(const Descriptor* descriptor) { + // 1. Message names are used as is (style calls for CamelCase, trust it). + // 2. Check for reserved word at the very end and then suffix things. + string prefix = FileClassPrefix(descriptor->file()); + string name = ClassNameWorker(descriptor); + return SanitizeNameForObjC(prefix + name, "_Class"); +} + +string EnumName(const EnumDescriptor* descriptor) { + // 1. Enum names are used as is (style calls for CamelCase, trust it). + // 2. Check for reserved word at the every end and then suffix things. + // message Fixed { + // message Size {...} + // enum Mumble {...} + // ... + // } + // yields Fixed_Class, Fixed_Size. + string name = FileClassPrefix(descriptor->file()); + name += ClassNameWorker(descriptor); + return SanitizeNameForObjC(name, "_Enum"); +} + +string EnumValueName(const EnumValueDescriptor* descriptor) { + // Because of the Switch enum compatibility, the name on the enum has to have + // the suffix handing, so it slightly diverges from how nested classes work. + // enum Fixed { + // FOO = 1 + // } + // yields Fixed_Enum and Fixed_Enum_Foo (not Fixed_Foo). + const string& class_name = EnumName(descriptor->type()); + const string& value_str = UnderscoresToCamelCase(descriptor->name(), true); + const string& name = class_name + "_" + value_str; + // There aren't really any reserved words with an underscore and a leading + // capital letter, but playing it safe and checking. + return SanitizeNameForObjC(name, "_Value"); +} + +string EnumValueShortName(const EnumValueDescriptor* descriptor) { + // Enum value names (EnumValueName above) are the enum name turned into + // a class name and then the value name is CamelCased and concatenated; the + // whole thing then gets sanitized for reserved words. + // The "short name" is intended to be the final leaf, the value name; but + // you can't simply send that off to sanitize as that could result in it + // getting modified when the full name didn't. For example enum + // "StorageModes" has a value "retain". So the full name is + // "StorageModes_Retain", but if we sanitize "retain" it would become + // "RetainValue". + // So the right way to get the short name is to take the full enum name + // and then strip off the enum name (leaving the value name and anything + // done by sanitize). + const string& class_name = EnumName(descriptor->type()); + const string& long_name_prefix = class_name + "_"; + const string& long_name = EnumValueName(descriptor); + return StripPrefixString(long_name, long_name_prefix); +} + +string UnCamelCaseEnumShortName(const string& name) { + string result; + for (int i = 0; i < name.size(); i++) { + char c = name[i]; + if (i > 0 && c >= 'A' && c <= 'Z') { + result += '_'; + } + result += ToUpper(c); + } + return result; +} + +string ExtensionMethodName(const FieldDescriptor* descriptor) { + const string& name = NameFromFieldDescriptor(descriptor); + const string& result = UnderscoresToCamelCase(name, false); + return SanitizeNameForObjC(result, "_Extension"); +} + +string FieldName(const FieldDescriptor* field) { + const string& name = NameFromFieldDescriptor(field); + string result = UnderscoresToCamelCase(name, false); + if (field->is_repeated() && !field->is_map()) { + // Add "Array" before do check for reserved worlds. + result += "Array"; + } else { + // If it wasn't repeated, but ends in "Array", force on the _p suffix. + if (HasSuffixString(result, "Array")) { + result += "_p"; + } + } + return SanitizeNameForObjC(result, "_p"); +} + +string FieldNameCapitalized(const FieldDescriptor* field) { + // Want the same suffix handling, so upcase the first letter of the other + // name. + string result = FieldName(field); + if (result.length() > 0) { + result[0] = ToUpper(result[0]); + } + return result; +} + +string OneofEnumName(const OneofDescriptor* descriptor) { + const Descriptor* fieldDescriptor = descriptor->containing_type(); + string name = ClassName(fieldDescriptor); + name += "_" + UnderscoresToCamelCase(descriptor->name(), true) + "_OneOfCase"; + // No sanitize needed because it the OS never has names that end in OneOfCase. + return name; +} + +string OneofName(const OneofDescriptor* descriptor) { + string name = UnderscoresToCamelCase(descriptor->name(), false); + // No sanitize needed because it gets OneOfCase added and that shouldn't + // ever conflict. + return name; +} + +string OneofNameCapitalized(const OneofDescriptor* descriptor) { + // Use the common handling and then up-case the first letter. + string result = OneofName(descriptor); + if (result.length() > 0) { + result[0] = ToUpper(result[0]); + } + return result; +} + +string UnCamelCaseFieldName(const string& name, const FieldDescriptor* field) { + string worker(name); + if (HasSuffixString(worker, "_p")) { + worker = StripSuffixString(worker, "_p"); + } + if (field->is_repeated() && HasSuffixString(worker, "Array")) { + worker = StripSuffixString(worker, "Array"); + } + if (field->type() == FieldDescriptor::TYPE_GROUP) { + if (worker.length() > 0) { + if (worker[0] >= 'a' && worker[0] <= 'z') { + worker[0] = ToUpper(worker[0]); + } + } + return worker; + } else { + string result; + for (int i = 0; i < worker.size(); i++) { + char c = worker[i]; + if (c >= 'A' && c <= 'Z') { + if (i > 0) { + result += '_'; + } + result += ToLower(c); + } else { + result += c; + } + } + return result; + } +} + +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 "Data"; + case FieldDescriptor::TYPE_ENUM: + return "Enum"; + case FieldDescriptor::TYPE_GROUP: + return "Group"; + case FieldDescriptor::TYPE_MESSAGE: + return "Message"; + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + +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; + } + + 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 string HandleExtremeFloatingPoint(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(".") != string::npos || val.find("e") != string::npos || + val.find("E") != string::npos)) { + val += "f"; + } + return val; + } +} + +string GPBValueFieldName(const FieldDescriptor* field) { + // Returns the field within the GPBValue 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"; + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + + +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 SimpleItoa(field->default_value_int32()); + case FieldDescriptor::CPPTYPE_UINT32: + return SimpleItoa(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 SimpleItoa(field->default_value_int64()) + "LL"; + case FieldDescriptor::CPPTYPE_UINT64: + return SimpleItoa(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 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 = htonl(default_string.length()); + string bytes((const char*)&length, sizeof(length)); + bytes.append(default_string); + return "(NSData*)\"" + CEscape(bytes) + "\""; + } else { + return "@\"" + EscapeTrigraphs(CEscape(default_string)) + "\""; + } + } + case FieldDescriptor::CPPTYPE_ENUM: + return EnumValueName(field->default_value_enum()); + case FieldDescriptor::CPPTYPE_MESSAGE: + return "nil"; + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + +string BuildFlagsString(const vector<string>& strings) { + if (strings.size() == 0) { + return "0"; + } + string string; + for (size_t i = 0; i != strings.size(); ++i) { + if (i > 0) { + string.append(" | "); + } + string.append(strings[i]); + } + return string; +} + +string BuildCommentsString(const SourceLocation& location) { + const string& comments = location.leading_comments.empty() + ? location.trailing_comments + : location.leading_comments; + vector<string> lines; + SplitStringAllowEmpty(comments, "\n", &lines); + while (!lines.empty() && lines.back().empty()) { + lines.pop_back(); + } + string prefix("//"); + string suffix("\n"); + string final_comments; + for (int i = 0; i < lines.size(); i++) { + // We use $ for delimiters, so replace comments with dollars with + // html escaped version. + // None of the other compilers handle this (as of this writing) but we + // ran into it once, so just to be safe. + final_comments += + prefix + StringReplace(lines[i], "$", "$", true) + suffix; + } + return final_comments; +} + +bool WriteClassList(string* error) { + const char* file_name = getenv("GPB_CLASSLIST_PATH"); + if (file_name != NULL) { +#ifndef O_EXLOCK + int fd = open(file_name, O_WRONLY | O_APPEND | O_CREAT, + (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); +#else + int fd = open(file_name, O_WRONLY | O_APPEND | O_EXLOCK | O_CREAT, + (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); +#endif + if (fd == -1) { + if (error != NULL) { + stringstream err_stream; + err_stream << endl << file_name << ":0:0: error:" + << "Unable to open (" << errno << ")"; + *error = err_stream.str(); + } + return false; + } +#ifndef O_EXLOCK + if (flock(fd, LOCK_EX) < 0) { + if (error != NULL) { + stringstream err_stream; + err_stream << endl << file_name << ":0:0: error:" + << "Unable to lock (" << errno << ")"; + *error = err_stream.str(); + } + return false; + } +#endif + // Need a local to hold the list so the cstring stays valid for the + // write call. + const string& class_list_str = gClassListStream.str(); + int write_out = write(fd, class_list_str.c_str(), class_list_str.length()); + int close_out = close(fd); + if (write_out == -1 || close_out == -1) { + if (error != NULL) { + stringstream err_stream; + err_stream << endl << file_name << ":0:0: error:" + << "Unable to write (" << errno << ")"; + *error = err_stream.str(); + } + return false; + } + } + return true; +} + +void WriteClassNameToClassList(const string& name) { + if (gClassListStream.good()) { + gClassListStream << name << '\n'; + } +} + +bool InitializeClassWhitelist(string* error) { + const char* env_var_value = getenv("GPB_OBJC_CLASS_WHITELIST_PATHS"); + if (env_var_value == NULL) { + return true; + } + + // The values are joined with ';' in case we ever want to make this a + // generator parameter also (instead of env var), and generator parameter + // parsing already has meaning for ',' and ':'. + vector<string> file_paths = Split(env_var_value, ";", true); + + for (vector<string>::const_iterator i = file_paths.begin(); + i != file_paths.end(); ++i) { + const string& file_path = *i; + + ifstream stream(file_path.c_str(), ifstream::in); + if (!stream.good()) { + if (error != NULL) { + stringstream err_stream; + err_stream << endl << file_path << ":0:0: error: Unable to open"; + *error = err_stream.str(); + return false; + } + } + + string input_line; + while (stream.good()) { + getline(stream, input_line); + string trimmed_line(TrimString(input_line)); + if (trimmed_line.length() == 0) { + // Skip empty lines + continue; + } + if (trimmed_line[0] == '/' || trimmed_line[0] == '#') { + // Skip comments and potential preprocessor symbols + continue; + } + gClassWhitelist.insert(trimmed_line); + } + } + return true; +} + +bool FilterClass(const string& name) { + if (gClassWhitelist.count(name) > 0) { + // Whitelisted, don't filter. + return false; + } + + // If there was no list, default to everything in. + // If there was a list, default to everything out. + return gClassWhitelist.size() > 0; +} + +void TextFormatDecodeData::AddString(int32_t key, + const string& input_for_decode, + const string& desired_output) { + for (vector<DataEntry>::const_iterator i = entries_.begin(); + i != entries_.end(); ++i) { + if (i->first == key) { + cerr << "error: duplicate key (" << key + << ") making TextFormat data, input: \"" << input_for_decode + << "\", desired: \"" << desired_output << "\"." << endl; + cerr.flush(); + abort(); + } + } + + const string& data = TextFormatDecodeData::DecodeDataForString( + input_for_decode, desired_output); + entries_.push_back(DataEntry(key, data)); +} + +string TextFormatDecodeData::Data() const { + 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 (vector<DataEntry>::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; + } + string Finish() { + Push(); + return decode_data_; + } + + private: + static const uint8_t kAddUnderscore = 0b10000000; + + static const uint8_t kOpAsIs = 0b00000000; + static const uint8_t kOpFirstUpper = 0b01000000; + static const uint8_t kOpFirstLower = 0b00100000; + static const uint8_t kOpAllUpper = 0b01100000; + + static const int kMaxSegmentLen = 0b00011111; + + void AddChar(const char desired) { + ++segment_len_; + is_all_upper_ &= 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 == ToUpper(input)) { + op_ = kOpFirstUpper; + } else if (desired == 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_; + + 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) || 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 == 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. +string DirectDecodeString(const string& str) { + string result; + result += (char)'\0'; // Marker for full string. + result += str; + result += (char)'\0'; // End of string. + return result; +} + +} // namespace + +// static +string TextFormatDecodeData::DecodeDataForString(const string& input_for_decode, + const string& desired_output) { + if ((input_for_decode.size() == 0) || (desired_output.size() == 0)) { + cerr << "error: got empty string for making TextFormat data, input: \"" + << input_for_decode << "\", desired: \"" << desired_output << "\"." + << endl; + cerr.flush(); + abort(); + } + if ((input_for_decode.find('\0') != string::npos) || + (desired_output.find('\0') != string::npos)) { + cerr << "error: got a null char in a string for making TextFormat data," + << " input: \"" << CEscape(input_for_decode) << "\", desired: \"" + << CEscape(desired_output) << "\"." << endl; + 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 objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/objectivec/objectivec_helpers.h b/src/google/protobuf/compiler/objectivec/objectivec_helpers.h new file mode 100644 index 00000000..2701a30c --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_helpers.h @@ -0,0 +1,176 @@ +// 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. + +#ifndef GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_HELPERS_H__ +#define GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_HELPERS_H__ + +#include <string> +#include <vector> + +#include <google/protobuf/descriptor.h> +#include <google/protobuf/descriptor.pb.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +// Strips ".proto" or ".protodevel" from the end of a filename. +string StripProto(const string& filename); + +// Returns true if the name requires a ns_returns_not_retained attribute applied +// to it. +bool IsRetainedName(const string& name); + +// Returns true if the name starts with "init" and will need to have special +// handling under ARC. +bool IsInitName(const string& name); + +// Gets the name of the file we're going to generate (sans the .pb.h +// extension). This does not include the path to that file. +string FileName(const FileDescriptor* file); + +// Gets the path of the file we're going to generate (sans the .pb.h +// extension). The path will be dependent on the objectivec package +// declared in the proto package. +string FilePath(const FileDescriptor* file); + +// Gets the name of the root class we'll generate in the file. This class +// is not meant for external consumption, but instead contains helpers that +// the rest of the the classes need +string FileClassName(const FileDescriptor* file); + +// These return the fully-qualified class name corresponding to the given +// descriptor. +string ClassName(const Descriptor* descriptor); +string EnumName(const EnumDescriptor* descriptor); + +// Returns the fully-qualified name of the enum value corresponding to the +// the descriptor. +string EnumValueName(const EnumValueDescriptor* descriptor); + +// Returns the name of the enum value corresponding to the descriptor. +string EnumValueShortName(const EnumValueDescriptor* descriptor); + +// Reverse what an enum does. +string UnCamelCaseEnumShortName(const string& name); + +// Returns the name to use for the extension (used as the method off the file's +// Root class). +string ExtensionMethodName(const FieldDescriptor* descriptor); + +// Returns the transformed field name. +string FieldName(const FieldDescriptor* field); +string FieldNameCapitalized(const FieldDescriptor* field); + +// Returns the transformed oneof name. +string OneofEnumName(const OneofDescriptor* descriptor); +string OneofName(const OneofDescriptor* descriptor); +string OneofNameCapitalized(const OneofDescriptor* descriptor); + +inline bool HasFieldPresence(const FileDescriptor* file) { + return file->syntax() != FileDescriptor::SYNTAX_PROTO3; +} + +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. +string UnCamelCaseFieldName(const 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 +}; + +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); +bool IsReferenceType(const FieldDescriptor* field); + +string GPBValueFieldName(const FieldDescriptor* field); +string DefaultValue(const FieldDescriptor* field); + +string BuildFlagsString(const vector<string>& strings); + +string BuildCommentsString(const SourceLocation& location); + +bool WriteClassList(string* error); +void WriteClassNameToClassList(const string& name); + +bool InitializeClassWhitelist(string* error); +bool FilterClass(const string& name); + +// Generate decode data needed for ObjC's GPBDecodeTextFormatName() to transform +// the input into the the expected output. +class TextFormatDecodeData { + public: + TextFormatDecodeData() {} + + void AddString(int32_t key, const string& input_for_decode, + const string& desired_output); + size_t num_entries() const { return entries_.size(); } + string Data() const; + + static string DecodeDataForString(const string& input_for_decode, + const string& desired_output); + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TextFormatDecodeData); + + typedef std::pair<int32_t, string> DataEntry; + vector<DataEntry> entries_; +}; + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_HELPERS_H__ diff --git a/src/google/protobuf/compiler/objectivec/objectivec_helpers_unittest.cc b/src/google/protobuf/compiler/objectivec/objectivec_helpers_unittest.cc new file mode 100644 index 00000000..c9682b08 --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_helpers_unittest.cc @@ -0,0 +1,242 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2014 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/objectivec_helpers.h> +#include <google/protobuf/testing/googletest.h> +#include <gtest/gtest.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { +namespace { + +TEST(ObjCHelper, TextFormatDecodeData_DecodeDataForString_RawStrings) { + string input_for_decode("abcdefghIJ"); + string desired_output_for_decode; + string expected; + string result; + + // Different data, can't transform. + + desired_output_for_decode = "zbcdefghIJ"; + expected = string("\0zbcdefghIJ\0", 12); + result = TextFormatDecodeData::DecodeDataForString(input_for_decode, + desired_output_for_decode); + EXPECT_EQ(expected, result); + + desired_output_for_decode = "abcdezghIJ"; + expected = string("\0abcdezghIJ\0", 12); + result = TextFormatDecodeData::DecodeDataForString(input_for_decode, + desired_output_for_decode); + EXPECT_EQ(expected, result); + + // Shortened data, can't transform. + + desired_output_for_decode = "abcdefghI"; + expected = string("\0abcdefghI\0", 11); + result = TextFormatDecodeData::DecodeDataForString(input_for_decode, + desired_output_for_decode); + EXPECT_EQ(expected, result); + + // Extra data, can't transform. + + desired_output_for_decode = "abcdefghIJz"; + expected = string("\0abcdefghIJz\0", 13); + result = TextFormatDecodeData::DecodeDataForString(input_for_decode, + desired_output_for_decode); + EXPECT_EQ(expected, result); +} + +TEST(ObjCHelper, TextFormatDecodeData_DecodeDataForString_ByteCodes) { + string input_for_decode("abcdefghIJ"); + string desired_output_for_decode; + string expected; + string result; + + desired_output_for_decode = "abcdefghIJ"; + expected = string("\x0A\x0", 2); + result = TextFormatDecodeData::DecodeDataForString(input_for_decode, + desired_output_for_decode); + EXPECT_EQ(expected, result); + + desired_output_for_decode = "_AbcdefghIJ"; + expected = string("\xCA\x0", 2); + result = TextFormatDecodeData::DecodeDataForString(input_for_decode, + desired_output_for_decode); + EXPECT_EQ(expected, result); + + desired_output_for_decode = "ABCD__EfghI_j"; + expected = string("\x64\x80\xC5\xA1\x0", 5); + result = TextFormatDecodeData::DecodeDataForString(input_for_decode, + desired_output_for_decode); + EXPECT_EQ(expected, result); + + // Long name so multiple decode ops are needed. + + input_for_decode = + "longFieldNameIsLooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1000"; + desired_output_for_decode = + "long_field_name_is_looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_1000"; + expected = string("\x04\xA5\xA4\xA2\xBF\x1F\x0E\x84\x0", 9); + result = TextFormatDecodeData::DecodeDataForString(input_for_decode, + desired_output_for_decode); + EXPECT_EQ(expected, result); +} + +TEST(ObjCHelperDeathTest, TextFormatDecodeData_DecodeDataForString_Failures) { + // Empty inputs. + + EXPECT_EXIT(TextFormatDecodeData::DecodeDataForString("", ""), + ::testing::KilledBySignal(SIGABRT), + "error: got empty string for making TextFormat data, input:"); + EXPECT_EXIT(TextFormatDecodeData::DecodeDataForString("a", ""), + ::testing::KilledBySignal(SIGABRT), + "error: got empty string for making TextFormat data, input:"); + EXPECT_EXIT(TextFormatDecodeData::DecodeDataForString("", "a"), + ::testing::KilledBySignal(SIGABRT), + "error: got empty string for making TextFormat data, input:"); + + // Null char in the string. + + string str_with_null_char("ab\0c", 4); + EXPECT_EXIT( + TextFormatDecodeData::DecodeDataForString(str_with_null_char, "def"), + ::testing::KilledBySignal(SIGABRT), + "error: got a null char in a string for making TextFormat data, input:"); + EXPECT_EXIT( + TextFormatDecodeData::DecodeDataForString("def", str_with_null_char), + ::testing::KilledBySignal(SIGABRT), + "error: got a null char in a string for making TextFormat data, input:"); +} + +TEST(ObjCHelper, TextFormatDecodeData_RawStrings) { + TextFormatDecodeData decode_data; + + // Different data, can't transform. + decode_data.AddString(1, "abcdefghIJ", "zbcdefghIJ"); + decode_data.AddString(3, "abcdefghIJ", "abcdezghIJ"); + // Shortened data, can't transform. + decode_data.AddString(2, "abcdefghIJ", "abcdefghI"); + // Extra data, can't transform. + decode_data.AddString(4, "abcdefghIJ", "abcdefghIJz"); + + EXPECT_EQ(4, decode_data.num_entries()); + + uint8_t expected_data[] = { + 0x4, + 0x1, 0x0, 'z', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'I', 'J', 0x0, + 0x3, 0x0, 'a', 'b', 'c', 'd', 'e', 'z', 'g', 'h', 'I', 'J', 0x0, + 0x2, 0x0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'I', 0x0, + 0x4, 0x0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'I', 'J', 'z', 0x0, + }; + string expected((const char*)expected_data, sizeof(expected_data)); + + EXPECT_EQ(expected, decode_data.Data()); +} + +TEST(ObjCHelper, TextFormatDecodeData_ByteCodes) { + TextFormatDecodeData decode_data; + + decode_data.AddString(1, "abcdefghIJ", "abcdefghIJ"); + decode_data.AddString(3, "abcdefghIJ", "_AbcdefghIJ"); + decode_data.AddString(2, "abcdefghIJ", "Abcd_EfghIJ"); + decode_data.AddString(4, "abcdefghIJ", "ABCD__EfghI_j"); + decode_data.AddString(1000, + "longFieldNameIsLooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1000", + "long_field_name_is_looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_1000"); + + EXPECT_EQ(5, decode_data.num_entries()); + + uint8_t expected_data[] = { + 0x5, + // All as is (00 op) + 0x1, 0x0A, 0x0, + // Underscore, upper + 9 (10 op) + 0x3, 0xCA, 0x0, + // Upper + 3 (10 op), underscore, upper + 5 (10 op) + 0x2, 0x44, 0xC6, 0x0, + // All Upper for 4 (11 op), underscore, underscore, upper + 5 (10 op), + // underscore, lower + 0 (01 op) + 0x4, 0x64, 0x80, 0xC5, 0xA1, 0x0, + // 2 byte key: as is + 3 (00 op), underscore, lower + 4 (01 op), + // underscore, lower + 3 (01 op), underscore, lower + 1 (01 op), + // underscore, lower + 30 (01 op), as is + 30 (00 op), as is + 13 (00 + // op), + // underscore, as is + 3 (00 op) + 0xE8, 0x07, 0x04, 0xA5, 0xA4, 0xA2, 0xBF, 0x1F, 0x0E, 0x84, 0x0, + }; + string expected((const char*)expected_data, sizeof(expected_data)); + + EXPECT_EQ(expected, decode_data.Data()); +} + +TEST(ObjCHelperDeathTest, TextFormatDecodeData_Failures) { + TextFormatDecodeData decode_data; + + // Empty inputs. + + EXPECT_EXIT(decode_data.AddString(1, "", ""), + ::testing::KilledBySignal(SIGABRT), + "error: got empty string for making TextFormat data, input:"); + EXPECT_EXIT(decode_data.AddString(1, "a", ""), + ::testing::KilledBySignal(SIGABRT), + "error: got empty string for making TextFormat data, input:"); + EXPECT_EXIT(decode_data.AddString(1, "", "a"), + ::testing::KilledBySignal(SIGABRT), + "error: got empty string for making TextFormat data, input:"); + + // Null char in the string. + + string str_with_null_char("ab\0c", 4); + EXPECT_EXIT( + decode_data.AddString(1, str_with_null_char, "def"), + ::testing::KilledBySignal(SIGABRT), + "error: got a null char in a string for making TextFormat data, input:"); + EXPECT_EXIT( + decode_data.AddString(1, "def", str_with_null_char), + ::testing::KilledBySignal(SIGABRT), + "error: got a null char in a string for making TextFormat data, input:"); + + // Duplicate keys + + decode_data.AddString(1, "abcdefghIJ", "abcdefghIJ"); + decode_data.AddString(3, "abcdefghIJ", "_AbcdefghIJ"); + decode_data.AddString(2, "abcdefghIJ", "Abcd_EfghIJ"); + EXPECT_EXIT(decode_data.AddString(2, "xyz", "x_yz"), + ::testing::KilledBySignal(SIGABRT), + "error: duplicate key \\(2\\) making TextFormat data, input:"); +} + +} // namespace +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/objectivec/objectivec_map_field.cc b/src/google/protobuf/compiler/objectivec/objectivec_map_field.cc new file mode 100644 index 00000000..cafdf39d --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_map_field.cc @@ -0,0 +1,161 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2015 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 <map> +#include <string> + +#include <google/protobuf/compiler/objectivec/objectivec_map_field.h> +#include <google/protobuf/stubs/common.h> +#include <google/protobuf/compiler/objectivec/objectivec_helpers.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/stubs/strutil.h> +#include <google/protobuf/stubs/substitute.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +// MapFieldGenerator uses RepeatedFieldGenerator as the parent because it +// provides a bunch of things (no has* methods, comments for contained type, +// etc.). + +namespace { + +const char* MapEntryTypeName(const FieldDescriptor* descriptor, bool isKey) { + ObjectiveCType type = GetObjectiveCType(descriptor); + switch (type) { + case OBJECTIVECTYPE_INT32: + return "Int32"; + case OBJECTIVECTYPE_UINT32: + return "UInt32"; + case OBJECTIVECTYPE_INT64: + return "Int64"; + case OBJECTIVECTYPE_UINT64: + return "UInt64"; + case OBJECTIVECTYPE_FLOAT: + return "Float"; + case OBJECTIVECTYPE_DOUBLE: + return "Double"; + case OBJECTIVECTYPE_BOOLEAN: + return "Bool"; + case OBJECTIVECTYPE_STRING: + return (isKey ? "String" : "Object"); + case OBJECTIVECTYPE_DATA: + return "Object"; + case OBJECTIVECTYPE_ENUM: + return "Enum"; + case OBJECTIVECTYPE_MESSAGE: + return "Object"; + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + +} // namespace + +MapFieldGenerator::MapFieldGenerator(const FieldDescriptor* descriptor) + : RepeatedFieldGenerator(descriptor) { + const FieldDescriptor* key_descriptor = + descriptor->message_type()->FindFieldByName("key"); + const FieldDescriptor* value_descriptor = + descriptor->message_type()->FindFieldByName("value"); + value_field_generator_.reset(FieldGenerator::Make(value_descriptor)); + + // Pull over some variables_ from the value. + variables_["field_type"] = value_field_generator_->variable("field_type"); + variables_["default"] = value_field_generator_->variable("default"); + variables_["default_name"] = value_field_generator_->variable("default_name"); + + // Build custom field flags. + std::vector<string> field_flags; + field_flags.push_back("GPBFieldMapKey" + GetCapitalizedType(key_descriptor)); + // Pull over the current text format custom name values that was calculated. + if (variables_["fieldflags"].find("GPBFieldTextFormatNameCustom") != + string::npos) { + field_flags.push_back("GPBFieldTextFormatNameCustom"); + } + // Pull over some info from the value's flags. + const string& value_field_flags = + value_field_generator_->variable("fieldflags"); + if (value_field_flags.find("GPBFieldHasDefaultValue") != string::npos) { + field_flags.push_back("GPBFieldHasDefaultValue"); + } + if (value_field_flags.find("GPBFieldHasEnumDescriptor") != string::npos) { + field_flags.push_back("GPBFieldHasEnumDescriptor"); + } + variables_["fieldflags"] = BuildFlagsString(field_flags); + + ObjectiveCType value_objc_type = GetObjectiveCType(value_descriptor); + if ((GetObjectiveCType(key_descriptor) == OBJECTIVECTYPE_STRING) && + ((value_objc_type == OBJECTIVECTYPE_STRING) || + (value_objc_type == OBJECTIVECTYPE_DATA) || + (value_objc_type == OBJECTIVECTYPE_MESSAGE))) { + variables_["array_storage_type"] = "NSMutableDictionary"; + } else { + string base_name = MapEntryTypeName(key_descriptor, true); + base_name += MapEntryTypeName(value_descriptor, false); + base_name += "Dictionary"; + variables_["array_storage_type"] = "GPB" + base_name; + } +} + +MapFieldGenerator::~MapFieldGenerator() {} + +void MapFieldGenerator::FinishInitialization(void) { + RepeatedFieldGenerator::FinishInitialization(); + // Use the array_comment suport in RepeatedFieldGenerator to output what the + // values in the map are. + const FieldDescriptor* value_descriptor = + descriptor_->message_type()->FindFieldByName("value"); + ObjectiveCType value_objc_type = GetObjectiveCType(value_descriptor); + if ((value_objc_type == OBJECTIVECTYPE_MESSAGE) || + (value_objc_type == OBJECTIVECTYPE_DATA) || + (value_objc_type == OBJECTIVECTYPE_STRING) || + (value_objc_type == OBJECTIVECTYPE_ENUM)) { + variables_["array_comment"] = + "// |" + variables_["name"] + "| values are |" + value_field_generator_->variable("storage_type") + "|\n"; + } else { + variables_["array_comment"] = ""; + } +} + +void MapFieldGenerator::GenerateFieldDescriptionTypeSpecific( + io::Printer* printer) const { + // Relay it to the value generator to provide enum validator, message + // class, etc. + value_field_generator_->GenerateFieldDescriptionTypeSpecific(printer); +} + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/objectivec/objectivec_map_field.h b/src/google/protobuf/compiler/objectivec/objectivec_map_field.h new file mode 100644 index 00000000..8862dc35 --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_map_field.h @@ -0,0 +1,64 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2015 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. + +#ifndef GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_MAP_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_MAP_FIELD_H__ + +#include <map> +#include <string> +#include <google/protobuf/compiler/objectivec/objectivec_field.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +class MapFieldGenerator : public RepeatedFieldGenerator { + friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field); + + public: + virtual void FinishInitialization(void); + virtual void GenerateFieldDescriptionTypeSpecific(io::Printer* printer) const; + + protected: + MapFieldGenerator(const FieldDescriptor* descriptor); + virtual ~MapFieldGenerator(); + + private: + scoped_ptr<FieldGenerator> value_field_generator_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MapFieldGenerator); +}; + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_MAP_FIELD_H__ diff --git a/src/google/protobuf/compiler/objectivec/objectivec_message.cc b/src/google/protobuf/compiler/objectivec/objectivec_message.cc new file mode 100644 index 00000000..87e4d0f8 --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_message.cc @@ -0,0 +1,642 @@ +// 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 <algorithm> +#include <iostream> +#include <sstream> + +#include <google/protobuf/stubs/hash.h> +#include <google/protobuf/compiler/objectivec/objectivec_message.h> +#include <google/protobuf/compiler/objectivec/objectivec_enum.h> +#include <google/protobuf/compiler/objectivec/objectivec_extension.h> +#include <google/protobuf/compiler/objectivec/objectivec_helpers.h> +#include <google/protobuf/stubs/stl_util.h> +#include <google/protobuf/stubs/strutil.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/io/coded_stream.h> +#include <google/protobuf/io/zero_copy_stream_impl.h> +#include <google/protobuf/wire_format.h> +#include <google/protobuf/wire_format_lite_inl.h> +#include <google/protobuf/descriptor.pb.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +using internal::WireFormat; +using internal::WireFormatLite; + +namespace { +struct FieldOrderingByNumber { + inline bool operator()(const FieldDescriptor* a, + const FieldDescriptor* b) const { + return a->number() < b->number(); + } +}; + +int OrderGroupForFieldDescriptor(const FieldDescriptor* descriptor) { + // The first item in the object structure is our uint32[] for has bits. + // We then want to order things to make the instances as small as + // possible. So we follow the has bits with: + // 1. Bools (1 byte) + // 2. Anything always 4 bytes - float, *32, enums + // 3. Anything that is always a pointer (they will be 8 bytes on 64 bit + // builds and 4 bytes on 32bit builds. + // 4. Anything always 8 bytes - double, *64 + // + // Why? Using 64bit builds as an example, this means worse case, we have + // enough bools that we overflow 1 byte from 4 byte alignment, so 3 bytes + // are wasted before the 4 byte values. Then if we have an odd number of + // those 4 byte values, the 8 byte values will be pushed down by 32bits to + // keep them aligned. But the structure will end 8 byte aligned, so no + // waste on the end. If you did the reverse order, you could waste 4 bytes + // before the first 8 byte value (after the has array), then a single + // bool on the end would need 7 bytes of padding to make the overall + // structure 8 byte aligned; so 11 bytes, wasted total. + + // Anything repeated is a GPB*Array/NSArray, so pointer. + if (descriptor->is_repeated()) { + return 3; + } + + switch (descriptor->type()) { + // All always 8 bytes. + case FieldDescriptor::TYPE_DOUBLE: + case FieldDescriptor::TYPE_INT64: + case FieldDescriptor::TYPE_SINT64: + case FieldDescriptor::TYPE_UINT64: + case FieldDescriptor::TYPE_SFIXED64: + case FieldDescriptor::TYPE_FIXED64: + return 4; + + // Pointers (string and bytes are NSString and NSData); 8 or 4 bytes + // depending on the build architecture. + case FieldDescriptor::TYPE_GROUP: + case FieldDescriptor::TYPE_MESSAGE: + case FieldDescriptor::TYPE_STRING: + case FieldDescriptor::TYPE_BYTES: + return 3; + + // All always 4 bytes (enums are int32s). + case FieldDescriptor::TYPE_FLOAT: + case FieldDescriptor::TYPE_INT32: + case FieldDescriptor::TYPE_SINT32: + case FieldDescriptor::TYPE_UINT32: + case FieldDescriptor::TYPE_SFIXED32: + case FieldDescriptor::TYPE_FIXED32: + case FieldDescriptor::TYPE_ENUM: + return 2; + + // 1 byte. + case FieldDescriptor::TYPE_BOOL: + return 1; + } +} + +struct FieldOrderingByStorageSize { + inline bool operator()(const FieldDescriptor* a, + const FieldDescriptor* b) const { + // Order by grouping. + const int order_group_a = OrderGroupForFieldDescriptor(a); + const int order_group_b = OrderGroupForFieldDescriptor(b); + if (order_group_a != order_group_b) { + return order_group_a < order_group_b; + } + // Within the group, order by field number (provides stable ordering). + return a->number() < b->number(); + } +}; + +struct ExtensionRangeOrdering { + bool operator()(const Descriptor::ExtensionRange* a, + const Descriptor::ExtensionRange* b) const { + return a->start < b->start; + } +}; + +// Sort the fields of the given Descriptor by number into a new[]'d array +// and return it. +const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor) { + const FieldDescriptor** fields = + new const FieldDescriptor* [descriptor->field_count()]; + for (int i = 0; i < descriptor->field_count(); i++) { + fields[i] = descriptor->field(i); + } + sort(fields, fields + descriptor->field_count(), FieldOrderingByNumber()); + return fields; +} + +// Sort the fields of the given Descriptor by storage size into a new[]'d +// array and return it. +const FieldDescriptor** SortFieldsByStorageSize(const Descriptor* descriptor) { + const FieldDescriptor** fields = + new const FieldDescriptor* [descriptor->field_count()]; + for (int i = 0; i < descriptor->field_count(); i++) { + fields[i] = descriptor->field(i); + } + sort(fields, fields + descriptor->field_count(), + FieldOrderingByStorageSize()); + return fields; +} +} // namespace + +MessageGenerator::MessageGenerator(const string& root_classname, + const Descriptor* descriptor) + : root_classname_(root_classname), + descriptor_(descriptor), + field_generators_(descriptor), + class_name_(ClassName(descriptor_)), + sub_content_filtered_(true) { + if (FilterClass(class_name_)) { + filter_reason_ = + string("Message |") + class_name_ + "| was not whitelisted."; + } + if (!IsFiltered()) { + // No need to generate extensions if this message is filtered + for (int i = 0; i < descriptor_->extension_count(); i++) { + extension_generators_.push_back( + new ExtensionGenerator(class_name_, descriptor_->extension(i))); + } + // No need to oneofs if this message is filtered + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + OneofGenerator* generator = new OneofGenerator(descriptor_->oneof_decl(i)); + oneof_generators_.push_back(generator); + } + } + + // We may have enums of this message that are used even if the message + // itself is filtered. + for (int i = 0; i < descriptor_->enum_type_count(); i++) { + EnumGenerator* generator = new EnumGenerator(descriptor_->enum_type(i)); + // The enums are exposed via C functions, so they will dead strip if + // not used. + sub_content_filtered_ &= false; + enum_generators_.push_back(generator); + } + + // We may have nested messages that are used even if the message itself + // is filtered. + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + const Descriptor* nested_descriptor = descriptor_->nested_type(i); + MessageGenerator* generator = + new MessageGenerator(root_classname_, nested_descriptor); + // Don't check map entries for being filtered, as they don't directly + // generate anything in Objective C. In theory, they only should include + // references to other toplevel types, but we still make the generators + // to be safe. + if (!IsMapEntryMessage(nested_descriptor)) { + sub_content_filtered_ &= generator->IsFiltered(); + } + sub_content_filtered_ &= generator->IsSubContentFiltered(); + nested_message_generators_.push_back(generator); + } +} + +MessageGenerator::~MessageGenerator() { + STLDeleteContainerPointers(extension_generators_.begin(), + extension_generators_.end()); + STLDeleteContainerPointers(enum_generators_.begin(), enum_generators_.end()); + STLDeleteContainerPointers(nested_message_generators_.begin(), + nested_message_generators_.end()); + STLDeleteContainerPointers(oneof_generators_.begin(), + oneof_generators_.end()); +} + +void MessageGenerator::GenerateStaticVariablesInitialization( + io::Printer* printer, bool* out_generated) { + if (!IsFiltered()) { + // Skip extensions if we are filtered. + for (vector<ExtensionGenerator*>::iterator iter = + extension_generators_.begin(); + iter != extension_generators_.end(); ++iter) { + (*iter)->GenerateStaticVariablesInitialization(printer, out_generated, + false); + } + } + + // Generating sub messages is perfectly fine though. + for (vector<MessageGenerator*>::iterator iter = + nested_message_generators_.begin(); + iter != nested_message_generators_.end(); ++iter) { + (*iter)->GenerateStaticVariablesInitialization(printer, out_generated); + } +} + +void MessageGenerator::DetermineDependencies(set<string>* dependencies) { + if (!IsFiltered() && !IsMapEntryMessage(descriptor_)) { + dependencies->insert("@class " + class_name_); + } + + for (vector<MessageGenerator*>::iterator iter = + nested_message_generators_.begin(); + iter != nested_message_generators_.end(); ++iter) { + (*iter)->DetermineDependencies(dependencies); + } +} + +void MessageGenerator::GenerateEnumHeader(io::Printer* printer) { + for (vector<EnumGenerator*>::iterator iter = enum_generators_.begin(); + iter != enum_generators_.end(); ++iter) { + (*iter)->GenerateHeader(printer); + } + + for (vector<MessageGenerator*>::iterator iter = + nested_message_generators_.begin(); + iter != nested_message_generators_.end(); ++iter) { + (*iter)->GenerateEnumHeader(printer); + } +} + +void MessageGenerator::GenerateExtensionRegistrationSource( + io::Printer* printer) { + if (!IsFiltered()) { + for (vector<ExtensionGenerator*>::iterator iter = + extension_generators_.begin(); + iter != extension_generators_.end(); ++iter) { + (*iter)->GenerateRegistrationSource(printer); + } + } + + for (vector<MessageGenerator*>::iterator iter = + nested_message_generators_.begin(); + iter != nested_message_generators_.end(); ++iter) { + (*iter)->GenerateExtensionRegistrationSource(printer); + } +} + +void MessageGenerator::GenerateMessageHeader(io::Printer* printer) { + // This a a map entry message, just recurse and do nothing directly. + if (IsMapEntryMessage(descriptor_)) { + for (vector<MessageGenerator*>::iterator iter = + nested_message_generators_.begin(); + iter != nested_message_generators_.end(); ++iter) { + (*iter)->GenerateMessageHeader(printer); + } + return; + } + + WriteClassNameToClassList(class_name_); + + if (IsFiltered()) { + printer->Print("// $filter_reason$\n\n", + "filter_reason", filter_reason_); + } else { + printer->Print( + "#pragma mark - $classname$\n" + "\n", + "classname", class_name_); + + if (descriptor_->field_count()) { + // Even if there are fields, they could be filtered away, so always use + // a buffer to confirm we have something. + ostringstream fieldnumber_stringstream; + { + scoped_array<const FieldDescriptor*> sorted_fields( + SortFieldsByNumber(descriptor_)); + + io::OstreamOutputStream fieldnumber_outputstream( + &fieldnumber_stringstream); + io::Printer fieldnumber_printer(&fieldnumber_outputstream, '$'); + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(sorted_fields[i]) + .GenerateFieldNumberConstant(&fieldnumber_printer); + } + fieldnumber_stringstream.flush(); + } + const string& fieldnumber_str = fieldnumber_stringstream.str(); + if (fieldnumber_str.length()) { + printer->Print("typedef GPB_ENUM($classname$_FieldNumber) {\n", + "classname", class_name_); + printer->Indent(); + printer->Print(fieldnumber_str.c_str()); + printer->Outdent(); + printer->Print("};\n\n"); + } + } + + for (vector<OneofGenerator*>::iterator iter = oneof_generators_.begin(); + iter != oneof_generators_.end(); ++iter) { + (*iter)->GenerateCaseEnum(printer); + } + + string message_comments; + SourceLocation location; + if (descriptor_->GetSourceLocation(&location)) { + message_comments = BuildCommentsString(location); + } else { + message_comments = ""; + } + + printer->Print( + "$comments$@interface $classname$ : GPBMessage\n\n", + "classname", class_name_, + "comments", message_comments); + + vector<bool> seen_oneofs(descriptor_->oneof_decl_count(), false); + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (field->containing_oneof() != NULL) { + const int oneof_index = field->containing_oneof()->index(); + if (!seen_oneofs[oneof_index]) { + seen_oneofs[oneof_index] = true; + oneof_generators_[oneof_index]->GeneratePublicCasePropertyDeclaration( + printer); + } + } + field_generators_.get(field) + .GeneratePropertyDeclaration(printer); + } + + printer->Print("@end\n\n"); + + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)) + .GenerateCFunctionDeclarations(printer); + } + + if (!oneof_generators_.empty()) { + for (vector<OneofGenerator*>::iterator iter = oneof_generators_.begin(); + iter != oneof_generators_.end(); ++iter) { + (*iter)->GenerateClearFunctionDeclaration(printer); + } + printer->Print("\n"); + } + + if (descriptor_->extension_count() > 0) { + printer->Print("@interface $classname$ (DynamicMethods)\n\n", + "classname", class_name_); + for (vector<ExtensionGenerator*>::iterator iter = + extension_generators_.begin(); + iter != extension_generators_.end(); ++iter) { + (*iter)->GenerateMembersHeader(printer); + } + printer->Print("@end\n\n"); + } + } + + for (vector<MessageGenerator*>::iterator iter = + nested_message_generators_.begin(); + iter != nested_message_generators_.end(); ++iter) { + (*iter)->GenerateMessageHeader(printer); + } +} + +void MessageGenerator::GenerateSource(io::Printer* printer) { + if (!IsFiltered() && !IsMapEntryMessage(descriptor_)) { + printer->Print( + "#pragma mark - $classname$\n" + "\n", + "classname", class_name_); + + printer->Print("@implementation $classname$\n\n", + "classname", class_name_); + + for (vector<OneofGenerator*>::iterator iter = oneof_generators_.begin(); + iter != oneof_generators_.end(); ++iter) { + (*iter)->GeneratePropertyImplementation(printer); + } + + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)) + .GeneratePropertyImplementation(printer); + } + + scoped_array<const FieldDescriptor*> sorted_fields( + SortFieldsByNumber(descriptor_)); + scoped_array<const FieldDescriptor*> size_order_fields( + SortFieldsByStorageSize(descriptor_)); + + vector<const Descriptor::ExtensionRange*> sorted_extensions; + for (int i = 0; i < descriptor_->extension_range_count(); ++i) { + sorted_extensions.push_back(descriptor_->extension_range(i)); + } + + sort(sorted_extensions.begin(), sorted_extensions.end(), + ExtensionRangeOrdering()); + + size_t num_has_bits = descriptor_->field_count(); + size_t sizeof_has_storage = (num_has_bits + 31) / 32; + // Tell all the fields the oneof base. + for (vector<OneofGenerator*>::iterator iter = oneof_generators_.begin(); + iter != oneof_generators_.end(); ++iter) { + (*iter)->SetOneofIndexBase(sizeof_has_storage); + } + field_generators_.SetOneofIndexBase(sizeof_has_storage); + // Add an int32 for each oneof to store which is set. + sizeof_has_storage += descriptor_->oneof_decl_count(); + + printer->Print( + "\n" + "typedef struct $classname$_Storage {\n" + " uint32_t _has_storage_[$sizeof_has_storage$];\n", + "classname", class_name_, + "sizeof_has_storage", SimpleItoa(sizeof_has_storage)); + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(size_order_fields[i]) + .GenerateFieldStorageDeclaration(printer); + } + printer->Outdent(); + + printer->Print("} $classname$_Storage;\n\n", "classname", class_name_); + + + printer->Print( + "// This method is threadsafe because it is initially called\n" + "// in +initialize for each subclass.\n" + "+ (GPBDescriptor *)descriptor {\n" + " static GPBDescriptor *descriptor = NULL;\n" + " if (!descriptor) {\n"); + + bool has_oneofs = oneof_generators_.size(); + if (has_oneofs) { + printer->Print( + " static GPBMessageOneofDescription oneofs[] = {\n"); + printer->Indent(); + printer->Indent(); + printer->Indent(); + for (vector<OneofGenerator*>::iterator iter = oneof_generators_.begin(); + iter != oneof_generators_.end(); ++iter) { + (*iter)->GenerateDescription(printer); + } + printer->Outdent(); + printer->Outdent(); + printer->Outdent(); + printer->Print( + " };\n"); + } + + printer->Print( + " static GPBMessageFieldDescription fields[] = {\n"); + printer->Indent(); + printer->Indent(); + printer->Indent(); + TextFormatDecodeData text_format_decode_data; + for (int i = 0; i < descriptor_->field_count(); ++i) { + const FieldGenerator& field_generator = + field_generators_.get(sorted_fields[i]); + field_generator.GenerateFieldDescription(printer); + if (field_generator.needs_textformat_name_support()) { + text_format_decode_data.AddString(sorted_fields[i]->number(), + field_generator.generated_objc_name(), + field_generator.raw_field_name()); + } + } + printer->Outdent(); + printer->Outdent(); + printer->Outdent(); + + bool has_enums = enum_generators_.size(); + if (has_enums) { + printer->Print( + " };\n" + " static GPBMessageEnumDescription enums[] = {\n"); + printer->Indent(); + printer->Indent(); + printer->Indent(); + for (vector<EnumGenerator*>::iterator iter = enum_generators_.begin(); + iter != enum_generators_.end(); ++iter) { + printer->Print("{ .enumDescriptorFunc = $name$_EnumDescriptor },\n", + "name", (*iter)->name()); + } + printer->Outdent(); + printer->Outdent(); + printer->Outdent(); + } + + bool has_extensions = sorted_extensions.size(); + if (has_extensions) { + printer->Print( + " };\n" + " static GPBExtensionRange ranges[] = {\n"); + printer->Indent(); + printer->Indent(); + printer->Indent(); + for (int i = 0; i < sorted_extensions.size(); i++) { + printer->Print("{ .start = $start$, .end = $end$ },\n", + "start", SimpleItoa(sorted_extensions[i]->start), + "end", SimpleItoa(sorted_extensions[i]->end)); + } + printer->Outdent(); + printer->Outdent(); + printer->Outdent(); + } + + map<string, string> vars; + vars["classname"] = class_name_; + vars["rootclassname"] = root_classname_; + vars["oneofs"] = has_oneofs ? "oneofs" : "NULL"; + vars["oneof_count"] = + has_oneofs ? "sizeof(oneofs) / sizeof(GPBMessageOneofDescription)" : "0"; + vars["enums"] = has_enums ? "enums" : "NULL"; + vars["enum_count"] = + has_enums ? "sizeof(enums) / sizeof(GPBMessageEnumDescription)" : "0"; + vars["ranges"] = has_extensions ? "ranges" : "NULL"; + vars["range_count"] = + has_extensions ? "sizeof(ranges) / sizeof(GPBExtensionRange)" : "0"; + vars["wireformat"] = + descriptor_->options().message_set_wire_format() ? "YES" : "NO"; + + printer->Print(" };\n"); + if (text_format_decode_data.num_entries() == 0) { + printer->Print( + vars, + " descriptor = [GPBDescriptor allocDescriptorForClass:[$classname$ class]\n" + " rootClass:[$rootclassname$ class]\n" + " file:$rootclassname$_FileDescriptor()\n" + " fields:fields\n" + " fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription)\n" + " oneofs:$oneofs$\n" + " oneofCount:$oneof_count$\n" + " enums:$enums$\n" + " enumCount:$enum_count$\n" + " ranges:$ranges$\n" + " rangeCount:$range_count$\n" + " storageSize:sizeof($classname$_Storage)\n" + " wireFormat:$wireformat$];\n"); + } else { + vars["extraTextFormatInfo"] = CEscape(text_format_decode_data.Data()); + printer->Print( + vars, + "#if GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS\n" + " const char *extraTextFormatInfo = NULL;\n" + "#else\n" + " static const char *extraTextFormatInfo = \"$extraTextFormatInfo$\";\n" + "#endif // GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS\n" + " descriptor = [GPBDescriptor allocDescriptorForClass:[$classname$ class]\n" + " rootClass:[$rootclassname$ class]\n" + " file:$rootclassname$_FileDescriptor()\n" + " fields:fields\n" + " fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription)\n" + " oneofs:$oneofs$\n" + " oneofCount:$oneof_count$\n" + " enums:$enums$\n" + " enumCount:$enum_count$\n" + " ranges:$ranges$\n" + " rangeCount:$range_count$\n" + " storageSize:sizeof($classname$_Storage)\n" + " wireFormat:$wireformat$\n" + " extraTextFormatInfo:extraTextFormatInfo];\n"); + } + printer->Print( + " }\n" + " return descriptor;\n" + "}\n\n" + "@end\n\n"); + + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)) + .GenerateCFunctionImplementations(printer); + } + + for (vector<OneofGenerator*>::iterator iter = oneof_generators_.begin(); + iter != oneof_generators_.end(); ++iter) { + (*iter)->GenerateClearFunctionImplementation(printer); + } + } + + for (vector<EnumGenerator*>::iterator iter = enum_generators_.begin(); + iter != enum_generators_.end(); ++iter) { + (*iter)->GenerateSource(printer); + } + + for (vector<MessageGenerator*>::iterator iter = + nested_message_generators_.begin(); + iter != nested_message_generators_.end(); ++iter) { + (*iter)->GenerateSource(printer); + } +} + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/objectivec/objectivec_message.h b/src/google/protobuf/compiler/objectivec/objectivec_message.h new file mode 100644 index 00000000..5992d0cf --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_message.h @@ -0,0 +1,103 @@ +// 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. + +#ifndef GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_MESSAGE_H__ +#define GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_MESSAGE_H__ + +#include <string> +#include <set> +#include <vector> +#include <google/protobuf/compiler/objectivec/objectivec_field.h> +#include <google/protobuf/compiler/objectivec/objectivec_helpers.h> +#include <google/protobuf/compiler/objectivec/objectivec_oneof.h> +#include <google/protobuf/stubs/common.h> + +namespace google { +namespace protobuf { + +namespace io { +class Printer; // printer.h +} // namespace io + +namespace compiler { +namespace objectivec { + +class ExtensionGenerator; +class EnumGenerator; + +class MessageGenerator { + public: + MessageGenerator(const string& root_classname, const Descriptor* descriptor); + ~MessageGenerator(); + + void GenerateStaticVariablesInitialization(io::Printer* printer, + bool* out_generated); + void GenerateEnumHeader(io::Printer* printer); + void GenerateMessageHeader(io::Printer* printer); + void GenerateSource(io::Printer* printer); + void GenerateExtensionRegistrationSource(io::Printer* printer); + void DetermineDependencies(set<string>* dependencies); + + // This only speaks for this message, not sub message/enums. + bool IsFiltered() const { return filter_reason_.length() > 0; } + // This message being filtered doesn't effect this, instead it covers if + // there are any nested messages or enums. + bool IsSubContentFiltered() const { return sub_content_filtered_; } + + private: + void GenerateParseFromMethodsHeader(io::Printer* printer); + + void GenerateSerializeOneFieldSource(io::Printer* printer, + const FieldDescriptor* field); + void GenerateSerializeOneExtensionRangeSource( + io::Printer* printer, const Descriptor::ExtensionRange* range); + + void GenerateMessageDescriptionSource(io::Printer* printer); + void GenerateDescriptionOneFieldSource(io::Printer* printer, + const FieldDescriptor* field); + + const string root_classname_; + const Descriptor* descriptor_; + FieldGeneratorMap field_generators_; + const string class_name_; + string filter_reason_; + bool sub_content_filtered_; + vector<ExtensionGenerator*> extension_generators_; + vector<EnumGenerator*> enum_generators_; + vector<MessageGenerator*> nested_message_generators_; + vector<OneofGenerator*> oneof_generators_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageGenerator); +}; +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_MESSAGE_H__ diff --git a/src/google/protobuf/compiler/objectivec/objectivec_message_field.cc b/src/google/protobuf/compiler/objectivec/objectivec_message_field.cc new file mode 100644 index 00000000..9c4a4e44 --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_message_field.cc @@ -0,0 +1,90 @@ +// 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 <map> +#include <string> + +#include <google/protobuf/compiler/objectivec/objectivec_message_field.h> +#include <google/protobuf/compiler/objectivec/objectivec_helpers.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/wire_format.h> +#include <google/protobuf/stubs/strutil.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +namespace { + +void SetMessageVariables(const FieldDescriptor* descriptor, + map<string, string>* variables) { + const string& message_type = ClassName(descriptor->message_type()); + (*variables)["type"] = message_type; + (*variables)["containing_class"] = ClassName(descriptor->containing_type()); + (*variables)["storage_type"] = message_type; + (*variables)["group_or_message"] = + (descriptor->type() == FieldDescriptor::TYPE_GROUP) ? "Group" : "Message"; + + (*variables)["typeSpecific_value"] = "GPBStringifySymbol(" + message_type + ")"; +} + +} // namespace + +MessageFieldGenerator::MessageFieldGenerator(const FieldDescriptor* descriptor) + : ObjCObjFieldGenerator(descriptor) { + SetMessageVariables(descriptor, &variables_); +} + +MessageFieldGenerator::~MessageFieldGenerator() {} + +bool MessageFieldGenerator::WantsHasProperty(void) const { + if (descriptor_->containing_oneof() != NULL) { + // If in a oneof, it uses the oneofcase instead of a has bit. + return false; + } + // In both proto2 & proto3, message fields have a has* property to tell + // when it is a non default value. + return true; +} + +RepeatedMessageFieldGenerator::RepeatedMessageFieldGenerator( + const FieldDescriptor* descriptor) + : RepeatedFieldGenerator(descriptor) { + SetMessageVariables(descriptor, &variables_); + variables_["array_storage_type"] = "NSMutableArray"; +} + +RepeatedMessageFieldGenerator::~RepeatedMessageFieldGenerator() {} + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/objectivec/objectivec_message_field.h b/src/google/protobuf/compiler/objectivec/objectivec_message_field.h new file mode 100644 index 00000000..a1ac2d39 --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_message_field.h @@ -0,0 +1,71 @@ +// 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. + +#ifndef GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_MESSAGE_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_MESSAGE_FIELD_H__ + +#include <map> +#include <string> +#include <google/protobuf/compiler/objectivec/objectivec_field.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +class MessageFieldGenerator : public ObjCObjFieldGenerator { + friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field); + + protected: + MessageFieldGenerator(const FieldDescriptor* descriptor); + virtual ~MessageFieldGenerator(); + virtual bool WantsHasProperty(void) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFieldGenerator); +}; + +class RepeatedMessageFieldGenerator : public RepeatedFieldGenerator { + friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field); + + protected: + RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor); + virtual ~RepeatedMessageFieldGenerator(); + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedMessageFieldGenerator); +}; + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_MESSAGE_FIELD_H__ diff --git a/src/google/protobuf/compiler/objectivec/objectivec_oneof.cc b/src/google/protobuf/compiler/objectivec/objectivec_oneof.cc new file mode 100644 index 00000000..77664c68 --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_oneof.cc @@ -0,0 +1,139 @@ +// 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 <map> +#include <string> + +#include <google/protobuf/compiler/objectivec/objectivec_oneof.h> +#include <google/protobuf/compiler/objectivec/objectivec_helpers.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/stubs/strutil.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +OneofGenerator::OneofGenerator(const OneofDescriptor* descriptor) + : descriptor_(descriptor) { + variables_["enum_name"] = OneofEnumName(descriptor_); + variables_["name"] = OneofName(descriptor_); + variables_["capitalized_name"] = OneofNameCapitalized(descriptor_); + variables_["raw_index"] = SimpleItoa(descriptor_->index()); + const Descriptor* msg_descriptor = descriptor_->containing_type(); + variables_["owning_message_class"] = ClassName(msg_descriptor); + + string comments; + SourceLocation location; + if (descriptor_->GetSourceLocation(&location)) { + comments = BuildCommentsString(location); + } else { + comments = ""; + } + variables_["comments"] = comments; +} + +OneofGenerator::~OneofGenerator() {} + +void OneofGenerator::SetOneofIndexBase(int index_base) { + int index = descriptor_->index() + index_base; + // Flip the sign to mark it as a oneof. + variables_["index"] = SimpleItoa(-index); +} + +void OneofGenerator::GenerateCaseEnum(io::Printer* printer) { + printer->Print( + variables_, + "typedef GPB_ENUM($enum_name$) {\n"); + printer->Indent(); + printer->Print( + variables_, + "$enum_name$_GPBUnsetOneOfCase = 0,\n"); + string enum_name = variables_["enum_name"]; + for (int j = 0; j < descriptor_->field_count(); j++) { + const FieldDescriptor* field = descriptor_->field(j); + string field_name = FieldNameCapitalized(field); + printer->Print( + "$enum_name$_$field_name$ = $field_number$,\n", + "enum_name", enum_name, + "field_name", field_name, + "field_number", SimpleItoa(field->number())); + } + printer->Outdent(); + printer->Print( + "};\n" + "\n"); +} + +void OneofGenerator::GeneratePublicCasePropertyDeclaration( + io::Printer* printer) { + printer->Print( + variables_, + "$comments$" + "@property(nonatomic, readonly) $enum_name$ $name$OneOfCase;\n" + "\n"); +} + +void OneofGenerator::GenerateClearFunctionDeclaration(io::Printer* printer) { + printer->Print( + variables_, + "void $owning_message_class$_Clear$capitalized_name$OneOfCase($owning_message_class$ *message);\n"); +} + +void OneofGenerator::GeneratePropertyImplementation(io::Printer* printer) { + printer->Print( + variables_, + "@dynamic $name$OneOfCase;\n"); +} + +void OneofGenerator::GenerateClearFunctionImplementation(io::Printer* printer) { + printer->Print( + variables_, + "void $owning_message_class$_Clear$capitalized_name$OneOfCase($owning_message_class$ *message) {\n" + " GPBDescriptor *descriptor = [message descriptor];\n" + " GPBOneofDescriptor *oneof = descriptor->oneofs_[$raw_index$];\n" + " GPBMaybeClearOneof(message, oneof, 0);\n" + "}\n"); +} + +void OneofGenerator::GenerateDescription(io::Printer* printer) { + printer->Print( + variables_, + "{\n" + " .name = \"$name$\",\n" + " .index = $index$,\n" + "},\n"); +} + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/objectivec/objectivec_oneof.h b/src/google/protobuf/compiler/objectivec/objectivec_oneof.h new file mode 100644 index 00000000..77b7f800 --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_oneof.h @@ -0,0 +1,77 @@ +// 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. + +#ifndef GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_ONEOF_H__ +#define GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_ONEOF_H__ + +#include <string> +#include <set> +#include <vector> +#include <google/protobuf/descriptor.h> + +namespace google { +namespace protobuf { +namespace io { +class Printer; // printer.h +} +} + +namespace protobuf { +namespace compiler { +namespace objectivec { + +class OneofGenerator { + public: + OneofGenerator(const OneofDescriptor* descriptor); + ~OneofGenerator(); + + void SetOneofIndexBase(int index_base); + + void GenerateCaseEnum(io::Printer* printer); + + void GeneratePublicCasePropertyDeclaration(io::Printer* printer); + void GenerateClearFunctionDeclaration(io::Printer* printer); + + void GeneratePropertyImplementation(io::Printer* printer); + void GenerateClearFunctionImplementation(io::Printer* printer); + void GenerateDescription(io::Printer* printer); + + private: + const OneofDescriptor* descriptor_; + map<string, string> variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(OneofGenerator); +}; + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_ONEOF_H__ diff --git a/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.cc b/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.cc new file mode 100644 index 00000000..8272c67b --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.cc @@ -0,0 +1,162 @@ +// 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 <map> +#include <string> + +#include <google/protobuf/compiler/objectivec/objectivec_primitive_field.h> +#include <google/protobuf/stubs/common.h> +#include <google/protobuf/compiler/objectivec/objectivec_helpers.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/wire_format.h> +#include <google/protobuf/wire_format_lite_inl.h> +#include <google/protobuf/stubs/strutil.h> +#include <google/protobuf/stubs/substitute.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +using internal::WireFormat; +using internal::WireFormatLite; + +namespace { + +const char* PrimitiveTypeName(const FieldDescriptor* descriptor) { + ObjectiveCType type = GetObjectiveCType(descriptor); + switch (type) { + case OBJECTIVECTYPE_INT32: + return "int32_t"; + case OBJECTIVECTYPE_UINT32: + return "uint32_t"; + case OBJECTIVECTYPE_INT64: + return "int64_t"; + case OBJECTIVECTYPE_UINT64: + return "uint64_t"; + case OBJECTIVECTYPE_FLOAT: + return "float"; + case OBJECTIVECTYPE_DOUBLE: + return "double"; + case OBJECTIVECTYPE_BOOLEAN: + return "BOOL"; + case OBJECTIVECTYPE_STRING: + return "NSString"; + case OBJECTIVECTYPE_DATA: + return "NSData"; + case OBJECTIVECTYPE_ENUM: + return "int32_t"; + case OBJECTIVECTYPE_MESSAGE: + return NULL; + } +} + +const char* PrimitiveArrayTypeName(const FieldDescriptor* descriptor) { + ObjectiveCType type = GetObjectiveCType(descriptor); + switch (type) { + case OBJECTIVECTYPE_INT32: + return "Int32"; + case OBJECTIVECTYPE_UINT32: + return "UInt32"; + case OBJECTIVECTYPE_INT64: + return "Int64"; + case OBJECTIVECTYPE_UINT64: + return "UInt64"; + case OBJECTIVECTYPE_FLOAT: + return "Float"; + case OBJECTIVECTYPE_DOUBLE: + return "Double"; + case OBJECTIVECTYPE_BOOLEAN: + return "Bool"; + case OBJECTIVECTYPE_STRING: + return ""; // Want NSArray + case OBJECTIVECTYPE_DATA: + return ""; // Want NSArray + case OBJECTIVECTYPE_ENUM: + return "Enum"; + case OBJECTIVECTYPE_MESSAGE: + return ""; // Want NSArray + } +} + +void SetPrimitiveVariables(const FieldDescriptor* descriptor, + map<string, string>* variables) { + std::string primitive_name = PrimitiveTypeName(descriptor); + (*variables)["type"] = primitive_name; + (*variables)["storage_type"] = primitive_name; +} + +} // namespace + +PrimitiveFieldGenerator::PrimitiveFieldGenerator( + const FieldDescriptor* descriptor) + : SingleFieldGenerator(descriptor) { + SetPrimitiveVariables(descriptor, &variables_); +} + +PrimitiveFieldGenerator::~PrimitiveFieldGenerator() {} + +PrimitiveObjFieldGenerator::PrimitiveObjFieldGenerator( + const FieldDescriptor* descriptor) + : ObjCObjFieldGenerator(descriptor) { + SetPrimitiveVariables(descriptor, &variables_); + variables_["property_storage_attribute"] = "copy"; +} + +PrimitiveObjFieldGenerator::~PrimitiveObjFieldGenerator() {} + +RepeatedPrimitiveFieldGenerator::RepeatedPrimitiveFieldGenerator( + const FieldDescriptor* descriptor) + : RepeatedFieldGenerator(descriptor) { + SetPrimitiveVariables(descriptor, &variables_); + + string base_name = PrimitiveArrayTypeName(descriptor); + if (base_name.length()) { + variables_["array_storage_type"] = "GPB" + base_name + "Array"; + } else { + variables_["array_storage_type"] = "NSMutableArray"; + } +} + +RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {} + +void RepeatedPrimitiveFieldGenerator::FinishInitialization(void) { + RepeatedFieldGenerator::FinishInitialization(); + if (IsPrimitiveType(descriptor_)) { + // No comment needed for primitive types. + variables_["array_comment"] = ""; + } +} + + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.h b/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.h new file mode 100644 index 00000000..b3599297 --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.h @@ -0,0 +1,82 @@ +// 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. + +#ifndef GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_PRIMITIVE_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_PRIMITIVE_FIELD_H__ + +#include <map> +#include <string> +#include <google/protobuf/compiler/objectivec/objectivec_field.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +class PrimitiveFieldGenerator : public SingleFieldGenerator { + friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field); + + protected: + PrimitiveFieldGenerator(const FieldDescriptor* descriptor); + virtual ~PrimitiveFieldGenerator(); + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveFieldGenerator); +}; + +class PrimitiveObjFieldGenerator : public ObjCObjFieldGenerator { + friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field); + + protected: + PrimitiveObjFieldGenerator(const FieldDescriptor* descriptor); + virtual ~PrimitiveObjFieldGenerator(); + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveObjFieldGenerator); +}; + +class RepeatedPrimitiveFieldGenerator : public RepeatedFieldGenerator { + friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field); + + protected: + RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor); + virtual ~RepeatedPrimitiveFieldGenerator(); + virtual void FinishInitialization(void); + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedPrimitiveFieldGenerator); +}; + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_PRIMITIVE_FIELD_H__ diff --git a/src/google/protobuf/descriptor.pb.cc b/src/google/protobuf/descriptor.pb.cc index 9556206c..c201136e 100644 --- a/src/google/protobuf/descriptor.pb.cc +++ b/src/google/protobuf/descriptor.pb.cc @@ -717,8 +717,8 @@ void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() { "\n\010Location\022\020\n\004path\030\001 \003(\005B\002\020\001\022\020\n\004span\030\002 \003" "(\005B\002\020\001\022\030\n\020leading_comments\030\003 \001(\t\022\031\n\021trai" "ling_comments\030\004 \001(\t\022!\n\031leading_detached_" - "comments\030\006 \003(\tB)\n\023com.google.protobufB\020D" - "escriptorProtosH\001", 4617); + "comments\030\006 \003(\tB/\n\023com.google.protobufB\020D" + "escriptorProtosH\001\242\002\003GPB", 4623); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "google/protobuf/descriptor.proto", &protobuf_RegisterTypes); FileDescriptorSet::default_instance_ = new FileDescriptorSet(); diff --git a/src/google/protobuf/descriptor.proto b/src/google/protobuf/descriptor.proto index 367b16e5..de242690 100644 --- a/src/google/protobuf/descriptor.proto +++ b/src/google/protobuf/descriptor.proto @@ -42,6 +42,7 @@ syntax = "proto2"; package google.protobuf; option java_package = "com.google.protobuf"; option java_outer_classname = "DescriptorProtos"; +option objc_class_prefix = "GPB"; // descriptor.proto must be optimized for speed because reflection-based // algorithms don't work during bootstrapping. diff --git a/src/google/protobuf/duration.proto b/src/google/protobuf/duration.proto index c6dd4adc..12337f53 100644 --- a/src/google/protobuf/duration.proto +++ b/src/google/protobuf/duration.proto @@ -35,6 +35,7 @@ option java_generate_equals_and_hash = true; option java_multiple_files = true; option java_outer_classname = "DurationProto"; option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; // A Duration represents a signed, fixed-length span of time represented diff --git a/src/google/protobuf/empty.proto b/src/google/protobuf/empty.proto index 46087d59..94df0397 100644 --- a/src/google/protobuf/empty.proto +++ b/src/google/protobuf/empty.proto @@ -34,6 +34,7 @@ package google.protobuf; option java_multiple_files = true; option java_outer_classname = "EmptyProto"; option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; // A generic empty message that you can re-use to avoid defining duplicated diff --git a/src/google/protobuf/field_mask.proto b/src/google/protobuf/field_mask.proto index 492d4b06..081c004a 100644 --- a/src/google/protobuf/field_mask.proto +++ b/src/google/protobuf/field_mask.proto @@ -34,6 +34,7 @@ package google.protobuf; option java_multiple_files = true; option java_outer_classname = "FieldMaskProto"; option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; // `FieldMask` represents a set of symbolic field paths, for example: diff --git a/src/google/protobuf/source_context.proto b/src/google/protobuf/source_context.proto index a3874d6a..2c8a17a8 100644 --- a/src/google/protobuf/source_context.proto +++ b/src/google/protobuf/source_context.proto @@ -34,6 +34,7 @@ package google.protobuf; option java_multiple_files = true; option java_outer_classname = "SourceContextProto"; option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; // `SourceContext` represents information about the source of a diff --git a/src/google/protobuf/struct.proto b/src/google/protobuf/struct.proto index 3a90ff37..04d59f77 100644 --- a/src/google/protobuf/struct.proto +++ b/src/google/protobuf/struct.proto @@ -35,6 +35,7 @@ option java_generate_equals_and_hash = true; option java_multiple_files = true; option java_outer_classname = "StructProto"; option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; // `Struct` represents a structured data value, consisting of fields diff --git a/src/google/protobuf/timestamp.proto b/src/google/protobuf/timestamp.proto index 89956229..72e8914c 100644 --- a/src/google/protobuf/timestamp.proto +++ b/src/google/protobuf/timestamp.proto @@ -35,6 +35,7 @@ option java_generate_equals_and_hash = true; option java_multiple_files = true; option java_outer_classname = "TimestampProto"; option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; // A Timestamp represents a point in time independent of any time zone diff --git a/src/google/protobuf/type.proto b/src/google/protobuf/type.proto index 04165c81..ace5d995 100644 --- a/src/google/protobuf/type.proto +++ b/src/google/protobuf/type.proto @@ -37,6 +37,7 @@ import "google/protobuf/source_context.proto"; option java_multiple_files = true; option java_outer_classname = "TypeProto"; option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; // A light-weight descriptor for a proto message type. diff --git a/src/google/protobuf/unittest_drop_unknown_fields.proto b/src/google/protobuf/unittest_drop_unknown_fields.proto index 66b31acf..2e513eb3 100644 --- a/src/google/protobuf/unittest_drop_unknown_fields.proto +++ b/src/google/protobuf/unittest_drop_unknown_fields.proto @@ -31,6 +31,7 @@ syntax = "proto3"; package unittest_drop_unknown_fields; +option objc_class_prefix = "DropUnknowns"; message Foo { enum NestedEnum { diff --git a/src/google/protobuf/unittest_preserve_unknown_enum.proto b/src/google/protobuf/unittest_preserve_unknown_enum.proto index 67e57499..e7929699 100644 --- a/src/google/protobuf/unittest_preserve_unknown_enum.proto +++ b/src/google/protobuf/unittest_preserve_unknown_enum.proto @@ -31,6 +31,7 @@ syntax = "proto3"; package proto3_preserve_unknown_enum_unittest; +option objc_class_prefix = "UnknownEnums"; enum MyEnum { FOO = 0; diff --git a/src/google/protobuf/wrappers.proto b/src/google/protobuf/wrappers.proto index 5164c24c..4c902d44 100644 --- a/src/google/protobuf/wrappers.proto +++ b/src/google/protobuf/wrappers.proto @@ -40,6 +40,7 @@ package google.protobuf; option java_multiple_files = true; option java_outer_classname = "WrappersProto"; option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; // Wrapper message for double. |