aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Fallin <cfallin@google.com>2015-02-12 16:08:01 -0800
committerChris Fallin <cfallin@google.com>2015-02-12 16:08:01 -0800
commita2bea0a0012b4adbc50c47246d968a85cb88cec2 (patch)
tree6ed5b7445bc098091fadf094b2ccbeb124d3569e
parent43beb6268bd3989154991bccb831e2bc951402d8 (diff)
downloadprotobuf-a2bea0a0012b4adbc50c47246d968a85cb88cec2.tar.gz
protobuf-a2bea0a0012b4adbc50c47246d968a85cb88cec2.tar.bz2
protobuf-a2bea0a0012b4adbc50c47246d968a85cb88cec2.zip
Properly support maps in Ruby protoc and another bugfix.
Previously, we supported map fields in the Ruby DSL. However, we never connected the final link in the chain and generated `map` DSL commands for map fields in `.proto` files. My apologies -- I had been testing with the DSL directly so I missed this. Also fixed a handlerdata-setup-infinite-loop when a map value field's type is its containing message.
-rw-r--r--ruby/ext/google/protobuf_c/encode_decode.c23
-rw-r--r--ruby/tests/generated_code.rb70
-rw-r--r--src/google/protobuf/compiler/ruby/ruby_generator.cc75
3 files changed, 79 insertions, 89 deletions
diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c
index 0630f567..256fc829 100644
--- a/ruby/ext/google/protobuf_c/encode_decode.c
+++ b/ruby/ext/google/protobuf_c/encode_decode.c
@@ -208,7 +208,7 @@ typedef struct {
size_t ofs;
upb_fieldtype_t key_field_type;
upb_fieldtype_t value_field_type;
- VALUE value_field_typeclass;
+ const upb_def* value_field_subdef;
} map_handlerdata_t;
// Temporary frame for map parsing: at the beginning of a map entry message, a
@@ -248,8 +248,15 @@ static bool endmap_handler(void *closure, const void *hd, upb_status* s) {
VALUE key = native_slot_get(
mapdata->key_field_type, Qnil,
&frame->key_storage);
+
+ VALUE value_field_typeclass = Qnil;
+ if (mapdata->value_field_type == UPB_TYPE_MESSAGE ||
+ mapdata->value_field_type == UPB_TYPE_ENUM) {
+ value_field_typeclass = get_def_obj(mapdata->value_field_subdef);
+ }
+
VALUE value = native_slot_get(
- mapdata->value_field_type, mapdata->value_field_typeclass,
+ mapdata->value_field_type, value_field_typeclass,
&frame->value_storage);
Map_index_set(frame->map, key, value);
@@ -280,17 +287,7 @@ static map_handlerdata_t* new_map_handlerdata(
MAP_VALUE_FIELD);
assert(value_field != NULL);
hd->value_field_type = upb_fielddef_type(value_field);
- hd->value_field_typeclass = field_type_class(value_field);
-
- // Ensure that value_field_typeclass is properly GC-rooted. We must do this
- // because we hold a reference to the Ruby class in the handlerdata, which is
- // owned by the handlers. The handlers are owned by *this* message's Ruby
- // object, but each Ruby object is rooted independently at the def -> Ruby
- // object map. So we have to ensure that the Ruby objects we depend on will
- // stick around as long as we're around.
- if (hd->value_field_typeclass != Qnil) {
- rb_ary_push(desc->typeclass_references, hd->value_field_typeclass);
- }
+ hd->value_field_subdef = upb_fielddef_subdef(value_field);
return hd;
}
diff --git a/ruby/tests/generated_code.rb b/ruby/tests/generated_code.rb
index db762ad9..5a685433 100644
--- a/ruby/tests/generated_code.rb
+++ b/ruby/tests/generated_code.rb
@@ -27,16 +27,16 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
repeated :repeated_bytes, :string, 29
repeated :repeated_enum, :enum, 30, "A.B.C.TestEnum"
repeated :repeated_msg, :message, 31, "A.B.C.TestMessage"
- repeated :map_int32_string, :message, 61, "A.B.C.TestMessage.MapInt32StringEntry"
- repeated :map_int64_string, :message, 62, "A.B.C.TestMessage.MapInt64StringEntry"
- repeated :map_uint32_string, :message, 63, "A.B.C.TestMessage.MapUint32StringEntry"
- repeated :map_uint64_string, :message, 64, "A.B.C.TestMessage.MapUint64StringEntry"
- repeated :map_bool_string, :message, 65, "A.B.C.TestMessage.MapBoolStringEntry"
- repeated :map_string_string, :message, 66, "A.B.C.TestMessage.MapStringStringEntry"
- repeated :map_string_msg, :message, 67, "A.B.C.TestMessage.MapStringMsgEntry"
- repeated :map_string_enum, :message, 68, "A.B.C.TestMessage.MapStringEnumEntry"
- repeated :map_string_int32, :message, 69, "A.B.C.TestMessage.MapStringInt32Entry"
- repeated :map_string_bool, :message, 70, "A.B.C.TestMessage.MapStringBoolEntry"
+ map :map_int32_string, :int32, :string, 61
+ map :map_int64_string, :int64, :string, 62
+ map :map_uint32_string, :uint32, :string, 63
+ map :map_uint64_string, :uint64, :string, 64
+ map :map_bool_string, :bool, :string, 65
+ map :map_string_string, :string, :string, 66
+ map :map_string_msg, :string, :message, 67, "A.B.C.TestMessage"
+ map :map_string_enum, :string, :enum, 68, "A.B.C.TestEnum"
+ map :map_string_int32, :string, :int32, 69
+ map :map_string_bool, :string, :bool, 70
optional :nested_message, :message, 80, "A.B.C.TestMessage.NestedMessage"
oneof :my_oneof do
optional :oneof_int32, :int32, 41
@@ -52,46 +52,6 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
optional :oneof_msg, :message, 51, "A.B.C.TestMessage"
end
end
- add_message "A.B.C.TestMessage.MapInt32StringEntry" do
- optional :key, :int32, 1
- optional :value, :string, 2
- end
- add_message "A.B.C.TestMessage.MapInt64StringEntry" do
- optional :key, :int64, 1
- optional :value, :string, 2
- end
- add_message "A.B.C.TestMessage.MapUint32StringEntry" do
- optional :key, :uint32, 1
- optional :value, :string, 2
- end
- add_message "A.B.C.TestMessage.MapUint64StringEntry" do
- optional :key, :uint64, 1
- optional :value, :string, 2
- end
- add_message "A.B.C.TestMessage.MapBoolStringEntry" do
- optional :key, :bool, 1
- optional :value, :string, 2
- end
- add_message "A.B.C.TestMessage.MapStringStringEntry" do
- optional :key, :string, 1
- optional :value, :string, 2
- end
- add_message "A.B.C.TestMessage.MapStringMsgEntry" do
- optional :key, :string, 1
- optional :value, :message, 2, "A.B.C.TestMessage"
- end
- add_message "A.B.C.TestMessage.MapStringEnumEntry" do
- optional :key, :string, 1
- optional :value, :enum, 2, "A.B.C.TestEnum"
- end
- add_message "A.B.C.TestMessage.MapStringInt32Entry" do
- optional :key, :string, 1
- optional :value, :int32, 2
- end
- add_message "A.B.C.TestMessage.MapStringBoolEntry" do
- optional :key, :string, 1
- optional :value, :bool, 2
- end
add_message "A.B.C.TestMessage.NestedMessage" do
optional :foo, :int32, 1
end
@@ -107,16 +67,6 @@ module A
module B
module C
TestMessage = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage").msgclass
- TestMessage::MapInt32StringEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapInt32StringEntry").msgclass
- TestMessage::MapInt64StringEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapInt64StringEntry").msgclass
- TestMessage::MapUint32StringEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapUint32StringEntry").msgclass
- TestMessage::MapUint64StringEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapUint64StringEntry").msgclass
- TestMessage::MapBoolStringEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapBoolStringEntry").msgclass
- TestMessage::MapStringStringEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapStringStringEntry").msgclass
- TestMessage::MapStringMsgEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapStringMsgEntry").msgclass
- TestMessage::MapStringEnumEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapStringEnumEntry").msgclass
- TestMessage::MapStringInt32Entry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapStringInt32Entry").msgclass
- TestMessage::MapStringBoolEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapStringBoolEntry").msgclass
TestMessage::NestedMessage = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.NestedMessage").msgclass
TestEnum = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestEnum").enummodule
end
diff --git a/src/google/protobuf/compiler/ruby/ruby_generator.cc b/src/google/protobuf/compiler/ruby/ruby_generator.cc
index be59fafd..88292891 100644
--- a/src/google/protobuf/compiler/ruby/ruby_generator.cc
+++ b/src/google/protobuf/compiler/ruby/ruby_generator.cc
@@ -102,24 +102,53 @@ std::string TypeName(const google::protobuf::FieldDescriptor* field) {
void GenerateField(const google::protobuf::FieldDescriptor* field,
google::protobuf::io::Printer* printer) {
- printer->Print(
- "$label$ :$name$, ",
- "label", LabelForField(field),
- "name", field->name());
- printer->Print(
- ":$type$, $number$",
- "type", TypeName(field),
- "number", IntToString(field->number()));
- if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
- printer->Print(
- ", \"$subtype$\"\n",
- "subtype", field->message_type()->full_name());
- } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
+
+ if (field->is_map()) {
+ const FieldDescriptor* key_field =
+ field->message_type()->FindFieldByNumber(1);
+ const FieldDescriptor* value_field =
+ field->message_type()->FindFieldByNumber(2);
+
printer->Print(
- ", \"$subtype$\"\n",
- "subtype", field->enum_type()->full_name());
+ "map :$name$, :$key_type$, :$value_type$, $number$",
+ "name", field->name(),
+ "key_type", TypeName(key_field),
+ "value_type", TypeName(value_field),
+ "number", IntToString(field->number()));
+
+ if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ printer->Print(
+ ", \"$subtype$\"\n",
+ "subtype", value_field->message_type()->full_name());
+ } else if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
+ printer->Print(
+ ", \"$subtype$\"\n",
+ "subtype", value_field->enum_type()->full_name());
+ } else {
+ printer->Print("\n");
+ }
} else {
- printer->Print("\n");
+
+ printer->Print(
+ "$label$ :$name$, ",
+ "label", LabelForField(field),
+ "name", field->name());
+ printer->Print(
+ ":$type$, $number$",
+ "type", TypeName(field),
+ "number", IntToString(field->number()));
+
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ printer->Print(
+ ", \"$subtype$\"\n",
+ "subtype", field->message_type()->full_name());
+ } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
+ printer->Print(
+ ", \"$subtype$\"\n",
+ "subtype", field->enum_type()->full_name());
+ } else {
+ printer->Print("\n");
+ }
}
}
@@ -141,6 +170,13 @@ void GenerateOneof(const google::protobuf::OneofDescriptor* oneof,
void GenerateMessage(const google::protobuf::Descriptor* message,
google::protobuf::io::Printer* printer) {
+
+ // Don't generate MapEntry messages -- we use the Ruby extension's native
+ // support for map fields instead.
+ if (message->options().map_entry()) {
+ return;
+ }
+
printer->Print(
"add_message \"$name$\" do\n",
"name", message->full_name());
@@ -213,6 +249,13 @@ void GenerateMessageAssignment(
const std::string& prefix,
const google::protobuf::Descriptor* message,
google::protobuf::io::Printer* printer) {
+
+ // Don't generate MapEntry messages -- we use the Ruby extension's native
+ // support for map fields instead.
+ if (message->options().map_entry()) {
+ return;
+ }
+
printer->Print(
"$prefix$$name$ = ",
"prefix", prefix,