In this series of blog-posts, we are explaining in more detail the Pharo features described in https://pharo.org/features.
The source code describes programs statically, but this perspective is insufficient if you want to fully understand the behavior of the computer system. The source code can, for example, describe a complex graph of data objects, although its concrete shape depends on input data and state of the application. To examine the real-time status of the system, you need to have tools that allow you to see the state of objects and relations between them. It is especially true if the program does not behave in an expected way, and you need to debug it. For modern development environments, to provide such tools for run-time analysis is an essential feature.
In Pharo, you can manipulate your program as any other objects. In fact programs are objects too. So to provide tools to examine your running program, Pharo needs to be able to inspect itself too. For this reason, this feature is named “reflection“. Pharo is a computer system that can examine and modify itself.
Pharo is truly strict in that and, on the level of the object memory, does not hide anything against itself. At the same time, it exposes all this information and capabilities to the programmers. So programmers can look inside every living object in the object memory, examine its state defined by instance variables, modify them and write their own tools to do the same. Of course, it does not limit this feature only on your objects: all the tools such as the browser are using reflection to introspect objects that are representing program itself (and Pharo itself).
The standard Pharo tools for examination of objects are named “inspectors“. An inspector exposes the internal structure of objects and, usually, you use multiple of them at the same time, because, in most cases, you want to see more than one object at once. It allows multiple inspectors to be opened on the same object too and each one can show a different aspect of the object. The inspectors use so-called pagers for diving into the internal structure of the objects – to inspect objects that are referenced by instance variables of the examined object. We will talk about inspectors more deeply in a separate article of this series.
Surprisingly, Pharo allows the opposite inspecting direction too. It is easy to know the objects that some object is referring but to know the objects that are referencing some object is hard, especially if this it should be fast. It is an extremely useful feature, mainly in cases when you are trying to find memory leaks.
Usually, a memory leak is a name for the state when you allocate a piece of memory explicitly and accidentally never free again while nobody has a pointer to it so it cannot be reused. In extreme but not uncommon case, it can cause exhaustion of available memory with all the nasty consequences. For managing the memory, Pharo uses automatic garbage collector, so it is safe of this kind of memory leaks, but it does not mean that it is safe from wasting memory. The garbage collector cleans only the objects that are not referenced by any other object, so if an object keeps a reference while it should not, it will not be garbage collected.
For detection and solution of such cases, Pharo offers several tools such as
ReferenceFinder that finds the shortest path of references between two objects. If the starting point is a root object of the garbage collector, you can detect why your object is not garbage-collected as expected.
You first need to know that you have some memory leak before you try to solve it. Pharo has the next reflective feature that enables it. It allows enumeration of living instances of a given class. So in a situation where you expect that a class should not have any instance, you can easily prove it. We will talk about this feature in more details later too.
The short video we created for the demonstration of Pharo reflection, uses these techniques.
- First, we ask the class
SystemWindowto return some instance of it using a message with the unsurprising name:
someInstance. We open an inspector on the resulting object sending such instance the message inspect.
- Then we look at the internals of this object. We can see, for example, the rectangle that forms boundaries of the window
- We send a message
pointersToto this window. It returns a collection of all objects that have a direct reference to the window object and open another inspector on it. This collection has 73 items.
- We choose one of them. A graphical object (morph) that is part of the window and is responsible for resizing of the window by mouse in one direction.
- Then we look at its bounds, the Inspector dives to show an object that represents it. It is a rectangle that has two points named
corner. We click on the second one to see the internals of it. The point has two instance variables with coordinates
One basic rule of object-oriented programming declares that objects must be encapsulated. No-one has the rights to look at their internal structure or modify them. What we have demonstrated clearly violates this rule! Right?
Actually not. If the Inspector looks at an object private content or it modifies the instance variables directly, it does it by sending special messages to the object. These messages are
instVarNamed:put: and so on. The object itself can still determine how it will react on such messages or if it will implement them at all. It can, for example, return false information or pretend that the state of the object was changed while it was not.
It is just extremely convenient to implement such methods for all the objects correctly, so the programmer has a right and complete view on the state of the system so he can study, understand and change it quickly.
For programmers, it is great to have the system fully exposed, but what about users? It may be dangerous to give them the power to examine and change everything they want, and the approach of Pharo may look like a recipe for disaster. But, in reality, Pharo isn’t different from any other computer system. You just do not let the users or attackers execute any code they create.