Overrides. Say the term in the Smalltalk world. It used to mean one thing. It meant using a subclass to implement the same selector as a superclass, thus "overriding" the definition. One of the first techniques new OO programmers learn, is that if they find a class that is close what they want, but just ever so wrong, they can subclass that class, and change the desired behavior by overriding the method.
This of course has limitations, but one pattern that is so common with the override, is the desire not to replace or redefine the original definition, but simply to "extend" it, by adding some pre or postamble to the original code. Thus one of Smalltalk's reserved 5 keywords is "super" which is a stand in for "self", but indicates that search for the method to execute is one class above the receivers.
Then came the other kind of "Override". Kind of like a patch. You don't use a subclass, you just change the method definition. But the system is savvy about the replacement, and keeps track of it, so that when the redefining module is removed, the method reverts back to the original. There were long and heated debates about the pollution of what the name meant. And what to call it instead. Everybody wore out, and nothing happened. So we're one step closer to the kind of obfuscation and ambiguities that C++ programmers love and hate--well, if they'd stay still, we would be.
On this one occasion, I was kind of happy about the pollution, it helped me see something I might not have seen otherwise. Recently, a package I used overrode a certain system method. And then someone came up with a different package I wanted to load that overrode the same method. So I get the behavior of whichever one is loaded second. In both cases though, the pattern is very similar to the common super send. Neither "override" was really interested in removing any behavior of the original implementation. They just wanted to "wrap" it with an extra check.
What would a "super send" look like for these new fangled kind of overrides? We don't want to look along the class heirarchy lines, but instead at the stack of overrides maintained for the overriding method. So I did just that:
iconFor: aMethodDefinition
| mIcon originalIcon overrides mySelector |
mySelector := thisContext selector.
overrides := Override overridesForSelector: mySelector
class: (self class whichClassIncludesSelector: mySelector).
originalIcon := overrides ifNotNil:
[| originalMethod |
originalMethod := overrides last method.
originalMethod valueWithReceiver: self
arguments: (Array with: aMethodDefinition)].
"what the override wanted to really add is below"
mIcon := MultiIcon withIcon: originalIcon.
^DynamicProtocols addIconsForMethod: aMethodDefinition intoMultiIcon: mIcon
Put your flamethrowers away. This has interesting issues, and I definitely do not see this as the correct solution to the problem. But it was an interesting experiment, to see what a "super send" looked like in the context of method overrides that are not, well, the old kind of overrides. It's a bit over the top, in that it uses the thisContext pseudo variable to actually reflect about the current method name. I should have figured how to use thisContext to fetch the "arguments" array too. The interesting nuggets are the example of how one searches the override stack of a method, and then how one can "send" a completely arbitrary message implementation to just about any object. One could even use this to circumvent the classical problem with super, where the programmer wants the skip over the first superclass implementation, and start the search above that.
The correct solution is to change the iconFor: engine, to be one based on annotated methods (pragmas), where programmers register the icon and condition for when it applies with separate methods. Then we can all just add our own favorite eye candy decorating methods