Closed
Description
I have noticed a weird behavior that occurs when you delete an instvar from a class, but keep the accessors for it and call them.
Steps to reproduce
- Create a class
Dummy
- Add an instvar
myVar
- Add accessors
myVar
andmyVar
(^ myVar
andmyVar := anObject
, respectively) - Delete the instvar
myVar
- Open a workspace
- Execute
Dummy new myVar: 42.
- Print
Dummy new myVar
(note: this is a new instance, do not keep the instance from the previous step)
Expected behavior
One of the following:
- An error is shown in step 4 (something like The instVar
myVar
is still referenced in these 2 methods: ...) - An error is shown in step 6 (something like The instVar
myVar
does not exist) nil
is printed in step 7
Actual behavior
42
is printed in step 7
Analysis
The problem occurs because after step 4, the accessors use the bytecodes pushLitVar:
and popIntoLit:
, with the referenced literal being the global Undeclared associationAt: #myVar
. This results in the myVar:
call storing the 42
into the Association's value, which the myVar
call on an unrelated instance will read, because the method references the same (global) Association in its literals.
I have traced the reason for the use of these bytecodes:
Class>>compileAllFrom:
is called during step 4- During parsing,
Parser>>variable
is called - It doesn't find the variable
myVar
in scope - It calls
Parser>>correctVariable:interval:
- Because the recompilation of all methods is not interactive,
Encoder>>undeclared:
is called Encoder>>global:name:
is called withUndeclared associationAt: #myVar
- A
LiteralVariableNode
with the association as itskey
is returned - Afterwards, either an
AssignmentNode
(for the setter) or aReturnNode
(for the getter) is created - After parsing,
MethodNode>>generate:
is called - In the setter case,
AssignmentNode>>emitCodeForEffect:encoder:
callsLiteralVariableNode>>emitCodeForStorePop:encoder:
, which callsEncoder>>genStorePopLiteralVar:
- In the getter case,
ReturnNode>>emitCodeForValue:encoder:
(indirectly) callsLiteralVariableNode>>emitCodeForValue:encoder:
, which callsEncoder>>genPushLiteralVar:
- Both reference the global Association, which was previously written into the literals of the
Encoder