blob: eb23fee19f8009ce919f874a2b703e75f3591e42 [file] [log] [blame]
// 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_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/strutil.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace objectivec {
namespace {
void SetCommonFieldVariables(const FieldDescriptor* descriptor,
std::map<std::string, std::string>* variables) {
std::string camel_case_name = FieldName(descriptor);
std::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 std::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, true);
} else {
(*variables)["comments"] = "\n";
}
const std::string& classname = ClassName(descriptor->containing_type());
(*variables)["classname"] = classname;
(*variables)["name"] = camel_case_name;
const std::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"] = StrCat(descriptor->number());
(*variables)["field_type"] = GetCapitalizedType(descriptor);
(*variables)["deprecated_attribute"] = GetOptionalDeprecatedAttribute(descriptor);
std::vector<std::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->is_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) {
field_flags.push_back("GPBFieldHasEnumDescriptor");
}
// It will clear on a zero value if...
// - not repeated/map
// - doesn't have presence
bool clear_on_zero =
(!descriptor->is_repeated() && !descriptor->has_presence());
if (clear_on_zero) {
field_flags.push_back("GPBFieldClearHasIvarOnZero");
}
(*variables)["fieldflags"] = BuildFlagsString(FLAGTYPE_FIELD, field_flags);
(*variables)["default"] = DefaultValue(descriptor);
(*variables)["default_name"] = GPBGenericValueFieldName(descriptor);
(*variables)["dataTypeSpecific_name"] = "clazz";
(*variables)["dataTypeSpecific_value"] = "Nil";
(*variables)["storage_offset_value"] =
"(uint32_t)offsetof(" + classname + "__storage_, " + camel_case_name + ")";
(*variables)["storage_offset_comment"] = "";
// Clear some common things so they can be set just when needed.
(*variables)["storage_attribute"] = "";
}
} // namespace
FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field,
const Options& options) {
FieldGenerator* result = NULL;
if (field->is_repeated()) {
switch (GetObjectiveCType(field)) {
case OBJECTIVECTYPE_MESSAGE: {
if (field->is_map()) {
result = new MapFieldGenerator(field, options);
} else {
result = new RepeatedMessageFieldGenerator(field, options);
}
break;
}
case OBJECTIVECTYPE_ENUM:
result = new RepeatedEnumFieldGenerator(field, options);
break;
default:
result = new RepeatedPrimitiveFieldGenerator(field, options);
break;
}
} else {
switch (GetObjectiveCType(field)) {
case OBJECTIVECTYPE_MESSAGE: {
result = new MessageFieldGenerator(field, options);
break;
}
case OBJECTIVECTYPE_ENUM:
result = new EnumFieldGenerator(field, options);
break;
default:
if (IsReferenceType(field)) {
result = new PrimitiveObjFieldGenerator(field, options);
} else {
result = new PrimitiveFieldGenerator(field, options);
}
break;
}
}
result->FinishInitialization();
return result;
}
FieldGenerator::FieldGenerator(const FieldDescriptor* descriptor,
const Options& options)
: 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::DetermineForwardDeclarations(
std::set<std::string>* fwd_decls) const {
// Nothing
}
void FieldGenerator::DetermineObjectiveCClassDefinitions(
std::set<std::string>* fwd_decls) const {
// Nothing
}
void FieldGenerator::GenerateFieldDescription(
io::Printer* printer, bool include_default) const {
// Printed in the same order as the structure decl.
if (include_default) {
printer->Print(
variables_,
"{\n"
" .defaultValue.$default_name$ = $default$,\n"
" .core.name = \"$name$\",\n"
" .core.dataTypeSpecific.$dataTypeSpecific_name$ = $dataTypeSpecific_value$,\n"
" .core.number = $field_number_name$,\n"
" .core.hasIndex = $has_index$,\n"
" .core.offset = $storage_offset_value$,$storage_offset_comment$\n"
" .core.flags = $fieldflags$,\n"
" .core.dataType = GPBDataType$field_type$,\n"
"},\n");
} else {
printer->Print(
variables_,
"{\n"
" .name = \"$name$\",\n"
" .dataTypeSpecific.$dataTypeSpecific_name$ = $dataTypeSpecific_value$,\n"
" .number = $field_number_name$,\n"
" .hasIndex = $has_index$,\n"
" .offset = $storage_offset_value$,$storage_offset_comment$\n"
" .flags = $fieldflags$,\n"
" .dataType = GPBDataType$field_type$,\n"
"},\n");
}
}
void FieldGenerator::SetRuntimeHasBit(int has_index) {
variables_["has_index"] = StrCat(has_index);
}
void FieldGenerator::SetNoHasBit(void) {
variables_["has_index"] = "GPBNoHasBit";
}
int FieldGenerator::ExtraRuntimeHasBitsNeeded(void) const {
return 0;
}
void FieldGenerator::SetExtraRuntimeHasBitsBase(int index_base) {
// NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
// error cases, so it seems to be ok to use as a back door for errors.
std::cerr << "Error: should have overridden SetExtraRuntimeHasBitsBase()." << std::endl;
std::cerr.flush();
abort();
}
void FieldGenerator::SetOneofIndexBase(int index_base) {
const OneofDescriptor* oneof = descriptor_->real_containing_oneof();
if (oneof != NULL) {
int index = oneof->index() + index_base;
// Flip the sign to mark it as a oneof.
variables_["has_index"] = StrCat(-index);
}
}
bool FieldGenerator::WantsHasProperty(void) const {
return descriptor_->has_presence() && !descriptor_->real_containing_oneof();
}
void FieldGenerator::FinishInitialization(void) {
// If "property_type" wasn't set, make it "storage_type".
if ((variables_.find("property_type") == variables_.end()) &&
(variables_.find("storage_type") != variables_.end())) {
variables_["property_type"] = variable("storage_type");
}
}
SingleFieldGenerator::SingleFieldGenerator(const FieldDescriptor* descriptor,
const Options& options)
: FieldGenerator(descriptor, options) {
// 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$");
printer->Print(
variables_,
"@property(nonatomic, readwrite) $property_type$ $name$$deprecated_attribute$;\n"
"\n");
if (WantsHasProperty()) {
printer->Print(
variables_,
"@property(nonatomic, readwrite) BOOL has$capitalized_name$$deprecated_attribute$;\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::RuntimeUsesHasBit(void) const {
if (descriptor_->real_containing_oneof()) {
// The oneof tracks what is set instead.
return false;
}
return true;
}
ObjCObjFieldGenerator::ObjCObjFieldGenerator(const FieldDescriptor* descriptor,
const Options& options)
: SingleFieldGenerator(descriptor, options) {
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$");
printer->Print(
variables_,
"@property(nonatomic, readwrite, $property_storage_attribute$, null_resettable) $property_type$ *$name$$storage_attribute$$deprecated_attribute$;\n");
if (WantsHasProperty()) {
printer->Print(
variables_,
"/** Test to see if @c $name$ has been set. */\n"
"@property(nonatomic, readwrite) BOOL has$capitalized_name$$deprecated_attribute$;\n");
}
if (IsInitName(variables_.find("name")->second)) {
// 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_,
"- ($property_type$ *)$name$ GPB_METHOD_FAMILY_NONE$deprecated_attribute$;\n");
}
printer->Print("\n");
}
RepeatedFieldGenerator::RepeatedFieldGenerator(
const FieldDescriptor* descriptor, const Options& options)
: ObjCObjFieldGenerator(descriptor, options) {
// Default to no comment and let the cases needing it fill it in.
variables_["array_comment"] = "";
}
RepeatedFieldGenerator::~RepeatedFieldGenerator() {}
void RepeatedFieldGenerator::FinishInitialization(void) {
FieldGenerator::FinishInitialization();
if (variables_.find("array_property_type") == variables_.end()) {
variables_["array_property_type"] = variable("array_storage_type");
}
}
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$, $name$_Count;\n");
}
void RepeatedFieldGenerator::GeneratePropertyDeclaration(
io::Printer* printer) const {
// Repeated fields don't need the has* properties, but they do expose a
// *Count (to check without autocreation). So for the field property we need
// 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, null_resettable) $array_property_type$ *$name$$storage_attribute$$deprecated_attribute$;\n"
"/** The number of items in @c $name$ without causing the array to be created. */\n"
"@property(nonatomic, readonly) NSUInteger $name$_Count$deprecated_attribute$;\n");
if (IsInitName(variables_.find("name")->second)) {
// 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_property_type$ *)$name$ GPB_METHOD_FAMILY_NONE$deprecated_attribute$;\n");
}
printer->Print("\n");
}
bool RepeatedFieldGenerator::RuntimeUsesHasBit(void) const {
return false; // The array (or map/dict) having anything is what is used.
}
FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor,
const Options& options)
: descriptor_(descriptor),
field_generators_(descriptor->field_count()),
extension_generators_(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), options));
}
for (int i = 0; i < descriptor->extension_count(); i++) {
extension_generators_[i].reset(
FieldGenerator::Make(descriptor->extension(i), options));
}
}
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];
}
int FieldGeneratorMap::CalculateHasBits(void) {
int total_bits = 0;
for (int i = 0; i < descriptor_->field_count(); i++) {
if (field_generators_[i]->RuntimeUsesHasBit()) {
field_generators_[i]->SetRuntimeHasBit(total_bits);
++total_bits;
} else {
field_generators_[i]->SetNoHasBit();
}
int extra_bits = field_generators_[i]->ExtraRuntimeHasBitsNeeded();
if (extra_bits) {
field_generators_[i]->SetExtraRuntimeHasBitsBase(total_bits);
total_bits += extra_bits;
}
}
return total_bits;
}
void FieldGeneratorMap::SetOneofIndexBase(int index_base) {
for (int i = 0; i < descriptor_->field_count(); i++) {
field_generators_[i]->SetOneofIndexBase(index_base);
}
}
bool FieldGeneratorMap::DoesAnyFieldHaveNonZeroDefault(void) const {
for (int i = 0; i < descriptor_->field_count(); i++) {
if (HasNonZeroDefaultValue(descriptor_->field(i))) {
return true;
}
}
return false;
}
} // namespace objectivec
} // namespace compiler
} // namespace protobuf
} // namespace google