In the good old days, finding your way around the Smalltalk system was easy. There was Smalltalk, the system dictionary, and everything revolved around it. To build something like a browser, you would ask Smalltalk for its "organization" and get back a SystemOrganizer. The organizer could tell you all the categories, or you could ask it for a list classes within a category. Or the category for a given class. Life was simple, web standards didn't exist, and programmers knew what they were doing. Or so they tell us.
As of VisualWorks 7.3, categories are being phased out, and Smalltalk is no longer the answer to finding things. Some time ago I wrote a quick reply to an email asking how to do that, and it reminded me how less approachable this system has become. We will have to do address that in future, but the good news now is that this is something I can write about and revive this blog.
The starting point for browsing things in the image is the shared variable Store.Registry, which holds an instance of PundleAccess. That object understands some interesting messages, most importantly:
- packageNamed: aString
- bundleNamed: aString
These messages answer instances, or collections of instances, of BundleModel or PackageModel. These instances are bundles and packages in the image (as opposed to those in the repository). Some interesting messages they understand are:
- definesClass: aClass
- extendsClass: aClass
Selectors of the last three don't quite reveal their true intentions. Those methods do not answer classes, but rather ClassDescriptors (ClassExtensionDescriptors for class extensions). The difference is that a Class in an active "instantiatable" entity within an image, while a ClassDescriptor is a passive record representing a class that may or may not be present--though descriptors that come from PackageModels and BundleModels do represent "live" classes. To get the real class, send the message #actual to a ClassDescriptor.
Overall, except for some poorly chosen names, this looks like a reasonable model. Unfortunately, it turns out to be less solid once you get down to interfaces and responsibilities. For example, one would expect that a ClassExtensionDescriptor should be able to answer selectors, or MethodDefinitions, or CompiledMethods--something to report the methods it extends the class with. It can do nothing of the sort. One has to go back to PackageModel for that--and with a real class rather than with a descriptor, to boot. This is a less than natural distribution of responsibilities--but in any case, this is the lay of the land for those who are curious.
As an example to sum this up, here is a snippet that returns a collection of all MethodDefitions for all extension methods in 'Tools-Trippy'.
| extensions package | extensions := OrderedCollection new. package := Store.Registry packageNamed: 'Tools-Trippy'. package classesExtendedInPackage do: [:eachClass | extensions addAll: ((package definedSelectorsFor: eachClass actual) collect: [:eachSelector | MethodDefinition class: eachClass actual selector: eachSelector])]. ^extensions