Like Ruby, YARV has its own control structures. Instead id if or unless, uses two low-level instructions called branchif and branchunless. Instead of while end or until loops YARV has jump. With these instructions YARV can execute most of Ruby’s simple control structures.

When code calls a method, YARV uses the send instruction. This process is known as as method dispatch. This could be considered as another Ruby control structure.

How Ruby executes an if statement

Looking at YARV instructions, Ruby follows a pattern for implementing if else statements:

i = 0
if i < 10
else
end
puts "done"
  • Evaluates condition, using opt_lt(optimized less-than) instruction
  • Branchunless jump to false if condition is false. Ruby uses branchuless, not branchif, for if … else conditions, because the positive case is compiled to appear after the condition code. YARV needs to jump if the condition is false.
  • True code, jump past false code. If conditions is true Ruby does not branch, just executes the positive case code. Once it is finished, it jumps down to the following if … else statement
  • False code. Whether or not in branches, Ruby continues to execute the subsequent code.

Jumping from one scope to another

Ruby can jump from one scope to another, using break can be used to exit a loop or an iteration.

i = 0
while i<10
puts i
i += 1
break
end
10.times do |n|
puts n
break
end
puts "continue from here"

YARV can exit the while loop using jump instruction.

In the second example YARV needs to jump to the parent scope and continue the execution after the call to 10.times.

To implement jumping from one scope to another in the Ruby call stack, Ruby uses throw YARV instruction. Sends the execution path back to a higher scope. The throw instruction is using a catch table, or a table of pointers, that is attached to a YARV code snippet. Later, when YARV executes the throw instruction, it checks to see whether there is a catch table containing a break pointer for current YARV instruction. Ruby continues to iterate until it finds a catch table with a break pointer.

Return, rescue, ensure, retry, redo and next are using the catch table.

The break control structure is implemented similar with raising and rescuing exceptions.

Ruby has no for loop control structures, internally implements for loops using each.

The send instruction

The send instruction tells YARV to jump to another method and start executing it.

First in method lookup, Ruby searches for the method the code should call. This means looping through the classes and modules that make up the receiver object.

Internally there are 11 different types of methods. During the method dispatch process Ruby determines which type of method the code is trying to call.

Calling normal Ruby methods

When we call a standard method YARV creates a new stack frame and then starts executing the instructions in the target method.

It creates a table of local variables and arguments for each method. There are 5 types of arguments

def argument_types(a,b=1, *args, c, &d)
...
end
  • Standard argument
  • Optional argument
  • Splat argument array
  • Post argument
  • Block argument

Calling built-in Ruby methods

There are many methods built into the Ruby language which are CFUNC methods, implemented using C code. Integer#times . When Ruby cals built-in CFUNC methods, doesn’t need to prepare the method arguments, it simply creates a new stack frame and calls the target method.

There are two special method types IVAR and ATTRSET to speed up the process of setting and accessing instance variables. When attr_writer or attr_accessor are used, Ruby uses a C function to set the instance variable fast.