static void check_repeated_field_type(VALUE val, const upb_fielddef* field) { RepeatedField* self; assert(upb_fielddef_label(field) == UPB_LABEL_REPEATED); if (!RB_TYPE_P(val, T_DATA) || !RTYPEDDATA_P(val) || RTYPEDDATA_TYPE(val) != &RepeatedField_type) { rb_raise(cTypeError, "Expected repeated field array"); } self = ruby_to_RepeatedField(val); if (self->field_type != upb_fielddef_type(field)) { rb_raise(cTypeError, "Repeated field array has wrong element type"); } if (self->field_type == UPB_TYPE_MESSAGE) { if (self->field_type_class != Descriptor_msgclass(get_def_obj(upb_fielddef_subdef(field)))) { rb_raise(cTypeError, "Repeated field array has wrong message class"); } } if (self->field_type == UPB_TYPE_ENUM) { if (self->field_type_class != EnumDescriptor_enummodule(get_def_obj(upb_fielddef_subdef(field)))) { rb_raise(cTypeError, "Repeated field array has wrong enum class"); } } }
VALUE field_type_class(const upb_fielddef* field) { VALUE type_class = Qnil; if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE) { VALUE submsgdesc = get_def_obj(upb_fielddef_subdef(field)); type_class = Descriptor_msgclass(submsgdesc); } else if (upb_fielddef_type(field) == UPB_TYPE_ENUM) { VALUE subenumdesc = get_def_obj(upb_fielddef_subdef(field)); type_class = EnumDescriptor_enummodule(subenumdesc); } return type_class; }
VALUE build_module_from_enumdesc(EnumDescriptor* enumdesc) { VALUE mod = rb_define_module_id( rb_intern(upb_enumdef_fullname(enumdesc->enumdef))); upb_enum_iter it; for (upb_enum_begin(&it, enumdesc->enumdef); !upb_enum_done(&it); upb_enum_next(&it)) { const char* name = upb_enum_iter_name(&it); int32_t value = upb_enum_iter_number(&it); if (name[0] < 'A' || name[0] > 'Z') { rb_raise(rb_eTypeError, "Enum value '%s' does not start with an uppercase letter " "as is required for Ruby constants.", name); } rb_define_const(mod, name, INT2NUM(value)); } rb_define_singleton_method(mod, "lookup", enum_lookup, 1); rb_define_singleton_method(mod, "resolve", enum_resolve, 1); rb_define_singleton_method(mod, "descriptor", enum_descriptor, 0); rb_iv_set(mod, kDescriptorInstanceVar, get_def_obj(enumdesc->enumdef)); return mod; }
static void check_map_field_type(VALUE val, const upb_fielddef* field) { const upb_fielddef* key_field = map_field_key(field); const upb_fielddef* value_field = map_field_value(field); Map* self; if (!RB_TYPE_P(val, T_DATA) || !RTYPEDDATA_P(val) || RTYPEDDATA_TYPE(val) != &Map_type) { rb_raise(cTypeError, "Expected Map instance"); } self = ruby_to_Map(val); if (self->key_type != upb_fielddef_type(key_field)) { rb_raise(cTypeError, "Map key type does not match field's key type"); } if (self->value_type != upb_fielddef_type(value_field)) { rb_raise(cTypeError, "Map value type does not match field's value type"); } if (upb_fielddef_type(value_field) == UPB_TYPE_MESSAGE || upb_fielddef_type(value_field) == UPB_TYPE_ENUM) { if (self->value_type_class != get_def_obj(upb_fielddef_subdef(value_field))) { rb_raise(cTypeError, "Map value type has wrong message/enum class"); } } }
// Handler for a submessage field in a oneof. static void *oneofsubmsg_handler(void *closure, const void *hd) { MessageHeader* msg = closure; const oneof_handlerdata_t *oneofdata = hd; uint32_t oldcase = DEREF(msg, oneofdata->case_ofs, uint32_t); VALUE subdesc = get_def_obj((void*)oneofdata->md); VALUE subklass = Descriptor_msgclass(subdesc); VALUE submsg_rb; MessageHeader* submsg; if (oldcase != oneofdata->oneof_case_num || DEREF(msg, oneofdata->ofs, VALUE) == Qnil) { DEREF(msg, oneofdata->ofs, VALUE) = rb_class_new_instance(0, NULL, subklass); } // Set the oneof case *after* allocating the new class instance -- otherwise, // if the Ruby GC is invoked as part of a call into the VM, it might invoke // our mark routines, and our mark routines might see the case value // indicating a VALUE is present and expect a valid VALUE. See comment in // layout_set() for more detail: basically, the change to the value and the // case must be atomic w.r.t. the Ruby VM. DEREF(msg, oneofdata->case_ofs, uint32_t) = oneofdata->oneof_case_num; submsg_rb = DEREF(msg, oneofdata->ofs, VALUE); TypedData_Get_Struct(submsg_rb, MessageHeader, &Message_type, submsg); return submsg; }
// Handler to end a map entry: inserts the value defined during the message into // the map. This is the 'endmsg' handler on the map entry msgdef. static bool endmap_handler(void *closure, const void *hd, upb_status* s) { map_parse_frame_t* frame = closure; const map_handlerdata_t* mapdata = hd; VALUE key = native_slot_get( mapdata->key_field_type, Qnil, &frame->key_storage); VALUE value_field_typeclass = Qnil; VALUE value; if (mapdata->value_field_type == UPB_TYPE_MESSAGE || mapdata->value_field_type == UPB_TYPE_ENUM) { value_field_typeclass = get_def_obj(mapdata->value_field_subdef); } value = native_slot_get( mapdata->value_field_type, value_field_typeclass, &frame->value_storage); Map_index_set(frame->map, key, value); free(frame); return true; }
/* * call-seq: * Descriptor.lookup(name) => FieldDescriptor * * Returns the field descriptor for the field with the given name, if present, * or nil if none. */ VALUE Descriptor_lookup(VALUE _self, VALUE name) { DEFINE_SELF(Descriptor, self, _self); const char* s = get_str(name); const upb_fielddef* field = upb_msgdef_ntofz(self->msgdef, s); if (field == NULL) { return Qnil; } return get_def_obj(field); }
/* * call-seq: * DescriptorPool.lookup(name) => descriptor * * Finds a Descriptor or EnumDescriptor by name and returns it, or nil if none * exists with the given name. */ VALUE DescriptorPool_lookup(VALUE _self, VALUE name) { DEFINE_SELF(DescriptorPool, self, _self); const char* name_str = get_str(name); const upb_def* def = upb_symtab_lookup(self->symtab, name_str); if (!def) { return Qnil; } return get_def_obj(def); }
VALUE create_submsg_from_hash(const upb_fielddef *f, VALUE hash) { const upb_def *d = upb_fielddef_subdef(f); assert(d != NULL); VALUE descriptor = get_def_obj(d); VALUE msgclass = rb_funcall(descriptor, rb_intern("msgclass"), 0, NULL); VALUE args[1] = { hash }; return rb_class_new_instance(1, args, msgclass); }
/* * call-seq: * FieldDescriptor.subtype => message_or_enum_descriptor * * Returns the message or enum descriptor corresponding to this field's type if * it is a message or enum field, respectively, or nil otherwise. Cannot be * called *until* the containing message type is added to a pool (and thus * resolved). */ VALUE FieldDescriptor_subtype(VALUE _self) { DEFINE_SELF(FieldDescriptor, self, _self); if (!upb_fielddef_hassubdef(self->fielddef)) { return Qnil; } const upb_def* def = upb_fielddef_subdef(self->fielddef); if (def == NULL) { return Qnil; } return get_def_obj(def); }
VALUE build_class_from_descriptor(Descriptor* desc) { const char *name; VALUE klass; if (desc->layout == NULL) { desc->layout = create_layout(desc->msgdef); } if (desc->fill_method == NULL) { desc->fill_method = new_fillmsg_decodermethod(desc, &desc->fill_method); } name = upb_msgdef_fullname(desc->msgdef); if (name == NULL) { rb_raise(rb_eRuntimeError, "Descriptor does not have assigned name."); } klass = rb_define_class_id( // Docs say this parameter is ignored. User will assign return value to // their own toplevel constant class name. rb_intern("Message"), rb_cObject); rb_ivar_set(klass, descriptor_instancevar_interned, get_def_obj(desc->msgdef)); rb_define_alloc_func(klass, Message_alloc); rb_require("google/protobuf/message_exts"); rb_include_module(klass, rb_eval_string("::Google::Protobuf::MessageExts")); rb_extend_object( klass, rb_eval_string("::Google::Protobuf::MessageExts::ClassMethods")); rb_define_method(klass, "method_missing", Message_method_missing, -1); rb_define_method(klass, "respond_to_missing?", Message_respond_to_missing, -1); rb_define_method(klass, "initialize", Message_initialize, -1); rb_define_method(klass, "dup", Message_dup, 0); // Also define #clone so that we don't inherit Object#clone. rb_define_method(klass, "clone", Message_dup, 0); rb_define_method(klass, "==", Message_eq, 1); rb_define_method(klass, "hash", Message_hash, 0); rb_define_method(klass, "to_h", Message_to_h, 0); rb_define_method(klass, "to_hash", Message_to_h, 0); rb_define_method(klass, "inspect", Message_inspect, 0); rb_define_method(klass, "[]", Message_index, 1); rb_define_method(klass, "[]=", Message_index_set, 2); rb_define_singleton_method(klass, "decode", Message_decode, 1); rb_define_singleton_method(klass, "encode", Message_encode, 1); rb_define_singleton_method(klass, "decode_json", Message_decode_json, -1); rb_define_singleton_method(klass, "encode_json", Message_encode_json, -1); rb_define_singleton_method(klass, "descriptor", Message_descriptor, 0); return klass; }
/* * call-seq: * Descriptor.each(&block) * * Iterates over fields in this message type, yielding to the block on each one. */ VALUE Descriptor_each(VALUE _self) { DEFINE_SELF(Descriptor, self, _self); upb_msg_iter it; for (upb_msg_begin(&it, self->msgdef); !upb_msg_done(&it); upb_msg_next(&it)) { const upb_fielddef* field = upb_msg_iter_field(&it); VALUE obj = get_def_obj(field); rb_yield(obj); } return Qnil; }
// Appends a submessage to a repeated field (a regular Ruby array for now). static void *appendsubmsg_handler(void *closure, const void *hd) { VALUE ary = (VALUE)closure; const submsg_handlerdata_t *submsgdata = hd; VALUE subdesc = get_def_obj((void*)submsgdata->md); VALUE subklass = Descriptor_msgclass(subdesc); MessageHeader* submsg; VALUE submsg_rb = rb_class_new_instance(0, NULL, subklass); RepeatedField_push(ary, submsg_rb); TypedData_Get_Struct(submsg_rb, MessageHeader, &Message_type, submsg); return submsg; }
// Sets a non-repeated submessage field in a message. static void *submsg_handler(void *closure, const void *hd) { MessageHeader* msg = closure; const submsg_handlerdata_t* submsgdata = hd; VALUE subdesc = get_def_obj((void*)submsgdata->md); VALUE subklass = Descriptor_msgclass(subdesc); if (DEREF(msg, submsgdata->ofs, VALUE) == Qnil) { DEREF(msg, submsgdata->ofs, VALUE) = rb_class_new_instance(0, NULL, subklass); } VALUE submsg_rb = DEREF(msg, submsgdata->ofs, VALUE); MessageHeader* submsg; TypedData_Get_Struct(submsg_rb, MessageHeader, &Message_type, submsg); return submsg; }
static void add_handlers_for_message(const void *closure, upb_handlers *h) { const upb_msgdef* msgdef = upb_handlers_msgdef(h); Descriptor* desc = ruby_to_Descriptor(get_def_obj((void*)msgdef)); upb_msg_field_iter i; // If this is a mapentry message type, set up a special set of handlers and // bail out of the normal (user-defined) message type handling. if (upb_msgdef_mapentry(msgdef)) { add_handlers_for_mapentry(msgdef, h, desc); return; } // Ensure layout exists. We may be invoked to create handlers for a given // message if we are included as a submsg of another message type before our // class is actually built, so to work around this, we just create the layout // (and handlers, in the class-building function) on-demand. if (desc->layout == NULL) { desc->layout = create_layout(desc->msgdef); } for (upb_msg_field_begin(&i, desc->msgdef); !upb_msg_field_done(&i); upb_msg_field_next(&i)) { const upb_fielddef *f = upb_msg_iter_field(&i); size_t offset = desc->layout->fields[upb_fielddef_index(f)].offset + sizeof(MessageHeader); if (upb_fielddef_containingoneof(f)) { size_t oneof_case_offset = desc->layout->fields[upb_fielddef_index(f)].case_offset + sizeof(MessageHeader); add_handlers_for_oneof_field(h, f, offset, oneof_case_offset); } else if (is_map_field(f)) { add_handlers_for_mapfield(h, f, offset, desc); } else if (upb_fielddef_isseq(f)) { add_handlers_for_repeated_field(h, f, offset); } else { add_handlers_for_singular_field(h, f, offset); } } }