Commit 4668c3dc authored by Jon Skeet's avatar Jon Skeet

Remove the usage of attributes for field/method discovery.

Instead, introduce GeneratedCodeInfo which passes in what we need, and adjust the codegen to take account of this.
parent 8d115298
...@@ -81,6 +81,7 @@ ...@@ -81,6 +81,7 @@
<Compile Include="Reflection\FieldDescriptor.cs" /> <Compile Include="Reflection\FieldDescriptor.cs" />
<Compile Include="Reflection\FieldType.cs" /> <Compile Include="Reflection\FieldType.cs" />
<Compile Include="Reflection\FileDescriptor.cs" /> <Compile Include="Reflection\FileDescriptor.cs" />
<Compile Include="Reflection\GeneratedCodeInfo.cs" />
<Compile Include="Reflection\IDescriptor.cs" /> <Compile Include="Reflection\IDescriptor.cs" />
<Compile Include="Reflection\IFieldAccessor.cs" /> <Compile Include="Reflection\IFieldAccessor.cs" />
<Compile Include="Reflection\MapFieldAccessor.cs" /> <Compile Include="Reflection\MapFieldAccessor.cs" />
...@@ -90,8 +91,6 @@ ...@@ -90,8 +91,6 @@
<Compile Include="Reflection\OneofDescriptor.cs" /> <Compile Include="Reflection\OneofDescriptor.cs" />
<Compile Include="Reflection\PackageDescriptor.cs" /> <Compile Include="Reflection\PackageDescriptor.cs" />
<Compile Include="Reflection\PartialClasses.cs" /> <Compile Include="Reflection\PartialClasses.cs" />
<Compile Include="Reflection\ProtobufOneofAttribute.cs" />
<Compile Include="Reflection\ProtobufFieldAttribute.cs" />
<Compile Include="Reflection\ReflectionUtil.cs" /> <Compile Include="Reflection\ReflectionUtil.cs" />
<Compile Include="Reflection\RepeatedFieldAccessor.cs" /> <Compile Include="Reflection\RepeatedFieldAccessor.cs" />
<Compile Include="Reflection\ServiceDescriptor.cs" /> <Compile Include="Reflection\ServiceDescriptor.cs" />
......
...@@ -46,10 +46,11 @@ namespace Google.Protobuf.Reflection ...@@ -46,10 +46,11 @@ namespace Google.Protobuf.Reflection
private readonly MessageDescriptor containingType; private readonly MessageDescriptor containingType;
private readonly OneofDescriptor containingOneof; private readonly OneofDescriptor containingOneof;
private FieldType fieldType; private FieldType fieldType;
private readonly string propertyName; // Annoyingly, needed in Crosslink.
private IFieldAccessor accessor; private IFieldAccessor accessor;
internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file, internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file,
MessageDescriptor parent, int index) MessageDescriptor parent, int index, string propertyName)
: base(file, file.ComputeFullName(parent, proto.Name), index) : base(file, file.ComputeFullName(parent, proto.Name), index)
{ {
this.proto = proto; this.proto = proto;
...@@ -76,6 +77,12 @@ namespace Google.Protobuf.Reflection ...@@ -76,6 +77,12 @@ namespace Google.Protobuf.Reflection
} }
file.DescriptorPool.AddSymbol(this); file.DescriptorPool.AddSymbol(this);
// We can't create the accessor until we've cross-linked, unfortunately, as we
// may not know whether the type of the field is a map or not. Remember the property name
// for later.
// We could trust the generated code and check whether the type of the property is
// a MapField, but that feels a tad nasty.
this.propertyName = propertyName;
} }
/// <summary> /// <summary>
...@@ -291,26 +298,19 @@ namespace Google.Protobuf.Reflection ...@@ -291,26 +298,19 @@ namespace Google.Protobuf.Reflection
{ {
throw new DescriptorValidationException(this, "MessageSet format is not supported."); throw new DescriptorValidationException(this, "MessageSet format is not supported.");
} }
accessor = CreateAccessor(propertyName);
accessor = CreateAccessor();
} }
private IFieldAccessor CreateAccessor() private IFieldAccessor CreateAccessor(string propertyName)
{ {
// TODO: Check the performance of this with some large protos. Each message is O(N^2) in the number of fields, if (containingType.GeneratedType == null || propertyName == null)
// which isn't great...
if (containingType.GeneratedType == null)
{ {
return null; return null;
} }
var property = containingType var property = containingType.GeneratedType.GetProperty(propertyName);
.GeneratedType
.GetProperties()
.FirstOrDefault(p => p.IsDefined(typeof(ProtobufFieldAttribute), false) &&
p.GetCustomAttributes(typeof(ProtobufFieldAttribute), false).Cast<ProtobufFieldAttribute>().Single().Number == FieldNumber);
if (property == null) if (property == null)
{ {
return null; throw new DescriptorValidationException(this, "Property " + propertyName + " not found in " + containingType.GeneratedType);
} }
return IsMap ? new MapFieldAccessor(property, this) return IsMap ? new MapFieldAccessor(property, this)
: IsRepeated ? new RepeatedFieldAccessor(property, this) : IsRepeated ? new RepeatedFieldAccessor(property, this)
......
...@@ -62,12 +62,11 @@ namespace Google.Protobuf.Reflection ...@@ -62,12 +62,11 @@ namespace Google.Protobuf.Reflection
get { return proto.Syntax == "proto3" ? ProtoSyntax.Proto3 : ProtoSyntax.Proto2; } get { return proto.Syntax == "proto3" ? ProtoSyntax.Proto3 : ProtoSyntax.Proto2; }
} }
private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies, Type[] generatedTypes) private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies, GeneratedCodeInfo generatedCodeInfo)
{ {
this.pool = pool; this.pool = pool;
this.proto = proto; this.proto = proto;
this.dependencies = new ReadOnlyCollection<FileDescriptor>((FileDescriptor[]) dependencies.Clone()); this.dependencies = new ReadOnlyCollection<FileDescriptor>((FileDescriptor[]) dependencies.Clone());
IEnumerator<Type> generatedTypeIterator = generatedTypes == null ? null : ((IEnumerable<Type>)generatedTypes).GetEnumerator();
publicDependencies = DeterminePublicDependencies(this, proto, dependencies, allowUnknownDependencies); publicDependencies = DeterminePublicDependencies(this, proto, dependencies, allowUnknownDependencies);
...@@ -75,21 +74,15 @@ namespace Google.Protobuf.Reflection ...@@ -75,21 +74,15 @@ namespace Google.Protobuf.Reflection
messageTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.MessageType, messageTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.MessageType,
(message, index) => (message, index) =>
new MessageDescriptor(message, this, null, index, generatedTypeIterator)); new MessageDescriptor(message, this, null, index, generatedCodeInfo == null ? null : generatedCodeInfo.NestedTypes[index]));
enumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumType, enumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumType,
(enumType, index) => (enumType, index) =>
new EnumDescriptor(enumType, this, null, index, ReflectionUtil.GetNextType(generatedTypeIterator))); new EnumDescriptor(enumType, this, null, index, generatedCodeInfo == null ? null : generatedCodeInfo.NestedEnums[index]));
services = DescriptorUtil.ConvertAndMakeReadOnly(proto.Service, services = DescriptorUtil.ConvertAndMakeReadOnly(proto.Service,
(service, index) => (service, index) =>
new ServiceDescriptor(service, this, index)); new ServiceDescriptor(service, this, index));
// We should now have consumed all the generated types.
if (generatedTypeIterator != null && generatedTypeIterator.MoveNext())
{
throw new ArgumentException("More generated types left over after consuming all expected ones", "generatedTypes");
}
} }
/// <summary> /// <summary>
...@@ -260,7 +253,7 @@ namespace Google.Protobuf.Reflection ...@@ -260,7 +253,7 @@ namespace Google.Protobuf.Reflection
} }
return null; return null;
} }
/// <summary> /// <summary>
/// Builds a FileDescriptor from its protocol buffer representation. /// Builds a FileDescriptor from its protocol buffer representation.
/// </summary> /// </summary>
...@@ -269,10 +262,11 @@ namespace Google.Protobuf.Reflection ...@@ -269,10 +262,11 @@ namespace Google.Protobuf.Reflection
/// file's dependencies, in the exact order listed in the .proto file. May be null, /// file's dependencies, in the exact order listed in the .proto file. May be null,
/// in which case it is treated as an empty array.</param> /// in which case it is treated as an empty array.</param>
/// <param name="allowUnknownDependencies">Whether unknown dependencies are ignored (true) or cause an exception to be thrown (false).</param> /// <param name="allowUnknownDependencies">Whether unknown dependencies are ignored (true) or cause an exception to be thrown (false).</param>
/// <param name="generatedCodeInfo">Reflection information, if any. May be null, specifically for non-generated code.</param>
/// <exception cref="DescriptorValidationException">If <paramref name="proto"/> is not /// <exception cref="DescriptorValidationException">If <paramref name="proto"/> is not
/// a valid descriptor. This can occur for a number of reasons, such as a field /// a valid descriptor. This can occur for a number of reasons, such as a field
/// having an undefined type or because two messages were defined with the same name.</exception> /// having an undefined type or because two messages were defined with the same name.</exception>
private static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies, Type[] generatedTypes) private static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies, GeneratedCodeInfo generatedCodeInfo)
{ {
// Building descriptors involves two steps: translating and linking. // Building descriptors involves two steps: translating and linking.
// In the translation step (implemented by FileDescriptor's // In the translation step (implemented by FileDescriptor's
...@@ -289,7 +283,7 @@ namespace Google.Protobuf.Reflection ...@@ -289,7 +283,7 @@ namespace Google.Protobuf.Reflection
} }
DescriptorPool pool = new DescriptorPool(dependencies); DescriptorPool pool = new DescriptorPool(dependencies);
FileDescriptor result = new FileDescriptor(proto, dependencies, pool, allowUnknownDependencies, generatedTypes); FileDescriptor result = new FileDescriptor(proto, dependencies, pool, allowUnknownDependencies, generatedCodeInfo);
// TODO(jonskeet): Reinstate these checks, or get rid of them entirely. They aren't in the Java code, // TODO(jonskeet): Reinstate these checks, or get rid of them entirely. They aren't in the Java code,
// and fail for the CustomOptions test right now. (We get "descriptor.proto" vs "google/protobuf/descriptor.proto".) // and fail for the CustomOptions test right now. (We get "descriptor.proto" vs "google/protobuf/descriptor.proto".)
...@@ -330,19 +324,13 @@ namespace Google.Protobuf.Reflection ...@@ -330,19 +324,13 @@ namespace Google.Protobuf.Reflection
/// Creates an instance for generated code. /// Creates an instance for generated code.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// The <paramref name="generatedTypes"/> parameter should be null for descriptors which don't correspond to /// The <paramref name="generatedCodeInfo"/> parameter should be null for descriptors which don't correspond to
/// generated types. Otherwise, the array should represent all the generated types in the file: messages then /// generated types. Otherwise, it should be a <see cref="GeneratedCodeInfo"/> with nested types and nested
/// enums. Within each message, there can be nested messages and enums, which must be specified "inline" in the array: /// enums corresponding to the types and enums contained within the file descriptor.
/// containing message, nested messages, nested enums - and of course each nested message may contain *more* nested messages,
/// etc. All messages within the descriptor should be represented, even if they do not have a generated type - any
/// type without a corresponding generated type (such as map entries) should respond to a null element.
/// For example, a file with a messages OuterMessage and InnerMessage, and enums OuterEnum and InnerEnum (where
/// InnerMessage and InnerEnum are nested within InnerMessage) would result in an array of
/// OuterMessage, InnerMessage, InnerEnum, OuterEnum.
/// </remarks> /// </remarks>
public static FileDescriptor InternalBuildGeneratedFileFrom(byte[] descriptorData, public static FileDescriptor InternalBuildGeneratedFileFrom(byte[] descriptorData,
FileDescriptor[] dependencies, FileDescriptor[] dependencies,
Type[] generatedTypes) GeneratedCodeInfo generatedCodeInfo)
{ {
FileDescriptorProto proto; FileDescriptorProto proto;
try try
...@@ -358,7 +346,7 @@ namespace Google.Protobuf.Reflection ...@@ -358,7 +346,7 @@ namespace Google.Protobuf.Reflection
{ {
// When building descriptors for generated code, we allow unknown // When building descriptors for generated code, we allow unknown
// dependencies by default. // dependencies by default.
return BuildFrom(proto, dependencies, true, generatedTypes); return BuildFrom(proto, dependencies, true, generatedCodeInfo);
} }
catch (DescriptorValidationException e) catch (DescriptorValidationException e)
{ {
......
using System;
namespace Google.Protobuf.Reflection
{
/// <summary>
/// Extra information provided by generated code when initializing a message or file descriptor.
/// These are constructed as required, and are not long-lived. Hand-written code should
/// never need to use this type.
/// </summary>
public sealed class GeneratedCodeInfo
{
private static readonly string[] EmptyNames = new string[0];
private static readonly GeneratedCodeInfo[] EmptyCodeInfo = new GeneratedCodeInfo[0];
/// <summary>
/// Irrelevant for file descriptors; the CLR type for the message for message descriptors.
/// </summary>
public Type ClrType { get; private set; }
/// <summary>
/// Irrelevant for file descriptors; the CLR property names (in message descriptor field order)
/// for fields in the message for message descriptors.
/// </summary>
public string[] PropertyNames { get; private set; }
/// <summary>
/// Irrelevant for file descriptors; the CLR property "base" names (in message descriptor oneof order)
/// for oneofs in the message for message descriptors. It is expected that for a oneof name of "Foo",
/// there will be a "FooCase" property and a "ClearFoo" method.
/// </summary>
public string[] OneofNames { get; private set; }
/// <summary>
/// The reflection information for types within this file/message descriptor. Elements may be null
/// if there is no corresponding generated type, e.g. for map entry types.
/// </summary>
public GeneratedCodeInfo[] NestedTypes { get; private set; }
/// <summary>
/// The CLR types for enums within this file/message descriptor.
/// </summary>
public Type[] NestedEnums { get; private set; }
/// <summary>
/// Creates a GeneratedCodeInfo for a message descriptor, with nested types, nested enums, the CLR type, property names and oneof names.
/// Each array parameter may be null, to indicate a lack of values.
/// The parameter order is designed to make it feasible to format the generated code readably.
/// </summary>
public GeneratedCodeInfo(Type clrType, string[] propertyNames, string[] oneofNames, Type[] nestedEnums, GeneratedCodeInfo[] nestedTypes)
{
NestedTypes = nestedTypes ?? EmptyCodeInfo;
NestedEnums = nestedEnums ?? ReflectionUtil.EmptyTypes;
ClrType = clrType;
PropertyNames = propertyNames ?? EmptyNames;
OneofNames = oneofNames ?? EmptyNames;
}
/// <summary>
/// Creates a GeneratedCodeInfo for a file descriptor, with only types and enums.
/// </summary>
public GeneratedCodeInfo(Type[] nestedEnums, GeneratedCodeInfo[] nestedTypes)
: this(null, null, null, nestedEnums, nestedTypes)
{
}
}
}
\ No newline at end of file
...@@ -66,29 +66,33 @@ namespace Google.Protobuf.Reflection ...@@ -66,29 +66,33 @@ namespace Google.Protobuf.Reflection
private readonly Type generatedType; private readonly Type generatedType;
private IDictionary<int, IFieldAccessor> fieldAccessorsByFieldNumber; private IDictionary<int, IFieldAccessor> fieldAccessorsByFieldNumber;
internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex, IEnumerator<Type> generatedTypeIterator) internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex, GeneratedCodeInfo generatedCodeInfo)
: base(file, file.ComputeFullName(parent, proto.Name), typeIndex) : base(file, file.ComputeFullName(parent, proto.Name), typeIndex)
{ {
this.proto = proto; this.proto = proto;
generatedType = ReflectionUtil.GetNextType(generatedTypeIterator); generatedType = generatedCodeInfo == null ? null : generatedCodeInfo.ClrType;
containingType = parent;
oneofs = DescriptorUtil.ConvertAndMakeReadOnly(proto.OneofDecl,
(oneof, index) =>
new OneofDescriptor(oneof, file, this, index));
nestedTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.NestedType,
(type, index) =>
new MessageDescriptor(type, file, this, index, generatedTypeIterator));
enumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumType, containingType = parent;
(type, index) =>
new EnumDescriptor(type, file, this, index, ReflectionUtil.GetNextType(generatedTypeIterator)));
// TODO(jonskeet): Sort fields first? oneofs = DescriptorUtil.ConvertAndMakeReadOnly(
fields = DescriptorUtil.ConvertAndMakeReadOnly(proto.Field, proto.OneofDecl,
(field, index) => (oneof, index) =>
new FieldDescriptor(field, file, this, index)); new OneofDescriptor(oneof, file, this, index, generatedCodeInfo == null ? null : generatedCodeInfo.OneofNames[index]));
nestedTypes = DescriptorUtil.ConvertAndMakeReadOnly(
proto.NestedType,
(type, index) =>
new MessageDescriptor(type, file, this, index, generatedCodeInfo == null ? null : generatedCodeInfo.NestedTypes[index]));
enumTypes = DescriptorUtil.ConvertAndMakeReadOnly(
proto.EnumType,
(type, index) =>
new EnumDescriptor(type, file, this, index, generatedCodeInfo == null ? null : generatedCodeInfo.NestedEnums[index]));
fields = DescriptorUtil.ConvertAndMakeReadOnly(
proto.Field,
(field, index) =>
new FieldDescriptor(field, file, this, index, generatedCodeInfo == null ? null : generatedCodeInfo.PropertyNames[index]));
file.DescriptorPool.AddSymbol(this); file.DescriptorPool.AddSymbol(this);
} }
...@@ -220,6 +224,6 @@ namespace Google.Protobuf.Reflection ...@@ -220,6 +224,6 @@ namespace Google.Protobuf.Reflection
} }
fieldAccessorsByFieldNumber = new ReadOnlyDictionary<int, IFieldAccessor>(fields.ToDictionary(field => field.FieldNumber, field => field.Accessor)); fieldAccessorsByFieldNumber = new ReadOnlyDictionary<int, IFieldAccessor>(fields.ToDictionary(field => field.FieldNumber, field => field.Accessor));
} }
} }
} }
\ No newline at end of file
...@@ -41,15 +41,16 @@ namespace Google.Protobuf.Reflection ...@@ -41,15 +41,16 @@ namespace Google.Protobuf.Reflection
private readonly OneofDescriptorProto proto; private readonly OneofDescriptorProto proto;
private MessageDescriptor containingType; private MessageDescriptor containingType;
private IList<FieldDescriptor> fields; private IList<FieldDescriptor> fields;
private OneofAccessor accessor; private readonly OneofAccessor accessor;
internal OneofDescriptor(OneofDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index) internal OneofDescriptor(OneofDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index, string clrName)
: base(file, file.ComputeFullName(parent, proto.Name), index) : base(file, file.ComputeFullName(parent, proto.Name), index)
{ {
this.proto = proto; this.proto = proto;
containingType = parent; containingType = parent;
file.DescriptorPool.AddSymbol(this); file.DescriptorPool.AddSymbol(this);
accessor = CreateAccessor(clrName);
} }
/// <summary> /// <summary>
...@@ -77,33 +78,23 @@ namespace Google.Protobuf.Reflection ...@@ -77,33 +78,23 @@ namespace Google.Protobuf.Reflection
} }
} }
fields = new ReadOnlyCollection<FieldDescriptor>(fieldCollection); fields = new ReadOnlyCollection<FieldDescriptor>(fieldCollection);
accessor = CreateAccessor();
} }
private OneofAccessor CreateAccessor() private OneofAccessor CreateAccessor(string clrName)
{ {
if (containingType.GeneratedType == null) if (containingType.GeneratedType == null || clrName == null)
{ {
return null; return null;
} }
var caseProperty = containingType var caseProperty = containingType.GeneratedType.GetProperty(clrName + "Case");
.GeneratedType
.GetProperties()
.FirstOrDefault(p => p.IsDefined(typeof(ProtobufOneofAttribute), false) &&
p.GetCustomAttributes(typeof(ProtobufOneofAttribute), false).Cast<ProtobufOneofAttribute>().Single().Name == Name);
if (caseProperty == null) if (caseProperty == null)
{ {
return null; throw new DescriptorValidationException(this, "Property " + clrName + "Case not found in " + containingType.GeneratedType);
} }
var clearMethod = containingType.GeneratedType.GetMethod("Clear" + clrName, ReflectionUtil.EmptyTypes);
var clearMethod = containingType
.GeneratedType
.GetMethods()
.FirstOrDefault(p => p.IsDefined(typeof(ProtobufOneofAttribute), false) &&
p.GetCustomAttributes(typeof(ProtobufOneofAttribute), false).Cast<ProtobufOneofAttribute>().Single().Name == Name);
if (clearMethod == null) if (clearMethod == null)
{ {
return null; throw new DescriptorValidationException(this, "Method Clear" + clrName + " not found in " + containingType.GeneratedType);
} }
return new OneofAccessor(caseProperty, clearMethod, this); return new OneofAccessor(caseProperty, clearMethod, this);
......
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2015 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.Reflection
{
/// <summary>
/// Attribute applied to a generated property corresponding to a field in a .proto file.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class ProtobufFieldAttribute : Attribute
{
/// <summary>
/// The field number in the original .proto file.
/// </summary>
public int Number { get; set; }
/// <summary>
/// The field name in the original .proto file.
/// </summary>
public string Name { get; set; }
public ProtobufFieldAttribute(int number, string name)
{
this.Number = number;
this.Name = name;
}
}
}
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2015 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.Reflection
{
/// <summary>
/// Attribute applied to the "case" property or "clear" method corresponding to a oneof in a .proto file.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = false)]
public sealed class ProtobufOneofAttribute : Attribute
{
/// <summary>
/// The oneof name in the original .proto file.
/// </summary>
public string Name { get; set; }
public ProtobufOneofAttribute(string name)
{
this.Name = name;
}
}
}
...@@ -102,25 +102,6 @@ namespace Google.Protobuf.Reflection ...@@ -102,25 +102,6 @@ namespace Google.Protobuf.Reflection
Expression castTarget = Expression.Convert(targetParameter, method.DeclaringType); Expression castTarget = Expression.Convert(targetParameter, method.DeclaringType);
Expression call = Expression.Call(castTarget, method); Expression call = Expression.Call(castTarget, method);
return Expression.Lambda<Action<object>>(call, targetParameter).Compile(); return Expression.Lambda<Action<object>>(call, targetParameter).Compile();
} }
/// <summary>
/// Returns the next type from an iterator of types, unless the iterator is a null reference,
/// in which case null is returned.
/// </summary>
internal static Type GetNextType(IEnumerator<Type> generatedTypeIterator)
{
if (generatedTypeIterator == null)
{
return null;
}
if (!generatedTypeIterator.MoveNext())
{
// This parameter name corresponds to any public method supplying the generated types to start with.
throw new ArgumentException("More generated types left over after consuming all expected ones", "generatedTypes");
}
return generatedTypeIterator.Current;
}
} }
} }
\ No newline at end of file
...@@ -74,7 +74,6 @@ void FieldGeneratorBase::SetCommonFieldVariables( ...@@ -74,7 +74,6 @@ void FieldGeneratorBase::SetCommonFieldVariables(
(*variables)["property_name"] = property_name(); (*variables)["property_name"] = property_name();
(*variables)["type_name"] = type_name(); (*variables)["type_name"] = type_name();
(*variables)["original_name"] = descriptor_->name();
(*variables)["name"] = name(); (*variables)["name"] = name();
(*variables)["descriptor_name"] = descriptor_->name(); (*variables)["descriptor_name"] = descriptor_->name();
(*variables)["default_value"] = default_value(); (*variables)["default_value"] = default_value();
...@@ -431,10 +430,6 @@ std::string FieldGeneratorBase::capitalized_type_name() { ...@@ -431,10 +430,6 @@ std::string FieldGeneratorBase::capitalized_type_name() {
} }
} }
std::string FieldGeneratorBase::field_ordinal() {
return SimpleItoa(fieldOrdinal_);
}
} // namespace csharp } // namespace csharp
} // namespace compiler } // namespace compiler
} // namespace protobuf } // namespace protobuf
......
...@@ -85,7 +85,6 @@ class FieldGeneratorBase : public SourceGeneratorBase { ...@@ -85,7 +85,6 @@ class FieldGeneratorBase : public SourceGeneratorBase {
std::string default_value(const FieldDescriptor* descriptor); std::string default_value(const FieldDescriptor* descriptor);
std::string number(); std::string number();
std::string capitalized_type_name(); std::string capitalized_type_name();
std::string field_ordinal();
private: private:
void SetCommonFieldVariables(map<string, string>* variables); void SetCommonFieldVariables(map<string, string>* variables);
......
...@@ -79,7 +79,6 @@ void MapFieldGenerator::GenerateMembers(io::Printer* printer) { ...@@ -79,7 +79,6 @@ void MapFieldGenerator::GenerateMembers(io::Printer* printer) {
AddDeprecatedFlag(printer); AddDeprecatedFlag(printer);
printer->Print( printer->Print(
variables_, variables_,
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
"$access_level$ pbc::MapField<$key_type_name$, $value_type_name$> $property_name$ {\n" "$access_level$ pbc::MapField<$key_type_name$, $value_type_name$> $property_name$ {\n"
" get { return $name$_; }\n" " get { return $name$_; }\n"
"}\n"); "}\n");
......
...@@ -115,19 +115,6 @@ void MessageGenerator::Generate(io::Printer* printer) { ...@@ -115,19 +115,6 @@ void MessageGenerator::Generate(io::Printer* printer) {
vars, vars,
"private static readonly pb::MessageParser<$class_name$> _parser = new pb::MessageParser<$class_name$>(() => new $class_name$());\n" "private static readonly pb::MessageParser<$class_name$> _parser = new pb::MessageParser<$class_name$>(() => new $class_name$());\n"
"public static pb::MessageParser<$class_name$> Parser { get { return _parser; } }\n\n"); "public static pb::MessageParser<$class_name$> Parser { get { return _parser; } }\n\n");
printer->Print(
"private static readonly string[] _fieldNames = "
"new string[] { $slash$$field_names$$slash$ };\n",
"field_names", JoinStrings(field_names(), "\", \""),
"slash", field_names().size() > 0 ? "\"" : "");
std::vector<std::string> tags;
for (int i = 0; i < field_names().size(); i++) {
uint32 tag = FixedMakeTag(descriptor_->FindFieldByName(field_names()[i]));
tags.push_back(SimpleItoa(tag));
}
printer->Print(
"private static readonly uint[] _fieldTags = new uint[] { $tags$ };\n",
"tags", JoinStrings(tags, ", "));
// Access the message descriptor via the relevant file descriptor or containing message descriptor. // Access the message descriptor via the relevant file descriptor or containing message descriptor.
if (!descriptor_->containing_type()) { if (!descriptor_->containing_type()) {
......
...@@ -64,7 +64,6 @@ void MessageFieldGenerator::GenerateMembers(io::Printer* printer) { ...@@ -64,7 +64,6 @@ void MessageFieldGenerator::GenerateMembers(io::Printer* printer) {
AddDeprecatedFlag(printer); AddDeprecatedFlag(printer);
printer->Print( printer->Print(
variables_, variables_,
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
"$access_level$ $type_name$ $property_name$ {\n" "$access_level$ $type_name$ $property_name$ {\n"
" get { return $name$_; }\n" " get { return $name$_; }\n"
" set {\n" " set {\n"
...@@ -159,7 +158,6 @@ void MessageOneofFieldGenerator::GenerateMembers(io::Printer* printer) { ...@@ -159,7 +158,6 @@ void MessageOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
AddDeprecatedFlag(printer); AddDeprecatedFlag(printer);
printer->Print( printer->Print(
variables_, variables_,
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
"$access_level$ $type_name$ $property_name$ {\n" "$access_level$ $type_name$ $property_name$ {\n"
" get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : null; }\n" " get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : null; }\n"
" set {\n" " set {\n"
......
...@@ -71,7 +71,6 @@ void PrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) { ...@@ -71,7 +71,6 @@ void PrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
AddDeprecatedFlag(printer); AddDeprecatedFlag(printer);
printer->Print( printer->Print(
variables_, variables_,
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
"$access_level$ $type_name$ $property_name$ {\n" "$access_level$ $type_name$ $property_name$ {\n"
" get { return $name$_; }\n" " get { return $name$_; }\n"
" set {\n" " set {\n"
...@@ -175,7 +174,6 @@ void PrimitiveOneofFieldGenerator::GenerateMembers(io::Printer* printer) { ...@@ -175,7 +174,6 @@ void PrimitiveOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
AddDeprecatedFlag(printer); AddDeprecatedFlag(printer);
printer->Print( printer->Print(
variables_, variables_,
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
"$access_level$ $type_name$ $property_name$ {\n" "$access_level$ $type_name$ $property_name$ {\n"
" get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : $default_value$; }\n" " get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : $default_value$; }\n"
" set {\n" " set {\n"
......
...@@ -65,7 +65,6 @@ void RepeatedEnumFieldGenerator::GenerateMembers(io::Printer* printer) { ...@@ -65,7 +65,6 @@ void RepeatedEnumFieldGenerator::GenerateMembers(io::Printer* printer) {
AddDeprecatedFlag(printer); AddDeprecatedFlag(printer);
printer->Print( printer->Print(
variables_, variables_,
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
"$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n" "$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n"
" get { return $name$_; }\n" " get { return $name$_; }\n"
"}\n"); "}\n");
......
...@@ -78,7 +78,6 @@ void RepeatedMessageFieldGenerator::GenerateMembers(io::Printer* printer) { ...@@ -78,7 +78,6 @@ void RepeatedMessageFieldGenerator::GenerateMembers(io::Printer* printer) {
AddDeprecatedFlag(printer); AddDeprecatedFlag(printer);
printer->Print( printer->Print(
variables_, variables_,
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
"$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n" "$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n"
" get { return $name$_; }\n" " get { return $name$_; }\n"
"}\n"); "}\n");
......
...@@ -65,7 +65,6 @@ void RepeatedPrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) { ...@@ -65,7 +65,6 @@ void RepeatedPrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
AddDeprecatedFlag(printer); AddDeprecatedFlag(printer);
printer->Print( printer->Print(
variables_, variables_,
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
"$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n" "$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n"
" get { return $name$_; }\n" " get { return $name$_; }\n"
"}\n"); "}\n");
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <google/protobuf/descriptor.pb.h> #include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/printer.h> #include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h> #include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/compiler/csharp/csharp_enum.h> #include <google/protobuf/compiler/csharp/csharp_enum.h>
...@@ -168,10 +169,10 @@ void UmbrellaClassGenerator::WriteDescriptor(io::Printer* printer) { ...@@ -168,10 +169,10 @@ void UmbrellaClassGenerator::WriteDescriptor(io::Printer* printer) {
printer->Print("\"$base64$\", \n", "base64", base64.substr(0, 60)); printer->Print("\"$base64$\", \n", "base64", base64.substr(0, 60));
base64 = base64.substr(60); base64 = base64.substr(60);
} }
printer->Outdent();
printer->Print("\"$base64$\"));\n", "base64", base64); printer->Print("\"$base64$\"));\n", "base64", base64);
printer->Outdent(); printer->Outdent();
printer->Outdent(); printer->Outdent();
printer->Outdent();
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// Invoke InternalBuildGeneratedFileFrom() to build the file. // Invoke InternalBuildGeneratedFileFrom() to build the file.
...@@ -184,34 +185,108 @@ void UmbrellaClassGenerator::WriteDescriptor(io::Printer* printer) { ...@@ -184,34 +185,108 @@ void UmbrellaClassGenerator::WriteDescriptor(io::Printer* printer) {
"full_umbrella_class_name", "full_umbrella_class_name",
GetFullUmbrellaClassName(file_->dependency(i))); GetFullUmbrellaClassName(file_->dependency(i)));
} }
// Specify all the generated types (messages and enums), recursively, as an array.
printer->Print("},\n" printer->Print("},\n"
" new global::System.Type[] { "); " new pbr::GeneratedCodeInfo(");
for (int i = 0; i < file_->message_type_count(); i++) { // Specify all the generated code information, recursively.
WriteTypeLiterals(file_->message_type(i), printer); if (file_->enum_type_count() > 0) {
printer->Print("new[] {");
for (int i = 0; i < file_->enum_type_count(); i++) {
printer->Print("typeof($type_name$), ", "type_name", GetClassName(file_->enum_type(i)));
}
printer->Print("}, ");
}
else {
printer->Print("null, ");
}
if (file_->message_type_count() > 0) {
printer->Print("new pbr::GeneratedCodeInfo[] {\n");
printer->Indent();
printer->Indent();
printer->Indent();
for (int i = 0; i < file_->message_type_count(); i++) {
WriteGeneratedCodeInfo(file_->message_type(i), printer, i == file_->message_type_count() - 1);
}
printer->Outdent();
printer->Print("\n}));\n");
printer->Outdent();
printer->Outdent();
} }
for (int i = 0; i < file_->enum_type_count(); i++) { else {
printer->Print("typeof($type_name$), ", "type_name", GetClassName(file_->enum_type(i))); printer->Print("null));\n");
} }
printer->Print("});\n");
printer->Outdent(); printer->Outdent();
printer->Print("}\n"); printer->Print("}\n");
printer->Print("#endregion\n\n"); printer->Print("#endregion\n\n");
} }
void UmbrellaClassGenerator::WriteTypeLiterals(const Descriptor* descriptor, io::Printer* printer) { // Write out the generated code for a particular message. This consists of the CLR type, property names
if (IsMapEntryMessage(descriptor)) { // corresponding to fields, names corresponding to oneofs, nested enums, and nested types. Each array part
printer->Print("null, "); // can be specified as null if it would be empty, to make the generated code somewhat simpler to read.
return; // We write a line break at the end of each generated code info, so that in the final file we'll see all
} // the types, pre-ordered depth first, one per line. The indentation will be slightly unusual,
printer->Print("typeof($type_name$), ", "type_name", GetClassName(descriptor)); // in that it will look like a single array when it's actually constructing a tree, but it'll be easy to
for (int i = 0; i < descriptor->nested_type_count(); i++) { // read even with multiple levels of nesting.
WriteTypeLiterals(descriptor->nested_type(i), printer); // The "last" parameter indicates whether this message descriptor is the last one being printed in this immediate
} // context. It governs whether or not a trailing comma and newline is written after the constructor, effectively
for (int i = 0; i < descriptor->enum_type_count(); i++) { // just controlling the formatting in the generated code.
printer->Print("typeof($type_name$), ", "type_name", GetClassName(descriptor->enum_type(i))); void UmbrellaClassGenerator::WriteGeneratedCodeInfo(const Descriptor* descriptor, io::Printer* printer, bool last) {
} if (IsMapEntryMessage(descriptor)) {
printer->Print("null, ");
return;
}
// Generated message type
printer->Print("new pbr::GeneratedCodeInfo(typeof($type_name$), ", "type_name", GetClassName(descriptor));
// Fields
if (descriptor->field_count() > 0) {
std::vector<std::string> fields;
for (int i = 0; i < descriptor->field_count(); i++) {
fields.push_back(GetPropertyName(descriptor->field(i)));
}
printer->Print("new[]{ \"$fields$\" }, ", "fields", JoinStrings(fields, "\", \""));
}
else {
printer->Print("null, ");
}
// Oneofs
if (descriptor->oneof_decl_count() > 0) {
std::vector<std::string> oneofs;
for (int i = 0; i < descriptor->oneof_decl_count(); i++) {
oneofs.push_back(UnderscoresToCamelCase(descriptor->oneof_decl(i)->name(), true));
}
printer->Print("new[]{ \"$oneofs$\" }, ", "oneofs", JoinStrings(oneofs, "\", \""));
}
else {
printer->Print("null, ");
}
// Nested enums
if (descriptor->enum_type_count() > 0) {
std::vector<std::string> enums;
for (int i = 0; i < descriptor->enum_type_count(); i++) {
enums.push_back(GetClassName(descriptor->enum_type(i)));
}
printer->Print("new[]{ typeof($enums$) }, ", "enums", JoinStrings(enums, "), typeof("));
}
else {
printer->Print("null, ");
}
// Nested types
if (descriptor->nested_type_count() > 0) {
// Need to specify array type explicitly here, as all elements may be null.
printer->Print("new pbr::GeneratedCodeInfo[] { ");
for (int i = 0; i < descriptor->nested_type_count(); i++) {
WriteGeneratedCodeInfo(descriptor->nested_type(i), printer, i == descriptor->nested_type_count() - 1);
}
printer->Print("}");
}
else {
printer->Print("null");
}
printer->Print(last ? ")" : "),\n");
} }
} // namespace csharp } // namespace csharp
......
...@@ -57,7 +57,7 @@ class UmbrellaClassGenerator : public SourceGeneratorBase { ...@@ -57,7 +57,7 @@ class UmbrellaClassGenerator : public SourceGeneratorBase {
void WriteIntroduction(io::Printer* printer); void WriteIntroduction(io::Printer* printer);
void WriteDescriptor(io::Printer* printer); void WriteDescriptor(io::Printer* printer);
void WriteTypeLiterals(const Descriptor* descriptor, io::Printer* printer); void WriteGeneratedCodeInfo(const Descriptor* descriptor, io::Printer* printer, bool last);
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UmbrellaClassGenerator); GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UmbrellaClassGenerator);
}; };
......
...@@ -73,7 +73,6 @@ void WrapperFieldGenerator::GenerateMembers(io::Printer* printer) { ...@@ -73,7 +73,6 @@ void WrapperFieldGenerator::GenerateMembers(io::Printer* printer) {
AddDeprecatedFlag(printer); AddDeprecatedFlag(printer);
printer->Print( printer->Print(
variables_, variables_,
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
"$access_level$ $type_name$ $property_name$ {\n" "$access_level$ $type_name$ $property_name$ {\n"
" get { return $name$_; }\n" " get { return $name$_; }\n"
" set {\n" " set {\n"
...@@ -170,7 +169,6 @@ void WrapperOneofFieldGenerator::GenerateMembers(io::Printer* printer) { ...@@ -170,7 +169,6 @@ void WrapperOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
AddDeprecatedFlag(printer); AddDeprecatedFlag(printer);
printer->Print( printer->Print(
variables_, variables_,
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
"$access_level$ $type_name$ $property_name$ {\n" "$access_level$ $type_name$ $property_name$ {\n"
" get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : ($type_name$) null; }\n" " get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : ($type_name$) null; }\n"
" set {\n" " set {\n"
......
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