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!
Christoph Thiede uploaded a new version of Kernel to project The Treated Inbox:
http://source.squeak.org/treated/Kernel-jar.1552.mcz
==================== Summary ====================
Name: Kernel-jar.1552
Author: jar
Time: 17 January 2024, 12:12:05.464149 am
UUID: c03919ae-db2f-1842-a906-a917a9fb1821
Ancestors: Kernel-mt.1551
for discussion (Christoph): remove code fragility.
Christoph observed disruptions during process terminations at random positions in a heavily multithreded environment.
The root cause has been tracked down to Context>>#contextEnsure. When a process gets terminated at the moment it's in the middle of inserting this ensure guard context the unwind procedure during the termination fails - precisely because the sender chain is momentarily in a disconnected state before being stitched back. The sequence building the ensure guard is replaced by a manual creation of the context.
This is just a first draft; if this is the right concept, the code will be polished before merging.
=============== Diff against Kernel-mt.1551 ===============
Item was changed:
----- Method: Context class>>contextEnsure: (in category 'special context creation') -----
contextEnsure: block
+ "Create an #ensure: context that is ready to return from executing its receiver."
- "Create an #ensure: context that is ready to return from executing its receiver.
+ ^(Context
+ sender: nil
+ receiver: nil
+ method: (BlockClosure>>#ensure:)
+ arguments: {block}) nextInstruction; nextInstruction; nextInstruction; yourself!
- As ctxt is *not* a top context as required by #jump, we need to put a (fake) return value (nil) on its stack. Otherwise, #jump will pop something different from the stack. Concretely, this caused the bug described in [1] (Scenario 1) because the latest stack top was the closure vector {chain}. This closure vector was accidently popped away so that in the final return statement, #pushRemoteTemp:inVectorAt: raised an error subscript bounds (because the next stack item was not variable). Read the linked bug report for more details.
-
- [1] http://forum.world.st/BUG-s-in-Context-control-jump-runUntilErrorOrReturnFr…"
-
- | ctxt chain |
- ctxt := thisContext.
- [chain := thisContext sender cut: ctxt.
- ctxt push: nil. "fake return value"
- ctxt jump] ensure: block.
- "jump above will resume here without unwinding chain"
- ^ chain!
Christoph Thiede uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-ct.1301.mcz
==================== Summary ====================
Name: Kernel-ct.1301
Author: ct
Time: 16 February 2020, 11:36:54.104878 pm
UUID: e766ce27-fd99-2c4b-ac94-408d4bde8d4f
Ancestors: Kernel-tonyg.1293
Documents the small but essential difference between #sendTo: and #sentTo:
Refactored against Kent's Type Suggesting Parameter Name idiom. Thanks to Chris for the reminder!
Committed again due to broken ancestry.
=============== Diff against Kernel-tonyg.1293 ===============
Item was changed:
----- Method: Message>>sendTo: (in category 'sending') -----
+ sendTo: receiverObject
+ "Answer the result of sending this message to receiverObject"
- sendTo: receiver
- "answer the result of sending this message to receiver"
+ ^ receiverObject perform: selector withArguments: args!
- ^ receiver perform: selector withArguments: args!
Item was changed:
----- Method: Message>>sentTo: (in category 'sending') -----
+ sentTo: receiverObject
+ "Answer the result of sending this message to receiver. Kind of private!! To send the message to a different receiver (for example, via #doesNotUnderstand:), use #sendTo: instead."
- sentTo: receiver
- "answer the result of sending this message to receiver"
+ ^ lookupClass == nil
+ ifTrue: [receiverObject perform: selector withArguments: args]
+ ifFalse: [receiverObject perform: selector withArguments: args inSuperclass: lookupClass]!
- lookupClass == nil
- ifTrue: [^ receiver perform: selector withArguments: args]
- ifFalse: [^ receiver perform: selector withArguments: args inSuperclass: lookupClass]!
Christoph Thiede uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-ct.1385.mcz
==================== Summary ====================
Name: Kernel-ct.1385
Author: ct
Time: 12 April 2021, 10:15:07.18369 pm
UUID: e2817a6e-1a1f-e246-aa66-2fa9289a7703
Ancestors: Kernel-nice.1384
Improves multilingual support for basic errors on Object.
=============== Diff against Kernel-nice.1384 ===============
Item was changed:
----- Method: Object>>error (in category 'error handling') -----
error
"Throw a generic Error exception."
+ ^ self error: 'Error!!' translated!
- ^self error: 'Error!!'.!
Item was changed:
----- Method: Object>>errorImproperStore (in category 'private') -----
errorImproperStore
"Create an error notification that an improper store was attempted."
+ ^ self error: 'Improper store into indexable object' translated!
- self error: 'Improper store into indexable object'!
Item was changed:
----- Method: Object>>errorNonIntegerIndex (in category 'private') -----
errorNonIntegerIndex
"Create an error notification that an improper object was used as an index."
+ ^ self error: 'Only integers should be used as indices' translated!
- self error: 'only integers should be used as indices'!
Item was changed:
----- Method: Object>>errorSubscriptBounds: (in category 'private') -----
errorSubscriptBounds: index
"Create an error notification that an improper integer was used as an index."
+ ^ self error: ('Subscript is out of bounds: {1}' translated format: {index})!
- self error: 'subscript is out of bounds: ' , index printString!
Christoph Thiede uploaded a new version of Tools to project The Trunk:
http://source.squeak.org/trunk/Tools-ct.1249.mcz
==================== Summary ====================
Name: Tools-ct.1249
Author: ct
Time: 3 March 2024, 8:10:31.669958 pm
UUID: d9867cc9-cad5-0a41-8b64-86f1fbb54fe3
Ancestors: Tools-ct.1248
Complements Kernel-ct.1559 (merges SimulationSideEffectWarning.5.cs, step 2/2).
Revision: Also handle label updates in debugger when resuming from a method with a possible primitive.
=============== Diff against Tools-ct.1248 ===============
Item was changed:
----- Method: Debugger>>handleLabelUpdatesIn:whenExecuting: (in category 'context stack menu') -----
handleLabelUpdatesIn: aBlock whenExecuting: aContext
"Send the selected message in the accessed method, and regain control
after the invoked method returns."
+
+ | originalProcess |
+ originalProcess := Processor activeProcess.
^aBlock
on: Notification
do: [:ex|
(ex tag isArray
and: [ex tag size = 2
and: [(ex tag first == aContext or: [ex tag first hasSender: aContext])]])
ifTrue:
[self labelString: ex tag second description.
ex resume]
ifFalse:
+ [ex pass]]
+
+ on: SimulationSideEffectWarning
+ do: [:ex |
+ ex isControlPrimitive ifTrue: [ex unsuppress].
+ ex resume:
+ "Avoid infinite recursion in Process>>effectiveProcess when debugging this warning, because the warning while the simulator was evaluating on behalf of the interrupted process."
+ (originalProcess
+ evaluate: [ex defaultAction]
+ onBehalfOf: nil)]!
- [ex pass]]!
Item was changed:
----- Method: Debugger>>tryRestartFrom: (in category 'context stack menu') -----
tryRestartFrom: context
"Try to restart from the initial state of the context.
Return whether an unwind error occurred."
+ | currentContext unwindError |
+ currentContext := self selectedContext.
+ currentContext := self
+ handleLabelUpdatesIn: [interruptedProcess popTo: context]
+ whenExecuting: currentContext.
+ unwindError := currentContext ~= context.
- | actualContext unwindError |
- actualContext := interruptedProcess popTo: context.
- unwindError := actualContext ~= context.
unwindError ifFalse: [
+ currentContext := self
+ handleLabelUpdatesIn: [interruptedProcess restartTop; stepToSendOrReturn]
+ whenExecuting: currentContext].
+ self resetContext: currentContext.
- actualContext := interruptedProcess restartTop; stepToSendOrReturn].
- self resetContext: actualContext.
^ unwindError!
Christoph Thiede uploaded a new version of ST80 to project The Trunk:
http://source.squeak.org/trunk/ST80-ct.298.mcz
==================== Summary ====================
Name: ST80-ct.298
Author: ct
Time: 3 March 2024, 7:56:11.711958 pm
UUID: fcb9f599-f766-3143-98b0-634c455acf9c
Ancestors: ST80-dtl.297
Complements Kernel-ct.1559 (merges SimulationSideEffectWarning.5.cs, step 2/2).
=============== Diff against ST80-dtl.297 ===============
Item was changed:
----- Method: ControlManager>>activeController: (in category 'accessing') -----
activeController: aController
+ "Set aController to be the currently active controller. Give the user control in it."
+ <simulationGuard>
+
- "Set aController to be the currently active controller. Give the user
- control in it."
- <primitive: 19> "Simulation guard"
activeController := aController.
(activeController == screenController)
ifFalse: [self promote: activeController].
activeControllerProcess :=
[activeController startUp.
self searchForActiveController] newProcess.
activeControllerProcess priority: Processor userSchedulingPriority.
activeControllerProcess resume!
Item was changed:
----- Method: ControlManager>>scheduleActive: (in category 'scheduling') -----
scheduleActive: aController
+ "Make aController be scheduled as the active controller. Presumably the active scheduling process asked to schedule this controller and that a new process associated this controller takes control. So this is the last act of the active scheduling process."
+ <simulationGuard>
+
- "Make aController be scheduled as the active controller. Presumably the
- active scheduling process asked to schedule this controller and that a
- new process associated this controller takes control. So this is the last act
- of the active scheduling process."
- <primitive: 19> "Simulation guard"
self scheduleActiveNoTerminate: aController.
Processor terminateActive!