Implement service & method descriptor lookup in Ruby (#15817)
This PR implements lookup of service descriptor and method descriptor objects in Ruby as described in issue https://github.com/protocolbuffers/protobuf/issues/14891. It contains three implementations - one for the CRuby extension API, one for JRuby, and one for FFI. With this patch, * `DescriptorPool#lookup('fully.qualified.service.name')` works and returns a `Google::Protobuf::ServiceDescriptor` object * You can call `#options` on that to get the service options * You can call `#methods` on that to get the services' methods as `Google::Protobuf::MethodDescriptor` objects, * You can call `MethodDescriptor#options` to get method options * You can also get the streaming flags & input/output types of the method with `#input_type`, `#output_type`, `#client_streaming`, and `#server_streaming`. In order to make the FFI implementation work, I had to mark some more methods in the UPB header as exported - I guess that's something which will have to be done on the UPB side, like thispull/16263/head01fed1cc1b
CC @dazuma & @haberman from the original issue, and @JasonLunn (since you work on protobuf it seems - small world!) I apologies for the large volume of copy-pasta'd code from the existing descriptor class implementations into the new ones - I felt this was probably better than designing new abstractions to reduce it off the bat though; this feels like it "fits in" with the existing implementation. Closes #15817 COPYBARA_INTEGRATE_REVIEW=https://github.com/protocolbuffers/protobuf/pull/15817 from KJTsanaktsidis:ktsanaktsidis/add_service_method_descriptors54d7218431
PiperOrigin-RevId: 618221016
parent
d8791df72e
commit
2082ee8bb5
|
@ -173,6 +173,7 @@ ruby/tests/test_import_pb.rb
|
|||
ruby/tests/test_ruby_package_pb.rb
|
||||
ruby/tests/generated_code_proto2_pb.rb
|
||||
ruby/tests/multi_level_nesting_test_pb.rb
|
||||
ruby/tests/service_test_pb.rb
|
||||
ruby/tests/test_import_proto2_pb.rb
|
||||
ruby/tests/test_ruby_package_proto2_pb.rb
|
||||
ruby/compatibility_tests/v3.0.0/protoc
|
||||
|
|
|
@ -29,6 +29,7 @@ test_protos = %w[
|
|||
tests/generated_code_editions.proto
|
||||
tests/multi_level_nesting_test.proto
|
||||
tests/repeated_field_test.proto
|
||||
tests/service_test.proto
|
||||
tests/stress.proto
|
||||
tests/test_import.proto
|
||||
tests/test_import_proto2.proto
|
||||
|
|
|
@ -23,6 +23,9 @@ 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_servicedef_obj(VALUE descriptor_pool,
|
||||
const upb_ServiceDef* def);
|
||||
static VALUE get_methoddef_obj(VALUE descriptor_pool, const upb_MethodDef* def);
|
||||
|
||||
// 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
|
||||
|
@ -153,6 +156,7 @@ static VALUE DescriptorPool_lookup(VALUE _self, VALUE name) {
|
|||
const upb_MessageDef* msgdef;
|
||||
const upb_EnumDef* enumdef;
|
||||
const upb_FieldDef* fielddef;
|
||||
const upb_ServiceDef* servicedef;
|
||||
|
||||
msgdef = upb_DefPool_FindMessageByName(self->symtab, name_str);
|
||||
if (msgdef) {
|
||||
|
@ -169,6 +173,11 @@ static VALUE DescriptorPool_lookup(VALUE _self, VALUE name) {
|
|||
return get_enumdef_obj(_self, enumdef);
|
||||
}
|
||||
|
||||
servicedef = upb_DefPool_FindServiceByName(self->symtab, name_str);
|
||||
if (servicedef) {
|
||||
return get_servicedef_obj(_self, servicedef);
|
||||
}
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
|
@ -1307,6 +1316,298 @@ static void EnumDescriptor_register(VALUE module) {
|
|||
cEnumDescriptor = klass;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// ServiceDescriptor
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct {
|
||||
const upb_ServiceDef* servicedef;
|
||||
// IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
|
||||
// macro to update VALUE references, as to trigger write barriers.
|
||||
VALUE module; // begins as nil
|
||||
VALUE descriptor_pool; // Owns the upb_ServiceDef.
|
||||
} ServiceDescriptor;
|
||||
|
||||
static VALUE cServiceDescriptor = Qnil;
|
||||
|
||||
static void ServiceDescriptor_mark(void* _self) {
|
||||
ServiceDescriptor* self = _self;
|
||||
rb_gc_mark(self->module);
|
||||
rb_gc_mark(self->descriptor_pool);
|
||||
}
|
||||
|
||||
static const rb_data_type_t ServiceDescriptor_type = {
|
||||
"Google::Protobuf::ServicDescriptor",
|
||||
{ServiceDescriptor_mark, RUBY_DEFAULT_FREE, NULL},
|
||||
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
|
||||
};
|
||||
|
||||
static ServiceDescriptor* ruby_to_ServiceDescriptor(VALUE val) {
|
||||
ServiceDescriptor* ret;
|
||||
TypedData_Get_Struct(val, ServiceDescriptor, &ServiceDescriptor_type, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static VALUE ServiceDescriptor_alloc(VALUE klass) {
|
||||
ServiceDescriptor* self = ALLOC(ServiceDescriptor);
|
||||
VALUE ret = TypedData_Wrap_Struct(klass, &ServiceDescriptor_type, self);
|
||||
self->servicedef = NULL;
|
||||
self->module = Qnil;
|
||||
self->descriptor_pool = Qnil;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ServiceDescriptor.new(c_only_cookie, ptr) => ServiceDescriptor
|
||||
*
|
||||
* Creates a descriptor wrapper object. May only be called from C.
|
||||
*/
|
||||
static VALUE ServiceDescriptor_initialize(VALUE _self, VALUE cookie,
|
||||
VALUE descriptor_pool, VALUE ptr) {
|
||||
ServiceDescriptor* self = ruby_to_ServiceDescriptor(_self);
|
||||
|
||||
if (cookie != c_only_cookie) {
|
||||
rb_raise(rb_eRuntimeError,
|
||||
"Descriptor objects may not be created from Ruby.");
|
||||
}
|
||||
|
||||
RB_OBJ_WRITE(_self, &self->descriptor_pool, descriptor_pool);
|
||||
self->servicedef = (const upb_ServiceDef*)NUM2ULL(ptr);
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ServiceDescriptor.name => name
|
||||
*
|
||||
* Returns the name of this service.
|
||||
*/
|
||||
static VALUE ServiceDescriptor_name(VALUE _self) {
|
||||
ServiceDescriptor* self = ruby_to_ServiceDescriptor(_self);
|
||||
return rb_str_maybe_null(upb_ServiceDef_FullName(self->servicedef));
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ServiceDescriptor.file_descriptor
|
||||
*
|
||||
* Returns the FileDescriptor object this service belongs to.
|
||||
*/
|
||||
static VALUE ServiceDescriptor_file_descriptor(VALUE _self) {
|
||||
ServiceDescriptor* self = ruby_to_ServiceDescriptor(_self);
|
||||
return get_filedef_obj(self->descriptor_pool,
|
||||
upb_ServiceDef_File(self->servicedef));
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ServiceDescriptor.each(&block)
|
||||
*
|
||||
* Iterates over methods in this service, yielding to the block on each one.
|
||||
*/
|
||||
static VALUE ServiceDescriptor_each(VALUE _self) {
|
||||
ServiceDescriptor* self = ruby_to_ServiceDescriptor(_self);
|
||||
|
||||
int n = upb_ServiceDef_MethodCount(self->servicedef);
|
||||
for (int i = 0; i < n; i++) {
|
||||
const upb_MethodDef* method = upb_ServiceDef_Method(self->servicedef, i);
|
||||
VALUE obj = get_methoddef_obj(self->descriptor_pool, method);
|
||||
rb_yield(obj);
|
||||
}
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ServiceDescriptor.options => options
|
||||
*
|
||||
* Returns the `ServiceOptions` for this `ServiceDescriptor`.
|
||||
*/
|
||||
static VALUE ServiceDescriptor_options(VALUE _self) {
|
||||
ServiceDescriptor* self = ruby_to_ServiceDescriptor(_self);
|
||||
const google_protobuf_ServiceOptions* opts =
|
||||
upb_ServiceDef_Options(self->servicedef);
|
||||
upb_Arena* arena = upb_Arena_New();
|
||||
size_t size;
|
||||
char* serialized =
|
||||
google_protobuf_ServiceOptions_serialize(opts, arena, &size);
|
||||
VALUE service_options = decode_options(_self, "ServiceOptions", size,
|
||||
serialized, self->descriptor_pool);
|
||||
upb_Arena_Free(arena);
|
||||
return service_options;
|
||||
}
|
||||
|
||||
static void ServiceDescriptor_register(VALUE module) {
|
||||
VALUE klass = rb_define_class_under(module, "ServiceDescriptor", rb_cObject);
|
||||
rb_define_alloc_func(klass, ServiceDescriptor_alloc);
|
||||
rb_define_method(klass, "initialize", ServiceDescriptor_initialize, 3);
|
||||
rb_define_method(klass, "name", ServiceDescriptor_name, 0);
|
||||
rb_define_method(klass, "each", ServiceDescriptor_each, 0);
|
||||
rb_define_method(klass, "file_descriptor", ServiceDescriptor_file_descriptor,
|
||||
0);
|
||||
rb_define_method(klass, "options", ServiceDescriptor_options, 0);
|
||||
rb_include_module(klass, rb_mEnumerable);
|
||||
rb_gc_register_address(&cServiceDescriptor);
|
||||
cServiceDescriptor = klass;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// MethodDescriptor
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct {
|
||||
const upb_MethodDef* methoddef;
|
||||
// IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
|
||||
// macro to update VALUE references, as to trigger write barriers.
|
||||
VALUE module; // begins as nil
|
||||
VALUE descriptor_pool; // Owns the upb_MethodDef.
|
||||
} MethodDescriptor;
|
||||
|
||||
static VALUE cMethodDescriptor = Qnil;
|
||||
|
||||
static void MethodDescriptor_mark(void* _self) {
|
||||
MethodDescriptor* self = _self;
|
||||
rb_gc_mark(self->module);
|
||||
rb_gc_mark(self->descriptor_pool);
|
||||
}
|
||||
|
||||
static const rb_data_type_t MethodDescriptor_type = {
|
||||
"Google::Protobuf::MethodDescriptor",
|
||||
{MethodDescriptor_mark, RUBY_DEFAULT_FREE, NULL},
|
||||
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
|
||||
};
|
||||
|
||||
static MethodDescriptor* ruby_to_MethodDescriptor(VALUE val) {
|
||||
MethodDescriptor* ret;
|
||||
TypedData_Get_Struct(val, MethodDescriptor, &MethodDescriptor_type, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static VALUE MethodDescriptor_alloc(VALUE klass) {
|
||||
MethodDescriptor* self = ALLOC(MethodDescriptor);
|
||||
VALUE ret = TypedData_Wrap_Struct(klass, &MethodDescriptor_type, self);
|
||||
self->methoddef = NULL;
|
||||
self->module = Qnil;
|
||||
self->descriptor_pool = Qnil;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* MethodDescriptor.new(c_only_cookie, ptr) => MethodDescriptor
|
||||
*
|
||||
* Creates a descriptor wrapper object. May only be called from C.
|
||||
*/
|
||||
static VALUE MethodDescriptor_initialize(VALUE _self, VALUE cookie,
|
||||
VALUE descriptor_pool, VALUE ptr) {
|
||||
MethodDescriptor* self = ruby_to_MethodDescriptor(_self);
|
||||
|
||||
if (cookie != c_only_cookie) {
|
||||
rb_raise(rb_eRuntimeError,
|
||||
"Descriptor objects may not be created from Ruby.");
|
||||
}
|
||||
|
||||
RB_OBJ_WRITE(_self, &self->descriptor_pool, descriptor_pool);
|
||||
self->methoddef = (const upb_ServiceDef*)NUM2ULL(ptr);
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* MethodDescriptor.name => name
|
||||
*
|
||||
* Returns the name of this method
|
||||
*/
|
||||
static VALUE MethodDescriptor_name(VALUE _self) {
|
||||
MethodDescriptor* self = ruby_to_MethodDescriptor(_self);
|
||||
return rb_str_maybe_null(upb_MethodDef_Name(self->methoddef));
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* MethodDescriptor.options => options
|
||||
*
|
||||
* Returns the `MethodOptions` for this `MethodDescriptor`.
|
||||
*/
|
||||
static VALUE MethodDescriptor_options(VALUE _self) {
|
||||
MethodDescriptor* self = ruby_to_MethodDescriptor(_self);
|
||||
const google_protobuf_MethodOptions* opts =
|
||||
upb_MethodDef_Options(self->methoddef);
|
||||
upb_Arena* arena = upb_Arena_New();
|
||||
size_t size;
|
||||
char* serialized =
|
||||
google_protobuf_MethodOptions_serialize(opts, arena, &size);
|
||||
VALUE method_options = decode_options(_self, "MethodOptions", size,
|
||||
serialized, self->descriptor_pool);
|
||||
upb_Arena_Free(arena);
|
||||
return method_options;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* MethodDescriptor.input_type => Descriptor
|
||||
*
|
||||
* Returns the `Descriptor` for the request message type of this method
|
||||
*/
|
||||
static VALUE MethodDescriptor_input_type(VALUE _self) {
|
||||
MethodDescriptor* self = ruby_to_MethodDescriptor(_self);
|
||||
const upb_MessageDef* type = upb_MethodDef_InputType(self->methoddef);
|
||||
return get_msgdef_obj(self->descriptor_pool, type);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* MethodDescriptor.output_type => Descriptor
|
||||
*
|
||||
* Returns the `Descriptor` for the response message type of this method
|
||||
*/
|
||||
static VALUE MethodDescriptor_output_type(VALUE _self) {
|
||||
MethodDescriptor* self = ruby_to_MethodDescriptor(_self);
|
||||
const upb_MessageDef* type = upb_MethodDef_OutputType(self->methoddef);
|
||||
return get_msgdef_obj(self->descriptor_pool, type);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* MethodDescriptor.client_streaming => bool
|
||||
*
|
||||
* Returns whether or not this is a streaming request method
|
||||
*/
|
||||
static VALUE MethodDescriptor_client_streaming(VALUE _self) {
|
||||
MethodDescriptor* self = ruby_to_MethodDescriptor(_self);
|
||||
return upb_MethodDef_ClientStreaming(self->methoddef) ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* MethodDescriptor.server_streaming => bool
|
||||
*
|
||||
* Returns whether or not this is a streaming response method
|
||||
*/
|
||||
static VALUE MethodDescriptor_server_streaming(VALUE _self) {
|
||||
MethodDescriptor* self = ruby_to_MethodDescriptor(_self);
|
||||
return upb_MethodDef_ServerStreaming(self->methoddef) ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
static void MethodDescriptor_register(VALUE module) {
|
||||
VALUE klass = rb_define_class_under(module, "MethodDescriptor", rb_cObject);
|
||||
rb_define_alloc_func(klass, MethodDescriptor_alloc);
|
||||
rb_define_method(klass, "initialize", MethodDescriptor_initialize, 3);
|
||||
rb_define_method(klass, "name", MethodDescriptor_name, 0);
|
||||
rb_define_method(klass, "options", MethodDescriptor_options, 0);
|
||||
rb_define_method(klass, "input_type", MethodDescriptor_input_type, 0);
|
||||
rb_define_method(klass, "output_type", MethodDescriptor_output_type, 0);
|
||||
rb_define_method(klass, "client_streaming", MethodDescriptor_client_streaming,
|
||||
0);
|
||||
rb_define_method(klass, "server_streaming", MethodDescriptor_server_streaming,
|
||||
0);
|
||||
rb_gc_register_address(&cMethodDescriptor);
|
||||
cMethodDescriptor = klass;
|
||||
}
|
||||
|
||||
static VALUE get_def_obj(VALUE _descriptor_pool, const void* ptr, VALUE klass) {
|
||||
DescriptorPool* descriptor_pool = ruby_to_DescriptorPool(_descriptor_pool);
|
||||
VALUE key = ULL2NUM((intptr_t)ptr);
|
||||
|
@ -1348,6 +1649,16 @@ static VALUE get_oneofdef_obj(VALUE descriptor_pool, const upb_OneofDef* def) {
|
|||
return get_def_obj(descriptor_pool, def, cOneofDescriptor);
|
||||
}
|
||||
|
||||
static VALUE get_servicedef_obj(VALUE descriptor_pool,
|
||||
const upb_ServiceDef* def) {
|
||||
return get_def_obj(descriptor_pool, def, cServiceDescriptor);
|
||||
}
|
||||
|
||||
static VALUE get_methoddef_obj(VALUE descriptor_pool,
|
||||
const upb_MethodDef* def) {
|
||||
return get_def_obj(descriptor_pool, def, cMethodDescriptor);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Shared functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -1423,6 +1734,8 @@ void Defs_register(VALUE module) {
|
|||
FieldDescriptor_register(module);
|
||||
OneofDescriptor_register(module);
|
||||
EnumDescriptor_register(module);
|
||||
ServiceDescriptor_register(module);
|
||||
MethodDescriptor_register(module);
|
||||
|
||||
rb_gc_register_address(&c_only_cookie);
|
||||
c_only_cookie = rb_class_new_instance(0, NULL, rb_cObject);
|
||||
|
|
|
@ -54,3 +54,19 @@ char* FieldDescriptor_serialized_options(const upb_FieldDef* fielddef,
|
|||
char* serialized = google_protobuf_FieldOptions_serialize(opts, arena, size);
|
||||
return serialized;
|
||||
}
|
||||
|
||||
char* ServiceDescriptor_serialized_options(const upb_ServiceDef* servicedef,
|
||||
size_t* size, upb_Arena* arena) {
|
||||
const google_protobuf_ServiceOptions* opts =
|
||||
upb_ServiceDef_Options(servicedef);
|
||||
char* serialized =
|
||||
google_protobuf_ServiceOptions_serialize(opts, arena, size);
|
||||
return serialized;
|
||||
}
|
||||
|
||||
char* MethodDescriptor_serialized_options(const upb_MethodDef* methoddef,
|
||||
size_t* size, upb_Arena* arena) {
|
||||
const google_protobuf_MethodOptions* opts = upb_MethodDef_Options(methoddef);
|
||||
char* serialized = google_protobuf_MethodOptions_serialize(opts, arena, size);
|
||||
return serialized;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ module Google
|
|||
attach_function :lookup_enum, :upb_DefPool_FindEnumByName, [:DefPool, :string], EnumDescriptor
|
||||
attach_function :lookup_extension, :upb_DefPool_FindExtensionByName,[:DefPool, :string], FieldDescriptor
|
||||
attach_function :lookup_msg, :upb_DefPool_FindMessageByName, [:DefPool, :string], Descriptor
|
||||
attach_function :lookup_service, :upb_DefPool_FindServiceByName, [:DefPool, :string], ServiceDescriptor
|
||||
|
||||
# FileDescriptorProto
|
||||
attach_function :parse, :FileDescriptorProto_parse, [:binary_string, :size_t, Internal::Arena], :FileDescriptorProto
|
||||
|
@ -54,7 +55,8 @@ module Google
|
|||
def lookup name
|
||||
Google::Protobuf::FFI.lookup_msg(@descriptor_pool, name) ||
|
||||
Google::Protobuf::FFI.lookup_enum(@descriptor_pool, name) ||
|
||||
Google::Protobuf::FFI.lookup_extension(@descriptor_pool, name)
|
||||
Google::Protobuf::FFI.lookup_extension(@descriptor_pool, name) ||
|
||||
Google::Protobuf::FFI.lookup_service(@descriptor_pool, name)
|
||||
end
|
||||
|
||||
def self.generated_pool
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2024 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
class MethodDescriptor
|
||||
attr :method_def, :descriptor_pool
|
||||
|
||||
include Google::Protobuf::Internal::Convert
|
||||
|
||||
# FFI Interface methods and setup
|
||||
extend ::FFI::DataConverter
|
||||
native_type ::FFI::Type::POINTER
|
||||
|
||||
class << self
|
||||
prepend Google::Protobuf::Internal::TypeSafety
|
||||
include Google::Protobuf::Internal::PointerHelper
|
||||
|
||||
# @param value [MethodDescriptor] MethodDescriptor to convert to an FFI native type
|
||||
# @param _ [Object] Unused
|
||||
def to_native(value, _)
|
||||
method_def_ptr = value.nil? ? nil : value.instance_variable_get(:@method_def)
|
||||
return ::FFI::Pointer::NULL if method_def_ptr.nil?
|
||||
raise "Underlying method_def was null!" if method_def_ptr.null?
|
||||
method_def_ptr
|
||||
end
|
||||
|
||||
##
|
||||
# @param service_def [::FFI::Pointer] MethodDef pointer to be wrapped
|
||||
# @param _ [Object] Unused
|
||||
def from_native(method_def, _ = nil)
|
||||
return nil if method_def.nil? or method_def.null?
|
||||
service_def = Google::Protobuf::FFI.raw_service_def_by_raw_method_def(method_def)
|
||||
file_def = Google::Protobuf::FFI.file_def_by_raw_service_def(service_def)
|
||||
descriptor_from_file_def(file_def, method_def)
|
||||
end
|
||||
end
|
||||
|
||||
def self.new(*arguments, &block)
|
||||
raise "Descriptor objects may not be created from Ruby."
|
||||
end
|
||||
|
||||
def to_s
|
||||
inspect
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#{self.class.name}: #{name}"
|
||||
end
|
||||
|
||||
def name
|
||||
@name ||= Google::Protobuf::FFI.get_method_name(self)
|
||||
end
|
||||
|
||||
def options
|
||||
@options ||= begin
|
||||
size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
|
||||
temporary_arena = Google::Protobuf::FFI.create_arena
|
||||
buffer = Google::Protobuf::FFI.method_options(self, size_ptr, temporary_arena)
|
||||
Google::Protobuf::MethodOptions.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze).freeze
|
||||
end
|
||||
end
|
||||
|
||||
def input_type
|
||||
@input_type ||= Google::Protobuf::FFI.method_input_type(self)
|
||||
end
|
||||
|
||||
def output_type
|
||||
@output_type ||= Google::Protobuf::FFI.method_output_type(self)
|
||||
end
|
||||
|
||||
def client_streaming
|
||||
@client_streaming ||= Google::Protobuf::FFI.method_client_streaming(self)
|
||||
end
|
||||
|
||||
def server_streaming
|
||||
@server_streaming ||= Google::Protobuf::FFI.method_server_streaming(self)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize(method_def, descriptor_pool)
|
||||
@method_def = method_def
|
||||
@descriptor_pool = descriptor_pool
|
||||
end
|
||||
|
||||
def self.private_constructor(method_def, descriptor_pool)
|
||||
instance = allocate
|
||||
instance.send(:initialize, method_def, descriptor_pool)
|
||||
instance
|
||||
end
|
||||
|
||||
def c_type
|
||||
@c_type ||= Google::Protobuf::FFI.get_c_type(self)
|
||||
end
|
||||
end
|
||||
|
||||
class FFI
|
||||
# MethodDef
|
||||
attach_function :raw_service_def_by_raw_method_def, :upb_MethodDef_Service, [:pointer], :pointer
|
||||
attach_function :get_method_name, :upb_MethodDef_Name, [MethodDescriptor], :string
|
||||
attach_function :method_options, :MethodDescriptor_serialized_options, [MethodDescriptor, :pointer, Internal::Arena], :pointer
|
||||
attach_function :method_input_type, :upb_MethodDef_InputType, [MethodDescriptor], Descriptor
|
||||
attach_function :method_output_type, :upb_MethodDef_OutputType, [MethodDescriptor], Descriptor
|
||||
attach_function :method_client_streaming, :upb_MethodDef_ClientStreaming, [MethodDescriptor], :bool
|
||||
attach_function :method_server_streaming, :upb_MethodDef_ServerStreaming, [MethodDescriptor], :bool
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2024 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
class ServiceDescriptor
|
||||
attr :service_def, :descriptor_pool
|
||||
include Enumerable
|
||||
|
||||
include Google::Protobuf::Internal::Convert
|
||||
|
||||
# FFI Interface methods and setup
|
||||
extend ::FFI::DataConverter
|
||||
native_type ::FFI::Type::POINTER
|
||||
|
||||
class << self
|
||||
prepend Google::Protobuf::Internal::TypeSafety
|
||||
include Google::Protobuf::Internal::PointerHelper
|
||||
|
||||
# @param value [ServiceDescriptor] ServiceDescriptor to convert to an FFI native type
|
||||
# @param _ [Object] Unused
|
||||
def to_native(value, _)
|
||||
service_def_ptr = value.nil? ? nil : value.instance_variable_get(:@service_def)
|
||||
return ::FFI::Pointer::NULL if service_def_ptr.nil?
|
||||
raise "Underlying service_def was null!" if service_def_ptr.null?
|
||||
service_def_ptr
|
||||
end
|
||||
|
||||
##
|
||||
# @param service_def [::FFI::Pointer] ServiceDef pointer to be wrapped
|
||||
# @param _ [Object] Unused
|
||||
def from_native(service_def, _ = nil)
|
||||
return nil if service_def.nil? or service_def.null?
|
||||
file_def = Google::Protobuf::FFI.file_def_by_raw_service_def(service_def)
|
||||
descriptor_from_file_def(file_def, service_def)
|
||||
end
|
||||
end
|
||||
|
||||
def self.new(*arguments, &block)
|
||||
raise "Descriptor objects may not be created from Ruby."
|
||||
end
|
||||
|
||||
def to_s
|
||||
inspect
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#{self.class.name}: #{name}"
|
||||
end
|
||||
|
||||
def name
|
||||
@name ||= Google::Protobuf::FFI.get_service_full_name(self)
|
||||
end
|
||||
|
||||
def file_descriptor
|
||||
@descriptor_pool.send(:get_file_descriptor, Google::Protobuf::FFI.file_def_by_raw_service_def(@service_def))
|
||||
end
|
||||
|
||||
def each &block
|
||||
n = Google::Protobuf::FFI.method_count(self)
|
||||
0.upto(n-1) do |i|
|
||||
yield(Google::Protobuf::FFI.get_method_by_index(self, i))
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def options
|
||||
@options ||= begin
|
||||
size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
|
||||
temporary_arena = Google::Protobuf::FFI.create_arena
|
||||
buffer = Google::Protobuf::FFI.service_options(self, size_ptr, temporary_arena)
|
||||
Google::Protobuf::ServiceOptions.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze).freeze
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize(service_def, descriptor_pool)
|
||||
@service_def = service_def
|
||||
@descriptor_pool = descriptor_pool
|
||||
end
|
||||
|
||||
def self.private_constructor(service_def, descriptor_pool)
|
||||
instance = allocate
|
||||
instance.send(:initialize, service_def, descriptor_pool)
|
||||
instance
|
||||
end
|
||||
|
||||
def c_type
|
||||
@c_type ||= Google::Protobuf::FFI.get_c_type(self)
|
||||
end
|
||||
end
|
||||
|
||||
class FFI
|
||||
# ServiceDef
|
||||
attach_function :file_def_by_raw_service_def, :upb_ServiceDef_File, [:pointer], :FileDef
|
||||
attach_function :get_service_full_name, :upb_ServiceDef_FullName, [ServiceDescriptor], :string
|
||||
attach_function :method_count, :upb_ServiceDef_MethodCount, [ServiceDescriptor], :int
|
||||
attach_function :get_method_by_index, :upb_ServiceDef_Method, [ServiceDescriptor, :int], MethodDescriptor
|
||||
attach_function :service_options, :ServiceDescriptor_serialized_options, [ServiceDescriptor, :pointer, Internal::Arena], :pointer
|
||||
end
|
||||
end
|
||||
end
|
|
@ -15,6 +15,8 @@ require 'google/protobuf/ffi/descriptor'
|
|||
require 'google/protobuf/ffi/enum_descriptor'
|
||||
require 'google/protobuf/ffi/field_descriptor'
|
||||
require 'google/protobuf/ffi/oneof_descriptor'
|
||||
require 'google/protobuf/ffi/method_descriptor'
|
||||
require 'google/protobuf/ffi/service_descriptor'
|
||||
require 'google/protobuf/ffi/descriptor_pool'
|
||||
require 'google/protobuf/ffi/file_descriptor'
|
||||
require 'google/protobuf/ffi/map'
|
||||
|
|
|
@ -38,6 +38,8 @@ import com.google.protobuf.Descriptors.DescriptorValidationException;
|
|||
import com.google.protobuf.Descriptors.EnumDescriptor;
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
import com.google.protobuf.Descriptors.FileDescriptor;
|
||||
import com.google.protobuf.Descriptors.ServiceDescriptor;
|
||||
import com.google.protobuf.Descriptors.MethodDescriptor;
|
||||
import com.google.protobuf.ExtensionRegistry;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import java.util.ArrayList;
|
||||
|
@ -73,6 +75,9 @@ public class RubyDescriptorPool extends RubyObject {
|
|||
cDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::Descriptor");
|
||||
cEnumDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::EnumDescriptor");
|
||||
cFieldDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::FieldDescriptor");
|
||||
cServiceDescriptor =
|
||||
(RubyClass) runtime.getClassFromPath("Google::Protobuf::ServiceDescriptor");
|
||||
cMethodDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::MethodDescriptor");
|
||||
}
|
||||
|
||||
public RubyDescriptorPool(Ruby runtime, RubyClass klazz) {
|
||||
|
@ -156,6 +161,8 @@ public class RubyDescriptorPool extends RubyObject {
|
|||
registerDescriptor(context, message, packageName);
|
||||
for (FieldDescriptor fieldDescriptor : fd.getExtensions())
|
||||
registerExtension(context, fieldDescriptor, packageName);
|
||||
for (ServiceDescriptor serviceDescriptor : fd.getServices())
|
||||
registerService(context, serviceDescriptor, packageName);
|
||||
|
||||
// Mark this as a loaded file
|
||||
fileDescriptors.add(fd);
|
||||
|
@ -206,6 +213,18 @@ public class RubyDescriptorPool extends RubyObject {
|
|||
symtab.put(name, des);
|
||||
}
|
||||
|
||||
private void registerService(
|
||||
ThreadContext context, ServiceDescriptor descriptor, String parentPath) {
|
||||
String fullName = parentPath + descriptor.getName();
|
||||
RubyString name = context.runtime.newString(fullName);
|
||||
RubyServiceDescriptor des =
|
||||
(RubyServiceDescriptor) cServiceDescriptor.newInstance(context, Block.NULL_BLOCK);
|
||||
des.setName(name);
|
||||
// n.b. this will also construct the descriptors for the service's methods.
|
||||
des.setDescriptor(context, descriptor, this);
|
||||
symtab.putIfAbsent(name, des);
|
||||
}
|
||||
|
||||
private FileDescriptor[] existingFileDescriptors() {
|
||||
return fileDescriptors.toArray(new FileDescriptor[fileDescriptors.size()]);
|
||||
}
|
||||
|
@ -213,6 +232,8 @@ public class RubyDescriptorPool extends RubyObject {
|
|||
private static RubyClass cDescriptor;
|
||||
private static RubyClass cEnumDescriptor;
|
||||
private static RubyClass cFieldDescriptor;
|
||||
private static RubyClass cServiceDescriptor;
|
||||
private static RubyClass cMethodDescriptor;
|
||||
private static RubyDescriptorPool descriptorPool;
|
||||
|
||||
private List<FileDescriptor> fileDescriptors;
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Protocol Buffers - Google's data interchange format
|
||||
* Copyright 2024 Google Inc. All rights reserved.
|
||||
* https://developers.google.com/protocol-buffers/
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.google.protobuf.jruby;
|
||||
|
||||
import com.google.protobuf.CodedInputStream;
|
||||
import com.google.protobuf.Descriptors.MethodDescriptor;
|
||||
import org.jruby.*;
|
||||
import org.jruby.anno.JRubyClass;
|
||||
import org.jruby.anno.JRubyMethod;
|
||||
import org.jruby.runtime.Block;
|
||||
import org.jruby.runtime.ObjectAllocator;
|
||||
import org.jruby.runtime.ThreadContext;
|
||||
import org.jruby.runtime.builtin.IRubyObject;
|
||||
import org.jruby.anno.JRubyClass;
|
||||
import org.jruby.anno.JRubyMethod;
|
||||
|
||||
@JRubyClass(name = "MethoDescriptor")
|
||||
public class RubyMethodDescriptor extends RubyObject {
|
||||
public static void createRubyMethodDescriptor(Ruby runtime) {
|
||||
RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
|
||||
RubyClass cMethodDescriptor =
|
||||
protobuf.defineClassUnder(
|
||||
"MethodDescriptor",
|
||||
runtime.getObject(),
|
||||
new ObjectAllocator() {
|
||||
@Override
|
||||
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
||||
return new RubyMethodDescriptor(runtime, klazz);
|
||||
}
|
||||
});
|
||||
cMethodDescriptor.defineAnnotatedMethods(RubyMethodDescriptor.class);
|
||||
}
|
||||
|
||||
public RubyMethodDescriptor(Ruby runtime, RubyClass klazz) {
|
||||
super(runtime, klazz);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* MethodDescriptor.name => name
|
||||
*
|
||||
* Returns the name of this method
|
||||
*/
|
||||
@JRubyMethod(name = "name")
|
||||
public IRubyObject getName(ThreadContext context) {
|
||||
return context.runtime.newString(this.descriptor.getName());
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* MethodDescriptor.options
|
||||
*
|
||||
* Returns the options set on this protobuf rpc method
|
||||
*/
|
||||
@JRubyMethod
|
||||
public IRubyObject options(ThreadContext context) {
|
||||
RubyDescriptorPool pool = (RubyDescriptorPool) RubyDescriptorPool.generatedPool(null, null);
|
||||
RubyDescriptor methodOptionsDescriptor =
|
||||
(RubyDescriptor)
|
||||
pool.lookup(context, context.runtime.newString("google.protobuf.MethodOptions"));
|
||||
RubyClass methodOptionsClass = (RubyClass) methodOptionsDescriptor.msgclass(context);
|
||||
RubyMessage msg = (RubyMessage) methodOptionsClass.newInstance(context, Block.NULL_BLOCK);
|
||||
return msg.decodeBytes(
|
||||
context,
|
||||
msg,
|
||||
CodedInputStream.newInstance(
|
||||
descriptor.getOptions().toByteString().toByteArray()), /*freeze*/
|
||||
true);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* MethodDescriptor.input_type => Descriptor
|
||||
*
|
||||
* Returns the `Descriptor` for the request message type of this method
|
||||
*/
|
||||
@JRubyMethod(name = "input_type")
|
||||
public IRubyObject getInputType(ThreadContext context) {
|
||||
return this.pool.lookup(
|
||||
context, context.runtime.newString(this.descriptor.getInputType().getFullName()));
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* MethodDescriptor.output_type => Descriptor
|
||||
*
|
||||
* Returns the `Descriptor` for the response message type of this method
|
||||
*/
|
||||
@JRubyMethod(name = "output_type")
|
||||
public IRubyObject getOutputType(ThreadContext context) {
|
||||
return this.pool.lookup(
|
||||
context, context.runtime.newString(this.descriptor.getOutputType().getFullName()));
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* MethodDescriptor.client_streaming => bool
|
||||
*
|
||||
* Returns whether or not this is a streaming request method
|
||||
*/
|
||||
@JRubyMethod(name = "client_streaming")
|
||||
public IRubyObject getClientStreaming(ThreadContext context) {
|
||||
return this.descriptor.isClientStreaming()
|
||||
? context.runtime.getTrue()
|
||||
: context.runtime.getFalse();
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* MethodDescriptor.server_streaming => bool
|
||||
*
|
||||
* Returns whether or not this is a streaming response method
|
||||
*/
|
||||
@JRubyMethod(name = "server_streaming")
|
||||
public IRubyObject getServerStreaming(ThreadContext context) {
|
||||
return this.descriptor.isServerStreaming()
|
||||
? context.runtime.getTrue()
|
||||
: context.runtime.getFalse();
|
||||
}
|
||||
|
||||
protected void setDescriptor(
|
||||
ThreadContext context, MethodDescriptor descriptor, RubyDescriptorPool pool) {
|
||||
this.descriptor = descriptor;
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
private MethodDescriptor descriptor;
|
||||
private IRubyObject name;
|
||||
private RubyDescriptorPool pool;
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Protocol Buffers - Google's data interchange format
|
||||
* Copyright 2024 Google Inc. All rights reserved.
|
||||
* https://developers.google.com/protocol-buffers/
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.google.protobuf.jruby;
|
||||
|
||||
import com.google.protobuf.CodedInputStream;
|
||||
import com.google.protobuf.Descriptors.MethodDescriptor;
|
||||
import com.google.protobuf.Descriptors.ServiceDescriptor;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import org.jruby.*;
|
||||
import org.jruby.anno.JRubyClass;
|
||||
import org.jruby.anno.JRubyMethod;
|
||||
import org.jruby.runtime.Block;
|
||||
import org.jruby.runtime.ObjectAllocator;
|
||||
import org.jruby.runtime.ThreadContext;
|
||||
import org.jruby.runtime.builtin.IRubyObject;
|
||||
import org.jruby.anno.JRubyClass;
|
||||
import org.jruby.anno.JRubyMethod;
|
||||
|
||||
@JRubyClass(name = "ServiceDescriptor")
|
||||
public class RubyServiceDescriptor extends RubyObject {
|
||||
public static void createRubyServiceDescriptor(Ruby runtime) {
|
||||
RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
|
||||
RubyClass cServiceDescriptor =
|
||||
protobuf.defineClassUnder(
|
||||
"ServiceDescriptor",
|
||||
runtime.getObject(),
|
||||
new ObjectAllocator() {
|
||||
@Override
|
||||
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
||||
return new RubyServiceDescriptor(runtime, klazz);
|
||||
}
|
||||
});
|
||||
cServiceDescriptor.includeModule(runtime.getEnumerable());
|
||||
cServiceDescriptor.defineAnnotatedMethods(RubyServiceDescriptor.class);
|
||||
cMethodDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::MethodDescriptor");
|
||||
}
|
||||
|
||||
public RubyServiceDescriptor(Ruby runtime, RubyClass klazz) {
|
||||
super(runtime, klazz);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ServiceDescriptor.name => name
|
||||
*
|
||||
* Returns the name of this service type as a fully-qualified string (e.g.,
|
||||
* My.Package.Service).
|
||||
*/
|
||||
@JRubyMethod(name = "name")
|
||||
public IRubyObject getName(ThreadContext context) {
|
||||
return name;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ServiceDescriptor.file_descriptor
|
||||
*
|
||||
* Returns the FileDescriptor object this service belongs to.
|
||||
*/
|
||||
@JRubyMethod(name = "file_descriptor")
|
||||
public IRubyObject getFileDescriptor(ThreadContext context) {
|
||||
return RubyFileDescriptor.getRubyFileDescriptor(context, descriptor);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ServiceDescriptor.options
|
||||
*
|
||||
* Returns the options set on this protobuf service
|
||||
*/
|
||||
@JRubyMethod
|
||||
public IRubyObject options(ThreadContext context) {
|
||||
RubyDescriptorPool pool = (RubyDescriptorPool) RubyDescriptorPool.generatedPool(null, null);
|
||||
RubyDescriptor serviceOptionsDescriptor =
|
||||
(RubyDescriptor)
|
||||
pool.lookup(context, context.runtime.newString("google.protobuf.ServiceOptions"));
|
||||
RubyClass serviceOptionsClass = (RubyClass) serviceOptionsDescriptor.msgclass(context);
|
||||
RubyMessage msg = (RubyMessage) serviceOptionsClass.newInstance(context, Block.NULL_BLOCK);
|
||||
return msg.decodeBytes(
|
||||
context,
|
||||
msg,
|
||||
CodedInputStream.newInstance(
|
||||
descriptor.getOptions().toByteString().toByteArray()), /*freeze*/
|
||||
true);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ServiceDescriptor.each(&block)
|
||||
*
|
||||
* Iterates over methods in this service, yielding to the block on each one.
|
||||
*/
|
||||
@JRubyMethod(name = "each")
|
||||
public IRubyObject each(ThreadContext context, Block block) {
|
||||
for (Map.Entry<IRubyObject, RubyMethodDescriptor> entry : methodDescriptors.entrySet()) {
|
||||
block.yield(context, entry.getValue());
|
||||
}
|
||||
return context.nil;
|
||||
}
|
||||
|
||||
protected void setDescriptor(
|
||||
ThreadContext context, ServiceDescriptor descriptor, RubyDescriptorPool pool) {
|
||||
this.descriptor = descriptor;
|
||||
|
||||
// Populate the methods (and preserve the order by using LinkedHashMap)
|
||||
methodDescriptors = new LinkedHashMap<IRubyObject, RubyMethodDescriptor>();
|
||||
|
||||
for (MethodDescriptor methodDescriptor : descriptor.getMethods()) {
|
||||
RubyMethodDescriptor md =
|
||||
(RubyMethodDescriptor) cMethodDescriptor.newInstance(context, Block.NULL_BLOCK);
|
||||
md.setDescriptor(context, methodDescriptor, pool);
|
||||
methodDescriptors.put(context.runtime.newString(methodDescriptor.getName()), md);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setName(IRubyObject name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
private static RubyClass cMethodDescriptor;
|
||||
|
||||
private ServiceDescriptor descriptor;
|
||||
private Map<IRubyObject, RubyMethodDescriptor> methodDescriptors;
|
||||
private IRubyObject name;
|
||||
}
|
|
@ -55,6 +55,8 @@ public class ProtobufJavaService implements BasicLibraryService {
|
|||
RubyMap.createRubyMap(ruby);
|
||||
RubyOneofDescriptor.createRubyOneofDescriptor(ruby);
|
||||
RubyDescriptor.createRubyDescriptor(ruby);
|
||||
RubyMethodDescriptor.createRubyMethodDescriptor(ruby);
|
||||
RubyServiceDescriptor.createRubyServiceDescriptor(ruby);
|
||||
RubyDescriptorPool.createRubyDescriptorPool(ruby);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package service_test_protos;
|
||||
|
||||
import "google/protobuf/descriptor.proto";
|
||||
|
||||
message UnaryRequestType {
|
||||
string ping = 1;
|
||||
}
|
||||
|
||||
message UnaryResponseType {
|
||||
string pong = 1;
|
||||
}
|
||||
|
||||
message StreamRequestType {
|
||||
string ping = 1;
|
||||
uint32 sequence = 2;
|
||||
}
|
||||
|
||||
message StreamResponseType {
|
||||
string pong = 1;
|
||||
uint32 sequence = 2;
|
||||
}
|
||||
|
||||
message TestOptionsType {
|
||||
uint32 int_option_value = 1;
|
||||
}
|
||||
|
||||
extend google.protobuf.ServiceOptions {
|
||||
optional TestOptionsType test_options = 50000;
|
||||
}
|
||||
|
||||
service TestService {
|
||||
option (test_options).int_option_value = 8325;
|
||||
|
||||
rpc UnaryOne(UnaryRequestType) returns (UnaryResponseType);
|
||||
rpc UnaryTwo(UnaryRequestType) returns (UnaryResponseType);
|
||||
|
||||
rpc IdempotentMethod(UnaryRequestType) returns (UnaryResponseType) {
|
||||
option idempotency_level = IDEMPOTENT;
|
||||
}
|
||||
rpc PureMethod(UnaryRequestType) returns (UnaryResponseType) {
|
||||
option idempotency_level = NO_SIDE_EFFECTS;
|
||||
}
|
||||
|
||||
rpc StreamingMethod(stream StreamRequestType)
|
||||
returns (stream StreamResponseType);
|
||||
}
|
||||
|
||||
service DeprecatedService {
|
||||
option deprecated = true;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
require 'google/protobuf'
|
||||
require 'service_test_pb'
|
||||
require 'test/unit'
|
||||
require 'json'
|
||||
|
||||
class ServiceTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@test_service = Google::Protobuf::DescriptorPool.generated_pool.lookup('service_test_protos.TestService')
|
||||
@deprecated_service = Google::Protobuf::DescriptorPool.generated_pool.lookup('service_test_protos.DeprecatedService')
|
||||
@test_service_methods = @test_service.to_h { |method| [method.name, method] }
|
||||
end
|
||||
|
||||
def test_lookup_service_descriptor
|
||||
assert_kind_of Google::Protobuf::ServiceDescriptor, @test_service
|
||||
assert_equal 'service_test_protos.TestService', @test_service.name
|
||||
end
|
||||
|
||||
def test_file_descriptor
|
||||
assert_kind_of Google::Protobuf::FileDescriptor, @test_service.file_descriptor
|
||||
assert_equal 'service_test.proto', @test_service.file_descriptor.name
|
||||
end
|
||||
|
||||
def test_method_iteration
|
||||
@test_service.each { |method| assert_kind_of Google::Protobuf::MethodDescriptor, method }
|
||||
assert_equal %w(UnaryOne UnaryTwo), @test_service.map { |method| method.name }.first(2)
|
||||
end
|
||||
|
||||
def test_service_options
|
||||
assert @deprecated_service.options.deprecated
|
||||
refute @test_service.options.deprecated
|
||||
end
|
||||
|
||||
def test_service_options_extensions
|
||||
extension_field = Google::Protobuf::DescriptorPool.generated_pool.lookup('service_test_protos.test_options')
|
||||
assert_equal 8325, extension_field.get(@test_service.options).int_option_value
|
||||
end
|
||||
|
||||
def test_method_options
|
||||
assert_equal :IDEMPOTENT, @test_service_methods['IdempotentMethod'].options.idempotency_level
|
||||
assert_equal :NO_SIDE_EFFECTS, @test_service_methods['PureMethod'].options.idempotency_level
|
||||
end
|
||||
|
||||
def test_method_input_type
|
||||
unary_request_type = Google::Protobuf::DescriptorPool.generated_pool.lookup('service_test_protos.UnaryRequestType')
|
||||
assert_same unary_request_type, @test_service_methods['UnaryOne'].input_type
|
||||
end
|
||||
|
||||
def test_method_output_type
|
||||
unary_response_type = Google::Protobuf::DescriptorPool.generated_pool.lookup('service_test_protos.UnaryResponseType')
|
||||
assert_same unary_response_type, @test_service_methods['UnaryOne'].output_type
|
||||
end
|
||||
|
||||
def test_method_streaming_flags
|
||||
refute @test_service_methods['UnaryOne'].client_streaming
|
||||
refute @test_service_methods['UnaryOne'].server_streaming
|
||||
assert @test_service_methods['StreamingMethod'].client_streaming
|
||||
assert @test_service_methods['StreamingMethod'].server_streaming
|
||||
end
|
||||
end
|
|
@ -66,8 +66,8 @@ const upb_FieldDef* upb_DefPool_FindExtensionByNumber(const upb_DefPool* s,
|
|||
const upb_MessageDef* m,
|
||||
int32_t fieldnum);
|
||||
|
||||
const upb_ServiceDef* upb_DefPool_FindServiceByName(const upb_DefPool* s,
|
||||
const char* name);
|
||||
UPB_API const upb_ServiceDef* upb_DefPool_FindServiceByName(
|
||||
const upb_DefPool* s, const char* name);
|
||||
|
||||
const upb_ServiceDef* upb_DefPool_FindServiceByNameWithSize(
|
||||
const upb_DefPool* s, const char* name, size_t size);
|
||||
|
|
|
@ -19,18 +19,19 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool upb_MethodDef_ClientStreaming(const upb_MethodDef* m);
|
||||
UPB_API bool upb_MethodDef_ClientStreaming(const upb_MethodDef* m);
|
||||
const char* upb_MethodDef_FullName(const upb_MethodDef* m);
|
||||
bool upb_MethodDef_HasOptions(const upb_MethodDef* m);
|
||||
int upb_MethodDef_Index(const upb_MethodDef* m);
|
||||
const upb_MessageDef* upb_MethodDef_InputType(const upb_MethodDef* m);
|
||||
const char* upb_MethodDef_Name(const upb_MethodDef* m);
|
||||
const UPB_DESC(MethodOptions) * upb_MethodDef_Options(const upb_MethodDef* m);
|
||||
UPB_API const upb_MessageDef* upb_MethodDef_InputType(const upb_MethodDef* m);
|
||||
UPB_API const char* upb_MethodDef_Name(const upb_MethodDef* m);
|
||||
UPB_API const UPB_DESC(MethodOptions) *
|
||||
upb_MethodDef_Options(const upb_MethodDef* m);
|
||||
const UPB_DESC(FeatureSet) *
|
||||
upb_MethodDef_ResolvedFeatures(const upb_MethodDef* m);
|
||||
const upb_MessageDef* upb_MethodDef_OutputType(const upb_MethodDef* m);
|
||||
bool upb_MethodDef_ServerStreaming(const upb_MethodDef* m);
|
||||
const upb_ServiceDef* upb_MethodDef_Service(const upb_MethodDef* m);
|
||||
UPB_API const upb_MessageDef* upb_MethodDef_OutputType(const upb_MethodDef* m);
|
||||
UPB_API bool upb_MethodDef_ServerStreaming(const upb_MethodDef* m);
|
||||
UPB_API const upb_ServiceDef* upb_MethodDef_Service(const upb_MethodDef* m);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
|
|
@ -19,16 +19,17 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
const upb_FileDef* upb_ServiceDef_File(const upb_ServiceDef* s);
|
||||
UPB_API const upb_FileDef* upb_ServiceDef_File(const upb_ServiceDef* s);
|
||||
const upb_MethodDef* upb_ServiceDef_FindMethodByName(const upb_ServiceDef* s,
|
||||
const char* name);
|
||||
const char* upb_ServiceDef_FullName(const upb_ServiceDef* s);
|
||||
UPB_API const char* upb_ServiceDef_FullName(const upb_ServiceDef* s);
|
||||
bool upb_ServiceDef_HasOptions(const upb_ServiceDef* s);
|
||||
int upb_ServiceDef_Index(const upb_ServiceDef* s);
|
||||
const upb_MethodDef* upb_ServiceDef_Method(const upb_ServiceDef* s, int i);
|
||||
int upb_ServiceDef_MethodCount(const upb_ServiceDef* s);
|
||||
UPB_API const upb_MethodDef* upb_ServiceDef_Method(const upb_ServiceDef* s,
|
||||
int i);
|
||||
UPB_API int upb_ServiceDef_MethodCount(const upb_ServiceDef* s);
|
||||
const char* upb_ServiceDef_Name(const upb_ServiceDef* s);
|
||||
const UPB_DESC(ServiceOptions) *
|
||||
UPB_API const UPB_DESC(ServiceOptions) *
|
||||
upb_ServiceDef_Options(const upb_ServiceDef* s);
|
||||
const UPB_DESC(FeatureSet) *
|
||||
upb_ServiceDef_ResolvedFeatures(const upb_ServiceDef* s);
|
||||
|
|
Loading…
Reference in New Issue