For the last week or so, there's been an interesting discussion in the VW-NC mailing list under the title Store Extensions. It revolves around the problem of managing method overrides (what I'll refer to as patched methods hereafter). The basic suggestion is that there be a formalized preamble/postamble method extension mechanism. CLOS has these. I believe Eiffel does as well. I assume some university based extension to Java with a funky name adds them for Java.
In A Super Send of a Different Color I described a poor man's implementation of how to call original methods from their patches. While "poor" it actually has more flexibility than a formalized before and after mechanism. Ironically, the case I used there (ProtocolItemNavigatorPart>>iconFor:), has been rewritten to make that behavior pluggable and no longer requires any patches.
I'll try to avoid too much dissertation on whether I think these are a good idea or not. You can draw your own conclusions.
What interests me in this case, is how useful it might or might not be. The application is obvious. As I groused through my own image, I found a few that looked like they fit the postamble/preamble pattern, only to discover on further inspection that they did not. Not wanting to locate all of them, not even knowing how many my current image had, I sat down and put together some workspace code to help me understand a) how many patched methods I have in my system b) what fraction of them would take advantage of a formalized preamble/postamble extensibility (thanks to Niall Ross for helping me with a piece I was missing).
The Query
The expression I came up with is this chunk:
amble := 0.
unamble := 0.
Override.Methods
do:
[:perClassSet |
perClassSet
do:
[:perSelectorSet |
perSelectorSet
do:
[:each |
| originalBody currentMethod |
each object getSource
ifNotNil:
[originalBody := (RBParser parseMethod: each sourceCode) body.
originalBody
temporaries:
(Array with: (RBPatternParser parseExpression: '`@temps')).
originalBody
statements:
(Array with: (RBPatternParser parseExpression: '`@.preamble.'))
, originalBody statements.
originalBody statements last isReturn
ifFalse:
[originalBody
statements:
(originalBody statements copyWith: (RBPatternParser parseExpression: '`@.postamble.'))].
currentMethod := RBParser parseMethod: each object getSource.
(ParseTreeSearcher
treeMatching: originalBody formattedCode
in: currentMethod)
ifNotNil: [amble := amble + 1]
ifNil: [unamble := unamble + 1]]]]].
Array with: unamble with: amble
What's it do? Basically we enumerate the multiple layers of the patched methods registry. We use the ParseTreeSearcher to see if the patching method, contains the body of the original method. We have to worry about fun things like whether a postamble can follow a return node, and whether people have their formatter set to emit trailing periods or not. Depending on wether it matches or not, we increment the amble of unamble count and return them so we can see what the ratio is.
A naive approach (that means it was my first attempt) is to use the DiffList facilities, and assume that if NO changes are registered in the original source, then it indicates a post/preamble candidate. But such misses the intermediate statement insertion.
Results
For my 7.7 development image I get a ratio of 61:32 (32 of the 93 patched methods in my system look like simple pre/postamble applications). I asked a friend to run it on his 7.7 image with his Seaside application loaded in it: 100:42. Michael's 7.6 Seaside image is 100:41. Alan's Glorp image came in at 84:32 (which means he's probably got the same 32 as me, plus 23 new ones that don't fit the bill). James's blog server is at 75:47. James's Bottom Feeder image is 304:497.
So it appears to be applicable about one third of the time in all but BottomFeeder's case, where it rises to 5/8ths. And we probably have a Guiness World Book entry for the most number of overrides as well. A number of the core ones I find just need to be integrated. They are silly patches in the first place. I'll let the reader make his own judgment about whether this data speaks for or against the general idea of a formalized before and after mechanism.
I would--as always when I run these queries--be interested in what others' images return for a a ratio.
And now back to my Thanksgiving Day vacation from which I delurked from to write this.