• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/csharp/csharp_reflection_class.h"
9 
10 #include <sstream>
11 
12 #include "absl/strings/str_join.h"
13 #include "google/protobuf/compiler/code_generator.h"
14 #include "google/protobuf/compiler/csharp/csharp_enum.h"
15 #include "google/protobuf/compiler/csharp/csharp_field_base.h"
16 #include "google/protobuf/compiler/csharp/csharp_helpers.h"
17 #include "google/protobuf/compiler/csharp/csharp_message.h"
18 #include "google/protobuf/compiler/csharp/csharp_options.h"
19 #include "google/protobuf/compiler/csharp/names.h"
20 #include "google/protobuf/descriptor.h"
21 #include "google/protobuf/descriptor.pb.h"
22 #include "google/protobuf/io/printer.h"
23 
24 // Must be last.
25 #include "google/protobuf/port_def.inc"
26 
27 namespace google {
28 namespace protobuf {
29 namespace compiler {
30 namespace csharp {
31 
ReflectionClassGenerator(const FileDescriptor * file,const Options * options)32 ReflectionClassGenerator::ReflectionClassGenerator(const FileDescriptor* file,
33                                                    const Options* options)
34     : SourceGeneratorBase(options),
35       file_(file) {
36   namespace_ = GetFileNamespace(file);
37   reflectionClassname_ = GetReflectionClassUnqualifiedName(file);
38   extensionClassname_ = GetExtensionClassUnqualifiedName(file);
39 }
40 
~ReflectionClassGenerator()41 ReflectionClassGenerator::~ReflectionClassGenerator() {
42 }
43 
Generate(io::Printer * printer)44 void ReflectionClassGenerator::Generate(io::Printer* printer) {
45   WriteIntroduction(printer);
46 
47   WriteDescriptor(printer);
48   // Close the class declaration.
49   printer->Outdent();
50   printer->Print("}\n");
51 
52   if (file_->extension_count() > 0) {
53     printer->Print(
54         "/// <summary>Holder for extension identifiers generated from the top "
55         "level of $file_name$</summary>\n"
56         "$access_level$ static partial class $class_name$ {\n",
57         "access_level", class_access_level(), "class_name", extensionClassname_,
58         "file_name", file_->name());
59     printer->Indent();
60     for (int i = 0; i < file_->extension_count(); i++) {
61         std::unique_ptr<FieldGeneratorBase> generator(
62           CreateFieldGenerator(file_->extension(i), -1, this->options()));
63         generator->GenerateExtensionCode(printer);
64     }
65     printer->Outdent();
66     printer->Print(
67     "}\n"
68     "\n");
69   }
70 
71   // write children: Enums
72   if (file_->enum_type_count() > 0) {
73     printer->Print("#region Enums\n");
74     for (int i = 0; i < file_->enum_type_count(); i++) {
75       EnumGenerator enumGenerator(file_->enum_type(i), this->options());
76       enumGenerator.Generate(printer);
77     }
78     printer->Print("#endregion\n");
79     printer->Print("\n");
80   }
81 
82   // write children: Messages
83   if (file_->message_type_count() > 0) {
84     printer->Print("#region Messages\n");
85     for (int i = 0; i < file_->message_type_count(); i++) {
86       MessageGenerator messageGenerator(file_->message_type(i), this->options());
87       messageGenerator.Generate(printer);
88     }
89     printer->Print("#endregion\n");
90     printer->Print("\n");
91   }
92 
93   // TODO: add insertion point for services.
94 
95   if (!namespace_.empty()) {
96     printer->Outdent();
97     printer->Print("}\n");
98   }
99   printer->Print("\n");
100   printer->Print("#endregion Designer generated code\n");
101 }
102 
WriteIntroduction(io::Printer * printer)103 void ReflectionClassGenerator::WriteIntroduction(io::Printer* printer) {
104   printer->Print(
105     "// <auto-generated>\n"
106     "//     Generated by the protocol buffer compiler.  DO NOT EDIT!\n"
107     "//     source: $file_name$\n"
108     "// </auto-generated>\n"
109     "#pragma warning disable 1591, 0612, 3021, 8981\n"
110     "#region Designer generated code\n"
111     "\n"
112     "using pb = global::Google.Protobuf;\n"
113     "using pbc = global::Google.Protobuf.Collections;\n"
114     "using pbr = global::Google.Protobuf.Reflection;\n"
115     "using scg = global::System.Collections.Generic;\n",
116     "file_name", file_->name());
117 
118   if (!namespace_.empty()) {
119     printer->Print("namespace $namespace$ {\n", "namespace", namespace_);
120     printer->Indent();
121     printer->Print("\n");
122   }
123 
124   printer->Print(
125     "/// <summary>Holder for reflection information generated from $file_name$</summary>\n"
126     "$access_level$ static partial class $reflection_class_name$ {\n"
127     "\n",
128     "file_name", file_->name(),
129     "access_level", class_access_level(),
130     "reflection_class_name", reflectionClassname_);
131   printer->Indent();
132 }
133 
WriteDescriptor(io::Printer * printer)134 void ReflectionClassGenerator::WriteDescriptor(io::Printer* printer) {
135   printer->Print(
136     "#region Descriptor\n"
137     "/// <summary>File descriptor for $file_name$</summary>\n"
138     "public static pbr::FileDescriptor Descriptor {\n"
139     "  get { return descriptor; }\n"
140     "}\n"
141     "private static pbr::FileDescriptor descriptor;\n"
142     "\n"
143     "static $reflection_class_name$() {\n",
144     "file_name", file_->name(),
145     "reflection_class_name", reflectionClassname_);
146   printer->Indent();
147   printer->Print(
148     "byte[] descriptorData = global::System.Convert.FromBase64String(\n");
149   printer->Indent();
150   printer->Indent();
151   printer->Print("string.Concat(\n");
152   printer->Indent();
153 
154   // TODO: Consider a C#-escaping format here instead of just Base64.
155   std::string base64 = options()->strip_nonfunctional_codegen
156                            ? ""
157                            : FileDescriptorToBase64(file_);
158   while (base64.size() > 60) {
159     printer->Print("\"$base64$\",\n", "base64", base64.substr(0, 60));
160     base64 = base64.substr(60);
161   }
162   printer->Print("\"$base64$\"));\n", "base64", base64);
163   printer->Outdent();
164   printer->Outdent();
165   printer->Outdent();
166 
167   // -----------------------------------------------------------------
168   // Invoke InternalBuildGeneratedFileFrom() to build the file.
169   printer->Print(
170       "descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,\n");
171   printer->Print("    new pbr::FileDescriptor[] { ");
172   for (int i = 0; i < file_->dependency_count(); i++) {
173     if (options()->strip_nonfunctional_codegen &&
174         IsKnownFeatureProto(file_->dependency(i)->name())) {
175       // Strip feature imports for editions codegen tests.
176       continue;
177     }
178     printer->Print("$full_reflection_class_name$.Descriptor, ",
179                    "full_reflection_class_name",
180                    GetReflectionClassName(file_->dependency(i)));
181   }
182   printer->Print("},\n"
183       "    new pbr::GeneratedClrTypeInfo(");
184   // Specify all the generated code information, recursively.
185   if (file_->enum_type_count() > 0) {
186       printer->Print("new[] {");
187       for (int i = 0; i < file_->enum_type_count(); i++) {
188           printer->Print("typeof($type_name$), ", "type_name", GetClassName(file_->enum_type(i)));
189       }
190       printer->Print("}, ");
191   }
192   else {
193       printer->Print("null, ");
194   }
195   if (file_->extension_count() > 0) {
196     std::vector<std::string> extensions;
197     for (int i = 0; i < file_->extension_count(); i++) {
198       extensions.push_back(GetFullExtensionName(file_->extension(i)));
199     }
200     printer->Print("new pb::Extension[] { $extensions$ }, ", "extensions", absl::StrJoin(extensions, ", "));
201   }
202   else {
203     printer->Print("null, ");
204   }
205   if (file_->message_type_count() > 0) {
206       printer->Print("new pbr::GeneratedClrTypeInfo[] {\n");
207       printer->Indent();
208       printer->Indent();
209       printer->Indent();
210       for (int i = 0; i < file_->message_type_count(); i++) {
211           WriteGeneratedCodeInfo(file_->message_type(i), printer, i == file_->message_type_count() - 1);
212       }
213       printer->Outdent();
214       printer->Print("\n}));\n");
215       printer->Outdent();
216       printer->Outdent();
217   }
218   else {
219       printer->Print("null));\n");
220   }
221 
222   printer->Outdent();
223   printer->Print("}\n");
224   printer->Print("#endregion\n\n");
225 }
226 
227 // Write out the generated code for a particular message. This consists of the CLR type, property names
228 // corresponding to fields, names corresponding to oneofs, nested enums, and nested types. Each array part
229 // can be specified as null if it would be empty, to make the generated code somewhat simpler to read.
230 // We write a line break at the end of each generated code info, so that in the final file we'll see all
231 // the types, pre-ordered depth first, one per line. The indentation will be slightly unusual,
232 // in that it will look like a single array when it's actually constructing a tree, but it'll be easy to
233 // read even with multiple levels of nesting.
234 // The "last" parameter indicates whether this message descriptor is the last one being printed in this immediate
235 // context. It governs whether or not a trailing comma and newline is written after the constructor, effectively
236 // just controlling the formatting in the generated code.
WriteGeneratedCodeInfo(const Descriptor * descriptor,io::Printer * printer,bool last)237 void ReflectionClassGenerator::WriteGeneratedCodeInfo(const Descriptor* descriptor, io::Printer* printer, bool last) {
238   if (IsMapEntryMessage(descriptor)) {
239     printer->Print("null, ");
240     return;
241   }
242   // Generated message type
243   printer->Print("new pbr::GeneratedClrTypeInfo(typeof($type_name$), $type_name$.Parser, ", "type_name", GetClassName(descriptor));
244 
245   // Fields
246   if (descriptor->field_count() > 0) {
247       std::vector<std::string> fields;
248       fields.reserve(descriptor->field_count());
249       for (int i = 0; i < descriptor->field_count(); i++) {
250           fields.push_back(GetPropertyName(descriptor->field(i)));
251       }
252       printer->Print("new[]{ \"$fields$\" }, ", "fields", absl::StrJoin(fields, "\", \""));
253   }
254   else {
255       printer->Print("null, ");
256   }
257 
258   // Oneofs
259   if (descriptor->oneof_decl_count() > 0) {
260       std::vector<std::string> oneofs;
261       oneofs.reserve(descriptor->oneof_decl_count());
262       for (int i = 0; i < descriptor->oneof_decl_count(); i++) {
263         if (options()->strip_nonfunctional_codegen &&
264             i >= descriptor->real_oneof_decl_count()) {
265           // Skip synthetic oneofs, which don't affect any actual behavior
266           // outside reflection.
267           break;
268         }
269         oneofs.push_back(
270             UnderscoresToCamelCase(descriptor->oneof_decl(i)->name(), true));
271       }
272       if (oneofs.empty()) {
273         printer->Print("null, ");
274       } else {
275         printer->Print("new[]{ \"$oneofs$\" }, ", "oneofs",
276                        absl::StrJoin(oneofs, "\", \""));
277       }
278   }
279   else {
280       printer->Print("null, ");
281   }
282 
283   // Nested enums
284   if (descriptor->enum_type_count() > 0) {
285       std::vector<std::string> enums;
286       enums.reserve(descriptor->enum_type_count());
287       for (int i = 0; i < descriptor->enum_type_count(); i++) {
288           enums.push_back(GetClassName(descriptor->enum_type(i)));
289       }
290       printer->Print("new[]{ typeof($enums$) }, ", "enums", absl::StrJoin(enums, "), typeof("));
291   }
292   else {
293       printer->Print("null, ");
294   }
295 
296   // Extensions
297   if (descriptor->extension_count() > 0) {
298     std::vector<std::string> extensions;
299     for (int i = 0; i < descriptor->extension_count(); i++) {
300       extensions.push_back(GetFullExtensionName(descriptor->extension(i)));
301     }
302     printer->Print("new pb::Extension[] { $extensions$ }, ", "extensions", absl::StrJoin(extensions, ", "));
303   }
304   else {
305     printer->Print("null, ");
306   }
307 
308   // Nested types
309   if (descriptor->nested_type_count() > 0) {
310       // Need to specify array type explicitly here, as all elements may be null.
311       printer->Print("new pbr::GeneratedClrTypeInfo[] { ");
312       for (int i = 0; i < descriptor->nested_type_count(); i++) {
313           WriteGeneratedCodeInfo(descriptor->nested_type(i), printer, i == descriptor->nested_type_count() - 1);
314       }
315       printer->Print("}");
316   }
317   else {
318       printer->Print("null");
319   }
320   printer->Print(last ? ")" : "),\n");
321 }
322 
323 }  // namespace csharp
324 }  // namespace compiler
325 }  // namespace protobuf
326 }  // namespace google
327 
328 #include "google/protobuf/port_undef.inc"
329