• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // Author: kenton@google.com (Kenton Varda)
32 //  Based on original Protocol Buffers design by
33 //  Sanjay Ghemawat, Jeff Dean, and others.
34 
35 #include <map>
36 
37 #include <google/protobuf/compiler/cpp/cpp_enum.h>
38 #include <google/protobuf/compiler/cpp/cpp_helpers.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 cpp {
46 
47 namespace {
48 // The GOOGLE_ARRAYSIZE constant is the max enum value plus 1. If the max enum value
49 // is ::google::protobuf::kint32max, GOOGLE_ARRAYSIZE will overflow. In such cases we should omit the
50 // generation of the GOOGLE_ARRAYSIZE constant.
ShouldGenerateArraySize(const EnumDescriptor * descriptor)51 bool ShouldGenerateArraySize(const EnumDescriptor* descriptor) {
52   int32 max_value = descriptor->value(0)->number();
53   for (int i = 0; i < descriptor->value_count(); i++) {
54     if (descriptor->value(i)->number() > max_value) {
55       max_value = descriptor->value(i)->number();
56     }
57   }
58   return max_value != ::google::protobuf::kint32max;
59 }
60 }  // namespace
61 
EnumGenerator(const EnumDescriptor * descriptor,const Options & options)62 EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
63                              const Options& options)
64   : descriptor_(descriptor),
65     classname_(ClassName(descriptor, false)),
66     options_(options),
67     generate_array_size_(ShouldGenerateArraySize(descriptor)) {
68 }
69 
~EnumGenerator()70 EnumGenerator::~EnumGenerator() {}
71 
FillForwardDeclaration(map<string,const EnumDescriptor * > * enum_names)72 void EnumGenerator::FillForwardDeclaration(
73     map<string, const EnumDescriptor*>* enum_names) {
74   if (!options_.proto_h) {
75     return;
76   }
77   (*enum_names)[classname_] = descriptor_;
78 }
79 
GenerateDefinition(io::Printer * printer)80 void EnumGenerator::GenerateDefinition(io::Printer* printer) {
81   map<string, string> vars;
82   vars["classname"] = classname_;
83   vars["short_name"] = descriptor_->name();
84   vars["enumbase"] = classname_ + (options_.proto_h ? " : int" : "");
85 
86   printer->Print(vars, "enum $enumbase$ {\n");
87   printer->Annotate("enumbase", descriptor_);
88   printer->Indent();
89 
90   const EnumValueDescriptor* min_value = descriptor_->value(0);
91   const EnumValueDescriptor* max_value = descriptor_->value(0);
92 
93   for (int i = 0; i < descriptor_->value_count(); i++) {
94     vars["name"] = EnumValueName(descriptor_->value(i));
95     // In C++, an value of -2147483648 gets interpreted as the negative of
96     // 2147483648, and since 2147483648 can't fit in an integer, this produces a
97     // compiler warning.  This works around that issue.
98     vars["number"] = Int32ToString(descriptor_->value(i)->number());
99     vars["prefix"] = (descriptor_->containing_type() == NULL) ?
100       "" : classname_ + "_";
101     vars["deprecation"] = descriptor_->value(i)->options().deprecated() ?
102         " PROTOBUF_DEPRECATED" : "";
103 
104     if (i > 0) printer->Print(",\n");
105     printer->Print(vars, "$prefix$$name$$deprecation$ = $number$");
106 
107     if (descriptor_->value(i)->number() < min_value->number()) {
108       min_value = descriptor_->value(i);
109     }
110     if (descriptor_->value(i)->number() > max_value->number()) {
111       max_value = descriptor_->value(i);
112     }
113   }
114 
115   if (HasPreservingUnknownEnumSemantics(descriptor_->file())) {
116     // For new enum semantics: generate min and max sentinel values equal to
117     // INT32_MIN and INT32_MAX
118     if (descriptor_->value_count() > 0) printer->Print(",\n");
119     printer->Print(vars,
120         "$classname$_$prefix$INT_MIN_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32min,\n"
121         "$classname$_$prefix$INT_MAX_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32max");
122   }
123 
124   printer->Outdent();
125   printer->Print("\n};\n");
126 
127   vars["min_name"] = EnumValueName(min_value);
128   vars["max_name"] = EnumValueName(max_value);
129 
130   if (options_.dllexport_decl.empty()) {
131     vars["dllexport"] = "";
132   } else {
133     vars["dllexport"] = options_.dllexport_decl + " ";
134   }
135 
136   printer->Print(vars,
137     "$dllexport$bool $classname$_IsValid(int value);\n"
138     "const $classname$ $prefix$$short_name$_MIN = $prefix$$min_name$;\n"
139     "const $classname$ $prefix$$short_name$_MAX = $prefix$$max_name$;\n");
140 
141   if (generate_array_size_) {
142     printer->Print(vars,
143       "const int $prefix$$short_name$_ARRAYSIZE = "
144       "$prefix$$short_name$_MAX + 1;\n\n");
145   }
146 
147   if (HasDescriptorMethods(descriptor_->file(), options_)) {
148     printer->Print(vars,
149       "$dllexport$const ::google::protobuf::EnumDescriptor* $classname$_descriptor();\n");
150     // The _Name and _Parse methods
151     printer->Print(
152         vars,
153         "inline const ::std::string& $classname$_Name($classname$ value) {\n"
154         "  return ::google::protobuf::internal::NameOfEnum(\n"
155         "    $classname$_descriptor(), value);\n"
156         "}\n");
157     printer->Print(vars,
158       "inline bool $classname$_Parse(\n"
159       "    const ::std::string& name, $classname$* value) {\n"
160       "  return ::google::protobuf::internal::ParseNamedEnum<$classname$>(\n"
161       "    $classname$_descriptor(), name, value);\n"
162       "}\n");
163   }
164 }
165 
166 void EnumGenerator::
GenerateGetEnumDescriptorSpecializations(io::Printer * printer)167 GenerateGetEnumDescriptorSpecializations(io::Printer* printer) {
168   printer->Print(
169       "template <> struct is_proto_enum< $classname$> : ::google::protobuf::internal::true_type "
170       "{};\n",
171       "classname", ClassName(descriptor_, true));
172   if (HasDescriptorMethods(descriptor_->file(), options_)) {
173     printer->Print(
174       "template <>\n"
175       "inline const EnumDescriptor* GetEnumDescriptor< $classname$>() {\n"
176       "  return $classname$_descriptor();\n"
177       "}\n",
178       "classname", ClassName(descriptor_, true));
179   }
180 }
181 
GenerateSymbolImports(io::Printer * printer)182 void EnumGenerator::GenerateSymbolImports(io::Printer* printer) {
183   map<string, string> vars;
184   vars["nested_name"] = descriptor_->name();
185   vars["classname"] = classname_;
186   vars["constexpr"] = options_.proto_h ? "constexpr " : "";
187   printer->Print(vars, "typedef $classname$ $nested_name$;\n");
188 
189   for (int j = 0; j < descriptor_->value_count(); j++) {
190     vars["tag"] = EnumValueName(descriptor_->value(j));
191     vars["deprecated_attr"] = descriptor_->value(j)->options().deprecated() ?
192       "PROTOBUF_DEPRECATED_ATTR " : "";
193     printer->Print(vars,
194       "$deprecated_attr$static $constexpr$const $nested_name$ $tag$ =\n"
195       "  $classname$_$tag$;\n");
196   }
197 
198   printer->Print(vars,
199     "static inline bool $nested_name$_IsValid(int value) {\n"
200     "  return $classname$_IsValid(value);\n"
201     "}\n"
202     "static const $nested_name$ $nested_name$_MIN =\n"
203     "  $classname$_$nested_name$_MIN;\n"
204     "static const $nested_name$ $nested_name$_MAX =\n"
205     "  $classname$_$nested_name$_MAX;\n");
206   if (generate_array_size_) {
207     printer->Print(vars,
208       "static const int $nested_name$_ARRAYSIZE =\n"
209       "  $classname$_$nested_name$_ARRAYSIZE;\n");
210   }
211 
212   if (HasDescriptorMethods(descriptor_->file(), options_)) {
213     printer->Print(vars,
214       "static inline const ::google::protobuf::EnumDescriptor*\n"
215       "$nested_name$_descriptor() {\n"
216       "  return $classname$_descriptor();\n"
217       "}\n");
218     printer->Print(vars,
219                    "static inline const ::std::string& "
220                    "$nested_name$_Name($nested_name$ value) {"
221                    "\n"
222                    "  return $classname$_Name(value);\n"
223                    "}\n");
224     printer->Print(vars,
225       "static inline bool $nested_name$_Parse(const ::std::string& name,\n"
226       "    $nested_name$* value) {\n"
227       "  return $classname$_Parse(name, value);\n"
228       "}\n");
229   }
230 }
231 
GenerateDescriptorInitializer(io::Printer * printer,int index)232 void EnumGenerator::GenerateDescriptorInitializer(
233     io::Printer* printer, int index) {
234   map<string, string> vars;
235   vars["classname"] = classname_;
236   vars["index"] = SimpleItoa(index);
237 
238   if (descriptor_->containing_type() == NULL) {
239     printer->Print(vars,
240       "$classname$_descriptor_ = file->enum_type($index$);\n");
241   } else {
242     vars["parent"] = ClassName(descriptor_->containing_type(), false);
243     printer->Print(vars,
244       "$classname$_descriptor_ = $parent$_descriptor_->enum_type($index$);\n");
245   }
246 }
247 
GenerateMethods(io::Printer * printer)248 void EnumGenerator::GenerateMethods(io::Printer* printer) {
249   map<string, string> vars;
250   vars["classname"] = classname_;
251   vars["constexpr"] = options_.proto_h ? "constexpr " : "";
252 
253   if (HasDescriptorMethods(descriptor_->file(), options_)) {
254     printer->Print(vars,
255       "const ::google::protobuf::EnumDescriptor* $classname$_descriptor() {\n"
256       "  protobuf_AssignDescriptorsOnce();\n"
257       "  return $classname$_descriptor_;\n"
258       "}\n");
259   }
260 
261   printer->Print(vars,
262     "bool $classname$_IsValid(int value) {\n"
263     "  switch(value) {\n");
264 
265   // Multiple values may have the same number.  Make sure we only cover
266   // each number once by first constructing a set containing all valid
267   // numbers, then printing a case statement for each element.
268 
269   set<int> numbers;
270   for (int j = 0; j < descriptor_->value_count(); j++) {
271     const EnumValueDescriptor* value = descriptor_->value(j);
272     numbers.insert(value->number());
273   }
274 
275   for (set<int>::iterator iter = numbers.begin();
276        iter != numbers.end(); ++iter) {
277     printer->Print(
278       "    case $number$:\n",
279       "number", Int32ToString(*iter));
280   }
281 
282   printer->Print(vars,
283     "      return true;\n"
284     "    default:\n"
285     "      return false;\n"
286     "  }\n"
287     "}\n"
288     "\n");
289 
290   if (descriptor_->containing_type() != NULL) {
291     // We need to "define" the static constants which were declared in the
292     // header, to give the linker a place to put them.  Or at least the C++
293     // standard says we have to.  MSVC actually insists that we do _not_ define
294     // them again in the .cc file, prior to VC++ 2015.
295     printer->Print("#if !defined(_MSC_VER) || _MSC_VER >= 1900\n");
296 
297     vars["parent"] = ClassName(descriptor_->containing_type(), false);
298     vars["nested_name"] = descriptor_->name();
299     for (int i = 0; i < descriptor_->value_count(); i++) {
300       vars["value"] = EnumValueName(descriptor_->value(i));
301       printer->Print(vars,
302         "$constexpr$const $classname$ $parent$::$value$;\n");
303     }
304     printer->Print(vars,
305       "const $classname$ $parent$::$nested_name$_MIN;\n"
306       "const $classname$ $parent$::$nested_name$_MAX;\n");
307     if (generate_array_size_) {
308       printer->Print(vars,
309         "const int $parent$::$nested_name$_ARRAYSIZE;\n");
310     }
311 
312     printer->Print("#endif  // !defined(_MSC_VER) || _MSC_VER >= 1900\n");
313   }
314 }
315 
316 }  // namespace cpp
317 }  // namespace compiler
318 }  // namespace protobuf
319 }  // namespace google
320