Konrad Eisele
Konrad Eisele

Reputation: 3194

Method lookup in Ruby's interrelation diagram?

Can someone explain the relation between the Ruby class interrelation diagram and the way method lookup is done in the actual Ruby C code?

Interrelation diagram from https://ruby-doc.org/core-2.5.3/Class.html:

                     +---------+             +-...
                     |         |             |
     BasicObject-----|-->(BasicObject)-------|-...
         ^           |         ^             |
         |           |         |             |
      Object---------|----->(Object)---------|-...
         ^           |         ^             |
         |           |         |             |
         +-------+   |         +--------+    |
         |       |   |         |        |    |
         |    Module-|---------|--->(Module)-|-...
         |       ^   |         |        ^    |
         |       |   |         |        |    |
         |     Class-|---------|---->(Class)-|-...
         |       ^   |         |        ^    |
         |       +---+         |        +----+
         |                     |
 obj--->OtherClass---------->(OtherClass)-----------...

and the lookup in Ruby code seems to traverse RClass's super member as a flat list here: pseudocode

while (!st_lookup(RCLASS(klass)->m_tbl, ...)) {
    klass = RCLASS(klass)->super;
    ...
}

Where in the above diagram would the internal RClass.super and RBasic.klass (C structures) pointer-arrows be mapped ? Which path would the method lookup take in this diagram? In particular the diagram seems to contain cycles. How is that to be interpreted? What does the ... in

obj--->OtherClass---------->(OtherClass)-----------...

mean (singleton of a singleton ?), how is singleton of a singleton accessed in Ruby and how is it modeled in the C implementation?

Upvotes: 0

Views: 58

Answers (1)

Konrad Eisele
Konrad Eisele

Reputation: 3194

I diggged around a bit and the ancestor function seem to traverse the RClass.super c-struct member, the same as the method looup does. So when I do a

class OtherClass end
obj = OtherClass.new
obj.class.singleton_class.singleton_class.ancestors => 
[#<Class:#<Class:OtherClass>>, \
 #<Class:#<Class:Object>>, \
 #<Class:#<Class:BasicObject>>, \
 #<Class:Class>, \
 #<Class:Module>, \
 #<Class:Object>, \
 #<Class:BasicObject>, \
 Class, \
 Module, \
 Object, \
 Kernel, \
 BasicObject]

     BasicObject
          ^           +---------+                 +--------+
          |           |         |                 |        |
      Kernel          | #<Class:BasicObject>      | #<Class:#<Class:BasicObject>>
          ^           |         ^                 |        ^
          |           |         |                 |        |
       Object         |   #<Class:Object>         | #<Class:#<Class:Object>>
          ^           |         ^                 |        ^
          |           |         |                 |        |
          +-------+   |         +--------+        |        |
                  |   |                  |        |        |
               Module |           #<Class:Module> |        |
                  ^   |                  ^        |        |
                  |   |                  |        |        |
                Class |           #<Class:Class>  |        |
                  ^   |                  ^        |        |
                  +---+                  +--------+        |
                                                           |
obj--->OtherClass --->#<Class:OtherClass>--->#<Class:#<Class:OtherClass>>

That means the vertical arrows in the diagram can be seen as the RClass.super c-member traversal. The horizontal arrows on the other hand should be related to RBasic.klass, however the Ruby code seems asymetric.

             ...
              |
obj--->   OtherClass 

When a singleton class is created the former RBasic.klass will get the RClass.super of the new singleton class.

             ...                           ...
            Object                   #<Class:Object>
              ^                             ^
              |                             |                  
          OtherClass                        |
              ^                             |
              |                             |
obj--->#<Class:#OtherClass:0x...> ->#<Class:OtherClass> -+
                                                       ^-+

and going one step futher a singleton of a singleton then looks like:

             ...                         ...                        ...
            Object                  #<Class:Object>          #Class<#<Class:Object>>
              ^                           ^                          ^
              |                           |                          |
          OtherClass                      |                          |
              ^                           |                          |
              |                           |                          |
obj-->#<Class:#OtherClass:0x...>-->#<Class:OtherClass>-->#<Class:#<Class:OtherClass>>-+
                                                                                    ^-+

The meaning/usage of a singleton class is understandable, however there meaning/usage of the metaclasses is a bit esoteric.

Upvotes: 1

Related Questions