The Artima Developer Community
Sponsored Link

Agile Buzz Forum
Super is as Super does

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
James Robertson

Posts: 29924
Nickname: jarober61
Registered: Jun, 2003

David Buck, Smalltalker at large
Super is as Super does Posted: Sep 25, 2007 2:57 PM
Reply to this message Reply

This post originated from an RSS feed registered with Agile Buzz by James Robertson.
Original Post: Super is as Super does
Feed Title: Travis Griggs - Blog
Feed URL: http://www.cincomsmalltalk.com/rssBlog/travis-rss.xml
Feed Description: This TAG Line is Extra
Latest Agile Buzz Posts
Latest Agile Buzz Posts by James Robertson
Latest Posts From Travis Griggs - Blog

Advertisement

Periodically, someone posts a message asking how they do super super send (this would be a message send where searching for the implementation would not start at one superclass up, but two superclasses up). In Smalltalk proper you can't. There are probably actually useful cases where it would be handy, but concensus seems to be that the complexity isn't worth the payback.

I admit, now and then again, I find myself wanting this. For example, I was recently playing with a subclass of the Trippy DictionaryInspector. I needed to modify a statement in the middle of the refresh method. The "right way" long term, is to refactor said method in the DictionaryInspector so that I can easily provide the appropriate subclass override. But at the time, I wasn't sure I wanted to keep going down this path. I was just exploring/prototyping. And it would have been really nice to just copy the method, change it, and replace the super refresh with a super super refresh.

So I figured out how to do the equivalent. But after the previous SuperPower post, I thought it'd be fun to figure out how to implement this generically. So I took a 30 minute tangent and published the package SuperSuper to the public/open repository. What's it do? It's chuck full of those "questionable" things to begin with.

Firstly it adds a message #super to Object. Doing so returns a proxy object (SuperSend) wrapped around the original object. The purpose of the proxy is to act as a "trampoline", forwarding the messages it intercepts on to the receiver, and in the process controlling precisely which superclass implementation is used. Subclassing nil is the first superpower invoked here.

The code also has example of how to create objects without sending messages to them at all to build up their initial state. We want to keep this trampoline object very lightweight so that it can forward just about anything. So one trick we play in the instance creation methods, is to do it like so:

SuperSend class>>self: anObject
	^(Array
		with: anObject
		with: anObject class superclass) changeClassTo: self
Doing so, means we don't have to have any methods on the instance side to help us get our variables set. That helps keep it lightweight.

The common thing to do with a nil subclass, is that message forwarding thing. So the superpower here is to play with doesNotUnderstand:. This is pretty common superpower. How it forwards the message though is kind of interesting:

SuperSend>>doesNotUnderstand: aMessage
	^((searchClass whichClassIncludesSelector: aMessage selector)
		compiledMethodAt: aMessage selector)
		valueWithReceiver: receiver
		arguments: aMessage arguments
The first two parts are just class object reflection (isn't it nice classes are real objects too?). Searching the class chain. But the last is fun. Did you know that you can take any method object and have it sent to any object, as long as the slots accessed by method are found in the target receiver? The latest versions of Trippy use this technique to bypass implementations of methods that it might question. An example of how weird you can be with this:
((Point compiledMethodAt: #theta) copy ) mclass: nil; valueWithReceiver: 4 -> 3 arguments: #()

Final superpower in this package. We want

	^self super yourself
to be different from
	^super super yourself
To do this we take advantage of the fact that we can grab the execution context of any code, and reflect about it. You can see this at the creation time of the trampoline:
Object>>super
	| context sendCode |
	context := thisContext sender.
	context changeToVPC.
	sendCode := context baseBytecodeAt: context pc - 2.
	^(sendCode == Kernel.OpcodePool.OpXSuper
		or: [sendCode == Kernel.OpcodePool.OpXXSuper])
		ifFalse: [SuperSend self: self]
		ifTrue: [SuperSend super: self]
Basically, we take a look at how we got to where we're at, and if the way we got to this message looks like it was a super send from the byte codes, we create the object differently than we would have otherwise (via the super: creator, which increments the searchClass one more).

Should you load this in your code and use it? Probably not. Or maybe so. Maybe you've got an object layout that really could use this, even after careful consideration of how you could refactor the code. For me, it was/is just nice to have it for prototyping purposes. I can use it to try some stuff, and if it works out, go throught the work of changing the code to make it work maintainably.

Read: Super is as Super does

Topic: How Stupid is DRM? Previous Topic   Next Topic Topic: Agile Open California: Wide Open Spaces

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use