Browse Source

Implement freezing for messages and repeated fields.

Fixes issue #523.
Jon Skeet 10 năm trước cách đây
mục cha
commit
bfee2dfe13
25 tập tin đã thay đổi với 1192 bổ sung229 xóa
  1. 52 10
      csharp/src/AddressBook/Addressbook.cs
  2. 19 1
      csharp/src/ProtocolBuffers.Test/GeneratedMessageTest.cs
  3. 45 2
      csharp/src/ProtocolBuffers.Test/RepeatedFieldTest.cs
  4. 14 2
      csharp/src/ProtocolBuffers.Test/TestProtos/UnittestImportProto3.cs
  5. 14 2
      csharp/src/ProtocolBuffers.Test/TestProtos/UnittestImportPublicProto3.cs
  6. 68 10
      csharp/src/ProtocolBuffers.Test/TestProtos/UnittestIssues.cs
  7. 388 88
      csharp/src/ProtocolBuffers.Test/TestProtos/UnittestProto3.cs
  8. 33 2
      csharp/src/ProtocolBuffers/Collections/RepeatedField.cs
  9. 381 103
      csharp/src/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs
  10. 60 0
      csharp/src/ProtocolBuffers/Freezable.cs
  11. 35 1
      csharp/src/ProtocolBuffers/IMessage.cs
  12. 1 0
      csharp/src/ProtocolBuffers/ProtocolBuffers.csproj
  13. 5 0
      src/google/protobuf/compiler/csharp/csharp_field_base.cc
  14. 1 0
      src/google/protobuf/compiler/csharp/csharp_field_base.h
  15. 35 1
      src/google/protobuf/compiler/csharp/csharp_message.cc
  16. 1 0
      src/google/protobuf/compiler/csharp/csharp_message.h
  17. 11 2
      src/google/protobuf/compiler/csharp/csharp_message_field.cc
  18. 1 0
      src/google/protobuf/compiler/csharp/csharp_message_field.h
  19. 10 5
      src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
  20. 5 0
      src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc
  21. 1 0
      src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.h
  22. 5 0
      src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc
  23. 1 0
      src/google/protobuf/compiler/csharp/csharp_repeated_message_field.h
  24. 5 0
      src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc
  25. 1 0
      src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h

+ 52 - 10
csharp/src/AddressBook/Addressbook.cs

@@ -76,6 +76,9 @@ namespace Google.ProtocolBuffers.Examples.AddressBook {
       get { return global::Google.ProtocolBuffers.Examples.AddressBook.Addressbook.internal__static_tutorial_Person__FieldAccessorTable; }
       get { return global::Google.ProtocolBuffers.Examples.AddressBook.Addressbook.internal__static_tutorial_Person__FieldAccessorTable; }
     }
     }
 
 
+    private bool _frozen = false;
+    public bool IsFrozen { get { return _frozen; } }
+
     public Person() { }
     public Person() { }
 
 
     public Person(Person other) {
     public Person(Person other) {
@@ -89,30 +92,44 @@ namespace Google.ProtocolBuffers.Examples.AddressBook {
       return new Person(this);
       return new Person(this);
     }
     }
 
 
+    public void Freeze() {
+      if (IsFrozen) {
+        return;
+      }
+      _frozen = true;
+      phone_.Freeze();
+    }
+
     public const int NameFieldNumber = 1;
     public const int NameFieldNumber = 1;
     private string name_ = "";
     private string name_ = "";
     public string Name {
     public string Name {
       get { return name_; }
       get { return name_; }
-      set { name_ = value ?? ""; }
+      set {
+        pb::Freezable.CheckMutable(this);
+        name_ = value ?? "";
+      }
     }
     }
 
 
-
     public const int IdFieldNumber = 2;
     public const int IdFieldNumber = 2;
     private int id_;
     private int id_;
     public int Id {
     public int Id {
       get { return id_; }
       get { return id_; }
-      set { id_ = value; }
+      set {
+        pb::Freezable.CheckMutable(this);
+        id_ = value;
+      }
     }
     }
 
 
-
     public const int EmailFieldNumber = 3;
     public const int EmailFieldNumber = 3;
     private string email_ = "";
     private string email_ = "";
     public string Email {
     public string Email {
       get { return email_; }
       get { return email_; }
-      set { email_ = value ?? ""; }
+      set {
+        pb::Freezable.CheckMutable(this);
+        email_ = value ?? "";
+      }
     }
     }
 
 
-
     public const int PhoneFieldNumber = 4;
     public const int PhoneFieldNumber = 4;
     private readonly pbc::RepeatedField<global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber> phone_ = new pbc::RepeatedField<global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber>();
     private readonly pbc::RepeatedField<global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber> phone_ = new pbc::RepeatedField<global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber>();
     public pbc::RepeatedField<global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber> Phone {
     public pbc::RepeatedField<global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber> Phone {
@@ -254,6 +271,9 @@ namespace Google.ProtocolBuffers.Examples.AddressBook {
           get { return global::Google.ProtocolBuffers.Examples.AddressBook.Addressbook.internal__static_tutorial_Person_PhoneNumber__FieldAccessorTable; }
           get { return global::Google.ProtocolBuffers.Examples.AddressBook.Addressbook.internal__static_tutorial_Person_PhoneNumber__FieldAccessorTable; }
         }
         }
 
 
+        private bool _frozen = false;
+        public bool IsFrozen { get { return _frozen; } }
+
         public PhoneNumber() { }
         public PhoneNumber() { }
 
 
         public PhoneNumber(PhoneNumber other) {
         public PhoneNumber(PhoneNumber other) {
@@ -265,22 +285,33 @@ namespace Google.ProtocolBuffers.Examples.AddressBook {
           return new PhoneNumber(this);
           return new PhoneNumber(this);
         }
         }
 
 
+        public void Freeze() {
+          if (IsFrozen) {
+            return;
+          }
+          _frozen = true;
+        }
+
         public const int NumberFieldNumber = 1;
         public const int NumberFieldNumber = 1;
         private string number_ = "";
         private string number_ = "";
         public string Number {
         public string Number {
           get { return number_; }
           get { return number_; }
-          set { number_ = value ?? ""; }
+          set {
+            pb::Freezable.CheckMutable(this);
+            number_ = value ?? "";
+          }
         }
         }
 
 
-
         public const int TypeFieldNumber = 2;
         public const int TypeFieldNumber = 2;
         private global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneType type_ = global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneType.HOME;
         private global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneType type_ = global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneType.HOME;
         public global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneType Type {
         public global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneType Type {
           get { return type_; }
           get { return type_; }
-          set { type_ = value; }
+          set {
+            pb::Freezable.CheckMutable(this);
+            type_ = value;
+          }
         }
         }
 
 
-
         public override bool Equals(object other) {
         public override bool Equals(object other) {
           return Equals(other as PhoneNumber);
           return Equals(other as PhoneNumber);
         }
         }
@@ -382,6 +413,9 @@ namespace Google.ProtocolBuffers.Examples.AddressBook {
       get { return global::Google.ProtocolBuffers.Examples.AddressBook.Addressbook.internal__static_tutorial_AddressBook__FieldAccessorTable; }
       get { return global::Google.ProtocolBuffers.Examples.AddressBook.Addressbook.internal__static_tutorial_AddressBook__FieldAccessorTable; }
     }
     }
 
 
+    private bool _frozen = false;
+    public bool IsFrozen { get { return _frozen; } }
+
     public AddressBook() { }
     public AddressBook() { }
 
 
     public AddressBook(AddressBook other) {
     public AddressBook(AddressBook other) {
@@ -392,6 +426,14 @@ namespace Google.ProtocolBuffers.Examples.AddressBook {
       return new AddressBook(this);
       return new AddressBook(this);
     }
     }
 
 
+    public void Freeze() {
+      if (IsFrozen) {
+        return;
+      }
+      _frozen = true;
+      person_.Freeze();
+    }
+
     public const int PersonFieldNumber = 1;
     public const int PersonFieldNumber = 1;
     private readonly pbc::RepeatedField<global::Google.ProtocolBuffers.Examples.AddressBook.Person> person_ = new pbc::RepeatedField<global::Google.ProtocolBuffers.Examples.AddressBook.Person>();
     private readonly pbc::RepeatedField<global::Google.ProtocolBuffers.Examples.AddressBook.Person> person_ = new pbc::RepeatedField<global::Google.ProtocolBuffers.Examples.AddressBook.Person>();
     public pbc::RepeatedField<global::Google.ProtocolBuffers.Examples.AddressBook.Person> Person {
     public pbc::RepeatedField<global::Google.ProtocolBuffers.Examples.AddressBook.Person> Person {

+ 19 - 1
csharp/src/ProtocolBuffers.Test/GeneratedMessageTest.cs

@@ -1,4 +1,5 @@
-using Google.Protobuf.TestProtos;
+using System;
+using Google.Protobuf.TestProtos;
 using NUnit.Framework;
 using NUnit.Framework;
 
 
 namespace Google.Protobuf
 namespace Google.Protobuf
@@ -257,5 +258,22 @@ namespace Google.Protobuf
             original.OneofNestedMessage.Bb = 30;
             original.OneofNestedMessage.Bb = 30;
             Assert.AreNotEqual(original, clone);
             Assert.AreNotEqual(original, clone);
         }
         }
+
+        [Test]
+        public void Freeze()
+        {
+            var frozen = new TestAllTypes();
+            frozen.Freeze();
+            Assert.IsTrue(frozen.IsFrozen);
+
+            Assert.Throws<InvalidOperationException>(() => frozen.ClearOneofField());
+            Assert.Throws<InvalidOperationException>(() => frozen.SingleInt32 = 0);
+            Assert.Throws<InvalidOperationException>(() => frozen.SingleNestedMessage = null);
+            Assert.Throws<InvalidOperationException>(() => frozen.SingleNestedEnum = 0);
+            Assert.Throws<InvalidOperationException>(() => frozen.OneofString = null);
+            Assert.Throws<InvalidOperationException>(() => frozen.OneofUint32 = 0U);
+            Assert.Throws<InvalidOperationException>(() => frozen.RepeatedDouble.Add(0.0));
+            Assert.Throws<InvalidOperationException>(() => frozen.RepeatedNestedMessage.Add(new TestAllTypes.Types.NestedMessage()));
+        }
     }
     }
 }
 }

+ 45 - 2
csharp/src/ProtocolBuffers.Test/RepeatedFieldTest.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using Google.Protobuf.Collections;
 using Google.Protobuf.Collections;
+using Google.Protobuf.TestProtos;
 using NUnit.Framework;
 using NUnit.Framework;
 
 
 namespace Google.Protobuf
 namespace Google.Protobuf
@@ -11,8 +12,8 @@ namespace Google.Protobuf
         public void NullValuesRejected()
         public void NullValuesRejected()
         {
         {
             var list = new RepeatedField<string>();
             var list = new RepeatedField<string>();
-            Assert.Throws<ArgumentNullException>(() => list.Add((string) null));
-            Assert.Throws<ArgumentNullException>(() => list.Add((IEnumerable<string>) null));
+            Assert.Throws<ArgumentNullException>(() => list.Add((string)null));
+            Assert.Throws<ArgumentNullException>(() => list.Add((IEnumerable<string>)null));
             Assert.Throws<ArgumentNullException>(() => list.Add((RepeatedField<string>)null));
             Assert.Throws<ArgumentNullException>(() => list.Add((RepeatedField<string>)null));
             Assert.Throws<ArgumentNullException>(() => list.Contains(null));
             Assert.Throws<ArgumentNullException>(() => list.Contains(null));
             Assert.Throws<ArgumentNullException>(() => list.IndexOf(null));
             Assert.Throws<ArgumentNullException>(() => list.IndexOf(null));
@@ -47,5 +48,47 @@ namespace Google.Protobuf
             Assert.AreEqual("foo", list[1]);
             Assert.AreEqual("foo", list[1]);
             Assert.AreEqual("bar", list[2]);
             Assert.AreEqual("bar", list[2]);
         }
         }
+
+        [Test]
+        public void Freeze_FreezesElements()
+        {
+            var list = new RepeatedField<TestAllTypes> { new TestAllTypes() };
+            Assert.IsFalse(list[0].IsFrozen);
+            list.Freeze();
+            Assert.IsTrue(list[0].IsFrozen);
+        }
+
+        [Test]
+        public void Freeze_PreventsMutations()
+        {
+            var list = new RepeatedField<int> { 0 };
+            list.Freeze();
+            Assert.Throws<InvalidOperationException>(() => list.Add(1));
+            Assert.Throws<InvalidOperationException>(() => list[0] = 1);
+            Assert.Throws<InvalidOperationException>(() => list.Clear());
+            Assert.Throws<InvalidOperationException>(() => list.RemoveAt(0));
+            Assert.Throws<InvalidOperationException>(() => list.Remove(0));
+            Assert.Throws<InvalidOperationException>(() => list.Insert(0, 0));
+        }
+
+        [Test]
+        public void Freeze_ReportsFrozen()
+        {
+            var list = new RepeatedField<int> { 0 };
+            Assert.IsFalse(list.IsFrozen);
+            Assert.IsFalse(list.IsReadOnly);
+            list.Freeze();
+            Assert.IsTrue(list.IsFrozen);
+            Assert.IsTrue(list.IsReadOnly);
+        }
+
+        [Test]
+        public void Clone_ReturnsMutable()
+        {
+            var list = new RepeatedField<int> { 0 };
+            list.Freeze();
+            var clone = list.Clone();
+            clone[0] = 1;
+        }
     }
     }
 }
 }

+ 14 - 2
csharp/src/ProtocolBuffers.Test/TestProtos/UnittestImportProto3.cs

@@ -74,6 +74,9 @@ namespace Google.Protobuf.TestProtos {
       get { return global::Google.Protobuf.TestProtos.UnittestImportProto3.internal__static_protobuf_unittest_import_ImportMessage__FieldAccessorTable; }
       get { return global::Google.Protobuf.TestProtos.UnittestImportProto3.internal__static_protobuf_unittest_import_ImportMessage__FieldAccessorTable; }
     }
     }
 
 
+    private bool _frozen = false;
+    public bool IsFrozen { get { return _frozen; } }
+
     public ImportMessage() { }
     public ImportMessage() { }
 
 
     public ImportMessage(ImportMessage other) {
     public ImportMessage(ImportMessage other) {
@@ -84,14 +87,23 @@ namespace Google.Protobuf.TestProtos {
       return new ImportMessage(this);
       return new ImportMessage(this);
     }
     }
 
 
+    public void Freeze() {
+      if (IsFrozen) {
+        return;
+      }
+      _frozen = true;
+    }
+
     public const int DFieldNumber = 1;
     public const int DFieldNumber = 1;
     private int d_;
     private int d_;
     public int D {
     public int D {
       get { return d_; }
       get { return d_; }
-      set { d_ = value; }
+      set {
+        pb::Freezable.CheckMutable(this);
+        d_ = value;
+      }
     }
     }
 
 
-
     public override bool Equals(object other) {
     public override bool Equals(object other) {
       return Equals(other as ImportMessage);
       return Equals(other as ImportMessage);
     }
     }

+ 14 - 2
csharp/src/ProtocolBuffers.Test/TestProtos/UnittestImportPublicProto3.cs

@@ -59,6 +59,9 @@ namespace Google.Protobuf.TestProtos {
       get { return global::Google.Protobuf.TestProtos.UnittestImportPublicProto3.internal__static_protobuf_unittest_import_PublicImportMessage__FieldAccessorTable; }
       get { return global::Google.Protobuf.TestProtos.UnittestImportPublicProto3.internal__static_protobuf_unittest_import_PublicImportMessage__FieldAccessorTable; }
     }
     }
 
 
+    private bool _frozen = false;
+    public bool IsFrozen { get { return _frozen; } }
+
     public PublicImportMessage() { }
     public PublicImportMessage() { }
 
 
     public PublicImportMessage(PublicImportMessage other) {
     public PublicImportMessage(PublicImportMessage other) {
@@ -69,14 +72,23 @@ namespace Google.Protobuf.TestProtos {
       return new PublicImportMessage(this);
       return new PublicImportMessage(this);
     }
     }
 
 
+    public void Freeze() {
+      if (IsFrozen) {
+        return;
+      }
+      _frozen = true;
+    }
+
     public const int EFieldNumber = 1;
     public const int EFieldNumber = 1;
     private int e_;
     private int e_;
     public int E {
     public int E {
       get { return e_; }
       get { return e_; }
-      set { e_ = value; }
+      set {
+        pb::Freezable.CheckMutable(this);
+        e_ = value;
+      }
     }
     }
 
 
-
     public override bool Equals(object other) {
     public override bool Equals(object other) {
       return Equals(other as PublicImportMessage);
       return Equals(other as PublicImportMessage);
     }
     }

+ 68 - 10
csharp/src/ProtocolBuffers.Test/TestProtos/UnittestIssues.cs

@@ -104,6 +104,9 @@ namespace UnitTest.Issues.TestProtos {
       get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_NegativeEnumMessage__FieldAccessorTable; }
       get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_NegativeEnumMessage__FieldAccessorTable; }
     }
     }
 
 
+    private bool _frozen = false;
+    public bool IsFrozen { get { return _frozen; } }
+
     public NegativeEnumMessage() { }
     public NegativeEnumMessage() { }
 
 
     public NegativeEnumMessage(NegativeEnumMessage other) {
     public NegativeEnumMessage(NegativeEnumMessage other) {
@@ -116,14 +119,25 @@ namespace UnitTest.Issues.TestProtos {
       return new NegativeEnumMessage(this);
       return new NegativeEnumMessage(this);
     }
     }
 
 
+    public void Freeze() {
+      if (IsFrozen) {
+        return;
+      }
+      _frozen = true;
+      values_.Freeze();
+      packedValues_.Freeze();
+    }
+
     public const int ValueFieldNumber = 1;
     public const int ValueFieldNumber = 1;
     private global::UnitTest.Issues.TestProtos.NegativeEnum value_ = global::UnitTest.Issues.TestProtos.NegativeEnum.NEGATIVE_ENUM_ZERO;
     private global::UnitTest.Issues.TestProtos.NegativeEnum value_ = global::UnitTest.Issues.TestProtos.NegativeEnum.NEGATIVE_ENUM_ZERO;
     public global::UnitTest.Issues.TestProtos.NegativeEnum Value {
     public global::UnitTest.Issues.TestProtos.NegativeEnum Value {
       get { return value_; }
       get { return value_; }
-      set { value_ = value; }
+      set {
+        pb::Freezable.CheckMutable(this);
+        value_ = value;
+      }
     }
     }
 
 
-
     public const int ValuesFieldNumber = 2;
     public const int ValuesFieldNumber = 2;
     private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> values_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum>();
     private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> values_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum>();
     public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> Values {
     public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> Values {
@@ -255,6 +269,9 @@ namespace UnitTest.Issues.TestProtos {
       get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_DeprecatedChild__FieldAccessorTable; }
       get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_DeprecatedChild__FieldAccessorTable; }
     }
     }
 
 
+    private bool _frozen = false;
+    public bool IsFrozen { get { return _frozen; } }
+
     public DeprecatedChild() { }
     public DeprecatedChild() { }
 
 
     public DeprecatedChild(DeprecatedChild other) {
     public DeprecatedChild(DeprecatedChild other) {
@@ -264,6 +281,13 @@ namespace UnitTest.Issues.TestProtos {
       return new DeprecatedChild(this);
       return new DeprecatedChild(this);
     }
     }
 
 
+    public void Freeze() {
+      if (IsFrozen) {
+        return;
+      }
+      _frozen = true;
+    }
+
     public override bool Equals(object other) {
     public override bool Equals(object other) {
       return Equals(other as DeprecatedChild);
       return Equals(other as DeprecatedChild);
     }
     }
@@ -328,6 +352,9 @@ namespace UnitTest.Issues.TestProtos {
       get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_DeprecatedFieldsMessage__FieldAccessorTable; }
       get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_DeprecatedFieldsMessage__FieldAccessorTable; }
     }
     }
 
 
+    private bool _frozen = false;
+    public bool IsFrozen { get { return _frozen; } }
+
     public DeprecatedFieldsMessage() { }
     public DeprecatedFieldsMessage() { }
 
 
     public DeprecatedFieldsMessage(DeprecatedFieldsMessage other) {
     public DeprecatedFieldsMessage(DeprecatedFieldsMessage other) {
@@ -343,15 +370,28 @@ namespace UnitTest.Issues.TestProtos {
       return new DeprecatedFieldsMessage(this);
       return new DeprecatedFieldsMessage(this);
     }
     }
 
 
+    public void Freeze() {
+      if (IsFrozen) {
+        return;
+      }
+      _frozen = true;
+      primitiveArray_.Freeze();
+      if (messageValue_ != null) MessageValue.Freeze();
+      messageArray_.Freeze();
+      enumArray_.Freeze();
+    }
+
     public const int PrimitiveValueFieldNumber = 1;
     public const int PrimitiveValueFieldNumber = 1;
     private int primitiveValue_;
     private int primitiveValue_;
     [global::System.ObsoleteAttribute()]
     [global::System.ObsoleteAttribute()]
     public int PrimitiveValue {
     public int PrimitiveValue {
       get { return primitiveValue_; }
       get { return primitiveValue_; }
-      set { primitiveValue_ = value; }
+      set {
+        pb::Freezable.CheckMutable(this);
+        primitiveValue_ = value;
+      }
     }
     }
 
 
-
     public const int PrimitiveArrayFieldNumber = 2;
     public const int PrimitiveArrayFieldNumber = 2;
     private readonly pbc::RepeatedField<int> primitiveArray_ = new pbc::RepeatedField<int>();
     private readonly pbc::RepeatedField<int> primitiveArray_ = new pbc::RepeatedField<int>();
     [global::System.ObsoleteAttribute()]
     [global::System.ObsoleteAttribute()]
@@ -364,7 +404,10 @@ namespace UnitTest.Issues.TestProtos {
     [global::System.ObsoleteAttribute()]
     [global::System.ObsoleteAttribute()]
     public global::UnitTest.Issues.TestProtos.DeprecatedChild MessageValue {
     public global::UnitTest.Issues.TestProtos.DeprecatedChild MessageValue {
       get { return messageValue_; }
       get { return messageValue_; }
-      set { messageValue_ = value; }
+      set {
+        pb::Freezable.CheckMutable(this);
+        messageValue_ = value;
+      }
     }
     }
 
 
     public const int MessageArrayFieldNumber = 4;
     public const int MessageArrayFieldNumber = 4;
@@ -379,10 +422,12 @@ namespace UnitTest.Issues.TestProtos {
     [global::System.ObsoleteAttribute()]
     [global::System.ObsoleteAttribute()]
     public global::UnitTest.Issues.TestProtos.DeprecatedEnum EnumValue {
     public global::UnitTest.Issues.TestProtos.DeprecatedEnum EnumValue {
       get { return enumValue_; }
       get { return enumValue_; }
-      set { enumValue_ = value; }
+      set {
+        pb::Freezable.CheckMutable(this);
+        enumValue_ = value;
+      }
     }
     }
 
 
-
     public const int EnumArrayFieldNumber = 6;
     public const int EnumArrayFieldNumber = 6;
     private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum> enumArray_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum>();
     private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum> enumArray_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum>();
     [global::System.ObsoleteAttribute()]
     [global::System.ObsoleteAttribute()]
@@ -403,7 +448,8 @@ namespace UnitTest.Issues.TestProtos {
       }
       }
       if (PrimitiveValue != other.PrimitiveValue) return false;
       if (PrimitiveValue != other.PrimitiveValue) return false;
       if(!primitiveArray_.Equals(other.primitiveArray_)) return false;
       if(!primitiveArray_.Equals(other.primitiveArray_)) return false;
-      if (!object.Equals(MessageValue, other.MessageValue)) return false;if(!messageArray_.Equals(other.messageArray_)) return false;
+      if (!object.Equals(MessageValue, other.MessageValue)) return false;
+      if(!messageArray_.Equals(other.messageArray_)) return false;
       if (EnumValue != other.EnumValue) return false;
       if (EnumValue != other.EnumValue) return false;
       if(!enumArray_.Equals(other.enumArray_)) return false;
       if(!enumArray_.Equals(other.enumArray_)) return false;
       return true;
       return true;
@@ -563,6 +609,9 @@ namespace UnitTest.Issues.TestProtos {
       get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_ItemField__FieldAccessorTable; }
       get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_ItemField__FieldAccessorTable; }
     }
     }
 
 
+    private bool _frozen = false;
+    public bool IsFrozen { get { return _frozen; } }
+
     public ItemField() { }
     public ItemField() { }
 
 
     public ItemField(ItemField other) {
     public ItemField(ItemField other) {
@@ -573,14 +622,23 @@ namespace UnitTest.Issues.TestProtos {
       return new ItemField(this);
       return new ItemField(this);
     }
     }
 
 
+    public void Freeze() {
+      if (IsFrozen) {
+        return;
+      }
+      _frozen = true;
+    }
+
     public const int ItemFieldNumber = 1;
     public const int ItemFieldNumber = 1;
     private int item_;
     private int item_;
     public int Item {
     public int Item {
       get { return item_; }
       get { return item_; }
-      set { item_ = value; }
+      set {
+        pb::Freezable.CheckMutable(this);
+        item_ = value;
+      }
     }
     }
 
 
-
     public override bool Equals(object other) {
     public override bool Equals(object other) {
       return Equals(other as ItemField);
       return Equals(other as ItemField);
     }
     }

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 388 - 88
csharp/src/ProtocolBuffers.Test/TestProtos/UnittestProto3.cs


+ 33 - 2
csharp/src/ProtocolBuffers/Collections/RepeatedField.cs

@@ -4,10 +4,16 @@ using System.Collections.Generic;
 
 
 namespace Google.Protobuf.Collections
 namespace Google.Protobuf.Collections
 {
 {
-    public sealed class RepeatedField<T> : IList<T>, IEquatable<RepeatedField<T>>
+    /// <summary>
+    /// The contents of a repeated field: essentially, a collection with some extra
+    /// restrictions (no null values) and capabilities (deep cloning and freezing).
+    /// </summary>
+    /// <typeparam name="T">The element type of the repeated field.</typeparam>
+    public sealed class RepeatedField<T> : IList<T>, IDeepCloneable<RepeatedField<T>>, IEquatable<RepeatedField<T>>, IFreezable
     {
     {
         private static readonly T[] EmptyArray = new T[0];
         private static readonly T[] EmptyArray = new T[0];
 
 
+        private bool frozen;
         private const int MinArraySize = 8;
         private const int MinArraySize = 8;
         private T[] array = EmptyArray;
         private T[] array = EmptyArray;
         private int count = 0;
         private int count = 0;
@@ -26,6 +32,7 @@ namespace Google.Protobuf.Collections
         public RepeatedField<T> Clone()
         public RepeatedField<T> Clone()
         {
         {
             RepeatedField<T> clone = new RepeatedField<T>();
             RepeatedField<T> clone = new RepeatedField<T>();
+            // Clone is implicitly *not* frozen, even if this object is.
             if (array != EmptyArray)
             if (array != EmptyArray)
             {
             {
                 clone.array = (T[])array.Clone();
                 clone.array = (T[])array.Clone();
@@ -42,6 +49,21 @@ namespace Google.Protobuf.Collections
             return clone;
             return clone;
         }
         }
 
 
+        public bool IsFrozen { get { return frozen; } }
+
+        public void Freeze()
+        {
+            frozen = true;
+            IFreezable[] freezableArray = array as IFreezable[];
+            if (freezableArray != null)
+            {
+                for (int i = 0; i < count; i++)
+                {
+                    freezableArray[i].Freeze();
+                }
+            }
+        }
+
         private void EnsureSize(int size)
         private void EnsureSize(int size)
         {
         {
             size = Math.Max(size, MinArraySize);
             size = Math.Max(size, MinArraySize);
@@ -60,6 +82,7 @@ namespace Google.Protobuf.Collections
             {
             {
                 throw new ArgumentNullException("item");
                 throw new ArgumentNullException("item");
             }
             }
+            this.CheckMutable();
             EnsureSize(count + 1);
             EnsureSize(count + 1);
             array[count++] = item;
             array[count++] = item;
         }
         }
@@ -70,6 +93,7 @@ namespace Google.Protobuf.Collections
         /// <param name="readEnum"></param>
         /// <param name="readEnum"></param>
         internal void AddInt32(int item)
         internal void AddInt32(int item)
         {
         {
+            this.CheckMutable();
             EnsureSize(count + 1);
             EnsureSize(count + 1);
             int[] castArray = (int[]) (object) array;
             int[] castArray = (int[]) (object) array;
             castArray[count++] = item;
             castArray[count++] = item;
@@ -77,6 +101,7 @@ namespace Google.Protobuf.Collections
 
 
         public void Clear()
         public void Clear()
         {
         {
+            this.CheckMutable();
             array = EmptyArray;
             array = EmptyArray;
             count = 0;
             count = 0;
         }
         }
@@ -93,6 +118,7 @@ namespace Google.Protobuf.Collections
 
 
         public bool Remove(T item)
         public bool Remove(T item)
         {
         {
+            this.CheckMutable();
             int index = IndexOf(item);
             int index = IndexOf(item);
             if (index == -1)
             if (index == -1)
             {
             {
@@ -107,7 +133,7 @@ namespace Google.Protobuf.Collections
         public int Count { get { return count; } }
         public int Count { get { return count; } }
 
 
         // TODO(jonskeet): If we implement freezing, make this reflect it.
         // TODO(jonskeet): If we implement freezing, make this reflect it.
-        public bool IsReadOnly { get { return false; } }
+        public bool IsReadOnly { get { return IsFrozen; } }
 
 
         public void Add(RepeatedField<T> values)
         public void Add(RepeatedField<T> values)
         {
         {
@@ -115,6 +141,7 @@ namespace Google.Protobuf.Collections
             {
             {
                 throw new ArgumentNullException("values");
                 throw new ArgumentNullException("values");
             }
             }
+            this.CheckMutable();
             EnsureSize(count + values.count);
             EnsureSize(count + values.count);
             // We know that all the values will be valid, because it's a RepeatedField.
             // We know that all the values will be valid, because it's a RepeatedField.
             Array.Copy(values.array, 0, array, count, values.count);
             Array.Copy(values.array, 0, array, count, values.count);
@@ -127,6 +154,7 @@ namespace Google.Protobuf.Collections
             {
             {
                 throw new ArgumentNullException("values");
                 throw new ArgumentNullException("values");
             }
             }
+            this.CheckMutable();
             // TODO: Check for ICollection and get the Count?
             // TODO: Check for ICollection and get the Count?
             foreach (T item in values)
             foreach (T item in values)
             {
             {
@@ -227,6 +255,7 @@ namespace Google.Protobuf.Collections
             {
             {
                 throw new ArgumentOutOfRangeException("index");
                 throw new ArgumentOutOfRangeException("index");
             }
             }
+            this.CheckMutable();
             EnsureSize(count + 1);
             EnsureSize(count + 1);
             Array.Copy(array, index, array, index + 1, count - index);
             Array.Copy(array, index, array, index + 1, count - index);
             count++;
             count++;
@@ -238,6 +267,7 @@ namespace Google.Protobuf.Collections
             {
             {
                 throw new ArgumentOutOfRangeException("index");
                 throw new ArgumentOutOfRangeException("index");
             }
             }
+            this.CheckMutable();
             Array.Copy(array, index + 1, array, index, count - index - 1);
             Array.Copy(array, index + 1, array, index, count - index - 1);
             count--;
             count--;
             array[count] = default(T);
             array[count] = default(T);
@@ -259,6 +289,7 @@ namespace Google.Protobuf.Collections
                 {
                 {
                     throw new ArgumentOutOfRangeException("index");
                     throw new ArgumentOutOfRangeException("index");
                 }
                 }
+                this.CheckMutable();
                 if (value == null)
                 if (value == null)
                 {
                 {
                     throw new ArgumentNullException("value");
                     throw new ArgumentNullException("value");

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 381 - 103
csharp/src/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs


+ 60 - 0
csharp/src/ProtocolBuffers/Freezable.cs

@@ -0,0 +1,60 @@
+#region Copyright notice and license
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// http://github.com/google/protobuf
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Extension methods for <see cref="IFreezable"/> types.
+    /// </summary>
+    public static class Freezable
+    {
+        /// <summary>
+        /// Throws an <see cref="InvalidOperationException"/> if <paramref name="target"/>
+        /// is frozen.
+        /// </summary>
+        /// <remarks>
+        /// This is a convenience methods that freezable types can call before all
+        /// mutations, to protect frozen objects.
+        /// </remarks>
+        public static void CheckMutable(this IFreezable target)
+        {
+            if (target.IsFrozen)
+            {
+                throw new InvalidOperationException("Attempt to mutate frozen object");
+            }
+        }
+    }
+}

+ 35 - 1
csharp/src/ProtocolBuffers/IMessage.cs

@@ -41,6 +41,7 @@ namespace Google.Protobuf
 {
 {
 
 
     // TODO(jonskeet): Do we want a "weak" (non-generic) version of IReflectedMessage?
     // TODO(jonskeet): Do we want a "weak" (non-generic) version of IReflectedMessage?
+    // TODO(jonskeet): Split these interfaces into separate files when we're happy with them.
 
 
     /// <summary>
     /// <summary>
     /// Reflection support for a specific message type. message
     /// Reflection support for a specific message type. message
@@ -85,7 +86,7 @@ namespace Google.Protobuf
     /// the implementation class.
     /// the implementation class.
     /// </summary>
     /// </summary>
     /// <typeparam name="T">The message type.</typeparam>
     /// <typeparam name="T">The message type.</typeparam>
-    public interface IMessage<T> : IMessage, IEquatable<T>, IDeepCloneable<T> where T : IMessage<T>
+    public interface IMessage<T> : IMessage, IEquatable<T>, IDeepCloneable<T>, IFreezable where T : IMessage<T>
     {
     {
         /// <summary>
         /// <summary>
         /// Merges the given message into this one.
         /// Merges the given message into this one.
@@ -102,6 +103,11 @@ namespace Google.Protobuf
     /// All generated messages implement this interface, but so do some non-message types.
     /// All generated messages implement this interface, but so do some non-message types.
     /// Additionally, due to the type constraint on <c>T</c> in <see cref="IMessage{T}"/>,
     /// Additionally, due to the type constraint on <c>T</c> in <see cref="IMessage{T}"/>,
     /// it is simpler to keep this as a separate interface.
     /// it is simpler to keep this as a separate interface.
+    /// </para>
+    /// <para>
+    /// Freezable types which implement this interface should always return a mutable clone,
+    /// even if the original object is frozen.
+    /// </para>
     /// </remarks>
     /// </remarks>
     /// <typeparam name="T">The type itself, returned by the <see cref="Clone"/> method.</typeparam>
     /// <typeparam name="T">The type itself, returned by the <see cref="Clone"/> method.</typeparam>
     public interface IDeepCloneable<T>
     public interface IDeepCloneable<T>
@@ -112,4 +118,32 @@ namespace Google.Protobuf
         /// <returns>A deep clone of this object.</returns>
         /// <returns>A deep clone of this object.</returns>
         T Clone();
         T Clone();
     }
     }
+
+    /// <summary>
+    /// Provides a mechanism for freezing a message (or repeated field collection)
+    /// to make it immutable.
+    /// </summary>
+    /// <remarks>
+    /// Implementations are under no obligation to make this thread-safe: if a freezable
+    /// type instance is shared between threads before being frozen, and one thread then
+    /// freezes it, it is possible for other threads to make changes during the freezing
+    /// operation and also to observe stale values for mutated fields. Objects should be
+    /// frozen before being made available to other threads.
+    /// </remarks>
+    public interface IFreezable
+    {
+        /// <summary>
+        /// Freezes this object.
+        /// </summary>
+        /// <remarks>
+        /// If the object is already frozen, this method has no effect.
+        /// </remarks>
+        void Freeze();
+
+        /// <summary>
+        /// Returns whether or not this object is frozen (and therefore immutable).
+        /// </summary>
+        /// <value><c>true</c> if this object is frozen; <c>false</c> otherwise.</value>
+        bool IsFrozen { get; }
+    }
 }
 }

+ 1 - 0
csharp/src/ProtocolBuffers/ProtocolBuffers.csproj

@@ -85,6 +85,7 @@
     <Compile Include="Descriptors\PackageDescriptor.cs" />
     <Compile Include="Descriptors\PackageDescriptor.cs" />
     <Compile Include="Descriptors\ServiceDescriptor.cs" />
     <Compile Include="Descriptors\ServiceDescriptor.cs" />
     <Compile Include="FrameworkPortability.cs" />
     <Compile Include="FrameworkPortability.cs" />
+    <Compile Include="Freezable.cs" />
     <Compile Include="MessageExtensions.cs" />
     <Compile Include="MessageExtensions.cs" />
     <Compile Include="FieldAccess\FieldAccessorBase.cs" />
     <Compile Include="FieldAccess\FieldAccessorBase.cs" />
     <Compile Include="FieldAccess\ReflectionUtil.cs" />
     <Compile Include="FieldAccess\ReflectionUtil.cs" />

+ 5 - 0
src/google/protobuf/compiler/csharp/csharp_field_base.cc

@@ -107,6 +107,11 @@ FieldGeneratorBase::FieldGeneratorBase(const FieldDescriptor* descriptor,
 FieldGeneratorBase::~FieldGeneratorBase() {
 FieldGeneratorBase::~FieldGeneratorBase() {
 }
 }
 
 
+void FieldGeneratorBase::GenerateFreezingCode(io::Printer* printer) {
+  // No-op: only message fields and repeated fields need
+  // special handling for freezing, so default to not generating any code.
+}
+
 void FieldGeneratorBase::AddDeprecatedFlag(io::Printer* printer) {
 void FieldGeneratorBase::AddDeprecatedFlag(io::Printer* printer) {
   if (descriptor_->options().deprecated())
   if (descriptor_->options().deprecated())
   {
   {

+ 1 - 0
src/google/protobuf/compiler/csharp/csharp_field_base.h

@@ -48,6 +48,7 @@ class FieldGeneratorBase : public SourceGeneratorBase {
   ~FieldGeneratorBase();
   ~FieldGeneratorBase();
 
 
   virtual void GenerateCloningCode(io::Printer* printer) = 0;
   virtual void GenerateCloningCode(io::Printer* printer) = 0;
+  virtual void GenerateFreezingCode(io::Printer* printer);
   virtual void GenerateMembers(io::Printer* printer) = 0;
   virtual void GenerateMembers(io::Printer* printer) = 0;
   virtual void GenerateMergingCode(io::Printer* printer) = 0;
   virtual void GenerateMergingCode(io::Printer* printer) = 0;
   virtual void GenerateParsingCode(io::Printer* printer) = 0;
   virtual void GenerateParsingCode(io::Printer* printer) = 0;

+ 35 - 1
src/google/protobuf/compiler/csharp/csharp_message.cc

@@ -211,7 +211,9 @@ void MessageGenerator::Generate(io::Printer* printer) {
     "public pb::FieldAccess.FieldAccessorTable<$class_name$> Fields {\n"
     "public pb::FieldAccess.FieldAccessorTable<$class_name$> Fields {\n"
     "  get { return $umbrella_class_name$.internal__$identifier$__FieldAccessorTable; }\n"
     "  get { return $umbrella_class_name$.internal__$identifier$__FieldAccessorTable; }\n"
     "}\n"
     "}\n"
-    "\n");
+    "\n"
+    "private bool _frozen = false;\n"
+    "public bool IsFrozen { get { return _frozen; } }\n\n");
 
 
   // Parameterless constructor
   // Parameterless constructor
   printer->Print(
   printer->Print(
@@ -219,6 +221,7 @@ void MessageGenerator::Generate(io::Printer* printer) {
     "public $class_name$() { }\n\n");
     "public $class_name$() { }\n\n");
 
 
   GenerateCloningCode(printer);
   GenerateCloningCode(printer);
+  GenerateFreezingCode(printer);
 
 
   // Fields/properties
   // Fields/properties
   for (int i = 0; i < descriptor_->field_count(); i++) {
   for (int i = 0; i < descriptor_->field_count(); i++) {
@@ -260,6 +263,7 @@ void MessageGenerator::Generate(io::Printer* printer) {
       "  get { return $name$Case_; }\n"
       "  get { return $name$Case_; }\n"
       "}\n\n"
       "}\n\n"
       "public void Clear$property_name$() {\n"
       "public void Clear$property_name$() {\n"
+      "  pb::Freezable.CheckMutable(this);\n"
       "  $name$Case_ = $property_name$OneofCase.None;\n"
       "  $name$Case_ = $property_name$OneofCase.None;\n"
       "  $name$_ = null;\n"
       "  $name$_ = null;\n"
       "}\n\n");
       "}\n\n");
@@ -346,6 +350,36 @@ void MessageGenerator::GenerateCloningCode(io::Printer* printer) {
     "}\n\n");
     "}\n\n");
 }
 }
 
 
+void MessageGenerator::GenerateFreezingCode(io::Printer* printer) {
+    map<string, string> vars;
+    vars["class_name"] = class_name();
+    printer->Print(
+      "public void Freeze() {\n"
+      "  if (IsFrozen) {\n"
+      "    return;\n"
+      "  }\n"
+      "  _frozen = true;\n");
+    printer->Indent();
+    // Freeze non-oneof fields first (only messages and repeated fields will actually generate any code)
+    for (int i = 0; i < descriptor_->field_count(); i++) {
+        if (!descriptor_->field(i)->containing_oneof()) {
+            scoped_ptr<FieldGeneratorBase> generator(
+                CreateFieldGeneratorInternal(descriptor_->field(i)));
+            generator->GenerateFreezingCode(printer);
+        }
+    }
+
+    // For each oneof, if the value is freezable, freeze it. We don't actually need to know which type it was.
+    for (int i = 0; i < descriptor_->oneof_decl_count(); ++i) {
+        vars["name"] = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false);
+        printer->Print(vars,
+            "if ($name$_ is IFreezable) ((IFreezable) $name$_).Freeze();\n");
+    }
+
+    printer->Outdent();
+    printer->Print("}\n\n");
+}
+
 void MessageGenerator::GenerateFrameworkMethods(io::Printer* printer) {
 void MessageGenerator::GenerateFrameworkMethods(io::Printer* printer) {
     map<string, string> vars;
     map<string, string> vars;
     vars["class_name"] = class_name();
     vars["class_name"] = class_name();

+ 1 - 0
src/google/protobuf/compiler/csharp/csharp_message.h

@@ -51,6 +51,7 @@ class MessageGenerator : public SourceGeneratorBase {
   ~MessageGenerator();
   ~MessageGenerator();
 
 
   void GenerateCloningCode(io::Printer* printer);
   void GenerateCloningCode(io::Printer* printer);
+  void GenerateFreezingCode(io::Printer* printer);
   void GenerateFrameworkMethods(io::Printer* printer);
   void GenerateFrameworkMethods(io::Printer* printer);
   void GenerateStaticVariables(io::Printer* printer);
   void GenerateStaticVariables(io::Printer* printer);
   void GenerateStaticVariableInitializers(io::Printer* printer);
   void GenerateStaticVariableInitializers(io::Printer* printer);

+ 11 - 2
src/google/protobuf/compiler/csharp/csharp_message_field.cc

@@ -66,7 +66,10 @@ void MessageFieldGenerator::GenerateMembers(io::Printer* printer) {
     variables_,
     variables_,
     "public $type_name$ $property_name$ {\n"
     "public $type_name$ $property_name$ {\n"
     "  get { return $name$_; }\n"
     "  get { return $name$_; }\n"
-    "  set { $name$_ = value; }\n"
+    "  set {\n"
+    "    pb::Freezable.CheckMutable(this);\n"
+    "    $name$_ = value;\n"
+    "  }\n"
     "}\n");
     "}\n");
 }
 }
 
 
@@ -116,7 +119,7 @@ void MessageFieldGenerator::WriteHash(io::Printer* printer) {
 void MessageFieldGenerator::WriteEquals(io::Printer* printer) {
 void MessageFieldGenerator::WriteEquals(io::Printer* printer) {
   printer->Print(
   printer->Print(
     variables_,
     variables_,
-    "if (!object.Equals($property_name$, other.$property_name$)) return false;");
+    "if (!object.Equals($property_name$, other.$property_name$)) return false;\n");
 }
 }
 void MessageFieldGenerator::WriteToString(io::Printer* printer) {
 void MessageFieldGenerator::WriteToString(io::Printer* printer) {
   variables_["field_name"] = GetFieldName(descriptor_);
   variables_["field_name"] = GetFieldName(descriptor_);
@@ -130,6 +133,11 @@ void MessageFieldGenerator::GenerateCloningCode(io::Printer* printer) {
     "$property_name$ = other.$has_property_check$ ? other.$property_name$.Clone() : null;\n");
     "$property_name$ = other.$has_property_check$ ? other.$property_name$.Clone() : null;\n");
 }
 }
 
 
+void MessageFieldGenerator::GenerateFreezingCode(io::Printer* printer) {
+  printer->Print(variables_,
+    "if ($has_property_check$) $property_name$.Freeze();\n");
+}
+
 MessageOneofFieldGenerator::MessageOneofFieldGenerator(const FieldDescriptor* descriptor,
 MessageOneofFieldGenerator::MessageOneofFieldGenerator(const FieldDescriptor* descriptor,
 						       int fieldOrdinal)
 						       int fieldOrdinal)
     : MessageFieldGenerator(descriptor, fieldOrdinal) {
     : MessageFieldGenerator(descriptor, fieldOrdinal) {
@@ -147,6 +155,7 @@ void MessageOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
     "public $type_name$ $property_name$ {\n"
     "public $type_name$ $property_name$ {\n"
     "  get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : null; }\n"
     "  get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : null; }\n"
     "  set {\n"
     "  set {\n"
+    "    pb::Freezable.CheckMutable(this);\n"
     "    $oneof_name$_ = value;\n"
     "    $oneof_name$_ = value;\n"
     "    $oneof_name$Case_ = value == null ? $oneof_property_name$OneofCase.None : $oneof_property_name$OneofCase.$property_name$;\n"
     "    $oneof_name$Case_ = value == null ? $oneof_property_name$OneofCase.None : $oneof_property_name$OneofCase.$property_name$;\n"
     "  }\n"
     "  }\n"

+ 1 - 0
src/google/protobuf/compiler/csharp/csharp_message_field.h

@@ -47,6 +47,7 @@ class MessageFieldGenerator : public FieldGeneratorBase {
   ~MessageFieldGenerator();
   ~MessageFieldGenerator();
 
 
   virtual void GenerateCloningCode(io::Printer* printer);
   virtual void GenerateCloningCode(io::Printer* printer);
+  virtual void GenerateFreezingCode(io::Printer* printer);
   virtual void GenerateMembers(io::Printer* printer);
   virtual void GenerateMembers(io::Printer* printer);
   virtual void GenerateMergingCode(io::Printer* printer);
   virtual void GenerateMergingCode(io::Printer* printer);
   virtual void GenerateParsingCode(io::Printer* printer);
   virtual void GenerateParsingCode(io::Printer* printer);

+ 10 - 5
src/google/protobuf/compiler/csharp/csharp_primitive_field.cc

@@ -72,17 +72,21 @@ void PrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
   printer->Print(
   printer->Print(
     variables_,
     variables_,
     "public $type_name$ $property_name$ {\n"
     "public $type_name$ $property_name$ {\n"
-    "  get { return $name$_; }\n");
+    "  get { return $name$_; }\n"
+    "  set {\n"
+    "    pb::Freezable.CheckMutable(this);\n");
   if (is_value_type) {
   if (is_value_type) {
     printer->Print(
     printer->Print(
       variables_,
       variables_,
-      "  set { $name$_ = value; }\n");
+      "    $name$_ = value;\n");
   } else {
   } else {
     printer->Print(
     printer->Print(
       variables_,
       variables_,
-      "  set { $name$_ = value ?? $default_value$; }\n");
+      "    $name$_ = value ?? $default_value$;\n");
   }
   }
-  printer->Print("}\n\n");
+  printer->Print(
+    "  }\n"
+    "}\n");
 }
 }
 
 
 void PrimitiveFieldGenerator::GenerateMergingCode(io::Printer* printer) {
 void PrimitiveFieldGenerator::GenerateMergingCode(io::Printer* printer) {
@@ -166,7 +170,8 @@ void PrimitiveOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
     variables_,
     variables_,
     "public $type_name$ $property_name$ {\n"
     "public $type_name$ $property_name$ {\n"
     "  get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : $default_value$; }\n"
     "  get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : $default_value$; }\n"
-    "  set {\n");
+    "  set {\n"
+    "    pb::Freezable.CheckMutable(this);\n");
     if (is_value_type) {
     if (is_value_type) {
       printer->Print(
       printer->Print(
         variables_,
         variables_,

+ 5 - 0
src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc

@@ -147,6 +147,11 @@ void RepeatedEnumFieldGenerator::GenerateCloningCode(io::Printer* printer) {
     "$name$_ = other.$name$_.Clone();\n");
     "$name$_ = other.$name$_.Clone();\n");
 }
 }
 
 
+void RepeatedEnumFieldGenerator::GenerateFreezingCode(io::Printer* printer) {
+  printer->Print(variables_,
+    "$name$_.Freeze();\n");
+}
+
 }  // namespace csharp
 }  // namespace csharp
 }  // namespace compiler
 }  // namespace compiler
 }  // namespace protobuf
 }  // namespace protobuf

+ 1 - 0
src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.h

@@ -49,6 +49,7 @@ class RepeatedEnumFieldGenerator : public FieldGeneratorBase {
   ~RepeatedEnumFieldGenerator();
   ~RepeatedEnumFieldGenerator();
 
 
   virtual void GenerateCloningCode(io::Printer* printer);
   virtual void GenerateCloningCode(io::Printer* printer);
+  virtual void GenerateFreezingCode(io::Printer* printer);
   virtual void GenerateMembers(io::Printer* printer);
   virtual void GenerateMembers(io::Printer* printer);
   virtual void GenerateMergingCode(io::Printer* printer);
   virtual void GenerateMergingCode(io::Printer* printer);
   virtual void GenerateParsingCode(io::Printer* printer);
   virtual void GenerateParsingCode(io::Printer* printer);

+ 5 - 0
src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc

@@ -123,6 +123,11 @@ void RepeatedMessageFieldGenerator::GenerateCloningCode(io::Printer* printer) {
     "$name$_ = other.$name$_.Clone();\n");
     "$name$_ = other.$name$_.Clone();\n");
 }
 }
 
 
+void RepeatedMessageFieldGenerator::GenerateFreezingCode(io::Printer* printer) {
+  printer->Print(variables_,
+    "$name$_.Freeze();\n");
+}
+
 }  // namespace csharp
 }  // namespace csharp
 }  // namespace compiler
 }  // namespace compiler
 }  // namespace protobuf
 }  // namespace protobuf

+ 1 - 0
src/google/protobuf/compiler/csharp/csharp_repeated_message_field.h

@@ -47,6 +47,7 @@ class RepeatedMessageFieldGenerator : public FieldGeneratorBase {
   ~RepeatedMessageFieldGenerator();
   ~RepeatedMessageFieldGenerator();
 
 
   virtual void GenerateCloningCode(io::Printer* printer);
   virtual void GenerateCloningCode(io::Printer* printer);
+  virtual void GenerateFreezingCode(io::Printer* printer);
   virtual void GenerateMembers(io::Printer* printer);
   virtual void GenerateMembers(io::Printer* printer);
   virtual void GenerateMergingCode(io::Printer* printer);
   virtual void GenerateMergingCode(io::Printer* printer);
   virtual void GenerateParsingCode(io::Printer* printer);
   virtual void GenerateParsingCode(io::Printer* printer);

+ 5 - 0
src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc

@@ -153,6 +153,11 @@ void RepeatedPrimitiveFieldGenerator::GenerateCloningCode(io::Printer* printer)
     "$name$_ = other.$name$_.Clone();\n");
     "$name$_ = other.$name$_.Clone();\n");
 }
 }
 
 
+void RepeatedPrimitiveFieldGenerator::GenerateFreezingCode(io::Printer* printer) {
+  printer->Print(variables_,
+    "$name$_.Freeze();\n");
+}
+
 }  // namespace csharp
 }  // namespace csharp
 }  // namespace compiler
 }  // namespace compiler
 }  // namespace protobuf
 }  // namespace protobuf

+ 1 - 0
src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h

@@ -47,6 +47,7 @@ class RepeatedPrimitiveFieldGenerator : public FieldGeneratorBase {
   ~RepeatedPrimitiveFieldGenerator();
   ~RepeatedPrimitiveFieldGenerator();
 
 
   virtual void GenerateCloningCode(io::Printer* printer);
   virtual void GenerateCloningCode(io::Printer* printer);
+  virtual void GenerateFreezingCode(io::Printer* printer);
   virtual void GenerateMembers(io::Printer* printer);
   virtual void GenerateMembers(io::Printer* printer);
   virtual void GenerateMergingCode(io::Printer* printer);
   virtual void GenerateMergingCode(io::Printer* printer);
   virtual void GenerateParsingCode(io::Printer* printer);
   virtual void GenerateParsingCode(io::Printer* printer);

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác