Alexandr Kurilin
Alexandr Kurilin

Reputation: 7855

Seeing under the hood of Ruby?

I've been recently working my way through Programming Language Pragmatics 3rd ed to learn more about how languages work underneath, and I've gotten a lot of mileage out of looking at the assembly produced by really basic GCC-compiled C code. I'm starting to get more comfortable with static languages from the C family, and I would like to begin looking into interpreted languages as well.

Ruby, being my favorite scripting language, would be an excellent candidate for this. I think it'd be OK to start with MRI for learning purposes, and figure out how exactly all of the scanning/parsing/semantic analysis/binding/scoping and other magic happens behind the scenes.

Is this described in detail anywhere, or is there any way for me to dig into it, like say, with the disassembly of a compiled program? I haven't done much digging with interpreted languages, so I wouldn't quite know where to start as far as MRI is concerned.

Thanks!

Upvotes: 3

Views: 760

Answers (3)

Matheus Moreira
Matheus Moreira

Reputation: 17030

There is a Japanese book that describes inner workings of MRI 1.7.3 called the Ruby Hacking Guide. A partial English translation is available.

Even though the translation is not complete and the book was based on an older version of ruby, I still find it to be a superb resource. Many things covered by the book are probably still relevant in current ruby versions, such as the structure of objects, how instance variables are stored, how method lookup is done and so on.

It is a very interesting read. You have to watch out for the differences between versions, though. Always keep the ruby source code at hand.

Upvotes: 1

user47322
user47322

Reputation:

You can peek at the YARV bytecode of any bit of Ruby source code with RubyVM::InstructionSequence

Here's a quick example:

class Greeter
  def initialize(name)
    @name = name
  end
  def greet!
    puts @name
  end
end

Greeter.new("Charlie").greet!

Then you can compile it and disassemble it:

puts RubyVM::InstructionSequence.compile(src).disassemble

And get some output like this:

== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
0000 trace            1                                               (   1)
0002 putspecialobject 3
0004 putnil           
0005 defineclass      :Greeter, <class:Greeter>, 3
0009 pop              
0010 trace            1                                               (  10)
0012 getinlinecache   19, <ic:0>
0015 getconstant      :Greeter
0017 setinlinecache   <ic:0>
0019 putstring        "Charlie"
0021 send             :new, 1, nil, 0, <ic:1>
0027 send             :greet!, 0, nil, 0, <ic:2>
0033 leave            
== disasm: <RubyVM::InstructionSequence:<class:Greeter>@<compiled>>=====
0000 trace            2                                               (   1)
0002 trace            1                                               (   2)
0004 putspecialobject 1
0006 putspecialobject 2
0008 putobject        :initialize
0010 putiseq          initialize
0012 send             :"core#define_method", 3, nil, 0, <ic:0>
0018 pop              
0019 trace            1                                               (   5)
0021 putspecialobject 1
0023 putspecialobject 2
0025 putobject        :greet!
0027 putiseq          greet!
0029 send             :"core#define_method", 3, nil, 0, <ic:1>
0035 trace            4                                               (   8)
0037 leave                                                            (   5)
== disasm: <RubyVM::InstructionSequence:initialize@<compiled>>==========
local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1] s1)
[ 2] name<Arg>  
0000 trace            8                                               (   2)
0002 trace            1                                               (   3)
0004 getlocal         name
0006 dup              
0007 setinstancevariable :@name, <ic:0>
0010 trace            16                                              (   4)
0012 leave                                                            (   3)
== disasm: <RubyVM::InstructionSequence:greet!@<compiled>>==============
0000 trace            8                                               (   5)
0002 trace            1                                               (   6)
0004 putnil           
0005 getinstancevariable :@name, <ic:0>
0008 send             :puts, 1, nil, 8, <ic:1>
0014 trace            16                                              (   7)
0016 leave                                                            (   6)

Upvotes: 4

Pedro Nascimento
Pedro Nascimento

Reputation: 13926

Ruby itself is written in C, so it might be worth taking a look at the code. https://github.com/ruby/ruby

I also recommend Patrick Farley's article about method dispatch. http://www.klankboomklang.com/2007/09/14/method-dispatch/

Upvotes: 2

Related Questions