| Index: third_party/protobuf/src/google/protobuf/descriptor.cc
|
| diff --git a/third_party/protobuf/src/google/protobuf/descriptor.cc b/third_party/protobuf/src/google/protobuf/descriptor.cc
|
| index 56e11fa967ad04018a48cddf28c4ccede664fea0..be1e1d69cc9ec2f122310075592625c46bbac5c7 100644
|
| --- a/third_party/protobuf/src/google/protobuf/descriptor.cc
|
| +++ b/third_party/protobuf/src/google/protobuf/descriptor.cc
|
| @@ -69,6 +69,7 @@
|
| #undef PACKAGE // autoheader #defines this. :(
|
|
|
| namespace google {
|
| +
|
| namespace protobuf {
|
|
|
| const FieldDescriptor::CppType
|
| @@ -164,6 +165,15 @@ const int FieldDescriptor::kLastReservedNumber;
|
|
|
| namespace {
|
|
|
| +// Note: I distrust ctype.h due to locales.
|
| +char ToUpper(char ch) {
|
| + return (ch >= 'a' && ch <= 'z') ? (ch - 'a' + 'A') : ch;
|
| +}
|
| +
|
| +char ToLower(char ch) {
|
| + return (ch >= 'A' && ch <= 'Z') ? (ch - 'A' + 'a') : ch;
|
| +}
|
| +
|
| string ToCamelCase(const string& input, bool lower_first) {
|
| bool capitalize_next = !lower_first;
|
| string result;
|
| @@ -173,12 +183,7 @@ string ToCamelCase(const string& input, bool lower_first) {
|
| if (input[i] == '_') {
|
| capitalize_next = true;
|
| } else if (capitalize_next) {
|
| - // Note: I distrust ctype.h due to locales.
|
| - if ('a' <= input[i] && input[i] <= 'z') {
|
| - result.push_back(input[i] - 'a' + 'A');
|
| - } else {
|
| - result.push_back(input[i]);
|
| - }
|
| + result.push_back(ToUpper(input[i]));
|
| capitalize_next = false;
|
| } else {
|
| result.push_back(input[i]);
|
| @@ -186,13 +191,116 @@ string ToCamelCase(const string& input, bool lower_first) {
|
| }
|
|
|
| // Lower-case the first letter.
|
| - if (lower_first && !result.empty() && 'A' <= result[0] && result[0] <= 'Z') {
|
| - result[0] = result[0] - 'A' + 'a';
|
| + if (lower_first && !result.empty()) {
|
| + result[0] = ToLower(result[0]);
|
| + }
|
| +
|
| + return result;
|
| +}
|
| +
|
| +string ToJsonName(const string& input) {
|
| + bool capitalize_next = false;
|
| + string result;
|
| + result.reserve(input.size());
|
| +
|
| + for (int i = 0; i < input.size(); i++) {
|
| + if (input[i] == '_') {
|
| + capitalize_next = true;
|
| + } else if (capitalize_next) {
|
| + result.push_back(ToUpper(input[i]));
|
| + capitalize_next = false;
|
| + } else {
|
| + result.push_back(input[i]);
|
| + }
|
| }
|
|
|
| return result;
|
| }
|
|
|
| +string EnumValueToPascalCase(const string& input) {
|
| + bool next_upper = true;
|
| + string result;
|
| + result.reserve(input.size());
|
| +
|
| + for (int i = 0; i < input.size(); i++) {
|
| + if (input[i] == '_') {
|
| + next_upper = true;
|
| + } else {
|
| + if (next_upper) {
|
| + result.push_back(ToUpper(input[i]));
|
| + } else {
|
| + result.push_back(ToLower(input[i]));
|
| + }
|
| + next_upper = false;
|
| + }
|
| + }
|
| +
|
| + return result;
|
| +}
|
| +
|
| +// Class to remove an enum prefix from enum values.
|
| +class PrefixRemover {
|
| + public:
|
| + PrefixRemover(StringPiece prefix) {
|
| + // Strip underscores and lower-case the prefix.
|
| + for (int i = 0; i < prefix.size(); i++) {
|
| + if (prefix[i] != '_') {
|
| + prefix_ += ascii_tolower(prefix[i]);
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Tries to remove the enum prefix from this enum value.
|
| + // If this is not possible, returns the input verbatim.
|
| + string MaybeRemove(StringPiece str) {
|
| + // We can't just lowercase and strip str and look for a prefix.
|
| + // We need to properly recognize the difference between:
|
| + //
|
| + // enum Foo {
|
| + // FOO_BAR_BAZ = 0;
|
| + // FOO_BARBAZ = 1;
|
| + // }
|
| + //
|
| + // This is acceptable (though perhaps not advisable) because even when
|
| + // we PascalCase, these two will still be distinct (BarBaz vs. Barbaz).
|
| + size_t i, j;
|
| +
|
| + // Skip past prefix_ in str if we can.
|
| + for (i = 0, j = 0; i < str.size() && j < prefix_.size(); i++) {
|
| + if (str[i] == '_') {
|
| + continue;
|
| + }
|
| +
|
| + if (ascii_tolower(str[i]) != prefix_[j++]) {
|
| + return str.as_string();
|
| + }
|
| + }
|
| +
|
| + // If we didn't make it through the prefix, we've failed to strip the
|
| + // prefix.
|
| + if (j < prefix_.size()) {
|
| + return str.as_string();
|
| + }
|
| +
|
| + // Skip underscores between prefix and further characters.
|
| + while (i < str.size() && str[i] == '_') {
|
| + i++;
|
| + }
|
| +
|
| + // Enum label can't be the empty string.
|
| + if (i == str.size()) {
|
| + return str.as_string();
|
| + }
|
| +
|
| + // We successfully stripped the prefix.
|
| + str.remove_prefix(i);
|
| + return str.as_string();
|
| + }
|
| +
|
| + private:
|
| + string prefix_;
|
| +};
|
| +
|
| // A DescriptorPool contains a bunch of hash_maps to implement the
|
| // various Find*By*() methods. Since hashtable lookups are O(1), it's
|
| // most efficient to construct a fixed set of large hash_maps used by
|
| @@ -1896,6 +2004,9 @@ void FieldDescriptor::CopyJsonNameTo(FieldDescriptorProto* proto) const {
|
|
|
| void OneofDescriptor::CopyTo(OneofDescriptorProto* proto) const {
|
| proto->set_name(name());
|
| + if (&options() != &OneofOptions::default_instance()) {
|
| + proto->mutable_options()->CopyFrom(options());
|
| + }
|
| }
|
|
|
| void EnumDescriptor::CopyTo(EnumDescriptorProto* proto) const {
|
| @@ -2387,6 +2498,17 @@ void FieldDescriptor::DebugString(int depth,
|
| strings::SubstituteAndAppend(contents, " [default = $0",
|
| DefaultValueAsString(true));
|
| }
|
| + if (has_json_name_) {
|
| + if (!bracketed) {
|
| + bracketed = true;
|
| + contents->append("[");
|
| + } else {
|
| + contents->append(", ");
|
| + }
|
| + contents->append("json_name = \"");
|
| + contents->append(CEscape(json_name()));
|
| + contents->append("\"");
|
| + }
|
|
|
| string formatted_options;
|
| if (FormatBracketedOptions(depth, options(), &formatted_options)) {
|
| @@ -2435,6 +2557,9 @@ void OneofDescriptor::DebugString(int depth, string* contents,
|
| comment_printer.AddPreComment(contents);
|
| strings::SubstituteAndAppend(
|
| contents, "$0 oneof $1 {", prefix, name());
|
| +
|
| + FormatLineOptions(depth, options(), contents);
|
| +
|
| if (debug_string_options.elide_oneof_body) {
|
| contents->append(" ... }\n");
|
| } else {
|
| @@ -2974,6 +3099,8 @@ class DescriptorBuilder {
|
| void BuildOneof(const OneofDescriptorProto& proto,
|
| Descriptor* parent,
|
| OneofDescriptor* result);
|
| + void CheckEnumValueUniqueness(const EnumDescriptorProto& proto,
|
| + const EnumDescriptor* result);
|
| void BuildEnum(const EnumDescriptorProto& proto,
|
| const Descriptor* parent,
|
| EnumDescriptor* result);
|
| @@ -4226,7 +4353,7 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto,
|
| result->json_name_ = tables_->AllocateString(proto.json_name());
|
| } else {
|
| result->has_json_name_ = false;
|
| - result->json_name_ = result->camelcase_name_;
|
| + result->json_name_ = tables_->AllocateString(ToJsonName(proto.name()));
|
| }
|
|
|
| // Some compilers do not allow static_cast directly between two enum types,
|
| @@ -4523,10 +4650,81 @@ void DescriptorBuilder::BuildOneof(const OneofDescriptorProto& proto,
|
| result->field_count_ = 0;
|
| result->fields_ = NULL;
|
|
|
| + // Copy options.
|
| + if (!proto.has_options()) {
|
| + result->options_ = NULL; // Will set to default_instance later.
|
| + } else {
|
| + AllocateOptions(proto.options(), result);
|
| + }
|
| +
|
| AddSymbol(result->full_name(), parent, result->name(),
|
| proto, Symbol(result));
|
| }
|
|
|
| +void DescriptorBuilder::CheckEnumValueUniqueness(
|
| + const EnumDescriptorProto& proto, const EnumDescriptor* result) {
|
| +
|
| + // Check that enum labels are still unique when we remove the enum prefix from
|
| + // values that have it.
|
| + //
|
| + // This will fail for something like:
|
| + //
|
| + // enum MyEnum {
|
| + // MY_ENUM_FOO = 0;
|
| + // FOO = 1;
|
| + // }
|
| + //
|
| + // By enforcing this reasonable constraint, we allow code generators to strip
|
| + // the prefix and/or PascalCase it without creating conflicts. This can lead
|
| + // to much nicer language-specific enums like:
|
| + //
|
| + // enum NameType {
|
| + // FirstName = 1,
|
| + // LastName = 2,
|
| + // }
|
| + //
|
| + // Instead of:
|
| + //
|
| + // enum NameType {
|
| + // NAME_TYPE_FIRST_NAME = 1,
|
| + // NAME_TYPE_LAST_NAME = 2,
|
| + // }
|
| + PrefixRemover remover(result->name());
|
| + map<string, const google::protobuf::EnumValueDescriptor*> values;
|
| + for (int i = 0; i < result->value_count(); i++) {
|
| + const google::protobuf::EnumValueDescriptor* value = result->value(i);
|
| + string stripped =
|
| + EnumValueToPascalCase(remover.MaybeRemove(value->name()));
|
| + std::pair<map<string, const google::protobuf::EnumValueDescriptor*>::iterator, bool>
|
| + insert_result = values.insert(std::make_pair(stripped, value));
|
| + bool inserted = insert_result.second;
|
| +
|
| + // We don't throw the error if the two conflicting symbols are identical, or
|
| + // if they map to the same number. In the former case, the normal symbol
|
| + // duplication error will fire so we don't need to (and its error message
|
| + // will make more sense). We allow the latter case so users can create
|
| + // aliases which add or remove the prefix (code generators that do prefix
|
| + // stripping should de-dup the labels in this case).
|
| + if (!inserted && insert_result.first->second->name() != value->name() &&
|
| + insert_result.first->second->number() != value->number()) {
|
| + string error_message =
|
| + "When enum name is stripped and label is PascalCased (" + stripped +
|
| + "), this value label conflicts with " + values[stripped]->name() +
|
| + ". This will make the proto fail to compile for some languages, such "
|
| + "as C#.";
|
| + // There are proto2 enums out there with conflicting names, so to preserve
|
| + // compatibility we issue only a warning for proto2.
|
| + if (result->file()->syntax() == FileDescriptor::SYNTAX_PROTO2) {
|
| + AddWarning(value->full_name(), proto.value(i),
|
| + DescriptorPool::ErrorCollector::NAME, error_message);
|
| + } else {
|
| + AddError(value->full_name(), proto.value(i),
|
| + DescriptorPool::ErrorCollector::NAME, error_message);
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| void DescriptorBuilder::BuildEnum(const EnumDescriptorProto& proto,
|
| const Descriptor* parent,
|
| EnumDescriptor* result) {
|
| @@ -4555,6 +4753,8 @@ void DescriptorBuilder::BuildEnum(const EnumDescriptorProto& proto,
|
|
|
| BUILD_ARRAY(proto, result, value, BuildEnumValue, result);
|
|
|
| + CheckEnumValueUniqueness(proto, result);
|
| +
|
| // Copy options.
|
| if (!proto.has_options()) {
|
| result->options_ = NULL; // Will set to default_instance later.
|
| @@ -4783,6 +4983,10 @@ void DescriptorBuilder::CrossLinkMessage(
|
| oneof_decl->fields_ =
|
| tables_->AllocateArray<const FieldDescriptor*>(oneof_decl->field_count_);
|
| oneof_decl->field_count_ = 0;
|
| +
|
| + if (oneof_decl->options_ == NULL) {
|
| + oneof_decl->options_ = &OneofOptions::default_instance();
|
| + }
|
| }
|
|
|
| // Then fill them in.
|
| @@ -5181,7 +5385,7 @@ void DescriptorBuilder::ValidateProto3Message(
|
| if (name_to_field.find(lowercase_name) != name_to_field.end()) {
|
| AddError(message->full_name(), proto,
|
| DescriptorPool::ErrorCollector::OTHER,
|
| - "The JSON camcel-case name of field \"" +
|
| + "The JSON camel-case name of field \"" +
|
| message->field(i)->name() + "\" conflicts with field \"" +
|
| name_to_field[lowercase_name]->name() + "\". This is not " +
|
| "allowed in proto3.");
|
|
|