765 lines
27 KiB
C++
765 lines
27 KiB
C++
// Protocol Buffers - Google's data interchange format
|
|
// Copyright 2008 Google Inc. All rights reserved.
|
|
//
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file or at
|
|
// https://developers.google.com/open-source/licenses/bsd
|
|
|
|
#include "google/protobuf/compiler/csharp/csharp_message.h"
|
|
|
|
#include <algorithm>
|
|
#include <sstream>
|
|
|
|
#include "google/protobuf/compiler/code_generator.h"
|
|
#include "absl/container/flat_hash_map.h"
|
|
#include "absl/log/absl_log.h"
|
|
#include "absl/strings/str_cat.h"
|
|
#include "google/protobuf/compiler/csharp/csharp_doc_comment.h"
|
|
#include "google/protobuf/compiler/csharp/csharp_enum.h"
|
|
#include "google/protobuf/compiler/csharp/csharp_field_base.h"
|
|
#include "google/protobuf/compiler/csharp/csharp_helpers.h"
|
|
#include "google/protobuf/compiler/csharp/csharp_options.h"
|
|
#include "google/protobuf/compiler/csharp/names.h"
|
|
#include "google/protobuf/descriptor.h"
|
|
#include "google/protobuf/descriptor.pb.h"
|
|
#include "google/protobuf/io/printer.h"
|
|
#include "google/protobuf/wire_format.h"
|
|
#include "google/protobuf/wire_format_lite.h"
|
|
|
|
// Must be last.
|
|
#include "google/protobuf/port_def.inc"
|
|
|
|
namespace google {
|
|
namespace protobuf {
|
|
namespace compiler {
|
|
namespace csharp {
|
|
|
|
bool CompareFieldNumbers(const FieldDescriptor* d1, const FieldDescriptor* d2) {
|
|
return d1->number() < d2->number();
|
|
}
|
|
|
|
MessageGenerator::MessageGenerator(const Descriptor* descriptor,
|
|
const Options* options)
|
|
: SourceGeneratorBase(options),
|
|
descriptor_(descriptor),
|
|
has_bit_field_count_(0),
|
|
has_extension_ranges_(descriptor->extension_range_count() > 0) {
|
|
// fields by number
|
|
for (int i = 0; i < descriptor_->field_count(); i++) {
|
|
fields_by_number_.push_back(descriptor_->field(i));
|
|
}
|
|
std::sort(fields_by_number_.begin(), fields_by_number_.end(),
|
|
CompareFieldNumbers);
|
|
|
|
int presence_bit_count = 0;
|
|
for (int i = 0; i < descriptor_->field_count(); i++) {
|
|
const FieldDescriptor* field = descriptor_->field(i);
|
|
if (RequiresPresenceBit(field)) {
|
|
presence_bit_count++;
|
|
if (has_bit_field_count_ == 0 || (presence_bit_count % 32) == 0) {
|
|
has_bit_field_count_++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
MessageGenerator::~MessageGenerator() {}
|
|
|
|
std::string MessageGenerator::class_name() { return descriptor_->name(); }
|
|
|
|
std::string MessageGenerator::full_class_name() {
|
|
return GetClassName(descriptor_);
|
|
}
|
|
|
|
const std::vector<const FieldDescriptor*>&
|
|
MessageGenerator::fields_by_number() {
|
|
return fields_by_number_;
|
|
}
|
|
|
|
void MessageGenerator::AddDeprecatedFlag(io::Printer* printer) {
|
|
if (descriptor_->options().deprecated()) {
|
|
printer->Print("[global::System.ObsoleteAttribute]\n");
|
|
}
|
|
}
|
|
|
|
void MessageGenerator::AddSerializableAttribute(io::Printer* printer) {
|
|
if (this->options()->serializable) {
|
|
printer->Print("[global::System.SerializableAttribute]\n");
|
|
}
|
|
}
|
|
|
|
void MessageGenerator::Generate(io::Printer* printer) {
|
|
absl::flat_hash_map<absl::string_view, std::string> vars;
|
|
vars["class_name"] = class_name();
|
|
vars["access_level"] = class_access_level();
|
|
|
|
WriteMessageDocComment(printer, options(), descriptor_);
|
|
AddDeprecatedFlag(printer);
|
|
AddSerializableAttribute(printer);
|
|
|
|
printer->Print(
|
|
"[global::System.Diagnostics.DebuggerDisplayAttribute(\"{ToString(),nq}"
|
|
"\")]\n");
|
|
printer->Print(vars, "$access_level$ sealed partial class $class_name$ : ");
|
|
|
|
if (has_extension_ranges_) {
|
|
printer->Print(vars, "pb::IExtendableMessage<$class_name$>\n");
|
|
} else {
|
|
printer->Print(vars, "pb::IMessage<$class_name$>\n");
|
|
}
|
|
printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
|
|
printer->Print(" , pb::IBufferMessage\n");
|
|
printer->Print("#endif\n");
|
|
printer->Print("{\n");
|
|
printer->Indent();
|
|
|
|
// All static fields and properties
|
|
printer->Print(
|
|
vars,
|
|
"private static readonly pb::MessageParser<$class_name$> _parser = new "
|
|
"pb::MessageParser<$class_name$>(() => new $class_name$());\n");
|
|
|
|
printer->Print("private pb::UnknownFieldSet _unknownFields;\n");
|
|
|
|
if (has_extension_ranges_) {
|
|
if (IsDescriptorProto(descriptor_->file())) {
|
|
printer->Print(vars,
|
|
// CustomOptions compatibility
|
|
"internal pb::ExtensionSet<$class_name$> _extensions;\n");
|
|
} else {
|
|
printer->Print(vars,
|
|
"private pb::ExtensionSet<$class_name$> _extensions;\n");
|
|
}
|
|
|
|
// a read-only property for fast
|
|
// retrieval of the set in IsInitialized
|
|
printer->Print(vars,
|
|
"private pb::ExtensionSet<$class_name$> _Extensions { get { "
|
|
"return _extensions; } }\n");
|
|
}
|
|
|
|
for (int i = 0; i < has_bit_field_count_; i++) {
|
|
// don't use arrays since all arrays are heap allocated, saving allocations
|
|
// use ints instead of bytes since bytes lack bitwise operators, saving
|
|
// casts
|
|
printer->Print("private int _hasBits$i$;\n", "i", absl::StrCat(i));
|
|
}
|
|
|
|
WriteGeneratedCodeAttributes(printer);
|
|
|
|
printer->Print(vars,
|
|
"public static pb::MessageParser<$class_name$> Parser { get { "
|
|
"return _parser; } }\n\n");
|
|
|
|
// Access the message descriptor via the relevant file descriptor or
|
|
// containing message descriptor.
|
|
if (!descriptor_->containing_type()) {
|
|
vars["descriptor_accessor"] =
|
|
absl::StrCat(GetReflectionClassName(descriptor_->file()),
|
|
".Descriptor.MessageTypes[", descriptor_->index(), "]");
|
|
} else {
|
|
vars["descriptor_accessor"] =
|
|
absl::StrCat(GetClassName(descriptor_->containing_type()),
|
|
".Descriptor.NestedTypes[", descriptor_->index(), "]");
|
|
}
|
|
|
|
WriteGeneratedCodeAttributes(printer);
|
|
printer->Print(vars,
|
|
"public static pbr::MessageDescriptor Descriptor {\n"
|
|
" get { return $descriptor_accessor$; }\n"
|
|
"}\n"
|
|
"\n");
|
|
WriteGeneratedCodeAttributes(printer);
|
|
printer->Print(vars,
|
|
"pbr::MessageDescriptor pb::IMessage.Descriptor {\n"
|
|
" get { return Descriptor; }\n"
|
|
"}\n"
|
|
"\n");
|
|
|
|
// Parameterless constructor and partial OnConstruction method.
|
|
WriteGeneratedCodeAttributes(printer);
|
|
printer->Print(vars,
|
|
"public $class_name$() {\n"
|
|
" OnConstruction();\n"
|
|
"}\n\n"
|
|
"partial void OnConstruction();\n\n");
|
|
|
|
GenerateCloningCode(printer);
|
|
GenerateFreezingCode(printer);
|
|
|
|
// Fields/properties
|
|
for (int i = 0; i < descriptor_->field_count(); i++) {
|
|
const FieldDescriptor* fieldDescriptor = descriptor_->field(i);
|
|
|
|
// Rats: we lose the debug comment here :(
|
|
printer->Print(
|
|
"/// <summary>Field number for the \"$field_name$\" field.</summary>\n"
|
|
"public const int $field_constant_name$ = $index$;\n",
|
|
"field_name", fieldDescriptor->name(), "field_constant_name",
|
|
GetFieldConstantName(fieldDescriptor), "index",
|
|
absl::StrCat(fieldDescriptor->number()));
|
|
std::unique_ptr<FieldGeneratorBase> generator(
|
|
CreateFieldGeneratorInternal(fieldDescriptor));
|
|
generator->GenerateMembers(printer);
|
|
printer->Print("\n");
|
|
}
|
|
|
|
// oneof properties (for real oneofs, which come before synthetic ones)
|
|
for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++) {
|
|
const OneofDescriptor* oneof = descriptor_->oneof_decl(i);
|
|
vars["name"] = UnderscoresToCamelCase(oneof->name(), false);
|
|
vars["property_name"] = UnderscoresToCamelCase(oneof->name(), true);
|
|
vars["original_name"] = oneof->name();
|
|
printer->Print(vars,
|
|
"private object $name$_;\n"
|
|
"/// <summary>Enum of possible cases for the "
|
|
"\"$original_name$\" oneof.</summary>\n"
|
|
"public enum $property_name$OneofCase {\n");
|
|
printer->Indent();
|
|
printer->Print("None = 0,\n");
|
|
for (int j = 0; j < oneof->field_count(); j++) {
|
|
const FieldDescriptor* field = oneof->field(j);
|
|
printer->Print("$oneof_case_name$ = $index$,\n", "oneof_case_name",
|
|
GetOneofCaseName(field), "index",
|
|
absl::StrCat(field->number()));
|
|
}
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
// TODO: Should we put the oneof .proto comments here?
|
|
// It's unclear exactly where they should go.
|
|
printer->Print(vars,
|
|
"private $property_name$OneofCase $name$Case_ = "
|
|
"$property_name$OneofCase.None;\n");
|
|
WriteGeneratedCodeAttributes(printer);
|
|
printer->Print(vars,
|
|
"public $property_name$OneofCase $property_name$Case {\n"
|
|
" get { return $name$Case_; }\n"
|
|
"}\n\n");
|
|
WriteGeneratedCodeAttributes(printer);
|
|
printer->Print(vars,
|
|
"public void Clear$property_name$() {\n"
|
|
" $name$Case_ = $property_name$OneofCase.None;\n"
|
|
" $name$_ = null;\n"
|
|
"}\n\n");
|
|
}
|
|
|
|
// Standard methods
|
|
GenerateFrameworkMethods(printer);
|
|
GenerateMessageSerializationMethods(printer);
|
|
GenerateMergingMethods(printer);
|
|
|
|
if (has_extension_ranges_) {
|
|
printer->Print(
|
|
vars,
|
|
"public TValue GetExtension<TValue>(pb::Extension<$class_name$, "
|
|
"TValue> extension) {\n"
|
|
" return pb::ExtensionSet.Get(ref _extensions, extension);\n"
|
|
"}\n"
|
|
"public pbc::RepeatedField<TValue> "
|
|
"GetExtension<TValue>(pb::RepeatedExtension<$class_name$, TValue> "
|
|
"extension) {\n"
|
|
" return pb::ExtensionSet.Get(ref _extensions, extension);\n"
|
|
"}\n"
|
|
"public pbc::RepeatedField<TValue> "
|
|
"GetOrInitializeExtension<TValue>(pb::RepeatedExtension<$class_name$, "
|
|
"TValue> extension) {\n"
|
|
" return pb::ExtensionSet.GetOrInitialize(ref _extensions, "
|
|
"extension);\n"
|
|
"}\n"
|
|
"public void SetExtension<TValue>(pb::Extension<$class_name$, TValue> "
|
|
"extension, TValue value) {\n"
|
|
" pb::ExtensionSet.Set(ref _extensions, extension, value);\n"
|
|
"}\n"
|
|
"public bool HasExtension<TValue>(pb::Extension<$class_name$, TValue> "
|
|
"extension) {\n"
|
|
" return pb::ExtensionSet.Has(ref _extensions, extension);\n"
|
|
"}\n"
|
|
"public void ClearExtension<TValue>(pb::Extension<$class_name$, "
|
|
"TValue> extension) {\n"
|
|
" pb::ExtensionSet.Clear(ref _extensions, extension);\n"
|
|
"}\n"
|
|
"public void "
|
|
"ClearExtension<TValue>(pb::RepeatedExtension<$class_name$, TValue> "
|
|
"extension) {\n"
|
|
" pb::ExtensionSet.Clear(ref _extensions, extension);\n"
|
|
"}\n\n");
|
|
}
|
|
|
|
// Nested messages and enums
|
|
if (HasNestedGeneratedTypes()) {
|
|
printer->Print(vars,
|
|
"#region Nested types\n"
|
|
"/// <summary>Container for nested types declared in the "
|
|
"$class_name$ message type.</summary>\n");
|
|
WriteGeneratedCodeAttributes(printer);
|
|
printer->Print("public static partial class Types {\n");
|
|
printer->Indent();
|
|
for (int i = 0; i < descriptor_->enum_type_count(); i++) {
|
|
EnumGenerator enumGenerator(descriptor_->enum_type(i), this->options());
|
|
enumGenerator.Generate(printer);
|
|
}
|
|
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
|
|
// Don't generate nested types for maps...
|
|
if (!IsMapEntryMessage(descriptor_->nested_type(i))) {
|
|
MessageGenerator messageGenerator(descriptor_->nested_type(i),
|
|
this->options());
|
|
messageGenerator.Generate(printer);
|
|
}
|
|
}
|
|
printer->Outdent();
|
|
printer->Print(
|
|
"}\n"
|
|
"#endregion\n"
|
|
"\n");
|
|
}
|
|
|
|
if (descriptor_->extension_count() > 0) {
|
|
printer->Print(vars,
|
|
"#region Extensions\n"
|
|
"/// <summary>Container for extensions for other messages "
|
|
"declared in the $class_name$ message type.</summary>\n");
|
|
WriteGeneratedCodeAttributes(printer);
|
|
printer->Print("public static partial class Extensions {\n");
|
|
printer->Indent();
|
|
for (int i = 0; i < descriptor_->extension_count(); i++) {
|
|
std::unique_ptr<FieldGeneratorBase> generator(
|
|
CreateFieldGeneratorInternal(descriptor_->extension(i)));
|
|
generator->GenerateExtensionCode(printer);
|
|
}
|
|
printer->Outdent();
|
|
printer->Print(
|
|
"}\n"
|
|
"#endregion\n"
|
|
"\n");
|
|
}
|
|
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
printer->Print("\n");
|
|
}
|
|
|
|
// Helper to work out whether we need to generate a class to hold nested
|
|
// types/enums. Only tricky because we don't want to generate map entry types.
|
|
bool MessageGenerator::HasNestedGeneratedTypes() {
|
|
if (descriptor_->enum_type_count() > 0) {
|
|
return true;
|
|
}
|
|
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
|
|
if (!IsMapEntryMessage(descriptor_->nested_type(i))) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void MessageGenerator::GenerateCloningCode(io::Printer* printer) {
|
|
absl::flat_hash_map<absl::string_view, std::string> vars;
|
|
WriteGeneratedCodeAttributes(printer);
|
|
vars["class_name"] = class_name();
|
|
printer->Print(vars, "public $class_name$($class_name$ other) : this() {\n");
|
|
printer->Indent();
|
|
for (int i = 0; i < has_bit_field_count_; i++) {
|
|
printer->Print("_hasBits$i$ = other._hasBits$i$;\n", "i", absl::StrCat(i));
|
|
}
|
|
// Clone non-oneof fields first (treating optional proto3 fields as non-oneof)
|
|
for (int i = 0; i < descriptor_->field_count(); i++) {
|
|
const FieldDescriptor* field = descriptor_->field(i);
|
|
if (field->real_containing_oneof()) {
|
|
continue;
|
|
}
|
|
std::unique_ptr<FieldGeneratorBase> generator(
|
|
CreateFieldGeneratorInternal(field));
|
|
generator->GenerateCloningCode(printer);
|
|
}
|
|
// Clone just the right field for each real oneof
|
|
for (int i = 0; i < descriptor_->real_oneof_decl_count(); ++i) {
|
|
const OneofDescriptor* oneof = descriptor_->oneof_decl(i);
|
|
vars["name"] = UnderscoresToCamelCase(oneof->name(), false);
|
|
vars["property_name"] = UnderscoresToCamelCase(oneof->name(), true);
|
|
printer->Print(vars, "switch (other.$property_name$Case) {\n");
|
|
printer->Indent();
|
|
for (int j = 0; j < oneof->field_count(); j++) {
|
|
const FieldDescriptor* field = oneof->field(j);
|
|
std::unique_ptr<FieldGeneratorBase> generator(
|
|
CreateFieldGeneratorInternal(field));
|
|
vars["oneof_case_name"] = GetOneofCaseName(field);
|
|
printer->Print(vars,
|
|
"case $property_name$OneofCase.$oneof_case_name$:\n");
|
|
printer->Indent();
|
|
generator->GenerateCloningCode(printer);
|
|
printer->Print("break;\n");
|
|
printer->Outdent();
|
|
}
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
// Clone unknown fields
|
|
printer->Print(
|
|
"_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);\n");
|
|
if (has_extension_ranges_) {
|
|
printer->Print(
|
|
"_extensions = pb::ExtensionSet.Clone(other._extensions);\n");
|
|
}
|
|
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
|
|
WriteGeneratedCodeAttributes(printer);
|
|
printer->Print(vars,
|
|
"public $class_name$ Clone() {\n"
|
|
" return new $class_name$(this);\n"
|
|
"}\n\n");
|
|
}
|
|
|
|
void MessageGenerator::GenerateFreezingCode(io::Printer* printer) {}
|
|
|
|
void MessageGenerator::GenerateFrameworkMethods(io::Printer* printer) {
|
|
absl::flat_hash_map<absl::string_view, std::string> vars;
|
|
vars["class_name"] = class_name();
|
|
|
|
// Equality
|
|
WriteGeneratedCodeAttributes(printer);
|
|
printer->Print(vars,
|
|
"public override bool Equals(object other) {\n"
|
|
" return Equals(other as $class_name$);\n"
|
|
"}\n\n");
|
|
WriteGeneratedCodeAttributes(printer);
|
|
printer->Print(vars,
|
|
"public bool Equals($class_name$ other) {\n"
|
|
" if (ReferenceEquals(other, null)) {\n"
|
|
" return false;\n"
|
|
" }\n"
|
|
" if (ReferenceEquals(other, this)) {\n"
|
|
" return true;\n"
|
|
" }\n");
|
|
printer->Indent();
|
|
for (int i = 0; i < descriptor_->field_count(); i++) {
|
|
std::unique_ptr<FieldGeneratorBase> generator(
|
|
CreateFieldGeneratorInternal(descriptor_->field(i)));
|
|
generator->WriteEquals(printer);
|
|
}
|
|
for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++) {
|
|
printer->Print(
|
|
"if ($property_name$Case != other.$property_name$Case) return false;\n",
|
|
"property_name",
|
|
UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true));
|
|
}
|
|
if (has_extension_ranges_) {
|
|
printer->Print(
|
|
"if (!Equals(_extensions, other._extensions)) {\n"
|
|
" return false;\n"
|
|
"}\n");
|
|
}
|
|
printer->Outdent();
|
|
printer->Print(
|
|
" return Equals(_unknownFields, other._unknownFields);\n"
|
|
"}\n\n");
|
|
|
|
// GetHashCode
|
|
// Start with a non-zero value to easily distinguish between null and "empty"
|
|
// messages.
|
|
WriteGeneratedCodeAttributes(printer);
|
|
printer->Print(
|
|
"public override int GetHashCode() {\n"
|
|
" int hash = 1;\n");
|
|
printer->Indent();
|
|
for (int i = 0; i < descriptor_->field_count(); i++) {
|
|
std::unique_ptr<FieldGeneratorBase> generator(
|
|
CreateFieldGeneratorInternal(descriptor_->field(i)));
|
|
generator->WriteHash(printer);
|
|
}
|
|
for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++) {
|
|
printer->Print(
|
|
"hash ^= (int) $name$Case_;\n", "name",
|
|
UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false));
|
|
}
|
|
if (has_extension_ranges_) {
|
|
printer->Print(
|
|
"if (_extensions != null) {\n"
|
|
" hash ^= _extensions.GetHashCode();\n"
|
|
"}\n");
|
|
}
|
|
printer->Print(
|
|
"if (_unknownFields != null) {\n"
|
|
" hash ^= _unknownFields.GetHashCode();\n"
|
|
"}\n"
|
|
"return hash;\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
|
|
WriteGeneratedCodeAttributes(printer);
|
|
printer->Print(
|
|
"public override string ToString() {\n"
|
|
" return pb::JsonFormatter.ToDiagnosticString(this);\n"
|
|
"}\n\n");
|
|
}
|
|
|
|
void MessageGenerator::GenerateMessageSerializationMethods(
|
|
io::Printer* printer) {
|
|
WriteGeneratedCodeAttributes(printer);
|
|
printer->Print("public void WriteTo(pb::CodedOutputStream output) {\n");
|
|
printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
|
|
printer->Indent();
|
|
printer->Print("output.WriteRawMessage(this);\n");
|
|
printer->Outdent();
|
|
printer->Print("#else\n");
|
|
printer->Indent();
|
|
GenerateWriteToBody(printer, false);
|
|
printer->Outdent();
|
|
printer->Print("#endif\n");
|
|
printer->Print("}\n\n");
|
|
|
|
printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
|
|
WriteGeneratedCodeAttributes(printer);
|
|
printer->Print(
|
|
"void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) "
|
|
"{\n");
|
|
printer->Indent();
|
|
GenerateWriteToBody(printer, true);
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
printer->Print("#endif\n\n");
|
|
|
|
WriteGeneratedCodeAttributes(printer);
|
|
printer->Print("public int CalculateSize() {\n");
|
|
printer->Indent();
|
|
printer->Print("int size = 0;\n");
|
|
for (int i = 0; i < descriptor_->field_count(); i++) {
|
|
std::unique_ptr<FieldGeneratorBase> generator(
|
|
CreateFieldGeneratorInternal(descriptor_->field(i)));
|
|
generator->GenerateSerializedSizeCode(printer);
|
|
}
|
|
|
|
if (has_extension_ranges_) {
|
|
printer->Print(
|
|
"if (_extensions != null) {\n"
|
|
" size += _extensions.CalculateSize();\n"
|
|
"}\n");
|
|
}
|
|
|
|
printer->Print(
|
|
"if (_unknownFields != null) {\n"
|
|
" size += _unknownFields.CalculateSize();\n"
|
|
"}\n");
|
|
|
|
printer->Print("return size;\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
|
|
void MessageGenerator::GenerateWriteToBody(io::Printer* printer,
|
|
bool use_write_context) {
|
|
// Serialize all the fields
|
|
for (int i = 0; i < fields_by_number().size(); i++) {
|
|
std::unique_ptr<FieldGeneratorBase> generator(
|
|
CreateFieldGeneratorInternal(fields_by_number()[i]));
|
|
generator->GenerateSerializationCode(printer, use_write_context);
|
|
}
|
|
|
|
if (has_extension_ranges_) {
|
|
// Serialize extensions
|
|
printer->Print(use_write_context ? "if (_extensions != null) {\n"
|
|
" _extensions.WriteTo(ref output);\n"
|
|
"}\n"
|
|
: "if (_extensions != null) {\n"
|
|
" _extensions.WriteTo(output);\n"
|
|
"}\n");
|
|
}
|
|
|
|
// Serialize unknown fields
|
|
printer->Print(use_write_context ? "if (_unknownFields != null) {\n"
|
|
" _unknownFields.WriteTo(ref output);\n"
|
|
"}\n"
|
|
: "if (_unknownFields != null) {\n"
|
|
" _unknownFields.WriteTo(output);\n"
|
|
"}\n");
|
|
|
|
// TODO: Memoize size of frozen messages?
|
|
}
|
|
|
|
void MessageGenerator::GenerateMergingMethods(io::Printer* printer) {
|
|
// Note: These are separate from GenerateMessageSerializationMethods()
|
|
// because they need to be generated even for messages that are optimized
|
|
// for code size.
|
|
absl::flat_hash_map<absl::string_view, std::string> vars;
|
|
vars["class_name"] = class_name();
|
|
|
|
WriteGeneratedCodeAttributes(printer);
|
|
printer->Print(vars, "public void MergeFrom($class_name$ other) {\n");
|
|
printer->Indent();
|
|
printer->Print(
|
|
"if (other == null) {\n"
|
|
" return;\n"
|
|
"}\n");
|
|
// Merge non-oneof fields, treating optional proto3 fields as normal fields
|
|
for (int i = 0; i < descriptor_->field_count(); i++) {
|
|
const FieldDescriptor* field = descriptor_->field(i);
|
|
if (field->real_containing_oneof()) {
|
|
continue;
|
|
}
|
|
std::unique_ptr<FieldGeneratorBase> generator(
|
|
CreateFieldGeneratorInternal(field));
|
|
generator->GenerateMergingCode(printer);
|
|
}
|
|
// Merge oneof fields (for non-synthetic oneofs)
|
|
for (int i = 0; i < descriptor_->real_oneof_decl_count(); ++i) {
|
|
const OneofDescriptor* oneof = descriptor_->oneof_decl(i);
|
|
vars["name"] = UnderscoresToCamelCase(oneof->name(), false);
|
|
vars["property_name"] = UnderscoresToCamelCase(oneof->name(), true);
|
|
printer->Print(vars, "switch (other.$property_name$Case) {\n");
|
|
printer->Indent();
|
|
for (int j = 0; j < oneof->field_count(); j++) {
|
|
const FieldDescriptor* field = oneof->field(j);
|
|
vars["oneof_case_name"] = GetOneofCaseName(field);
|
|
printer->Print(vars,
|
|
"case $property_name$OneofCase.$oneof_case_name$:\n");
|
|
printer->Indent();
|
|
std::unique_ptr<FieldGeneratorBase> generator(
|
|
CreateFieldGeneratorInternal(field));
|
|
generator->GenerateMergingCode(printer);
|
|
printer->Print("break;\n");
|
|
printer->Outdent();
|
|
}
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
// Merge extensions
|
|
if (has_extension_ranges_) {
|
|
printer->Print(
|
|
"pb::ExtensionSet.MergeFrom(ref _extensions, other._extensions);\n");
|
|
}
|
|
|
|
// Merge unknown fields.
|
|
printer->Print(
|
|
"_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, "
|
|
"other._unknownFields);\n");
|
|
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
|
|
WriteGeneratedCodeAttributes(printer);
|
|
printer->Print("public void MergeFrom(pb::CodedInputStream input) {\n");
|
|
printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
|
|
printer->Indent();
|
|
printer->Print("input.ReadRawMessage(this);\n");
|
|
printer->Outdent();
|
|
printer->Print("#else\n");
|
|
printer->Indent();
|
|
GenerateMainParseLoop(printer, false);
|
|
printer->Outdent();
|
|
printer->Print("#endif\n");
|
|
printer->Print("}\n\n");
|
|
|
|
printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
|
|
WriteGeneratedCodeAttributes(printer);
|
|
printer->Print(
|
|
"void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) "
|
|
"{\n");
|
|
printer->Indent();
|
|
GenerateMainParseLoop(printer, true);
|
|
printer->Outdent();
|
|
printer->Print("}\n"); // method
|
|
printer->Print("#endif\n\n");
|
|
}
|
|
|
|
void MessageGenerator::GenerateMainParseLoop(io::Printer* printer,
|
|
bool use_parse_context) {
|
|
absl::flat_hash_map<absl::string_view, std::string> vars;
|
|
vars["maybe_ref_input"] = use_parse_context ? "ref input" : "input";
|
|
|
|
printer->Emit(R"csharp(
|
|
uint tag;
|
|
while ((tag = input.ReadTag()) != 0) {
|
|
if ((tag & 7) == 4) {
|
|
// Abort on any end group tag.
|
|
return;
|
|
}
|
|
switch(tag) {
|
|
)csharp");
|
|
printer->Indent();
|
|
printer->Indent();
|
|
if (has_extension_ranges_) {
|
|
printer->Print(vars,
|
|
"default:\n"
|
|
" if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, "
|
|
"$maybe_ref_input$)) {\n"
|
|
" _unknownFields = "
|
|
"pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, "
|
|
"$maybe_ref_input$);\n"
|
|
" }\n"
|
|
" break;\n");
|
|
} else {
|
|
printer->Print(
|
|
vars,
|
|
"default:\n"
|
|
" _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, "
|
|
"$maybe_ref_input$);\n"
|
|
" break;\n");
|
|
}
|
|
for (int i = 0; i < fields_by_number().size(); i++) {
|
|
const FieldDescriptor* field = fields_by_number()[i];
|
|
internal::WireFormatLite::WireType wt =
|
|
internal::WireFormat::WireTypeForFieldType(field->type());
|
|
uint32_t tag = internal::WireFormatLite::MakeTag(field->number(), wt);
|
|
// Handle both packed and unpacked repeated fields with the same Read*Array
|
|
// call; the two generated cases are the packed and unpacked tags.
|
|
// TODO: Check that is_packable is equivalent to
|
|
// is_repeated && wt in { VARINT, FIXED32, FIXED64 }.
|
|
// It looks like it is...
|
|
if (field->is_packable()) {
|
|
printer->Print("case $packed_tag$:\n", "packed_tag",
|
|
absl::StrCat(internal::WireFormatLite::MakeTag(
|
|
field->number(),
|
|
internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED)));
|
|
}
|
|
|
|
printer->Print("case $tag$: {\n", "tag", absl::StrCat(tag));
|
|
printer->Indent();
|
|
std::unique_ptr<FieldGeneratorBase> generator(
|
|
CreateFieldGeneratorInternal(field));
|
|
generator->GenerateParsingCode(printer, use_parse_context);
|
|
printer->Print("break;\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
}
|
|
printer->Outdent();
|
|
printer->Print("}\n"); // switch
|
|
printer->Outdent();
|
|
printer->Print("}\n"); // while
|
|
}
|
|
|
|
// it's a waste of space to track presence for all values, so we only track them
|
|
// if they're not nullable
|
|
int MessageGenerator::GetPresenceIndex(const FieldDescriptor* descriptor) {
|
|
if (!RequiresPresenceBit(descriptor)) {
|
|
return -1;
|
|
}
|
|
|
|
int index = 0;
|
|
for (int i = 0; i < fields_by_number().size(); i++) {
|
|
const FieldDescriptor* field = fields_by_number()[i];
|
|
if (field == descriptor) {
|
|
return index;
|
|
}
|
|
if (RequiresPresenceBit(field)) {
|
|
index++;
|
|
}
|
|
}
|
|
ABSL_DLOG(FATAL) << "Could not find presence index for field "
|
|
<< descriptor->name();
|
|
return -1;
|
|
}
|
|
|
|
FieldGeneratorBase* MessageGenerator::CreateFieldGeneratorInternal(
|
|
const FieldDescriptor* descriptor) {
|
|
return CreateFieldGenerator(descriptor, GetPresenceIndex(descriptor),
|
|
this->options());
|
|
}
|
|
|
|
} // namespace csharp
|
|
} // namespace compiler
|
|
} // namespace protobuf
|
|
} // namespace google
|
|
|
|
#include "google/protobuf/port_undef.inc"
|