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 <sstream> 32 33#include <google/protobuf/compiler/code_generator.h> 34#include <google/protobuf/compiler/plugin.h> 35#include <google/protobuf/descriptor.h> 36#include <google/protobuf/descriptor.pb.h> 37#include <google/protobuf/io/printer.h> 38#include <google/protobuf/io/zero_copy_stream.h> 39#include <google/protobuf/stubs/strutil.h> 40 41 42#include <google/protobuf/compiler/csharp/csharp_enum.h> 43#include <google/protobuf/compiler/csharp/csharp_helpers.h> 44#include <google/protobuf/compiler/csharp/csharp_message.h> 45#include <google/protobuf/compiler/csharp/csharp_names.h> 46#include <google/protobuf/compiler/csharp/csharp_options.h> 47#include <google/protobuf/compiler/csharp/csharp_reflection_class.h> 48 49namespace google { 50namespace protobuf { 51namespace compiler { 52namespace csharp { 53 54ReflectionClassGenerator::ReflectionClassGenerator(const FileDescriptor* file, 55 const Options* options) 56 : SourceGeneratorBase(file, options), 57 file_(file) { 58 namespace_ = GetFileNamespace(file); 59 reflectionClassname_ = GetReflectionClassUnqualifiedName(file); 60} 61 62ReflectionClassGenerator::~ReflectionClassGenerator() { 63} 64 65void ReflectionClassGenerator::Generate(io::Printer* printer) { 66 WriteIntroduction(printer); 67 68 WriteDescriptor(printer); 69 // Close the class declaration. 70 printer->Outdent(); 71 printer->Print("}\n"); 72 73 // write children: Enums 74 if (file_->enum_type_count() > 0) { 75 printer->Print("#region Enums\n"); 76 for (int i = 0; i < file_->enum_type_count(); i++) { 77 EnumGenerator enumGenerator(file_->enum_type(i), this->options()); 78 enumGenerator.Generate(printer); 79 } 80 printer->Print("#endregion\n"); 81 printer->Print("\n"); 82 } 83 84 // write children: Messages 85 if (file_->message_type_count() > 0) { 86 printer->Print("#region Messages\n"); 87 for (int i = 0; i < file_->message_type_count(); i++) { 88 MessageGenerator messageGenerator(file_->message_type(i), this->options()); 89 messageGenerator.Generate(printer); 90 } 91 printer->Print("#endregion\n"); 92 printer->Print("\n"); 93 } 94 95 // TODO(jtattermusch): add insertion point for services. 96 97 if (!namespace_.empty()) { 98 printer->Outdent(); 99 printer->Print("}\n"); 100 } 101 printer->Print("\n"); 102 printer->Print("#endregion Designer generated code\n"); 103} 104 105void ReflectionClassGenerator::WriteIntroduction(io::Printer* printer) { 106 printer->Print( 107 "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" 108 "// source: $file_name$\n" 109 "#pragma warning disable 1591, 0612, 3021\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 "[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n", 127 "file_name", file_->name()); 128 WriteGeneratedCodeAttributes(printer); 129 printer->Print( 130 "$access_level$ static partial class $reflection_class_name$ {\n" 131 "\n", 132 "access_level", class_access_level(), 133 "reflection_class_name", reflectionClassname_); 134 printer->Indent(); 135} 136 137void ReflectionClassGenerator::WriteDescriptor(io::Printer* printer) { 138 printer->Print( 139 "#region Descriptor\n" 140 "/// <summary>File descriptor for $file_name$</summary>\n" 141 "public static pbr::FileDescriptor Descriptor {\n" 142 " get { return descriptor; }\n" 143 "}\n" 144 "private static pbr::FileDescriptor descriptor;\n" 145 "\n" 146 "static $reflection_class_name$() {\n", 147 "file_name", file_->name(), 148 "reflection_class_name", reflectionClassname_); 149 printer->Indent(); 150 printer->Print( 151 "byte[] descriptorData = global::System.Convert.FromBase64String(\n"); 152 printer->Indent(); 153 printer->Indent(); 154 printer->Print("string.Concat(\n"); 155 printer->Indent(); 156 157 // TODO(jonskeet): Consider a C#-escaping format here instead of just Base64. 158 std::string base64 = FileDescriptorToBase64(file_); 159 while (base64.size() > 60) { 160 printer->Print("\"$base64$\",\n", "base64", base64.substr(0, 60)); 161 base64 = base64.substr(60); 162 } 163 printer->Print("\"$base64$\"));\n", "base64", base64); 164 printer->Outdent(); 165 printer->Outdent(); 166 printer->Outdent(); 167 168 // ----------------------------------------------------------------- 169 // Invoke InternalBuildGeneratedFileFrom() to build the file. 170 printer->Print( 171 "descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,\n"); 172 printer->Print(" new pbr::FileDescriptor[] { "); 173 for (int i = 0; i < file_->dependency_count(); i++) { 174 // descriptor.proto is special: we don't allow access to the generated code, but there's 175 // a separately-exposed property to get at the file descriptor, specifically to allow this 176 // kind of dependency. 177 if (IsDescriptorProto(file_->dependency(i))) { 178 printer->Print("pbr::FileDescriptor.DescriptorProtoFileDescriptor, "); 179 } else { 180 printer->Print( 181 "$full_reflection_class_name$.Descriptor, ", 182 "full_reflection_class_name", 183 GetReflectionClassName(file_->dependency(i))); 184 } 185 } 186 printer->Print("},\n" 187 " new pbr::GeneratedClrTypeInfo("); 188 // Specify all the generated code information, recursively. 189 if (file_->enum_type_count() > 0) { 190 printer->Print("new[] {"); 191 for (int i = 0; i < file_->enum_type_count(); i++) { 192 printer->Print("typeof($type_name$), ", "type_name", GetClassName(file_->enum_type(i))); 193 } 194 printer->Print("}, "); 195 } 196 else { 197 printer->Print("null, "); 198 } 199 if (file_->message_type_count() > 0) { 200 printer->Print("new pbr::GeneratedClrTypeInfo[] {\n"); 201 printer->Indent(); 202 printer->Indent(); 203 printer->Indent(); 204 for (int i = 0; i < file_->message_type_count(); i++) { 205 WriteGeneratedCodeInfo(file_->message_type(i), printer, i == file_->message_type_count() - 1); 206 } 207 printer->Outdent(); 208 printer->Print("\n}));\n"); 209 printer->Outdent(); 210 printer->Outdent(); 211 } 212 else { 213 printer->Print("null));\n"); 214 } 215 216 printer->Outdent(); 217 printer->Print("}\n"); 218 printer->Print("#endregion\n\n"); 219} 220 221// Write out the generated code for a particular message. This consists of the CLR type, property names 222// corresponding to fields, names corresponding to oneofs, nested enums, and nested types. Each array part 223// can be specified as null if it would be empty, to make the generated code somewhat simpler to read. 224// We write a line break at the end of each generated code info, so that in the final file we'll see all 225// the types, pre-ordered depth first, one per line. The indentation will be slightly unusual, 226// in that it will look like a single array when it's actually constructing a tree, but it'll be easy to 227// read even with multiple levels of nesting. 228// The "last" parameter indicates whether this message descriptor is the last one being printed in this immediate 229// context. It governs whether or not a trailing comma and newline is written after the constructor, effectively 230// just controlling the formatting in the generated code. 231void ReflectionClassGenerator::WriteGeneratedCodeInfo(const Descriptor* descriptor, io::Printer* printer, bool last) { 232 if (IsMapEntryMessage(descriptor)) { 233 printer->Print("null, "); 234 return; 235 } 236 // Generated message type 237 printer->Print("new pbr::GeneratedClrTypeInfo(typeof($type_name$), $type_name$.Parser, ", "type_name", GetClassName(descriptor)); 238 239 // Fields 240 if (descriptor->field_count() > 0) { 241 std::vector<std::string> fields; 242 for (int i = 0; i < descriptor->field_count(); i++) { 243 fields.push_back(GetPropertyName(descriptor->field(i))); 244 } 245 printer->Print("new[]{ \"$fields$\" }, ", "fields", JoinStrings(fields, "\", \"")); 246 } 247 else { 248 printer->Print("null, "); 249 } 250 251 // Oneofs 252 if (descriptor->oneof_decl_count() > 0) { 253 std::vector<std::string> oneofs; 254 for (int i = 0; i < descriptor->oneof_decl_count(); i++) { 255 oneofs.push_back(UnderscoresToCamelCase(descriptor->oneof_decl(i)->name(), true)); 256 } 257 printer->Print("new[]{ \"$oneofs$\" }, ", "oneofs", JoinStrings(oneofs, "\", \"")); 258 } 259 else { 260 printer->Print("null, "); 261 } 262 263 // Nested enums 264 if (descriptor->enum_type_count() > 0) { 265 std::vector<std::string> enums; 266 for (int i = 0; i < descriptor->enum_type_count(); i++) { 267 enums.push_back(GetClassName(descriptor->enum_type(i))); 268 } 269 printer->Print("new[]{ typeof($enums$) }, ", "enums", JoinStrings(enums, "), typeof(")); 270 } 271 else { 272 printer->Print("null, "); 273 } 274 275 // Nested types 276 if (descriptor->nested_type_count() > 0) { 277 // Need to specify array type explicitly here, as all elements may be null. 278 printer->Print("new pbr::GeneratedClrTypeInfo[] { "); 279 for (int i = 0; i < descriptor->nested_type_count(); i++) { 280 WriteGeneratedCodeInfo(descriptor->nested_type(i), printer, i == descriptor->nested_type_count() - 1); 281 } 282 printer->Print("}"); 283 } 284 else { 285 printer->Print("null"); 286 } 287 printer->Print(last ? ")" : "),\n"); 288} 289 290} // namespace csharp 291} // namespace compiler 292} // namespace protobuf 293} // namespace google 294