| 1 | // Protocol Buffers - Google's data interchange format |
| 2 | // Copyright 2008 Google Inc. All rights reserved. |
| 3 | // https://developers.google.com/protocol-buffers/ |
| 4 | // |
| 5 | // Redistribution and use in source and binary forms, with or without |
| 6 | // modification, are permitted provided that the following conditions are |
| 7 | // met: |
| 8 | // |
| 9 | // * Redistributions of source code must retain the above copyright |
| 10 | // notice, this list of conditions and the following disclaimer. |
| 11 | // * Redistributions in binary form must reproduce the above |
| 12 | // copyright notice, this list of conditions and the following disclaimer |
| 13 | // in the documentation and/or other materials provided with the |
| 14 | // distribution. |
| 15 | // * Neither the name of Google Inc. nor the names of its |
| 16 | // contributors may be used to endorse or promote products derived from |
| 17 | // this software without specific prior written permission. |
| 18 | // |
| 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 | |
| 31 | #include <iostream> |
| 32 | |
| 33 | #include <google/protobuf/compiler/objectivec/objectivec_field.h> |
| 34 | #include <google/protobuf/compiler/objectivec/objectivec_helpers.h> |
| 35 | #include <google/protobuf/compiler/objectivec/objectivec_enum_field.h> |
| 36 | #include <google/protobuf/compiler/objectivec/objectivec_map_field.h> |
| 37 | #include <google/protobuf/compiler/objectivec/objectivec_message_field.h> |
| 38 | #include <google/protobuf/compiler/objectivec/objectivec_primitive_field.h> |
| 39 | #include <google/protobuf/io/printer.h> |
| 40 | #include <google/protobuf/stubs/strutil.h> |
| 41 | |
| 42 | namespace google { |
| 43 | namespace protobuf { |
| 44 | namespace compiler { |
| 45 | namespace objectivec { |
| 46 | |
| 47 | namespace { |
| 48 | |
| 49 | void SetCommonFieldVariables(const FieldDescriptor* descriptor, |
| 50 | std::map<std::string, std::string>* variables) { |
| 51 | std::string camel_case_name = FieldName(field: descriptor); |
| 52 | std::string raw_field_name; |
| 53 | if (descriptor->type() == FieldDescriptor::TYPE_GROUP) { |
| 54 | raw_field_name = descriptor->message_type()->name(); |
| 55 | } else { |
| 56 | raw_field_name = descriptor->name(); |
| 57 | } |
| 58 | // The logic here has to match -[GGPBFieldDescriptor textFormatName]. |
| 59 | const std::string un_camel_case_name( |
| 60 | UnCamelCaseFieldName(name: camel_case_name, field: descriptor)); |
| 61 | const bool needs_custom_name = (raw_field_name != un_camel_case_name); |
| 62 | |
| 63 | SourceLocation location; |
| 64 | if (descriptor->GetSourceLocation(out_location: &location)) { |
| 65 | (*variables)["comments" ] = BuildCommentsString(location, prefer_single_line: true); |
| 66 | } else { |
| 67 | (*variables)["comments" ] = "\n" ; |
| 68 | } |
| 69 | const std::string& classname = ClassName(descriptor: descriptor->containing_type()); |
| 70 | (*variables)["classname" ] = classname; |
| 71 | (*variables)["name" ] = camel_case_name; |
| 72 | const std::string& capitalized_name = FieldNameCapitalized(field: descriptor); |
| 73 | (*variables)["capitalized_name" ] = capitalized_name; |
| 74 | (*variables)["raw_field_name" ] = raw_field_name; |
| 75 | (*variables)["field_number_name" ] = |
| 76 | classname + "_FieldNumber_" + capitalized_name; |
| 77 | (*variables)["field_number" ] = StrCat(a: descriptor->number()); |
| 78 | (*variables)["field_type" ] = GetCapitalizedType(field: descriptor); |
| 79 | (*variables)["deprecated_attribute" ] = GetOptionalDeprecatedAttribute(descriptor); |
| 80 | std::vector<std::string> field_flags; |
| 81 | if (descriptor->is_repeated()) field_flags.push_back(x: "GPBFieldRepeated" ); |
| 82 | if (descriptor->is_required()) field_flags.push_back(x: "GPBFieldRequired" ); |
| 83 | if (descriptor->is_optional()) field_flags.push_back(x: "GPBFieldOptional" ); |
| 84 | if (descriptor->is_packed()) field_flags.push_back(x: "GPBFieldPacked" ); |
| 85 | |
| 86 | // ObjC custom flags. |
| 87 | if (descriptor->has_default_value()) |
| 88 | field_flags.push_back(x: "GPBFieldHasDefaultValue" ); |
| 89 | if (needs_custom_name) field_flags.push_back(x: "GPBFieldTextFormatNameCustom" ); |
| 90 | if (descriptor->type() == FieldDescriptor::TYPE_ENUM) { |
| 91 | field_flags.push_back(x: "GPBFieldHasEnumDescriptor" ); |
| 92 | } |
| 93 | // It will clear on a zero value if... |
| 94 | // - not repeated/map |
| 95 | // - doesn't have presence |
| 96 | bool clear_on_zero = |
| 97 | (!descriptor->is_repeated() && !descriptor->has_presence()); |
| 98 | if (clear_on_zero) { |
| 99 | field_flags.push_back(x: "GPBFieldClearHasIvarOnZero" ); |
| 100 | } |
| 101 | |
| 102 | (*variables)["fieldflags" ] = BuildFlagsString(type: FLAGTYPE_FIELD, strings: field_flags); |
| 103 | |
| 104 | (*variables)["default" ] = DefaultValue(field: descriptor); |
| 105 | (*variables)["default_name" ] = GPBGenericValueFieldName(field: descriptor); |
| 106 | |
| 107 | (*variables)["dataTypeSpecific_name" ] = "clazz" ; |
| 108 | (*variables)["dataTypeSpecific_value" ] = "Nil" ; |
| 109 | |
| 110 | (*variables)["storage_offset_value" ] = |
| 111 | "(uint32_t)offsetof(" + classname + "__storage_, " + camel_case_name + ")" ; |
| 112 | (*variables)["storage_offset_comment" ] = "" ; |
| 113 | |
| 114 | // Clear some common things so they can be set just when needed. |
| 115 | (*variables)["storage_attribute" ] = "" ; |
| 116 | } |
| 117 | |
| 118 | } // namespace |
| 119 | |
| 120 | FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field) { |
| 121 | FieldGenerator* result = NULL; |
| 122 | if (field->is_repeated()) { |
| 123 | switch (GetObjectiveCType(field)) { |
| 124 | case OBJECTIVECTYPE_MESSAGE: { |
| 125 | if (field->is_map()) { |
| 126 | result = new MapFieldGenerator(field); |
| 127 | } else { |
| 128 | result = new RepeatedMessageFieldGenerator(field); |
| 129 | } |
| 130 | break; |
| 131 | } |
| 132 | case OBJECTIVECTYPE_ENUM: |
| 133 | result = new RepeatedEnumFieldGenerator(field); |
| 134 | break; |
| 135 | default: |
| 136 | result = new RepeatedPrimitiveFieldGenerator(field); |
| 137 | break; |
| 138 | } |
| 139 | } else { |
| 140 | switch (GetObjectiveCType(field)) { |
| 141 | case OBJECTIVECTYPE_MESSAGE: { |
| 142 | result = new MessageFieldGenerator(field); |
| 143 | break; |
| 144 | } |
| 145 | case OBJECTIVECTYPE_ENUM: |
| 146 | result = new EnumFieldGenerator(field); |
| 147 | break; |
| 148 | default: |
| 149 | if (IsReferenceType(field)) { |
| 150 | result = new PrimitiveObjFieldGenerator(field); |
| 151 | } else { |
| 152 | result = new PrimitiveFieldGenerator(field); |
| 153 | } |
| 154 | break; |
| 155 | } |
| 156 | } |
| 157 | result->FinishInitialization(); |
| 158 | return result; |
| 159 | } |
| 160 | |
| 161 | FieldGenerator::FieldGenerator(const FieldDescriptor* descriptor) |
| 162 | : descriptor_(descriptor) { |
| 163 | SetCommonFieldVariables(descriptor, variables: &variables_); |
| 164 | } |
| 165 | |
| 166 | FieldGenerator::~FieldGenerator() {} |
| 167 | |
| 168 | void FieldGenerator::GenerateFieldNumberConstant(io::Printer* printer) const { |
| 169 | printer->Print( |
| 170 | variables: variables_, |
| 171 | text: "$field_number_name$ = $field_number$,\n" ); |
| 172 | } |
| 173 | |
| 174 | void FieldGenerator::GenerateCFunctionDeclarations( |
| 175 | io::Printer* printer) const { |
| 176 | // Nothing |
| 177 | } |
| 178 | |
| 179 | void FieldGenerator::GenerateCFunctionImplementations( |
| 180 | io::Printer* printer) const { |
| 181 | // Nothing |
| 182 | } |
| 183 | |
| 184 | void FieldGenerator::DetermineForwardDeclarations( |
| 185 | std::set<std::string>* fwd_decls, |
| 186 | bool include_external_types) const { |
| 187 | // Nothing |
| 188 | } |
| 189 | |
| 190 | void FieldGenerator::DetermineObjectiveCClassDefinitions( |
| 191 | std::set<std::string>* fwd_decls) const { |
| 192 | // Nothing |
| 193 | } |
| 194 | |
| 195 | void FieldGenerator::GenerateFieldDescription( |
| 196 | io::Printer* printer, bool include_default) const { |
| 197 | // Printed in the same order as the structure decl. |
| 198 | if (include_default) { |
| 199 | printer->Print( |
| 200 | variables: variables_, |
| 201 | text: "{\n" |
| 202 | " .defaultValue.$default_name$ = $default$,\n" |
| 203 | " .core.name = \"$name$\",\n" |
| 204 | " .core.dataTypeSpecific.$dataTypeSpecific_name$ = $dataTypeSpecific_value$,\n" |
| 205 | " .core.number = $field_number_name$,\n" |
| 206 | " .core.hasIndex = $has_index$,\n" |
| 207 | " .core.offset = $storage_offset_value$,$storage_offset_comment$\n" |
| 208 | " .core.flags = $fieldflags$,\n" |
| 209 | " .core.dataType = GPBDataType$field_type$,\n" |
| 210 | "},\n" ); |
| 211 | } else { |
| 212 | printer->Print( |
| 213 | variables: variables_, |
| 214 | text: "{\n" |
| 215 | " .name = \"$name$\",\n" |
| 216 | " .dataTypeSpecific.$dataTypeSpecific_name$ = $dataTypeSpecific_value$,\n" |
| 217 | " .number = $field_number_name$,\n" |
| 218 | " .hasIndex = $has_index$,\n" |
| 219 | " .offset = $storage_offset_value$,$storage_offset_comment$\n" |
| 220 | " .flags = $fieldflags$,\n" |
| 221 | " .dataType = GPBDataType$field_type$,\n" |
| 222 | "},\n" ); |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | void FieldGenerator::SetRuntimeHasBit(int has_index) { |
| 227 | variables_["has_index" ] = StrCat(a: has_index); |
| 228 | } |
| 229 | |
| 230 | void FieldGenerator::SetNoHasBit(void) { |
| 231 | variables_["has_index" ] = "GPBNoHasBit" ; |
| 232 | } |
| 233 | |
| 234 | int FieldGenerator::(void) const { |
| 235 | return 0; |
| 236 | } |
| 237 | |
| 238 | void FieldGenerator::(int index_base) { |
| 239 | // NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some |
| 240 | // error cases, so it seems to be ok to use as a back door for errors. |
| 241 | std::cerr << "Error: should have overridden SetExtraRuntimeHasBitsBase()." << std::endl; |
| 242 | std::cerr.flush(); |
| 243 | abort(); |
| 244 | } |
| 245 | |
| 246 | void FieldGenerator::SetOneofIndexBase(int index_base) { |
| 247 | const OneofDescriptor* oneof = descriptor_->real_containing_oneof(); |
| 248 | if (oneof != NULL) { |
| 249 | int index = oneof->index() + index_base; |
| 250 | // Flip the sign to mark it as a oneof. |
| 251 | variables_["has_index" ] = StrCat(a: -index); |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | bool FieldGenerator::WantsHasProperty(void) const { |
| 256 | return descriptor_->has_presence() && !descriptor_->real_containing_oneof(); |
| 257 | } |
| 258 | |
| 259 | void FieldGenerator::FinishInitialization(void) { |
| 260 | // If "property_type" wasn't set, make it "storage_type". |
| 261 | if ((variables_.find(x: "property_type" ) == variables_.end()) && |
| 262 | (variables_.find(x: "storage_type" ) != variables_.end())) { |
| 263 | variables_["property_type" ] = variable(key: "storage_type" ); |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | SingleFieldGenerator::SingleFieldGenerator(const FieldDescriptor* descriptor) |
| 268 | : FieldGenerator(descriptor) { |
| 269 | // Nothing |
| 270 | } |
| 271 | |
| 272 | SingleFieldGenerator::~SingleFieldGenerator() {} |
| 273 | |
| 274 | void SingleFieldGenerator::GenerateFieldStorageDeclaration( |
| 275 | io::Printer* printer) const { |
| 276 | printer->Print(variables: variables_, text: "$storage_type$ $name$;\n" ); |
| 277 | } |
| 278 | |
| 279 | void SingleFieldGenerator::GeneratePropertyDeclaration( |
| 280 | io::Printer* printer) const { |
| 281 | printer->Print(variables: variables_, text: "$comments$" ); |
| 282 | printer->Print( |
| 283 | variables: variables_, |
| 284 | text: "@property(nonatomic, readwrite) $property_type$ $name$$deprecated_attribute$;\n" |
| 285 | "\n" ); |
| 286 | if (WantsHasProperty()) { |
| 287 | printer->Print( |
| 288 | variables: variables_, |
| 289 | text: "@property(nonatomic, readwrite) BOOL has$capitalized_name$$deprecated_attribute$;\n" ); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | void SingleFieldGenerator::GeneratePropertyImplementation( |
| 294 | io::Printer* printer) const { |
| 295 | if (WantsHasProperty()) { |
| 296 | printer->Print(variables: variables_, text: "@dynamic has$capitalized_name$, $name$;\n" ); |
| 297 | } else { |
| 298 | printer->Print(variables: variables_, text: "@dynamic $name$;\n" ); |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | bool SingleFieldGenerator::RuntimeUsesHasBit(void) const { |
| 303 | if (descriptor_->real_containing_oneof()) { |
| 304 | // The oneof tracks what is set instead. |
| 305 | return false; |
| 306 | } |
| 307 | return true; |
| 308 | } |
| 309 | |
| 310 | ObjCObjFieldGenerator::ObjCObjFieldGenerator(const FieldDescriptor* descriptor) |
| 311 | : SingleFieldGenerator(descriptor) { |
| 312 | variables_["property_storage_attribute" ] = "strong" ; |
| 313 | if (IsRetainedName(name: variables_["name" ])) { |
| 314 | variables_["storage_attribute" ] = " NS_RETURNS_NOT_RETAINED" ; |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | ObjCObjFieldGenerator::~ObjCObjFieldGenerator() {} |
| 319 | |
| 320 | void ObjCObjFieldGenerator::GenerateFieldStorageDeclaration( |
| 321 | io::Printer* printer) const { |
| 322 | printer->Print(variables: variables_, text: "$storage_type$ *$name$;\n" ); |
| 323 | } |
| 324 | |
| 325 | void ObjCObjFieldGenerator::GeneratePropertyDeclaration( |
| 326 | io::Printer* printer) const { |
| 327 | |
| 328 | // Differs from SingleFieldGenerator::GeneratePropertyDeclaration() in that |
| 329 | // it uses pointers and deals with Objective C's rules around storage name |
| 330 | // conventions (init*, new*, etc.) |
| 331 | |
| 332 | printer->Print(variables: variables_, text: "$comments$" ); |
| 333 | printer->Print( |
| 334 | variables: variables_, |
| 335 | text: "@property(nonatomic, readwrite, $property_storage_attribute$, null_resettable) $property_type$ *$name$$storage_attribute$$deprecated_attribute$;\n" ); |
| 336 | if (WantsHasProperty()) { |
| 337 | printer->Print( |
| 338 | variables: variables_, |
| 339 | text: "/** Test to see if @c $name$ has been set. */\n" |
| 340 | "@property(nonatomic, readwrite) BOOL has$capitalized_name$$deprecated_attribute$;\n" ); |
| 341 | } |
| 342 | if (IsInitName(name: variables_.find(x: "name" )->second)) { |
| 343 | // If property name starts with init we need to annotate it to get past ARC. |
| 344 | // http://stackoverflow.com/questions/18723226/how-do-i-annotate-an-objective-c-property-with-an-objc-method-family/18723227#18723227 |
| 345 | printer->Print(variables: variables_, |
| 346 | text: "- ($property_type$ *)$name$ GPB_METHOD_FAMILY_NONE$deprecated_attribute$;\n" ); |
| 347 | } |
| 348 | printer->Print(text: "\n" ); |
| 349 | } |
| 350 | |
| 351 | RepeatedFieldGenerator::RepeatedFieldGenerator( |
| 352 | const FieldDescriptor* descriptor) |
| 353 | : ObjCObjFieldGenerator(descriptor) { |
| 354 | // Default to no comment and let the cases needing it fill it in. |
| 355 | variables_["array_comment" ] = "" ; |
| 356 | } |
| 357 | |
| 358 | RepeatedFieldGenerator::~RepeatedFieldGenerator() {} |
| 359 | |
| 360 | void RepeatedFieldGenerator::FinishInitialization(void) { |
| 361 | FieldGenerator::FinishInitialization(); |
| 362 | if (variables_.find(x: "array_property_type" ) == variables_.end()) { |
| 363 | variables_["array_property_type" ] = variable(key: "array_storage_type" ); |
| 364 | } |
| 365 | } |
| 366 | |
| 367 | void RepeatedFieldGenerator::GenerateFieldStorageDeclaration( |
| 368 | io::Printer* printer) const { |
| 369 | printer->Print(variables: variables_, text: "$array_storage_type$ *$name$;\n" ); |
| 370 | } |
| 371 | |
| 372 | void RepeatedFieldGenerator::GeneratePropertyImplementation( |
| 373 | io::Printer* printer) const { |
| 374 | printer->Print(variables: variables_, text: "@dynamic $name$, $name$_Count;\n" ); |
| 375 | } |
| 376 | |
| 377 | void RepeatedFieldGenerator::GeneratePropertyDeclaration( |
| 378 | io::Printer* printer) const { |
| 379 | |
| 380 | // Repeated fields don't need the has* properties, but they do expose a |
| 381 | // *Count (to check without autocreation). So for the field property we need |
| 382 | // the same logic as ObjCObjFieldGenerator::GeneratePropertyDeclaration() for |
| 383 | // dealing with needing Objective C's rules around storage name conventions |
| 384 | // (init*, new*, etc.) |
| 385 | |
| 386 | printer->Print( |
| 387 | variables: variables_, |
| 388 | text: "$comments$" |
| 389 | "$array_comment$" |
| 390 | "@property(nonatomic, readwrite, strong, null_resettable) $array_property_type$ *$name$$storage_attribute$$deprecated_attribute$;\n" |
| 391 | "/** The number of items in @c $name$ without causing the array to be created. */\n" |
| 392 | "@property(nonatomic, readonly) NSUInteger $name$_Count$deprecated_attribute$;\n" ); |
| 393 | if (IsInitName(name: variables_.find(x: "name" )->second)) { |
| 394 | // If property name starts with init we need to annotate it to get past ARC. |
| 395 | // http://stackoverflow.com/questions/18723226/how-do-i-annotate-an-objective-c-property-with-an-objc-method-family/18723227#18723227 |
| 396 | printer->Print(variables: variables_, |
| 397 | text: "- ($array_property_type$ *)$name$ GPB_METHOD_FAMILY_NONE$deprecated_attribute$;\n" ); |
| 398 | } |
| 399 | printer->Print(text: "\n" ); |
| 400 | } |
| 401 | |
| 402 | bool RepeatedFieldGenerator::RuntimeUsesHasBit(void) const { |
| 403 | return false; // The array (or map/dict) having anything is what is used. |
| 404 | } |
| 405 | |
| 406 | FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor) |
| 407 | : descriptor_(descriptor), |
| 408 | field_generators_(descriptor->field_count()), |
| 409 | extension_generators_(descriptor->extension_count()) { |
| 410 | // Construct all the FieldGenerators. |
| 411 | for (int i = 0; i < descriptor->field_count(); i++) { |
| 412 | field_generators_[i].reset( |
| 413 | p: FieldGenerator::Make(field: descriptor->field(index: i))); |
| 414 | } |
| 415 | for (int i = 0; i < descriptor->extension_count(); i++) { |
| 416 | extension_generators_[i].reset( |
| 417 | p: FieldGenerator::Make(field: descriptor->extension(index: i))); |
| 418 | } |
| 419 | } |
| 420 | |
| 421 | FieldGeneratorMap::~FieldGeneratorMap() {} |
| 422 | |
| 423 | const FieldGenerator& FieldGeneratorMap::get( |
| 424 | const FieldDescriptor* field) const { |
| 425 | GOOGLE_CHECK_EQ(field->containing_type(), descriptor_); |
| 426 | return *field_generators_[field->index()]; |
| 427 | } |
| 428 | |
| 429 | const FieldGenerator& FieldGeneratorMap::get_extension(int index) const { |
| 430 | return *extension_generators_[index]; |
| 431 | } |
| 432 | |
| 433 | int FieldGeneratorMap::CalculateHasBits(void) { |
| 434 | int total_bits = 0; |
| 435 | for (int i = 0; i < descriptor_->field_count(); i++) { |
| 436 | if (field_generators_[i]->RuntimeUsesHasBit()) { |
| 437 | field_generators_[i]->SetRuntimeHasBit(total_bits); |
| 438 | ++total_bits; |
| 439 | } else { |
| 440 | field_generators_[i]->SetNoHasBit(); |
| 441 | } |
| 442 | int = field_generators_[i]->ExtraRuntimeHasBitsNeeded(); |
| 443 | if (extra_bits) { |
| 444 | field_generators_[i]->SetExtraRuntimeHasBitsBase(total_bits); |
| 445 | total_bits += extra_bits; |
| 446 | } |
| 447 | } |
| 448 | return total_bits; |
| 449 | } |
| 450 | |
| 451 | void FieldGeneratorMap::SetOneofIndexBase(int index_base) { |
| 452 | for (int i = 0; i < descriptor_->field_count(); i++) { |
| 453 | field_generators_[i]->SetOneofIndexBase(index_base); |
| 454 | } |
| 455 | } |
| 456 | |
| 457 | bool FieldGeneratorMap::DoesAnyFieldHaveNonZeroDefault(void) const { |
| 458 | for (int i = 0; i < descriptor_->field_count(); i++) { |
| 459 | if (HasNonZeroDefaultValue(field: descriptor_->field(index: i))) { |
| 460 | return true; |
| 461 | } |
| 462 | } |
| 463 | |
| 464 | return false; |
| 465 | } |
| 466 | |
| 467 | } // namespace objectivec |
| 468 | } // namespace compiler |
| 469 | } // namespace protobuf |
| 470 | } // namespace google |
| 471 | |