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 <google/protobuf/compiler/objectivec/objectivec_file.h>
32 #include <google/protobuf/compiler/objectivec/objectivec_enum.h>
33 #include <google/protobuf/compiler/objectivec/objectivec_extension.h>
34 #include <google/protobuf/compiler/objectivec/objectivec_message.h>
35 #include <google/protobuf/compiler/code_generator.h>
36 #include <google/protobuf/io/printer.h>
37 #include <google/protobuf/io/zero_copy_stream_impl.h>
38 #include <google/protobuf/stubs/stl_util.h>
39 #include <google/protobuf/stubs/strutil.h>
40 #include <sstream>
41
42 namespace google {
43 namespace protobuf {
44
45 // This is also found in GPBBootstrap.h, and needs to be kept in sync. It
46 // is the version check done to ensure generated code works with the current
47 // runtime being used.
48 const int32 GOOGLE_PROTOBUF_OBJC_GEN_VERSION = 30001;
49
50 namespace compiler {
51 namespace objectivec {
52
FileGenerator(const FileDescriptor * file,const Options & options)53 FileGenerator::FileGenerator(const FileDescriptor *file, const Options& options)
54 : file_(file),
55 root_class_name_(FileClassName(file)),
56 is_public_dep_(false),
57 options_(options) {
58 for (int i = 0; i < file_->enum_type_count(); i++) {
59 EnumGenerator *generator = new EnumGenerator(file_->enum_type(i));
60 enum_generators_.push_back(generator);
61 }
62 for (int i = 0; i < file_->message_type_count(); i++) {
63 MessageGenerator *generator =
64 new MessageGenerator(root_class_name_, file_->message_type(i), options_);
65 message_generators_.push_back(generator);
66 }
67 for (int i = 0; i < file_->extension_count(); i++) {
68 ExtensionGenerator *generator =
69 new ExtensionGenerator(root_class_name_, file_->extension(i));
70 extension_generators_.push_back(generator);
71 }
72 }
73
~FileGenerator()74 FileGenerator::~FileGenerator() {
75 STLDeleteContainerPointers(dependency_generators_.begin(),
76 dependency_generators_.end());
77 STLDeleteContainerPointers(enum_generators_.begin(), enum_generators_.end());
78 STLDeleteContainerPointers(message_generators_.begin(),
79 message_generators_.end());
80 STLDeleteContainerPointers(extension_generators_.begin(),
81 extension_generators_.end());
82 }
83
GenerateHeader(io::Printer * printer)84 void FileGenerator::GenerateHeader(io::Printer *printer) {
85 printer->Print(
86 "// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
87 "// source: $filename$\n"
88 "\n",
89 "filename", file_->name());
90
91 printer->Print(
92 "#import \"GPBProtocolBuffers.h\"\n"
93 "\n");
94
95 // Add some verification that the generated code matches the source the
96 // code is being compiled with.
97 printer->Print(
98 "#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != $protoc_gen_objc_version$\n"
99 "#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.\n"
100 "#endif\n"
101 "\n",
102 "protoc_gen_objc_version",
103 SimpleItoa(GOOGLE_PROTOBUF_OBJC_GEN_VERSION));
104
105 const vector<FileGenerator *> &dependency_generators = DependencyGenerators();
106 for (vector<FileGenerator *>::const_iterator iter =
107 dependency_generators.begin();
108 iter != dependency_generators.end(); ++iter) {
109 if ((*iter)->IsPublicDependency()) {
110 printer->Print("#import \"$header$.pbobjc.h\"\n",
111 "header", (*iter)->Path());
112 }
113 }
114
115 printer->Print(
116 "// @@protoc_insertion_point(imports)\n"
117 "\n"
118 "#pragma clang diagnostic push\n"
119 "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n"
120 "\n"
121 "CF_EXTERN_C_BEGIN\n"
122 "\n");
123
124 set<string> fwd_decls;
125 for (vector<MessageGenerator *>::iterator iter = message_generators_.begin();
126 iter != message_generators_.end(); ++iter) {
127 (*iter)->DetermineForwardDeclarations(&fwd_decls);
128 }
129 for (set<string>::const_iterator i(fwd_decls.begin());
130 i != fwd_decls.end(); ++i) {
131 printer->Print("$value$;\n", "value", *i);
132 }
133 if (fwd_decls.begin() != fwd_decls.end()) {
134 printer->Print("\n");
135 }
136
137 printer->Print(
138 "NS_ASSUME_NONNULL_BEGIN\n"
139 "\n");
140
141 // need to write out all enums first
142 for (vector<EnumGenerator *>::iterator iter = enum_generators_.begin();
143 iter != enum_generators_.end(); ++iter) {
144 (*iter)->GenerateHeader(printer);
145 }
146
147 for (vector<MessageGenerator *>::iterator iter = message_generators_.begin();
148 iter != message_generators_.end(); ++iter) {
149 (*iter)->GenerateEnumHeader(printer);
150 }
151
152 // For extensions to chain together, the Root gets created even if there
153 // are no extensions.
154 printer->Print(
155 "#pragma mark - $root_class_name$\n"
156 "\n"
157 "/// Exposes the extension registry for this file.\n"
158 "///\n"
159 "/// The base class provides:\n"
160 "/// @code\n"
161 "/// + (GPBExtensionRegistry *)extensionRegistry;\n"
162 "/// @endcode\n"
163 "/// which is a @c GPBExtensionRegistry that includes all the extensions defined by\n"
164 "/// this file and all files that it depends on.\n"
165 "@interface $root_class_name$ : GPBRootObject\n"
166 "@end\n"
167 "\n",
168 "root_class_name", root_class_name_);
169
170 if (extension_generators_.size() > 0) {
171 // The dynamic methods block is only needed if there are extensions.
172 printer->Print(
173 "@interface $root_class_name$ (DynamicMethods)\n",
174 "root_class_name", root_class_name_);
175
176 for (vector<ExtensionGenerator *>::iterator iter =
177 extension_generators_.begin();
178 iter != extension_generators_.end(); ++iter) {
179 (*iter)->GenerateMembersHeader(printer);
180 }
181
182 printer->Print("@end\n\n");
183 } // extension_generators_.size() > 0
184
185 for (vector<MessageGenerator *>::iterator iter = message_generators_.begin();
186 iter != message_generators_.end(); ++iter) {
187 (*iter)->GenerateMessageHeader(printer);
188 }
189
190 printer->Print(
191 "NS_ASSUME_NONNULL_END\n"
192 "\n"
193 "CF_EXTERN_C_END\n"
194 "\n"
195 "#pragma clang diagnostic pop\n"
196 "\n"
197 "// @@protoc_insertion_point(global_scope)\n");
198 }
199
GenerateSource(io::Printer * printer)200 void FileGenerator::GenerateSource(io::Printer *printer) {
201 printer->Print(
202 "// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
203 "// source: $filename$\n"
204 "\n",
205 "filename", file_->name());
206
207 string header_file = Path() + ".pbobjc.h";
208 printer->Print(
209 "#import \"GPBProtocolBuffers_RuntimeSupport.h\"\n"
210 "#import \"$header_file$\"\n",
211 "header_file", header_file);
212 const vector<FileGenerator *> &dependency_generators =
213 DependencyGenerators();
214 for (vector<FileGenerator *>::const_iterator iter =
215 dependency_generators.begin();
216 iter != dependency_generators.end(); ++iter) {
217 if (!(*iter)->IsPublicDependency()) {
218 printer->Print("#import \"$header$.pbobjc.h\"\n",
219 "header", (*iter)->Path());
220 }
221 }
222 printer->Print(
223 "// @@protoc_insertion_point(imports)\n"
224 "\n"
225 "#pragma clang diagnostic push\n"
226 "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n"
227 "\n");
228
229 printer->Print(
230 "#pragma mark - $root_class_name$\n"
231 "\n"
232 "@implementation $root_class_name$\n\n",
233 "root_class_name", root_class_name_);
234
235 // Generate the extension initialization structures for the top level and
236 // any nested messages.
237 ostringstream extensions_stringstream;
238 if (file_->extension_count() + file_->message_type_count() > 0) {
239 io::OstreamOutputStream extensions_outputstream(&extensions_stringstream);
240 io::Printer extensions_printer(&extensions_outputstream, '$');
241 for (vector<ExtensionGenerator *>::iterator iter =
242 extension_generators_.begin();
243 iter != extension_generators_.end(); ++iter) {
244 (*iter)->GenerateStaticVariablesInitialization(&extensions_printer);
245 }
246 for (vector<MessageGenerator *>::iterator iter =
247 message_generators_.begin();
248 iter != message_generators_.end(); ++iter) {
249 (*iter)->GenerateStaticVariablesInitialization(&extensions_printer);
250 }
251 extensions_stringstream.flush();
252 }
253
254 // If there were any extensions or this file has any dependencies, output
255 // a registry to override to create the file specific registry.
256 const string& extensions_str = extensions_stringstream.str();
257 if (extensions_str.length() > 0 || file_->dependency_count() > 0) {
258 printer->Print(
259 "+ (GPBExtensionRegistry*)extensionRegistry {\n"
260 " // This is called by +initialize so there is no need to worry\n"
261 " // about thread safety and initialization of registry.\n"
262 " static GPBExtensionRegistry* registry = nil;\n"
263 " if (!registry) {\n"
264 " GPBDebugCheckRuntimeVersion();\n"
265 " registry = [[GPBExtensionRegistry alloc] init];\n");
266
267 printer->Indent();
268 printer->Indent();
269
270 if (extensions_str.length() > 0) {
271 printer->Print(
272 "static GPBExtensionDescription descriptions[] = {\n");
273 printer->Indent();
274 printer->Print(extensions_str.c_str());
275 printer->Outdent();
276 printer->Print(
277 "};\n"
278 "for (size_t i = 0; i < sizeof(descriptions) / sizeof(descriptions[0]); ++i) {\n"
279 " GPBExtensionDescriptor *extension =\n"
280 " [[GPBExtensionDescriptor alloc] initWithExtensionDescription:&descriptions[i]];\n"
281 " [registry addExtension:extension];\n"
282 " [self globallyRegisterExtension:extension];\n"
283 " [extension release];\n"
284 "}\n");
285 }
286
287 const vector<FileGenerator *> &dependency_generators =
288 DependencyGenerators();
289 for (vector<FileGenerator *>::const_iterator iter =
290 dependency_generators.begin();
291 iter != dependency_generators.end(); ++iter) {
292 printer->Print(
293 "[registry addExtensions:[$dependency$ extensionRegistry]];\n",
294 "dependency", (*iter)->RootClassName());
295 }
296
297 printer->Outdent();
298 printer->Outdent();
299
300 printer->Print(
301 " }\n"
302 " return registry;\n"
303 "}\n"
304 "\n");
305 }
306
307 printer->Print("@end\n\n");
308
309 // File descriptor only needed if there are messages to use it.
310 if (message_generators_.size() > 0) {
311 string syntax;
312 switch (file_->syntax()) {
313 case FileDescriptor::SYNTAX_UNKNOWN:
314 syntax = "GPBFileSyntaxUnknown";
315 break;
316 case FileDescriptor::SYNTAX_PROTO2:
317 syntax = "GPBFileSyntaxProto2";
318 break;
319 case FileDescriptor::SYNTAX_PROTO3:
320 syntax = "GPBFileSyntaxProto3";
321 break;
322 }
323 printer->Print(
324 "#pragma mark - $root_class_name$_FileDescriptor\n"
325 "\n"
326 "static GPBFileDescriptor *$root_class_name$_FileDescriptor(void) {\n"
327 " // This is called by +initialize so there is no need to worry\n"
328 " // about thread safety of the singleton.\n"
329 " static GPBFileDescriptor *descriptor = NULL;\n"
330 " if (!descriptor) {\n"
331 " GPBDebugCheckRuntimeVersion();\n"
332 " descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n"
333 " syntax:$syntax$];\n"
334 " }\n"
335 " return descriptor;\n"
336 "}\n"
337 "\n",
338 "root_class_name", root_class_name_,
339 "package", file_->package(),
340 "syntax", syntax);
341 }
342
343 for (vector<EnumGenerator *>::iterator iter = enum_generators_.begin();
344 iter != enum_generators_.end(); ++iter) {
345 (*iter)->GenerateSource(printer);
346 }
347 for (vector<MessageGenerator *>::iterator iter = message_generators_.begin();
348 iter != message_generators_.end(); ++iter) {
349 (*iter)->GenerateSource(printer);
350 }
351
352 printer->Print(
353 "\n"
354 "#pragma clang diagnostic pop\n"
355 "\n"
356 "// @@protoc_insertion_point(global_scope)\n");
357 }
358
Path() const359 const string FileGenerator::Path() const { return FilePath(file_); }
360
DependencyGenerators()361 const vector<FileGenerator *> &FileGenerator::DependencyGenerators() {
362 if (file_->dependency_count() != dependency_generators_.size()) {
363 set<string> public_import_names;
364 for (int i = 0; i < file_->public_dependency_count(); i++) {
365 public_import_names.insert(file_->public_dependency(i)->name());
366 }
367 for (int i = 0; i < file_->dependency_count(); i++) {
368 FileGenerator *generator =
369 new FileGenerator(file_->dependency(i), options_);
370 const string& name = file_->dependency(i)->name();
371 bool public_import = (public_import_names.count(name) != 0);
372 generator->SetIsPublicDependency(public_import);
373 dependency_generators_.push_back(generator);
374 }
375 }
376 return dependency_generators_;
377 }
378
379 } // namespace objectivec
380 } // namespace compiler
381 } // namespace protobuf
382 } // namespace google
383