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