SuperPower Lessons?

Mutating your Process, for Instance

May 29, 2009 19:01:42.164

Eliot Miranda writes a great and lengthy dissertation about the value of first class contexts. This is a recurring theme in Smalltalk. It's that everything is objects, all the way down, at least to a depth not found in peer OO languages. It's not just the contexts either.

This post shows how we do instance mutants in Smalltalk, and yet another example of first class everything.

I had a hard test to write the other day. It involved two processes and potential race condition involving interruptWith: and terminate, both Process APIs. It was meant to test some code that looked, simplified, like:

	watchedProcess := Processor activeProcess.
	alarm :=  [aDuration wait. watchedProcess interruptWith: ["something happens"]] forkAt: watchedProcess priority + 1.
	["do some stuff] ensure: [alarm terminate]
The problem was that I needed/wanted to emulate reliably the rare case where watchedProcess was finished, but there was enough life left in alarm to still do the interrupt block. What I needed, for the duration of just this test, was for alarm terminate to not happen.

Smalltalk behaviors are just first class method dictionaries. Every object's class is one. These are usually ClassDescription objects, found through the normal directed graph of classes you see in your browser. But they don't need to be. Consider the following example:

| p |
p := 4 @ 3.
p negated
This produces the result of (-4 @ -3). But what if I wanted for some absurd reason to change Point behavior, not for the whole system, because likely things would come crashing down pretty fast, but just for that instance, for a bit. We could modify the code to read:
p := 4 @ 3.
p changeClassTo: (p class copy superclass: p class).
p class methodDictionary at: #negated put: (p class methodDictionary at: #transpose).
p negated
Now it returns (3@4) instead of the negated value. Just that instance. No other Point in the system will behave that way. Just the mutant one I made. What's going on? The first new line added, is how create a mutant instance. A copy is created of it's defining class, that copy is made a lightweight subclass of the original Point class we know about. We say it's lightweight because, Point doesn't know it as a subclass, so it won't show up in the system tools. And we change the instance class to be this no anonymous copy.

The third line, is then free to modify the new anonymous behavior, which it does by mucking with the methodDictionary directly, replacing the #negated method with the #transpose one.

Other than raising the hackles of the security minded, what, if anything is this good for? It can be handy for instrumenting things to discover behavior. It can be handy for temporarily "fudging" or "faking" a computation, for testing purposes, or other other code discovery. In the case of my test, it was handy for causing my alarm process behave a little differently than processes normally do, thus allowing me to reliably create what was otherwise a rare luck of the roll to get the timing right.

I was able to modify my test to read something like:

thatProcess := self codeToFindThatOneInstance.
thatProcess changeClassTo: (thatProcess class copy superclass: thatProcess class).
thatProcess class compile: 'terminate "do nothing"'.
....
Using this technique, I was able to force a process under test to take up some mutant behavior to cause the tested scenario to recur reliably. Because not only are contexts real objects, and classes first class objects, but so too are the very Processes themselves.

Comments

Wonderful post

[Hentai] May 30, 2009 13:22:19.154

That was great.  A wonderful explanation of Smalltalk's malleability and highly adaptive nature. One can easily mock objects using this technique for testing and debugging. Thank you for writing this.  Please present more ideas like this!

Re: Mutating your Process, for Instance

[ Terry] May 30, 2009 16:35:45.526

Comment by Terry

"p class copy superclass: p class" is asking for trouble with instance variables definition.

A Smalltalker turned Rubyist's View

[] May 30, 2009 19:37:38.951

Travis,

 

Great article.

 

The Ruby community often looks to Smalltalk for the source of ideas in Ruby.  I just wrote an article contrasting two aspects of Smalltalk and Ruby  http://talklikeaduck.denhaven2.com/2009/05/30/singleton-methods-in-smalltalk-and-ruby prompted by this article.

Yeah, no need for that superclass

[Randal L. Schwartz] May 30, 2009 20:24:39.550

p changeClassTo: (p class copy)

 

should have sufficed.  Leave the common superclass, so it becomes a sister class, not a child class.

[Adam Spitz] May 30, 2009 22:29:47.272

 

How hard would it be to enhance Inspectors to make it easy to create and see and edit these methods? (Right-click the inspector, choose the "add method" menu item, and type in the code the code for the new method. That sort of thing.)

(Or do Inspectors already do this? It's been a while since I used any Smalltalk other than Self. But my memory seems to think that Inspectors only showed instance variables, not methods.)

Of course, once you can program inside inspectors, it's easy to start wondering why you don't just abandon the class browser and forget about classes entirely... ;)

 

Re: Adam Spitz

[Travis Griggs] May 31, 2009 2:47:43.185

Adam, check out the movie in the previous blog entery. Jump to about 60% of the way through to get to a pointer where we show exactly that feature, being used to learn more and more about Floating Point representation.

Re: Randal Schwarz

[Travis Griggs] May 31, 2009 2:52:09.470

Randal, I admit to doing this out of pure habit/practice (there's an irony not at all lost on me, that in the next blog post, I'm talking about learning when our "preferred Smalltalk patterns" may not apply, and it's ok to embrace something different).

There's pros and cons both ways. If you don't do the superclass trick, then any new behavior that might be added, would not be available to your mutant instance. Indeed, in my specific case, it's not important either way. Just do it that way out of habbit. To really make it mutant, in fact, we'd chuck the mutant's method dictionary completely, so that not only an additions, but also any removals would be visible to the mutant. OTOH, you might really want a sibling mutant.

[Adam Spitz] June 1, 2009 16:12:39.869

That's great. :) Are there people who actually use that facility and spend all their time programming inside Inspectors, rather than using the regular class browsers?

I've never actually worked in a system that supported both styles - Self doesn't have anything like a "class browser", and if I remember correctly, Squeak (which is the Smalltalk I'm most familiar with) doesn't really have Inspectors that can do this kind of thing. So I'm very curious about which style people find more natural, when they work in a system that has both available.