Unverified Commit 37bf540f authored by Jan Tattermusch's avatar Jan Tattermusch Committed by GitHub

Merge pull request #5936 from ObsidianMinor/csharp/proto2-feature/finale

C# Proto2 feature : Finale
parents bc1773c4 4d5ae5b4
......@@ -68,9 +68,12 @@ csharp_EXTRA_DIST= \
csharp/protos/map_unittest_proto3.proto \
csharp/protos/unittest_custom_options_proto3.proto \
csharp/protos/unittest_import_public_proto3.proto \
csharp/protos/unittest_import_public.proto \
csharp/protos/unittest_import_proto3.proto \
csharp/protos/unittest_import.proto \
csharp/protos/unittest_issues.proto \
csharp/protos/unittest_proto3.proto \
csharp/protos/unittest.proto \
csharp/src/AddressBook/AddPerson.cs \
csharp/src/AddressBook/Addressbook.cs \
csharp/src/AddressBook/AddressBook.csproj \
......@@ -100,9 +103,11 @@ csharp_EXTRA_DIST= \
csharp/src/Google.Protobuf.Test/Compatibility/TypeExtensionsTest.cs \
csharp/src/Google.Protobuf.Test/DeprecatedMemberTest.cs \
csharp/src/Google.Protobuf.Test/EqualityTester.cs \
csharp/src/Google.Protobuf.Test/ExtensionSetTest.cs \
csharp/src/Google.Protobuf.Test/FieldCodecTest.cs \
csharp/src/Google.Protobuf.Test/FieldMaskTreeTest.cs \
csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs \
csharp/src/Google.Protobuf.Test/GeneratedMessageTest.Proto2.cs \
csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj \
csharp/src/Google.Protobuf.Test/IssuesTest.cs \
csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs \
......@@ -119,13 +124,17 @@ csharp_EXTRA_DIST= \
csharp/src/Google.Protobuf.Test/TestCornerCases.cs \
csharp/src/Google.Protobuf.Test/TestProtos/ForeignMessagePartial.cs \
csharp/src/Google.Protobuf.Test/TestProtos/MapUnittestProto3.cs \
csharp/src/Google.Protobuf.Test/TestProtos/TestMessagesProto2.cs \
csharp/src/Google.Protobuf.Test/TestProtos/TestMessagesProto3.cs \
csharp/src/Google.Protobuf.Test/TestProtos/UnittestCustomOptionsProto3.cs \
csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportProto3.cs \
csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportPublicProto3.cs \
csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportPublic.cs \
csharp/src/Google.Protobuf.Test/TestProtos/UnittestImport.cs \
csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs \
csharp/src/Google.Protobuf.Test/TestProtos/UnittestProto3.cs \
csharp/src/Google.Protobuf.Test/TestProtos/UnittestWellKnownTypes.cs \
csharp/src/Google.Protobuf.Test/TestProtos/Unittest.cs \
csharp/src/Google.Protobuf.Test/WellKnownTypes/AnyTest.cs \
csharp/src/Google.Protobuf.Test/WellKnownTypes/DurationTest.cs \
csharp/src/Google.Protobuf.Test/WellKnownTypes/FieldMaskTest.cs \
......
......@@ -52,8 +52,12 @@ $PROTOC -Isrc -Icsharp/protos \
csharp/protos/unittest_proto3.proto \
csharp/protos/unittest_import_proto3.proto \
csharp/protos/unittest_import_public_proto3.proto \
csharp/protos/unittest.proto \
csharp/protos/unittest_import.proto \
csharp/protos/unittest_import_public.proto \
src/google/protobuf/unittest_well_known_types.proto \
src/google/protobuf/test_messages_proto3.proto
src/google/protobuf/test_messages_proto3.proto \
src/google/protobuf/test_messages_proto2.proto
# AddressBook sample protos
$PROTOC -Iexamples -Isrc --csharp_out=csharp/src/AddressBook \
......
This diff is collapsed.
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// 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.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// A proto file which is imported by unittest.proto to test importing.
syntax = "proto2";
// We don't put this in a package within proto2 because we need to make sure
// that the generated code doesn't depend on being in the proto2 namespace.
// In test_util.h we do
// "using namespace unittest_import = protobuf_unittest_import".
package protobuf_unittest_import_proto2;
option optimize_for = SPEED;
option cc_enable_arenas = true;
option csharp_namespace = "Google.Protobuf.TestProtos.Proto2";
// Test public import
import public "unittest_import_public.proto";
message ImportMessage {
optional int32 d = 1;
}
enum ImportEnum {
IMPORT_FOO = 7;
IMPORT_BAR = 8;
IMPORT_BAZ = 9;
}
// To use an enum in a map, it must has the first value as 0.
enum ImportEnumForMap {
UNKNOWN = 0;
FOO = 1;
BAR = 2;
}
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// 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.
// Author: liujisi@google.com (Pherl Liu)
syntax = "proto2";
package protobuf_unittest_import_proto2;
option csharp_namespace = "Google.Protobuf.TestProtos.Proto2";
message PublicImportMessage {
optional int32 e = 1;
}
......@@ -48,7 +48,9 @@ namespace Google.Protobuf.Conformance
// This way we get the binary streams instead of readers/writers.
var input = new BinaryReader(Console.OpenStandardInput());
var output = new BinaryWriter(Console.OpenStandardOutput());
var typeRegistry = TypeRegistry.FromMessages(ProtobufTestMessages.Proto3.TestAllTypesProto3.Descriptor);
var typeRegistry = TypeRegistry.FromMessages(
ProtobufTestMessages.Proto3.TestAllTypesProto3.Descriptor,
ProtobufTestMessages.Proto2.TestAllTypesProto2.Descriptor);
int count = 0;
while (RunTest(input, output, typeRegistry))
......@@ -81,7 +83,7 @@ namespace Google.Protobuf.Conformance
private static ConformanceResponse PerformRequest(ConformanceRequest request, TypeRegistry typeRegistry)
{
ProtobufTestMessages.Proto3.TestAllTypesProto3 message;
IMessage message;
try
{
switch (request.PayloadCase)
......@@ -101,7 +103,13 @@ namespace Google.Protobuf.Conformance
}
else if (request.MessageType.Equals("protobuf_test_messages.proto2.TestAllTypesProto2"))
{
return new ConformanceResponse { Skipped = "CSharp doesn't support proto2" };
ExtensionRegistry registry = new ExtensionRegistry()
{
ProtobufTestMessages.Proto2.TestMessagesProto2Extensions.ExtensionInt32,
ProtobufTestMessages.Proto2.TestAllTypesProto2.Types.MessageSetCorrectExtension1.Extensions.MessageSetExtension,
ProtobufTestMessages.Proto2.TestAllTypesProto2.Types.MessageSetCorrectExtension2.Extensions.MessageSetExtension
};
message = ProtobufTestMessages.Proto2.TestAllTypesProto2.Parser.WithExtensionRegistry(registry).ParseFrom(request.ProtobufPayload);
}
else
{
......
using Google.Protobuf.TestProtos.Proto2;
using NUnit.Framework;
using static Google.Protobuf.TestProtos.Proto2.UnittestExtensions;
namespace Google.Protobuf
{
public class ExtensionSetTest
{
[Test]
public void EmptyExtensionSet()
{
ExtensionSet<TestAllExtensions> extensions = new ExtensionSet<TestAllExtensions>();
Assert.AreEqual(0, extensions.CalculateSize());
}
[Test]
public void MergeExtensionSet()
{
ExtensionSet<TestAllExtensions> extensions = null;
ExtensionSet.Set(ref extensions, OptionalBoolExtension, true);
ExtensionSet<TestAllExtensions> other = null;
Assert.IsFalse(ExtensionSet.Has(ref other, OptionalBoolExtension));
ExtensionSet.MergeFrom(ref other, extensions);
Assert.IsTrue(ExtensionSet.Has(ref other, OptionalBoolExtension));
}
[Test]
public void TestMergeCodedInput()
{
var message = new TestAllExtensions();
message.SetExtension(OptionalBoolExtension, true);
var serialized = message.ToByteArray();
var other = TestAllExtensions.Parser
.WithExtensionRegistry(new ExtensionRegistry() { OptionalBoolExtension })
.ParseFrom(serialized);
Assert.AreEqual(message, other);
Assert.AreEqual(message.CalculateSize(), other.CalculateSize());
}
[Test]
public void TestMergeMessage()
{
var message = new TestAllExtensions();
message.SetExtension(OptionalBoolExtension, true);
var other = new TestAllExtensions();
Assert.AreNotEqual(message, other);
Assert.AreNotEqual(message.CalculateSize(), other.CalculateSize());
other.MergeFrom(message);
Assert.AreEqual(message, other);
Assert.AreEqual(message.CalculateSize(), other.CalculateSize());
}
[Test]
public void TestEquals()
{
var message = new TestAllExtensions();
message.SetExtension(OptionalBoolExtension, true);
var other = new TestAllExtensions();
Assert.AreNotEqual(message, other);
Assert.AreNotEqual(message.CalculateSize(), other.CalculateSize());
other.SetExtension(OptionalBoolExtension, true);
Assert.AreEqual(message, other);
Assert.AreEqual(message.CalculateSize(), other.CalculateSize());
}
[Test]
public void TestHashCode()
{
var message = new TestAllExtensions();
var hashCode = message.GetHashCode();
message.SetExtension(OptionalBoolExtension, true);
Assert.AreNotEqual(hashCode, message.GetHashCode());
}
[Test]
public void TestClone()
{
var message = new TestAllExtensions();
message.SetExtension(OptionalBoolExtension, true);
var other = message.Clone();
Assert.AreEqual(message, other);
Assert.AreEqual(message.CalculateSize(), message.CalculateSize());
}
}
}
This diff is collapsed.
......@@ -33,6 +33,7 @@
using System;
using System.IO;
using Google.Protobuf.TestProtos;
using Proto2 = Google.Protobuf.TestProtos.Proto2;
using NUnit.Framework;
using System.Collections;
using System.Collections.Generic;
......@@ -44,7 +45,7 @@ namespace Google.Protobuf
/// <summary>
/// Tests around the generated TestAllTypes message.
/// </summary>
public class GeneratedMessageTest
public partial class GeneratedMessageTest
{
[Test]
public void EmptyMessageFieldDistinctFromMissingMessageField()
......
This diff is collapsed.
......@@ -165,7 +165,7 @@ namespace UnitTest.Issues.TestProtos {
}
/// <summary>Holder for extension identifiers generated from the top level of unittest_custom_options_proto3.proto</summary>
internal static partial class UnittestCustomOptionsProto3Extensions {
public static partial class UnittestCustomOptionsProto3Extensions {
public static readonly pb::Extension<global::Google.Protobuf.Reflection.FileOptions, ulong> FileOpt1 =
new pb::Extension<global::Google.Protobuf.Reflection.FileOptions, ulong>(7736974, pb::FieldCodec.ForUInt64(61895792, 0UL));
public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, int> MessageOpt1 =
......@@ -2138,7 +2138,7 @@ namespace UnitTest.Issues.TestProtos {
#region Extensions
/// <summary>Container for extensions for other messages declared in the ComplexOptionType4 message type.</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
internal static partial class Extensions {
public static partial class Extensions {
public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, global::UnitTest.Issues.TestProtos.ComplexOptionType2.Types.ComplexOptionType4> ComplexOpt4 =
new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, global::UnitTest.Issues.TestProtos.ComplexOptionType2.Types.ComplexOptionType4>(7633546, pb::FieldCodec.ForMessage(61068370, global::UnitTest.Issues.TestProtos.ComplexOptionType2.Types.ComplexOptionType4.Parser));
}
......
// <auto-generated>
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: unittest_import.proto
// </auto-generated>
#pragma warning disable 1591, 0612, 3021
#region Designer generated code
using pb = global::Google.Protobuf;
using pbc = global::Google.Protobuf.Collections;
using pbr = global::Google.Protobuf.Reflection;
using scg = global::System.Collections.Generic;
namespace Google.Protobuf.TestProtos.Proto2 {
/// <summary>Holder for reflection information generated from unittest_import.proto</summary>
public static partial class UnittestImportReflection {
#region Descriptor
/// <summary>File descriptor for unittest_import.proto</summary>
public static pbr::FileDescriptor Descriptor {
get { return descriptor; }
}
private static pbr::FileDescriptor descriptor;
static UnittestImportReflection() {
byte[] descriptorData = global::System.Convert.FromBase64String(
string.Concat(
"ChV1bml0dGVzdF9pbXBvcnQucHJvdG8SH3Byb3RvYnVmX3VuaXR0ZXN0X2lt",
"cG9ydF9wcm90bzIaHHVuaXR0ZXN0X2ltcG9ydF9wdWJsaWMucHJvdG8iGgoN",
"SW1wb3J0TWVzc2FnZRIJCgFkGAEgASgFKjwKCkltcG9ydEVudW0SDgoKSU1Q",
"T1JUX0ZPTxAHEg4KCklNUE9SVF9CQVIQCBIOCgpJTVBPUlRfQkFaEAkqMQoQ",
"SW1wb3J0RW51bUZvck1hcBILCgdVTktOT1dOEAASBwoDRk9PEAESBwoDQkFS",
"EAJCKUgB+AEBqgIhR29vZ2xlLlByb3RvYnVmLlRlc3RQcm90b3MuUHJvdG8y",
"UAA="));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { global::Google.Protobuf.TestProtos.Proto2.UnittestImportPublicReflection.Descriptor, },
new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.TestProtos.Proto2.ImportEnum), typeof(global::Google.Protobuf.TestProtos.Proto2.ImportEnumForMap), }, null, new pbr::GeneratedClrTypeInfo[] {
new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.Proto2.ImportMessage), global::Google.Protobuf.TestProtos.Proto2.ImportMessage.Parser, new[]{ "D" }, null, null, null, null)
}));
}
#endregion
}
#region Enums
public enum ImportEnum {
[pbr::OriginalName("IMPORT_FOO")] ImportFoo = 7,
[pbr::OriginalName("IMPORT_BAR")] ImportBar = 8,
[pbr::OriginalName("IMPORT_BAZ")] ImportBaz = 9,
}
/// <summary>
/// To use an enum in a map, it must has the first value as 0.
/// </summary>
public enum ImportEnumForMap {
[pbr::OriginalName("UNKNOWN")] Unknown = 0,
[pbr::OriginalName("FOO")] Foo = 1,
[pbr::OriginalName("BAR")] Bar = 2,
}
#endregion
#region Messages
public sealed partial class ImportMessage : pb::IMessage<ImportMessage> {
private static readonly pb::MessageParser<ImportMessage> _parser = new pb::MessageParser<ImportMessage>(() => new ImportMessage());
private pb::UnknownFieldSet _unknownFields;
private int _hasBits0;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static pb::MessageParser<ImportMessage> Parser { get { return _parser; } }
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static pbr::MessageDescriptor Descriptor {
get { return global::Google.Protobuf.TestProtos.Proto2.UnittestImportReflection.Descriptor.MessageTypes[0]; }
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
pbr::MessageDescriptor pb::IMessage.Descriptor {
get { return Descriptor; }
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public ImportMessage() {
OnConstruction();
}
partial void OnConstruction();
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public ImportMessage(ImportMessage other) : this() {
_hasBits0 = other._hasBits0;
d_ = other.d_;
_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public ImportMessage Clone() {
return new ImportMessage(this);
}
/// <summary>Field number for the "d" field.</summary>
public const int DFieldNumber = 1;
private readonly static int DDefaultValue = 0;
private int d_;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public int D {
get { if ((_hasBits0 & 1) != 0) { return d_; } else { return DDefaultValue; } }
set {
_hasBits0 |= 1;
d_ = value;
}
}
/// <summary>Gets whether the "d" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasD {
get { return (_hasBits0 & 1) != 0; }
}
/// <summary>Clears the value of the "d" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearD() {
_hasBits0 &= ~1;
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public override bool Equals(object other) {
return Equals(other as ImportMessage);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool Equals(ImportMessage other) {
if (ReferenceEquals(other, null)) {
return false;
}
if (ReferenceEquals(other, this)) {
return true;
}
if (D != other.D) return false;
return Equals(_unknownFields, other._unknownFields);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public override int GetHashCode() {
int hash = 1;
if (HasD) hash ^= D.GetHashCode();
if (_unknownFields != null) {
hash ^= _unknownFields.GetHashCode();
}
return hash;
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public override string ToString() {
return pb::JsonFormatter.ToDiagnosticString(this);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void WriteTo(pb::CodedOutputStream output) {
if (HasD) {
output.WriteRawTag(8);
output.WriteInt32(D);
}
if (_unknownFields != null) {
_unknownFields.WriteTo(output);
}
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public int CalculateSize() {
int size = 0;
if (HasD) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(D);
}
if (_unknownFields != null) {
size += _unknownFields.CalculateSize();
}
return size;
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void MergeFrom(ImportMessage other) {
if (other == null) {
return;
}
if (other.HasD) {
D = other.D;
}
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void MergeFrom(pb::CodedInputStream input) {
uint tag;
while ((tag = input.ReadTag()) != 0) {
switch(tag) {
default:
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
break;
case 8: {
D = input.ReadInt32();
break;
}
}
}
}
}
#endregion
}
#endregion Designer generated code
// <auto-generated>
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: unittest_import_public.proto
// </auto-generated>
#pragma warning disable 1591, 0612, 3021
#region Designer generated code
using pb = global::Google.Protobuf;
using pbc = global::Google.Protobuf.Collections;
using pbr = global::Google.Protobuf.Reflection;
using scg = global::System.Collections.Generic;
namespace Google.Protobuf.TestProtos.Proto2 {
/// <summary>Holder for reflection information generated from unittest_import_public.proto</summary>
public static partial class UnittestImportPublicReflection {
#region Descriptor
/// <summary>File descriptor for unittest_import_public.proto</summary>
public static pbr::FileDescriptor Descriptor {
get { return descriptor; }
}
private static pbr::FileDescriptor descriptor;
static UnittestImportPublicReflection() {
byte[] descriptorData = global::System.Convert.FromBase64String(
string.Concat(
"Chx1bml0dGVzdF9pbXBvcnRfcHVibGljLnByb3RvEh9wcm90b2J1Zl91bml0",
"dGVzdF9pbXBvcnRfcHJvdG8yIiAKE1B1YmxpY0ltcG9ydE1lc3NhZ2USCQoB",
"ZRgBIAEoBUIkqgIhR29vZ2xlLlByb3RvYnVmLlRlc3RQcm90b3MuUHJvdG8y"));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.Proto2.PublicImportMessage), global::Google.Protobuf.TestProtos.Proto2.PublicImportMessage.Parser, new[]{ "E" }, null, null, null, null)
}));
}
#endregion
}
#region Messages
public sealed partial class PublicImportMessage : pb::IMessage<PublicImportMessage> {
private static readonly pb::MessageParser<PublicImportMessage> _parser = new pb::MessageParser<PublicImportMessage>(() => new PublicImportMessage());
private pb::UnknownFieldSet _unknownFields;
private int _hasBits0;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static pb::MessageParser<PublicImportMessage> Parser { get { return _parser; } }
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static pbr::MessageDescriptor Descriptor {
get { return global::Google.Protobuf.TestProtos.Proto2.UnittestImportPublicReflection.Descriptor.MessageTypes[0]; }
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
pbr::MessageDescriptor pb::IMessage.Descriptor {
get { return Descriptor; }
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public PublicImportMessage() {
OnConstruction();
}
partial void OnConstruction();
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public PublicImportMessage(PublicImportMessage other) : this() {
_hasBits0 = other._hasBits0;
e_ = other.e_;
_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public PublicImportMessage Clone() {
return new PublicImportMessage(this);
}
/// <summary>Field number for the "e" field.</summary>
public const int EFieldNumber = 1;
private readonly static int EDefaultValue = 0;
private int e_;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public int E {
get { if ((_hasBits0 & 1) != 0) { return e_; } else { return EDefaultValue; } }
set {
_hasBits0 |= 1;
e_ = value;
}
}
/// <summary>Gets whether the "e" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasE {
get { return (_hasBits0 & 1) != 0; }
}
/// <summary>Clears the value of the "e" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearE() {
_hasBits0 &= ~1;
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public override bool Equals(object other) {
return Equals(other as PublicImportMessage);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool Equals(PublicImportMessage other) {
if (ReferenceEquals(other, null)) {
return false;
}
if (ReferenceEquals(other, this)) {
return true;
}
if (E != other.E) return false;
return Equals(_unknownFields, other._unknownFields);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public override int GetHashCode() {
int hash = 1;
if (HasE) hash ^= E.GetHashCode();
if (_unknownFields != null) {
hash ^= _unknownFields.GetHashCode();
}
return hash;
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public override string ToString() {
return pb::JsonFormatter.ToDiagnosticString(this);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void WriteTo(pb::CodedOutputStream output) {
if (HasE) {
output.WriteRawTag(8);
output.WriteInt32(E);
}
if (_unknownFields != null) {
_unknownFields.WriteTo(output);
}
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public int CalculateSize() {
int size = 0;
if (HasE) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(E);
}
if (_unknownFields != null) {
size += _unknownFields.CalculateSize();
}
return size;
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void MergeFrom(PublicImportMessage other) {
if (other == null) {
return;
}
if (other.HasE) {
E = other.E;
}
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void MergeFrom(pb::CodedInputStream input) {
uint tag;
while ((tag = input.ReadTag()) != 0) {
switch(tag) {
default:
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
break;
case 8: {
E = input.ReadInt32();
break;
}
}
}
}
}
#endregion
}
#endregion Designer generated code
......@@ -33,12 +33,25 @@
using System;
using System.IO;
using Google.Protobuf.TestProtos;
using Proto2 = Google.Protobuf.TestProtos.Proto2;
using NUnit.Framework;
namespace Google.Protobuf
{
public class UnknownFieldSetTest
{
public class Data
{
public static System.Collections.IEnumerable Messages
{
get
{
yield return SampleMessages.CreateFullTestAllTypesProto2();
yield return SampleMessages.CreateFullTestAllTypes();
}
}
}
[Test]
public void EmptyUnknownFieldSet()
{
......@@ -60,24 +73,23 @@ namespace Google.Protobuf
}
[Test]
public void TestMergeCodedInput()
[TestCaseSource(typeof(Data), "Messages")]
public void TestMergeCodedInput(IMessage message)
{
var message = SampleMessages.CreateFullTestAllTypes();
var emptyMessage = new TestEmptyMessage();
emptyMessage.MergeFrom(message.ToByteArray());
Assert.AreEqual(message.CalculateSize(), emptyMessage.CalculateSize());
Assert.AreEqual(message.ToByteArray(), emptyMessage.ToByteArray());
var newMessage = new TestAllTypes();
newMessage.MergeFrom(emptyMessage.ToByteArray());
var newMessage = message.Descriptor.Parser.ParseFrom(emptyMessage.ToByteArray());
Assert.AreEqual(message, newMessage);
Assert.AreEqual(message.CalculateSize(), newMessage.CalculateSize());
}
[Test]
public void TestMergeMessage()
[TestCaseSource(typeof(Data), "Messages")]
public void TestMergeMessage(IMessage message)
{
var message = SampleMessages.CreateFullTestAllTypes();
var emptyMessage = new TestEmptyMessage();
var otherEmptyMessage = new TestEmptyMessage();
emptyMessage.MergeFrom(message.ToByteArray());
......@@ -88,9 +100,9 @@ namespace Google.Protobuf
}
[Test]
public void TestEquals()
[TestCaseSource(typeof(Data), "Messages")]
public void TestEquals(IMessage message)
{
var message = SampleMessages.CreateFullTestAllTypes();
var emptyMessage = new TestEmptyMessage();
var otherEmptyMessage = new TestEmptyMessage();
Assert.AreEqual(emptyMessage, otherEmptyMessage);
......@@ -101,9 +113,9 @@ namespace Google.Protobuf
}
[Test]
public void TestHashCode()
[TestCaseSource(typeof(Data), "Messages")]
public void TestHashCode(IMessage message)
{
var message = SampleMessages.CreateFullTestAllTypes();
var emptyMessage = new TestEmptyMessage();
int hashCode = emptyMessage.GetHashCode();
emptyMessage.MergeFrom(message.ToByteArray());
......@@ -111,7 +123,8 @@ namespace Google.Protobuf
}
[Test]
public void TestClone()
[TestCaseSource(typeof(Data), "Messages")]
public void TestClone(IMessage message)
{
var emptyMessage = new TestEmptyMessage();
var otherEmptyMessage = new TestEmptyMessage();
......@@ -119,7 +132,6 @@ namespace Google.Protobuf
Assert.AreEqual(emptyMessage.CalculateSize(), otherEmptyMessage.CalculateSize());
Assert.AreEqual(emptyMessage.ToByteArray(), otherEmptyMessage.ToByteArray());
var message = SampleMessages.CreateFullTestAllTypes();
emptyMessage.MergeFrom(message.ToByteArray());
otherEmptyMessage = emptyMessage.Clone();
Assert.AreEqual(message.CalculateSize(), otherEmptyMessage.CalculateSize());
......@@ -127,9 +139,9 @@ namespace Google.Protobuf
}
[Test]
public void TestDiscardUnknownFields()
[TestCaseSource(typeof(Data), "Messages")]
public void TestDiscardUnknownFields(IMessage message)
{
var message = SampleMessages.CreateFullTestAllTypes();
var goldenEmptyMessage = new TestEmptyMessage();
byte[] data = message.ToByteArray();
int fullSize = message.CalculateSize();
......
......@@ -148,6 +148,10 @@ namespace Google.Protobuf.Collections
{
var sizeCalculator = codec.ValueSizeCalculator;
int size = count * CodedOutputStream.ComputeRawVarint32Size(tag);
if (codec.EndTag != 0)
{
size += count * CodedOutputStream.ComputeRawVarint32Size(codec.EndTag);
}
for (int i = 0; i < count; i++)
{
size += sizeCalculator(array[i]);
......
......@@ -89,7 +89,7 @@ namespace Google.Protobuf
/// <summary>
/// Gets the value of the specified repeated extension, registering it if it doesn't exist
/// </summary>
public static RepeatedField<TValue> GetOrRegister<TTarget, TValue>(ref ExtensionSet<TTarget> set, RepeatedExtension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>
public static RepeatedField<TValue> GetOrInitialize<TTarget, TValue>(ref ExtensionSet<TTarget> set, RepeatedExtension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>
{
IExtensionValue value;
if (set == null)
......@@ -115,6 +115,8 @@ namespace Google.Protobuf
/// </summary>
public static void Set<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension, TValue value) where TTarget : IExtendableMessage<TTarget>
{
ProtoPreconditions.CheckNotNullUnconstrained(value, nameof(value));
IExtensionValue extensionValue;
if (set == null)
{
......@@ -330,5 +332,10 @@ namespace Google.Protobuf
value.WriteTo(stream);
}
}
internal bool IsInitialized()
{
return ValuesByNumber.Values.All(v => v.IsInitialized());
}
}
}
......@@ -32,6 +32,7 @@
using Google.Protobuf.Collections;
using System;
using System.Linq;
namespace Google.Protobuf
{
......@@ -41,11 +42,11 @@ namespace Google.Protobuf
void MergeFrom(IExtensionValue value);
void WriteTo(CodedOutputStream output);
int CalculateSize();
bool IsInitialized();
}
internal sealed class ExtensionValue<T> : IExtensionValue
{
private bool hasValue;
private T field;
private FieldCodec<T> codec;
......@@ -57,10 +58,6 @@ namespace Google.Protobuf
public int CalculateSize()
{
if (!hasValue)
{
return 0;
}
return codec.CalculateSizeWithTag(field);
}
......@@ -68,7 +65,6 @@ namespace Google.Protobuf
{
return new ExtensionValue<T>(codec)
{
hasValue = hasValue,
field = field is IDeepCloneable<T> ? (field as IDeepCloneable<T>).Clone() : field
};
}
......@@ -80,7 +76,6 @@ namespace Google.Protobuf
return other is ExtensionValue<T>
&& codec.Equals((other as ExtensionValue<T>).codec)
&& hasValue.Equals((other as ExtensionValue<T>).hasValue)
&& Equals(field, (other as ExtensionValue<T>).field);
// we check for equality in the codec since we could have equal field values however the values could be written in different ways
}
......@@ -90,7 +85,6 @@ namespace Google.Protobuf
unchecked
{
int hash = 17;
hash = hash * 31 + hasValue.GetHashCode();
hash = hash * 31 + field.GetHashCode();
hash = hash * 31 + codec.GetHashCode();
return hash;
......@@ -99,7 +93,6 @@ namespace Google.Protobuf
public void MergeFrom(CodedInputStream input)
{
hasValue = true;
codec.ValueMerger(input, ref field);
}
......@@ -107,24 +100,18 @@ namespace Google.Protobuf
{
if (value is ExtensionValue<T>)
{
var extensionValue = value as ExtensionValue<T>;
if (extensionValue.hasValue)
{
hasValue |= codec.FieldMerger(ref field, extensionValue.field);
}
var extensionValue = value as ExtensionValue<T>;
codec.FieldMerger(ref field, extensionValue.field);
}
}
public void WriteTo(CodedOutputStream output)
{
if (hasValue)
output.WriteTag(codec.Tag);
codec.ValueWriter(output, field);
if (codec.EndTag != 0)
{
output.WriteTag(codec.Tag);
codec.ValueWriter(output, field);
if (codec.EndTag != 0)
{
output.WriteTag(codec.EndTag);
}
output.WriteTag(codec.EndTag);
}
}
......@@ -132,11 +119,20 @@ namespace Google.Protobuf
public void SetValue(T value)
{
hasValue = true;
field = value;
}
public bool HasValue => hasValue;
public bool IsInitialized()
{
if (field is IMessage)
{
return (field as IMessage).IsInitialized();
}
else
{
return true;
}
}
}
internal sealed class RepeatedExtensionValue<T> : IExtensionValue
......@@ -203,5 +199,26 @@ namespace Google.Protobuf
}
public RepeatedField<T> GetValue() => field;
public bool IsInitialized()
{
for (int i = 0; i < field.Count; i++)
{
var element = field[i];
if (element is IMessage)
{
if (!(element as IMessage).IsInitialized())
{
return false;
}
}
else
{
break;
}
}
return true;
}
}
}
This diff is collapsed.
......@@ -35,7 +35,8 @@ using Google.Protobuf.Collections;
namespace Google.Protobuf
{
/// <summary>
/// Generic interface for a Protocol Buffers message containing one or more extensions, where the type parameter is expected to be the same type as the implementation class
/// Generic interface for a Protocol Buffers message containing one or more extensions, where the type parameter is expected to be the same type as the implementation class.
/// This interface is experiemental and is subject to change.
/// </summary>
public interface IExtendableMessage<T> : IMessage<T> where T : IExtendableMessage<T>
{
......@@ -46,14 +47,14 @@ namespace Google.Protobuf
/// <summary>
/// Gets the value of the specified repeated extension or null if the extension isn't registered in this set.
/// For a version of this method that never returns null, use <see cref="IExtendableMessage{T}.GetOrRegisterExtension{TValue}(RepeatedExtension{T, TValue})"/>
/// For a version of this method that never returns null, use <see cref="IExtendableMessage{T}.GetOrInitializeExtension{TValue}(RepeatedExtension{T, TValue})"/>
/// </summary>
RepeatedField<TValue> GetExtension<TValue>(RepeatedExtension<T, TValue> extension);
/// <summary>
/// Gets the value of the specified repeated extension, registering it if it isn't
/// <summary>
/// Gets the value of the specified repeated extension, registering it if it hasn't already been registered.
/// </summary>
RepeatedField<TValue> GetOrRegisterExtension<TValue>(RepeatedExtension<T, TValue> extension);
RepeatedField<TValue> GetOrInitializeExtension<TValue>(RepeatedExtension<T, TValue> extension);
/// <summary>
/// Sets the value of the specified extension
......@@ -65,13 +66,13 @@ namespace Google.Protobuf
/// </summary>
bool HasExtension<TValue>(Extension<T, TValue> extension);
/// <summary>
/// Clears the value of the specified extension
/// <summary>
/// Clears the value of the specified extension
/// </summary>
void ClearExtension<TValue>(Extension<T, TValue> extension);
/// <summary>
/// Clears the value of the specified repeated extension
/// <summary>
/// Clears the value of the specified repeated extension
/// </summary>
void ClearExtension<TValue>(RepeatedExtension<T, TValue> extension);
}
......
......@@ -148,11 +148,16 @@ namespace Google.Protobuf
/// </summary>
public static bool IsInitialized(this IMessage message)
{
if (message.Descriptor.File.Proto.Syntax != "proto2")
if (message.Descriptor.File.Syntax == Syntax.Proto3)
{
return true;
}
if (!message.Descriptor.IsExtensionsInitialized(message))
{
return false;
}
return message.Descriptor
.Fields
.InDeclarationOrder()
......@@ -160,8 +165,16 @@ namespace Google.Protobuf
{
if (f.IsMap)
{
var map = (IDictionary)f.Accessor.GetValue(message);
return map.Values.OfType<IMessage>().All(IsInitialized);
var valueField = f.MessageType.Fields[2];
if (valueField.FieldType == FieldType.Message)
{
var map = (IDictionary)f.Accessor.GetValue(message);
return map.Values.Cast<IMessage>().All(IsInitialized);
}
else
{
return true;
}
}
else if (f.IsRepeated && f.FieldType == FieldType.Message || f.FieldType == FieldType.Group)
{
......
......@@ -254,11 +254,8 @@ namespace Google.Protobuf.Reflection
if (extensionValue is ExtensionValue<T>)
{
ExtensionValue<T> single = extensionValue as ExtensionValue<T>;
if (single.HasValue)
{
value = single.GetValue();
return true;
}
value = single.GetValue();
return true;
}
else if (extensionValue is RepeatedExtensionValue<T>)
{
......@@ -279,11 +276,8 @@ namespace Google.Protobuf.Reflection
var typeArgs = typeInfo.GenericTypeArguments;
if (typeArgs.Length == 1 && typeArgs[0].GetTypeInfo().IsEnum)
{
if ((bool)typeInfo.GetDeclaredProperty(nameof(ExtensionValue<T>.HasValue)).GetValue(extensionValue))
{
value = (T)typeInfo.GetDeclaredMethod(nameof(ExtensionValue<T>.GetValue)).Invoke(extensionValue, EmptyParameters);
return true;
}
value = (T)typeInfo.GetDeclaredMethod(nameof(ExtensionValue<T>.GetValue)).Invoke(extensionValue, EmptyParameters);
return true;
}
}
else if (type.GetGenericTypeDefinition() == typeof(RepeatedExtensionValue<>))
......
......@@ -128,17 +128,16 @@ namespace Google.Protobuf.Reflection
/// <summary>
/// The (possibly empty) set of custom options for this enum.
/// </summary>
//[Obsolete("CustomOptions are obsolete. Use GetOption")]
[Obsolete("CustomOptions are obsolete. Use GetOption")]
public CustomOptions CustomOptions => new CustomOptions(Proto.Options._extensions?.ValuesByNumber);
/* // uncomment this in the full proto2 support PR
/// <summary>
/// Gets a single value enum option for this descriptor
/// </summary>
public T GetOption<T>(Extension<EnumOptions, T> extension)
{
var value = Proto.Options.GetExtension(extension);
return value is IDeepCloneable<T> clonable ? clonable.Clone() : value;
return value is IDeepCloneable<T> ? (value as IDeepCloneable<T>).Clone() : value;
}
/// <summary>
......@@ -148,6 +147,5 @@ namespace Google.Protobuf.Reflection
{
return Proto.Options.GetExtension(extension).Clone();
}
*/
}
}
\ No newline at end of file
......@@ -73,17 +73,16 @@ namespace Google.Protobuf.Reflection
/// <summary>
/// The (possibly empty) set of custom options for this enum value.
/// </summary>
//[Obsolete("CustomOptions are obsolete. Use GetOption")]
[Obsolete("CustomOptions are obsolete. Use GetOption")]
public CustomOptions CustomOptions => new CustomOptions(Proto.Options._extensions?.ValuesByNumber);
/* // uncomment this in the full proto2 support PR
/// <summary>
/// Gets a single value enum option for this descriptor
/// </summary>
public T GetOption<T>(Extension<EnumValueOptions, T> extension)
{
var value = Proto.Options.GetExtension(extension);
return value is IDeepCloneable<T> clonable ? clonable.Clone() : value;
return value is IDeepCloneable<T> ? (value as IDeepCloneable<T>).Clone() : value;
}
/// <summary>
......@@ -93,7 +92,6 @@ namespace Google.Protobuf.Reflection
{
return Proto.Options.GetExtension(extension).Clone();
}
*/
}
}
\ No newline at end of file
......@@ -39,7 +39,7 @@ namespace Google.Protobuf.Reflection
/// <summary>
/// A collection to simplify retrieving the descriptors of extensions in a descriptor for a message
/// </summary>
public class ExtensionCollection
public sealed class ExtensionCollection
{
private IDictionary<MessageDescriptor, IList<FieldDescriptor>> extensionsByTypeInDeclarationOrder;
private IDictionary<MessageDescriptor, IList<FieldDescriptor>> extensionsByTypeInNumberOrder;
......
......@@ -66,7 +66,10 @@ namespace Google.Protobuf.Reflection
internal FieldDescriptorProto Proto { get; }
internal Extension Extension { get; }
/// <summary>
/// An extension identifier for this field, or <c>null</c> if this field isn't an extension.
/// </summary>
public Extension Extension { get; }
internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file,
MessageDescriptor parent, int index, string propertyName, Extension extension)
......@@ -201,7 +204,25 @@ namespace Google.Protobuf.Reflection
/// <summary>
/// Returns <c>true</c> if this field is a packed, repeated field; <c>false</c> otherwise.
/// </summary>
public bool IsPacked => File.Proto.Syntax == "proto2" ? Proto.Options?.Packed ?? false : !Proto.Options.HasPacked || Proto.Options.Packed;
public bool IsPacked
{
get
{
if (File.Syntax != Syntax.Proto3)
{
return Proto.Options?.Packed ?? false;
}
else
{
return !Proto.Options.HasPacked || Proto.Options.Packed;
}
}
}
/// <summary>
/// Returns <c>true</c> if this field extends another message type; <c>false</c> otherwise.
/// </summary>
public bool IsExtension => Proto.HasExtendee;
/// <summary>
/// Returns the type of the field.
......@@ -277,17 +298,16 @@ namespace Google.Protobuf.Reflection
/// <summary>
/// The (possibly empty) set of custom options for this field.
/// </summary>
//[Obsolete("CustomOptions are obsolete. Use GetOption")]
[Obsolete("CustomOptions are obsolete. Use GetOption")]
public CustomOptions CustomOptions => new CustomOptions(Proto.Options._extensions?.ValuesByNumber);
/* // uncomment this in the full proto2 support PR
/// <summary>
/// Gets a single value enum option for this descriptor
/// </summary>
public T GetOption<T>(Extension<FieldOptions, T> extension)
{
var value = Proto.Options.GetExtension(extension);
return value is IDeepCloneable<T> clonable ? clonable.Clone() : value;
return value is IDeepCloneable<T> ? (value as IDeepCloneable<T>).Clone() : value;
}
/// <summary>
......@@ -297,7 +317,6 @@ namespace Google.Protobuf.Reflection
{
return Proto.Options.GetExtension(extension).Clone();
}
*/
/// <summary>
/// Look up and cross-link all field types etc.
......@@ -378,6 +397,11 @@ namespace Google.Protobuf.Reflection
private IFieldAccessor CreateAccessor()
{
if (Extension != null)
{
return new ExtensionAccessor(this);
}
// If we're given no property name, that's because we really don't want an accessor.
// This could be because it's a map message, or it could be that we're loading a FileDescriptor dynamically.
// TODO: Support dynamic messages.
......@@ -386,10 +410,6 @@ namespace Google.Protobuf.Reflection
return null;
}
if (Extension != null)
{
return new ExtensionAccessor(this);
}
var property = ContainingType.ClrType.GetProperty(propertyName);
if (property == null)
{
......
......@@ -42,6 +42,25 @@ using static Google.Protobuf.Reflection.SourceCodeInfo.Types;
namespace Google.Protobuf.Reflection
{
/// <summary>
/// The syntax of a .proto file
/// </summary>
public enum Syntax
{
/// <summary>
/// Proto2 syntax
/// </summary>
Proto2,
/// <summary>
/// Proto3 syntax
/// </summary>
Proto3,
/// <summary>
/// An unknown declared syntax
/// </summary>
Unknown
}
/// <summary>
/// Describes a .proto file, including everything defined within.
/// IDescriptor is implemented such that the File property returns this descriptor,
......@@ -87,6 +106,19 @@ namespace Google.Protobuf.Reflection
Extensions = new ExtensionCollection(this, generatedCodeInfo?.Extensions);
declarations = new Lazy<Dictionary<IDescriptor, DescriptorDeclaration>>(CreateDeclarationMap, LazyThreadSafetyMode.ExecutionAndPublication);
if (!proto.HasSyntax || proto.Syntax == "proto2")
{
Syntax = Syntax.Proto2;
}
else if (proto.Syntax == "proto3")
{
Syntax = Syntax.Proto3;
}
else
{
Syntax = Syntax.Unknown;
}
}
private Dictionary<IDescriptor, DescriptorDeclaration> CreateDeclarationMap()
......@@ -217,6 +249,11 @@ namespace Google.Protobuf.Reflection
/// </value>
internal FileDescriptorProto Proto { get; }
/// <summary>
/// The syntax of the file
/// </summary>
public Syntax Syntax { get; }
/// <value>
/// The file name.
/// </value>
......@@ -504,17 +541,16 @@ namespace Google.Protobuf.Reflection
/// <summary>
/// The (possibly empty) set of custom options for this file.
/// </summary>
//[Obsolete("CustomOptions are obsolete. Use GetOption")]
[Obsolete("CustomOptions are obsolete. Use GetOption")]
public CustomOptions CustomOptions => new CustomOptions(Proto.Options._extensions?.ValuesByNumber);
/* // uncomment this in the full proto2 support PR
/// <summary>
/// Gets a single value enum option for this descriptor
/// </summary>
public T GetOption<T>(Extension<FileOptions, T> extension)
{
var value = Proto.Options.GetExtension(extension);
return value is IDeepCloneable<T> clonable ? clonable.Clone() : value;
return value is IDeepCloneable<T> ? (value as IDeepCloneable<T>).Clone() : value;
}
/// <summary>
......@@ -524,7 +560,6 @@ namespace Google.Protobuf.Reflection
{
return Proto.Options.GetExtension(extension).Clone();
}
*/
/// <summary>
/// Performs initialization for the given generic type argument.
......
......@@ -51,11 +51,6 @@ namespace Google.Protobuf.Reflection
/// </summary>
void Clear(IMessage message);
/// <summary>
/// Indicates whether the field in the specified message is set. For proto3 fields, this throws an <see cref="InvalidOperationException"/>
/// </summary>
bool HasValue(IMessage message);
/// <summary>
/// Fetches the field value. For repeated values, this will be an
/// <see cref="IList"/> implementation. For map values, this will be an
......@@ -63,6 +58,11 @@ namespace Google.Protobuf.Reflection
/// </summary>
object GetValue(IMessage message);
/// <summary>
/// Indicates whether the field in the specified message is set. For proto3 fields, this throws an <see cref="InvalidOperationException"/>
/// </summary>
bool HasValue(IMessage message);
/// <summary>
/// Mutator for single "simple" fields only.
/// </summary>
......
......@@ -34,6 +34,7 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
#if NET35
// Needed for ReadOnlyDictionary, which does not exist in .NET 3.5
using Google.Protobuf.Collections;
......@@ -63,6 +64,7 @@ namespace Google.Protobuf.Reflection
private readonly IList<FieldDescriptor> fieldsInDeclarationOrder;
private readonly IList<FieldDescriptor> fieldsInNumberOrder;
private readonly IDictionary<string, FieldDescriptor> jsonFieldMap;
private Func<IMessage, bool> extensionSetIsInitialized;
internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex, GeneratedClrTypeInfo generatedCodeInfo)
: base(file, file.ComputeFullName(parent, proto.Name), typeIndex)
......@@ -134,6 +136,21 @@ namespace Google.Protobuf.Reflection
internal DescriptorProto Proto { get; }
internal bool IsExtensionsInitialized(IMessage message)
{
if (Proto.ExtensionRange.Count == 0)
{
return true;
}
if (extensionSetIsInitialized == null)
{
extensionSetIsInitialized = ReflectionUtil.CreateIsInitializedCaller(ClrType);
}
return extensionSetIsInitialized(message);
}
/// <summary>
/// The CLR type used to represent message instances from this descriptor.
/// </summary>
......@@ -243,17 +260,16 @@ namespace Google.Protobuf.Reflection
/// <summary>
/// The (possibly empty) set of custom options for this message.
/// </summary>
//[Obsolete("CustomOptions are obsolete. Use GetOption")]
[Obsolete("CustomOptions are obsolete. Use GetOption")]
public CustomOptions CustomOptions => new CustomOptions(Proto.Options._extensions?.ValuesByNumber);
/* // uncomment this in the full proto2 support PR
/// <summary>
/// Gets a single value enum option for this descriptor
/// </summary>
public T GetOption<T>(Extension<MessageOptions, T> extension)
{
var value = Proto.Options.GetExtension(extension);
return value is IDeepCloneable<T> clonable ? clonable.Clone() : value;
return value is IDeepCloneable<T> ? (value as IDeepCloneable<T>).Clone() : value;
}
/// <summary>
......@@ -263,7 +279,6 @@ namespace Google.Protobuf.Reflection
{
return Proto.Options.GetExtension(extension).Clone();
}
*/
/// <summary>
/// Looks up and cross-links all fields and nested types.
......
......@@ -73,17 +73,16 @@ namespace Google.Protobuf.Reflection
/// <summary>
/// The (possibly empty) set of custom options for this method.
/// </summary>
//[Obsolete("CustomOptions are obsolete. Use GetOption")]
[Obsolete("CustomOptions are obsolete. Use GetOption")]
public CustomOptions CustomOptions => new CustomOptions(Proto.Options._extensions?.ValuesByNumber);
/* // uncomment this in the full proto2 support PR
/// <summary>
/// Gets a single value enum option for this descriptor
/// </summary>
public T GetOption<T>(Extension<MethodOptions, T> extension)
{
var value = Proto.Options.GetExtension(extension);
return value is IDeepCloneable<T> clonable ? clonable.Clone() : value;
return value is IDeepCloneable<T> ? (value as IDeepCloneable<T>).Clone() : value;
}
/// <summary>
......@@ -93,7 +92,6 @@ namespace Google.Protobuf.Reflection
{
return Proto.Options.GetExtension(extension).Clone();
}
*/
internal MethodDescriptor(MethodDescriptorProto proto, FileDescriptor file,
ServiceDescriptor parent, int index)
......
......@@ -105,17 +105,16 @@ namespace Google.Protobuf.Reflection
/// <summary>
/// The (possibly empty) set of custom options for this oneof.
/// </summary>
//[Obsolete("CustomOptions are obsolete. Use GetOption")]
[Obsolete("CustomOptions are obsolete. Use GetOption")]
public CustomOptions CustomOptions => new CustomOptions(proto.Options._extensions?.ValuesByNumber);
/* // uncomment this in the full proto2 support PR
/// <summary>
/// Gets a single value enum option for this descriptor
/// </summary>
public T GetOption<T>(Extension<OneofOptions, T> extension)
{
var value = proto.Options.GetExtension(extension);
return value is IDeepCloneable<T> clonable ? clonable.Clone() : value;
return value is IDeepCloneable<T> ? (value as IDeepCloneable<T>).Clone() : value;
}
/// <summary>
......@@ -125,7 +124,6 @@ namespace Google.Protobuf.Reflection
{
return proto.Options.GetExtension(extension).Clone();
}
*/
internal void CrossLink()
{
......
......@@ -115,12 +115,15 @@ namespace Google.Protobuf.Reflection
internal static Func<IMessage, bool> CreateFuncIMessageBool(MethodInfo method) =>
GetReflectionHelper(method.DeclaringType, method.ReturnType).CreateFuncIMessageBool(method);
internal static Func<IMessage, bool> CreateIsInitializedCaller(Type msg) =>
((IExtensionSetReflector)Activator.CreateInstance(typeof(ExtensionSetReflector<>).MakeGenericType(msg))).CreateIsInitializedCaller();
/// <summary>
/// Creates a delegate which will execute the given method after casting the first argument to
/// the type that declares the method, and the second argument to the first parameter type of the method.
/// </summary>
internal static IExtensionReflectionHelper CreateExtensionHelper(Extension extension) =>
(IExtensionReflectionHelper)Activator.CreateInstance(typeof(ExtensionReflectionHelper<,>).MakeGenericType(extension.TargetType, extension.GetType().GenericTypeArguments[1]));
(IExtensionReflectionHelper)Activator.CreateInstance(typeof(ExtensionReflectionHelper<,>).MakeGenericType(extension.TargetType, extension.GetType().GenericTypeArguments[1]), extension);
/// <summary>
/// Creates a reflection helper for the given type arguments. Currently these are created on demand
......@@ -150,6 +153,11 @@ namespace Google.Protobuf.Reflection
void ClearExtension(IMessage message);
}
private interface IExtensionSetReflector
{
Func<IMessage, bool> CreateIsInitializedCaller();
}
private class ReflectionHelper<T1, T2> : IReflectionHelper
{
......@@ -222,7 +230,7 @@ namespace Google.Protobuf.Reflection
}
else if (extension is RepeatedExtension<T1, T3>)
{
return extensionMessage.GetExtension(extension as RepeatedExtension<T1, T3>);
return extensionMessage.GetOrInitializeExtension(extension as RepeatedExtension<T1, T3>);
}
else
{
......@@ -300,6 +308,28 @@ namespace Google.Protobuf.Reflection
}
}
private class ExtensionSetReflector<T1> : IExtensionSetReflector where T1 : IExtendableMessage<T1>
{
public Func<IMessage, bool> CreateIsInitializedCaller()
{
var prop = typeof(T1).GetTypeInfo().GetDeclaredProperty("_Extensions");
#if NET35
var getFunc = (Func<T1, ExtensionSet<T1>>)prop.GetGetMethod(true).CreateDelegate(typeof(Func<T1, ExtensionSet<T1>>));
#else
var getFunc = (Func<T1, ExtensionSet<T1>>)prop.GetMethod.CreateDelegate(typeof(Func<T1, ExtensionSet<T1>>));
#endif
var initializedFunc = (Func<ExtensionSet<T1>, bool>)
typeof(ExtensionSet<T1>)
.GetTypeInfo()
.GetDeclaredMethod("IsInitialized")
.CreateDelegate(typeof(Func<ExtensionSet<T1>, bool>));
return (m) => {
var set = getFunc((T1)m);
return set == null || initializedFunc(set);
};
}
}
// Runtime compatibility checking code - see ReflectionHelper<T1, T2>.CreateFuncIMessageInt32 for
// details about why we're doing this.
......
......@@ -94,17 +94,16 @@ namespace Google.Protobuf.Reflection
/// <summary>
/// The (possibly empty) set of custom options for this service.
/// </summary>
//[Obsolete("CustomOptions are obsolete. Use GetOption")]
[Obsolete("CustomOptions are obsolete. Use GetOption")]
public CustomOptions CustomOptions => new CustomOptions(Proto.Options._extensions?.ValuesByNumber);
/* // uncomment this in the full proto2 support PR
/// <summary>
/// Gets a single value enum option for this descriptor
/// </summary>
public T GetOption<T>(Extension<ServiceOptions, T> extension)
{
var value = Proto.Options.GetExtension(extension);
return value is IDeepCloneable<T> clonable ? clonable.Clone() : value;
return value is IDeepCloneable<T> ? (value as IDeepCloneable<T>).Clone() : value;
}
/// <summary>
......@@ -114,7 +113,6 @@ namespace Google.Protobuf.Reflection
{
return Proto.Options.GetExtension(extension).Clone();
}
*/
internal void CrossLink()
{
......
......@@ -57,20 +57,7 @@ namespace Google.Protobuf.Reflection
throw new ArgumentException("Not all required properties/methods available");
}
setValueDelegate = ReflectionUtil.CreateActionIMessageObject(property.GetSetMethod());
if (descriptor.File.Proto.Syntax == "proto2")
{
MethodInfo hasMethod = property.DeclaringType.GetRuntimeProperty("Has" + property.Name).GetMethod;
if (hasMethod == null) {
throw new ArgumentException("Not all required properties/methods are available");
}
hasDelegate = ReflectionUtil.CreateFuncIMessageBool(hasMethod);
MethodInfo clearMethod = property.DeclaringType.GetRuntimeMethod("Clear" + property.Name, ReflectionUtil.EmptyTypes);
if (clearMethod == null) {
throw new ArgumentException("Not all required properties/methods are available");
}
clearDelegate = ReflectionUtil.CreateActionIMessage(clearMethod);
}
else
if (descriptor.File.Syntax == Syntax.Proto3)
{
hasDelegate = message => {
throw new InvalidOperationException("HasValue is not implemented for proto3 fields");
......@@ -85,6 +72,19 @@ namespace Google.Protobuf.Reflection
: Activator.CreateInstance(clrType);
clearDelegate = message => SetValue(message, defaultValue);
}
else
{
MethodInfo hasMethod = property.DeclaringType.GetRuntimeProperty("Has" + property.Name).GetMethod;
if (hasMethod == null) {
throw new ArgumentException("Not all required properties/methods are available");
}
hasDelegate = ReflectionUtil.CreateFuncIMessageBool(hasMethod);
MethodInfo clearMethod = property.DeclaringType.GetRuntimeMethod("Clear" + property.Name, ReflectionUtil.EmptyTypes);
if (clearMethod == null) {
throw new ArgumentException("Not all required properties/methods are available");
}
clearDelegate = ReflectionUtil.CreateActionIMessage(clearMethod);
}
}
public override void Clear(IMessage message)
......
......@@ -306,7 +306,7 @@ std::string FieldGeneratorBase::GetStringDefaultValueInternal(const FieldDescrip
if (descriptor->default_value_string().empty())
return "\"\"";
else
return "global::System.Encoding.UTF8.GetString(global::System.Convert.FromBase64String(\" +" + StringToBase64(descriptor->default_value_string()) + " +\"))";
return "global::System.Text.Encoding.UTF8.GetString(global::System.Convert.FromBase64String(\"" + StringToBase64(descriptor->default_value_string()) + "\"))";
}
std::string FieldGeneratorBase::GetBytesDefaultValueInternal(const FieldDescriptor* descriptor) {
......
......@@ -63,12 +63,6 @@ bool Generator::Generate(
std::vector<std::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;
}
struct Options cli_options;
for (int i = 0; i < options.size(); i++) {
......
......@@ -284,15 +284,34 @@ std::string GetEnumValueName(const std::string& enum_name, const std::string& en
uint GetGroupEndTag(const Descriptor* descriptor) {
const Descriptor* containing_type = descriptor->containing_type();
if (containing_type == NULL) {
return 0;
}
const FieldDescriptor* field = containing_type->FindFieldByName(descriptor->name());
if (field != NULL && field->type() == FieldDescriptor::Type::TYPE_GROUP) {
return internal::WireFormatLite::MakeTag(field->number(), internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED);
if (containing_type != NULL) {
const FieldDescriptor* field;
for (int i = 0; i < containing_type->field_count(); i++) {
field = containing_type->field(i);
if (field->type() == FieldDescriptor::Type::TYPE_GROUP && field->message_type() == descriptor) {
return internal::WireFormatLite::MakeTag(field->number(), internal::WireFormatLite::WIRETYPE_END_GROUP);
}
}
for (int i = 0; i < containing_type->extension_count(); i++) {
field = containing_type->extension(i);
if (field->type() == FieldDescriptor::Type::TYPE_GROUP && field->message_type() == descriptor) {
return internal::WireFormatLite::MakeTag(field->number(), internal::WireFormatLite::WIRETYPE_END_GROUP);
}
}
} else {
return 0;
const FileDescriptor* containing_file = descriptor->file();
if (containing_file != NULL) {
const FieldDescriptor* field;
for (int i = 0; i < containing_file->extension_count(); i++) {
field = containing_file->extension(i);
if (field->type() == FieldDescriptor::Type::TYPE_GROUP && field->message_type() == descriptor) {
return internal::WireFormatLite::MakeTag(field->number(), internal::WireFormatLite::WIRETYPE_END_GROUP);
}
}
}
}
return 0;
}
std::string ToCSharpName(const std::string& name, const FileDescriptor* file) {
......
......@@ -149,6 +149,8 @@ void MessageGenerator::Generate(io::Printer* printer) {
} else {
printer->Print(vars, "private pb::ExtensionSet<$class_name$> _extensions;\n");
}
printer->Print(vars, "private pb::ExtensionSet<$class_name$> _Extensions => _extensions;\n"); // a read-only property for fast retrieval of the set in IsInitialized
}
for (int i = 0; i < has_bit_field_count_; i++) {
......@@ -270,8 +272,8 @@ void MessageGenerator::Generate(io::Printer* printer) {
"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> GetOrRegisterExtension<TValue>(pb::RepeatedExtension<$class_name$, TValue> extension) {\n"
" return pb::ExtensionSet.GetOrRegister(ref _extensions, extension);\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"
......@@ -320,7 +322,7 @@ void MessageGenerator::Generate(io::Printer* printer) {
"#region Extensions\n"
"/// <summary>Container for extensions for other messages declared in the $class_name$ message type.</summary>\n");
WriteGeneratedCodeAttributes(printer);
printer->Print("internal static partial class Extensions {\n");
printer->Print("public static partial class Extensions {\n");
printer->Indent();
for (int i = 0; i < descriptor_->extension_count(); i++) {
std::unique_ptr<FieldGeneratorBase> generator(
......@@ -625,7 +627,7 @@ void MessageGenerator::GenerateMergingMethods(io::Printer* printer) {
printer->Indent();
if (end_tag_ != 0) {
printer->Print(
"$end_tag$:\n"
"case $end_tag$:\n"
" return;\n",
"end_tag", StrCat(end_tag_));
}
......@@ -681,7 +683,7 @@ void MessageGenerator::GenerateMergingMethods(io::Printer* printer) {
// 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 (IsNullable(descriptor) || !IsProto2(descriptor_->file())) {
if (IsNullable(descriptor) || !IsProto2(descriptor->file()) || descriptor->is_extension()) {
return -1;
}
......
......@@ -74,7 +74,7 @@ void ReflectionClassGenerator::Generate(io::Printer* printer) {
if (file_->extension_count() > 0) {
printer->Print(
"/// <summary>Holder for extension identifiers generated from the top level of $file_name$</summary>\n"
"internal static partial class $class_name$ {\n",
"$access_level$ static partial class $class_name$ {\n",
"access_level", class_access_level(),
"class_name", extensionClassname_,
"file_name", file_->name());
......
......@@ -59,7 +59,7 @@ void SourceGeneratorBase::WriteGeneratedCodeAttributes(io::Printer* printer) {
}
std::string SourceGeneratorBase::class_access_level() {
return (IsDescriptorProto(descriptor_) || this->options()->internal_access) ? "internal" : "public";
return this->options()->internal_access ? "internal" : "public";
}
const Options* SourceGeneratorBase::options() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment