From 493e3db98532e99a53cec914d8f4827a556ee408 Mon Sep 17 00:00:00 2001
From: Jon Skeet <jonskeet@google.com>
Date: Wed, 1 Jul 2015 17:11:17 +0100
Subject: [PATCH] Codegen changes to support descriptor runtime changes

- Add a partial method called by all constructors
- Generate internal classes for descriptor.proto (only)
- Forbid proto2 descriptors except for descriptor.proto
---
 src/google/protobuf/compiler/csharp/csharp_field_base.cc | 1 +
 src/google/protobuf/compiler/csharp/csharp_generator.cc  | 6 ++++++
 src/google/protobuf/compiler/csharp/csharp_helpers.h     | 9 +++++++++
 src/google/protobuf/compiler/csharp/csharp_map_field.cc  | 2 +-
 src/google/protobuf/compiler/csharp/csharp_message.cc    | 9 ++++++---
 .../protobuf/compiler/csharp/csharp_message_field.cc     | 4 ++--
 .../protobuf/compiler/csharp/csharp_primitive_field.cc   | 4 ++--
 .../compiler/csharp/csharp_repeated_enum_field.cc        | 2 +-
 .../compiler/csharp/csharp_repeated_message_field.cc     | 2 +-
 .../compiler/csharp/csharp_repeated_primitive_field.cc   | 2 +-
 .../compiler/csharp/csharp_source_generator_base.cc      | 2 +-
 11 files changed, 31 insertions(+), 12 deletions(-)

diff --git a/src/google/protobuf/compiler/csharp/csharp_field_base.cc b/src/google/protobuf/compiler/csharp/csharp_field_base.cc
index bfb39a64f4..ae7336ca83 100644
--- a/src/google/protobuf/compiler/csharp/csharp_field_base.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_field_base.cc
@@ -65,6 +65,7 @@ void FieldGeneratorBase::SetCommonFieldVariables(
     tag_bytes += ", " + SimpleItoa(tag_array[i]);
   }
 
+  (*variables)["access_level"] = class_access_level();
   (*variables)["tag"] = SimpleItoa(tag);
   (*variables)["tag_size"] = SimpleItoa(tag_size);
   (*variables)["tag_bytes"] = tag_bytes;
diff --git a/src/google/protobuf/compiler/csharp/csharp_generator.cc b/src/google/protobuf/compiler/csharp/csharp_generator.cc
index 2a416faccb..f06d79d8d5 100644
--- a/src/google/protobuf/compiler/csharp/csharp_generator.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_generator.cc
@@ -68,6 +68,12 @@ bool Generator::Generate(
   vector<pair<string, string> > options;
   ParseGeneratorParameter(parameter, &options);
 
+  // We only support proto3 - but we make an exception for descriptor.proto.
+  if (file->syntax() != FileDescriptor::SYNTAX_PROTO3 && !IsDescriptorProto(file)) {
+    *error = "C# code generation only supports proto3 syntax";
+    return false;
+  }
+
   std::string file_extension = ".cs";
   for (int i = 0; i < options.size(); i++) {
     if (options[i].first == "file_extension") {
diff --git a/src/google/protobuf/compiler/csharp/csharp_helpers.h b/src/google/protobuf/compiler/csharp/csharp_helpers.h
index a8c1fa1884..d9576fbd89 100644
--- a/src/google/protobuf/compiler/csharp/csharp_helpers.h
+++ b/src/google/protobuf/compiler/csharp/csharp_helpers.h
@@ -101,6 +101,15 @@ uint FixedMakeTag(const FieldDescriptor* descriptor);
 
 FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal);
 
+// Determines whether we're generating code for the proto representation of descriptors etc,
+// for use in the runtime. This is the only type which is allowed to use proto2 syntax,
+// and it generates internal classes.
+inline bool IsDescriptorProto(const FileDescriptor* descriptor) {
+  // TODO: Do this better! (Currently this depends on a hack in generate_protos.sh to rename
+  // the file...)
+  return descriptor->name() == "google/protobuf/descriptor_proto_file.proto";
+}
+
 }  // namespace csharp
 }  // namespace compiler
 }  // namespace protobuf
diff --git a/src/google/protobuf/compiler/csharp/csharp_map_field.cc b/src/google/protobuf/compiler/csharp/csharp_map_field.cc
index b8f1592c2a..cb7ce5f006 100644
--- a/src/google/protobuf/compiler/csharp/csharp_map_field.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_map_field.cc
@@ -78,7 +78,7 @@ void MapFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   printer->Print(
     variables_,
-    "public pbc::MapField<$key_type_name$, $value_type_name$> $property_name$ {\n"
+    "$access_level$ pbc::MapField<$key_type_name$, $value_type_name$> $property_name$ {\n"
     "  get { return $name$_; }\n"
     "}\n");
 }
diff --git a/src/google/protobuf/compiler/csharp/csharp_message.cc b/src/google/protobuf/compiler/csharp/csharp_message.cc
index c7a1e290ac..13544b2681 100644
--- a/src/google/protobuf/compiler/csharp/csharp_message.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_message.cc
@@ -221,10 +221,13 @@ void MessageGenerator::Generate(io::Printer* printer) {
     "private bool _frozen = false;\n"
     "public bool IsFrozen { get { return _frozen; } }\n\n");
 
-  // Parameterless constructor
+  // Parameterless constructor and partial OnConstruction method.
   printer->Print(
     vars,
-    "public $class_name$() { }\n\n");
+    "public $class_name$() {\n"
+    "  OnConstruction();\n"
+    "}\n\n"
+    "partial void OnConstruction();\n\n");
 
   GenerateCloningCode(printer);
   GenerateFreezingCode(printer);
@@ -311,7 +314,7 @@ void MessageGenerator::GenerateCloningCode(io::Printer* printer) {
   vars["class_name"] = class_name();
     printer->Print(
     vars,
-    "public $class_name$($class_name$ other) {\n");
+    "public $class_name$($class_name$ other) : this() {\n");
   printer->Indent();
   // Clone non-oneof fields first
   for (int i = 0; i < descriptor_->field_count(); i++) {
diff --git a/src/google/protobuf/compiler/csharp/csharp_message_field.cc b/src/google/protobuf/compiler/csharp/csharp_message_field.cc
index d8c82271f3..d2c3a88b48 100644
--- a/src/google/protobuf/compiler/csharp/csharp_message_field.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_message_field.cc
@@ -64,7 +64,7 @@ void MessageFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   printer->Print(
     variables_,
-    "public $type_name$ $property_name$ {\n"
+    "$access_level$ $type_name$ $property_name$ {\n"
     "  get { return $name$_; }\n"
     "  set {\n"
     "    pb::Freezable.CheckMutable(this);\n"
@@ -158,7 +158,7 @@ void MessageOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   printer->Print(
     variables_,
-    "public $type_name$ $property_name$ {\n"
+    "$access_level$ $type_name$ $property_name$ {\n"
     "  get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : null; }\n"
     "  set {\n"
     "    pb::Freezable.CheckMutable(this);\n"
diff --git a/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
index c40cba13af..4454ef029f 100644
--- a/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
@@ -71,7 +71,7 @@ void PrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   printer->Print(
     variables_,
-    "public $type_name$ $property_name$ {\n"
+    "$access_level$ $type_name$ $property_name$ {\n"
     "  get { return $name$_; }\n"
     "  set {\n"
     "    pb::Freezable.CheckMutable(this);\n");
@@ -174,7 +174,7 @@ void PrimitiveOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   printer->Print(
     variables_,
-    "public $type_name$ $property_name$ {\n"
+    "$access_level$ $type_name$ $property_name$ {\n"
     "  get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : $default_value$; }\n"
     "  set {\n"
     "    pb::Freezable.CheckMutable(this);\n");
diff --git a/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc b/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc
index 4445c28105..d5fc6d9820 100644
--- a/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc
@@ -65,7 +65,7 @@ void RepeatedEnumFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   printer->Print(
     variables_,
-    "public pbc::RepeatedField<$type_name$> $property_name$ {\n"
+    "$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n"
     "  get { return $name$_; }\n"
     "}\n");
 }
diff --git a/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc b/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc
index c74e5d0d76..6d4e698434 100644
--- a/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc
@@ -65,7 +65,7 @@ void RepeatedMessageFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   printer->Print(
     variables_,
-    "public pbc::RepeatedField<$type_name$> $property_name$ {\n"
+    "$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n"
     "  get { return $name$_; }\n"
     "}\n");
 }
diff --git a/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc b/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc
index a3014da815..5b5d9b3d9b 100644
--- a/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc
@@ -65,7 +65,7 @@ void RepeatedPrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   printer->Print(
     variables_,
-    "public pbc::RepeatedField<$type_name$> $property_name$ {\n"
+    "$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n"
     "  get { return $name$_; }\n"
     "}\n");
 }
diff --git a/src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc b/src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc
index c666c82007..735d164a48 100644
--- a/src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc
@@ -57,7 +57,7 @@ void SourceGeneratorBase::WriteGeneratedCodeAttributes(io::Printer* printer) {
 }
 
 std::string SourceGeneratorBase::class_access_level() {
-  return "public";  // public_classes is always on.
+  return IsDescriptorProto(descriptor_) ? "internal" : "public";  // public_classes is always on.
 }
 
 }  // namespace csharp