Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for descriptor options in ruby interface #12828

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
100 changes: 99 additions & 1 deletion ruby/ext/google/protobuf_c/defs.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ static VALUE get_enumdef_obj(VALUE descriptor_pool, const upb_EnumDef* def);
static VALUE get_fielddef_obj(VALUE descriptor_pool, const upb_FieldDef* def);
static VALUE get_filedef_obj(VALUE descriptor_pool, const upb_FileDef* def);
static VALUE get_oneofdef_obj(VALUE descriptor_pool, const upb_OneofDef* def);
static VALUE get_serialized_options_obj(const char* serialized, size_t size, upb_Arena* arena);

// A distinct object that is not accessible from Ruby. We use this as a
// constructor argument to enforce that certain objects cannot be created from
Expand Down Expand Up @@ -397,6 +398,22 @@ static VALUE Descriptor_msgclass(VALUE _self) {
return self->klass;
}

/*
* call-seq:
* Descriptor.serialized_options => options
*
* Returns a binary string containing the serialized options for this message.
*/
static VALUE Descriptor_serialized_options(VALUE _self) {
Descriptor* self = ruby_to_Descriptor(_self);
const google_protobuf_MessageOptions* opts = upb_MessageDef_Options(self->msgdef);
upb_Arena* arena = upb_Arena_New();
size_t size;
char* serialized = google_protobuf_MessageOptions_serialize(opts, arena, &size);

return get_serialized_options_obj(serialized, size, arena);
}

static void Descriptor_register(VALUE module) {
VALUE klass = rb_define_class_under(module, "Descriptor", rb_cObject);
rb_define_alloc_func(klass, Descriptor_alloc);
Expand All @@ -408,6 +425,7 @@ static void Descriptor_register(VALUE module) {
rb_define_method(klass, "msgclass", Descriptor_msgclass, 0);
rb_define_method(klass, "name", Descriptor_name, 0);
rb_define_method(klass, "file_descriptor", Descriptor_file_descriptor, 0);
rb_define_private_method(klass, "serialized_options", Descriptor_serialized_options, 0);
rb_include_module(klass, rb_mEnumerable);
rb_gc_register_address(&cDescriptor);
cDescriptor = klass;
Expand Down Expand Up @@ -507,12 +525,29 @@ static VALUE FileDescriptor_syntax(VALUE _self) {
}
}

/*
* call-seq:
* FileDescriptor.serialized_options => options
*
* Returns a binary string containing the serialized options for this message.
*/
static VALUE FileDescriptor_serialized_options(VALUE _self) {
FileDescriptor* self = ruby_to_FileDescriptor(_self);
const google_protobuf_FileOptions* opts = upb_FileDef_Options(self->filedef);
upb_Arena* arena = upb_Arena_New();
size_t size;
char* serialized = google_protobuf_FileOptions_serialize(opts, arena, &size);

return get_serialized_options_obj(serialized, size, arena);
}

static void FileDescriptor_register(VALUE module) {
VALUE klass = rb_define_class_under(module, "FileDescriptor", rb_cObject);
rb_define_alloc_func(klass, FileDescriptor_alloc);
rb_define_method(klass, "initialize", FileDescriptor_initialize, 3);
rb_define_method(klass, "name", FileDescriptor_name, 0);
rb_define_method(klass, "syntax", FileDescriptor_syntax, 0);
rb_define_private_method(klass, "serialized_options", FileDescriptor_serialized_options, 0);
rb_gc_register_address(&cFileDescriptor);
cFileDescriptor = klass;
}
Expand Down Expand Up @@ -563,7 +598,7 @@ static VALUE FieldDescriptor_alloc(VALUE klass) {

/*
* call-seq:
* EnumDescriptor.new(c_only_cookie, pool, ptr) => EnumDescriptor
* FieldDescriptor.new(c_only_cookie, pool, ptr) => FieldDescriptor
*
* Creates a descriptor wrapper object. May only be called from C.
*/
Expand Down Expand Up @@ -864,6 +899,22 @@ static VALUE FieldDescriptor_set(VALUE _self, VALUE msg_rb, VALUE value) {
return Qnil;
}

/*
* call-seq:
* FieldDescriptor.serialized_options => options
*
* Returns a binary string containing the serialized options for this message.
*/
static VALUE FieldDescriptor_serialized_options(VALUE _self) {
FieldDescriptor* self = ruby_to_FieldDescriptor(_self);
const google_protobuf_FieldOptions* opts = upb_FieldDef_Options(self->fielddef);
upb_Arena* arena = upb_Arena_New();
size_t size;
char* serialized = google_protobuf_FieldOptions_serialize(opts, arena, &size);

return get_serialized_options_obj(serialized, size, arena);
}

static void FieldDescriptor_register(VALUE module) {
VALUE klass = rb_define_class_under(module, "FieldDescriptor", rb_cObject);
rb_define_alloc_func(klass, FieldDescriptor_alloc);
Expand All @@ -880,6 +931,7 @@ static void FieldDescriptor_register(VALUE module) {
rb_define_method(klass, "clear", FieldDescriptor_clear, 1);
rb_define_method(klass, "get", FieldDescriptor_get, 1);
rb_define_method(klass, "set", FieldDescriptor_set, 2);
rb_define_private_method(klass, "serialized_options", FieldDescriptor_serialized_options, 0);
rb_gc_register_address(&cFieldDescriptor);
cFieldDescriptor = klass;
}
Expand Down Expand Up @@ -979,12 +1031,29 @@ static VALUE OneofDescriptor_each(VALUE _self) {
return Qnil;
}

/*
* call-seq:
* OneofDescriptor.serialized_options => options
*
* Returns a binary string containing the serialized options for this message.
*/
static VALUE OneOfDescriptor_serialized_options(VALUE _self) {
OneofDescriptor* self = ruby_to_OneofDescriptor(_self);
const google_protobuf_OneofOptions* opts = upb_OneofDef_Options(self->oneofdef);
upb_Arena* arena = upb_Arena_New();
size_t size;
char* serialized = google_protobuf_OneofOptions_serialize(opts, arena, &size);

return get_serialized_options_obj(serialized, size, arena);
}

static void OneofDescriptor_register(VALUE module) {
VALUE klass = rb_define_class_under(module, "OneofDescriptor", rb_cObject);
rb_define_alloc_func(klass, OneofDescriptor_alloc);
rb_define_method(klass, "initialize", OneofDescriptor_initialize, 3);
rb_define_method(klass, "name", OneofDescriptor_name, 0);
rb_define_method(klass, "each", OneofDescriptor_each, 0);
rb_define_private_method(klass, "serialized_options", OneOfDescriptor_serialized_options, 0);
rb_include_module(klass, rb_mEnumerable);
rb_gc_register_address(&cOneofDescriptor);
cOneofDescriptor = klass;
Expand Down Expand Up @@ -1153,6 +1222,22 @@ static VALUE EnumDescriptor_enummodule(VALUE _self) {
return self->module;
}

/*
* call-seq:
* EnumDescriptor.serialized_options => options
*
* Returns a binary string containing the serialized options for this message.
*/
static VALUE EnumDescriptor_serialized_options(VALUE _self) {
EnumDescriptor* self = ruby_to_EnumDescriptor(_self);
const google_protobuf_EnumOptions* opts = upb_EnumDef_Options(self->enumdef);
upb_Arena* arena = upb_Arena_New();
size_t size;
char* serialized = google_protobuf_EnumOptions_serialize(opts, arena, &size);

return get_serialized_options_obj(serialized, size, arena);
}

static void EnumDescriptor_register(VALUE module) {
VALUE klass = rb_define_class_under(module, "EnumDescriptor", rb_cObject);
rb_define_alloc_func(klass, EnumDescriptor_alloc);
Expand All @@ -1163,6 +1248,7 @@ static void EnumDescriptor_register(VALUE module) {
rb_define_method(klass, "each", EnumDescriptor_each, 0);
rb_define_method(klass, "enummodule", EnumDescriptor_enummodule, 0);
rb_define_method(klass, "file_descriptor", EnumDescriptor_file_descriptor, 0);
rb_define_private_method(klass, "serialized_options", EnumDescriptor_serialized_options, 0);
rb_include_module(klass, rb_mEnumerable);
rb_gc_register_address(&cEnumDescriptor);
cEnumDescriptor = klass;
Expand Down Expand Up @@ -1209,6 +1295,18 @@ static VALUE get_oneofdef_obj(VALUE descriptor_pool, const upb_OneofDef* def) {
return get_def_obj(descriptor_pool, def, cOneofDescriptor);
}

static VALUE get_serialized_options_obj(const char* serialized, size_t size, upb_Arena* arena) {
if (serialized) {
VALUE ret = rb_str_new(serialized, size);
rb_enc_associate(ret, rb_ascii8bit_encoding());
upb_Arena_Free(arena);
return ret;
} else {
upb_Arena_Free(arena);
rb_raise(rb_eRuntimeError, "Error encoding");
}
}

// -----------------------------------------------------------------------------
// Shared functions
// -----------------------------------------------------------------------------
Expand Down
29 changes: 29 additions & 0 deletions ruby/ext/google/protobuf_c/map.c
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,33 @@ static VALUE Map_freeze(VALUE _self) {
return _self;
}

/*
* call-seq:
* Map.internal_deep_freeze => self
*
* Deep freezes the map and values recursively.
* Internal use only.
*/
static VALUE Map_internal_deep_freeze(VALUE _self) {
Map* self = ruby_to_Map(_self);

if (!RB_OBJ_FROZEN(_self)) {
Map_freeze(_self);

if (self->value_type_info.type == kUpb_CType_Message) {
size_t iter = kUpb_Map_Begin;
upb_MessageValue key, val;

while (upb_Map_Next(self->map, &key, &val, &iter)) {
VALUE val_val = Convert_UpbToRuby(val, self->value_type_info, self->arena);
rb_funcall(val_val, rb_intern("internal_deep_freeze"), 0);
}
}
}

return _self;
}

/*
* call-seq:
* Map.hash => hash_value
Expand Down Expand Up @@ -679,6 +706,8 @@ void Map_register(VALUE module) {
rb_define_method(klass, "clone", Map_dup, 0);
rb_define_method(klass, "==", Map_eq, 1);
rb_define_method(klass, "freeze", Map_freeze, 0);
rb_define_private_method(klass, "internal_deep_freeze",
Map_internal_deep_freeze, 0);
rb_define_method(klass, "hash", Map_hash, 0);
rb_define_method(klass, "to_h", Map_to_h, 0);
rb_define_method(klass, "inspect", Map_inspect, 0);
Expand Down
32 changes: 32 additions & 0 deletions ruby/ext/google/protobuf_c/message.c
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,36 @@ static VALUE Message_freeze(VALUE _self) {
return _self;
}

/*
* call-seq:
* Message.internal_deep_freeze => self
*
* Deep freezes the message object recursively.
* Internal use only.
*/
static VALUE Message_internal_deep_freeze(VALUE _self) {
Message* self = ruby_to_Message(_self);
if (!RB_OBJ_FROZEN(_self)) {
Message_freeze(_self);

int n = upb_MessageDef_FieldCount(self->msgdef);
for (int i = 0; i < n; i++) {
const upb_FieldDef* f = upb_MessageDef_Field(self->msgdef, i);
VALUE field = Message_getfield(_self, f);

if (field != Qnil) {
if (upb_FieldDef_IsMap(f) || upb_FieldDef_IsRepeated(f)) {
rb_funcall(field, rb_intern("internal_deep_freeze"), 0);
} else if (upb_FieldDef_IsSubMessage(f)) {
Message_internal_deep_freeze(field);
}
}
}
}

return _self;
}

/*
* call-seq:
* Message.[](index) => value
Expand Down Expand Up @@ -1407,6 +1437,8 @@ static void Message_define_class(VALUE klass) {
rb_define_method(klass, "==", Message_eq, 1);
rb_define_method(klass, "eql?", Message_eq, 1);
rb_define_method(klass, "freeze", Message_freeze, 0);
rb_define_private_method(klass, "internal_deep_freeze",
Message_internal_deep_freeze, 0);
rb_define_method(klass, "hash", Message_hash, 0);
rb_define_method(klass, "to_h", Message_to_h, 0);
rb_define_method(klass, "inspect", Message_inspect, 0);
Expand Down
30 changes: 30 additions & 0 deletions ruby/ext/google/protobuf_c/repeated_field.c
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,34 @@ static VALUE RepeatedField_freeze(VALUE _self) {
return _self;
}

/*
* call-seq:
* RepeatedField.internal_deep_freeze => self
*
* Deep freezes the repeated field and values recursively.
* Internal use only.
*/
static VALUE RepeatedField_internal_deep_freeze(VALUE _self) {
RepeatedField* self = ruby_to_RepeatedField(_self);

if (!RB_OBJ_FROZEN(_self)) {
RepeatedField_freeze(_self);

if (self->type_info.type == kUpb_CType_Message) {
int size = upb_Array_Size(self->array);
int i;

for (i = 0; i < size; i++) {
upb_MessageValue msgval = upb_Array_Get(self->array, i);
VALUE val = Convert_UpbToRuby(msgval, self->type_info, self->arena);
rb_funcall(val, rb_intern("internal_deep_freeze"), 0);
}
}
}

return _self;
}

/*
* call-seq:
* RepeatedField.hash => hash_value
Expand Down Expand Up @@ -650,6 +678,8 @@ void RepeatedField_register(VALUE module) {
rb_define_method(klass, "==", RepeatedField_eq, 1);
rb_define_method(klass, "to_ary", RepeatedField_to_ary, 0);
rb_define_method(klass, "freeze", RepeatedField_freeze, 0);
rb_define_private_method(klass, "internal_deep_freeze",
RepeatedField_internal_deep_freeze, 0);
rb_define_method(klass, "hash", RepeatedField_hash, 0);
rb_define_method(klass, "+", RepeatedField_plus, 1);
rb_define_method(klass, "concat", RepeatedField_concat, 1);
Expand Down
54 changes: 54 additions & 0 deletions ruby/lib/google/protobuf/descriptor_dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,10 @@ def value(name, number)
end
end

def self.decode_options(klass, serialized_options)
options = klass.decode(serialized_options)
options.send(:internal_deep_freeze)
end
end

# Re-open the class (the rest of the class is implemented in C)
Expand All @@ -461,5 +465,55 @@ def build(&block)
builder.build
end
end

# Re-open the class (the rest of the class is implemented in C)
class Descriptor
def options
@options ||= Google::Protobuf::Internal.decode_options(
Google::Protobuf::MessageOptions,
serialized_options
)
end
end

# Re-open the class (the rest of the class is implemented in C)
class FileDescriptor
def options
@options ||= Google::Protobuf::Internal.decode_options(
Google::Protobuf::FileOptions,
serialized_options
)
end
end

# Re-open the class (the rest of the class is implemented in C)
class FieldDescriptor
def options
@options ||= Google::Protobuf::Internal.decode_options(
Google::Protobuf::FieldOptions,
serialized_options
)
end
end

# Re-open the class (the rest of the class is implemented in C)
class EnumDescriptor
def options
@options ||= Google::Protobuf::Internal.decode_options(
Google::Protobuf::EnumOptions,
serialized_options
)
end
end

# Re-open the class (the rest of the class is implemented in C)
class OneofDescriptor
def options
@options ||= Google::Protobuf::Internal.decode_options(
Google::Protobuf::OneofOptions,
serialized_options
)
end
end
end
end