Pharo Superpower: Create Anonymous Class
One of the superpowers in Pharo Smalltalk is to create new classes at runtime. Actually, whenever you accept a class definition in the class browser that very definition is evaluated to create a new class. And since all development in Smalltalk happens eo ipso at runtime the accpeted class definition creates a class at runtime. Superpower for the masses, it can be done.
In this post, I shall cover how to create anonymous classes at runtime. As an example, we’ll create an anonymous subclass of point that extends Point with a color attribute.
To create an anonymous class, you’ll first need to create an anonymous metaclass. (Hey, nobody said superpowers ain’t confusing!) The newly created metaclass needs a superclass, a method dictionary and a magic format number. Computation of format numbers is explained later.
m := Metaclass new.
m superclass: Point class.
m methodDict: MethodDictionary new.
m setFormat: 156.
Now we can create the actual anonymous class. Classes are instances of their metaclass. For each metaclass there is only one instance, thus if you send #new twice an error is thrown. As above, the newly created class needs a superclass, a method dictionary and a magic format number.
c := m new.
c superclass: Point.
c methodDict: MethodDictionary new.
c setFormat: 136.
Now we can create an instance of the anonymous class. We’ll verify the new instance to check that it actually meets our expectations. (Please refer to Phexample for more information on expectation matchers.)
p := c x: 3 y: 4.
p asString should = '3@4'.
p class should = c.
p class class should = m.
p should beKindOf: Point.
Next we’ll create accessors for the additional instance variable of c, which shall be named color. And we’ll also override #printOn: to report the color.
c setInstVarNames: { 'color' }.
c compile: 'color
^color'.
c compile: 'color: aString
color := aString'.
c compile: 'printOn: out
super printOn: out.
out nextPutAll: '' is ''.
out nextPutAll: color'.
Again, we’ll verify our instance.
p color should = nil.
p color: #yellow.
p color should = #yellow.
p asString should = '3@4 is yellow'.
I promised to explain how to compute the magic numbers above. The format number encodes both the number of instance variables and the type of an object. For example, an object can be indexable or not. All this is stuffed into a 32-bit number. To compute the format number, we’ll use a method in ClassBuilder that does the bit-fiddling for us.
metaformat := ClassBuilder new
computeFormat: #normal instSize: 0
forSuper: Point class ccIndex: 0.
format := ClassBuilder new
computeFormat: #normal instSize: 1
forSuper: Point ccIndex: 0.
Important for us are the parameters instSize: and forSuper: which expect the number of instance variables and the superclass of the to be created class. Please note, the number of instance variables should not include inherited instance variables, but only the number of to be added instance variables: which is zero for our metaclass, and one for our colored point class.
Hackety hacking!

December 29th, 2009 at 06:06
[...] Random notes on software, programming and languages.By Adrian Kuhn Pharo Superpower: Create Anonymous Class [...]