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 <google/protobuf/compiler/cpp/extension.h>
36
37#include <map>
38
39#include <google/protobuf/io/printer.h>
40#include <google/protobuf/stubs/strutil.h>
41#include <google/protobuf/compiler/cpp/helpers.h>
42#include <google/protobuf/descriptor.pb.h>
43
44namespace google {
45namespace protobuf {
46namespace compiler {
47namespace cpp {
48
49ExtensionGenerator::ExtensionGenerator(const FieldDescriptor* descriptor,
50 const Options& options,
51 MessageSCCAnalyzer* scc_analyzer)
52 : descriptor_(descriptor), options_(options), scc_analyzer_(scc_analyzer) {
53 // Construct type_traits_.
54 if (descriptor_->is_repeated()) {
55 type_traits_ = "Repeated";
56 }
57
58 switch (descriptor_->cpp_type()) {
59 case FieldDescriptor::CPPTYPE_ENUM:
60 type_traits_.append(s: "EnumTypeTraits< ");
61 type_traits_.append(str: ClassName(descriptor: descriptor_->enum_type(), qualified: true));
62 type_traits_.append(s: ", ");
63 type_traits_.append(str: ClassName(descriptor: descriptor_->enum_type(), qualified: true));
64 type_traits_.append(s: "_IsValid>");
65 break;
66 case FieldDescriptor::CPPTYPE_STRING:
67 type_traits_.append(s: "StringTypeTraits");
68 break;
69 case FieldDescriptor::CPPTYPE_MESSAGE:
70 type_traits_.append(s: "MessageTypeTraits< ");
71 type_traits_.append(str: ClassName(descriptor: descriptor_->message_type(), qualified: true));
72 type_traits_.append(s: " >");
73 break;
74 default:
75 type_traits_.append(s: "PrimitiveTypeTraits< ");
76 type_traits_.append(str: PrimitiveTypeName(options: options_, type: descriptor_->cpp_type()));
77 type_traits_.append(s: " >");
78 break;
79 }
80 SetCommonVars(options, variables: &variables_);
81 SetCommonMessageDataVariables(descriptor: descriptor_->containing_type(), variables: &variables_);
82 variables_["extendee"] =
83 QualifiedClassName(d: descriptor_->containing_type(), options: options_);
84 variables_["type_traits"] = type_traits_;
85 std::string name = descriptor_->name();
86 variables_["name"] = ResolveKeyword(name);
87 variables_["constant_name"] = FieldConstantName(field: descriptor_);
88 variables_["field_type"] =
89 StrCat(a: static_cast<int>(descriptor_->type()));
90 variables_["packed"] = descriptor_->is_packed() ? "true" : "false";
91
92 std::string scope =
93 IsScoped() ? ClassName(descriptor: descriptor_->extension_scope(), qualified: false) + "::" : "";
94 variables_["scope"] = scope;
95 variables_["scoped_name"] = ExtensionName(d: descriptor_);
96 variables_["number"] = StrCat(a: descriptor_->number());
97
98 bool add_verify_fn =
99 // Only verify msgs.
100 descriptor_->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
101 // Options say to verify.
102 ShouldVerify(descriptor: descriptor_->message_type(), options: options_, scc_analyzer: scc_analyzer_) &&
103 ShouldVerify(descriptor: descriptor_->containing_type(), options: options_, scc_analyzer: scc_analyzer_);
104
105 variables_["verify_fn"] =
106 add_verify_fn
107 ? StrCat(a: "&", b: FieldMessageTypeName(field: descriptor_, options: options_),
108 c: "::InternalVerify")
109 : "nullptr";
110}
111
112ExtensionGenerator::~ExtensionGenerator() {}
113
114bool ExtensionGenerator::IsScoped() const {
115 return descriptor_->extension_scope() != nullptr;
116}
117
118void ExtensionGenerator::GenerateDeclaration(io::Printer* printer) const {
119 Formatter format(printer, variables_);
120
121 // If this is a class member, it needs to be declared "static". Otherwise,
122 // it needs to be "extern". In the latter case, it also needs the DLL
123 // export/import specifier.
124 std::string qualifier;
125 if (!IsScoped()) {
126 qualifier = "extern";
127 if (!options_.dllexport_decl.empty()) {
128 qualifier = options_.dllexport_decl + " " + qualifier;
129 }
130 } else {
131 qualifier = "static";
132 }
133
134 format(
135 "static const int $constant_name$ = $number$;\n"
136 "$1$ ::$proto_ns$::internal::ExtensionIdentifier< $extendee$,\n"
137 " ::$proto_ns$::internal::$type_traits$, $field_type$, $packed$ >\n"
138 " ${2$$name$$}$;\n",
139 qualifier, descriptor_);
140}
141
142void ExtensionGenerator::GenerateDefinition(io::Printer* printer) {
143 // If we are building for lite with implicit weak fields, we want to skip over
144 // any custom options (i.e. extensions of messages from descriptor.proto).
145 // This prevents the creation of any unnecessary linker references to the
146 // descriptor messages.
147 if (options_.lite_implicit_weak_fields &&
148 descriptor_->containing_type()->file()->name() ==
149 "net/proto2/proto/descriptor.proto") {
150 return;
151 }
152
153 Formatter format(printer, variables_);
154 std::string default_str;
155 // If this is a class member, it needs to be declared in its class scope.
156 if (descriptor_->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
157 // We need to declare a global string which will contain the default value.
158 // We cannot declare it at class scope because that would require exposing
159 // it in the header which would be annoying for other reasons. So we
160 // replace :: with _ in the name and declare it as a global.
161 default_str =
162 StringReplace(s: variables_["scoped_name"], oldsub: "::", newsub: "_", replace_all: true) + "_default";
163 format("const std::string $1$($2$);\n", default_str,
164 DefaultValue(options: options_, field: descriptor_));
165 } else if (descriptor_->message_type()) {
166 // We have to initialize the default instance for extensions at registration
167 // time.
168 default_str =
169 FieldMessageTypeName(field: descriptor_, options: options_) + "::default_instance()";
170 } else {
171 default_str = DefaultValue(options: options_, field: descriptor_);
172 }
173
174 // Likewise, class members need to declare the field constant variable.
175 if (IsScoped()) {
176 format(
177 "#if !defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912)\n"
178 "const int $scope$$constant_name$;\n"
179 "#endif\n");
180 }
181
182 format(
183 "PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 "
184 "::$proto_ns$::internal::ExtensionIdentifier< $extendee$,\n"
185 " ::$proto_ns$::internal::$type_traits$, $field_type$, $packed$>\n"
186 " $scoped_name$($constant_name$, $1$, $verify_fn$);\n",
187 default_str);
188}
189
190} // namespace cpp
191} // namespace compiler
192} // namespace protobuf
193} // namespace google
194