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.
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
Static resolution is done at the Sol 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).
Sol breaks the resolution into it's logical component steps, ie:
- Check the current type against cached type
- Update cached type if needed
- Update cached method if needed
- Call the cached method
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 SlotMachine instructions, but basically does:
- Load methods from current (cached) type
- Enter a while loop until we hit nil (goto NOT_FOUND if nil)
- compare the name of the method with the given name and goto OK if equal
- get the next method (they too are a linked list) and start at while
- NOT_FOUND find method_missing and save in cache (similar process)
- OK: save method in cache
The actual dynamic call does differ from it's static counterpart, but maybe surprising little. In essence both static and dynamic calls:
- create a label and save the return address
- load the methods jump address
- swap in the next message, replacing the current
- jump to the address
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.