From ca304b15c8f9d9b2b436985e801617e7a0b42419 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Dec 2021 00:14:10 +0100 Subject: [PATCH] Enable multi-base support for RBA + Qt binding --- src/pya/pya/pyaModule.cc | 8 ++-- src/rba/rba/rba.cc | 71 ++++++++++++++++++--------------- src/rba/rba/rbaConvert.cc | 4 +- src/rba/rba/rbaInternal.cc | 14 +++---- src/rba/rba/rbaInternal.h | 6 +-- testdata/ruby/basic_testcore.rb | 1 + 6 files changed, 57 insertions(+), 47 deletions(-) diff --git a/src/pya/pya/pyaModule.cc b/src/pya/pya/pyaModule.cc index 6aed6692c..4ff62f83f 100644 --- a/src/pya/pya/pyaModule.cc +++ b/src/pya/pya/pyaModule.cc @@ -2488,9 +2488,11 @@ public: // produce the child classes for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) { - PyTypeObject *child_class = make_class (cc.operator-> (), as_static); - PythonRef attr ((PyObject *) child_class, false /*borrowed*/); - set_type_attr (type, cc->name ().c_str (), attr); + if (cc->declaration () == cc.operator-> ()) { + PyTypeObject *child_class = make_class (cc.operator-> (), as_static); + PythonRef attr ((PyObject *) child_class, false /*borrowed*/); + set_type_attr (type, cc->name ().c_str (), attr); + } } // add named extensions diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index aafe5e881..e17c57ad6 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -1541,7 +1541,6 @@ public: // got an extension tl_assert (cls->parent ()); m_extensions_for [cls->parent ()->declaration ()].push_back (cls->declaration ()); - m_extensions.insert (cls->declaration ()); } else { @@ -1550,76 +1549,84 @@ public: } } - VALUE make_class (const gsi::ClassBase *cls) + VALUE make_class (const gsi::ClassBase *cls, bool as_static, VALUE parent_class = (VALUE) 0, const gsi::ClassBase *parent = 0) { - if (is_registered (cls)) { - return ruby_cls (cls); + if (is_registered (cls, as_static)) { + return ruby_cls (cls, as_static); } - bool is_extension = (m_extensions.find (cls) != m_extensions.end ()); - - bool base_class_is_extension = (m_extensions.find (cls->base ()) != m_extensions.end ()); - VALUE super = rb_cObject; if (cls->base () != 0) { - super = make_class (cls->base ()); + super = make_class (cls->base (), as_static); } +if (cls->name() == "ViewMode") {// @@@ + printf("@@@ BANG!\n"); fflush(stdout); +}// @@@ VALUE klass; - if (is_extension) { + if (as_static) { if (tl::verbosity () >= 20) { tl::log << tl::to_string (tr ("Registering class as Ruby module:) ")) << cls->name (); } - if (cls->base () && ! base_class_is_extension) { - tl::warn << tl::to_string (tr ("Base class of mixin module ignored: ")) << cls->name (); + + std::string mixin_name = cls->name () + "_Mixin"; + + if (parent) { + klass = rb_define_module_under (parent_class, mixin_name.c_str ()); + } else { + klass = rb_define_module_under (m_module, mixin_name.c_str ()); } - if (cls->parent ()) { - VALUE parent_class = make_class (cls->parent ()->declaration ()); - klass = rb_define_module_under (parent_class, cls->name ().c_str ()); - } else { - klass = rb_define_module_under (m_module, cls->name ().c_str ()); + // if the base class is an extension (mixin), we cannot use it as superclass because it's a module + if (cls->base () != 0) { + rb_include_module (klass, super); } } else { - VALUE direct_super = base_class_is_extension ? rb_cObject : super; - if (cls->parent ()) { - VALUE parent_class = make_class (cls->parent ()->declaration ()); - klass = rb_define_class_under (parent_class, cls->name ().c_str (), direct_super); + if (parent) { + klass = rb_define_class_under (parent_class, cls->name ().c_str (), super); } else { - klass = rb_define_class_under (m_module, cls->name ().c_str (), direct_super); + klass = rb_define_class_under (m_module, cls->name ().c_str (), super); } rb_define_alloc_func (klass, alloc_proxy); } - // if the base class is an extension (mixin), we cannot use it as superclass because it's a module - if (cls->base () != 0 && base_class_is_extension) { - rb_include_module (klass, super); - } - - register_class (klass, cls); + register_class (klass, cls, as_static); // mix-in unnamed extensions auto exts = m_extensions_for.find (cls); if (exts != m_extensions_for.end ()) { for (auto ie = exts->second.begin (); ie != exts->second.end (); ++ie) { - VALUE ext_module = make_class (*ie); + VALUE ext_module = make_class (*ie, true); rb_include_module (klass, ext_module); rb_extend_object (klass, ext_module); } } + // produce the child classes + + for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) { + if (cc->declaration () == cc.operator-> ()) { + if (! is_registered (cc->declaration (), false)) { + make_class (cc->declaration (), false, klass, cc->declaration ()); + } else { + VALUE child_class = ruby_cls (cc->declaration (), false); + rb_define_const (klass, cc->name ().c_str (), child_class); + } + } + } + // add named extensions auto links = m_links_for.find (cls); if (links != m_links_for.end ()) { for (auto il = links->second.begin (); il != links->second.end (); ++il) { - VALUE linked_class = make_class (il->second); + VALUE linked_class = make_class (il->second, false); rb_define_const (klass, il->first.c_str (), linked_class); } } @@ -1702,7 +1709,7 @@ public: // NOTE: extensions can't carry methods - this is due to the method numbering scheme // which can only handle direct base classes. So only constants are carried forward. - if (! is_extension) { + if (! as_static) { // Hint: we need to do static methods before the non-static ones because // rb_define_module_function creates an private instance method. @@ -1838,7 +1845,7 @@ rba_init (RubyInterpreterPrivateData *d) // second pass: make the classes for (auto c = sorted_classes.begin (); c != sorted_classes.end (); ++c) { if ((*c)->declaration () == *c) { - gen.make_class (*c); + gen.make_class (*c, false); } } diff --git a/src/rba/rba/rbaConvert.cc b/src/rba/rba/rbaConvert.cc index da7304264..919a52e9e 100644 --- a/src/rba/rba/rbaConvert.cc +++ b/src/rba/rba/rbaConvert.cc @@ -213,7 +213,7 @@ object_to_ruby (void *obj, Proxy *self, const gsi::ClassBase *cls, bool pass_obj // of the exposed property. Hence copying is safer. // create a instance and copy the value - ret = rb_obj_alloc (ruby_cls (clsact)); + ret = rb_obj_alloc (ruby_cls (clsact, false)); Proxy *p = 0; Data_Get_Struct (ret, Proxy, p); clsact->assign (p->obj (), obj); @@ -243,7 +243,7 @@ object_to_ruby (void *obj, Proxy *self, const gsi::ClassBase *cls, bool pass_obj // TODO: we will create a fresh object here, delete it again and link the // reference to the existing object to the Ruby object. This is not quite // efficient - we should avoid creating and deleting a dummy object first. - ret = rb_obj_alloc (ruby_cls (clsact)); + ret = rb_obj_alloc (ruby_cls (clsact, false)); Proxy *p = 0; Data_Get_Struct (ret, Proxy, p); p->set (obj, pass_obj, is_const /*const*/, can_destroy /*can_destroy*/, ret); diff --git a/src/rba/rba/rbaInternal.cc b/src/rba/rba/rbaInternal.cc index a7a1ecaf5..d752a2493 100644 --- a/src/rba/rba/rbaInternal.cc +++ b/src/rba/rba/rbaInternal.cc @@ -919,24 +919,24 @@ void SignalHandler::call (const gsi::MethodBase *meth, gsi::SerialArgs &args, gs // Class map management static std::map cls_map; -static std::map rev_cls_map; +static std::map , VALUE> rev_cls_map; -void register_class (VALUE ruby_cls, const gsi::ClassBase *gsi_cls) +void register_class (VALUE ruby_cls, const gsi::ClassBase *gsi_cls, bool as_static) { cls_map.insert (std::make_pair (ruby_cls, gsi_cls)); - rev_cls_map.insert (std::make_pair (gsi_cls, ruby_cls)); + rev_cls_map.insert (std::make_pair (std::make_pair (gsi_cls, as_static), ruby_cls)); } -VALUE ruby_cls (const gsi::ClassBase *cls) +VALUE ruby_cls (const gsi::ClassBase *cls, bool as_static) { - std::map ::const_iterator c = rev_cls_map.find (cls); + auto c = rev_cls_map.find (std::make_pair (cls, as_static)); tl_assert (c != rev_cls_map.end ()); return c->second; } -bool is_registered (const gsi::ClassBase *cls) +bool is_registered (const gsi::ClassBase *cls, bool as_static) { - return rev_cls_map.find (cls) != rev_cls_map.end (); + return rev_cls_map.find (std::make_pair (cls, as_static)) != rev_cls_map.end (); } const gsi::ClassBase *find_cclass (VALUE k) diff --git a/src/rba/rba/rbaInternal.h b/src/rba/rba/rbaInternal.h index 73a1a0011..b5b36948c 100644 --- a/src/rba/rba/rbaInternal.h +++ b/src/rba/rba/rbaInternal.h @@ -211,7 +211,7 @@ private: /** * @brief Registers a Ruby class for a gsi class */ -void register_class (VALUE ruby_cls, const gsi::ClassBase *gsi_cls); +void register_class (VALUE ruby_cls, const gsi::ClassBase *gsi_cls, bool as_static); /** * @brief Find the class declaration from the Ruby object @@ -226,12 +226,12 @@ const gsi::ClassBase *find_cclass_maybe_null (VALUE k); /** * @brief Finds the Ruby class for a gsi class */ -VALUE ruby_cls (const gsi::ClassBase *cls); +VALUE ruby_cls (const gsi::ClassBase *cls, bool as_static); /** * @brief Gets a value indicating whether a Ruby class is registered for a GSI class */ -bool is_registered (const gsi::ClassBase *gsi_cls); +bool is_registered (const gsi::ClassBase *gsi_cls, bool as_static); /** * @brief Locks the Ruby object against destruction by the GC diff --git a/testdata/ruby/basic_testcore.rb b/testdata/ruby/basic_testcore.rb index 65344ce41..de8e911b3 100644 --- a/testdata/ruby/basic_testcore.rb +++ b/testdata/ruby/basic_testcore.rb @@ -3019,6 +3019,7 @@ class Basic_TestClass < TestBase bb.set1(17) # B1 assert_equal(bb.get1, 17) # B1 bb.set1(21) # B1 + assert_equal(RBA::B3::E::E3B.to_i, 101) # B3 assert_equal(bb.get1, 21) # B1 assert_equal(RBA::BB::C2, 17) # B2