After we improved how thisContext and self is read reflectively, we should look 
at “super”.

Super is always interesting, as , at first, you think that it is some magic 
object “self but with the class of the superclass”, or something? Like a 
“perspective" on that object?

But it is much more trivial than that, and we can see that when we look at 
SuperVariable. This is one nice aspect of reflective implementations: you can 
just look at the code to understand...

So we know that in Pharo ‘super’ is not some hard-coded magic, but just a 
Variable, with an implementation in class SuperVariable. The Compiler delegates 
code generation to that object, and after [PR #13812] 
https://github.com/pharo-project/pharo/pull/13812, reading super in the 
debugger is using #readInContext: like temporary variables do.

So let’s see what super does regarding code generation.


```
SuperVariable>>#emitValue:
        "super references the receiver, send that follows is a super send (the 
message lookup starts in the superclass, see 
OCASTTranslator>>#emitMessageNode:)"
        methodBuilder pushReceiver
```

Who calls this? is is the OCASTTranslator, the visitor that takes a 
name-analyzed AST, visits that an calls methods on IRBuilder to create 
byte-code:


```
visitVariableNode: aVariableNode

        "Invalid variable should fail"
        aVariableNode variable isInvalidVariable ifTrue: [ ^ self 
emitRuntimeError: aVariableNode  ].

        "Because we are producing a CompiledMethod, register undeclared 
variables to `Undeclared`"
        aVariableNode variable isUndeclaredVariable ifTrue: [
                aVariableNode variable register ].

        aVariableNode variable emitValue: methodBuilder
```

So in the end, the OCASTTranslator just delegates code generation to the 
Variable. The self variable got set in the name analyzer, just like any other 
variable by looking it up in the current scope:

```
resolveVariableNode: aVariableNode
        | var |
        var := (scope lookupVar: aVariableNode name)
                ifNil: [ self undeclaredVariable: aVariableNode ].
        aVariableNode variable: var.
        ^var
``

lookupVar is sent to the current scope, but if the variable is not defined 
there, it goes to the parent scope until it reaches “Smalltalk gloabls”, which 
(in Pharo) knows all the pseudo variables:

```
SystemDictionary>>#lookupVar: name
        "Return a var with this name.  Return nil if none found"
        ^self reservedVariables at: name ifAbsent: [self bindingOf: name]
```

(we still call these “ReservedVariables”, but with this scheme they are not 
reserved at all, they are just variables of the global scope, just like 
“Object”. Shadowing rules apply (thus we can shadow these variables with a 
DoItVariable, to force reflective read as explained in 
https://blog.marcusdenker.de/improving-thiscontext-in-the-debugger-using-first-class-variables
 )


So how comes super sends are different if super is just read the same as self? 

The magic happens just for sends, as you can see in 
OCASTTranslator>>#emitMessageNode:


```
emitMessageNode: aMessageNode

        aMessageNode isCascaded ifFalse: [
                self visitNode: aMessageNode receiver].
        aMessageNode arguments do: [:each |
                self visitNode: each].
        aMessageNode isSuperSend
                ifTrue: [methodBuilder send: aMessageNode selector toSuperOf: 
aMessageNode superOf ]
                ifFalse: [methodBuilder send: aMessageNode selector]
```

with:

```
RBMessageNode>>#isSuperSend
        ^ self receiver isSuperVariable
```


So back to reflective read. As super is just self when reading, we need to 
implement readInContext: the same as in SelfVariable:

```
readInContext: aContext
        "super in a block is the receiver of the home context
        For clean blocks it might not be known (nil)"
        ^aContext home ifNotNil: [:home | home receiver ]
```

PR is here: https://github.com/pharo-project/pharo/pull/13878

Reply via email to