This course explores another advanced feature.
I want to show you how we can transform
the execution stack into an object, on demand.
The aim is to give you intuitive tools without going into details.
We can manipulate and navigate the stack,
and modify it, but we'll focus on navigating.
There are two chapters you can read on this subject.
They are the chapters on blocks
and exceptions in the book Deep into Pharo.
They're worth reading as they give clear explanations.
In Pharo, the Context class also represents the stack.
So,
we'll use a scenario that is already in your debugger.
Here, a message you sent is not found because you didn't define it,
while you execute a unitary test.
In the debugger, you answer that you want to create a method.
It compiles and installs it in the class.
Then it reexecutes it.
This method raises an exception since the system is not magic!
It sends you the shouldBeImplemented message
telling you to edit the method in the debugger.
You implement the method,
you recompile the method on the spot, then select "proceed"
to resume the program.
There are two key points in this scenario.
Firstly, we recompile the method on the fly.
Several times, but that's the compiler's job.
Secondly, and importantly, we modify the execution stack
in order to inject new chunks of stack,
which allow us to continue the program after an error.
It's not just a matter of reifying the stack, or making it an object.
It's not just a formal exercise, it can enhance user experience
and create new tools.
It's also used in Seaside to utilize web applications.
Pharo usually has a C stack, the virtual machine's own stack.
On demand, we can turn this into a live object.
What's interesting is that we can navigate and modify this object.
By modify, I mean that when we change this Pharo object,
it will change the implicit C stack, so it's very powerful.
It also supports all exceptions, so I recommend reading those chapters.
We navigate the stack for exceptions
to search for block catches,
known as exception handlers.
In addition, this ability to transform the stack into an object
allows us to create continuations
and web servers, such as for functional languages like Scheme.
To explain how this works, we can look at the variable
called thisContext, one of Pharo's three pseudovariables.
These are self, super, and thisContext.
When you ask for thisContext's value, it returns the execution stack.
That's what you see when you open the debugger.
The execution stack is displayed, based on thisContext.
As an exercise, you can define a method
in which you insert "halt."
The debugger will pop up, but you can also type "thisContext"
and an inspector will open on the execution stack itself.
Now I'll show you two examples
of how we use thisContext.
The first is deprecation, used when we want to change API.
As a programmer, we edit the method and use the message "deprecated"
with "on" and "in," as I explained in the course on exceptions.
Here we want to express the message "Use bar."
And what will the deprecation display?
It will show the user the message
"Message foo is deprecated in Pharo."
It's important to note that as a user,
I did not declare the method used,
but the system identified foo as the method caller.
As you can see, we don't use foo in the arguments of deprecated
or in the method caller.
So, how is this implemented?
The message "deprecated"
is an exception, "Deprecation."
The system takes the arguments, anExplanationString, and so on,
and adds the expression "thisContext sender method."
ThisContext is the stack at the time of execution of the deprecated method.
Now, using sender we can access the method caller.
That will give us foo, which was our example.
Then we ask for the method.
ThisContext sender method returns the compiled method,
which is an object,
named A>>foo.
So, the exception uses what it needs to be able to extract
the method selector and create a clearer message.
It's used to make messages easier for users to understand,
without forcing the programmer to hard-code the message source.
Now I'll show you another very powerful function.
Often when debugging, we want to insert a breakpoint
in a method that is heavily used.
You may simply want to debug your version of the program
without halting the whole system.
Conditions like "halt once" will interrupt the system once,
but what you want is to halt execution only if the method
has been called by another method.
How does a programmer express this?
We express that we only want to halt if foo has been called
by the method testSetInitialized.
How do we implement this?
Usually, this method must not be halted.
You can open the code in Pharo to see how it's implemented.
So, halt throws an exception
by showing the message "If:."
We have several argument options.
In a case involving a symbol, we pose a query to confirm it's true.
We look to see if the call chain contains the symbol.
Let's check.
Let's look at the method that comes up.
Suppose that we have a test that is named
testSetInitialized.
It's an argument here.
First, the method will retrieve the execution stack, or context,
which is a synonym for execution stack.
So we use Context to access the stack, then we add whileFalse.
Note that we're not at the top of the execution stack,
where there is no sender invoking us.
At the top of the stack, the sender is nil.
In a case where the sender is not nil,
we move between sections of the stack.
We can picture the areas of stack like this,
and we travel upwards using sender,
each time.
Now we need to see if the selector...
Somewhere in this area, we should see the test
asking the question
if this stack's call symbol matches where we intend to halt.
We ask if we have been called by testSet.
If so, we stop and mark a signal because the class is an exception.
It's important to understand that this is difficult to implement
in a language that does not reify the stack.
Here you see it in five lines, even if it seems hard to understand.
It's compact and powerful, and only possible through reification.
ThisContext is rarely used as it is an advanced feature.
But it is very important for new innovations,
such as tools, as shown by these tests,
which are facilitated by this language.
It's also used to represent continuations.
Seaside's creator used it in Pharo's ancestor
to manipulate the stack to represent continuations.
This is the basis of the call-and-answer mechanism in Seaside.
Here you have an advanced feature of Pharo to play around with.