DEV Community

Rory O'Connell
Rory O'Connell

Posted on

Integrated Ruby GUI day two: I guess I'm doing this

This project keeps me up at night. I keep thinking of all the possibilities and how to proceed. A little experiment turned into an obsession. I'll provide a view of the work log of an obsession.

Have a basic class and method browser up and going. It's live and here I play around with infamous ways of dynamically updating classes. A huge problem I have in the back of my mind is I'm using ObjectSpace.each_object for building the class list. Looking at the C code that method does a full GC before iterating the objects. This is a huge problem as it is currently doing a full GC every. single. frame. 60 times a second. Leaving the class browser open a few minutes the FPS tanks, and immediately springs back hiding the browser window. There isn't a memory leak, it may be memory fragmentation on the VM side.

There's a few more GUI methods exposed to the Ruby VM. I have a lot more to go, I'm adding them as I need them. I'm not happy with the interface at the moment, they're exposed basically one to one. The GUI methods make sense for C and feel awkward in Ruby. I predict I'll change them frequently on the Ruby side.

Right now there's one bootstrap file. Currently the program reads every Ruby file in the ruby directory and throws it at the VM. Not elegant, works for now. As I can pull more things into the VM directly and make it more robust I can add Ruby code into the VM startup; like sys-v init scripts or autoexec.bat files.

module Inspector
  @selected = nil
  @konst = nil

  def self.render
    a = []
    # these mirror the imgui api almost exactly which doesn't feel like ruby
    # the imgui api makes sense with C. i'll come up with better constructions when i get
    # more insight
    GUI.begin_window("Class list") do
      GUI.begin_child("Classes", 400, 0) do
        # each_object triggers a full GC, so we're doing a full GC
        # each frame the class list is visibe :/
        # we must fix this later. after several minutes fps tanks
        # see gc.c mrb_objspace_each_objects
        # will need our own object iterator coming from the C side
        # which doesn't do a full GC first
        ObjectSpace.each_object(Class) { |k| a.push k.name unless k.name.nil? }
        a.sort.each do |n|
          # this is a yucky api
          if GUI.selectable(n, @selected == n)
            @selected = n
            @konst = Kernel.const_get(@selected.to_sym)
          end
        end
      end

      if @selected
        GUI.same_line
        GUI.begin_child("Class methods", 400, 0) do
          @konst.methods(false).sort.each do |m|
            GUI.text m.to_s
          end
        end

        GUI.same_line
        GUI.begin_child("Instance methods", 400, 0) do
          @konst.instance_methods(false).sort.each do |m|
            GUI.text m.to_s
          end
        end
      end
    end
  end
end

Speaking of VM state I have a long term worry about saving the state of the VM and reconstituting it later, like how Pharo handles images. MRuby has a far better design than the mainline VM as the state of the VM is in one mrb_state type. The mainline Ruby VM has a global state like OpenGL. However an mrb_state is a massive series of pointers. Serializing it means following all the pointers of pointers to pointers and copying the memory at the end of the pointer chain. It looks like the mruby-thread library does at least part of that.

There's also the manner of the original Ruby source becoming lost once it's compiled and placed into the VM state. That makes viewing source on methods impossible unless there's a secondary record kept. I'll look into how Pharo handles it.

So much to do.

I have a book to write, damnit.

Top comments (0)