Browse Source

WIP: first steps towards lazily creating wrappers.

Joshua Haberman 6 năm trước cách đây
mục cha
commit
969d245bd3

+ 136 - 2
ruby/ext/google/protobuf_c/encode_decode.c

@@ -298,6 +298,15 @@ static void *submsg_handler(void *closure, const void *hd) {
   return submsg;
 }
 
+static void* startwrapper(void* closure, const void* hd) {
+  char* msg = closure;
+  const submsg_handlerdata_t* submsgdata = hd;
+
+  set_hasbit(closure, submsgdata->hasbit);
+
+  return msg + submsgdata->ofs;
+}
+
 // Handler data for startmap/endmap handlers.
 typedef struct {
   size_t ofs;
@@ -541,6 +550,85 @@ static void add_handlers_for_repeated_field(upb_handlers *h,
   }
 }
 
+static bool doublewrapper_handler(void* closure, const void* hd, double val) {
+  VALUE* rbval = closure;
+  *rbval = DBL2NUM(val);
+  return true;
+}
+
+static bool floatwrapper_handler(void* closure, const void* hd, float val) {
+  VALUE* rbval = closure;
+  *rbval = DBL2NUM(val);
+  return true;
+}
+
+static bool int64wrapper_handler(void* closure, const void* hd, int64_t val) {
+  VALUE* rbval = closure;
+  *rbval = LL2NUM(val);
+  return true;
+}
+
+static bool uint64wrapper_handler(void* closure, const void* hd, uint64_t val) {
+  VALUE* rbval = closure;
+  *rbval = ULL2NUM(val);
+  return true;
+}
+
+static bool int32wrapper_handler(void* closure, const void* hd, int32_t val) {
+  VALUE* rbval = closure;
+  *rbval = INT2NUM(val);
+  return true;
+}
+
+static bool uint32wrapper_handler(void* closure, const void* hd, uint32_t val) {
+  VALUE* rbval = closure;
+  *rbval = UINT2NUM(val);
+  return true;
+}
+
+static size_t stringwrapper_handler(void* closure, const void* hd,
+                                    const char* ptr, size_t len,
+                                    const upb_bufhandle* handle) {
+  VALUE* rbval = closure;
+  *rbval = get_frozen_string(ptr, len, false);
+  return len;
+}
+
+static size_t byteswrapper_handler(void* closure, const void* hd,
+                                   const char* ptr, size_t len,
+                                   const upb_bufhandle* handle) {
+  VALUE* rbval = closure;
+  *rbval = get_frozen_string(ptr, len, true);
+  return len;
+}
+
+static bool boolwrapper_handler(void* closure, const void* hd, bool val) {
+  VALUE* rbval = closure;
+  if (val) {
+    *rbval = Qtrue;
+  } else {
+    *rbval = Qfalse;
+  }
+  return true;
+}
+
+bool is_wrapper(const upb_msgdef* m) {
+  switch (upb_msgdef_wellknowntype(m)) {
+    case UPB_WELLKNOWN_DOUBLEVALUE:
+    case UPB_WELLKNOWN_FLOATVALUE:
+    case UPB_WELLKNOWN_INT64VALUE:
+    case UPB_WELLKNOWN_UINT64VALUE:
+    case UPB_WELLKNOWN_INT32VALUE:
+    case UPB_WELLKNOWN_UINT32VALUE:
+    case UPB_WELLKNOWN_STRINGVALUE:
+    case UPB_WELLKNOWN_BYTESVALUE:
+    case UPB_WELLKNOWN_BOOLVALUE:
+      return true;
+    default:
+      return false;
+  }
+}
+
 // Set up handlers for a singular field.
 static void add_handlers_for_singular_field(const Descriptor* desc,
                                             upb_handlers* h,
@@ -580,8 +668,11 @@ static void add_handlers_for_singular_field(const Descriptor* desc,
       upb_handlerattr attr = UPB_HANDLERATTR_INIT;
       attr.handler_data = newsubmsghandlerdata(
           h, offset, hasbit, field_type_class(desc->layout, f));
-      upb_handlers_setstartsubmsg(h, f, submsg_handler, &attr);
-      break;
+      if (is_wrapper(upb_fielddef_msgsubdef(f))) {
+        upb_handlers_setstartsubmsg(h, f, startwrapper, &attr);
+      } else {
+        upb_handlers_setstartsubmsg(h, f, submsg_handler, &attr);
+      }
     }
   }
 }
@@ -623,6 +714,43 @@ static void add_handlers_for_mapentry(const upb_msgdef* msgdef, upb_handlers* h,
       MESSAGE_FIELD_NO_HASBIT);
 }
 
+static void add_handlers_for_wrapper(const upb_msgdef* msgdef,
+                                     upb_handlers* h) {
+  const upb_fielddef* f = upb_msgdef_itof(msgdef, 1);
+  switch (upb_msgdef_wellknowntype(msgdef)) {
+    case UPB_WELLKNOWN_DOUBLEVALUE:
+      upb_handlers_setdouble(h, f, doublewrapper_handler, NULL);
+      break;
+    case UPB_WELLKNOWN_FLOATVALUE:
+      upb_handlers_setfloat(h, f, floatwrapper_handler, NULL);
+      break;
+    case UPB_WELLKNOWN_INT64VALUE:
+      upb_handlers_setint64(h, f, int64wrapper_handler, NULL);
+      break;
+    case UPB_WELLKNOWN_UINT64VALUE:
+      upb_handlers_setuint64(h, f, uint64wrapper_handler, NULL);
+      break;
+    case UPB_WELLKNOWN_INT32VALUE:
+      upb_handlers_setint32(h, f, int32wrapper_handler, NULL);
+      break;
+    case UPB_WELLKNOWN_UINT32VALUE:
+      upb_handlers_setuint32(h, f, uint32wrapper_handler, NULL);
+      break;
+    case UPB_WELLKNOWN_STRINGVALUE:
+      upb_handlers_setstring(h, f, stringwrapper_handler, NULL);
+      break;
+    case UPB_WELLKNOWN_BYTESVALUE:
+      upb_handlers_setstring(h, f, byteswrapper_handler, NULL);
+      break;
+    case UPB_WELLKNOWN_BOOLVALUE:
+      upb_handlers_setbool(h, f, boolwrapper_handler, NULL);
+      return;
+    default:
+      rb_raise(rb_eRuntimeError,
+               "Internal logic error with well-known types.");
+  }
+}
+
 // Set up handlers for a oneof field.
 static void add_handlers_for_oneof_field(upb_handlers *h,
                                          const upb_fielddef *f,
@@ -706,6 +834,12 @@ void add_handlers_for_message(const void *closure, upb_handlers *h) {
     return;
   }
 
+  // If this is a wrapper type, use special handlers and bail.
+  if (is_wrapper(msgdef)) {
+    add_handlers_for_wrapper(msgdef, h);
+    return;
+  }
+
   upb_handlers_setunknown(h, unknown_field_handler, &attr);
 
   for (upb_msg_field_begin(&i, desc->msgdef);

+ 26 - 20
ruby/ext/google/protobuf_c/message.c

@@ -62,13 +62,12 @@ VALUE Message_alloc(VALUE klass) {
   Descriptor* desc = ruby_to_Descriptor(descriptor);
   MessageHeader* msg;
   VALUE ret;
-  size_t size;
 
   if (desc->layout == NULL) {
     create_layout(desc);
   }
 
-  msg = ALLOC_N(uint8_t, sizeof(MessageHeader) + desc->layout->size);
+  msg = (void*)ALLOC_N(uint8_t, sizeof(MessageHeader) + desc->layout->size);
   msg->descriptor = desc;
   msg->unknown_fields = NULL;
   memcpy(Message_data(msg), desc->layout->empty_template, desc->layout->size);
@@ -109,25 +108,28 @@ enum {
 };
 
 // Check if the field is a well known wrapper type
-static bool is_wrapper_type_field(const MessageLayout* layout,
-                                  const upb_fielddef* field) {
-  const char* field_type_name = rb_class2name(field_type_class(layout, field));
-
-  return strcmp(field_type_name, "Google::Protobuf::DoubleValue") == 0 ||
-         strcmp(field_type_name, "Google::Protobuf::FloatValue") == 0 ||
-         strcmp(field_type_name, "Google::Protobuf::Int32Value") == 0 ||
-         strcmp(field_type_name, "Google::Protobuf::Int64Value") == 0 ||
-         strcmp(field_type_name, "Google::Protobuf::UInt32Value") == 0 ||
-         strcmp(field_type_name, "Google::Protobuf::UInt64Value") == 0 ||
-         strcmp(field_type_name, "Google::Protobuf::BoolValue") == 0 ||
-         strcmp(field_type_name, "Google::Protobuf::StringValue") == 0 ||
-         strcmp(field_type_name, "Google::Protobuf::BytesValue") == 0;
+static bool is_wrapper_type_field(const upb_fielddef* field) {
+  const upb_msgdef *m = upb_fielddef_msgsubdef(field);
+  switch (upb_msgdef_wellknowntype(m)) {
+    case UPB_WELLKNOWN_DOUBLEVALUE:
+    case UPB_WELLKNOWN_FLOATVALUE:
+    case UPB_WELLKNOWN_INT64VALUE:
+    case UPB_WELLKNOWN_UINT64VALUE:
+    case UPB_WELLKNOWN_INT32VALUE:
+    case UPB_WELLKNOWN_UINT32VALUE:
+    case UPB_WELLKNOWN_STRINGVALUE:
+    case UPB_WELLKNOWN_BYTESVALUE:
+    case UPB_WELLKNOWN_BOOLVALUE:
+      return true;
+    default:
+      return false;
+  }
 }
 
 // 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) {
-  if (is_wrapper_type_field(layout, field) && value != Qnil) {
+  if (is_wrapper_type_field(field) && value != Qnil) {
     VALUE hash = rb_hash_new();
     rb_hash_aset(hash, rb_str_new2("value"), value);
     {
@@ -194,7 +196,7 @@ static int extract_method_call(VALUE method_name, MessageHeader* self,
     if (upb_msgdef_lookupname(self->descriptor->msgdef, wrapper_field_name,
                               name_len - 9, &test_f_wrapper, &test_o_wrapper) &&
         upb_fielddef_type(test_f_wrapper) == UPB_TYPE_MESSAGE &&
-        is_wrapper_type_field(self->descriptor->layout, test_f_wrapper)) {
+        is_wrapper_type_field(test_f_wrapper)) {
       // It does exist!
       has_field = true;
       if (accessor_type == METHOD_SETTER) {
@@ -329,10 +331,14 @@ VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) {
     return layout_has(self->descriptor->layout, Message_data(self), f);
   } else if (accessor_type == METHOD_WRAPPER_GETTER) {
     VALUE value = layout_get(self->descriptor->layout, Message_data(self), f);
-    if (value != Qnil) {
-      value = rb_funcall(value, rb_intern("value"), 0);
+    switch (TYPE(value)) {
+      case T_DATA:
+        return rb_funcall(value, rb_intern("value"), 0);
+      case T_NIL:
+        return Qnil;
+      default:
+        return value;
     }
-    return value;
   } else if (accessor_type == METHOD_WRAPPER_SETTER) {
     VALUE wrapper = ruby_wrapper_type(self->descriptor->layout, f, argv[1]);
     layout_set(self->descriptor->layout, Message_data(self), f, wrapper);

+ 43 - 19
ruby/tests/common_tests.rb

@@ -1266,6 +1266,44 @@ module CommonTests
   end
 
   def test_wrapper_getters
+    run_asserts = ->(m) {
+      assert_equal 2.0, m.double_as_value
+      assert_equal 2.0, m.double.value
+      assert_equal 2.0, m.double_as_value
+
+      assert_equal 4.0, m.float_as_value
+      assert_equal 4.0, m.float.value
+      assert_equal 4.0, m.float_as_value
+
+      assert_equal 3, m.int32_as_value
+      assert_equal 3, m.int32.value
+      assert_equal 3, m.int32_as_value
+
+      assert_equal 4, m.int64_as_value
+      assert_equal 4, m.int64.value
+      assert_equal 4, m.int64_as_value
+
+      assert_equal 5, m.uint32_as_value
+      assert_equal 5, m.uint32.value
+      assert_equal 5, m.uint32_as_value
+
+      assert_equal 6, m.uint64_as_value
+      assert_equal 6, m.uint64.value
+      assert_equal 6, m.uint64_as_value
+
+      assert_equal true, m.bool_as_value
+      assert_equal true, m.bool.value
+      assert_equal true, m.bool_as_value
+
+      assert_equal 'str', m.string_as_value
+      assert_equal 'str', m.string.value
+      assert_equal 'str', m.string_as_value
+
+      assert_equal 'fun', m.bytes_as_value
+      assert_equal 'fun', m.bytes.value
+      assert_equal 'fun', m.bytes_as_value
+    }
+
     m = proto_module::Wrapper.new(
       double: Google::Protobuf::DoubleValue.new(value: 2.0),
       float: Google::Protobuf::FloatValue.new(value: 4.0),
@@ -1279,24 +1317,10 @@ module CommonTests
       real_string: '100'
     )
 
-    assert_equal 2.0, m.double_as_value
-    assert_equal 2.0, m.double.value
-    assert_equal 4.0, m.float_as_value
-    assert_equal 4.0, m.float.value
-    assert_equal 3, m.int32_as_value
-    assert_equal 3, m.int32.value
-    assert_equal 4, m.int64_as_value
-    assert_equal 4, m.int64.value
-    assert_equal 5, m.uint32_as_value
-    assert_equal 5, m.uint32.value
-    assert_equal 6, m.uint64_as_value
-    assert_equal 6, m.uint64.value
-    assert_equal true, m.bool_as_value
-    assert_equal true, m.bool.value
-    assert_equal 'str', m.string_as_value
-    assert_equal 'str', m.string.value
-    assert_equal 'fun', m.bytes_as_value
-    assert_equal 'fun', m.bytes.value
+    run_asserts.call(m)
+    serialized = proto_module::Wrapper::encode(m)
+    m2 = proto_module::Wrapper::decode(serialized)
+    run_asserts.call(m2)
   end
 
   def test_wrapper_setters_as_value
@@ -1443,7 +1467,7 @@ module CommonTests
     assert_raise(NoMethodError) { m.string_XXXXXXXXX }
     assert_raise(NoMethodError) { m.string_XXXXXXXXXX }
   end
-  
+
   def test_converts_time
     m = proto_module::TimeMessage.new