From 53c399a1d65df65e9f83a70b55041a01cf8d7489 Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Mon, 20 Jul 2015 19:24:31 +0100 Subject: Revamp to reflection. Changes in brief: 1. Descriptor is now the entry point for all reflection. 2. IReflectedMessage has gone; there's now a Descriptor property in IMessage, which is explicitly implemented (due to the static property). 3. FieldAccessorTable has gone away 4. IFieldAccessor and OneofFieldAccessor still exist; we *could* put the functionality straight into FieldDescriptor and OneofDescriptor... I'm unsure about that. 5. There's a temporary property MessageDescriptor.FieldAccessorsByFieldNumber to make the test changes small - we probably want this to go away 6. Discovery for delegates is now via attributes applied to properties and the Clear method of a oneof I'm happy with 1-3. 4 I'm unsure about - feedback welcome. 5 will go away 6 I'm unsure about, both in design and implementation. Should we have a ProtobufMessageAttribute too? Should we find all the relevant attributes in MessageDescriptor and pass them down, to avoid an O(N^2) scenario? Generated code changes coming in the next commit. --- .../Google.Protobuf/Reflection/FileDescriptor.cs | 36 +++++++++++++++++----- 1 file changed, 29 insertions(+), 7 deletions(-) (limited to 'csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs') diff --git a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs index db393480..a10e617b 100644 --- a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs @@ -62,11 +62,12 @@ namespace Google.Protobuf.Reflection get { return proto.Syntax == "proto3" ? ProtoSyntax.Proto3 : ProtoSyntax.Proto2; } } - private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies) + private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies, Type[] generatedTypes) { this.pool = pool; this.proto = proto; this.dependencies = new ReadOnlyCollection((FileDescriptor[]) dependencies.Clone()); + IEnumerator generatedTypeIterator = generatedTypes == null ? null : ((IEnumerable)generatedTypes).GetEnumerator(); publicDependencies = DeterminePublicDependencies(this, proto, dependencies, allowUnknownDependencies); @@ -74,15 +75,21 @@ namespace Google.Protobuf.Reflection messageTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.MessageType, (message, index) => - new MessageDescriptor(message, this, null, index)); + new MessageDescriptor(message, this, null, index, generatedTypeIterator)); enumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumType, (enumType, index) => - new EnumDescriptor(enumType, this, null, index)); + new EnumDescriptor(enumType, this, null, index, ReflectionUtil.GetNextType(generatedTypeIterator))); services = DescriptorUtil.ConvertAndMakeReadOnly(proto.Service, (service, 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"); + } } /// @@ -265,7 +272,7 @@ namespace Google.Protobuf.Reflection /// If is not /// 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. - private static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies) + private static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies, Type[] generatedTypes) { // Building descriptors involves two steps: translating and linking. // In the translation step (implemented by FileDescriptor's @@ -282,7 +289,7 @@ namespace Google.Protobuf.Reflection } DescriptorPool pool = new DescriptorPool(dependencies); - FileDescriptor result = new FileDescriptor(proto, dependencies, pool, allowUnknownDependencies); + FileDescriptor result = new FileDescriptor(proto, dependencies, pool, allowUnknownDependencies, generatedTypes); // 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".) @@ -319,8 +326,23 @@ namespace Google.Protobuf.Reflection } } + /// + /// Creates an instance for generated code. + /// + /// + /// The parameter should be null for descriptors which don't correspond to + /// generated types. Otherwise, the array should be represent all the generated types in the file: messages then + /// enums. Within each message, there can be nested messages and enums, which must be specified "inline" in the array: + /// 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. + /// public static FileDescriptor InternalBuildGeneratedFileFrom(byte[] descriptorData, - FileDescriptor[] dependencies) + FileDescriptor[] dependencies, + Type[] generatedTypes) { FileDescriptorProto proto; try @@ -336,7 +358,7 @@ namespace Google.Protobuf.Reflection { // When building descriptors for generated code, we allow unknown // dependencies by default. - return BuildFrom(proto, dependencies, true); + return BuildFrom(proto, dependencies, true, generatedTypes); } catch (DescriptorValidationException e) { -- cgit v1.2.3