The basic place where
Pocket Smalltalk starts is with the class PstSqueakProject.
This class is a subclass of the standard Project class that
exists within Squeak. Modifications were made to the base
Project class, see System
Modifications, in order to add support for entering and
exiting a project.
A Pocket Smalltalk
Project is created by:
PstSqueakProject
openNewMorphic
Please note that
what follows can seem pretty nasty, but what the hell it works.
When this newly created project is entered, the method entering
is called:
PstSqueakProject>>#entering
self swapPstProtoObjectSubclasses;
initializeEnvironmentIfNecessary;
setupGlobals;
setupPatchedMethods
swapPstProtoObjectSubclasses
This method takes
the existing subclasses of the class PstProtoObject and stores
them to restore them later, and replaces them with the classes
that exist in the project being entered. Yes plays around
with the subclasses instance variable of the class. All classes
created within a Pocket Smalltalk project are rooted in this
class. Unlike Squeak, where all classes are rooted by
nil. If you think this is bad, then don't read any further.
initializeEnvironmentIfNecessary
Create a new environment,
much like the Smalltalk global, that will keep track of all
the global variables and classes created within the project.
Note that even the class Object is not sacred. Pocket
Smalltalk has its own Object class, and this is supported
by these tricks.
The reason we can have Pocket Smalltalk classes and Squeak
classes with the same name, but potentially totally different
structures is a mind twister. When Smalltalk code containing
references to global variables is compiled, the resulting
code stores the association that exists in Smalltalk at the
time of compilation. What that means, is that if in
the future the Smalltalk dictionary, or current environment
is changed to be a new one, the old references stay.
This is why the Squeak code still runs, even though I place
a new environment. I had considered replacing the Smalltalk
variable with another, but too much code in the system depends
on this variable. So instead I created a subclass of
the compiler that requests the current project for its environment.
setupGlobals
Here I replace
some global variables with ones that suit my needs, but still
allow the system to function. The variables and their
corresponding values are as follows:
- ClassBuilder
with PstSqueakClassBuilder.
- ClassCategoryReader
with PstSqueakClassCategoryReader
- Compiler PstSqueakCompiler
- Parser PstSqueakParser
- SystemOrganization
with the PST project's system organization
- Utilities with
PstUtilities
Most of these global
variables are actually classes. Most of them are replaced
with specialized subclasses of the originals. These
subclasses take into account the fact that we are dealing
with a project with its own environment for compilation and
such. We did not want to modify system code, and therefore
created subclasses. This does pose a problem if the
class changes, but we will deal with those situations when
they arise. We could have used the method replacement
trick that follows for some of the cases, but this works,
we may revisit some of these and use the other trick.
setupPatchedMethods
As in the above
trick, where we replace global variables, here we replace
actual methods themselves. The cool trick here is that
the methods are replaced by ones implemented in PstSqueakProject
itself. Since the Squeak method lookup does not actually
use the selector (in fact there isn't one) that could be stored
in the CompiledMethod. It uses the key in the method
dictionary. So we the following methods quietly, no
change set or recompiling, simply replace one CompiledMethod
with another:
- SystemDictionary>>#environmentForCategory:
Return the
environment for the current project, instead of Smalltalk.
This change should be made in general to support environments.
- SystemDictionary>>#allBehaviorsDo:
Instead of
iterating over all of the classes in the system, the
replacement method goes through all of the methods in
the current PstProtoObject hierarchy. This works for
Pocket Smalltalk since PstProtoObject is guaranteed
to be the root of all classes.
There is an equivalent
restore method that restores the system to its original state.
Every replacement made above records the original value, so
that it can be restored upon project exit. Note that nested
Pocket Smalltalk projects are supported as well, and special
considerations in the initializeEnvironment method had to take
this situation into account.
Lastly, there were
a few System Modifications
that had to be made, but these are very minor and we may release
these as general fixes since they do not harm normal Squeak
operations.
Figuring this stuff
out was a bit of a problem. Realize that if any problems
are encountered on entering or exiting a project, that the Squeak
environment gets left in a not so good state. It took
a few tries to get this right. This is one of the reasons
why we STRONGLY recommend to save your main image before filing
this stuff in. If entering or exiting a Pocket Smalltalk
project breaks, then you might be left hanging without much
recourse.
Eric Arseneau
July 8, 2000