1// Protocol Buffers - Google's data interchange format 2// Copyright 2008 Google Inc. All rights reserved. 3// http://code.google.com/p/protobuf/ 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 <algorithm> 36#include <google/protobuf/stubs/hash.h> 37#include <google/protobuf/compiler/javamicro/javamicro_message.h> 38#include <google/protobuf/compiler/javamicro/javamicro_enum.h> 39#include <google/protobuf/compiler/javamicro/javamicro_helpers.h> 40#include <google/protobuf/stubs/strutil.h> 41#include <google/protobuf/io/printer.h> 42#include <google/protobuf/io/coded_stream.h> 43#include <google/protobuf/wire_format.h> 44#include <google/protobuf/descriptor.pb.h> 45 46namespace google { 47namespace protobuf { 48namespace compiler { 49namespace javamicro { 50 51using internal::WireFormat; 52using internal::WireFormatLite; 53 54namespace { 55 56void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field) { 57 // Print the field's proto-syntax definition as a comment. We don't want to 58 // print group bodies so we cut off after the first line. 59 string def = field->DebugString(); 60 printer->Print("// $def$\n", 61 "def", def.substr(0, def.find_first_of('\n'))); 62} 63 64struct FieldOrderingByNumber { 65 inline bool operator()(const FieldDescriptor* a, 66 const FieldDescriptor* b) const { 67 return a->number() < b->number(); 68 } 69}; 70 71// Sort the fields of the given Descriptor by number into a new[]'d array 72// and return it. 73const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor) { 74 const FieldDescriptor** fields = 75 new const FieldDescriptor*[descriptor->field_count()]; 76 for (int i = 0; i < descriptor->field_count(); i++) { 77 fields[i] = descriptor->field(i); 78 } 79 sort(fields, fields + descriptor->field_count(), 80 FieldOrderingByNumber()); 81 return fields; 82} 83 84// Get an identifier that uniquely identifies this type within the file. 85// This is used to declare static variables related to this type at the 86// outermost file scope. 87string UniqueFileScopeIdentifier(const Descriptor* descriptor) { 88 return "static_" + StringReplace(descriptor->full_name(), ".", "_", true); 89} 90 91// Returns true if the message type has any required fields. If it doesn't, 92// we can optimize out calls to its isInitialized() method. 93// 94// already_seen is used to avoid checking the same type multiple times 95// (and also to protect against recursion). 96static bool HasRequiredFields( 97 const Descriptor* type, 98 hash_set<const Descriptor*>* already_seen) { 99 if (already_seen->count(type) > 0) { 100 // The type is already in cache. This means that either: 101 // a. The type has no required fields. 102 // b. We are in the midst of checking if the type has required fields, 103 // somewhere up the stack. In this case, we know that if the type 104 // has any required fields, they'll be found when we return to it, 105 // and the whole call to HasRequiredFields() will return true. 106 // Therefore, we don't have to check if this type has required fields 107 // here. 108 return false; 109 } 110 already_seen->insert(type); 111 112 // If the type has extensions, an extension with message type could contain 113 // required fields, so we have to be conservative and assume such an 114 // extension exists. 115 if (type->extension_range_count() > 0) return true; 116 117 for (int i = 0; i < type->field_count(); i++) { 118 const FieldDescriptor* field = type->field(i); 119 if (field->is_required()) { 120 return true; 121 } 122 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 123 if (HasRequiredFields(field->message_type(), already_seen)) { 124 return true; 125 } 126 } 127 } 128 129 return false; 130} 131 132static bool HasRequiredFields(const Descriptor* type) { 133 hash_set<const Descriptor*> already_seen; 134 return HasRequiredFields(type, &already_seen); 135} 136 137} // namespace 138 139// =================================================================== 140 141MessageGenerator::MessageGenerator(const Descriptor* descriptor, const Params& params) 142 : params_(params), 143 descriptor_(descriptor), 144 field_generators_(descriptor, params) { 145} 146 147MessageGenerator::~MessageGenerator() {} 148 149void MessageGenerator::GenerateStaticVariables(io::Printer* printer) { 150 // Generate static members for all nested types. 151 for (int i = 0; i < descriptor_->nested_type_count(); i++) { 152 // TODO(kenton): Reuse MessageGenerator objects? 153 MessageGenerator(descriptor_->nested_type(i), params_) 154 .GenerateStaticVariables(printer); 155 } 156} 157 158void MessageGenerator::GenerateStaticVariableInitializers( 159 io::Printer* printer) { 160 // Generate static member initializers for all nested types. 161 for (int i = 0; i < descriptor_->nested_type_count(); i++) { 162 // TODO(kenton): Reuse MessageGenerator objects? 163 MessageGenerator(descriptor_->nested_type(i), params_) 164 .GenerateStaticVariableInitializers(printer); 165 } 166 167 if (descriptor_->extension_count() != 0) { 168 GOOGLE_LOG(FATAL) << "Extensions not supported in MICRO_RUNTIME\n"; 169 } 170} 171 172void MessageGenerator::Generate(io::Printer* printer) { 173 bool is_own_file = 174 params_.java_multiple_files() || ((descriptor_->containing_type() == NULL) 175 && !params_.has_java_outer_classname(descriptor_->file()->name())); 176 177#if 0 178 GOOGLE_LOG(INFO) << "is_own_file=" << is_own_file; 179 GOOGLE_LOG(INFO) << "containing_type()=" << ((descriptor_->containing_type() == NULL) ? "NULL" : "not null"); 180 GOOGLE_LOG(INFO) << "java_multiple_files()=" << params_.java_multiple_files(); 181 GOOGLE_LOG(INFO) << "has_java_outer_classname()=" << params_.has_java_outer_classname(file_->name()); 182#endif 183 184 if ((descriptor_->extension_count() != 0) 185 || (descriptor_->extension_range_count() != 0)) { 186 GOOGLE_LOG(FATAL) << "Extensions not supported in MICRO_RUNTIME\n"; 187 } 188 189 // Note: Fields (which will be emitted in the loop, below) may have the same names as fields in 190 // the inner or outer class. This causes Java warnings, but is not fatal, so we suppress those 191 // warnings here in the class declaration. 192 printer->Print( 193 "@SuppressWarnings(\"hiding\")\n" 194 "public $modifiers$ final class $classname$ extends\n" 195 " com.google.protobuf.micro.MessageMicro {\n", 196 "modifiers", is_own_file ? "" : "static", 197 "classname", descriptor_->name()); 198 printer->Indent(); 199 printer->Print( 200 "public $classname$() {}\n" 201 "\n", 202 "classname", descriptor_->name()); 203 204 // Nested types and extensions 205 for (int i = 0; i < descriptor_->enum_type_count(); i++) { 206 EnumGenerator(descriptor_->enum_type(i), params_).Generate(printer); 207 } 208 209 for (int i = 0; i < descriptor_->nested_type_count(); i++) { 210 MessageGenerator(descriptor_->nested_type(i), params_).Generate(printer); 211 } 212 213 // Fields 214 for (int i = 0; i < descriptor_->field_count(); i++) { 215 PrintFieldComment(printer, descriptor_->field(i)); 216 printer->Print("public static final int $constant_name$ = $number$;\n", 217 "constant_name", FieldConstantName(descriptor_->field(i)), 218 "number", SimpleItoa(descriptor_->field(i)->number())); 219 field_generators_.get(descriptor_->field(i)).GenerateMembers(printer); 220 printer->Print("\n"); 221 } 222 223 GenerateClear(printer); 224 GenerateIsInitialized(printer); 225 GenerateMessageSerializationMethods(printer); 226 GenerateMergeFromMethods(printer); 227 GenerateParseFromMethods(printer); 228 229 printer->Outdent(); 230 printer->Print("}\n\n"); 231} 232 233// =================================================================== 234 235void MessageGenerator:: 236GenerateMessageSerializationMethods(io::Printer* printer) { 237 scoped_array<const FieldDescriptor*> sorted_fields( 238 SortFieldsByNumber(descriptor_)); 239 240 if (descriptor_->extension_range_count() != 0) { 241 GOOGLE_LOG(FATAL) << "Extensions not supported in MICRO_RUNTIME\n"; 242 } 243 244 // writeTo only throws an exception if it contains one or more fields to write 245 if (descriptor_->field_count() > 0) { 246 printer->Print( 247 "@Override\n" 248 "public void writeTo(com.google.protobuf.micro.CodedOutputStreamMicro output)\n" 249 " throws java.io.IOException {\n"); 250 } else { 251 printer->Print( 252 "@Override\n" 253 "public void writeTo(com.google.protobuf.micro.CodedOutputStreamMicro output) {\n"); 254 } 255 printer->Indent(); 256 257 // Output the fields in sorted order 258 for (int i = 0; i < descriptor_->field_count(); i++) { 259 GenerateSerializeOneField(printer, sorted_fields[i]); 260 } 261 262 printer->Outdent(); 263 printer->Print( 264 "}\n" 265 "\n" 266 "private int cachedSize = -1;\n" 267 "@Override\n" 268 "public int getCachedSize() {\n" 269 " if (cachedSize < 0) {\n" 270 " // getSerializedSize sets cachedSize\n" 271 " getSerializedSize();\n" 272 " }\n" 273 " return cachedSize;\n" 274 "}\n" 275 "\n" 276 "@Override\n" 277 "public int getSerializedSize() {\n" 278 " int size = 0;\n"); 279 printer->Indent(); 280 281 for (int i = 0; i < descriptor_->field_count(); i++) { 282 field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer); 283 } 284 285 printer->Outdent(); 286 printer->Print( 287 " cachedSize = size;\n" 288 " return size;\n" 289 "}\n" 290 "\n"); 291} 292 293void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) { 294 scoped_array<const FieldDescriptor*> sorted_fields( 295 SortFieldsByNumber(descriptor_)); 296 297 if (params_.java_use_vector()) { 298 printer->Print( 299 "@Override\n" 300 "public com.google.protobuf.micro.MessageMicro mergeFrom(\n" 301 " com.google.protobuf.micro.CodedInputStreamMicro input)\n" 302 " throws java.io.IOException {\n", 303 "classname", descriptor_->name()); 304 } else { 305 printer->Print( 306 "@Override\n" 307 "public $classname$ mergeFrom(\n" 308 " com.google.protobuf.micro.CodedInputStreamMicro input)\n" 309 " throws java.io.IOException {\n", 310 "classname", descriptor_->name()); 311 } 312 printer->Indent(); 313 314 printer->Print( 315 "while (true) {\n"); 316 printer->Indent(); 317 318 printer->Print( 319 "int tag = input.readTag();\n" 320 "switch (tag) {\n"); 321 printer->Indent(); 322 323 printer->Print( 324 "case 0:\n" // zero signals EOF / limit reached 325 " return this;\n" 326 "default: {\n" 327 " if (!parseUnknownField(input, tag)) {\n" 328 " return this;\n" // it's an endgroup tag 329 " }\n" 330 " break;\n" 331 "}\n"); 332 333 for (int i = 0; i < descriptor_->field_count(); i++) { 334 const FieldDescriptor* field = sorted_fields[i]; 335 uint32 tag = WireFormatLite::MakeTag(field->number(), 336 WireFormat::WireTypeForField(field)); 337 338 printer->Print( 339 "case $tag$: {\n", 340 "tag", SimpleItoa(tag)); 341 printer->Indent(); 342 343 field_generators_.get(field).GenerateParsingCode(printer); 344 345 printer->Outdent(); 346 printer->Print( 347 " break;\n" 348 "}\n"); 349 } 350 351 printer->Outdent(); 352 printer->Outdent(); 353 printer->Outdent(); 354 printer->Print( 355 " }\n" // switch (tag) 356 " }\n" // while (true) 357 "}\n" 358 "\n"); 359} 360 361void MessageGenerator:: 362GenerateParseFromMethods(io::Printer* printer) { 363 bool is_own_file = 364 descriptor_->containing_type() == NULL; 365 366 // Note: These are separate from GenerateMessageSerializationMethods() 367 // because they need to be generated even for messages that are optimized 368 // for code size. 369 printer->Print( 370 "public $static$ $classname$ parseFrom(byte[] data)\n" 371 " throws com.google.protobuf.micro.InvalidProtocolBufferMicroException {\n" 372 " return ($classname$) (new $classname$().mergeFrom(data));\n" 373 "}\n" 374 "\n" 375 "public $static$ $classname$ parseFrom(\n" 376 " com.google.protobuf.micro.CodedInputStreamMicro input)\n" 377 " throws java.io.IOException {\n" 378 " return new $classname$().mergeFrom(input);\n" 379 "}\n" 380 "\n", 381 "static", (is_own_file ? "static" : ""), 382 "classname", descriptor_->name()); 383} 384 385void MessageGenerator::GenerateSerializeOneField( 386 io::Printer* printer, const FieldDescriptor* field) { 387 field_generators_.get(field).GenerateSerializationCode(printer); 388} 389 390void MessageGenerator::GenerateClear(io::Printer* printer) { 391 printer->Print( 392 "public final $classname$ clear() {\n", 393 "classname", descriptor_->name()); 394 printer->Indent(); 395 396 // Call clear for all of the fields. 397 for (int i = 0; i < descriptor_->field_count(); i++) { 398 const FieldDescriptor* field = descriptor_->field(i); 399 400 printer->Print( 401 "clear$name$();\n", 402 "name", UnderscoresToCapitalizedCamelCase(field)); 403 } 404 405 printer->Outdent(); 406 printer->Print( 407 " cachedSize = -1;\n" 408 " return this;\n" 409 "}\n" 410 "\n"); 411} 412 413 414void MessageGenerator::GenerateIsInitialized(io::Printer* printer) { 415 printer->Print( 416 "public final boolean isInitialized() {\n"); 417 printer->Indent(); 418 419 // Check that all required fields in this message are set. 420 // TODO(kenton): We can optimize this when we switch to putting all the 421 // "has" fields into a single bitfield. 422 for (int i = 0; i < descriptor_->field_count(); i++) { 423 const FieldDescriptor* field = descriptor_->field(i); 424 425 if (field->is_required()) { 426 printer->Print( 427 "if (!has$name$) return false;\n", 428 "name", UnderscoresToCapitalizedCamelCase(field)); 429 } 430 } 431 432 // Now check that all embedded messages are initialized. 433 for (int i = 0; i < descriptor_->field_count(); i++) { 434 const FieldDescriptor* field = descriptor_->field(i); 435 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && 436 HasRequiredFields(field->message_type())) { 437 switch (field->label()) { 438 case FieldDescriptor::LABEL_REQUIRED: 439 printer->Print( 440 "if (!get$name$().isInitialized()) return false;\n", 441 "type", ClassName(params_, field->message_type()), 442 "name", UnderscoresToCapitalizedCamelCase(field)); 443 break; 444 case FieldDescriptor::LABEL_OPTIONAL: 445 printer->Print( 446 "if (has$name$()) {\n" 447 " if (!get$name$().isInitialized()) return false;\n" 448 "}\n", 449 "type", ClassName(params_, field->message_type()), 450 "name", UnderscoresToCapitalizedCamelCase(field)); 451 break; 452 case FieldDescriptor::LABEL_REPEATED: 453 if (params_.java_use_vector()) { 454 printer->Print( 455 "for (int i = 0; i < get$name$List().size(); i++) {\n" 456 " if (get$name$(i).isInitialized()) return false;\n" 457 "}\n", 458 "type", ClassName(params_, field->message_type()), 459 "name", UnderscoresToCapitalizedCamelCase(field)); 460 } else { 461 printer->Print( 462 "for ($type$ element : get$name$List()) {\n" 463 " if (!element.isInitialized()) return false;\n" 464 "}\n", 465 "type", ClassName(params_, field->message_type()), 466 "name", UnderscoresToCapitalizedCamelCase(field)); 467 } 468 break; 469 } 470 } 471 } 472 473 if (descriptor_->extension_range_count() > 0) { 474 printer->Print( 475 "if (!extensionsAreInitialized()) return false;\n"); 476 } 477 478 printer->Outdent(); 479 printer->Print( 480 " return true;\n" 481 "}\n" 482 "\n"); 483} 484 485// =================================================================== 486 487} // namespace javamicro 488} // namespace compiler 489} // namespace protobuf 490} // namespace google 491