Christoph Thiede uploaded a new version of Kernel to project The Treated Inbox: http://source.squeak.org/treated/Kernel-jar.1545.mcz
==================== Summary ====================
Name: Kernel-jar.1545 Author: jar Time: 28 December 2023, 7:35:53.222247 pm UUID: 467e9c01-0094-c141-9980-1703a639d5a5 Ancestors: Kernel-mt.1544
fix the "stepOver bug" (Cannot #stepOver '^2' in example '[^2] ensure: []')
This is a more conceptual solution and replaces the previous patch in Kernel-jar.1503 which departed from exact mimicking the VM behavior (thanks to Christoph for pointing this out).
Detailed description: The whole issue emerged probably in 2012 when #aboutToReturn:through: started using the `firstUnwindContext` argument, I guess as an optimization, and forced a starting point to searching for unwind contexts in the resumtion logic (#resumeEvaluating:through:) - which was inconsistent with inserting the ensure guard context **before** this starting point during simulation (by #runUntilErrorOrReturnFrom: called by stepping methods). This resulted in erroneous skipping over the inserted guard ensure context during stepOver simulation and the unexpected behavior.
The idea is for the resumption logic (#resumeEvaluating:through:) to recognize a simulated execution and search for a potential guard (i.e. #ensure:) context inserted during the simulation. I suggest using a `simulationFlag` tempVar in #ensure: which would be set in #return:from:, i.e. if, and only if, the `firstUnwindContext` is supplied in simulaton (i.e. potentially dangerous). When the resumption logic recognizes a simulation supplied `firstUnwindContext` it recomputes the real first unwind context at the moment and proceeds as expected.
=============== Diff against Kernel-mt.1544 ===============
Item was changed: ----- Method: BlockClosure>>ensure: (in category 'exceptions') ----- ensure: aBlock "Evaluate a termination block after evaluating the receiver, regardless of whether the receiver's evaluation completes. N.B. This method is *not* implemented as a primitive. Primitive 198 always fails. The VM uses prim 198 in a context's method as the mark for an ensure:/ifCurtailed: activation."
+ | complete returnValue simulationFlag | - | complete returnValue | <primitive: 198> returnValue := self valueNoContextSwitch. complete ifNil:[ complete := true. aBlock value. ]. ^ returnValue!
Item was changed: ----- Method: BlockClosure>>ifCurtailed: (in category 'exceptions') ----- ifCurtailed: aBlock "Evaluate the receiver with an abnormal termination action. Evaluate aBlock only if execution is unwound during execution of the receiver. If execution of the receiver finishes normally do not evaluate aBlock. N.B. This method is *not* implemented as a primitive. Primitive 198 always fails. The VM uses prim 198 in a context's method as the mark for an ensure:/ifCurtailed: activation." + | complete result simulationFlag | - | complete result | <primitive: 198> result := self valueNoContextSwitch. complete := true. ^result!
Item was added: + ----- Method: Context>>isSimulationFlagSet (in category 'controlling') ----- + isSimulationFlagSet + + ^self numTemps >= 3 and: [(self tempAt: 3) notNil]!
Item was changed: ----- Method: Context>>resumeEvaluating: (in category 'controlling') ----- resumeEvaluating: aBlock "Unwind thisContext to self and resume with value as result of last send. Execute unwind blocks when unwinding. ASSUMES self is a sender of thisContext"
+ ^self resumeEvaluating: aBlock through: thisContext! - ^self resumeEvaluating: aBlock through: nil!
Item was changed: ----- Method: Context>>resumeEvaluating:through: (in category 'controlling') ----- + resumeEvaluating: aBlock through: aContext - resumeEvaluating: aBlock through: firstUnwindCtxtOrNil "Unwind thisContext to self and resume with value as result of last send. Execute unwind blocks when unwinding. ASSUMES self is a sender of thisContext."
self isDead ifTrue: [self cannotReturn: aBlock value to: self]. + (aContext isSimulationFlagSet ifTrue: [thisContext] ifFalse: [aContext]) + unwindTo: self safely: false. - (firstUnwindCtxtOrNil ifNil: thisContext) unwindTo: self safely: false. thisContext terminateTo: self. ^aBlock value!
Item was changed: ----- Method: Context>>return:from: (in category 'instruction decoding') ----- return: value from: aSender "For simulation. Roll back self to aSender and return value from it. Execute any unwind blocks on the way. ASSUMES aSender is a sender of self"
| newTop | aSender isDead ifTrue: [^self send: #cannotReturn: to: self with: {value}]. newTop := aSender sender. (self findNextUnwindContextUpTo: newTop) ifNotNil: + [:firstUnwindContext | firstUnwindContext tempAt: 3 put: true. "simulation flag" + ^self send: #aboutToReturn:through: to: self with: {value. firstUnwindContext}]. - "Send #aboutToReturn:through: with nil as the second argument to avoid this bug: - Cannot #stepOver '^2' in example '[^2] ensure: []'. - See http://lists.squeakfoundation.org/pipermail/squeak-dev/2022-June/220975.html" - [^self send: #aboutToReturn:through: to: self with: {value. nil}]. self releaseTo: newTop. newTop ifNotNil: [newTop push: value]. ^newTop!
packages@lists.squeakfoundation.org