> El 18 sept 2019, a las 15:05, Alistair Grant <akgrant0...@gmail.com> escribió:
>>> 
>>> TraitedMetaclass stores methods in two different instance variables:
>>> 
>>> - methodDict (inherited from Behavior)
>>> - localMethods
>>> 
>>> And the same method can be different in each dictionary.
>> 
>> Yes, trais actually work by flattening the methods of traits into the user 
>> class.
>> - localMethods defines the methods actually defined in the class
>> - methodDict has the raw low-level flattened version the VM uses for the 
>> lookup
> 
> So methodDict and localMethods should always contain the same instance
> of CompiledMethod for a given selector, right?

Yes it should.

>> 
>> We are tracking this, it seems you have found two bugs :D.
>> 
>> 1st bug) Calypso and the senders/implementors should be in sync and show the 
>> same (always the local methods for traited classes)
> 
> From what you've written above (and below), the two dictionaries
> should always have the same instance of CompiledMethod so it shouldn't
> matter where it comes from.

Just for the record, yes and no :).

With the new trait implementation, we have actually a modular MOP.
All classes (whether traited or not) understand:
  - #localMethods => returning the methods as they are implemented in the class
  - #methods => returning the actual methods executed by the VM

For normal classes, #localMethods is defined as follows:

localMethods     
        ^ self methods

And methods understand
 - #origin => where that method is actually defined
  For example, in a traited class a lookup should be done in the trait 
composition, otherwise is the class where the method is installed.

Tools should work against #localMethods and #origin, which hide how the actual 
implementation stores the things :).
We already had in the past a lot of tools that hardcoded/duplicated the 
internals of Traits for example.

The other advantage of this is that, imagine you want to extend Pharo with: 
  - multimethods => localMethods will have a single multi-method 
implementation, and the implementation side will contain maybe an expanded 
version with type checks or even one method per type combination
  - optional parameters => a single method with many parameters should be 
expanded to many methods with less parameters + default values 

The idea is that tools do not hardcode the implementation details of these 
extensions, so the public API is #localMethods and #origin :)
Of course, one could design a tool showing #methods, which will show the 
low-level view of classes.

> 
> 
>> Moreover, (I’m confirming this with Pablo on line right now) the local 
>> methods dictionary should be just a subset of the method dictionary.
>> In other words, the invariant is that methods in localMethods should be 
>> included in the methoddict, they should not differ as they are differing 
>> there.
>> 
>> 2nd bug) UFFI? seems to be messing with this invariant :)
>> 
>> When a UFFI binding is executed for the very first time, the ffi call 
>> definition gets compiled and a new compiled method is generated performing 
>> that call.
>> When a session finishes (the image stops/restarts) ffi bindings are reset 
>> (because their compilation is platform dependent).
>> 
>> What we are seeing there is that somehow UFFI managed to reset a binding 
>> method and put a wrong one in the local method
>> 
>> I’ve seen this bug before but never got the insight of what was producing it 
>> (neither put too much time to dig on it either :P).
>> Another possibility is that the culprit are the LibGit bindings, which 
>> extend UFFI binding compilation.
> 
> I haven't looked at this, but what I saw was the localMethods was
> correct and the methodDict was old:
> 
> Redefine a base system method (LGitCommit>>commit_author:) using an
> extension method resulted in methodDict containing the original
> definition and the localMethods containing the overridden method.
> What I would expect is that the overridden method is what is used
> everywhere.
> 
> I'm not sure if this is related to UFFI or not.

Pablo tracked this down to possibly

FFIMethodRegistry >> removeMethod: aMethod

        aMethod methodClass methodDict
                at: aMethod selector
                put: (aMethod propertyValueAt: #ffiNonCompiledMethod)

Reply via email to