1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 #include "google/protobuf/compiler/objectivec/enum_field.h"
9
10 #include <string>
11
12 #include "absl/container/btree_set.h"
13 #include "absl/container/flat_hash_map.h"
14 #include "absl/container/flat_hash_set.h"
15 #include "absl/strings/str_cat.h"
16 #include "absl/strings/string_view.h"
17 #include "google/protobuf/compiler/objectivec/field.h"
18 #include "google/protobuf/compiler/objectivec/names.h"
19 #include "google/protobuf/compiler/objectivec/options.h"
20 #include "google/protobuf/descriptor.h"
21 #include "google/protobuf/io/printer.h"
22
23 namespace google {
24 namespace protobuf {
25 namespace compiler {
26 namespace objectivec {
27
28 namespace {
29
SetEnumVariables(const FieldDescriptor * descriptor,const GenerationOptions & generation_options,absl::flat_hash_map<absl::string_view,std::string> * variables)30 void SetEnumVariables(
31 const FieldDescriptor* descriptor,
32 const GenerationOptions& generation_options,
33 absl::flat_hash_map<absl::string_view, std::string>* variables) {
34 const std::string type = EnumName(descriptor->enum_type());
35 const std::string enum_desc_func = absl::StrCat(type, "_EnumDescriptor");
36 (*variables)["enum_name"] = type;
37 // When using fwd decls, for non repeated fields, if it was defined in a
38 // different file, the property decls need to use "enum NAME" rather than just
39 // "NAME" to support the forward declaration of the enums.
40 if (generation_options.headers_use_forward_declarations &&
41 !descriptor->is_repeated() &&
42 !IsProtobufLibraryBundledProtoFile(descriptor->enum_type()->file()) &&
43 (descriptor->file() != descriptor->enum_type()->file())) {
44 (*variables)["property_type"] = absl::StrCat("enum ", type, " ");
45 }
46 (*variables)["enum_verifier"] = absl::StrCat(type, "_IsValidValue");
47 (*variables)["enum_desc_func"] = enum_desc_func;
48
49 (*variables)["dataTypeSpecific_name"] = "enumDescFunc";
50 (*variables)["dataTypeSpecific_value"] = enum_desc_func;
51
52 const Descriptor* msg_descriptor = descriptor->containing_type();
53 (*variables)["owning_message_class"] = ClassName(msg_descriptor);
54 }
55 } // namespace
56
EnumFieldGenerator(const FieldDescriptor * descriptor,const GenerationOptions & generation_options)57 EnumFieldGenerator::EnumFieldGenerator(
58 const FieldDescriptor* descriptor,
59 const GenerationOptions& generation_options)
60 : SingleFieldGenerator(descriptor, generation_options) {
61 SetEnumVariables(descriptor, generation_options, &variables_);
62 }
63
GenerateCFunctionDeclarations(io::Printer * printer) const64 void EnumFieldGenerator::GenerateCFunctionDeclarations(
65 io::Printer* printer) const {
66 if (descriptor_->enum_type()->is_closed()) {
67 return;
68 }
69
70 auto vars = printer->WithVars(variables_);
71 printer->Emit(R"objc(
72 /**
73 * Fetches the raw value of a @c $owning_message_class$'s @c $name$ property, even
74 * if the value was not defined by the enum at the time the code was generated.
75 **/
76 int32_t $owning_message_class$_$capitalized_name$_RawValue($owning_message_class$ *message);
77 /**
78 * Sets the raw value of an @c $owning_message_class$'s @c $name$ property, allowing
79 * it to be set to a value that was not defined by the enum at the time the code
80 * was generated.
81 **/
82 void Set$owning_message_class$_$capitalized_name$_RawValue($owning_message_class$ *message, int32_t value);
83 )objc");
84 printer->Emit("\n");
85 }
86
GenerateCFunctionImplementations(io::Printer * printer) const87 void EnumFieldGenerator::GenerateCFunctionImplementations(
88 io::Printer* printer) const {
89 if (descriptor_->enum_type()->is_closed()) {
90 return;
91 }
92
93 auto vars = printer->WithVars(variables_);
94 printer->Emit(R"objc(
95 int32_t $owning_message_class$_$capitalized_name$_RawValue($owning_message_class$ *message) {
96 GPBDescriptor *descriptor = [$owning_message_class$ descriptor];
97 GPBFieldDescriptor *field = [descriptor fieldWithNumber:$field_number_name$];
98 return GPBGetMessageRawEnumField(message, field);
99 }
100
101 void Set$owning_message_class$_$capitalized_name$_RawValue($owning_message_class$ *message, int32_t value) {
102 GPBDescriptor *descriptor = [$owning_message_class$ descriptor];
103 GPBFieldDescriptor *field = [descriptor fieldWithNumber:$field_number_name$];
104 GPBSetMessageRawEnumField(message, field, value);
105 }
106 )objc");
107 printer->Emit("\n");
108 }
109
DetermineForwardDeclarations(absl::btree_set<std::string> * fwd_decls,bool include_external_types) const110 void EnumFieldGenerator::DetermineForwardDeclarations(
111 absl::btree_set<std::string>* fwd_decls,
112 bool include_external_types) const {
113 SingleFieldGenerator::DetermineForwardDeclarations(fwd_decls,
114 include_external_types);
115 // If it is an enum defined in a different file (and not a WKT), then we'll
116 // need a forward declaration for it. When it is in our file, all the enums
117 // are output before the message, so it will be declared before it is needed.
118 if (include_external_types &&
119 descriptor_->file() != descriptor_->enum_type()->file() &&
120 !IsProtobufLibraryBundledProtoFile(descriptor_->enum_type()->file())) {
121 const std::string& name = variable("enum_name");
122 fwd_decls->insert(absl::StrCat("GPB_ENUM_FWD_DECLARE(", name, ");"));
123 }
124 }
125
DetermineNeededFiles(absl::flat_hash_set<const FileDescriptor * > * deps) const126 void EnumFieldGenerator::DetermineNeededFiles(
127 absl::flat_hash_set<const FileDescriptor*>* deps) const {
128 if (descriptor_->file() != descriptor_->enum_type()->file()) {
129 deps->insert(descriptor_->enum_type()->file());
130 }
131 }
132
RepeatedEnumFieldGenerator(const FieldDescriptor * descriptor,const GenerationOptions & generation_options)133 RepeatedEnumFieldGenerator::RepeatedEnumFieldGenerator(
134 const FieldDescriptor* descriptor,
135 const GenerationOptions& generation_options)
136 : RepeatedFieldGenerator(descriptor, generation_options) {
137 SetEnumVariables(descriptor, generation_options, &variables_);
138 }
139
EmitArrayComment(io::Printer * printer) const140 void RepeatedEnumFieldGenerator::EmitArrayComment(io::Printer* printer) const {
141 auto vars = printer->WithVars(variables_);
142 printer->Emit(R"objc(
143 // |$name$| contains |$enum_name$|
144 )objc");
145 }
146
147 // NOTE: RepeatedEnumFieldGenerator::DetermineForwardDeclarations isn't needed
148 // because `GPBEnumArray` isn't generic (like `NSArray` would be for messages)
149 // and thus doesn't reference the type in the header.
150
DetermineNeededFiles(absl::flat_hash_set<const FileDescriptor * > * deps) const151 void RepeatedEnumFieldGenerator::DetermineNeededFiles(
152 absl::flat_hash_set<const FileDescriptor*>* deps) const {
153 if (descriptor_->file() != descriptor_->enum_type()->file()) {
154 deps->insert(descriptor_->enum_type()->file());
155 }
156 }
157
158 } // namespace objectivec
159 } // namespace compiler
160 } // namespace protobuf
161 } // namespace google
162