Christoph Thiede uploaded a new version of Kernel to project The Inbox:
http://source.squeak.org/inbox/Kernel-ct.1547.mcz
==================== Summary ====================
Name: Kernel-ct.1547
Author: ct
Time: 30 December 2023, 8:54:13.166665 pm
UUID: 60bc9f54-c12a-4545-9ff9-b3d2875784cd
Ancestors: Kernel-ct.1545
Proposal: When simulating #mustBeBoolean errors, send #mustBeBoolean instead of #mustBeBooleanIn: just like the VM. I don't see any harm in this.
=============== Diff against Kernel-ct.1545 ===============
Item was changed:
----- Method: Context>>jump:if: (in category 'instruction decoding') -----
jump: distance if: condition
"Simulate the action of a 'conditional jump' bytecode whose offset is the
argument, distance, and whose condition is the argument, condition."
| bool |
bool := self pop.
condition == bool
ifTrue: [self jump: distance]
ifFalse:
[(true == bool or: [false == bool]) ifFalse:
+ [^self send: #mustBeBoolean to: bool with: #()]]!
- [^self send: #mustBeBooleanIn: to: bool with: {self}]]!
Christoph Thiede uploaded a new version of Kernel to project The Inbox:
http://source.squeak.org/inbox/Kernel-ct.1546.mcz
==================== Summary ====================
Name: Kernel-ct.1546
Author: ct
Time: 30 December 2023, 8:52:22.257665 pm
UUID: 0c54d495-74a1-cd4e-8c0f-f95dec89f2a1
Ancestors: Kernel-ct.1545
In the debugger/simulator, fixes stepping through #mustBeBoolean errors and restarting from primitive methods that no longer fail. There is no need to throw up another debugger here. Just continue with the new context, and the client (i.e., the debugger) must honor the answer of #stepToSendOrReturn.
=============== Diff against Kernel-ct.1545 ===============
Item was changed:
----- Method: Context>>stepToSendOrReturn (in category 'system simulation') -----
stepToSendOrReturn
"Simulate the execution of bytecodes until either sending a message or
returning a value to the receiver (that is, until switching contexts)."
| ctxt |
[self willReallySend or: [self willReturn or: [self willReallyStore]]] whileFalse:
[ctxt := self step.
ctxt == self ifFalse:
+ ["Caused by mustBeBoolean handling or callPrimitive:"
+ ^ctxt stepToSendOrReturn]]!
- [self halt.
- "Caused by mustBeBoolean handling"
- ^ctxt]]!
A new version of KernelTests was added to project The Inbox:
http://source.squeak.org/inbox/KernelTests-jar.448.mcz
==================== Summary ====================
Name: KernelTests-jar.448
Author: jar
Time: 30 December 2023, 6:28:02.589852 pm
UUID: 058e235e-90d4-d648-9442-b468fee2bb2e
Ancestors: KernelTests-jar.447
Test if #return:from: correctly handles returns to dead or nil contexts.
=============== Diff against KernelTests-jar.447 ===============
Item was added:
+ ----- Method: ContextTest>>testBlockCannotReturn (in category 'tests') -----
+ testBlockCannotReturn
+ "Test if #return:from: correctly handles returns to dead or nil contexts."
+
+ | p |
+ p := [ thisContext pc: nil ] newProcess.
+ p runUntil: [:ctx | ctx method selector = #pc: and: [ctx sender isDead]].
+
+ "assert p stoped at the last instruction, i.e. a return, of #pc:
+ and is about to return to a dead context"
+ self assert: p suspendedContext method selector equals: #pc:.
+ self assert: p suspendedContext sender isDead.
+ self assert: p suspendedContext willReturn.
+ self assert: p suspendedContext atEnd.
+
+ p step. "step into a return to a dead sender"
+
+ self assert: p suspendedContext method selector = #cannotReturn:!
Christoph Thiede uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-jar.1539.mcz
==================== Summary ====================
Name: Kernel-jar.1539
Author: jar
Time: 28 November 2023, 1:07:51.739521 am
UUID: 7dfe0ed8-86ec-f54a-9b12-11c337e0e8e5
Ancestors: Kernel-jar.1538
fix a bug in runUntilErrorOrReturnFrom: that prevents removing the guard contexts if they are placed below the bottom context (or above a dead context). The bug is a part of a series of bugs described in detail in the post from 27 Nov 2023 at:
https://lists.squeakfoundation.org/archives/list/squeak-dev@lists.squeakfou…
This changeset requires and complements its ancestor Kernel-jar.1538
=============== Diff against Kernel-jar.1538 ===============
Item was changed:
----- Method: Context>>runUntilErrorOrReturnFrom: (in category 'controlling') -----
runUntilErrorOrReturnFrom: aSender
"ASSUMES aSender is a sender of self. Execute self's stack until aSender returns or an unhandled exception is raised. Return a pair containing the new top context and a possibly nil exception. The exception is not nil if it was raised before aSender returned and it was not handled. The exception is returned rather than openning the debugger, giving the caller the choice of how to handle it."
"Self is run by jumping directly to it (the active process abandons thisContext and executes self). However, before jumping to self we insert an ensure block under aSender that jumps back to thisContext when evaluated. We also insert an exception handler under aSender that jumps back to thisContext when an unhandled exception is raised. In either case, the inserted ensure and exception handler are removed once control jumps back to thisContext."
| error ctxt here topContext |
here := thisContext.
"Insert ensure and exception handler contexts under aSender"
error := nil.
ctxt := aSender insertSender: (Context
contextOn: UnhandledError do: [:ex |
error ifNil: [
error := ex exception.
topContext := thisContext.
ex resumeUnchecked: here jump]
ifNotNil: [ex pass]
]).
ctxt := ctxt insertSender: (Context
contextEnsure: [error ifNil: [
topContext := thisContext.
here jump]
]).
self jump. "Control jumps to self"
"Control resumes here once above ensure block or exception handler is executed"
^ error ifNil: [
"No error was raised, remove ensure context by stepping until popped"
+ [ctxt isDead or: [topContext isNil]] whileFalse: [topContext := topContext stepToCalleeOrNil].
- [ctxt isDead or: [topContext isNil]] whileFalse: [topContext := topContext stepToCallee].
{topContext. nil}
] ifNotNil: [
"Error was raised, remove inserted above contexts then return signaler context"
aSender terminateTo: ctxt sender. "remove above ensure and handler contexts"
{topContext. error}
]!
Item was added:
+ ----- Method: Context>>stepToCalleeOrNil (in category 'private') -----
+ stepToCalleeOrNil
+ "Step to callee or sender; step to return and answer nil in case sender cannot be returned to."
+
+ | ctxt |
+ ctxt := self.
+ [(ctxt willReturn and: [ctxt sender isNil or: [ctxt sender isDead]]) not and: [(ctxt := ctxt step) == self]] whileTrue.
+ ctxt == self ifTrue: [^nil].
+ ^ctxt!
Christoph Thiede uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-jar.1538.mcz
==================== Summary ====================
Name: Kernel-jar.1538
Author: jar
Time: 27 November 2023, 12:00:15.196233 am
UUID: d37b82ee-c73e-ed4b-be1c-478b708c5d9c
Ancestors: Kernel-jar.1537
fix stepOver bug when debugging e.g.
[[self halt. ^ 1] on: BlockCannotReturn do: [:ex | ex resume] ] fork
(step over halt and then stepover ^1 you get a nonsensical error as a result of decoding nil as an instruction)
the root cause is in the #return:from: method: it only checks whether aSender is dead but ignores the possibility that aSender sender may be nil or dead
This changeset requires its ancestor Kernel-jar.1537
(Please remove Kernel-jar.1535 from the Inbox)
=============== Diff against Kernel-jar.1537 ===============
Item was changed:
Error subclass: #BlockCannotReturn
+ instanceVariableNames: 'pc result deadHome finalContext'
- instanceVariableNames: 'pc result deadHome'
classVariableNames: ''
poolDictionaries: ''
category: 'Kernel-Exceptions'!
!BlockCannotReturn commentStamp: '<historical>' prior: 0!
This class is private to the EHS implementation. Its use allows for ensured execution to survive code such as:
[self doThis.
^nil]
ensure: [self doThat]
Signaling or handling this exception is not recommended.!
Item was added:
+ ----- Method: BlockCannotReturn>>finalContext (in category 'accessing') -----
+ finalContext
+
+ ^ finalContext!
Item was added:
+ ----- Method: BlockCannotReturn>>finalContext: (in category 'accessing') -----
+ finalContext: context
+
+ finalContext := context!
Item was changed:
----- Method: Context>>cannotReturn:to: (in category 'private') -----
cannotReturn: result to: homeContext
"The receiver tried to return result to homeContext that cannot be returned from.
+ Capture the return context/pc in a BlockCannotReturn. Nil the pc to prevent repeat
- Capture the return pc in a BlockCannotReturn. Nil the pc to prevent repeat
attempts and/or invalid continuation. Answer the result of raising the exception."
+ | exception previousPc |
- | exception |
exception := BlockCannotReturn new.
+ previousPc := pc ifNotNil: [self previousPc].
exception
result: result;
deadHome: homeContext;
+ finalContext: self;
+ pc: previousPc.
- pc: self previousPc.
pc := nil.
^exception signal!
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.
+ (aSender isDead or: [newTop isNil or: [newTop isDead]]) ifTrue:
+ [^self pc: nil; send: #cannotReturn: to: self with: {value}].
(self findNextUnwindContextUpTo: newTop) ifNotNil:
"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 Trunk:
http://source.squeak.org/trunk/Kernel-jar.1537.mcz
==================== Summary ====================
Name: Kernel-jar.1537
Author: jar
Time: 25 November 2023, 12:03:48.496163 am
UUID: 94c7c0ef-7a40-3149-b9df-0751d16ead54
Ancestors: Kernel-eem.1536
replace debugger invocation with an error; complements Kernel-eem.1535, Kernel-eem.1536 and KernelTests-jar.447.
=============== Diff against Kernel-eem.1536 ===============
Item was changed:
----- Method: Context>>cannotReturn: (in category 'private-exceptions') -----
cannotReturn: result
closureOrNil ifNotNil: [^ self cannotReturn: result to: self home sender].
+ self error: 'Computation has been terminated!!'!
- Processor debugWithTitle: 'Computation has been terminated!!' translated full: false.!
Christoph Thiede uploaded a new version of ToolsTests to project The Trunk:
http://source.squeak.org/trunk/ToolsTests-ct.125.mcz
==================== Summary ====================
Name: ToolsTests-ct.125
Author: ct
Time: 30 December 2023, 6:08:05.812543 pm
UUID: 4fea1ba5-b1d9-3947-8e2f-2f2ae82374bd
Ancestors: ToolsTests-mt.124
Complements Kernel-jar.1537.
=============== Diff against ToolsTests-mt.124 ===============
Item was changed:
----- Method: DebuggerTests>>test16HandleSimulationError (in category 'tests') -----
test16HandleSimulationError
"Regression test. In the past, the scenario below led to an infinite debugger chain freezing your image. Be careful when running this test in an unpatched image!! For more information, see http://forum.world.st/I-broke-the-debugger-td5110752.html."
| userProcess |
process := Process forBlock: [
+ "the faulty user code - representative for any kind of (yet) unhandled bug in the simulator "
+ thisContext pc: false].
- "the faulty user code"
- thisContext pc: nil].
debugger := process debug.
[userProcess := [debugger stepThrough] fork.
Processor yield.
self deny: [process isTerminated].
self assert: [userProcess isSuspended]]
ensure: [
process destroy.
debugger close.
process := userProcess.
self findDebugger "for correct tearDown"].!
Christoph Thiede uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-ct.1545.mcz
==================== Summary ====================
Name: Kernel-ct.1545
Author: ct
Time: 30 December 2023, 6:07:29.181543 pm
UUID: 4089c9dd-3a41-1240-8e53-b7a8a6fe1cae
Ancestors: Kernel-mt.1544, Kernel-jar.1539
Merges Kernel-jar.1537, Kernel-jar.1538, and Kernel-jar.1539, which make 'Computation has been terminated' a regular error, fix the simulation of non-local returns for dead senders, and store the final context in BlockCannotReturn exceptions.
=============== Diff against Kernel-mt.1544 ===============
Item was changed:
Error subclass: #BlockCannotReturn
+ instanceVariableNames: 'pc result deadHome finalContext'
- instanceVariableNames: 'pc result deadHome'
classVariableNames: ''
poolDictionaries: ''
category: 'Kernel-Exceptions'!
!BlockCannotReturn commentStamp: '<historical>' prior: 0!
This class is private to the EHS implementation. Its use allows for ensured execution to survive code such as:
[self doThis.
^nil]
ensure: [self doThat]
Signaling or handling this exception is not recommended.!
Item was added:
+ ----- Method: BlockCannotReturn>>finalContext (in category 'accessing') -----
+ finalContext
+
+ ^ finalContext!
Item was added:
+ ----- Method: BlockCannotReturn>>finalContext: (in category 'accessing') -----
+ finalContext: context
+
+ finalContext := context!
Item was changed:
----- Method: Context>>cannotReturn: (in category 'private-exceptions') -----
cannotReturn: result
closureOrNil ifNotNil: [^ self cannotReturn: result to: self home sender].
+ self error: 'Computation has been terminated!!'!
- Processor debugWithTitle: 'Computation has been terminated!!' translated full: false.!
Item was changed:
----- Method: Context>>cannotReturn:to: (in category 'private') -----
cannotReturn: result to: homeContext
"The receiver tried to return result to homeContext that cannot be returned from.
+ Capture the return context/pc in a BlockCannotReturn. Nil the pc to prevent repeat
- Capture the return pc in a BlockCannotReturn. Nil the pc to prevent repeat
attempts and/or invalid continuation. Answer the result of raising the exception."
+ | exception previousPc |
- | exception |
exception := BlockCannotReturn new.
+ previousPc := pc ifNotNil: [self previousPc].
exception
result: result;
deadHome: homeContext;
+ finalContext: self;
+ pc: previousPc.
- pc: self previousPc.
pc := nil.
^exception signal!
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.
+ (aSender isDead or: [newTop isNil or: [newTop isDead]]) ifTrue:
+ [^self pc: nil; send: #cannotReturn: to: self with: {value}].
(self findNextUnwindContextUpTo: newTop) ifNotNil:
"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!
Item was changed:
----- Method: Context>>runUntilErrorOrReturnFrom: (in category 'controlling') -----
runUntilErrorOrReturnFrom: aSender
"ASSUMES aSender is a sender of self. Execute self's stack until aSender returns or an unhandled exception is raised. Return a pair containing the new top context and a possibly nil exception. The exception is not nil if it was raised before aSender returned and it was not handled. The exception is returned rather than openning the debugger, giving the caller the choice of how to handle it."
"Self is run by jumping directly to it (the active process abandons thisContext and executes self). However, before jumping to self we insert an ensure block under aSender that jumps back to thisContext when evaluated. We also insert an exception handler under aSender that jumps back to thisContext when an unhandled exception is raised. In either case, the inserted ensure and exception handler are removed once control jumps back to thisContext."
| error ctxt here topContext |
here := thisContext.
"Insert ensure and exception handler contexts under aSender"
error := nil.
ctxt := aSender insertSender: (Context
contextOn: UnhandledError do: [:ex |
error ifNil: [
error := ex exception.
topContext := thisContext.
ex resumeUnchecked: here jump]
ifNotNil: [ex pass]
]).
ctxt := ctxt insertSender: (Context
contextEnsure: [error ifNil: [
topContext := thisContext.
here jump]
]).
self jump. "Control jumps to self"
"Control resumes here once above ensure block or exception handler is executed"
^ error ifNil: [
"No error was raised, remove ensure context by stepping until popped"
+ [ctxt isDead or: [topContext isNil]] whileFalse: [topContext := topContext stepToCalleeOrNil].
- [ctxt isDead or: [topContext isNil]] whileFalse: [topContext := topContext stepToCallee].
{topContext. nil}
] ifNotNil: [
"Error was raised, remove inserted above contexts then return signaler context"
aSender terminateTo: ctxt sender. "remove above ensure and handler contexts"
{topContext. error}
]!
Item was added:
+ ----- Method: Context>>stepToCalleeOrNil (in category 'private') -----
+ stepToCalleeOrNil
+ "Step to callee or sender; step to return and answer nil in case sender cannot be returned to."
+
+ | ctxt |
+ ctxt := self.
+ [(ctxt willReturn and: [ctxt sender isNil or: [ctxt sender isDead]]) not and: [(ctxt := ctxt step) == self]] whileTrue.
+ ctxt == self ifTrue: [^nil].
+ ^ctxt!