Christoph Thiede uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-ct.1559.mcz
==================== Summary ====================
Name: Kernel-ct.1559
Author: ct
Time: 3 March 2024, 7:49:53.952958 pm
UUID: 12cceefa-01f6-fa46-917b-cc2ec6f91312
Ancestors: Kernel-ct.1558
Merges SimulationSideEffectWarning.5.cs (step 2/2):
In Context>>doPrimitive:method:receiver:args:, replaces generic "simulation guard" warnings for undebuggable methods by new specific, suppressable SimulationSideEffectWarnings. Also warns when primitive 87 (primitiveResume) is hit. Refactors <primitive: 19> simulation guards into a named alias pragma <simulationGuard>.
Revision: Revise documentation and document emergence and usage of SimulationSideEffectWarning in Context>>runSimulated:.... Fix resumption value of SimulationSideEffectWarning when proceeding from a debugger. Avoid infinite recursion due to process-faithful debugging when debugging a SimulationSideEffectWarning from a debugger. Improves tests.
Step 2/2: After the new <simulationGuard> pragma has been added to the compiler (Compiler-ct.497), we can use it in the tests and the ST80 package.
Thanks to Marcel (mt) for the advice! See: https://lists.squeakfoundation.org/archives/list/squeak-dev%40lists.squeakf…
=============== Diff against Kernel-ct.1558 ===============
Item was changed:
----- Method: Context class>>runSimulated: (in category 'simulation') -----
runSimulated: aBlock
+ "Simulate the execution of aBlock, until it ends or is curtailed. Answer the result it returns.
+
+ If aBlock attempts any action that escapes the control of the simulator (e.g., resuming another process), signal a SimulationSideEffectWarning. The client of the simulator (i.e., the sender of this method) may handle those warnings to remain control, or otherwise, these actions are allowed. Examples:
+ Context runSimulated: [[] fork]. --> fork is executed
+ [Context runSimulated: [[] fork]]
+ on: SimulationSideEffectWarning
+ do: [:ex |
+ ex isControlPrimitive ifTrue: [ex return: #forbidden].
+ ex pass]. --> fork is prevented"
- "Simulate the execution of aBlock, until it ends or is curtailed. Answer the result it returns."
^thisContext
runSimulated: aBlock
contextAtEachStep: []
"Context runSimulated: [Pen new ifNotNil: [:pen| pen defaultNib: 5. 4 timesRepeat: [pen go: 100; turn: 90]]]"
"Here's a fun example, reaching into the computation to squash the Display>>fillWhite that mandala: begins with..."
"thisContext
runSimulated: [Pen new mandala: 45]
contextAtEachStep: [:ctxt| ctxt selector == #fillWhite ifTrue: [ctxt scanFor: [:ign| ctxt willReturn]]]"!
Item was changed:
----- Method: Context>>doPrimitive:method:receiver:args: (in category 'private') -----
doPrimitive: primitiveIndex method: meth receiver: receiver args: arguments
"Simulate a primitive method whose index is primitiveIndex. The simulated receiver and
arguments are given as arguments to this message. If successful, push result and return
resuming context, else ^ {errCode, PrimitiveFailToken}. Any primitive which provokes
execution needs to be intercepted and simulated to avoid execution running away."
| value |
+ "Test for unsimulatable side effects (that is, code that will be triggered in the image outside of the simulator range). This includes simulation guards, which are traditionally flagged using primitive 19 (a null primitive that doesn't do anything), as well as certain control primitives that might trigger code on other processes. If a side effect is detected, raise a warning to give the user/client a chance to cancel or virtualize the operation."
+ "#(19 87) do: [:primitive | self systemNavigation browseAllSelect: [:m | m primitive = primitive]]"
+ (primitiveIndex = 19 "simulationGuard" or: [primitiveIndex = 87 "primitiveResume"]) ifTrue:
+ [[(SimulationSideEffectWarning forPrimitive: primitiveIndex)
+ context: self method: meth receiver: receiver arguments: arguments;
+ signalIfSkipped: [^ self]]
+ ifCurtailed:
+ [self push: receiver "Cheap fix of the context's internal state. Note that unwinding the receiver -- so that the next step would invoke the primitive again -- would be challenging due to to the variety of senders to this method."]].
- "Judicious use of primitive 19 (a null primitive that doesn't do anything) prevents
- the debugger from entering various run-away activities such as spawning a new
- process, etc. Injudicious use results in the debugger not being able to debug
- interesting code, such as the debugger itself. Hence use primitive 19 with care :-)"
- "SystemNavigation new browseAllSelect: [:m| m primitive = 19]"
- primitiveIndex = 19 ifTrue: [
- [self notify: ('The code being simulated is trying to control a process ({1}). Process controlling cannot be simulated. If you proceed, things may happen outside the observable area of the simulator.' translated format: {meth reference})]
- ifCurtailed: [self push: nil "Cheap fix of the context's internal state"]].
((primitiveIndex between: 201 and: 222)
and: [(self objectClass: receiver) includesBehavior: BlockClosure]) ifTrue:
[(primitiveIndex = 206
or: [primitiveIndex = 208]) ifTrue: "[Full]BlockClosure>>valueWithArguments:"
[^receiver simulateValueWithArguments: arguments first caller: self].
((primitiveIndex between: 201 and: 209) "[Full]BlockClosure>>value[:value:...]"
or: [primitiveIndex between: 221 and: 222]) ifTrue: "[Full]BlockClosure>>valueNoContextSwitch[:]"
[^receiver simulateValueWithArguments: arguments caller: self]].
primitiveIndex = 83 ifTrue: "afr 9/11/1998 19:50" "Object>>perform:[with:...]"
[| selector |
selector := arguments at: 1 ifAbsent:
[^ self class primitiveFailTokenFor: #'bad argument'].
^self send: selector to: receiver with: arguments allButFirst].
primitiveIndex = 84 ifTrue: "afr 9/11/1998 19:50 & eem 8/18/2009 17:04" "Object>>perform:withArguments:"
[| selector args |
arguments size = 2 ifFalse:
[^ self class primitiveFailTokenFor: #'bad argument'].
selector := arguments first.
args := arguments second.
args isArray ifFalse:
[^ self class primitiveFailTokenFor: #'bad argument'].
^self send: selector to: receiver with: args].
primitiveIndex = 100 ifTrue: "eem 8/18/2009 16:57" "Object>>perform:withArguments:inSuperclass:"
[| rcvr selector args superclass |
arguments size
caseOf: {
[3] -> [
rcvr := receiver.
selector := arguments first.
args := arguments second.
superclass := arguments third].
[4] -> ["mirror primitive"
rcvr := arguments first.
selector := arguments second.
args := arguments third.
superclass := arguments fourth] }
otherwise: [^ self class primitiveFailTokenFor: #'bad number of arguments'].
args isArray ifFalse:
[^ self class primitiveFailTokenFor: #'bad argument'].
((self objectClass: rcvr) includesBehavior: superclass) ifFalse:
[^ self class primitiveFailTokenFor: #'bad argument'].
^self send: selector to: rcvr with: args lookupIn: superclass].
"Mutex>>primitiveEnterCriticalSection
Mutex>>primitiveTestAndSetOwnershipOfCriticalSection"
(primitiveIndex = 186 or: [primitiveIndex = 187]) ifTrue:
[| effective |
effective := Processor activeProcess effectiveProcess.
"active == effective"
value := primitiveIndex = 186
ifTrue: [receiver primitiveEnterCriticalSectionOnBehalfOf: effective]
ifFalse: [receiver primitiveTestAndSetOwnershipOfCriticalSectionOnBehalfOf: effective].
^(self isPrimFailToken: value)
ifTrue: [value]
ifFalse: [self push: value]].
(primitiveIndex = 188 or: [primitiveIndex = 189]) ifTrue:
[| n args methodArg thisReceiver |
primitiveIndex caseOf:
{[188 "primitiveExecuteMethodArgsArray"] ->
["Object>>withArgs:executeMethod:
CompiledMethod class>>receiver:withArguments:executeMethod:
VMMirror>>ifFail:object:with:executeMethod: et al"
((n := arguments size) between: 2 and: 4) ifFalse:
[^self class primitiveFailTokenFor: #'unsupported operation'].
((self objectClass: (args := arguments at: n - 1)) == Array
and: [(self objectClass: (methodArg := arguments at: n)) includesBehavior: CompiledMethod]) ifFalse:
[^self class primitiveFailTokenFor: #'bad argument'].
thisReceiver := arguments at: n - 2 ifAbsent: [receiver]].
[189 "primitiveExecuteMethod"] ->
["Object>>executeMethod:
Object>>with:...executeMethod:"
(arguments size > 0) ifFalse:
[^self class primitiveFailTokenFor: #'bad argument'].
((self objectClass: (methodArg := arguments atLast: 1)) includesBehavior: CompiledMethod) ifFalse:
[^self class primitiveFailTokenFor: #'bad argument'].
args := arguments allButLast.
thisReceiver := receiver]}.
methodArg numArgs = args size ifFalse:
[^self class primitiveFailTokenFor: #'bad number of arguments'].
methodArg primitive > 0 ifTrue:
[methodArg isQuick ifTrue:
[^self push: (methodArg valueWithReceiver: thisReceiver arguments: args)].
^self doPrimitive: methodArg primitive method: methodArg receiver: thisReceiver args: args].
^self
activateMethod: methodArg
withArgs: args
receiver: thisReceiver].
primitiveIndex = 118 ifTrue: "[receiver:]tryPrimitive:withArgs:; avoid recursing in the VM"
[(arguments size = 3
and: [(self objectClass: arguments second) == SmallInteger
and: [(self objectClass: arguments last) == Array]]) ifTrue:
[^self doPrimitive: arguments second method: meth receiver: arguments first args: arguments last].
(arguments size = 2
and: [(self objectClass: arguments first) == SmallInteger
and: [(self objectClass: arguments last) == Array]]) ifFalse:
[^self class primitiveFailTokenFor: -3].
^self doPrimitive: arguments first method: meth receiver: receiver args: arguments last].
value := primitiveIndex = 120 "FFI method"
ifTrue: [(meth literalAt: 1) tryInvokeWithArguments: arguments]
ifFalse:
[primitiveIndex = 117 "named primitives"
ifTrue: [self tryNamedPrimitiveIn: meth for: receiver withArgs: arguments]
ifFalse: "should use self receiver: receiver tryPrimitive: primitiveIndex withArgs: arguments but this is only in later VMs (and appears to be broken)"
[receiver tryPrimitive: primitiveIndex withArgs: arguments]].
^(self isPrimFailToken: value)
ifTrue: [value]
ifFalse: [self push: value]!
Item was changed:
----- Method: Context>>runSimulated:contextAtEachStep: (in category 'system simulation') -----
runSimulated: aBlock contextAtEachStep: anotherBlock
+ "Simulate the execution of the argument, aBlock, until it ends or is curtailed. If any exception is signaled during the execution, simulate it being handled on the present caller stack. Evaluate anotherBlock with the current context prior to each instruction executed. Answer the simulated value of aBlock.
+
+ If aBlock attempts any action that escapes the control of the simulator (e.g., resuming another process), signal a SimulationSideEffectWarning. The client of the simulator (i.e., the sender of this method) may handle those warnings to remain control, or otherwise, these actions are allowed. Examples:
+ Context runSimulated: [[] fork]. --> fork is executed
+ [Context runSimulated: [[] fork]]
+ on: SimulationSideEffectWarning
+ do: [:ex |
+ ex isControlPrimitive ifTrue: [ex return: #forbidden].
+ ex pass]. --> fork is prevented"
- "Simulate the execution of the argument, aBlock, until it ends or is curtailed. If any exception is signaled during the execution, simulate it being handled on the present caller stack. Evaluate anotherBlock with the current context prior to each instruction executed. Answer the simulated value of aBlock."
| current resume ensure |
resume := false.
"Affect the context stack of the receiver during the simulation of aBlock."
current := aBlock asContextWithSender: self.
"Insert outer context denoting the end of the simulation."
ensure := (ensure := current) insertSender: (Context contextEnsure:
[resume := true.
ensure privSender: thisContext home sender]).
(anotherBlock numArgs = 0
ifTrue: ["optimized" [resume]]
ifFalse: ["stop execution on time, don't expose simulation details to caller"
[current == ensure or:
["Context >> #resume:"
current size >= 2 and:
[(current at: 2) == ensure]]] ])
whileFalse:
[anotherBlock cull: current.
current := current step].
"Continue with the execution in the previous context."
^ current jump!
Item was added:
+ Warning subclass: #SimulationSideEffectWarning
+ instanceVariableNames: 'primitiveIndex context method receiver arguments suppressed'
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Kernel-Exceptions'!
+
+ !SimulationSideEffectWarning commentStamp: 'ct 3/3/2024 19:17' prior: 0!
+ I am signaled to notify the client of the simulator (i.e., a sender of Context>>step) about potential side effects of the next instruction to be executed that would escape the control of the simulator. For example, I am signaled before the simulated code starts another process. See Context>>doPrimitive:method:receiver:args:, my messageText, and Parser>>simulationGuard for more information.!
Item was added:
+ ----- Method: SimulationSideEffectWarning class>>forPrimitive: (in category 'instance creation') -----
+ forPrimitive: primitiveIndex
+
+ ^ self new primitive: primitiveIndex!
Item was added:
+ ----- Method: SimulationSideEffectWarning>>arguments (in category 'accessing') -----
+ arguments
+
+ ^ arguments!
Item was added:
+ ----- Method: SimulationSideEffectWarning>>context (in category 'accessing') -----
+ context
+
+ ^ context!
Item was added:
+ ----- Method: SimulationSideEffectWarning>>context:method:receiver:arguments: (in category 'initialize-release') -----
+ context: aContext method: aCompiledMethod receiver: rcvr arguments: args
+
+ context := aContext.
+ method := aCompiledMethod.
+ receiver := rcvr.
+ arguments := args.!
Item was added:
+ ----- Method: SimulationSideEffectWarning>>defaultAction (in category 'priv handling') -----
+ defaultAction
+
+ self suppressed ifFalse: [super defaultAction].
+ self flag: #forLater. "When we support explicit exception handler invocation (e.g., #resume, #retry) from the debugger, this exception should publish a #resume handler rather than relying on the weakly defined proceed semantics of the debugger."
+ ^ self defaultResumeValue!
Item was added:
+ ----- Method: SimulationSideEffectWarning>>defaultResumeValue (in category 'defaults') -----
+ defaultResumeValue
+
+ ^ true!
Item was added:
+ ----- Method: SimulationSideEffectWarning>>isControlPrimitive (in category 'testing') -----
+ isControlPrimitive
+ "See StackInterpreter class>>#initializePrimitiveTable."
+
+ ^ self primitive between: 80 and: 89!
Item was added:
+ ----- Method: SimulationSideEffectWarning>>isSimulationGuard (in category 'testing') -----
+ isSimulationGuard
+ "See Parser >> #simulationGuard."
+
+ ^ self primitive = 19!
Item was added:
+ ----- Method: SimulationSideEffectWarning>>messageText (in category 'printing') -----
+ messageText
+
+ ^ messageText ifNil: [
+ 'The code being simulated is trying to control a process ({1}). {2}' translated format: {
+ self context method reference.
+ self isSimulationGuard
+ ifTrue: ['If you proceed, your image may be locked. Continue at own risk, and better save your image before.' translated]
+ ifFalse: ['Process controlling cannot be simulated. If you proceed, side effects may occur outside the observable area of the simulator.' translated]}]!
Item was added:
+ ----- Method: SimulationSideEffectWarning>>method (in category 'accessing') -----
+ method
+
+ ^ method!
Item was added:
+ ----- Method: SimulationSideEffectWarning>>primitive (in category 'accessing') -----
+ primitive
+
+ ^ primitiveIndex!
Item was added:
+ ----- Method: SimulationSideEffectWarning>>primitive: (in category 'initialize-release') -----
+ primitive: anInteger
+
+ primitiveIndex := anInteger.!
Item was added:
+ ----- Method: SimulationSideEffectWarning>>signalIfSkipped: (in category 'signaling') -----
+ signalIfSkipped: skipBlock
+
+ ^ self signal ifFalse: skipBlock!
Item was added:
+ ----- Method: SimulationSideEffectWarning>>skipPrimitive (in category 'handling') -----
+ skipPrimitive
+
+ ^ self resume: false!
Item was added:
+ ----- Method: SimulationSideEffectWarning>>suppress (in category 'accessing') -----
+ suppress
+
+ suppressed := true.!
Item was added:
+ ----- Method: SimulationSideEffectWarning>>suppressed (in category 'accessing') -----
+ suppressed
+
+ ^ suppressed ifNil: [self isSimulationGuard not]!
Item was added:
+ ----- Method: SimulationSideEffectWarning>>theReceiver (in category 'accessing') -----
+ theReceiver
+
+ ^ receiver!
Item was added:
+ ----- Method: SimulationSideEffectWarning>>unsuppress (in category 'accessing') -----
+ unsuppress
+
+ suppressed := false.!
Christoph Thiede uploaded a new version of Compiler to project The Trunk:
http://source.squeak.org/trunk/Compiler-ct.497.mcz
==================== Summary ====================
Name: Compiler-ct.497
Author: ct
Time: 3 March 2024, 7:35:18.153958 pm
UUID: 7ee52c4b-be72-c64f-bcb5-0607b502b9b5
Ancestors: Compiler-ct.496
Merges SimulationSideEffectWarning.5.cs (step 1/2):
In Context>>doPrimitive:method:receiver:args:, replaces generic "simulation guard" warnings for undebuggable methods by new specific, suppressable SimulationSideEffectWarnings. Also warns when primitive 87 (primitiveResume) is hit. Refactors <primitive: 19> simulation guards into a named alias pragma <simulationGuard>.
Revision: Revise documentation and document emergence and usage of SimulationSideEffectWarning in Context>>runSimulated:.... Fix resumption value of SimulationSideEffectWarning when proceeding from a debugger. Avoid infinite recursion due to process-faithful debugging when debugging a SimulationSideEffectWarning from a debugger. Improves tests.
Step 1/2: Prepare the parser to handle the new <simulationGuard> pragma (as well as unary pragma parsers in general).
Thanks to Marcel (mt) for the advice! See: https://lists.squeakfoundation.org/archives/list/squeak-dev%40lists.squeakf…
=============== Diff against Compiler-ct.496 ===============
Item was changed:
----- Method: Parser>>pragmaStatement (in category 'pragmas') -----
pragmaStatement
"Parse a pragma statement. The leading '<' has already been consumed. The 'here' token is the first one in the pragma. Use that token to dispatch to a custom pragma-parsing method if one can be found with a selector that matches it.
Note that custom pragma parsers need to fulfill two requirements:
- method selector must match the current token as simple getter,
e.g., <apicall: ...> matches #apicall or <primitive: ...> matches #primitive
- method must have pragma <pragmaParser> to be called."
"0) Early exit"
(hereType = #keyword or: [ hereType = #word or: [ hereType = #binary ] ])
ifFalse: [ ^ self expected: 'pragma declaration' ].
+
-
"1) Do not consider one-word pragmas such as <primitive> and <foobar>. Only keyword pragmas."
+ "2) Avoid interning new symbols for made-up pragmas such as #my for <my: 1 pragma: 2>."
+ (Symbol lookup: (here last == $: ifTrue: [here allButLast] ifFalse: [here]))
+ ifNotNil: [:parserSelector |
- here last == $: ifTrue: [
- "2) Avoid interning new symbols for made-up pragmas such as #my for <my: 1 pragma: 2>."
- (Symbol lookup: here allButLast) ifNotNil: [:parserSelector |
Parser methodDict at: parserSelector ifPresent: [:parserMethod |
"3) Only call methods that claim to be a custom pragma parser via <pragmaParser>."
(parserMethod hasPragma: #pragmaParser)
+ ifTrue: [^ self executeMethod: parserMethod]]].
+
- ifTrue: [^ self executeMethod: parserMethod]]]].
-
"X) No custom pragma parser found. Use the default one."
^ self pragmaStatementKeywords!
Item was added:
+ ----- Method: Parser>>simulationGuard (in category 'primitives') -----
+ simulationGuard
+ "primitive 19 is a null primitive that always fails. Just a marker for the simulator for methods that are typically undebuggable and could lock your image when simulated."
+ <pragmaParser>
+
+ self addPragma: (Pragma keyword: #primitive: arguments: #(19)).
+
+ self advance.
+ ^ true!
Christoph Thiede uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-ct.1558.mcz
==================== Summary ====================
Name: Kernel-ct.1558
Author: ct
Time: 2 March 2024, 8:30:30.372052 pm
UUID: b8e15dd5-2d06-b148-b83b-083c3e6aaaa9
Ancestors: Kernel-ct.1557
Repairs the comment in Object>>instVarNamed:put:.
=============== Diff against Kernel-ct.1557 ===============
Item was changed:
----- Method: Object>>instVarNamed:put: (in category 'system primitives') -----
instVarNamed: aString put: aValue
+ "Store the value into the instance variable in me with that name. Slow and unclean, but very useful. "
- "Store into the value of the instance variable in me of that name. Slow and unclean, but very useful. "
^self
instVarAt: (self class
instVarIndexFor: aString asString
ifAbsent: [self error: 'no such inst var' translated])
put: aValue
!
Christoph Thiede uploaded a new version of Tools to project The Trunk:
http://source.squeak.org/trunk/Tools-ct.1247.mcz
==================== Summary ====================
Name: Tools-ct.1247
Author: ct
Time: 2 March 2024, 8:28:46.009052 pm
UUID: 7ce4a4da-cc0f-1646-8421-a10fe85212de
Ancestors: Tools-ct.1246
Merges temputils.1.cs: Adds Context>>tempNamed: and Context>>tempNamed:put:.
Thanks to Eliot (emm) for the review!
=============== Diff against Tools-ct.1246 ===============
Item was added:
+ ----- Method: Context>>tempNamed: (in category '*Tools-debugger access') -----
+ tempNamed: aString
+ "Answer the value of the temporary variable in me with that name. Slower than #tempAt:, but very useful."
+
+ ^ self debuggerMap tempNamed: aString in: self!
Item was added:
+ ----- Method: Context>>tempNamed:put: (in category '*Tools-debugger access') -----
+ tempNamed: aString put: aValue
+ "Store the value into the temporary variable in me of that name. Slower than #tempAt:put:, but very useful."
+
+ ^ self debuggerMap
+ tempNamed: aString
+ put: aValue
+ in: self!
Item was added:
+ ----- Method: DebuggerMethodMap>>tempIndexFor:in:ifAbsent: (in category 'accessing') -----
+ tempIndexFor: tempName in: aContext ifAbsent: aBlock
+
+ ^ (self tempNamesForContext: aContext)
+ indexOf: tempName
+ ifAbsent: aBlock!
Item was added:
+ ----- Method: DebuggerMethodMap>>tempNamed:in: (in category 'accessing') -----
+ tempNamed: aString in: aContext
+ "Answer the value of the temporary variable in aContext with that name. Slower than Context>>tempAt:, but very useful."
+
+ ^ self
+ namedTempAt: (self
+ tempIndexFor: aString asString
+ in: aContext
+ ifAbsent: [self error: 'no such temp'])
+ in: aContext!
Item was added:
+ ----- Method: DebuggerMethodMap>>tempNamed:put:in: (in category 'accessing') -----
+ tempNamed: aString put: aValue in: aContext
+ "Store into the value of the temporary variable in aContext of that name. Slower than Context>>tempAt:put:, but very useful."
+
+ ^ self
+ namedTempAt: (self
+ tempIndexFor: aString asString
+ in: aContext
+ ifAbsent: [self error: 'no such temp'])
+ put: aValue
+ in: aContext!
Christoph Thiede uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-ct.1410.mcz
==================== Summary ====================
Name: Kernel-ct.1410
Author: ct
Time: 30 August 2021, 8:43:40.47076 pm
UUID: 46b520fa-b225-e349-8f11-a423359f0466
Ancestors: Kernel-eem.1408
Proposal: Makes IllegalResumeAttempt an error. Please find my argument in [1].
[1] http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-May/215520.html
=============== Diff against Kernel-eem.1408 ===============
Item was changed:
+ Error subclass: #IllegalResumeAttempt
- Exception subclass: #IllegalResumeAttempt
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Kernel-Exceptions'!
!IllegalResumeAttempt commentStamp: '<historical>' prior: 0!
This class is private to the EHS implementation. An instance of it is signaled whenever an attempt is made to resume from an exception which answers false to #isResumable.!
Item was removed:
- ----- Method: IllegalResumeAttempt>>defaultAction (in category 'handling') -----
- defaultAction
- "No one has handled this error, but now give them a chance to decide how to debug it. If none handle this either then open debugger (see UnhandedError-defaultAction)"
-
- UnhandledError signalForException: self!
Item was removed:
- ----- Method: IllegalResumeAttempt>>isResumable (in category 'handling') -----
- isResumable
-
- ^ false!
Item was removed:
- ----- Method: IllegalResumeAttempt>>readMe (in category 'comment') -----
- readMe
-
- "Never handle this exception!!"!
Christoph Thiede uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-ct.1442.mcz
==================== Summary ====================
Name: Kernel-ct.1442
Author: ct
Time: 3 January 2022, 9:25:00.293397 pm
UUID: 1c8eb404-f420-9f48-99fa-32c965fb5491
Ancestors: Kernel-mt.1441
Improves multilingual support in common error messages on Object.
Uploaded to the inbox because I am not sure whether any of these places should NOT invoke Get-Text to avoid infinite recursions. I already excluded the error selectors in the private category and #primitiveFailed: for this reason. How far do we want to go with multilingual support in low-level Kernel?
=============== Diff against Kernel-mt.1441 ===============
Item was changed:
----- Method: AdditionalMethodState>>copyWith: (in category 'copying') -----
copyWith: aPropertyOrPragma "<Association|Pragma>"
"Answer a copy of the receiver which includes aPropertyOrPragma"
| bs copy |
(Association == aPropertyOrPragma class
or: [Pragma == aPropertyOrPragma class]) ifFalse:
+ [self error: ('{1} instances should hold only Associations or Pragmas.' translated format: {self class name})].
- [self error: self class name, ' instances should hold only Associations or Pragmas.'].
"no need to initialize here; we're copying all inst vars"
copy := self class basicNew: (bs := self basicSize) + 1.
1 to: bs do:
[:i|
copy basicAt: i put: (self basicAt: i) shallowCopy].
copy basicAt: bs + 1 put: aPropertyOrPragma.
1 to: self class instSize do:
[:i| copy instVarAt: i put: (self instVarAt: i)].
^copy!
Item was changed:
----- Method: Object>>assert: (in category 'error handling') -----
assert: aBlock
"Throw an assertion error if aBlock does not evaluates to true."
+ aBlock value ifFalse: [AssertionFailure signal: 'Assertion failed' translated]!
- aBlock value ifFalse: [AssertionFailure signal: 'Assertion failed']!
Item was changed:
----- Method: Object>>backwardCompatibilityOnly: (in category 'error handling') -----
backwardCompatibilityOnly: explanationString
"Warn that the sending method has been deprecated. Methods that are tagt with #backwardCompatibility:
are kept for compatibility."
Deprecation
signalForContext: thisContext sender
+ message: ' (but will be kept for compatibility)' translated
- message: ' (but will be kept for compatibility)'
explanation: explanationString!
Item was changed:
----- Method: Object>>caseError (in category 'error handling') -----
caseError
"Report an error from an in-line or explicit case statement."
+ self error: ('Case not found ({1}), and no otherwise clause' translated format: {self printString})!
- self error: ('Case not found ({1}), and no otherwise clause' format: {self printString})!
Item was changed:
----- Method: Object>>doesNotUnderstand: (in category 'error handling') -----
doesNotUnderstand: aMessage
"Handle the fact that there was an attempt to send the given
message to the receiver but the receiver does not understand
this message (typically sent from the machine when a message
is sent to the receiver and no method is defined for that selector).
Raise the MessageNotUnderstood signal. If it is caught, answer
the result supplied by the exception handler. If it is not caught,
answer the result of resending the message within a guard for
infinite recursion. This allows, for example, the programmer to
implement the method and continue."
"Testing: (3 activeProcess)"
| exception resumeValue |
(exception := MessageNotUnderstood new)
message: aMessage;
receiver: self.
resumeValue := exception signal.
^exception reachedDefaultHandler "i.e. exception was not caught..."
ifTrue:
[[aMessage sentTo: self]
on: MessageNotUnderstood
do: [:ex|
(self == ex receiver
and: [aMessage hasIdenticalContentsAs: ex message]) ifFalse:
[ex pass].
+ self error: 'infinite recursion in doesNotUnderstand:' translated]]
- self error: 'infinite recursion in doesNotUnderstand:']]
ifFalse: [resumeValue]!
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>>shouldBeImplemented (in category 'error handling') -----
shouldBeImplemented
"Announce that this message should be implemented"
+ ^ NotImplemented signal: ('{1} or a superclass should implement {2}' translated format: {self className. thisContext sender selector})!
- ^ NotImplemented signal: ('{1} or a superclass should implement {2}' format: {self className. thisContext sender selector})!
Item was changed:
----- Method: Object>>shouldNotImplement (in category 'error handling') -----
shouldNotImplement
"Announce that, although the receiver inherits this message, it should
not implement it."
+ NotImplemented signal: ('{1} is not a message appropriate for a {2}' translated format: {thisContext sender selector. self className}).!
- NotImplemented signal: ('{1} is not a message appropriate for a {2}' format: {thisContext sender selector. self className}).!
Item was changed:
----- Method: Object>>subclassResponsibility (in category 'error handling') -----
subclassResponsibility
"This message sets up a framework for the behavior of the class' subclasses.
Announce that the subclass should have implemented this message."
^ SubclassResponsibility
signal: ('My {1} subclass should have overridden {2}'
+ translated format: {self className. thisContext sender selector}).!
- format: {self className. thisContext sender selector}).!
Item was changed:
----- Method: Object>>traitConflict (in category 'error handling') -----
traitConflict
+ self error: 'A class or trait does not properly resolve a conflict between multiple traits it uses.' translated!
- self error: 'A class or trait does not properly resolve a conflict between multiple traits it uses.'!
Christoph Thiede uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-ct.1557.mcz
==================== Summary ====================
Name: Kernel-ct.1557
Author: ct
Time: 2 March 2024, 6:57:26.957512 pm
UUID: 6f373fc9-2314-3c4b-b252-07eb1d5e1f4b
Ancestors: Kernel-ct.1556, Kernel-ct.1442, Kernel-ct.1410, Kernel-ct.1385
Merge commit.
Kernel-ct.1410:
Makes IllegalResumeAttempt an error.
Revision: Makes IllegalResumeAttempt resumable. See: http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-May/215520.html
Kernel-ct.1385 & Kernel-ct.1442:
Improves multilingual support in common error messages on Object & others.
=============== Diff against Kernel-ct.1556 ===============
Item was changed:
----- Method: AdditionalMethodState>>copyWith: (in category 'copying') -----
copyWith: aPropertyOrPragma "<Association|Pragma>"
"Answer a copy of the receiver which includes aPropertyOrPragma"
| bs copy |
(Association == aPropertyOrPragma class
or: [Pragma == aPropertyOrPragma class]) ifFalse:
+ [self error: ('{1} instances should hold only Associations or Pragmas.' translated format: {self class name})].
- [self error: self class name, ' instances should hold only Associations or Pragmas.'].
"no need to initialize here; we're copying all inst vars"
copy := self class basicNew: (bs := self basicSize) + 1.
1 to: bs do:
[:i|
copy basicAt: i put: (self basicAt: i) shallowCopy].
copy basicAt: bs + 1 put: aPropertyOrPragma.
1 to: self class instSize do:
[:i| copy instVarAt: i put: (self instVarAt: i)].
^copy!
Item was changed:
+ Error subclass: #IllegalResumeAttempt
- Exception subclass: #IllegalResumeAttempt
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Kernel-Exceptions'!
!IllegalResumeAttempt commentStamp: '<historical>' prior: 0!
This class is private to the EHS implementation. An instance of it is signaled whenever an attempt is made to resume from an exception which answers false to #isResumable.!
Item was removed:
- ----- Method: IllegalResumeAttempt>>defaultAction (in category 'handling') -----
- defaultAction
- "No one has handled this error, but now give them a chance to decide how to debug it. If none handle this either then open debugger (see UnhandedError-defaultAction)"
-
- UnhandledError signalForException: self!
Item was changed:
+ ----- Method: IllegalResumeAttempt>>isResumable (in category 'priv handling') -----
- ----- Method: IllegalResumeAttempt>>isResumable (in category 'handling') -----
isResumable
+
+ ^ true!
-
- ^ false!
Item was removed:
- ----- Method: IllegalResumeAttempt>>readMe (in category 'comment') -----
- readMe
-
- "Never handle this exception!!"!
Item was changed:
----- Method: Object>>assert: (in category 'error handling') -----
assert: aBlock
"Throw an assertion error if aBlock does not evaluates to true."
+ aBlock value ifFalse: [AssertionFailure signal: 'Assertion failed' translated]!
- aBlock value ifFalse: [AssertionFailure signal: 'Assertion failed']!
Item was changed:
----- Method: Object>>backwardCompatibilityOnly: (in category 'error handling') -----
backwardCompatibilityOnly: explanationString
"Warn that the sending method has been deprecated. Methods that are tagt with #backwardCompatibility:
are kept for compatibility."
Deprecation
signalForContext: thisContext sender
+ message: ' (but will be kept for compatibility)' translated
- message: ' (but will be kept for compatibility)'
explanation: explanationString!
Item was changed:
----- Method: Object>>caseError (in category 'error handling') -----
caseError
"Report an error from an in-line or explicit case statement."
+ self error: ('Case not found ({1}), and no otherwise clause' translated format: {self printString})!
- self error: ('Case not found ({1}), and no otherwise clause' format: {self printString})!
Item was changed:
----- Method: Object>>doesNotUnderstand: (in category 'error handling') -----
doesNotUnderstand: aMessage
"Handle the fact that there was an attempt to send the given
message to the receiver but the receiver does not understand
this message (typically sent from the machine when a message
is sent to the receiver and no method is defined for that selector).
Raise the MessageNotUnderstood signal. If it is caught, answer
the result supplied by the exception handler. If it is not caught,
answer the result of resending the message within a guard for
infinite recursion. This allows, for example, the programmer to
implement the method and continue."
"Testing: (3 activeProcess)"
| exception resumeValue |
(exception := MessageNotUnderstood new)
message: aMessage;
receiver: self.
resumeValue := exception signal.
^exception reachedDefaultHandler "i.e. exception was not caught..."
ifTrue:
[[aMessage sentTo: self]
on: MessageNotUnderstood
do: [:ex|
(self == ex receiver
and: [aMessage hasIdenticalContentsAs: ex message]) ifFalse:
[ex pass].
+ self error: 'infinite recursion in doesNotUnderstand:' translated]]
- self error: 'infinite recursion in doesNotUnderstand:']]
ifFalse: [resumeValue]!
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!
Item was changed:
----- Method: Object>>instVarNamed: (in category 'system primitives') -----
instVarNamed: aString
"Return the value of the instance variable in me with that name. Slow and unclean, but very useful. "
^ self instVarAt: (self class
instVarIndexFor: aString asString
+ ifAbsent: [self error: 'no such inst var' translated])
- ifAbsent: [self error: 'no such inst var'])
!
Item was changed:
----- Method: Object>>instVarNamed:put: (in category 'system primitives') -----
instVarNamed: aString put: aValue
"Store into the value of the instance variable in me of that name. Slow and unclean, but very useful. "
^self
instVarAt: (self class
instVarIndexFor: aString asString
+ ifAbsent: [self error: 'no such inst var' translated])
- ifAbsent: [self error: 'no such inst var'])
put: aValue
!
Item was changed:
----- Method: Object>>shouldBeImplemented (in category 'error handling') -----
shouldBeImplemented
"Announce that this message should be implemented"
+ ^ NotImplemented signal: ('{1} or a superclass should implement {2}' translated format: {self className. thisContext sender selector})!
- ^ NotImplemented signal: ('{1} or a superclass should implement {2}' format: {self className. thisContext sender selector})!
Item was changed:
----- Method: Object>>shouldNotImplement (in category 'error handling') -----
shouldNotImplement
"Announce that, although the receiver inherits this message, it should
not implement it."
+ NotImplemented signal: ('{1} is not a message appropriate for a {2}' translated format: {thisContext sender selector. self className}).!
- NotImplemented signal: ('{1} is not a message appropriate for a {2}' format: {thisContext sender selector. self className}).!
Item was changed:
----- Method: Object>>subclassResponsibility (in category 'error handling') -----
subclassResponsibility
"This message sets up a framework for the behavior of the class' subclasses.
Announce that the subclass should have implemented this message."
^ SubclassResponsibility
signal: ('My {1} subclass should have overridden {2}'
+ translated format: {self className. thisContext sender selector}).!
- format: {self className. thisContext sender selector}).!
Item was changed:
----- Method: Object>>traitConflict (in category 'error handling') -----
traitConflict
+ self error: 'A class or trait does not properly resolve a conflict between multiple traits it uses.' translated!
- self error: 'A class or trait does not properly resolve a conflict between multiple traits it uses.'!
Christoph Thiede uploaded a new version of Kernel to project The Treated Inbox:
http://source.squeak.org/treated/Kernel-ct.1405.mcz
==================== Summary ====================
Name: Kernel-ct.1405
Author: ct
Time: 15 May 2021, 3:56:24.713389 pm
UUID: 9a92be9b-d778-b54f-b659-713451a2ddb2
Ancestors: Kernel-nice.1402
Counterproposal to Kernel-jar.1404 for fixing VM crashes when resuming from a BlockCannotReturn. Instead of enforcing retrial, repair the context stack if the receiver has ended. There are two reasons for that:
1. Not in all situations, the receiver of #cannotReturn: is actually unable to resume. Consider this example for a disproof:
a := [true ifTrue: [^ 1]. 2].
"Both statements need to be executed separately in a Workspace so that [a outerContext sender] becomes nil!"
a value.
In this situation, it is valid to resume from BlockCannotReturn and currently also possible in the Trunk. Note that BlockCannotReturn even overrides #isResumable to answer true, though the class comment discrecommends resuming it.
2. The pattern proposed by Jaromir reminds me of the current implementation of Object >> #doesNotUnderstand: or Object >> #at:, that is, when the error was resumed, just try it again in the manner of a (potentially) infinite recursion. While the issue with infinite debuggers (which was eventually tripped by exactly this pattern) has been solved some time ago [1], I do not really agree with the pattern in general - it makes it unnecessarily hard for ToolSet implementors to consciously resume from an error instead of retrying it (which we have an extra selector on Exception for).
[1] http://forum.world.st/Please-try-out-Fixes-for-debugger-invocation-during-c…
=============== Diff against Kernel-nice.1402 ===============
Item was added:
+ ----- Method: BlockCannotReturn>>defaultResumeValue (in category 'defaults') -----
+ defaultResumeValue
+
+ ^ self result!
Item was changed:
----- Method: Context>>cannotReturn: (in category 'private-exceptions') -----
cannotReturn: result
+ closureOrNil ifNotNil: [
+ | resumptionValue |
+ resumptionValue := self cannotReturn: result to: self home sender.
+ self pc > self endPC ifTrue: [
+ "This block has ended, continue with sender"
+ thisContext privSender: self sender].
+ ^ resumptionValue].
- closureOrNil ifNotNil: [^ self cannotReturn: result to: self home sender].
Processor debugWithTitle: 'Computation has been terminated!!' translated full: false.!