Pārlūkot izejas kodu

Tests pass for all common operations.

A few things that don't work or aren't tested yet:
- wrappers at the top level
- equality checking for not-yet-expanded wrappers.
Joshua Haberman 6 gadi atpakaļ
vecāks
revīzija
9cfb12bf0a

+ 8 - 3
ruby/ext/google/protobuf_c/encode_decode.c

@@ -1440,8 +1440,14 @@ static void putmsg(VALUE msg_rb, const Descriptor* desc,
         putstr(str, f, sink);
         putstr(str, f, sink);
       }
       }
     } else if (upb_fielddef_issubmsg(f)) {
     } else if (upb_fielddef_issubmsg(f)) {
-      putsubmsg(DEREF(msg, offset, VALUE), f, sink, depth,
-                emit_defaults, is_json);
+      VALUE val = DEREF(msg, offset, VALUE);
+      int type = TYPE(val);
+      if (type != T_DATA && type != T_NIL && is_wrapper_type_field(f)) {
+        // OPT: could try to avoid expanding the wrapper here.
+        val = ruby_wrapper_type(desc->layout, f, val);
+        DEREF(msg, offset, VALUE) = val;
+      }
+      putsubmsg(val, f, sink, depth, emit_defaults, is_json);
     } else {
     } else {
       upb_selector_t sel = getsel(f, upb_handlers_getprimitivehandlertype(f));
       upb_selector_t sel = getsel(f, upb_handlers_getprimitivehandlertype(f));
 
 
@@ -1475,7 +1481,6 @@ static void putmsg(VALUE msg_rb, const Descriptor* desc,
       }
       }
 
 
 #undef T
 #undef T
-
     }
     }
   }
   }
 
 

+ 8 - 5
ruby/ext/google/protobuf_c/message.c

@@ -108,8 +108,12 @@ enum {
 };
 };
 
 
 // Check if the field is a well known wrapper type
 // Check if the field is a well known wrapper type
-static bool is_wrapper_type_field(const upb_fielddef* field) {
-  const upb_msgdef *m = upb_fielddef_msgsubdef(field);
+bool is_wrapper_type_field(const upb_fielddef* field) {
+  const upb_msgdef *m;
+  if (upb_fielddef_type(field) != UPB_TYPE_MESSAGE) {
+    return false;
+  }
+  m = upb_fielddef_msgsubdef(field);
   switch (upb_msgdef_wellknowntype(m)) {
   switch (upb_msgdef_wellknowntype(m)) {
     case UPB_WELLKNOWN_DOUBLEVALUE:
     case UPB_WELLKNOWN_DOUBLEVALUE:
     case UPB_WELLKNOWN_FLOATVALUE:
     case UPB_WELLKNOWN_FLOATVALUE:
@@ -127,8 +131,8 @@ static bool is_wrapper_type_field(const upb_fielddef* field) {
 }
 }
 
 
 // Get a new Ruby wrapper type and set the initial value
 // Get a new Ruby wrapper type and set the initial value
-static VALUE ruby_wrapper_type(const MessageLayout* layout,
-                               const upb_fielddef* field, const VALUE value) {
+VALUE ruby_wrapper_type(const MessageLayout* layout, const upb_fielddef* field,
+                        const VALUE value) {
   if (is_wrapper_type_field(field) && value != Qnil) {
   if (is_wrapper_type_field(field) && value != Qnil) {
     VALUE hash = rb_hash_new();
     VALUE hash = rb_hash_new();
     rb_hash_aset(hash, rb_str_new2("value"), value);
     rb_hash_aset(hash, rb_str_new2("value"), value);
@@ -195,7 +199,6 @@ static int extract_method_call(VALUE method_name, MessageHeader* self,
     // Check if field exists and is a wrapper type
     // Check if field exists and is a wrapper type
     if (upb_msgdef_lookupname(self->descriptor->msgdef, wrapper_field_name,
     if (upb_msgdef_lookupname(self->descriptor->msgdef, wrapper_field_name,
                               name_len - 9, &test_f_wrapper, &test_o_wrapper) &&
                               name_len - 9, &test_f_wrapper, &test_o_wrapper) &&
-        upb_fielddef_type(test_f_wrapper) == UPB_TYPE_MESSAGE &&
         is_wrapper_type_field(test_f_wrapper)) {
         is_wrapper_type_field(test_f_wrapper)) {
       // It does exist!
       // It does exist!
       has_field = true;
       has_field = true;

+ 4 - 0
ruby/ext/google/protobuf_c/protobuf.h

@@ -556,6 +556,10 @@ VALUE layout_eq(MessageLayout* layout, void* msg1, void* msg2);
 VALUE layout_hash(MessageLayout* layout, void* storage);
 VALUE layout_hash(MessageLayout* layout, void* storage);
 VALUE layout_inspect(MessageLayout* layout, void* storage);
 VALUE layout_inspect(MessageLayout* layout, void* storage);
 
 
+bool is_wrapper_type_field(const upb_fielddef* field);
+VALUE ruby_wrapper_type(const MessageLayout* layout, const upb_fielddef* field,
+                        const VALUE value);
+
 // -----------------------------------------------------------------------------
 // -----------------------------------------------------------------------------
 // Message class creation.
 // Message class creation.
 // -----------------------------------------------------------------------------
 // -----------------------------------------------------------------------------

+ 9 - 2
ruby/ext/google/protobuf_c/storage.c

@@ -843,8 +843,15 @@ VALUE layout_get(MessageLayout* layout,
   } else if (!field_set) {
   } else if (!field_set) {
     return layout_get_default(field);
     return layout_get_default(field);
   } else {
   } else {
-    return native_slot_get(upb_fielddef_type(field),
-                           field_type_class(layout, field), memory);
+    VALUE type_class = field_type_class(layout, field);
+    VALUE val = native_slot_get(upb_fielddef_type(field), type_class, memory);
+    int type = TYPE(val);
+    if (type != T_DATA && type != T_NIL && is_wrapper_type_field(field)) {
+      val = ruby_wrapper_type(layout, field, val);
+      native_slot_set(upb_fielddef_name(field), upb_fielddef_type(field),
+                      type_class, memory, val);
+    }
+    return val;
   }
   }
 }
 }
 
 

+ 166 - 110
ruby/tests/common_tests.rb

@@ -1321,126 +1321,182 @@ module CommonTests
     serialized = proto_module::Wrapper::encode(m)
     serialized = proto_module::Wrapper::encode(m)
     m2 = proto_module::Wrapper::decode(serialized)
     m2 = proto_module::Wrapper::decode(serialized)
     run_asserts.call(m2)
     run_asserts.call(m2)
+
+    # Test the case where we are serializing directly from the parsed form
+    # (before anything lazy is materialized).
+    m3 = proto_module::Wrapper::decode(serialized)
+    serialized2 = proto_module::Wrapper::encode(m3)
+    m4 = proto_module::Wrapper::decode(serialized2)
+    run_asserts.call(m4)
+
+    # Test that the lazy form compares equal to the expanded form.
+    m5 = proto_module::Wrapper::decode(serialized2)
+
+    # This doesn't work yet.
+    # assert_equal m5, m
   end
   end
 
 
   def test_wrapper_setters_as_value
   def test_wrapper_setters_as_value
+    run_asserts = ->(m) {
+      m.double_as_value = 4.8
+      assert_equal 4.8, m.double_as_value
+      assert_equal Google::Protobuf::DoubleValue.new(value: 4.8), m.double
+      m.float_as_value = 2.4
+      assert_in_delta 2.4, m.float_as_value
+      assert_in_delta Google::Protobuf::FloatValue.new(value: 2.4).value, m.float.value
+      m.int32_as_value = 5
+      assert_equal 5, m.int32_as_value
+      assert_equal Google::Protobuf::Int32Value.new(value: 5), m.int32
+      m.int64_as_value = 15
+      assert_equal 15, m.int64_as_value
+      assert_equal Google::Protobuf::Int64Value.new(value: 15), m.int64
+      m.uint32_as_value = 50
+      assert_equal 50, m.uint32_as_value
+      assert_equal Google::Protobuf::UInt32Value.new(value: 50), m.uint32
+      m.uint64_as_value = 500
+      assert_equal 500, m.uint64_as_value
+      assert_equal Google::Protobuf::UInt64Value.new(value: 500), m.uint64
+      m.bool_as_value = false
+      assert_equal false, m.bool_as_value
+      assert_equal Google::Protobuf::BoolValue.new(value: false), m.bool
+      m.string_as_value = 'xy'
+      assert_equal 'xy', m.string_as_value
+      assert_equal Google::Protobuf::StringValue.new(value: 'xy'), m.string
+      m.bytes_as_value = '123'
+      assert_equal '123', m.bytes_as_value
+      assert_equal Google::Protobuf::BytesValue.new(value: '123'), m.bytes
+
+      m.double_as_value = nil
+      assert_nil m.double
+      assert_nil m.double_as_value
+      m.float_as_value = nil
+      assert_nil m.float
+      assert_nil m.float_as_value
+      m.int32_as_value = nil
+      assert_nil m.int32
+      assert_nil m.int32_as_value
+      m.int64_as_value = nil
+      assert_nil m.int64
+      assert_nil m.int64_as_value
+      m.uint32_as_value = nil
+      assert_nil m.uint32
+      assert_nil m.uint32_as_value
+      m.uint64_as_value = nil
+      assert_nil m.uint64
+      assert_nil m.uint64_as_value
+      m.bool_as_value = nil
+      assert_nil m.bool
+      assert_nil m.bool_as_value
+      m.string_as_value = nil
+      assert_nil m.string
+      assert_nil m.string_as_value
+      m.bytes_as_value = nil
+      assert_nil m.bytes
+      assert_nil m.bytes_as_value
+    }
+
     m = proto_module::Wrapper.new
     m = proto_module::Wrapper.new
 
 
-    m.double_as_value = 4.8
-    assert_equal 4.8, m.double_as_value
-    assert_equal Google::Protobuf::DoubleValue.new(value: 4.8), m.double
-    m.float_as_value = 2.4
-    assert_in_delta 2.4, m.float_as_value
-    assert_in_delta Google::Protobuf::FloatValue.new(value: 2.4).value, m.float.value
-    m.int32_as_value = 5
-    assert_equal 5, m.int32_as_value
-    assert_equal Google::Protobuf::Int32Value.new(value: 5), m.int32
-    m.int64_as_value = 15
-    assert_equal 15, m.int64_as_value
-    assert_equal Google::Protobuf::Int64Value.new(value: 15), m.int64
-    m.uint32_as_value = 50
-    assert_equal 50, m.uint32_as_value
-    assert_equal Google::Protobuf::UInt32Value.new(value: 50), m.uint32
-    m.uint64_as_value = 500
-    assert_equal 500, m.uint64_as_value
-    assert_equal Google::Protobuf::UInt64Value.new(value: 500), m.uint64
-    m.bool_as_value = false
-    assert_equal false, m.bool_as_value
-    assert_equal Google::Protobuf::BoolValue.new(value: false), m.bool
-    m.string_as_value = 'xy'
-    assert_equal 'xy', m.string_as_value
-    assert_equal Google::Protobuf::StringValue.new(value: 'xy'), m.string
-    m.bytes_as_value = '123'
-    assert_equal '123', m.bytes_as_value
-    assert_equal Google::Protobuf::BytesValue.new(value: '123'), m.bytes
-
-    m.double_as_value = nil
-    assert_nil m.double
-    assert_nil m.double_as_value
-    m.float_as_value = nil
-    assert_nil m.float
-    assert_nil m.float_as_value
-    m.int32_as_value = nil
-    assert_nil m.int32
-    assert_nil m.int32_as_value
-    m.int64_as_value = nil
-    assert_nil m.int64
-    assert_nil m.int64_as_value
-    m.uint32_as_value = nil
-    assert_nil m.uint32
-    assert_nil m.uint32_as_value
-    m.uint64_as_value = nil
-    assert_nil m.uint64
-    assert_nil m.uint64_as_value
-    m.bool_as_value = nil
-    assert_nil m.bool
-    assert_nil m.bool_as_value
-    m.string_as_value = nil
-    assert_nil m.string
-    assert_nil m.string_as_value
-    m.bytes_as_value = nil
-    assert_nil m.bytes
-    assert_nil m.bytes_as_value
+    m2 = proto_module::Wrapper.new(
+      double: Google::Protobuf::DoubleValue.new(value: 2.0),
+      float: Google::Protobuf::FloatValue.new(value: 4.0),
+      int32: Google::Protobuf::Int32Value.new(value: 3),
+      int64: Google::Protobuf::Int64Value.new(value: 4),
+      uint32: Google::Protobuf::UInt32Value.new(value: 5),
+      uint64: Google::Protobuf::UInt64Value.new(value: 6),
+      bool: Google::Protobuf::BoolValue.new(value: true),
+      string: Google::Protobuf::StringValue.new(value: 'str'),
+      bytes: Google::Protobuf::BytesValue.new(value: 'fun'),
+      real_string: '100'
+    )
+
+    run_asserts.call(m2)
+
+    serialized = proto_module::Wrapper::encode(m2)
+    m3 = proto_module::Wrapper::decode(serialized)
+    run_asserts.call(m3)
   end
   end
 
 
   def test_wrapper_setters
   def test_wrapper_setters
+    run_asserts = ->(m) {
+      m.double = Google::Protobuf::DoubleValue.new(value: 4.8)
+      assert_equal 4.8, m.double_as_value
+      assert_equal Google::Protobuf::DoubleValue.new(value: 4.8), m.double
+      m.float = Google::Protobuf::FloatValue.new(value: 2.4)
+      assert_in_delta 2.4, m.float_as_value
+      assert_in_delta Google::Protobuf::FloatValue.new(value: 2.4).value, m.float.value
+      m.int32 = Google::Protobuf::Int32Value.new(value: 5)
+      assert_equal 5, m.int32_as_value
+      assert_equal Google::Protobuf::Int32Value.new(value: 5), m.int32
+      m.int64 = Google::Protobuf::Int64Value.new(value: 15)
+      assert_equal 15, m.int64_as_value
+      assert_equal Google::Protobuf::Int64Value.new(value: 15), m.int64
+      m.uint32 = Google::Protobuf::UInt32Value.new(value: 50)
+      assert_equal 50, m.uint32_as_value
+      assert_equal Google::Protobuf::UInt32Value.new(value: 50), m.uint32
+      m.uint64 = Google::Protobuf::UInt64Value.new(value: 500)
+      assert_equal 500, m.uint64_as_value
+      assert_equal Google::Protobuf::UInt64Value.new(value: 500), m.uint64
+      m.bool = Google::Protobuf::BoolValue.new(value: false)
+      assert_equal false, m.bool_as_value
+      assert_equal Google::Protobuf::BoolValue.new(value: false), m.bool
+      m.string = Google::Protobuf::StringValue.new(value: 'xy')
+      assert_equal 'xy', m.string_as_value
+      assert_equal Google::Protobuf::StringValue.new(value: 'xy'), m.string
+      m.bytes = Google::Protobuf::BytesValue.new(value: '123')
+      assert_equal '123', m.bytes_as_value
+      assert_equal Google::Protobuf::BytesValue.new(value: '123'), m.bytes
+
+      m.double = nil
+      assert_nil m.double
+      assert_nil m.double_as_value
+      m.float = nil
+      assert_nil m.float
+      assert_nil m.float_as_value
+      m.int32 = nil
+      assert_nil m.int32
+      assert_nil m.int32_as_value
+      m.int64 = nil
+      assert_nil m.int64
+      assert_nil m.int64_as_value
+      m.uint32 = nil
+      assert_nil m.uint32
+      assert_nil m.uint32_as_value
+      m.uint64 = nil
+      assert_nil m.uint64
+      assert_nil m.uint64_as_value
+      m.bool = nil
+      assert_nil m.bool
+      assert_nil m.bool_as_value
+      m.string = nil
+      assert_nil m.string
+      assert_nil m.string_as_value
+      m.bytes = nil
+      assert_nil m.bytes
+      assert_nil m.bytes_as_value
+    }
+
     m = proto_module::Wrapper.new
     m = proto_module::Wrapper.new
+    run_asserts.call(m)
+
+    m2 = proto_module::Wrapper.new(
+      double: Google::Protobuf::DoubleValue.new(value: 2.0),
+      float: Google::Protobuf::FloatValue.new(value: 4.0),
+      int32: Google::Protobuf::Int32Value.new(value: 3),
+      int64: Google::Protobuf::Int64Value.new(value: 4),
+      uint32: Google::Protobuf::UInt32Value.new(value: 5),
+      uint64: Google::Protobuf::UInt64Value.new(value: 6),
+      bool: Google::Protobuf::BoolValue.new(value: true),
+      string: Google::Protobuf::StringValue.new(value: 'str'),
+      bytes: Google::Protobuf::BytesValue.new(value: 'fun'),
+      real_string: '100'
+    )
+
+    run_asserts.call(m2)
 
 
-    m.double = Google::Protobuf::DoubleValue.new(value: 4.8)
-    assert_equal 4.8, m.double_as_value
-    assert_equal Google::Protobuf::DoubleValue.new(value: 4.8), m.double
-    m.float = Google::Protobuf::FloatValue.new(value: 2.4)
-    assert_in_delta 2.4, m.float_as_value
-    assert_in_delta Google::Protobuf::FloatValue.new(value: 2.4).value, m.float.value
-    m.int32 = Google::Protobuf::Int32Value.new(value: 5)
-    assert_equal 5, m.int32_as_value
-    assert_equal Google::Protobuf::Int32Value.new(value: 5), m.int32
-    m.int64 = Google::Protobuf::Int64Value.new(value: 15)
-    assert_equal 15, m.int64_as_value
-    assert_equal Google::Protobuf::Int64Value.new(value: 15), m.int64
-    m.uint32 = Google::Protobuf::UInt32Value.new(value: 50)
-    assert_equal 50, m.uint32_as_value
-    assert_equal Google::Protobuf::UInt32Value.new(value: 50), m.uint32
-    m.uint64 = Google::Protobuf::UInt64Value.new(value: 500)
-    assert_equal 500, m.uint64_as_value
-    assert_equal Google::Protobuf::UInt64Value.new(value: 500), m.uint64
-    m.bool = Google::Protobuf::BoolValue.new(value: false)
-    assert_equal false, m.bool_as_value
-    assert_equal Google::Protobuf::BoolValue.new(value: false), m.bool
-    m.string = Google::Protobuf::StringValue.new(value: 'xy')
-    assert_equal 'xy', m.string_as_value
-    assert_equal Google::Protobuf::StringValue.new(value: 'xy'), m.string
-    m.bytes = Google::Protobuf::BytesValue.new(value: '123')
-    assert_equal '123', m.bytes_as_value
-    assert_equal Google::Protobuf::BytesValue.new(value: '123'), m.bytes
-
-    m.double = nil
-    assert_nil m.double
-    assert_nil m.double_as_value
-    m.float = nil
-    assert_nil m.float
-    assert_nil m.float_as_value
-    m.int32 = nil
-    assert_nil m.int32
-    assert_nil m.int32_as_value
-    m.int64 = nil
-    assert_nil m.int64
-    assert_nil m.int64_as_value
-    m.uint32 = nil
-    assert_nil m.uint32
-    assert_nil m.uint32_as_value
-    m.uint64 = nil
-    assert_nil m.uint64
-    assert_nil m.uint64_as_value
-    m.bool = nil
-    assert_nil m.bool
-    assert_nil m.bool_as_value
-    m.string = nil
-    assert_nil m.string
-    assert_nil m.string_as_value
-    m.bytes = nil
-    assert_nil m.bytes
-    assert_nil m.bytes_as_value
+    serialized = proto_module::Wrapper::encode(m2)
+    m3 = proto_module::Wrapper::decode(serialized)
+    run_asserts.call(m3)
   end
   end
 
 
   def test_wrappers_only
   def test_wrappers_only