Hi all, I know we are in feature freeze, but that won't stop me from having {fun with, new ideas for} Squeak :-)
So please have a look at the following mini-changeset. It implements #tempNamed: and #tempNamed:put: on Context, which I personally find quite useful when debugging and working with Context objects.
Print it:
thisContext sender tempNamed: #value put: 42
:-)
Feedback welcome as always!
Best,
Christoph
<http://www.hpi.de/>
A new version of KernelTests was added to project The Inbox:
http://source.squeak.org/inbox/KernelTests-ct.387.mcz
==================== Summary ====================
Name: KernelTests-ct.387
Author: ct
Time: 24 October 2020, 12:40:40.526058 am
UUID: aa73c56f-88e1-3b4d-9d59-fb29df3363a7
Ancestors: KernelTests-ul.386
Adds regression test for Kernel-ct.1355 (source logging after SyntaxError). Should I invest further complexity into this test for the purpose of keeping the production source logs clean?
=============== Diff against KernelTests-ul.386 ===============
Item was added:
+ ----- Method: ClassDescriptionTest>>testLogSourceAfterReparseSyntaxError (in category 'testing') -----
+ testLogSourceAfterReparseSyntaxError
+
+ | class source |
+ SystemChangeNotifier uniqueInstance doSilently: [[
+ class := Object newSubclass.
+ source := 'foo [x'.
+ [class compile: source]
+ on: SyntaxErrorNotification do: [:error |
+ error resume: (source := source , ']')].
+ self assert: source equals: (class sourceCodeAt: #foo) asString.
+ ] ensure: [class removeFromSystem]].!
Item was changed:
+ ----- Method: ClassDescriptionTest>>testOrganization (in category 'testing') -----
- ----- Method: ClassDescriptionTest>>testOrganization (in category 'tests') -----
testOrganization
| aClassOrganizer |
aClassOrganizer := ClassDescription organization.
self assert: (aClassOrganizer isKindOf: ClassOrganizer).!
Nicolas Cellier uploaded a new version of Kernel to project The Inbox:
http://source.squeak.org/inbox/Kernel-nice.1294.mcz
==================== Summary ====================
Name: Kernel-nice.1294
Author: nice
Time: 6 February 2020, 10:00:19.603883 pm
UUID: c78e67da-95e5-42ad-af82-089c149f165d
Ancestors: Kernel-tonyg.1293
Complex log function was missing (base 10 logarithm)
Complex already provides ln and log:, so why not log?
=============== Diff against Kernel-tonyg.1293 ===============
Item was added:
+ ----- Method: Complex>>log (in category 'mathematical functions') -----
+ log
+ "Answer the base 10 logarithm of the receiver."
+
+ ^self ln / 10 ln!
Hi all!
I rarely discovered that much errors with one single command :-)
Steps to reproduce
Print it:
(ObjectTracer on: true) ifTrue: [1]
Expected behavior
It is printed: 1
Actual behavior
The following tracer notifications appear (proceed them all):
1. (3x) #printStringLimitedTo:, #longPrintOn:limitedTo:indent: and again #printStringLimitedTo:, caused by SmalltalkImage >> #logSqueakError:inContext:
2. #mustBeBoolean
3. (3 more print message called by #logSqueakError:inContext: again, see the next error)
4. Error: Where's the jump?
Caused by #mustBeBooleanIn:, #skipBackBeforeJump
If you still keep proceeding:
1. (3 more print message called by #logSqueakError:inContext: again, see the next error)
2. Finally, the NonBooleanReceiver error from 2.
3. VM crashes.
Summarizing the bugs
ObjectTracer traces too much
The amount of messages printed by the ObjectTracer is an unintended side effect of the way it signals calls via the debugger. The debugger logs this error, including the stack trace. How can we avoid this? Should we suppress all further notifications from one ObjectTracer instance during the first one is debugged?
#mustBeBoolean is sent to boolean proxy
This is caused by the simulation of the inlined #ifTrue: call.
I don't know whether this is worth to be fixed before Scorch?
If yes, I think we would need to disable inlining for this particular selector or make inlining opt-out-able as proposed here (this would probably be way too slow)<http://forum.world.st/The-Inbox-HelpSystem-Core-ct-123-mcz-tp5105402p511363…>.
#mustBoBoolean depends on caller chain
It turns out that #mustBeBoolean relies on being called from a context that just did a jump. This makes it impossible to forward this message as usual via a transparent proxy/decorator.
We use this pattern in other methods as well. How can we enable transparent wrappers not to collide with method contexts depending on their sender?
Best,
Christoph
Hello,
Try dragging and dropping the Tile object into the PasteUp (World) and
try clicking on it. It disappears when clicked. The same is for Flaps.
Version 5.1 is the last one it worked correctly.
regards,
Vaidotas
Patrick Rein uploaded a new version of Collections to project The Trunk:
http://source.squeak.org/trunk/Collections-pre.857.mcz
==================== Summary ====================
Name: Collections-pre.857
Author: pre
Time: 4 October 2019, 11:04:30.363303 am
UUID: 5ef00b65-3884-c445-b276-0cc01f0b10a1
Ancestors: Collections-pre.856
Adds startOfHeader to Character, adds empty abstract implementations of scanFrom:, writeScanOn: to TextAttribute to allow for Texts which include TextAttributes which do not implement serialization to still be serialized, adds a comment to these methods.
=============== Diff against Collections-pre.856 ===============
Item was added:
+ ----- Method: Character class>>startOfHeader (in category 'accessing untypeable characters') -----
+ startOfHeader
+
+ ^ self value: 1 !
Item was added:
+ ----- Method: TextAttribute class>>scanFrom: (in category 'fileIn/Out') -----
+ scanFrom: strm
+ "Read the text attribute properties from the stream. When this method has
+ been called the concrete TextAttribute class has already been selected via
+ scanCharacter. (see TextAttribute class>>#newFrom:).
+ For writing the format see TextAttribute>>#writeScanOn:"!
Item was added:
+ ----- Method: TextAttribute>>writeScanOn: (in category 'fileIn/fileOut') -----
+ writeScanOn: strm
+ "Implement this method for a text attribute to define how it it should be written
+ to a serialized form of a text object. The form should correspond to the source
+ file format, i.e. use a scan character to denote its subclass.
+ As TextAttributes are stored in RunArrays, this method is mostly called from RunArray>>#write scan.
+ For reading the written information see TextAttribute class>>#scanFrom:"
+
+ "Do nothing because of abstract class"!
Hi all,
Tony has recently fixed our Promise implementation to make it more
Promises/A+ compliant, thank you!
In the discussion that lead to this fix [1], I already pointed out a
difference between exceptions in Smalltalk and in JavaScript, where
the Promises/A+ specification originates: Smalltalk exceptions can be
resumed, while JavaScript exceptions cannot be resumed and always
unroll the stack.
The spec [2] says that if the onFulfilled or onRejected callback of a
#then call on a promise throws an exception, then the promise returned
by the #then call shall be rejected with the exception thrown.
Our current Promise implementation matches this for the blocks
supplied to #then:, #ifRejected: or #then:ifRejected, by catching all
Errors in the blocks and rejecting the promise. But this does not
allow a Squeak user to deal with exceptions in a debugger if they are
signalled in the callbacks, because they are caught. The same also
applies to #future promises. The latter are not really covered by the
Promises/A+ spec (because it does not force the resolution or
rejection of a promise that is not the result of a #then, and there is
no #future in JavaScript), but futures exhibit the same problem of not
being resumable in the debugger. Example:
promise := 1 future / 0. "<= inspect it => promise is rejected,
regardless of your actions in the debugger"
Number compile: 'methodWithTypo ^ self asstring'.
promise := 1 future methodWithTypo. "<= inspect it => promise is
rejected, no chance to fix the misspelling of asString in the debugger
and proceed"
I could imagine instead letting all exceptions pass during the future
or callback block evaluation, and only reject the promise if the
evaluation is eventually curtailed due to the exception (be it an
Error or not, think of Warning or ModificationForbidden). Example
expectations:
promise := 1 future / 0. "<= inspect it, press Proceed in the
debugger, => promise is resolved"
promise := 1 future / 0. "<= inspect it, press Abandon in the
debugger, => promise is rejected"
promise := 1 future methodWithTypo. "<= inspect it, fix the typo of
asString in the debugger, proceed, => promise is resolved with '1'"
It could be done by fulfilling a Promise about aBlock similar to this:
[ self resolveWith: aBlock value ]
on: Exception
do: [ :ex | | resumed |
resumed := false.
[ | result |
result := ex outer.
resumed := true.
ex resume: result]
ifCurtailed: [resumed ifFalse: [self future rejectWith: ex]]]
(Find the current implementations here:
Promise>>#fulfillWith:passErrors: and Promise>>#then:ifRejected:)
Note that the #outer send would only trigger handlers in the
Project/World loop, or the defaultAction of the exception. The #future
in front of #rejectWith: is there to avoid curtailing the unwind block
context of ifCurtailed: itself if there are further errors in the
rejection callbacks of the promise. The behavior of non-local exits
from unwind contexts is undefined in the Smalltalk ANSI standard (just
like resume: or return: in a defaultAction, or not sending resume: or
return: in an on:do: exception handler at all -- VA Smalltalk
interprets that as resume, while Squeak does return, for example).
This implementation would also allow all deferred Notifications to
pass and not reject the promise. That is because true notifications
just resume silently if they are not handled.
promise := [Notification signal: 'hi there'. 42] future value. "<=
inspect it => Expected: resolved with 42. Actual (today): it is
needlessly rejected with Notification 'hi there'"
Pressing Proceed in the debugger on officially non-resumable errors
(which is possible) would also not reject the promise. But further
errors/debuggers are likely to appear, of which one may eventually be
used to abort the execution. If the execution finishes after
repeatedly pressing Proceed, then fine, resolve the promise with
whatever the outcome was.
promise := [self error: 'Fatal error'. 42] future value. "<= inspect
it, proceed after the so-called fatal error, => Expected: resolved
with 42. Actual: there is no debugger, the promise is immediately
rejected."
promise := [1 / 0 + 3] future value. "<= Cannot be resumed/proceeded
because if ZeroDivide is resumed, it will return the exception, and
ZeroDivide does not understand +, which cannot be resumed without
changing the code. So you'd have to curtail the block execution =>
Expected: rejected with ZeroDivide or MessageNotUnderstood (depending
on when you press Abandon or recompile the DoIt)."
promise := [1 / 0 + 3] future value. "... or instead of changing the
code or aborting, you could choose 'return entered value' in one of
the debuggers, and thereby complete the evaluation of the block =>
Expected: resolved with whatever you entered to return in the
debugger"
Promises with whenRejected:/ifRejected: callbacks would no longer
swallow errors, and would only be rejected when the user aborts in the
debuggers, or if the future execution catches errors by itself and
converts them to rejected promises, so the future promise will also be
rejected. This could pose a compatibility problem for existing code.
promise := (1 future / 0) then: [:result | result + 3] ifRejected:
[:reason | #cancelled]. "<= inspect it => Actual: resolved with
#cancelled immediately. Expected with my proposed changes: it would
first show the ZeroDivide debugger, which you can abandon to resolve
with #cancelled, or proceed to a MessageNotUnderstood +. If you
abandon the MNU, the promise would be rejected with the MNU, not
#cancelled, in accordance with the Promises/A+ spec."
How to get back a catch-all->reject-immediately future under these
circumstances:
promise := [[1 / 0] on: Error do: [:e | e return: (Promise new
rejectWith: e)]] future value.
promise := [1 future + 1 then: [:n | [n / 0] on: Error do: [:e | e
return: (Promise new rejectWith: e)]] future value.
We could also introduce a convenience constructor for
immediately-rejected promises like in JavaScript: Promise rejected: e.
Or a convenience exception handler: [...] rejectOn: Error. Or [...]
rejectIfCurtailed (the fullfill/then methods would probably use this
as well).
What do you think?
As Tom Beckmann has already suggested in the last thread on the topic
[1], I could also use a custom class of Promise to get just the
behavior I want. But then I cannot solve it for the use of #future. At
least not without patching something about the compiler in my package
preamble... ;-)
[1] http://lists.squeakfoundation.org/pipermail/squeak-dev/2020-April/208546.ht…
[2] https://promisesaplus.com/
Kind regards,
Jakob
A new version of Morphic was added to project The Inbox:
http://source.squeak.org/inbox/Morphic-ct.1718.mcz
==================== Summary ====================
Name: Morphic-ct.1718
Author: ct
Time: 24 January 2021, 5:22:59.352273 pm
UUID: f630cc0f-15e2-5642-94f6-6277bb8d7bd4
Ancestors: Morphic-mt.1710
PluggableTextMorph: Fixes #selectAll to keep the current scroll position intact, as it is best practice in most modern editor implementations such as Chromium.
I'm very open to refactoring proposals, but otherwise I don't care if you merge this as-is. :-)
=============== Diff against Morphic-mt.1710 ===============
Item was changed:
----- Method: Editor>>selectAll: (in category 'typing/selecting keys') -----
selectAll: aKeyboardEvent
"select everything, invoked by cmd-a. 1/17/96 sw"
self insertAndCloseTypeIn.
self selectFrom: 1 to: self string size.
+ morph skipOnceScrollSelectionIntoView.
^ true!
Item was changed:
----- Method: PluggableTextMorph>>scrollSelectionIntoView: (in category 'editor access') -----
scrollSelectionIntoView: event
"Scroll my text into view. Due to line composition mechanism, we must never use the right of a character block because the lines last character block right value always comes from a global container and is *not* line specific."
self rememberSelectionInterval.
+ (self valueOfProperty: #skipScrollSelectionIntoView ifAbsent: [false]) ifTrue: [
+ self removeProperty: #skipScrollSelectionIntoView.
+ ^ true].
+
textMorph editor hasSelection
ifFalse: [self scrollToShow: (textMorph editor startBlock withWidth: 1)]
ifTrue: [
self scrollToShow: (textMorph editor startBlock topLeft corner: textMorph editor stopBlock bottomLeft).
self scrollToShow: (textMorph editor pointBlock withWidth: 1). "Ensure text cursor visibility."].
^ true!
Item was added:
+ ----- Method: TextMorph>>skipOnceScrollSelectionIntoView (in category 'as yet unclassified') -----
+ skipOnceScrollSelectionIntoView
+
+ self owner owner setProperty: #skipScrollSelectionIntoView toValue: true.!