WIP: first steps towards lazily creating wrappers.

This commit is contained in:
Joshua Haberman 2019-10-21 07:27:36 -07:00
parent 9fdb2cf2bb
commit 969d245bd3
3 changed files with 205 additions and 41 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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