diff options
author | Jon Skeet <jonskeet@google.com> | 2015-04-28 14:49:49 +0100 |
---|---|---|
committer | Jon Skeet <jonskeet@google.com> | 2015-04-28 14:53:24 +0100 |
commit | f3504cf3b1d456a843e8242fdee9ba0bf2991dc1 (patch) | |
tree | 019441e4649f66f6273a13bcff5dbafbd12fc9b6 /csharp/src/ProtocolBuffers/Descriptors | |
parent | b977c3ed63146525064463c33ddfaa96152989ed (diff) | |
download | protobuf-f3504cf3b1d456a843e8242fdee9ba0bf2991dc1.tar.gz protobuf-f3504cf3b1d456a843e8242fdee9ba0bf2991dc1.tar.bz2 protobuf-f3504cf3b1d456a843e8242fdee9ba0bf2991dc1.zip |
First part of making the C# runtime work with the new codegen.
1) Remove CSharpOptions
2) A new version of DescriptorProtoFile (with manual changes from codegen - it would otherwise be Descriptor.cs)
3) Turn off CLS compliance (which we'll remove from the codebase entirely; I don't think it's actually relevant these days)
4) Add "public imports" to FileDescriptor, with code broadly copied from the Java codebase.
Lots more changes to commit before it will build and tests run, but one step at a time...
Diffstat (limited to 'csharp/src/ProtocolBuffers/Descriptors')
4 files changed, 98 insertions, 190 deletions
diff --git a/csharp/src/ProtocolBuffers/Descriptors/DescriptorPool.cs b/csharp/src/ProtocolBuffers/Descriptors/DescriptorPool.cs index ae819801..30718709 100644 --- a/csharp/src/ProtocolBuffers/Descriptors/DescriptorPool.cs +++ b/csharp/src/ProtocolBuffers/Descriptors/DescriptorPool.cs @@ -50,14 +50,15 @@ namespace Google.ProtocolBuffers.Descriptors private readonly IDictionary<DescriptorIntPair, EnumValueDescriptor> enumValuesByNumber =
new Dictionary<DescriptorIntPair, EnumValueDescriptor>();
- private readonly DescriptorPool[] dependencies;
+ private readonly HashSet<FileDescriptor> dependencies;
internal DescriptorPool(FileDescriptor[] dependencyFiles)
{
- dependencies = new DescriptorPool[dependencyFiles.Length];
+ dependencies = new HashSet<FileDescriptor>();
for (int i = 0; i < dependencyFiles.Length; i++)
{
- dependencies[i] = dependencyFiles[i].DescriptorPool;
+ dependencies.Add(dependencyFiles[i]);
+ ImportPublicDependencies(dependencyFiles[i]);
}
foreach (FileDescriptor dependency in dependencyFiles)
@@ -66,6 +67,17 @@ namespace Google.ProtocolBuffers.Descriptors }
}
+ private void ImportPublicDependencies(FileDescriptor file)
+ {
+ foreach (FileDescriptor dependency in file.PublicDependencies)
+ {
+ if (dependencies.Add(dependency))
+ {
+ ImportPublicDependencies(dependency);
+ }
+ }
+ }
+
/// <summary>
/// Finds a symbol of the given name within the pool.
/// </summary>
@@ -83,9 +95,9 @@ namespace Google.ProtocolBuffers.Descriptors return descriptor;
}
- foreach (DescriptorPool dependency in dependencies)
+ foreach (FileDescriptor dependency in dependencies)
{
- dependency.descriptorsByName.TryGetValue(fullName, out result);
+ dependency.DescriptorPool.descriptorsByName.TryGetValue(fullName, out result);
descriptor = result as T;
if (descriptor != null)
{
diff --git a/csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs b/csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs index 6d17ae2a..98de5435 100644 --- a/csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs +++ b/csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs @@ -51,7 +51,6 @@ namespace Google.ProtocolBuffers.Descriptors private FieldType fieldType;
private MappedType mappedType;
- private CSharpFieldOptions csharpFieldOptions;
private readonly object optionsLock = new object();
internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file,
@@ -101,38 +100,6 @@ namespace Google.ProtocolBuffers.Descriptors file.DescriptorPool.AddSymbol(this);
}
- private CSharpFieldOptions BuildOrFakeCSharpOptions()
- {
- // TODO(jonskeet): Check if we could use FileDescriptorProto.Descriptor.Name - interesting bootstrap issues
- if (File.Proto.Name == "google/protobuf/csharp_options.proto")
- {
- if (Name == "csharp_field_options")
- {
- return new CSharpFieldOptions.Builder {PropertyName = "CSharpFieldOptions"}.Build();
- }
- if (Name == "csharp_file_options")
- {
- return new CSharpFieldOptions.Builder {PropertyName = "CSharpFileOptions"}.Build();
- }
- }
- CSharpFieldOptions.Builder builder = CSharpFieldOptions.CreateBuilder();
- if (Proto.Options.HasExtension(DescriptorProtos.CSharpOptions.CSharpFieldOptions))
- {
- builder.MergeFrom(Proto.Options.GetExtension(DescriptorProtos.CSharpOptions.CSharpFieldOptions));
- }
- if (!builder.HasPropertyName)
- {
- string fieldName = FieldType == FieldType.Group ? MessageType.Name : Name;
- string propertyName = NameHelpers.UnderscoresToPascalCase(fieldName);
- if (propertyName == ContainingType.Name)
- {
- propertyName += "_";
- }
- builder.PropertyName = propertyName;
- }
- return builder.Build();
- }
-
/// <summary>
/// Maps a field type as included in the .proto file to a FieldType.
/// </summary>
@@ -286,26 +253,7 @@ namespace Google.ProtocolBuffers.Descriptors {
get { return containingType; }
}
-
- /// <summary>
- /// Returns the C#-specific options for this field descriptor. This will always be
- /// completely filled in.
- /// </summary>
- public CSharpFieldOptions CSharpOptions
- {
- get
- {
- lock (optionsLock)
- {
- if (csharpFieldOptions == null)
- {
- csharpFieldOptions = BuildOrFakeCSharpOptions();
- }
- }
- return csharpFieldOptions;
- }
- }
-
+
/// <summary>
/// For extensions defined nested within message types, gets
/// the outer type. Not valid for non-extension fields.
diff --git a/csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs b/csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs index d7075e35..354e99a3 100644 --- a/csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs +++ b/csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs @@ -51,16 +51,17 @@ namespace Google.ProtocolBuffers.Descriptors private readonly IList<ServiceDescriptor> services;
private readonly IList<FieldDescriptor> extensions;
private readonly IList<FileDescriptor> dependencies;
+ private readonly IList<FileDescriptor> publicDependencies;
private readonly DescriptorPool pool;
- private CSharpFileOptions csharpFileOptions;
- private readonly object optionsLock = new object();
- private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool)
+ private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies)
{
this.pool = pool;
this.proto = proto;
this.dependencies = new ReadOnlyCollection<FileDescriptor>((FileDescriptor[]) dependencies.Clone());
+ publicDependencies = DeterminePublicDependencies(this, proto, dependencies, allowUnknownDependencies);
+
pool.AddPackage(Package, this);
messageTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.MessageTypeList,
@@ -80,94 +81,46 @@ namespace Google.ProtocolBuffers.Descriptors new FieldDescriptor(field, this, null, index, true));
}
-
/// <summary>
- /// Allows a file descriptor to be configured with a set of external options, e.g. from the
- /// command-line arguments to protogen.
+ /// Extracts public dependencies from direct dependencies. This is a static method despite its
+ /// first parameter, as the value we're in the middle of constructing is only used for exceptions.
/// </summary>
- public void ConfigureWithDefaultOptions(CSharpFileOptions options)
- {
- csharpFileOptions = BuildOrFakeWithDefaultOptions(options);
- }
-
- static readonly char[] PathSeperators = new char[] { '/', '\\' };
- private CSharpFileOptions BuildOrFakeWithDefaultOptions(CSharpFileOptions defaultOptions)
+ private static IList<FileDescriptor> DeterminePublicDependencies(FileDescriptor @this, FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies)
{
- // Fix for being able to relocate these files to any directory structure
- if (proto.Package == "google.protobuf")
- {
- int ixslash = proto.Name.LastIndexOfAny(PathSeperators);
- string filename = ixslash < 0 ? proto.Name : proto.Name.Substring(ixslash + 1);
- // TODO(jonskeet): Check if we could use FileDescriptorProto.Descriptor.Name - interesting bootstrap issues)
- if (filename == "descriptor.proto")
- {
- return new CSharpFileOptions.Builder
- {
- Namespace = "Google.ProtocolBuffers.DescriptorProtos",
- UmbrellaClassname = "DescriptorProtoFile",
- NestClasses = false,
- MultipleFiles = false,
- PublicClasses = true,
- OutputDirectory = defaultOptions.OutputDirectory,
- IgnoreGoogleProtobuf = defaultOptions.IgnoreGoogleProtobuf
- }.Build();
- }
- if (filename == "csharp_options.proto")
- {
- return new CSharpFileOptions.Builder
- {
- Namespace = "Google.ProtocolBuffers.DescriptorProtos",
- UmbrellaClassname = "CSharpOptions",
- NestClasses = false,
- MultipleFiles = false,
- PublicClasses = true,
- OutputDirectory = defaultOptions.OutputDirectory,
- IgnoreGoogleProtobuf = defaultOptions.IgnoreGoogleProtobuf
- }.Build();
- }
- }
- CSharpFileOptions.Builder builder = defaultOptions.ToBuilder();
- if (proto.Options.HasExtension(DescriptorProtos.CSharpOptions.CSharpFileOptions))
- {
- builder.MergeFrom(proto.Options.GetExtension(DescriptorProtos.CSharpOptions.CSharpFileOptions));
- }
- if (!builder.HasNamespace)
- {
- builder.Namespace = Package;
- }
- if (!builder.HasUmbrellaClassname)
+ var nameToFileMap = new Dictionary<string, FileDescriptor>();
+ foreach (var file in dependencies)
{
- int lastSlash = Name.LastIndexOf('/');
- string baseName = Name.Substring(lastSlash + 1);
- builder.UmbrellaClassname = NameHelpers.UnderscoresToPascalCase(NameHelpers.StripProto(baseName));
+ nameToFileMap[file.Name] = file;
}
-
- // Auto-fix for name collision by placing umbrella class into a new namespace. This
- // still won't fix the collisions with nesting enabled; however, you have to turn that on explicitly anyway.
- if (!builder.NestClasses && !builder.HasUmbrellaNamespace)
+ var publicDependencies = new List<FileDescriptor>();
+ for (int i = 0; i < proto.PublicDependencyCount; i++)
{
- bool collision = false;
- foreach (IDescriptor d in MessageTypes)
- {
- collision |= d.Name == builder.UmbrellaClassname;
- }
- foreach (IDescriptor d in Services)
+ int index = proto.PublicDependencyList[i];
+ if (index < 0 || index >= proto.DependencyCount)
{
- collision |= d.Name == builder.UmbrellaClassname;
+ throw new DescriptorValidationException(@this, "Invalid public dependency index.");
}
- foreach (IDescriptor d in EnumTypes)
+ string name = proto.DependencyList[index];
+ FileDescriptor file = nameToFileMap[name];
+ if (file == null)
{
- collision |= d.Name == builder.UmbrellaClassname;
+ if (!allowUnknownDependencies)
+ {
+ throw new DescriptorValidationException(@this, "Invalid public dependency: " + name);
+ }
+ // Ignore unknown dependencies.
}
- if (collision)
+ else
{
- builder.UmbrellaNamespace = "Proto";
+ publicDependencies.Add(file);
}
}
-
- return builder.Build();
+ return new ReadOnlyCollection<FileDescriptor>(publicDependencies);
}
+
+ static readonly char[] PathSeperators = new char[] { '/', '\\' };
+
/// <value>
/// The descriptor in its protocol message representation.
/// </value>
@@ -184,25 +137,6 @@ namespace Google.ProtocolBuffers.Descriptors get { return proto.Options; }
}
- /// <summary>
- /// Returns the C#-specific options for this file descriptor. This will always be
- /// completely filled in.
- /// </summary>
- public CSharpFileOptions CSharpOptions
- {
- get
- {
- lock (optionsLock)
- {
- if (csharpFileOptions == null)
- {
- csharpFileOptions = BuildOrFakeWithDefaultOptions(CSharpFileOptions.DefaultInstance);
- }
- }
- return csharpFileOptions;
- }
- }
-
/// <value>
/// The file name.
/// </value>
@@ -261,6 +195,14 @@ namespace Google.ProtocolBuffers.Descriptors }
/// <value>
+ /// Unmodifiable list of this file's public dependencies (public imports).
+ /// </value>
+ public IList<FileDescriptor> PublicDependencies
+ {
+ get { return publicDependencies; }
+ }
+
+ /// <value>
/// Implementation of IDescriptor.FullName - just returns the same as Name.
/// </value>
string IDescriptor.FullName
@@ -331,6 +273,22 @@ namespace Google.ProtocolBuffers.Descriptors /// having an undefined type or because two messages were defined with the same name.</exception>
public static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies)
{
+ return BuildFrom(proto, dependencies, false);
+ }
+
+ /// <summary>
+ /// Builds a FileDescriptor from its protocol buffer representation.
+ /// </summary>
+ /// <param name="proto">The protocol message form of the FileDescriptor.</param>
+ /// <param name="dependencies">FileDescriptors corresponding to all of the
+ /// 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>
+ /// <param name="allowUnknownDependencies">Whether unknown dependencies are ignored (true) or cause an exception to be thrown (false).</param>
+ /// <exception cref="DescriptorValidationException">If <paramref name="proto"/> 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.</exception>
+ private static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies)
+ {
// Building descriptors involves two steps: translating and linking.
// In the translation step (implemented by FileDescriptor's
// constructor), we build an object tree mirroring the
@@ -346,23 +304,25 @@ namespace Google.ProtocolBuffers.Descriptors }
DescriptorPool pool = new DescriptorPool(dependencies);
- FileDescriptor result = new FileDescriptor(proto, dependencies, pool);
-
- if (dependencies.Length != proto.DependencyCount)
- {
- throw new DescriptorValidationException(result,
- "Dependencies passed to FileDescriptor.BuildFrom() don't match " +
- "those listed in the FileDescriptorProto.");
- }
- for (int i = 0; i < proto.DependencyCount; i++)
- {
- if (dependencies[i].Name != proto.DependencyList[i])
- {
- throw new DescriptorValidationException(result,
- "Dependencies passed to FileDescriptor.BuildFrom() don't match " +
- "those listed in the FileDescriptorProto.");
- }
- }
+ FileDescriptor result = new FileDescriptor(proto, dependencies, pool, allowUnknownDependencies);
+
+ // 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".)
+ //if (dependencies.Length != proto.DependencyCount)
+ //{
+ // throw new DescriptorValidationException(result,
+ // "Dependencies passed to FileDescriptor.BuildFrom() don't match " +
+ // "those listed in the FileDescriptorProto.");
+ //}
+ //for (int i = 0; i < proto.DependencyCount; i++)
+ //{
+ // if (dependencies[i].Name != proto.DependencyList[i])
+ // {
+ // throw new DescriptorValidationException(result,
+ // "Dependencies passed to FileDescriptor.BuildFrom() don't match " +
+ // "those listed in the FileDescriptorProto.");
+ // }
+ //}
result.CrossLink();
return result;
@@ -434,7 +394,9 @@ namespace Google.ProtocolBuffers.Descriptors FileDescriptor result;
try
{
- result = BuildFrom(proto, dependencies);
+ // When building descriptors for generated code, we allow unknown
+ // dependencies by default.
+ result = BuildFrom(proto, dependencies, true);
}
catch (DescriptorValidationException e)
{
@@ -494,5 +456,10 @@ namespace Google.ProtocolBuffers.Descriptors extensions[i].ReplaceProto(proto.GetExtension(i));
}
}
+
+ public override string ToString()
+ {
+ return "FileDescriptor for " + proto.Name;
+ }
}
}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/Descriptors/MessageDescriptor.cs b/csharp/src/ProtocolBuffers/Descriptors/MessageDescriptor.cs index d438c0ff..5b29849c 100644 --- a/csharp/src/ProtocolBuffers/Descriptors/MessageDescriptor.cs +++ b/csharp/src/ProtocolBuffers/Descriptors/MessageDescriptor.cs @@ -159,25 +159,6 @@ namespace Google.ProtocolBuffers.Descriptors }
/// <summary>
- /// Finds a field by its property name, as it would be generated by protogen.
- /// </summary>
- /// <param name="propertyName">The property name within this message type.</param>
- /// <returns>The field's descriptor, or null if not found.</returns>
- public FieldDescriptor FindFieldByPropertyName(string propertyName)
- {
- // For reasonably short messages, this will be more efficient than a dictionary
- // lookup. It also means we don't need to do things lazily with locks etc.
- foreach (FieldDescriptor field in Fields)
- {
- if (field.CSharpOptions.PropertyName == propertyName)
- {
- return field;
- }
- }
- return null;
- }
-
- /// <summary>
/// Finds a nested descriptor by name. The is valid for fields, nested
/// message types and enums.
/// </summary>
|