Marcel Taeumel uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-ct.1287.mcz
==================== Summary ====================
Name: Kernel-ct.1287
Author: ct
Time: 13 December 2019, 7:01:04.635029 pm
UUID: b748ffbb-527a-3c42-8669-654a28068319
Ancestors: Kernel-mt.1286
Fixes selector classification with special category names, such as the all category or the null category.
Reproduce this issue by moving a method from one class into another and dropping it over the all category.
This bug might probably be the reason why we Utilities >> #fixUpProblemsWithAllCategory was introduced.
=============== Diff against Kernel-mt.1286 ===============
Item was changed:
----- Method: Categorizer>>addCategory:before: (in category 'accessing') -----
addCategory: catString before: nextCategory
"Add a new category named heading.
If default category exists and is empty, remove it.
If nextCategory is nil, then add the new one at the end,
otherwise, insert it before nextCategory."
| index newCategory |
newCategory := catString asSymbol.
(categoryArray indexOf: newCategory) > 0
ifTrue: [^self]. "heading already exists, so done"
+ (self isSpecialCategoryName: newCategory)
+ ifTrue: [^self inform: 'This category name is system reserved' translated].
index := categoryArray indexOf: nextCategory
ifAbsent: [categoryArray size + 1].
categoryArray := categoryArray
copyReplaceFrom: index
to: index-1
with: (Array with: newCategory).
categoryStops := categoryStops
copyReplaceFrom: index
to: index-1
with: (Array with: (index = 1
ifTrue: [0]
ifFalse: [categoryStops at: index-1])).
"remove empty default category"
(newCategory ~= Default
and: [(self listAtCategoryNamed: Default) isEmpty])
ifTrue: [self removeCategory: Default]!
Item was changed:
----- Method: Categorizer>>classify:under:suppressIfDefault: (in category 'classifying') -----
classify: element under: heading suppressIfDefault: aBoolean
"Store the argument, element, in the category named heading. If aBoolean is true, then invoke special logic such that the classification is NOT done if the new heading is the Default and the element already had a non-Default classification -- useful for filein"
| catName catIndex elemIndex realHeading |
+ realHeading := (heading isNil or: [self isSpecialCategoryName: heading])
+ ifTrue: [Default]
+ ifFalse: [heading asSymbol].
- ((heading = NullCategory) or: [heading == nil])
- ifTrue: [realHeading := Default]
- ifFalse: [realHeading := heading asSymbol].
(catName := self categoryOfElement: element) = realHeading
ifTrue: [^ self]. "done if already under that category"
+ catName ifNotNil: [
+ (aBoolean and: [realHeading = Default])
- catName ~~ nil ifTrue:
- [(aBoolean and: [realHeading = Default])
ifTrue: [^ self]. "return if non-Default category already assigned in memory"
self basicRemoveElement: element]. "remove if in another category"
(categoryArray indexOf: realHeading) = 0 ifTrue: [self addCategory: realHeading].
catIndex := categoryArray indexOf: realHeading.
elemIndex :=
catIndex > 1
ifTrue: [categoryStops at: catIndex - 1]
ifFalse: [0].
[(elemIndex := elemIndex + 1) <= (categoryStops at: catIndex)
and: [element >= (elementArray at: elemIndex)]] whileTrue.
"elemIndex is now the index for inserting the element. Do the insertion before it."
elementArray := elementArray copyReplaceFrom: elemIndex to: elemIndex-1
with: (Array with: element).
"add one to stops for this and later categories"
catIndex to: categoryArray size do:
[:i | categoryStops at: i put: (categoryStops at: i) + 1].
((categoryArray includes: Default)
and: [(self listAtCategoryNamed: Default) size = 0]) ifTrue: [self removeCategory: Default].
self assertInvariant.!
Item was added:
+ ----- Method: Categorizer>>isSpecialCategoryName: (in category 'testing') -----
+ isSpecialCategoryName: aName
+
+ ^ {self class nullCategory. self class allCategory}
+ includes: aName asSymbol!
Marcel Taeumel uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-ct.1296.mcz
==================== Summary ====================
Name: Kernel-ct.1296
Author: ct
Time: 27 January 2020, 12:59:15.057199 pm
UUID: e60f4637-b4fc-9947-94d3-50e7aa58ab82
Ancestors: Kernel-tonyg.1293
Fixes context simulation bug in #contextEnsure: and #contextOn:do:
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…
=============== Diff against Kernel-tonyg.1293 ===============
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"
| ctxt chain |
ctxt := thisContext.
+ [chain := thisContext sender cut: ctxt.
+ ctxt push: nil.
+ ctxt jump] ensure: block.
- [chain := thisContext sender cut: ctxt. ctxt jump] ensure: block.
"jump above will resume here without unwinding chain"
^ chain!
Item was changed:
----- Method: Context class>>contextOn:do: (in category 'special context creation') -----
contextOn: exceptionClass do: block
"Create an #on:do: context that is ready to return from executing its receiver"
| ctxt chain |
ctxt := thisContext.
+ [chain := thisContext sender cut: ctxt.
+ ctxt push: nil.
+ ctxt jump] on: exceptionClass do: block.
- [chain := thisContext sender cut: ctxt. ctxt jump] on: exceptionClass do: block.
"jump above will resume here without unwinding chain"
^ chain!
Marcel Taeumel uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-ct.1298.mcz
==================== Summary ====================
Name: Kernel-ct.1298
Author: ct
Time: 17 February 2020, 4:48:43.681432 pm
UUID: d57152c7-359a-054c-9dd7-c6353a6f700f
Ancestors: Kernel-tonyg.1293
Fixes a simulation bug regarding #doesNotUnderstand:. Don't forget to indicate the correct lookup class.
Steps to reproduce this bug:
1. Debug "42 foo"
2. Step over
The debugger label was: 'MessageNotUnderstood: nil>>foo'.
=============== Diff against Kernel-tonyg.1293 ===============
Item was changed:
----- Method: Context>>send:to:with:lookupIn: (in category 'controlling') -----
send: selector to: rcvr with: arguments lookupIn: lookupClass
"Simulate the action of sending a message with selector and arguments
to rcvr. The argument, lookupClass, is the class in which to lookup the
message. This is the receiver's class for normal messages, but for super
messages it will be some specific class related to the source method."
| meth primIndex val ctxt |
(meth := lookupClass lookupSelector: selector) ifNil:
[^self send: #doesNotUnderstand:
to: rcvr
+ with: {(Message selector: selector arguments: arguments) lookupClass: lookupClass}
- with: {Message selector: selector arguments: arguments}
lookupIn: lookupClass].
meth numArgs ~= arguments size ifTrue:
[^self error: 'Wrong number of arguments in simulated message ', selector printString].
(primIndex := meth primitive) > 0 ifTrue:
[val := self doPrimitive: primIndex method: meth receiver: rcvr args: arguments.
(self isPrimFailToken: val) ifFalse:
[^val]].
(selector == #doesNotUnderstand: and: [lookupClass == ProtoObject]) ifTrue:
[^self error: 'Simulated message ', arguments first selector, ' not understood'].
ctxt := Context sender: self receiver: rcvr method: meth arguments: arguments.
primIndex > 0 ifTrue:
[ctxt failPrimitiveWith: val].
^ctxt!
Marcel Taeumel uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-mt.1303.mcz
==================== Summary ====================
Name: Kernel-mt.1303
Author: mt
Time: 21 February 2020, 4:20:32.494211 pm
UUID: 0ef679e3-e47a-9945-b433-a81e5ecb8bf7
Ancestors: Kernel-ct.1287, Kernel-ct.1296, Kernel-ct.1298, Kernel-ct.1302, Kernel-eem.1296
Merges several smaller fixes from inbox. Thanks Christoph (ct)!
- fixes drag-drop methods between classes on the "--all--" category
- fixes two simulation bugs concerning #ensure: and #on:do:
- fixes ObjectTracer DNU
- fixes #readCarefullyFrom: in Object by correctly catching SyntaxErrorNotification
=============== Diff against Kernel-eem.1296 ===============
Item was changed:
----- Method: Categorizer>>addCategory:before: (in category 'accessing') -----
addCategory: catString before: nextCategory
"Add a new category named heading.
If default category exists and is empty, remove it.
If nextCategory is nil, then add the new one at the end,
otherwise, insert it before nextCategory."
| index newCategory |
newCategory := catString asSymbol.
(categoryArray indexOf: newCategory) > 0
ifTrue: [^self]. "heading already exists, so done"
+ (self isSpecialCategoryName: newCategory)
+ ifTrue: [^self inform: 'This category name is system reserved' translated].
index := categoryArray indexOf: nextCategory
ifAbsent: [categoryArray size + 1].
categoryArray := categoryArray
copyReplaceFrom: index
to: index-1
with: (Array with: newCategory).
categoryStops := categoryStops
copyReplaceFrom: index
to: index-1
with: (Array with: (index = 1
ifTrue: [0]
ifFalse: [categoryStops at: index-1])).
"remove empty default category"
(newCategory ~= Default
and: [(self listAtCategoryNamed: Default) isEmpty])
ifTrue: [self removeCategory: Default]!
Item was changed:
----- Method: Categorizer>>classify:under:suppressIfDefault: (in category 'classifying') -----
classify: element under: heading suppressIfDefault: aBoolean
"Store the argument, element, in the category named heading. If aBoolean is true, then invoke special logic such that the classification is NOT done if the new heading is the Default and the element already had a non-Default classification -- useful for filein"
| catName catIndex elemIndex realHeading |
+ realHeading := (heading isNil or: [self isSpecialCategoryName: heading])
+ ifTrue: [Default]
+ ifFalse: [heading asSymbol].
- ((heading = NullCategory) or: [heading == nil])
- ifTrue: [realHeading := Default]
- ifFalse: [realHeading := heading asSymbol].
(catName := self categoryOfElement: element) = realHeading
ifTrue: [^ self]. "done if already under that category"
+ catName ifNotNil: [
+ (aBoolean and: [realHeading = Default])
- catName ~~ nil ifTrue:
- [(aBoolean and: [realHeading = Default])
ifTrue: [^ self]. "return if non-Default category already assigned in memory"
self basicRemoveElement: element]. "remove if in another category"
(categoryArray indexOf: realHeading) = 0 ifTrue: [self addCategory: realHeading].
catIndex := categoryArray indexOf: realHeading.
elemIndex :=
catIndex > 1
ifTrue: [categoryStops at: catIndex - 1]
ifFalse: [0].
[(elemIndex := elemIndex + 1) <= (categoryStops at: catIndex)
and: [element >= (elementArray at: elemIndex)]] whileTrue.
"elemIndex is now the index for inserting the element. Do the insertion before it."
elementArray := elementArray copyReplaceFrom: elemIndex to: elemIndex-1
with: (Array with: element).
"add one to stops for this and later categories"
catIndex to: categoryArray size do:
[:i | categoryStops at: i put: (categoryStops at: i) + 1].
((categoryArray includes: Default)
and: [(self listAtCategoryNamed: Default) size = 0]) ifTrue: [self removeCategory: Default].
self assertInvariant.!
Item was added:
+ ----- Method: Categorizer>>isSpecialCategoryName: (in category 'testing') -----
+ isSpecialCategoryName: aName
+
+ ^ aName = self class nullCategory
+ or: [aName = self class allCategory]!
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.
+
+ 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.
- "Create an #ensure: context that is ready to return from executing its receiver"
+ [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.
- [chain := thisContext sender cut: ctxt. ctxt jump] ensure: block.
"jump above will resume here without unwinding chain"
^ chain!
Item was changed:
----- Method: Context class>>contextOn:do: (in category 'special context creation') -----
contextOn: exceptionClass do: block
+ "Create an #on:do: context that is ready to return from executing its receiver.
+
+ 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.
- "Create an #on:do: context that is ready to return from executing its receiver"
+ [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] on: exceptionClass do: block.
- [chain := thisContext sender cut: ctxt. ctxt jump] on: exceptionClass do: block.
"jump above will resume here without unwinding chain"
^ chain!
Item was changed:
----- Method: Context>>send:to:with:lookupIn: (in category 'controlling') -----
send: selector to: rcvr with: arguments lookupIn: lookupClass
"Simulate the action of sending a message with selector and arguments
to rcvr. The argument, lookupClass, is the class in which to lookup the
message. This is the receiver's class for normal messages, but for super
messages it will be some specific class related to the source method."
| meth primIndex val ctxt |
(meth := lookupClass lookupSelector: selector) ifNil:
[^self send: #doesNotUnderstand:
to: rcvr
+ with: {(Message selector: selector arguments: arguments) lookupClass: lookupClass}
- with: {Message selector: selector arguments: arguments}
lookupIn: lookupClass].
meth numArgs ~= arguments size ifTrue:
[^self error: 'Wrong number of arguments in simulated message ', selector printString].
(primIndex := meth primitive) > 0 ifTrue:
[val := self doPrimitive: primIndex method: meth receiver: rcvr args: arguments.
(self isPrimFailToken: val) ifFalse:
[^val]].
(selector == #doesNotUnderstand: and: [lookupClass == ProtoObject]) ifTrue:
[^self error: 'Simulated message ', arguments first selector, ' not understood'].
ctxt := Context sender: self receiver: rcvr method: meth arguments: arguments.
primIndex > 0 ifTrue:
[ctxt failPrimitiveWith: val].
^ctxt!
Item was added:
+ ----- Method: Object class>>basicReadFrom: (in category 'instance creation') -----
+ basicReadFrom: textStringOrStream
+ "Create an object based on the contents of textStringOrStream."
+
+ | object |
+ (Compiler couldEvaluate: textStringOrStream)
+ ifFalse: [^ self error: 'expected String, Stream, or Text' translated].
+ object := self environment beCurrentDuring: [
+ Compiler evaluate: textStringOrStream environment: self environment].
+ (object isKindOf: self) ifFalse: [self error: ('{1} expected' translated format: {self name})].
+ ^object!
Item was changed:
----- Method: Object class>>readCarefullyFrom: (in category 'instance creation') -----
readCarefullyFrom: textStringOrStream
"Create an object based on the contents of textStringOrStream. Return an error instead of putting up a SyntaxError window."
+ ^ [self basicReadFrom: textStringOrStream]
+ on: SyntaxErrorNotification
+ do: [:ex | self error: ex messageText]!
- | object |
- (Compiler couldEvaluate: textStringOrStream)
- ifFalse: [^ self error: 'expected String, Stream, or Text'].
- object := Compiler evaluate: textStringOrStream for: nil
- notifying: #error: "signal we want errors".
- (object isKindOf: self) ifFalse: [self error: self name, ' expected'].
- ^object!
Item was changed:
----- Method: Object class>>readFrom: (in category 'instance creation') -----
readFrom: textStringOrStream
"Create an object based on the contents of textStringOrStream."
+ ^ self basicReadFrom: textStringOrStream!
- | object |
- (Compiler couldEvaluate: textStringOrStream)
- ifFalse: [^ self error: 'expected String, Stream, or Text'].
- object := self environment beCurrentDuring: [Compiler evaluate: textStringOrStream environment: self environment].
- (object isKindOf: self) ifFalse: [self error: self name, ' expected'].
- ^object!
Item was removed:
- ----- Method: ObjectTracer class>>initialize (in category 'initialize-release') -----
- initialize
- "Fix for inconsistent image state in which ObjectTracer improperly appears as a subclass
- of Class. This initialization should appear in the Squeak update stream in order to repair
- existing images, and may be removed in a future update."
-
- Class removeSubclass: ObjectTracer class!
Item was changed:
----- Method: ObjectTracer>>doesNotUnderstand: (in category 'very few messages') -----
doesNotUnderstand: aMessage
+ "Present a debugger before proceeding to re-send the message"
- "All external messages (those not caused by the re-send) get trapped here"
- "Present a dubugger before proceeding to re-send the message"
+ "All external messages (those not caused by the re-send) get trapped here"
+ Warning signal: ('About to perform: {1}' translated format: {aMessage selector storeString}).
+ ^ aMessage sendTo: tracedObject!
- self notify: 'About to perform: ', aMessage selector.
- ^ aMessage sentTo: tracedObject.
- !
Marcel Taeumel uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-nice.1293.mcz
==================== Summary ====================
Name: Kernel-nice.1293
Author: nice
Time: 8 January 2020, 12:56:57.717854 am
UUID: 2a023bb6-8b85-41b9-bc59-fb247fc48658
Ancestors: Kernel-nice.1292
Connect the highBit primitive provided by new VM.
Since the primitive is jitted, it's about 3x faster than highBitOfPositiveReceiver.
[0 to: 1<<24 do: #highBit] bench.
'7.66 per second. 131 milliseconds per run. 0 % GC time.'
[0 to: 1<<24 do: #highBitOfPositiveReceiver] bench.
'2.59 per second. 386 milliseconds per run. 0 % GC time.'
It's even a bit faster than highBitOfByte.
[0 to: 255 do: #highBit] bench.
'472,000 per second. 2.12 microseconds per run. 0 % GC time.'
[0 to: 255 do: #highBitOfByte] bench.
'323,000 per second. 3.09 microseconds per run. 0 % GC time.'
Note: this has been tested on intel x86 and x64 architecture.
Please report the status on ARM (or mips).
=============== Diff against Kernel-nice.1292 ===============
Item was changed:
----- Method: SmallInteger>>highBit (in category 'bit manipulation') -----
highBit
"Answer the index of the high order bit of the receiver, or zero if the
receiver is zero. Raise an error if the receiver is negative, since
negative integers are defined to have an infinite number of leading 1's
in 2's-complement arithmetic. Use >>highBitOfMagnitude if you want to
get the highest bit of the magnitude."
+ <primitive: 575>
self < 0 ifTrue: [^ self error: 'highBit is not defined for negative integers'].
^ self highBitOfPositiveReceiver!
Item was changed:
----- Method: SmallInteger>>highBitOfMagnitude (in category 'bit manipulation') -----
highBitOfMagnitude
"Answer the index of the high order bit of the receiver, or zero if the
receiver is zero. This method is used for negative SmallIntegers as well,
since Squeak's LargeIntegers are sign/magnitude."
+ <primitive: 575>
+ self < 0 ifTrue: [^self negated highBit].
- self < 0 ifTrue: [
- "Beware: do not use highBitOfPositiveReceiver
- because self negated is not necessarily a SmallInteger
- (see SmallInteger minVal)"
- ^self negated highBitOfMagnitude].
-
- "Implementation note: this method could be as well inlined here."
^self highBitOfPositiveReceiver!
Marcel Taeumel uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-eem.1295.mcz
==================== Summary ====================
Name: Kernel-eem.1295
Author: eem
Time: 24 January 2020, 12:09:14.247651 pm
UUID: 1f1fc6c8-7160-4101-9c48-56ec5784616f
Ancestors: Kernel-eem.1294
Fix comment typo for perform:with:with:with:with:with:
=============== Diff against Kernel-eem.1294 ===============
Item was changed:
----- Method: Object>>perform:with:with:with:with:with: (in category 'message handling') -----
perform: aSymbol with: firstObject with: secondObject with: thirdObject with: fourthObject with: fifthObject
"Send the selector, aSymbol, to the receiver with the given arguments.
+ Fail if the number of arguments expected by the selector is not five.
- Fail if the number of arguments expected by the selector is not four.
Primitive. Optional. See Object documentation whatIsAPrimitive."
<primitive: 83>
^ self perform: aSymbol withArguments: { firstObject. secondObject. thirdObject. fourthObject. fifthObject }!
Marcel Taeumel uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-eem.1294.mcz
==================== Summary ====================
Name: Kernel-eem.1294
Author: eem
Time: 24 January 2020, 12:04:41.143093 pm
UUID: 3c3915d5-7426-4d3b-91b7-479deaf5468d
Ancestors: Kernel-eem.1285, Kernel-nice.1293
A much better Behavior>>instSpec using the newe integer array classes as examples.
A resumable AssertionFailure (essential for my usage; I want to be able to log AssertFails and continue in one crucial VM debuggingissue that has been in the background for years).
Nicolas's primitive highBit faaaaaast.
Nicolas' fixes for event dispatch which make my system usable.
But this one is really about Behavior>instSpec and resumable AssertFailure.
=============== Diff against Kernel-eem.1285 ===============
Item was changed:
+ Error subclass: #AssertionFailure
- Halt subclass: #AssertionFailure
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Kernel-Exceptions'!
!AssertionFailure commentStamp: 'gh 5/2/2002 20:29' prior: 0!
AsssertionFailure is the exception signaled from Object>>assert: when the assertion block evaluates to false.!
Item was added:
+ ----- Method: AssertionFailure>>isResumable (in category 'priv handling') -----
+ isResumable
+ ^ true!
Item was changed:
----- Method: Behavior>>instSpec (in category 'testing') -----
instSpec
"Answer the instance specification part of the format that defines what kind of object
an instance of the receiver is. The formats are
0 = 0 sized objects (UndefinedObject True False et al)
1 = non-indexable objects with inst vars (Point et al)
2 = indexable objects with no inst vars (Array et al)
3 = indexable objects with inst vars (Context BlockClosure AdditionalMethodState et al)
4 = weak indexable objects with inst vars (WeakArray et al)
5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron)
6 = unused
7 = immediates (SmallInteger, Character)
8 = unused
+ 9 = 64-bit indexable (DoubleWordArray et al)
+ 10-11 = 32-bit indexable (WordArray et al) (includes one odd bit, unused in 32-bit instances)
+ 12-15 = 16-bit indexable (DoubleByteArray et al) (includes two odd bits, one unused in 32-bit instances)
+ 16-23 = 8-bit indexable (ByteArray et al) (includes three odd bits, one unused in 32-bit instances)
+ 24-31 = compiled code (CompiledCode et al) (includes three odd bits, one unused in 32-bit instances)
+
- 9 = 64-bit indexable
- 10-11 = 32-bit indexable (Bitmap) (plus one odd bit, unused in 32-bits)
- 12-15 = 16-bit indexable (plus two odd bits, one unused in 32-bits)
- 16-23 = 8-bit indexable (plus three odd bits, one unused in 32-bits)
- 24-31 = compiled methods (CompiledMethod) (plus three odd bits, one unused in 32-bits)
Note that in the VM instances also have a 5 bit format field that relates to their class's format.
Formats 11, 13-15, 17-23 & 25-31 are unused in classes but used in instances to define the
number of elements missing up to the slot size. For example, a 2-byte ByteString instance
+ has format 18 in 32 bits, since its size is one 32-bit slot - 2 bytes ((18 bitAnd: 3) = 2), and
+ 22 in 64 bits, since its size is one 64-bit slot - 6 bytes ((22 bitAnd: 7) = 6).
+ Formats 24-31 are for compiled code which is a combination of pointers and bytes. The number of pointers is
+ determined by literal count field of the method header, which is the first field of the object and must be a SmallInteger.
+ The literal count field occupies the least significant 15 bits of the mehtod header, allowing up to 32,767 pointer fields,
+ not including the header."
- has format 18 in 32-bits, since its size is one 32-bit slot - 2 bytes ((18 bitAnd: 3) = 2), and
- 22 in 64 bits, since its size is one 64-bit slot - 6 bytes ((22 bitAnd: 7) = 6)."
^(format bitShift: -16) bitAnd: 16r1F!
Item was changed:
Object subclass: #EventSensor
instanceVariableNames: 'mouseButtons mousePosition mouseWheelDelta keyboardBuffer interruptKey interruptSemaphore eventQueue inputSemaphore lastEventPoll hasInputSemaphore'
classVariableNames: 'ButtonDecodeTable EventPollPeriod EventTicklerProcess InterruptSemaphore InterruptWatcherProcess KeyDecodeTable'
poolDictionaries: 'EventSensorConstants'
category: 'Kernel-Processes'!
+ !EventSensor commentStamp: 'mt 12/13/2019 14:38' prior: 0!
- !EventSensor commentStamp: 'dtl 1/30/2016 14:44' prior: 0!
An EventSensor is an interface to the user input devices.
There is at least one instance of EventSensor named Sensor in the system.
EventSensor is a replacement for the earlier InputSensor implementation based on a set of (optional) event primitives. An EventSensor updates its state when events are received so that all state based users of Sensor (e.g., Sensor keyboard, Sensor leftShiftDown, Sensor mouseButtons) will work exactly as before, by moving the current VM mechanisms into EventSensor itself. An optional input semaphore is part of the new design.
For platforms that support true asynchronous event notification, the semaphore will be signaled to indicate pending events.
On platforms that do not support asynchronous notifications about events, the UI will have to poll EventSensor periodically to read events from the VM.
Instance variables:
mouseButtons <Integer> - mouse button state as replacement for primMouseButtons
mousePosition <Point> - mouse position as replacement for primMousePt
keyboardBuffer <SharedQueue> - keyboard input buffer
interruptKey <Integer> - currently defined interrupt key
interruptSemaphore <Semaphore> - the semaphore signaled when the interruptKey is detected
eventQueue <SharedQueue> - an optional event queue for event driven applications
inputSemaphore <Semaphore>- the semaphore signaled by the VM if asynchronous event notification is supported
lastEventPoll <Integer> - the last millisecondClockValue at which we called fetchMoreEvents
hasInputSemaphore <Boolean> - true if my inputSemaphore has actually been signaled at least once.
Class variables:
ButtonDecodeTable <ByteArray> - maps mouse buttons as reported by the VM to ones reported in the events.
KeyDecodeTable <Dictionary<SmallInteger->SmallInteger>> - maps some keys and their modifiers to other keys (used for instance to map Ctrl-X to Alt-X)
InterruptSemaphore <Semaphore> - signalled by the the VM and/or the event loop upon receiving an interrupt keystroke.
InterruptWatcherProcess <Process> - waits on the InterruptSemaphore and then responds as appropriate.
EventPollPeriod <Integer> - the number of milliseconds to wait between polling for more events in the userInterruptHandler.
EventTicklerProcess <Process> - the process that makes sure that events are polled for often enough (at least every EventPollPeriod milliseconds).
Event format:
The current event format is very simple. Each event is recorded into an 8 element array. All events must provide some SmallInteger ID (the first field in the event buffer) and a time stamp (the second field in the event buffer), so that the difference between the time stamp of an event and the current time can be reported.
Currently, the following events are defined:
Null event
=============
The Null event is returned when the ST side asks for more events but no more events are available.
Structure:
[1] - event type 0
[2-8] - unused
Mouse event structure
==========================
Mouse events are generated when mouse input is detected.
- Structure:
[1] - event type 1
[2] - time stamp
[3] - mouse x position
[4] - mouse y position
[5] - button state; bitfield with the following entries:
+ 1 - 2r001 yellow (e.g., right) button
+ 2 - 2r010 blue (e.g., middle) button
+ 4 - 2r100 red (e.g., left) button
- 1 - yellow (e.g., right) button
- 2 - blue (e.g., middle) button
- 4 - red (e.g., left) button
[all other bits are currently undefined]
[6] - modifier keys; bitfield with the following entries:
1 - shift key
2 - ctrl key
4 - (Mac specific) option key
8 - Cmd/Alt key
[all other bits are currently undefined]
[7] - reserved.
+ [8] - host window id.
- [8] - reserved.
Keyboard events
====================
Keyboard events are generated when keyboard input is detected.
[1] - event type 2
[2] - time stamp
+ [3] - character code (Ascii)
+ For now the character code is in Mac Roman encoding. See #macToSqueak.
+ For key press/release (see [4]), character codes are normalized.
- [3] - character code
- For now the character code is in Mac Roman encoding.
[4] - press state; integer with the following meaning
+ 0 - character (aka. key stroke or key still pressed)
+ 1 - key press (aka. key down)
+ 2 - key release (aka. key up)
- 0 - character
- 1 - key press (down)
- 2 - key release (up)
[5] - modifier keys (same as in mouse events)
+ For key press/release (see [4]), modifier keys are still accessible.
+ [6] - character code (Unicode UTF32)
+ Manual decoding via KeyboardInputInterpreter possible.
+ For key press/release (see [4]), character codes are normalized.
- [6] - reserved.
[7] - reserved.
+ [8] - host window id.
+
+ Mouse-wheel event structure
+ ==========================
+ Mouse-wheel events are generated when mouse-wheel input is detected.
+ [1] - event type 7
+ [2] - time stamp
+ [3] - horizontal scroll delta
+ [4] - vertical scroll delta
+ [5] - button state (same as in mouse events)
+ [6] - modifier keys (same as in mouse events)
+ [7] - reserved.
+ [8] - host window id.
- [8] - reserved.
!
Item was changed:
----- Method: EventSensor class>>installDuplicateKeyEntryFor: (in category 'key decode table') -----
+ installDuplicateKeyEntryFor: aPrintableCharacter
+ "Updates the key-decode table, which maps between pairs of {character code . modifier code}.
+ See the class comment for more information.
+ The purpose of this change is to let ctrl+key act like cmd+key (Mac) or alt+key (linux/windows).
+ It is especially usefull on windows VM where default feel is to use ctrl as shortcut (ctrl+C = copy, etc...).
+ Note that the bitmask 16r9F removes the high bits, which subtracts 64 from the key code for (upper) $A to $Z and 96 for (lower) $a to $z. The VM sends non-printable control characters for [ctrl]+[A-Za-Z] in ASCII < 32, but the given character is expected to be ASCII >= 32 and thus printable. So we have to convert control characters to printable characters in this mapping table."
+
+ | upper lower |
+ upper := aPrintableCharacter asUppercase asInteger.
+ lower := aPrintableCharacter asLowercase asInteger.
+
+ KeyDecodeTable at: { lower bitAnd: 16r9F . 2 "ctrl" } put: { lower . 8 "cmd/alt" }.
+ KeyDecodeTable at: { upper bitAnd: 16r9F . 2 bitOr: 1 "ctrl + shift" } put: { upper . 8 bitOr: 1 "cmd/alt + shift" }.!
- installDuplicateKeyEntryFor: c
- | key |
- key := c asInteger.
- "first do control->alt key"
- KeyDecodeTable at: { key bitAnd: 16r9F . 2 } put: { key . 8 }.
- "then alt->alt key"
- KeyDecodeTable at: { key . 8 } put: { key . 8 }
- !
Item was changed:
----- Method: EventSensor class>>installKeyDecodeTable (in category 'class initialization') -----
installKeyDecodeTable
+ "Create a decode table that swaps or duplicates some keys if the respective preference is set."
+
- "Create a decode table that swaps some keys if
- Preferences swapControlAndAltKeys is set"
KeyDecodeTable := Dictionary new.
+
- Preferences duplicateControlAndAltKeys
- ifTrue: [ self defaultCrossPlatformKeys do:
- [ :c | self installDuplicateKeyEntryFor: c ] ].
Preferences swapControlAndAltKeys
+ ifTrue: [ (Character allByteCharacters select: [:ea | ea isAlphaNumeric]) do:
- ifTrue: [ self defaultCrossPlatformKeys do:
[ :c | self installSwappedKeyEntryFor: c ] ].
Preferences duplicateAllControlAndAltKeys
ifTrue: [ (Character allByteCharacters select: [:ea | ea isAlphaNumeric]) do:
[ :c | self installDuplicateKeyEntryFor: c ] ].
+
+ self flag: #toDeprecate. "mt: This mapping should be deprecated in the future."
+ Preferences duplicateControlAndAltKeys
+ ifTrue: [ self defaultCrossPlatformKeys do:
+ [ :c | self installDuplicateKeyEntryFor: c ] ].
!
Item was changed:
----- Method: EventSensor class>>installSwappedKeyEntryFor: (in category 'key decode table') -----
+ installSwappedKeyEntryFor: aPrintableCharacter
+ "Updates the key-decode table, which maps between pairs of {character code . modifier code}. See the class comment for more information.
+ Note that the bitmask 16r9F removes the high bits, which subtracts 64 from the key code for (upper) $A to $Z and 96 for (lower) $a to $z. The VM sends non-printable control characters for [ctrl]+[A-Za-Z] in ASCII < 32, but the given character is expected to be ASCII >= 32 and thus printable. So we have to convert printable characters to control characters in this mapping table."
+
+ | upper lower |
+ upper := aPrintableCharacter asUppercase asInteger.
+ lower := aPrintableCharacter asLowercase asInteger.
+
+ KeyDecodeTable at: { lower bitAnd: 16r9F . 2 "ctrl" } put: { lower . 8 "cmd/alt" }.
+ KeyDecodeTable at: { lower . 8 "cmd/alt" } put: { lower bitAnd: 16r9F . 2 "ctrl" }.
+ KeyDecodeTable at: { upper bitAnd: 16r9F . 2 bitOr: 1 "ctrl+shift" } put: { upper . 8 bitOr: 1 "cmd/alt+shift" }.
+ KeyDecodeTable at: { upper . 8 bitOr: 1 "cmd/alt+shift" } put: { upper bitAnd: 16r9F . 2 bitOr: 1 "ctrl+shift" }.!
- installSwappedKeyEntryFor: c
- | key |
- key := c asInteger.
- "first do control->alt key"
- KeyDecodeTable at: { key bitAnd: 16r9F . 2 } put: { key . 8 }.
- "then alt->control key"
- KeyDecodeTable at: { key . 8 } put: { key bitAnd: 16r9F . 2 }!
Item was changed:
----- Method: EventSensor>>processEvent: (in category 'private-I/O') -----
processEvent: evt
"Process a single event. This method is run at high priority."
| type buttons window |
type := evt at: 1.
"Only process main window events, forward others to host window proxies"
window := evt at: 8.
(window isNil or: [window isZero]) ifTrue:
[window := 1.
evt at: 8 put: window].
window = 1 ifFalse: [
^Smalltalk at: #HostWindowProxy ifPresent: [:w | w processEvent: evt]].
"Tackle mouse events and mouse wheel events first"
(type = EventTypeMouse or: [type = EventTypeMouseWheel])
ifTrue: [buttons := (ButtonDecodeTable at: (evt at: 5) + 1).
evt at: 5 put: (Smalltalk platformName = 'Mac OS'
ifTrue: [ buttons ]
ifFalse: [ self mapButtons: buttons modifiers: (evt at: 6) ]).
self queueEvent: evt.
type = EventTypeMouse ifTrue: [self processMouseEvent: evt].
type = EventTypeMouseWheel ifTrue: [self processMouseWheelEvent: evt].
^self].
"Store the event in the queue if there's any"
type = EventTypeKeyboard
ifTrue: [ "Check if the event is a user interrupt"
+ ((evt at: 4) = EventKeyChar
- ((evt at: 4) = 0
and: [((evt at: 3)
bitOr: (((evt at: 5)
bitAnd: 8)
bitShift: 8))
= interruptKey])
ifTrue: ["interrupt key is meta - not reported as event"
^ interruptSemaphore signal].
+ "Decode keys for characters (i.e., duplicate or swap, ctrl <-> alt/cmd)."
+ (evt at: 4) = EventKeyChar
+ ifTrue: [ | unicode ascii |
+ "Copy lookup key first in case of key swap."
+ unicode := {evt at: 6. evt at: 5}.
+ ascii := {evt at: 3. evt at: 5}.
+ KeyDecodeTable "Unicode character first"
+ at: unicode
+ ifPresent: [:a | evt at: 6 put: a first;
+ at: 5 put: a second].
+ KeyDecodeTable "ASCII character second"
+ at: ascii
+ ifPresent: [:a | evt at: 3 put: a first;
+ at: 5 put: a second]].
- "Else swap ctrl/alt keys if neeeded.
- Look at the Unicode char first, then ascii."
- KeyDecodeTable
- at: {evt at: 6. evt at: 5}
- ifPresent: [:a | evt at: 6 put: a first;
- at: 5 put: a second].
- KeyDecodeTable
- at: {evt at: 3. evt at: 5}
- ifPresent: [:a | evt at: 3 put: a first;
- at: 5 put: a second].
self queueEvent: evt.
self processKeyboardEvent: evt .
^self ].
+ "Handle all events other than Keyboard or Mouse."
- "Handle all events other than Keyborad or Mouse."
self queueEvent: evt.
!
Item was changed:
----- Method: SmallInteger>>highBit (in category 'bit manipulation') -----
highBit
"Answer the index of the high order bit of the receiver, or zero if the
receiver is zero. Raise an error if the receiver is negative, since
negative integers are defined to have an infinite number of leading 1's
in 2's-complement arithmetic. Use >>highBitOfMagnitude if you want to
get the highest bit of the magnitude."
+ <primitive: 575>
self < 0 ifTrue: [^ self error: 'highBit is not defined for negative integers'].
^ self highBitOfPositiveReceiver!
Item was changed:
----- Method: SmallInteger>>highBitOfMagnitude (in category 'bit manipulation') -----
highBitOfMagnitude
"Answer the index of the high order bit of the receiver, or zero if the
receiver is zero. This method is used for negative SmallIntegers as well,
since Squeak's LargeIntegers are sign/magnitude."
+ <primitive: 575>
+ self < 0 ifTrue: [^self negated highBit].
- self < 0 ifTrue: [
- "Beware: do not use highBitOfPositiveReceiver
- because self negated is not necessarily a SmallInteger
- (see SmallInteger minVal)"
- ^self negated highBitOfMagnitude].
-
- "Implementation note: this method could be as well inlined here."
^self highBitOfPositiveReceiver!
Marcel Taeumel uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-eem.1285.mcz
==================== Summary ====================
Name: Kernel-eem.1285
Author: eem
Time: 11 December 2019, 11:50:19.928718 am
UUID: 3dce70bd-9166-47fe-a86f-22456db9845a
Ancestors: Kernel-mt.1284
Commit my own take on sendsSelector: vs sendsMessage:, and selectorsDo: vs messagesDo:. IMNERHO sendsMessage: messagesDo: are *wrong*!! :-) c.f. Behavior>>selectorsDo:
=============== Diff against Kernel-mt.1284 ===============
Item was changed:
----- Method: CompiledCode>>messagesDo: (in category 'scanning') -----
messagesDo: workBlock
+ "Evaluate aBlock with all the message selectors sent by me. Duplicate seletors are possible."
- "Evaluate aBlock with all the message selectors sent by me. Duplicate sends possible."
+ "If anything should be deprecated it is messagesDo:; it can be an extension in AST/Refactoring.
+ This method enumerates over selectors, not messages. c.f. Behavior>>selectorsDo: etc"
+ ^self selectorsDo: workBlock!
- | scanner selector |
- self isQuick ifTrue: [^ self].
-
- self codeLiteralsDo: [:compiledCode |
- scanner := InstructionStream on: compiledCode.
- scanner scanFor: [ :x |
- (selector := scanner selectorToSendOrSelf) == scanner
- ifFalse: [workBlock value: selector].
- false "keep scanning" ] ].!
Item was added:
+ ----- Method: CompiledCode>>selectorsDo: (in category 'scanning') -----
+ selectorsDo: workBlock
+ "Evaluate aBlock with all the message selectors sent by me. Duplicate seletors are possible."
+
+ self isQuick ifTrue: [^self].
+
+ self codeLiteralsDo:
+ [:compiledCode | | scanner |
+ (scanner := InstructionStream on: compiledCode) scanFor:
+ [:x| | selector |
+ (selector := scanner selectorToSendOrSelf) ~~ scanner ifTrue:
+ [workBlock value: selector].
+ false "keep scanning"]]!
Item was changed:
----- Method: CompiledCode>>sendsMessage: (in category 'testing') -----
+ sendsMessage: aSelector
+ "eem: this should be deprecated. This method does not check if a method sends a message;
+ it checks if a method sends a message with a particular selector."
+ self flag: #todo.
- sendsMessage: aSelector
-
self messagesDo: [:selector |
selector = aSelector ifTrue: [^ true]].
^ false!
Item was changed:
----- Method: CompiledCode>>sendsSelector: (in category 'testing') -----
sendsSelector: aSelector
+ "Answer if the receiver sends a message whose selector is aSelector."
+ self selectorsDo:
+ [:selector | selector = aSelector ifTrue: [^true]].
+ ^false!
- self flag: #todo. "mt: Deprecate? AST/Refactoring project needs it..."
- ^ self sendsMessage: aSelector!