Commit 54176b26 authored by Sydney Acksman's avatar Sydney Acksman Committed by Jie Luo

C# Proto2 feature : Field presence and default values (#4642)

* Compiler changes

* Generated code changes

* Library changes

* Compiler style changes

* Generated style changes

* Fix Windows build errors

* Implement changes from review

* Reintroduce proto2 check

* Compiler changes (required handling review)

* Generated code changes (required handling review)

* Library changes (required handling review

* Field presence rewrite (compiler changes)

* Field presence rewrite (generated code changes)

* Compiler comment

* IFieldAccessor.HasValue library implementation

* Remove Clear methods and default values from proto3 code (Compiler)

* Remove Clear methods and default values from proto3 code (Generated)

* Remove Clear methods and default values from proto3 code (Library)

* Fix distcheck error

* Rewrite default string values to use base64 and convert

* Library changes (IMessage2)

* Compiler changes (IMessage2)

* Generated changes (IMessage2)

* Rebased and regenerated

* Compiler changes (initialized extension)

* Generated changes (initialized extension)

* Library changes (initialized extension)

* Refactor MessageExtensions.IsRequired

* Move string default value creator and bytes default value creator back to seperate methods

* Dead code cleanup

* Fixed segmentation fault
Removed unused header method declarations
parent fb0a74b6
......@@ -179,7 +179,6 @@ csharp_EXTRA_DIST= \
csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs \
csharp/src/Google.Protobuf/Reflection/OriginalNameAttribute.cs \
csharp/src/Google.Protobuf/Reflection/PackageDescriptor.cs \
csharp/src/Google.Protobuf/Reflection/PartialClasses.cs \
csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs \
csharp/src/Google.Protobuf/Reflection/RepeatedFieldAccessor.cs \
csharp/src/Google.Protobuf/Reflection/ServiceDescriptor.cs \
......
......@@ -247,7 +247,7 @@ namespace Google.Protobuf.Examples.AddressBook {
phones_.Add(other.phones_);
if (other.lastUpdated_ != null) {
if (lastUpdated_ == null) {
lastUpdated_ = new global::Google.Protobuf.WellKnownTypes.Timestamp();
LastUpdated = new global::Google.Protobuf.WellKnownTypes.Timestamp();
}
LastUpdated.MergeFrom(other.LastUpdated);
}
......@@ -280,9 +280,9 @@ namespace Google.Protobuf.Examples.AddressBook {
}
case 42: {
if (lastUpdated_ == null) {
lastUpdated_ = new global::Google.Protobuf.WellKnownTypes.Timestamp();
LastUpdated = new global::Google.Protobuf.WellKnownTypes.Timestamp();
}
input.ReadMessage(lastUpdated_);
input.ReadMessage(LastUpdated);
break;
}
}
......@@ -447,7 +447,7 @@ namespace Google.Protobuf.Examples.AddressBook {
break;
}
case 16: {
type_ = (global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType) input.ReadEnum();
Type = (global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType) input.ReadEnum();
break;
}
}
......
......@@ -354,7 +354,7 @@ namespace Conformance {
break;
}
case 24: {
requestedOutputFormat_ = (global::Conformance.WireFormat) input.ReadEnum();
RequestedOutputFormat = (global::Conformance.WireFormat) input.ReadEnum();
break;
}
case 34: {
......@@ -362,7 +362,7 @@ namespace Conformance {
break;
}
case 40: {
testCategory_ = (global::Conformance.TestCategory) input.ReadEnum();
TestCategory = (global::Conformance.TestCategory) input.ReadEnum();
break;
}
}
......
......@@ -724,7 +724,7 @@ namespace Google.Protobuf.TestProtos {
}
if (other.testMap_ != null) {
if (testMap_ == null) {
testMap_ = new global::Google.Protobuf.TestProtos.TestMap();
TestMap = new global::Google.Protobuf.TestProtos.TestMap();
}
TestMap.MergeFrom(other.TestMap);
}
......@@ -741,9 +741,9 @@ namespace Google.Protobuf.TestProtos {
break;
case 10: {
if (testMap_ == null) {
testMap_ = new global::Google.Protobuf.TestProtos.TestMap();
TestMap = new global::Google.Protobuf.TestProtos.TestMap();
}
input.ReadMessage(testMap_);
input.ReadMessage(TestMap);
break;
}
}
......
......@@ -1879,7 +1879,7 @@ namespace UnitTest.Issues.TestProtos {
}
if (other.bar_ != null) {
if (bar_ == null) {
bar_ = new global::UnitTest.Issues.TestProtos.ComplexOptionType1();
Bar = new global::UnitTest.Issues.TestProtos.ComplexOptionType1();
}
Bar.MergeFrom(other.Bar);
}
......@@ -1888,7 +1888,7 @@ namespace UnitTest.Issues.TestProtos {
}
if (other.fred_ != null) {
if (fred_ == null) {
fred_ = new global::UnitTest.Issues.TestProtos.ComplexOptionType2.Types.ComplexOptionType4();
Fred = new global::UnitTest.Issues.TestProtos.ComplexOptionType2.Types.ComplexOptionType4();
}
Fred.MergeFrom(other.Fred);
}
......@@ -1906,9 +1906,9 @@ namespace UnitTest.Issues.TestProtos {
break;
case 10: {
if (bar_ == null) {
bar_ = new global::UnitTest.Issues.TestProtos.ComplexOptionType1();
Bar = new global::UnitTest.Issues.TestProtos.ComplexOptionType1();
}
input.ReadMessage(bar_);
input.ReadMessage(Bar);
break;
}
case 16: {
......@@ -1917,9 +1917,9 @@ namespace UnitTest.Issues.TestProtos {
}
case 26: {
if (fred_ == null) {
fred_ = new global::UnitTest.Issues.TestProtos.ComplexOptionType2.Types.ComplexOptionType4();
Fred = new global::UnitTest.Issues.TestProtos.ComplexOptionType2.Types.ComplexOptionType4();
}
input.ReadMessage(fred_);
input.ReadMessage(Fred);
break;
}
case 34: {
......@@ -2462,7 +2462,7 @@ namespace UnitTest.Issues.TestProtos {
}
if (other.sub_ != null) {
if (sub_ == null) {
sub_ = new global::UnitTest.Issues.TestProtos.Aggregate();
Sub = new global::UnitTest.Issues.TestProtos.Aggregate();
}
Sub.MergeFrom(other.Sub);
}
......@@ -2487,9 +2487,9 @@ namespace UnitTest.Issues.TestProtos {
}
case 26: {
if (sub_ == null) {
sub_ = new global::UnitTest.Issues.TestProtos.Aggregate();
Sub = new global::UnitTest.Issues.TestProtos.Aggregate();
}
input.ReadMessage(sub_);
input.ReadMessage(Sub);
break;
}
}
......
......@@ -557,7 +557,7 @@ namespace UnitTest.Issues.TestProtos {
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
break;
case 8: {
value_ = (global::UnitTest.Issues.TestProtos.NegativeEnum) input.ReadEnum();
Value = (global::UnitTest.Issues.TestProtos.NegativeEnum) input.ReadEnum();
break;
}
case 18:
......@@ -881,7 +881,7 @@ namespace UnitTest.Issues.TestProtos {
primitiveArray_.Add(other.primitiveArray_);
if (other.messageValue_ != null) {
if (messageValue_ == null) {
messageValue_ = new global::UnitTest.Issues.TestProtos.DeprecatedChild();
MessageValue = new global::UnitTest.Issues.TestProtos.DeprecatedChild();
}
MessageValue.MergeFrom(other.MessageValue);
}
......@@ -912,9 +912,9 @@ namespace UnitTest.Issues.TestProtos {
}
case 26: {
if (messageValue_ == null) {
messageValue_ = new global::UnitTest.Issues.TestProtos.DeprecatedChild();
MessageValue = new global::UnitTest.Issues.TestProtos.DeprecatedChild();
}
input.ReadMessage(messageValue_);
input.ReadMessage(MessageValue);
break;
}
case 34: {
......@@ -922,7 +922,7 @@ namespace UnitTest.Issues.TestProtos {
break;
}
case 40: {
enumValue_ = (global::UnitTest.Issues.TestProtos.DeprecatedEnum) input.ReadEnum();
EnumValue = (global::UnitTest.Issues.TestProtos.DeprecatedEnum) input.ReadEnum();
break;
}
case 50:
......
......@@ -342,6 +342,7 @@ namespace Google.Protobuf.TestProtos {
}
}
/// <summary>Field number for the "float_field" field.</summary>
public const int FloatFieldFieldNumber = 11;
private static readonly pb::FieldCodec<float?> _single_floatField_codec = pb::FieldCodec.ForStructWrapper<float>(90);
......@@ -354,6 +355,7 @@ namespace Google.Protobuf.TestProtos {
}
}
/// <summary>Field number for the "int64_field" field.</summary>
public const int Int64FieldFieldNumber = 12;
private static readonly pb::FieldCodec<long?> _single_int64Field_codec = pb::FieldCodec.ForStructWrapper<long>(98);
......@@ -366,6 +368,7 @@ namespace Google.Protobuf.TestProtos {
}
}
/// <summary>Field number for the "uint64_field" field.</summary>
public const int Uint64FieldFieldNumber = 13;
private static readonly pb::FieldCodec<ulong?> _single_uint64Field_codec = pb::FieldCodec.ForStructWrapper<ulong>(106);
......@@ -378,6 +381,7 @@ namespace Google.Protobuf.TestProtos {
}
}
/// <summary>Field number for the "int32_field" field.</summary>
public const int Int32FieldFieldNumber = 14;
private static readonly pb::FieldCodec<int?> _single_int32Field_codec = pb::FieldCodec.ForStructWrapper<int>(114);
......@@ -390,6 +394,7 @@ namespace Google.Protobuf.TestProtos {
}
}
/// <summary>Field number for the "uint32_field" field.</summary>
public const int Uint32FieldFieldNumber = 15;
private static readonly pb::FieldCodec<uint?> _single_uint32Field_codec = pb::FieldCodec.ForStructWrapper<uint>(122);
......@@ -402,6 +407,7 @@ namespace Google.Protobuf.TestProtos {
}
}
/// <summary>Field number for the "bool_field" field.</summary>
public const int BoolFieldFieldNumber = 16;
private static readonly pb::FieldCodec<bool?> _single_boolField_codec = pb::FieldCodec.ForStructWrapper<bool>(130);
......@@ -414,6 +420,7 @@ namespace Google.Protobuf.TestProtos {
}
}
/// <summary>Field number for the "string_field" field.</summary>
public const int StringFieldFieldNumber = 17;
private static readonly pb::FieldCodec<string> _single_stringField_codec = pb::FieldCodec.ForClassWrapper<string>(138);
......@@ -426,6 +433,7 @@ namespace Google.Protobuf.TestProtos {
}
}
/// <summary>Field number for the "bytes_field" field.</summary>
public const int BytesFieldFieldNumber = 18;
private static readonly pb::FieldCodec<pb::ByteString> _single_bytesField_codec = pb::FieldCodec.ForClassWrapper<pb::ByteString>(146);
......@@ -438,6 +446,7 @@ namespace Google.Protobuf.TestProtos {
}
}
/// <summary>Field number for the "value_field" field.</summary>
public const int ValueFieldFieldNumber = 19;
private global::Google.Protobuf.WellKnownTypes.Value valueField_;
......@@ -667,55 +676,55 @@ namespace Google.Protobuf.TestProtos {
}
if (other.anyField_ != null) {
if (anyField_ == null) {
anyField_ = new global::Google.Protobuf.WellKnownTypes.Any();
AnyField = new global::Google.Protobuf.WellKnownTypes.Any();
}
AnyField.MergeFrom(other.AnyField);
}
if (other.apiField_ != null) {
if (apiField_ == null) {
apiField_ = new global::Google.Protobuf.WellKnownTypes.Api();
ApiField = new global::Google.Protobuf.WellKnownTypes.Api();
}
ApiField.MergeFrom(other.ApiField);
}
if (other.durationField_ != null) {
if (durationField_ == null) {
durationField_ = new global::Google.Protobuf.WellKnownTypes.Duration();
DurationField = new global::Google.Protobuf.WellKnownTypes.Duration();
}
DurationField.MergeFrom(other.DurationField);
}
if (other.emptyField_ != null) {
if (emptyField_ == null) {
emptyField_ = new global::Google.Protobuf.WellKnownTypes.Empty();
EmptyField = new global::Google.Protobuf.WellKnownTypes.Empty();
}
EmptyField.MergeFrom(other.EmptyField);
}
if (other.fieldMaskField_ != null) {
if (fieldMaskField_ == null) {
fieldMaskField_ = new global::Google.Protobuf.WellKnownTypes.FieldMask();
FieldMaskField = new global::Google.Protobuf.WellKnownTypes.FieldMask();
}
FieldMaskField.MergeFrom(other.FieldMaskField);
}
if (other.sourceContextField_ != null) {
if (sourceContextField_ == null) {
sourceContextField_ = new global::Google.Protobuf.WellKnownTypes.SourceContext();
SourceContextField = new global::Google.Protobuf.WellKnownTypes.SourceContext();
}
SourceContextField.MergeFrom(other.SourceContextField);
}
if (other.structField_ != null) {
if (structField_ == null) {
structField_ = new global::Google.Protobuf.WellKnownTypes.Struct();
StructField = new global::Google.Protobuf.WellKnownTypes.Struct();
}
StructField.MergeFrom(other.StructField);
}
if (other.timestampField_ != null) {
if (timestampField_ == null) {
timestampField_ = new global::Google.Protobuf.WellKnownTypes.Timestamp();
TimestampField = new global::Google.Protobuf.WellKnownTypes.Timestamp();
}
TimestampField.MergeFrom(other.TimestampField);
}
if (other.typeField_ != null) {
if (typeField_ == null) {
typeField_ = new global::Google.Protobuf.WellKnownTypes.Type();
TypeField = new global::Google.Protobuf.WellKnownTypes.Type();
}
TypeField.MergeFrom(other.TypeField);
}
......@@ -766,7 +775,7 @@ namespace Google.Protobuf.TestProtos {
}
if (other.valueField_ != null) {
if (valueField_ == null) {
valueField_ = new global::Google.Protobuf.WellKnownTypes.Value();
ValueField = new global::Google.Protobuf.WellKnownTypes.Value();
}
ValueField.MergeFrom(other.ValueField);
}
......@@ -783,65 +792,65 @@ namespace Google.Protobuf.TestProtos {
break;
case 10: {
if (anyField_ == null) {
anyField_ = new global::Google.Protobuf.WellKnownTypes.Any();
AnyField = new global::Google.Protobuf.WellKnownTypes.Any();
}
input.ReadMessage(anyField_);
input.ReadMessage(AnyField);
break;
}
case 18: {
if (apiField_ == null) {
apiField_ = new global::Google.Protobuf.WellKnownTypes.Api();
ApiField = new global::Google.Protobuf.WellKnownTypes.Api();
}
input.ReadMessage(apiField_);
input.ReadMessage(ApiField);
break;
}
case 26: {
if (durationField_ == null) {
durationField_ = new global::Google.Protobuf.WellKnownTypes.Duration();
DurationField = new global::Google.Protobuf.WellKnownTypes.Duration();
}
input.ReadMessage(durationField_);
input.ReadMessage(DurationField);
break;
}
case 34: {
if (emptyField_ == null) {
emptyField_ = new global::Google.Protobuf.WellKnownTypes.Empty();
EmptyField = new global::Google.Protobuf.WellKnownTypes.Empty();
}
input.ReadMessage(emptyField_);
input.ReadMessage(EmptyField);
break;
}
case 42: {
if (fieldMaskField_ == null) {
fieldMaskField_ = new global::Google.Protobuf.WellKnownTypes.FieldMask();
FieldMaskField = new global::Google.Protobuf.WellKnownTypes.FieldMask();
}
input.ReadMessage(fieldMaskField_);
input.ReadMessage(FieldMaskField);
break;
}
case 50: {
if (sourceContextField_ == null) {
sourceContextField_ = new global::Google.Protobuf.WellKnownTypes.SourceContext();
SourceContextField = new global::Google.Protobuf.WellKnownTypes.SourceContext();
}
input.ReadMessage(sourceContextField_);
input.ReadMessage(SourceContextField);
break;
}
case 58: {
if (structField_ == null) {
structField_ = new global::Google.Protobuf.WellKnownTypes.Struct();
StructField = new global::Google.Protobuf.WellKnownTypes.Struct();
}
input.ReadMessage(structField_);
input.ReadMessage(StructField);
break;
}
case 66: {
if (timestampField_ == null) {
timestampField_ = new global::Google.Protobuf.WellKnownTypes.Timestamp();
TimestampField = new global::Google.Protobuf.WellKnownTypes.Timestamp();
}
input.ReadMessage(timestampField_);
input.ReadMessage(TimestampField);
break;
}
case 74: {
if (typeField_ == null) {
typeField_ = new global::Google.Protobuf.WellKnownTypes.Type();
TypeField = new global::Google.Protobuf.WellKnownTypes.Type();
}
input.ReadMessage(typeField_);
input.ReadMessage(TypeField);
break;
}
case 82: {
......@@ -909,9 +918,9 @@ namespace Google.Protobuf.TestProtos {
}
case 154: {
if (valueField_ == null) {
valueField_ = new global::Google.Protobuf.WellKnownTypes.Value();
ValueField = new global::Google.Protobuf.WellKnownTypes.Value();
}
input.ReadMessage(valueField_);
input.ReadMessage(ValueField);
break;
}
}
......
......@@ -125,5 +125,10 @@ namespace Google.Protobuf
return new InvalidProtocolBufferException(
"Stream of protocol messages had invalid tag. Expected tag is length-delimited field 1.");
}
}
internal static InvalidProtocolBufferException MissingFields()
{
return new InvalidProtocolBufferException("Message was missing required fields");
}
}
}
\ No newline at end of file
......@@ -30,7 +30,10 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using Google.Protobuf.Reflection;
using System.Collections;
using System.IO;
using System.Linq;
namespace Google.Protobuf
{
......@@ -140,6 +143,53 @@ namespace Google.Protobuf
return ByteString.AttachBytes(message.ToByteArray());
}
/// <summary>
/// Checks if all required fields in a message have values set. For proto3 messages, this returns true
/// </summary>
public static bool IsInitialized(this IMessage message)
{
if (message.Descriptor.File.Proto.Syntax != "proto2")
{
return true;
}
return message.Descriptor
.Fields
.InDeclarationOrder()
.All(f =>
{
if (f.IsMap)
{
var map = (IDictionary)f.Accessor.GetValue(message);
return map.Values.OfType<IMessage>().All(IsInitialized);
}
else if (f.IsRepeated && f.MessageType != null)
{
var enumerable = (IEnumerable)f.Accessor.GetValue(message);
return enumerable.Cast<IMessage>().All(IsInitialized);
}
else if (f.MessageType != null)
{
if (f.Accessor.HasValue(message))
{
return ((IMessage)f.Accessor.GetValue(message)).IsInitialized();
}
else
{
return !f.IsRequired;
}
}
else if (f.IsRequired)
{
return f.Accessor.HasValue(message);
}
else
{
return true;
}
});
}
// Implementations allowing unknown fields to be discarded.
internal static void MergeFrom(this IMessage message, byte[] data, bool discardUnknownFields)
{
......
......@@ -69,6 +69,7 @@ namespace Google.Protobuf
{
IMessage message = factory();
message.MergeFrom(data, DiscardUnknownFields);
CheckMergedRequiredFields(message);
return message;
}
......@@ -83,6 +84,7 @@ namespace Google.Protobuf
{
IMessage message = factory();
message.MergeFrom(data, offset, length, DiscardUnknownFields);
CheckMergedRequiredFields(message);
return message;
}
......@@ -95,6 +97,7 @@ namespace Google.Protobuf
{
IMessage message = factory();
message.MergeFrom(data, DiscardUnknownFields);
CheckMergedRequiredFields(message);
return message;
}
......@@ -107,6 +110,7 @@ namespace Google.Protobuf
{
IMessage message = factory();
message.MergeFrom(input, DiscardUnknownFields);
CheckMergedRequiredFields(message);
return message;
}
......@@ -123,6 +127,7 @@ namespace Google.Protobuf
{
IMessage message = factory();
message.MergeDelimitedFrom(input, DiscardUnknownFields);
CheckMergedRequiredFields(message);
return message;
}
......@@ -135,6 +140,7 @@ namespace Google.Protobuf
{
IMessage message = factory();
MergeFrom(message, input);
CheckMergedRequiredFields(message);
return message;
}
......@@ -167,6 +173,12 @@ namespace Google.Protobuf
}
}
internal static void CheckMergedRequiredFields(IMessage message)
{
if (!message.IsInitialized())
throw new InvalidOperationException("Parsed message does not contain all required fields");
}
/// <summary>
/// Creates a new message parser which optionally discards unknown fields when parsing.
/// </summary>
......
#region Copyright notice and license
// 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.
#endregion
using System;
namespace Google.Protobuf
{
/// <summary>
/// Helper methods for throwing exceptions when preconditions are not met.
/// </summary>
/// <remarks>
/// This class is used internally and by generated code; it is not particularly
/// expected to be used from application code, although nothing prevents it
/// from being used that way.
/// </remarks>
public static class ProtoPreconditions
{
/// <summary>
/// Throws an ArgumentNullException if the given value is null, otherwise
/// return the value to the caller.
/// </summary>
public static T CheckNotNull<T>(T value, string name) where T : class
{
if (value == null)
{
throw new ArgumentNullException(name);
}
return value;
}
/// <summary>
/// Throws an ArgumentNullException if the given value is null, otherwise
/// return the value to the caller.
/// </summary>
/// <remarks>
/// This is equivalent to <see cref="CheckNotNull{T}(T, string)"/> but without the type parameter
/// constraint. In most cases, the constraint is useful to prevent you from calling CheckNotNull
/// with a value type - but it gets in the way if either you want to use it with a nullable
/// value type, or you want to use it with an unconstrained type parameter.
/// </remarks>
internal static T CheckNotNullUnconstrained<T>(T value, string name)
{
if (value == null)
{
throw new ArgumentNullException(name);
}
return value;
}
}
#region Copyright notice and license
// 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.
#endregion
using System;
namespace Google.Protobuf
{
/// <summary>
/// Helper methods for throwing exceptions when preconditions are not met.
/// </summary>
/// <remarks>
/// This class is used internally and by generated code; it is not particularly
/// expected to be used from application code, although nothing prevents it
/// from being used that way.
/// </remarks>
public static class ProtoPreconditions
{
/// <summary>
/// Throws an ArgumentNullException if the given value is null, otherwise
/// return the value to the caller.
/// </summary>
public static T CheckNotNull<T>(T value, string name) where T : class
{
if (value == null)
{
throw new ArgumentNullException(name);
}
return value;
}
/// <summary>
/// Throws an ArgumentNullException if the given value is null, otherwise
/// return the value to the caller.
/// </summary>
/// <remarks>
/// This is equivalent to <see cref="CheckNotNull{T}(T, string)"/> but without the type parameter
/// constraint. In most cases, the constraint is useful to prevent you from calling CheckNotNull
/// with a value type - but it gets in the way if either you want to use it with a nullable
/// value type, or you want to use it with an unconstrained type parameter.
/// </remarks>
internal static T CheckNotNullUnconstrained<T>(T value, string name)
{
if (value == null)
{
throw new ArgumentNullException(name);
}
return value;
}
}
}
\ No newline at end of file
......@@ -57,6 +57,7 @@ namespace Google.Protobuf.Reflection
return getValueDelegate(message);
}
public abstract bool HasValue(IMessage message);
public abstract void Clear(IMessage message);
public abstract void SetValue(IMessage message, object value);
}
......
......@@ -79,8 +79,7 @@ namespace Google.Protobuf.Reflection
throw new DescriptorValidationException(this, "Field numbers must be positive integers.");
}
ContainingType = parent;
// OneofIndex "defaults" to -1 due to a hack in FieldDescriptor.OnConstruction.
if (proto.OneofIndex != -1)
if (proto.HasOneofIndex)
{
if (proto.OneofIndex < 0 || proto.OneofIndex >= parent.Proto.OneofDecl.Count)
{
......@@ -184,6 +183,11 @@ namespace Google.Protobuf.Reflection
/// </summary>
public bool IsRepeated => Proto.Label == FieldDescriptorProto.Types.Label.Repeated;
/// <summary>
/// Returns <c>true</c> if this field is a required field; <c>false</c> otherwise.
/// </summary>
public bool IsRequired => Proto.Label == FieldDescriptorProto.Types.Label.Required;
/// <summary>
/// Returns <c>true</c> if this field is a map field; <c>false</c> otherwise.
/// </summary>
......@@ -192,13 +196,8 @@ 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 =>
// Note the || rather than && here - we're effectively defaulting to packed, because that *is*
// the default in proto3, which is all we support. We may give the wrong result for the protos
// within descriptor.proto, but that's okay, as they're never exposed and we don't use IsPacked
// within the runtime.
Proto.Options == null || Proto.Options.Packed;
public bool IsPacked => File.Proto.Syntax == "proto2" ? Proto.Options?.Packed ?? false : !Proto.Options.HasPacked || Proto.Options.Packed;
/// <summary>
/// Returns the type of the field.
/// </summary>
......@@ -247,9 +246,9 @@ namespace Google.Protobuf.Reflection
{
get
{
if (fieldType != FieldType.Message)
if (fieldType != FieldType.Message && fieldType != FieldType.Group)
{
throw new InvalidOperationException("MessageType is only valid for message fields.");
throw new InvalidOperationException("MessageType is only valid for message or group fields.");
}
return messageType;
}
......@@ -265,12 +264,12 @@ namespace Google.Protobuf.Reflection
/// </summary>
internal void CrossLink()
{
if (Proto.TypeName != "")
if (Proto.HasTypeName)
{
IDescriptor typeDescriptor =
File.DescriptorPool.LookupSymbol(Proto.TypeName, this);
if (Proto.Type != 0)
if (Proto.HasType)
{
// Choose field type based on symbol.
if (typeDescriptor is MessageDescriptor)
......@@ -287,7 +286,7 @@ namespace Google.Protobuf.Reflection
}
}
if (fieldType == FieldType.Message)
if (fieldType == FieldType.Message || fieldType == FieldType.Group)
{
if (!(typeDescriptor is MessageDescriptor))
{
......@@ -295,7 +294,7 @@ namespace Google.Protobuf.Reflection
}
messageType = (MessageDescriptor) typeDescriptor;
if (Proto.DefaultValue != "")
if (Proto.HasDefaultValue)
{
throw new DescriptorValidationException(this, "Messages can't have default values.");
}
......@@ -325,7 +324,7 @@ namespace Google.Protobuf.Reflection
File.DescriptorPool.AddFieldByNumber(this);
if (ContainingType != null && ContainingType.Proto.Options != null && ContainingType.Proto.Options.MessageSetWireFormat)
if (ContainingType != null && ContainingType.Proto.HasOptions && ContainingType.Proto.Options.MessageSetWireFormat)
{
throw new DescriptorValidationException(this, "MessageSet format is not supported.");
}
......
......@@ -74,7 +74,7 @@ namespace Google.Protobuf.Reflection
/// </summary>
String,
/// <summary>
/// The field type used for groups (not supported in this implementation).
/// The field type used for groups.
/// </summary>
Group,
/// <summary>
......
......@@ -51,6 +51,11 @@ 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
......
......@@ -51,6 +51,11 @@ namespace Google.Protobuf.Reflection
list.Clear();
}
public override bool HasValue(IMessage message)
{
throw new InvalidOperationException("HasValue is not implemented for map fields");
}
public override void SetValue(IMessage message, object value)
{
throw new InvalidOperationException("SetValue is not implemented for map fields");
......
#region Copyright notice and license
// 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.
#endregion
// This file just contains partial classes for any autogenerated classes that need additional support.
namespace Google.Protobuf.Reflection
{
internal partial class FieldDescriptorProto
{
// We can't tell the difference between "explicitly set to 0" and "not set"
// in proto3, but we need to tell the difference for OneofIndex. descriptor.proto
// is really a proto2 file, but the runtime doesn't know about proto2 semantics...
// We fake it by defaulting to -1.
partial void OnConstruction()
{
OneofIndex = -1;
}
}
internal partial class FieldOptions
{
// We can't tell the difference between "explicitly set to false" and "not set"
// in proto3, but we need to tell the difference for FieldDescriptor.IsPacked.
// This won't work if we ever need to support proto2, but at that point we'll be
// able to remove this hack and use field presence instead.
partial void OnConstruction()
{
Packed = true;
}
}
}
\ No newline at end of file
......@@ -112,6 +112,9 @@ namespace Google.Protobuf.Reflection
internal static Action<IMessage> CreateActionIMessage(MethodInfo method) =>
GetReflectionHelper(method.DeclaringType, typeof(object)).CreateActionIMessage(method);
internal static Func<IMessage, bool> CreateFuncIMessageBool(MethodInfo method) =>
GetReflectionHelper(method.DeclaringType, method.ReturnType).CreateFuncIMessageBool(method);
/// <summary>
/// Creates a reflection helper for the given type arguments. Currently these are created on demand
/// rather than cached; this will be "busy" when initially loading a message's descriptor, but after that
......@@ -129,6 +132,7 @@ namespace Google.Protobuf.Reflection
Action<IMessage> CreateActionIMessage(MethodInfo method);
Func<IMessage, object> CreateFuncIMessageObject(MethodInfo method);
Action<IMessage, object> CreateActionIMessageObject(MethodInfo method);
Func<IMessage, bool> CreateFuncIMessageBool(MethodInfo method);
}
private class ReflectionHelper<T1, T2> : IReflectionHelper
......@@ -170,6 +174,12 @@ namespace Google.Protobuf.Reflection
var del = (Action<T1, T2>) method.CreateDelegate(typeof(Action<T1, T2>));
return (message, arg) => del((T1) message, (T2) arg);
}
public Func<IMessage, bool> CreateFuncIMessageBool(MethodInfo method)
{
var del = (Func<T1, bool>)method.CreateDelegate(typeof(Func<T1, bool>));
return message => del((T1)message);
}
}
// Runtime compatibility checking code - see ReflectionHelper<T1, T2>.CreateFuncIMessageInt32 for
......
......@@ -51,6 +51,11 @@ namespace Google.Protobuf.Reflection
list.Clear();
}
public override bool HasValue(IMessage message)
{
throw new InvalidOperationException("HasValue is not implemented for repeated fields");
}
public override void SetValue(IMessage message, object value)
{
throw new InvalidOperationException("SetValue is not implemented for repeated fields");
......
......@@ -48,6 +48,7 @@ namespace Google.Protobuf.Reflection
private readonly Action<IMessage, object> setValueDelegate;
private readonly Action<IMessage> clearDelegate;
private readonly Func<IMessage, bool> hasDelegate;
internal SingleFieldAccessor(PropertyInfo property, FieldDescriptor descriptor) : base(property, descriptor)
{
......@@ -56,16 +57,25 @@ 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;
hasDelegate = ReflectionUtil.CreateFuncIMessageBool(hasMethod ?? throw new ArgumentException("Not all required properties/methods are available"));
MethodInfo clearMethod = property.DeclaringType.GetRuntimeMethod("Clear" + property.Name, ReflectionUtil.EmptyTypes);
clearDelegate = ReflectionUtil.CreateActionIMessage(clearMethod ?? throw new ArgumentException("Not all required properties/methods are available"));
}
else
{
hasDelegate = (_) => throw new InvalidOperationException("HasValue is not implemented for proto3 fields"); var clrType = property.PropertyType;
var clrType = property.PropertyType;
// TODO: Validate that this is a reasonable single field? (Should be a value type, a message type, or string/ByteString.)
object defaultValue =
descriptor.FieldType == FieldType.Message ? null
: clrType == typeof(string) ? ""
: clrType == typeof(ByteString) ? ByteString.Empty
: Activator.CreateInstance(clrType);
clearDelegate = message => SetValue(message, defaultValue);
// TODO: Validate that this is a reasonable single field? (Should be a value type, a message type, or string/ByteString.)
object defaultValue =
descriptor.FieldType == FieldType.Message ? null
: clrType == typeof(string) ? ""
: clrType == typeof(ByteString) ? ByteString.Empty
: Activator.CreateInstance(clrType);
clearDelegate = message => SetValue(message, defaultValue);
}
}
public override void Clear(IMessage message)
......@@ -73,6 +83,11 @@ namespace Google.Protobuf.Reflection
clearDelegate(message);
}
public override bool HasValue(IMessage message)
{
return hasDelegate(message);
}
public override void SetValue(IMessage message, object value)
{
setValueDelegate(message, value);
......
......@@ -328,7 +328,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
if (other.sourceContext_ != null) {
if (sourceContext_ == null) {
sourceContext_ = new global::Google.Protobuf.WellKnownTypes.SourceContext();
SourceContext = new global::Google.Protobuf.WellKnownTypes.SourceContext();
}
SourceContext.MergeFrom(other.SourceContext);
}
......@@ -365,9 +365,9 @@ namespace Google.Protobuf.WellKnownTypes {
}
case 42: {
if (sourceContext_ == null) {
sourceContext_ = new global::Google.Protobuf.WellKnownTypes.SourceContext();
SourceContext = new global::Google.Protobuf.WellKnownTypes.SourceContext();
}
input.ReadMessage(sourceContext_);
input.ReadMessage(SourceContext);
break;
}
case 50: {
......@@ -375,7 +375,7 @@ namespace Google.Protobuf.WellKnownTypes {
break;
}
case 56: {
syntax_ = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
Syntax = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
break;
}
}
......@@ -688,7 +688,7 @@ namespace Google.Protobuf.WellKnownTypes {
break;
}
case 56: {
syntax_ = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
Syntax = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
break;
}
}
......
......@@ -312,7 +312,7 @@ namespace Google.Protobuf.WellKnownTypes {
options_.Add(other.options_);
if (other.sourceContext_ != null) {
if (sourceContext_ == null) {
sourceContext_ = new global::Google.Protobuf.WellKnownTypes.SourceContext();
SourceContext = new global::Google.Protobuf.WellKnownTypes.SourceContext();
}
SourceContext.MergeFrom(other.SourceContext);
}
......@@ -348,13 +348,13 @@ namespace Google.Protobuf.WellKnownTypes {
}
case 42: {
if (sourceContext_ == null) {
sourceContext_ = new global::Google.Protobuf.WellKnownTypes.SourceContext();
SourceContext = new global::Google.Protobuf.WellKnownTypes.SourceContext();
}
input.ReadMessage(sourceContext_);
input.ReadMessage(SourceContext);
break;
}
case 48: {
syntax_ = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
Syntax = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
break;
}
}
......@@ -726,11 +726,11 @@ namespace Google.Protobuf.WellKnownTypes {
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
break;
case 8: {
kind_ = (global::Google.Protobuf.WellKnownTypes.Field.Types.Kind) input.ReadEnum();
Kind = (global::Google.Protobuf.WellKnownTypes.Field.Types.Kind) input.ReadEnum();
break;
}
case 16: {
cardinality_ = (global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality) input.ReadEnum();
Cardinality = (global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality) input.ReadEnum();
break;
}
case 24: {
......@@ -1084,7 +1084,7 @@ namespace Google.Protobuf.WellKnownTypes {
options_.Add(other.options_);
if (other.sourceContext_ != null) {
if (sourceContext_ == null) {
sourceContext_ = new global::Google.Protobuf.WellKnownTypes.SourceContext();
SourceContext = new global::Google.Protobuf.WellKnownTypes.SourceContext();
}
SourceContext.MergeFrom(other.SourceContext);
}
......@@ -1116,13 +1116,13 @@ namespace Google.Protobuf.WellKnownTypes {
}
case 34: {
if (sourceContext_ == null) {
sourceContext_ = new global::Google.Protobuf.WellKnownTypes.SourceContext();
SourceContext = new global::Google.Protobuf.WellKnownTypes.SourceContext();
}
input.ReadMessage(sourceContext_);
input.ReadMessage(SourceContext);
break;
}
case 40: {
syntax_ = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
Syntax = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
break;
}
}
......@@ -1467,7 +1467,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
if (other.value_ != null) {
if (value_ == null) {
value_ = new global::Google.Protobuf.WellKnownTypes.Any();
Value = new global::Google.Protobuf.WellKnownTypes.Any();
}
Value.MergeFrom(other.Value);
}
......@@ -1488,9 +1488,9 @@ namespace Google.Protobuf.WellKnownTypes {
}
case 18: {
if (value_ == null) {
value_ = new global::Google.Protobuf.WellKnownTypes.Any();
Value = new global::Google.Protobuf.WellKnownTypes.Any();
}
input.ReadMessage(value_);
input.ReadMessage(Value);
break;
}
}
......
......@@ -61,11 +61,11 @@ namespace Google.Protobuf
/// </summary>
LengthDelimited = 2,
/// <summary>
/// A "start group" value - not supported by this implementation.
/// A "start group" value
/// </summary>
StartGroup = 3,
/// <summary>
/// An "end group" value - not supported by this implementation.
/// An "end group" value
/// </summary>
EndGroup = 4,
/// <summary>
......
......@@ -47,8 +47,8 @@ namespace compiler {
namespace csharp {
EnumFieldGenerator::EnumFieldGenerator(const FieldDescriptor* descriptor,
int fieldOrdinal, const Options *options)
: PrimitiveFieldGenerator(descriptor, fieldOrdinal, options) {
int presenceIndex, const Options *options)
: PrimitiveFieldGenerator(descriptor, presenceIndex, options) {
}
EnumFieldGenerator::~EnumFieldGenerator() {
......@@ -56,7 +56,7 @@ EnumFieldGenerator::~EnumFieldGenerator() {
void EnumFieldGenerator::GenerateParsingCode(io::Printer* printer) {
printer->Print(variables_,
"$name$_ = ($type_name$) input.ReadEnum();\n");
"$property_name$ = ($type_name$) input.ReadEnum();\n");
}
void EnumFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
......@@ -82,8 +82,8 @@ void EnumFieldGenerator::GenerateCodecCode(io::Printer* printer) {
}
EnumOneofFieldGenerator::EnumOneofFieldGenerator(
const FieldDescriptor* descriptor, int fieldOrdinal, const Options *options)
: PrimitiveOneofFieldGenerator(descriptor, fieldOrdinal, options) {
const FieldDescriptor* descriptor, int presenceIndex, const Options *options)
: PrimitiveOneofFieldGenerator(descriptor, presenceIndex, options) {
}
EnumOneofFieldGenerator::~EnumOneofFieldGenerator() {
......
......@@ -44,7 +44,7 @@ namespace csharp {
class EnumFieldGenerator : public PrimitiveFieldGenerator {
public:
EnumFieldGenerator(const FieldDescriptor* descriptor,
int fieldOrdinal,
int presenceIndex,
const Options *options);
~EnumFieldGenerator();
......@@ -60,7 +60,7 @@ class EnumFieldGenerator : public PrimitiveFieldGenerator {
class EnumOneofFieldGenerator : public PrimitiveOneofFieldGenerator {
public:
EnumOneofFieldGenerator(const FieldDescriptor* descriptor,
int fieldOrdinal,
int presenceIndex,
const Options *options);
~EnumOneofFieldGenerator();
......
......@@ -57,6 +57,9 @@ void FieldGeneratorBase::SetCommonFieldVariables(
// repeated fields varies by wire format. The wire format is encoded in the bottom 3 bits, which
// never effects the tag size.
int tag_size = internal::WireFormat::TagSize(descriptor_->number(), descriptor_->type());
if (descriptor_->type() == FieldDescriptor::TYPE_GROUP) {
tag_size /= 2;
}
uint tag = internal::WireFormat::MakeTag(descriptor_);
uint8 tag_array[5];
io::CodedOutputStream::WriteTagToArray(tag, tag_array);
......@@ -75,34 +78,52 @@ void FieldGeneratorBase::SetCommonFieldVariables(
(*variables)["name"] = name();
(*variables)["descriptor_name"] = descriptor_->name();
(*variables)["default_value"] = default_value();
if (has_default_value()) {
(*variables)["capitalized_type_name"] = capitalized_type_name();
(*variables)["number"] = number();
if (has_default_value() && !IsProto2(descriptor_->file())) {
(*variables)["name_def_message"] =
(*variables)["name"] + "_ = " + (*variables)["default_value"];
} else {
(*variables)["name_def_message"] = (*variables)["name"] + "_";
}
(*variables)["capitalized_type_name"] = capitalized_type_name();
(*variables)["number"] = number();
(*variables)["has_property_check"] =
(*variables)["property_name"] + " != " + (*variables)["default_value"];
(*variables)["other_has_property_check"] = "other." +
(*variables)["property_name"] + " != " + (*variables)["default_value"];
if (IsProto2(descriptor_->file())) {
(*variables)["has_property_check"] = "Has" + (*variables)["property_name"];
(*variables)["other_has_property_check"] = "other.Has" + (*variables)["property_name"];
(*variables)["has_not_property_check"] = "!" + (*variables)["has_property_check"];
(*variables)["other_has_not_property_check"] = "!" + (*variables)["other_has_property_check"];
if (presenceIndex_ != -1) {
string hasBitsNumber = SimpleItoa(presenceIndex_ / 32);
string hasBitsMask = SimpleItoa(1 << (presenceIndex_ % 32));
(*variables)["has_field_check"] = "(_hasBits" + hasBitsNumber + " & " + hasBitsMask + ") != 0";
(*variables)["set_has_field"] = "_hasBits" + hasBitsNumber + " |= " + hasBitsMask;
(*variables)["clear_has_field"] = "_hasBits" + hasBitsNumber + " &= ~" + hasBitsMask;
}
} else {
(*variables)["has_property_check"] =
(*variables)["property_name"] + " != " + (*variables)["default_value"];
(*variables)["other_has_property_check"] = "other." +
(*variables)["property_name"] + " != " + (*variables)["default_value"];
}
}
void FieldGeneratorBase::SetCommonOneofFieldVariables(
std::map<string, string>* variables) {
(*variables)["oneof_name"] = oneof_name();
(*variables)["has_property_check"] =
oneof_name() + "Case_ == " + oneof_property_name() +
"OneofCase." + property_name();
if (IsProto2(descriptor_->file())) {
(*variables)["has_property_check"] = "Has" + property_name();
} else {
(*variables)["has_property_check"] =
oneof_name() + "Case_ == " + oneof_property_name() +
"OneofCase." + property_name();
}
(*variables)["oneof_property_name"] = oneof_property_name();
}
FieldGeneratorBase::FieldGeneratorBase(const FieldDescriptor* descriptor,
int fieldOrdinal, const Options* options)
int presenceIndex, const Options* options)
: SourceGeneratorBase(descriptor->file(), options),
descriptor_(descriptor),
fieldOrdinal_(fieldOrdinal) {
presenceIndex_(presenceIndex) {
SetCommonFieldVariables(&variables_);
}
......@@ -251,36 +272,6 @@ bool FieldGeneratorBase::has_default_value() {
}
}
bool FieldGeneratorBase::is_nullable_type() {
switch (descriptor_->type()) {
case FieldDescriptor::TYPE_ENUM:
case FieldDescriptor::TYPE_DOUBLE:
case FieldDescriptor::TYPE_FLOAT:
case FieldDescriptor::TYPE_INT64:
case FieldDescriptor::TYPE_UINT64:
case FieldDescriptor::TYPE_INT32:
case FieldDescriptor::TYPE_FIXED64:
case FieldDescriptor::TYPE_FIXED32:
case FieldDescriptor::TYPE_BOOL:
case FieldDescriptor::TYPE_UINT32:
case FieldDescriptor::TYPE_SFIXED32:
case FieldDescriptor::TYPE_SFIXED64:
case FieldDescriptor::TYPE_SINT32:
case FieldDescriptor::TYPE_SINT64:
return false;
case FieldDescriptor::TYPE_MESSAGE:
case FieldDescriptor::TYPE_GROUP:
case FieldDescriptor::TYPE_STRING:
case FieldDescriptor::TYPE_BYTES:
return true;
default:
GOOGLE_LOG(FATAL)<< "Unknown field type.";
return true;
}
}
bool AllPrintableAscii(const std::string& text) {
for(int i = 0; i < text.size(); i++) {
if (text[i] < 0x20 || text[i] > 0x7e) {
......@@ -290,14 +281,18 @@ bool AllPrintableAscii(const std::string& text) {
return true;
}
std::string FieldGeneratorBase::GetStringDefaultValueInternal() {
// No other default values needed for proto3...
return "\"\"";
std::string FieldGeneratorBase::GetStringDefaultValueInternal(const FieldDescriptor* descriptor) {
if (descriptor->default_value_string().empty())
return "\"\"";
else
return "global::System.Encoding.UTF8.GetString(global::System.Convert.FromBase64String(\" +" + StringToBase64(descriptor->default_value_string()) + " +\"))";
}
std::string FieldGeneratorBase::GetBytesDefaultValueInternal() {
// No other default values needed for proto3...
return "pb::ByteString.Empty";
std::string FieldGeneratorBase::GetBytesDefaultValueInternal(const FieldDescriptor* descriptor) {
if (descriptor->default_value_string().empty())
return "pb::ByteString.Empty";
else
return "pb::ByteString.FromBase64(\"" + StringToBase64(descriptor->default_value_string()) + "\")";
}
std::string FieldGeneratorBase::default_value() {
......@@ -307,9 +302,13 @@ std::string FieldGeneratorBase::default_value() {
std::string FieldGeneratorBase::default_value(const FieldDescriptor* descriptor) {
switch (descriptor->type()) {
case FieldDescriptor::TYPE_ENUM:
// All proto3 enums have a default value of 0, and there's an implicit conversion from the constant 0 to
// any C# enum. This means we don't need to work out what we actually mapped the enum value name to.
return "0";
if (IsProto2(descriptor_->file())) {
return GetClassName(descriptor->default_value_enum()->type()) + "." +
GetEnumValueName(descriptor->default_value_enum()->type()->name(), descriptor->default_value_enum()->name());
}
else {
return "0";
}
case FieldDescriptor::TYPE_MESSAGE:
case FieldDescriptor::TYPE_GROUP:
if (IsWrapperType(descriptor)) {
......@@ -357,9 +356,9 @@ std::string FieldGeneratorBase::default_value(const FieldDescriptor* descriptor)
return "false";
}
case FieldDescriptor::TYPE_STRING:
return GetStringDefaultValueInternal();
return GetStringDefaultValueInternal(descriptor);
case FieldDescriptor::TYPE_BYTES:
return GetBytesDefaultValueInternal();
return GetBytesDefaultValueInternal(descriptor);
case FieldDescriptor::TYPE_UINT32:
return SimpleItoa(descriptor->default_value_uint32());
case FieldDescriptor::TYPE_SFIXED32:
......
......@@ -47,7 +47,7 @@ namespace csharp {
class FieldGeneratorBase : public SourceGeneratorBase {
public:
FieldGeneratorBase(const FieldDescriptor* descriptor,
int fieldOrdinal,
int presenceIndex,
const Options* options);
~FieldGeneratorBase();
......@@ -67,7 +67,7 @@ class FieldGeneratorBase : public SourceGeneratorBase {
protected:
const FieldDescriptor* descriptor_;
const int fieldOrdinal_;
const int presenceIndex_;
std::map<string, string> variables_;
void AddDeprecatedFlag(io::Printer* printer);
......@@ -84,7 +84,6 @@ class FieldGeneratorBase : public SourceGeneratorBase {
std::string type_name();
std::string type_name(const FieldDescriptor* descriptor);
bool has_default_value();
bool is_nullable_type();
std::string default_value();
std::string default_value(const FieldDescriptor* descriptor);
std::string number();
......@@ -92,8 +91,8 @@ class FieldGeneratorBase : public SourceGeneratorBase {
private:
void SetCommonFieldVariables(std::map<string, string>* variables);
std::string GetStringDefaultValueInternal();
std::string GetBytesDefaultValueInternal();
std::string GetStringDefaultValueInternal(const FieldDescriptor* descriptor);
std::string GetBytesDefaultValueInternal(const FieldDescriptor* descriptor);
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGeneratorBase);
};
......
......@@ -65,11 +65,11 @@ 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.
// 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";
*error = "C# code generation only supports proto3 syntax";
return false;
}
}
struct Options cli_options;
......
......@@ -36,6 +36,7 @@
#include <google/protobuf/stubs/hash.h>
#include <limits>
#include <vector>
#include <sstream>
#include <google/protobuf/compiler/csharp/csharp_helpers.h>
#include <google/protobuf/compiler/csharp/csharp_names.h>
......@@ -452,55 +453,89 @@ std::string FileDescriptorToBase64(const FileDescriptor* descriptor) {
}
FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor,
int fieldOrdinal,
int presenceIndex,
const Options* options) {
switch (descriptor->type()) {
case FieldDescriptor::TYPE_GROUP:
case FieldDescriptor::TYPE_MESSAGE:
if (descriptor->is_repeated()) {
if (descriptor->is_map()) {
return new MapFieldGenerator(descriptor, fieldOrdinal, options);
return new MapFieldGenerator(descriptor, presenceIndex, options);
} else {
return new RepeatedMessageFieldGenerator(descriptor, fieldOrdinal, options);
return new RepeatedMessageFieldGenerator(descriptor, presenceIndex, options);
}
} else {
if (IsWrapperType(descriptor)) {
if (descriptor->containing_oneof()) {
return new WrapperOneofFieldGenerator(descriptor, fieldOrdinal, options);
return new WrapperOneofFieldGenerator(descriptor, presenceIndex, options);
} else {
return new WrapperFieldGenerator(descriptor, fieldOrdinal, options);
return new WrapperFieldGenerator(descriptor, presenceIndex, options);
}
} else {
if (descriptor->containing_oneof()) {
return new MessageOneofFieldGenerator(descriptor, fieldOrdinal, options);
return new MessageOneofFieldGenerator(descriptor, presenceIndex, options);
} else {
return new MessageFieldGenerator(descriptor, fieldOrdinal, options);
return new MessageFieldGenerator(descriptor, presenceIndex, options);
}
}
}
case FieldDescriptor::TYPE_ENUM:
if (descriptor->is_repeated()) {
return new RepeatedEnumFieldGenerator(descriptor, fieldOrdinal, options);
return new RepeatedEnumFieldGenerator(descriptor, presenceIndex, options);
} else {
if (descriptor->containing_oneof()) {
return new EnumOneofFieldGenerator(descriptor, fieldOrdinal, options);
return new EnumOneofFieldGenerator(descriptor, presenceIndex, options);
} else {
return new EnumFieldGenerator(descriptor, fieldOrdinal, options);
return new EnumFieldGenerator(descriptor, presenceIndex, options);
}
}
default:
if (descriptor->is_repeated()) {
return new RepeatedPrimitiveFieldGenerator(descriptor, fieldOrdinal, options);
return new RepeatedPrimitiveFieldGenerator(descriptor, presenceIndex, options);
} else {
if (descriptor->containing_oneof()) {
return new PrimitiveOneofFieldGenerator(descriptor, fieldOrdinal, options);
return new PrimitiveOneofFieldGenerator(descriptor, presenceIndex, options);
} else {
return new PrimitiveFieldGenerator(descriptor, fieldOrdinal, options);
return new PrimitiveFieldGenerator(descriptor, presenceIndex, options);
}
}
}
}
bool IsNullable(const FieldDescriptor* descriptor) {
if (descriptor->is_repeated()) {
return true;
}
switch (descriptor->type()) {
case FieldDescriptor::TYPE_ENUM:
case FieldDescriptor::TYPE_DOUBLE:
case FieldDescriptor::TYPE_FLOAT:
case FieldDescriptor::TYPE_INT64:
case FieldDescriptor::TYPE_UINT64:
case FieldDescriptor::TYPE_INT32:
case FieldDescriptor::TYPE_FIXED64:
case FieldDescriptor::TYPE_FIXED32:
case FieldDescriptor::TYPE_BOOL:
case FieldDescriptor::TYPE_UINT32:
case FieldDescriptor::TYPE_SFIXED32:
case FieldDescriptor::TYPE_SFIXED64:
case FieldDescriptor::TYPE_SINT32:
case FieldDescriptor::TYPE_SINT64:
return false;
case FieldDescriptor::TYPE_MESSAGE:
case FieldDescriptor::TYPE_GROUP:
case FieldDescriptor::TYPE_STRING:
case FieldDescriptor::TYPE_BYTES:
return true;
default:
GOOGLE_LOG(FATAL) << "Unknown field type.";
return true;
}
}
} // namespace csharp
} // namespace compiler
} // namespace protobuf
......
......@@ -107,9 +107,11 @@ std::string StringToBase64(const std::string& input);
std::string FileDescriptorToBase64(const FileDescriptor* descriptor);
FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor,
int fieldOrdinal,
int presenceIndex,
const Options* options);
bool IsNullable(const FieldDescriptor* descriptor);
// Determines whether the given message is a map entry message,
// i.e. one implicitly created by protoc due to a map<key, value> field.
inline bool IsMapEntryMessage(const Descriptor* descriptor) {
......@@ -144,6 +146,10 @@ inline bool IsWrapperType(const FieldDescriptor* descriptor) {
descriptor->message_type()->file()->name() == "google/protobuf/wrappers.proto";
}
inline bool IsProto2(const FileDescriptor* descriptor) {
return descriptor->syntax() == FileDescriptor::SYNTAX_PROTO2;
}
} // namespace csharp
} // namespace compiler
} // namespace protobuf
......
......@@ -48,9 +48,9 @@ namespace compiler {
namespace csharp {
MapFieldGenerator::MapFieldGenerator(const FieldDescriptor* descriptor,
int fieldOrdinal,
int presenceIndex,
const Options* options)
: FieldGeneratorBase(descriptor, fieldOrdinal, options) {
: FieldGeneratorBase(descriptor, presenceIndex, options) {
}
MapFieldGenerator::~MapFieldGenerator() {
......
......@@ -44,7 +44,7 @@ namespace csharp {
class MapFieldGenerator : public FieldGeneratorBase {
public:
MapFieldGenerator(const FieldDescriptor* descriptor,
int fieldOrdinal,
int presenceIndex,
const Options* options);
~MapFieldGenerator();
......
......@@ -61,20 +61,27 @@ bool CompareFieldNumbers(const FieldDescriptor* d1, const FieldDescriptor* d2) {
MessageGenerator::MessageGenerator(const Descriptor* descriptor,
const Options* options)
: SourceGeneratorBase(descriptor->file(), options),
descriptor_(descriptor) {
// sorted field names
for (int i = 0; i < descriptor_->field_count(); i++) {
field_names_.push_back(descriptor_->field(i)->name());
}
std::sort(field_names_.begin(), field_names_.end());
descriptor_(descriptor),
has_bit_field_count_(0) {
// fields by number
for (int i = 0; i < descriptor_->field_count(); i++) {
fields_by_number_.push_back(descriptor_->field(i));
}
std::sort(fields_by_number_.begin(), fields_by_number_.end(),
CompareFieldNumbers);
if (IsProto2(descriptor_->file())) {
int primitiveCount = 0;
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* field = descriptor_->field(i);
if (!IsNullable(field)) {
primitiveCount++;
if (has_bit_field_count_ == 0 || (primitiveCount % 32) == 0) {
has_bit_field_count_++;
}
}
}
}
}
MessageGenerator::~MessageGenerator() {
......@@ -88,10 +95,6 @@ std::string MessageGenerator::full_class_name() {
return GetClassName(descriptor_);
}
const std::vector<std::string>& MessageGenerator::field_names() {
return field_names_;
}
const std::vector<const FieldDescriptor*>& MessageGenerator::fields_by_number() {
return fields_by_number_;
}
......@@ -123,6 +126,12 @@ void MessageGenerator::Generate(io::Printer* printer) {
printer->Print(
"private pb::UnknownFieldSet _unknownFields;\n");
for (int i = 0; i < has_bit_field_count_; i++) {
// don't use arrays since all arrays are heap allocated, saving allocations
// use ints instead of bytes since bytes lack bitwise operators, saving casts
printer->Print("private int _hasBits$i$;\n", "i", SimpleItoa(i));
}
WriteGeneratedCodeAttributes(printer);
printer->Print(
......@@ -288,6 +297,9 @@ void MessageGenerator::GenerateCloningCode(io::Printer* printer) {
vars,
"public $class_name$($class_name$ other) : this() {\n");
printer->Indent();
for (int i = 0; i < has_bit_field_count_; i++) {
printer->Print("_hasBits$i$ = other._hasBits$i$;\n", "i", SimpleItoa(i));
}
// Clone non-oneof fields first
for (int i = 0; i < descriptor_->field_count(); i++) {
if (!descriptor_->field(i)->containing_oneof()) {
......@@ -559,19 +571,29 @@ void MessageGenerator::GenerateMergingMethods(io::Printer* printer) {
printer->Print("}\n\n"); // method
}
int MessageGenerator::GetFieldOrdinal(const FieldDescriptor* descriptor) {
for (int i = 0; i < field_names().size(); i++) {
if (field_names()[i] == descriptor->name()) {
return i;
// 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())) {
return -1;
}
int index = 0;
for (int i = 0; i < fields_by_number().size(); i++) {
const FieldDescriptor* field = fields_by_number()[i];
if (field == descriptor) {
return index;
}
if (!IsNullable(field)) {
index++;
}
}
GOOGLE_LOG(DFATAL)<< "Could not find ordinal for field " << descriptor->name();
GOOGLE_LOG(DFATAL)<< "Could not find presence index for field " << descriptor->name();
return -1;
}
FieldGeneratorBase* MessageGenerator::CreateFieldGeneratorInternal(
const FieldDescriptor* descriptor) {
return CreateFieldGenerator(descriptor, GetFieldOrdinal(descriptor), this->options());
return CreateFieldGenerator(descriptor, GetPresenceIndex(descriptor), this->options());
}
} // namespace csharp
......
......@@ -57,13 +57,13 @@ class MessageGenerator : public SourceGeneratorBase {
private:
const Descriptor* descriptor_;
std::vector<std::string> field_names_;
std::vector<const FieldDescriptor*> fields_by_number_;
int has_bit_field_count_;
void GenerateMessageSerializationMethods(io::Printer* printer);
void GenerateMergingMethods(io::Printer* printer);
int GetFieldOrdinal(const FieldDescriptor* descriptor);
int GetPresenceIndex(const FieldDescriptor* descriptor);
FieldGeneratorBase* CreateFieldGeneratorInternal(
const FieldDescriptor* descriptor);
......@@ -74,9 +74,6 @@ class MessageGenerator : public SourceGeneratorBase {
std::string class_name();
std::string full_class_name();
// field names sorted alphabetically
const std::vector<std::string>& field_names();
// field descriptors sorted by number
const std::vector<const FieldDescriptor*>& fields_by_number();
......
......@@ -49,11 +49,13 @@ namespace compiler {
namespace csharp {
MessageFieldGenerator::MessageFieldGenerator(const FieldDescriptor* descriptor,
int fieldOrdinal,
int presenceIndex,
const Options *options)
: FieldGeneratorBase(descriptor, fieldOrdinal, options) {
variables_["has_property_check"] = name() + "_ != null";
variables_["has_not_property_check"] = name() + "_ == null";
: FieldGeneratorBase(descriptor, presenceIndex, options) {
if (!IsProto2(descriptor_->file())) {
variables_["has_property_check"] = name() + "_ != null";
variables_["has_not_property_check"] = name() + "_ == null";
}
}
MessageFieldGenerator::~MessageFieldGenerator() {
......@@ -74,6 +76,26 @@ void MessageFieldGenerator::GenerateMembers(io::Printer* printer) {
" $name$_ = value;\n"
" }\n"
"}\n");
if (IsProto2(descriptor_->file())) {
printer->Print(
variables_,
"/// <summary>Gets whether the $descriptor_name$ field is set</summary>\n");
AddPublicMemberAttributes(printer);
printer->Print(
variables_,
"$access_level$ bool Has$property_name$ {\n"
" get { return $name$_ != null; }\n"
"}\n");
printer->Print(
variables_,
"/// <summary>Clears the value of the $descriptor_name$ field</summary>\n");
AddPublicMemberAttributes(printer);
printer->Print(
variables_,
"$access_level$ void Clear$property_name$() {\n"
" $name$_ = null;\n"
"}\n");
}
}
void MessageFieldGenerator::GenerateMergingCode(io::Printer* printer) {
......@@ -81,7 +103,7 @@ void MessageFieldGenerator::GenerateMergingCode(io::Printer* printer) {
variables_,
"if (other.$has_property_check$) {\n"
" if ($has_not_property_check$) {\n"
" $name$_ = new $type_name$();\n"
" $property_name$ = new $type_name$();\n"
" }\n"
" $property_name$.MergeFrom(other.$property_name$);\n"
"}\n");
......@@ -91,10 +113,9 @@ void MessageFieldGenerator::GenerateParsingCode(io::Printer* printer) {
printer->Print(
variables_,
"if ($has_not_property_check$) {\n"
" $name$_ = new $type_name$();\n"
" $property_name$ = new $type_name$();\n"
"}\n"
// TODO(jonskeet): Do we really need merging behaviour like this?
"input.ReadMessage($name$_);\n"); // No need to support TYPE_GROUP...
"input.ReadMessage($property_name$);\n");
}
void MessageFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
......@@ -130,7 +151,6 @@ void MessageFieldGenerator::WriteToString(io::Printer* printer) {
variables_,
"PrintField(\"$field_name$\", has$property_name$, $name$_, writer);\n");
}
void MessageFieldGenerator::GenerateCloningCode(io::Printer* printer) {
printer->Print(variables_,
"$name$_ = other.$has_property_check$ ? other.$name$_.Clone() : null;\n");
......@@ -147,9 +167,9 @@ void MessageFieldGenerator::GenerateCodecCode(io::Printer* printer) {
MessageOneofFieldGenerator::MessageOneofFieldGenerator(
const FieldDescriptor* descriptor,
int fieldOrdinal,
int presenceIndex,
const Options *options)
: MessageFieldGenerator(descriptor, fieldOrdinal, options) {
: MessageFieldGenerator(descriptor, presenceIndex, options) {
SetCommonOneofFieldVariables(&variables_);
}
......@@ -169,6 +189,28 @@ void MessageOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
" $oneof_name$Case_ = value == null ? $oneof_property_name$OneofCase.None : $oneof_property_name$OneofCase.$property_name$;\n"
" }\n"
"}\n");
if (IsProto2(descriptor_->file())) {
printer->Print(
variables_,
"/// <summary>Gets whether the \"$descriptor_name$\" field is set</summary>\n");
AddPublicMemberAttributes(printer);
printer->Print(
variables_,
"$access_level$ bool Has$property_name$ {\n"
" get { return $oneof_name$Case_ == $oneof_property_name$OneofCase.$property_name$; }\n"
"}\n");
printer->Print(
variables_,
"/// <summary> Clears the value of the oneof if it's currently set to \"$descriptor_name$\" </summary>\n");
AddPublicMemberAttributes(printer);
printer->Print(
variables_,
"$access_level$ void Clear$property_name$() {\n"
" if ($has_property_check$) {\n"
" Clear$oneof_property_name$();\n"
" }\n"
"}\n");
}
}
void MessageOneofFieldGenerator::GenerateMergingCode(io::Printer* printer) {
......@@ -187,7 +229,7 @@ void MessageOneofFieldGenerator::GenerateParsingCode(io::Printer* printer) {
"if ($has_property_check$) {\n"
" subBuilder.MergeFrom($property_name$);\n"
"}\n"
"input.ReadMessage(subBuilder);\n" // No support of TYPE_GROUP
"input.ReadMessage(subBuilder);\n"
"$property_name$ = subBuilder;\n");
}
......
......@@ -44,7 +44,7 @@ namespace csharp {
class MessageFieldGenerator : public FieldGeneratorBase {
public:
MessageFieldGenerator(const FieldDescriptor* descriptor,
int fieldOrdinal,
int presenceIndex,
const Options *options);
~MessageFieldGenerator();
......@@ -68,7 +68,7 @@ class MessageFieldGenerator : public FieldGeneratorBase {
class MessageOneofFieldGenerator : public MessageFieldGenerator {
public:
MessageOneofFieldGenerator(const FieldDescriptor* descriptor,
int fieldOrdinal,
int presenceIndex,
const Options *options);
~MessageOneofFieldGenerator();
......
......@@ -46,7 +46,7 @@ struct Options;
class PrimitiveFieldGenerator : public FieldGeneratorBase {
public:
PrimitiveFieldGenerator(const FieldDescriptor* descriptor,
int fieldOrdinal,
int presenceIndex,
const Options *options);
~PrimitiveFieldGenerator();
......@@ -72,7 +72,7 @@ class PrimitiveFieldGenerator : public FieldGeneratorBase {
class PrimitiveOneofFieldGenerator : public PrimitiveFieldGenerator {
public:
PrimitiveOneofFieldGenerator(const FieldDescriptor* descriptor,
int fieldOrdinal,
int presenceIndex,
const Options *options);
~PrimitiveOneofFieldGenerator();
......
......@@ -48,8 +48,8 @@ namespace compiler {
namespace csharp {
RepeatedEnumFieldGenerator::RepeatedEnumFieldGenerator(
const FieldDescriptor* descriptor, int fieldOrdinal, const Options *options)
: FieldGeneratorBase(descriptor, fieldOrdinal, options) {
const FieldDescriptor* descriptor, int presenceIndex, const Options *options)
: FieldGeneratorBase(descriptor, presenceIndex, options) {
}
RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {
......
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