Fork me on GitHub

Dynamic Method resolution

Dynamic method resolution is the process of finding a method to call. The calling convention defines how arguments are transferred and control changes and returns.

Resolution in general

To determine a Method in Rubyx, we need to determine the Type.

Types store the methods, and with the method name we can then find the appropriate Method.

Ruby has a fallback for when a method is not found: we then call method_missing. This is defined on Object, and as every object is an object, method_missing is guaranteed to be found.

This is the same process for dynamic and static resolution.

Static Resolution

The need to resolve a method dynamically stems from an inability to determine the Method at compile-time.

Currently the only way to be sure of an expressions type, or the only case the compiler recognises, is an assignment directly before the call. This "types" the variable that is assigned and we can retrieve the method.

Off course calls on constants are also recognised. But otherwise a dynamic resolution is initiated.

Dynamic Method Resolution

When static resolution has failed, the compiler emits code to resolve the method at runtime. This process resolves around a cache, and currently a cache of one, captured by the class CacheEntry

Vool

Static resolution is done at the Vool level, where the CacheEntry is also created and then used as any other constant. Ie there is exactly one CacheEntry for every call site (SendStatement).

Vool breaks the resolution into it's logical component steps, ie:

Breaking down means that Mom Instructions are emitted to effect the logic above. Most of the logic (like the if that is needed) already exists. But the last two steps require special instructions described below.

Mom

The method cache update described above is basically an assignment where we assign the resolved method to the cache. But to do that, we need to finally do the method resolution.

The implementation of the ResolveMethod is quite a bit longer than most other Mom instructions, but basically does:

To achieve readable code for this low level assembler programming a DSL is used. The result is quite understandable.

The actual dynamic call does differ from it's static counterpart, but maybe surprising little. In essence both static and dynamic calls:

The main difference is in loading the method's address (step 2). Where a static setup just loads the method constant, the dynamic one load the CacheEntry first, and the method from that.
That is basically the main difference. Currently (4/18) a FunctionCall/DynamicJump is still issued, but they are so similar that they will probably be unified soon.