 Method Dispatch in the Rubinius VM
====================================

Work in progress, may contain omissions and/or errors.


 Fundamental Concepts
----------------------

Main loop:

Tasks:



 Basic Flow
------------

0.  When translating Ruby code into Rubinius' bytecode, the compiler
    generates a SendSite object for every method call it encounters.
    This object is stored as a literal in the CompiledMethod where
    the call takes place. To represent the method call, the compiler
    outputs one of the send_* instructions and gives the index of the
    literal as its argument (instead of just the method name.) If the
    CompiledMethod gets stored on disk, as is usually the case, the
    SendSite object is recreated along with the CompiledMethod itself
    when the file is unmarshalled into runtime objects.

1.  During execution of the program, the send_* instruction is encountered
    and invoked. The instruction starts constructing a Message object,
    which represents a unique method call including details like its
    receiver, the number of arguments given, a block if any etc. At
    this point, the SendSite corresponding to the instruction's argument
    is retrieved from the literals of the executing CompiledMethod. The
    Message also stores the Module in which to start looking for the
    method, obtained from the receiver. The Message object is then
    passed to Task::send_method() in the currently executing task.

2.  The Task calls SendSite::locate() on our site, passing along the
    Message object.

3.  The SendSite has a MethodResolver object it can use to attempt to
    find the method. The resolver can be changed by various optimizations,
    but the standard one is GlobalCacheResolver. If the resolver should
    fail to find the method, the SendSite changes the name of the method
    it is looking for to :method_missing and attempts to locate it instead.

4.  GlobalCacheResolver::resolve() receives the Message and first attempts
    to find it in its cache. The cache is indexed by method name per module,
    so if the name and module we are looking in match, the stored method
    is used. If there is no cached entry, the resolver falls back on the
    slower lookup from HierarchyResolver, which performs the familiar Ruby
    method lookup, stepping up in the object's inheritance hierarchy until
    it finds the correct method name. The HierarchyResolver also checks
    the method visibility when necessary. If the method is found, it is
    recorded into the cache along with the Module in which it was found,
    as well as in the Message object. Then execution returns all the way
    back to Task::send_message() which then calls the execute() method
    on the located method object, passing in the Task and the Message.

5.  The exact behaviour of execute() depends on which type of Executable
    (method object) is in question. A NativeFunction, for example, will
    actually perform the native call right here and then returns. More
    interesting for us is a regular CompiledMethod (and its VMMethod
    representation.) First, a MethodContext is created. These roughly
    represent a "stack frame" with the important difference that each
    of them actually contains its own mini-stack, only large enough for
    what this particular method needs. The MethodContext is filled out
    by importing the actual arguments of this call using the information
    in the Message. The other information is also copied from the Message
    to the context.

6.  Task::make_active() is called with the MethodContext. It first stores
    the currently active context as the sender in our new context, after
    which the new context is set as the active one. Execution then returns
    completely, all the way back to the send_* instruction where the now
    useless Message object is cleared out. The instruction then returns
    back into the instruction dispatch loop (VMMethod::resume() in the
    file vm/gen/instructions.cpp.) All send_* instructions are defined to
    cause a return from the resume() function, so that is what happens.

7.  The final destination is Task::execute(), where after some other
    duties the loop starts over and calls the resume() method on the
    method object of the currently active MethodContext which we just
    updated to the new one. This means that we start executing the
    bytecodes of the method that was called. Success!

8.  Eventually, the called method returns. When this happens, the context
    is removed and the previous context stored as the sender is again made
    the active one and can continue its execution from where the method call
    was made (and naturally has access to the return value of the call.)


 Notes
-------

Parallel Executable/VMExecutable hierarchy and formalization:

Selectors:

StaticScopes:
