A new version of Morphic was added to project The Inbox:
http://source.squeak.org/inbox/Morphic-EG.1983.mcz
==================== Summary ====================
Name: Morphic-EG.1983
Author: EG
Time: 5 May 2022, 7:05:04.44181 am
UUID: fa611fc3-37ca-4bce-a541-5c5265fc8d3a
Ancestors: Morphic-mt.1982
Switching from AnimatedImageMorph pre-composing full Forms from AnimatedImageFrames to doing live composition during animation playback. The composition of frames is based on the disposal information for each frame.
This should decrease loading times for displaying animated GIFs in the system.
For the moment, we only handle #leaveCurrent and #restoreBackground disposals, though these seem to cover 99% of cases in the wild.
=============== Diff against Morphic-mt.1982 ===============
Item was changed:
ImageMorph subclass: #AnimatedImageMorph
+ instanceVariableNames: 'images delays stepTime imageIndex disposals'
- instanceVariableNames: 'images delays stepTime imageIndex'
classVariableNames: ''
poolDictionaries: ''
category: 'Morphic-Basic'!
!AnimatedImageMorph commentStamp: 'EG 12/4/2021 12:33' prior: 0!
I am an ImageMorph that can hold more than one image. Each image has its own delay time. I am most commonly created using GIFReaderWriter when it contains multiple images/frames.!
Item was removed:
- ----- Method: AnimatedImageMorph>>composedFormsFromReader: (in category 'frame composition') -----
- composedFormsFromReader: aGIFReader
- "Compose a collection of Forms that are composited
- from the incoming collection of Frames. We do this instead of
- compositing each Form on the fly for performance reasons.
- With this method, we can achieve better framerates for
- Animated GIFs."
- | nextForm compForm |
- nextForm := Form extent: (aGIFReader canvasWidth)@(aGIFReader canvasHeight) depth: 32.
- ^ aGIFReader frames collect: [ :frame |
- frame form displayOn: nextForm at: 0@0 rule: Form paint.
- compForm := nextForm.
- frame disposal = #leaveCurrent
- ifTrue: [ nextForm := nextForm copy ]
- ifFalse: [ frame disposal = #restoreBackground
- ifTrue: [ nextForm := Form extent: (aGIFReader canvasWidth)@(aGIFReader canvasHeight) depth: 32 ]].
- compForm ]!
Item was added:
+ ----- Method: AnimatedImageMorph>>fromFrames: (in category 'initialization') -----
+ fromFrames: aFrameCollection
+ "Initialize this morph from a colleciton of
+ AnimatedImageFrames"
+ delays := OrderedCollection new.
+ disposals := OrderedCollection new.
+ images := OrderedCollection new.
+ aFrameCollection do: [ :frame |
+ delays add: frame delay.
+ disposals add: frame disposal.
+ images add: frame form ].!
Item was changed:
----- Method: AnimatedImageMorph>>fromGIFReader: (in category 'initialization') -----
fromGIFReader: aGIFReader
+ self
+ fromFrames: aGIFReader frames;
+ color: aGIFReader backgroundColor;
+ width: aGIFReader canvasWidth;
+ height: aGIFReader canvasHeight.
+
+ "Set the current active image form to
+ a form that is the same dimensions as this morph"
+ self image: ((FormCanvas extent: (aGIFReader canvasWidth)@(aGIFReader canvasHeight))
+ fillColor: self color;
+ drawImage: images first
+ at: images first offset;
+ form)!
- delays := aGIFReader delays.
- self
- stepTime: aGIFReader delays first;
- images: (self composedFormsFromReader: aGIFReader);
- yourself!
Item was changed:
----- Method: AnimatedImageMorph>>step (in category 'stepping and presenter') -----
step
+ | disposal canvas current |
+ "If we are about to go beyond the number of images,
+ then loop back to the first one"
+ ((imageIndex) >= images size)
- ((imageIndex + 1) >= images size)
ifTrue: [ imageIndex := 1 ]
ifFalse: [ imageIndex := imageIndex + 1 ].
+
+ current := images at: imageIndex.
+
+ "If this is the first image in the sequence, we don't
+ have a previous disposal to look at. So just paint
+ the image -- equiv to #leaveCurrent"
+ canvas := image getCanvas.
+ (imageIndex == 1)
+ ifTrue: [
+ image getCanvas drawImage: current at: current offset ]
+ ifFalse: [
+ "Otherwise, we need to look at the previous image's disposal method
+ to see if we should restore the background"
+ disposal := disposals at: imageIndex - 1.
+ (disposal == #restoreBackground)
+ ifTrue: [
+ "canvas
+ fillRectangle: self bounds
+ color: self color"
+ canvas := (Form extent: canvas extent depth: 32) getCanvas.
+ canvas fillRectangle: ((current offset) corner: (current offset + current extent)) color: self color.
+ canvas drawImage: current at: current offset ]
+ ifFalse: [ canvas paintImage: current at: current offset ]
+ ].
+ self image: canvas form.
+ self stepTime: (delays at: imageIndex)!
- self
- stepTime: (delays at: imageIndex);
- image: (images at: imageIndex)
- !
Levente Uzonyi uploaded a new version of Collections to project The Trunk:
http://source.squeak.org/trunk/Collections-ul.1000.mcz
==================== Summary ====================
Name: Collections-ul.1000
Author: ul
Time: 28 March 2022, 8:25:48.093286 pm
UUID: 37ba296b-06e0-48f5-bdc8-2101110f360e
Ancestors: Collections-ct.998
Symbol changes:
- comment the symbol table implemented in a non-object-oriented fashion in the class comment
- both WeakSets of the symbol table are immutable (#beReadOnlyObject)
- #condenseNewSymbols does nothing if the tables are already empty and compact
- added a comment to all methods accessing the symbol table class variables
- use a loop instead of recursion in #rehash and #condenseNewSymbols
General:
- methods of the HashedCollection hierachy that update the tally and modify the array variable will update the tally first, so that immutable hashed collections raise an error before their non-immutable array variable is updated
- use #lookup: instead of #hasInterned:ifTrue:
- speed up WeakSet >> #postCopy
- added #isCompact to HashedCollection and Heap (thanks Christoph)
- avoid compaction of Heap if it is already compact
- updated the comment of various methods
=============== Diff against Collections-ct.998 ===============
Item was changed:
----- Method: Dictionary>>removeKey:ifAbsent: (in category 'removing') -----
removeKey: key ifAbsent: aBlock
"Remove key (and its associated value) from the receiver. If key is not in
the receiver, answer the result of evaluating aBlock. Otherwise, answer
the value externally named by key."
| index association |
index := self scanFor: key.
association := (array at: index) ifNil: [ ^aBlock value ].
+ tally := tally - 1. "Update tally first, so that read-only hashed collections raise an error before modifying array."
array at: index put: nil.
- tally := tally - 1.
self fixCollisionsFrom: index.
^association value!
Item was changed:
----- Method: HashedCollection>>atNewIndex:put: (in category 'private') -----
atNewIndex: index put: anObject
+ tally := tally + 1. "Update tally first, so that read-only hashed collections raise an error before modifying array."
array at: index put: anObject.
- tally := tally + 1.
"Keep array at least 1/4 free for decent hash behavior"
array size * 3 < (tally * 4) ifTrue: [ self grow ]!
Item was changed:
----- Method: HashedCollection>>growTo: (in category 'private') -----
growTo: anInteger
+ "Reallocate the elements array with the given size and reinsert the old elements. Do it even if the size of the array is the same as the argument because this method is also used to rehash the collection."
+
- "Grow the elements array and reinsert the old elements. Do it even if the size of the array is the same as the argument because this methods is also used to rehash the collection."
-
| oldElements |
oldElements := array.
array := self arrayType new: anInteger.
self noCheckNoGrowFillFrom: oldElements!
Item was added:
+ ----- Method: HashedCollection>>isCompact (in category 'testing') -----
+ isCompact
+ "Answer true if I have the smallest possible capacity to store the elements."
+
+ ^array size = (self class sizeFor: self slowSize)!
Item was changed:
----- Method: Heap>>compact (in category 'growing') -----
compact
"Remove any empty slots in the receiver."
+ self isCompact ifTrue: [ ^self ].
self growTo: self size.!
Item was added:
+ ----- Method: Heap>>isCompact (in category 'growing') -----
+ isCompact
+ "Answer true if I have the smallest possible capacity to store the elements."
+
+ ^array size = tally!
Item was changed:
----- Method: KeyedSet>>remove:ifAbsent: (in category 'removing') -----
remove: oldObject ifAbsent: aBlock
| index |
index := self scanFor: (keyBlock value: oldObject).
(array at: index) ifNil: [ ^ aBlock value ].
+ tally := tally - 1. "Update tally first, so that read-only hashed collections raise an error before modifying array."
array at: index put: nil.
- tally := tally - 1.
self fixCollisionsFrom: index.
^ oldObject!
Item was changed:
----- Method: KeyedSet>>removeKey:ifAbsent: (in category 'removing') -----
removeKey: key ifAbsent: aBlock
| index obj |
index := self scanFor: key.
obj := (array at: index) ifNil: [ ^ aBlock value ].
+ tally := tally - 1. "Update tally first, so that read-only hashed collections raise an error before modifying array."
array at: index put: nil.
- tally := tally - 1.
self fixCollisionsFrom: index.
^ obj enclosedSetElement!
Item was changed:
----- Method: Set>>remove:ifAbsent: (in category 'removing') -----
remove: oldObject ifAbsent: aBlock
| index |
index := self scanFor: oldObject.
(array at: index) ifNil: [ ^ aBlock value ].
+ tally := tally - 1. "Update tally first, so that read-only hashed collections raise an error before modifying array."
array at: index put: nil.
- tally := tally - 1.
self fixCollisionsFrom: index.
^ oldObject!
Item was changed:
String subclass: #Symbol
instanceVariableNames: ''
classVariableNames: 'NewSymbols SymbolTable'
poolDictionaries: ''
category: 'Collections-Strings'!
+ !Symbol commentStamp: 'ul 3/28/2022 19:53' prior: 0!
- !Symbol commentStamp: 'mt 4/13/2021 17:04' prior: 0!
I represent Strings that are created uniquely. Thus, someString asSymbol == someString asSymbol.
+ On my class-side, there is an implementation of a symbol table which provides concurrent access (both read and write) without using locks (Semaphore, Mutex, Monitor).
+ The state of the symbol table is stored in two immutable (see #beReadOnlyObject) WeakSets stored by the class variables SymbolTable and NewSymbols.
+ SymbolTable holds most of the interned symbols, while new symbols are always added to NewSymbols.
+ Once the size of NewSymbols exceeds a limit (1000 currently, see #intern:), its content is merged into SymbolTable (see #condenseNewSymbols).
+
+ To ensure a consistent view of the symbol table, all methods accessing it start with atomically creating a "snapshot" of the state, the two variables, by assigning them to two temporaries. Except for #intern:, which only accesses NewSymbols, hence it only creates a snapshot of that.
+ If the symbol table changes, NewSymbols will always be a different object, so it is enough to check whether NewSymbols is the same as before the operation to verify that the symbol table has not been modified.
+
+ There are three methods that can update the symbol table: #condenseNewSymbols, #rehash and #intern:. They create a snapshot first as described above, then create copies of the updated parts, and finally check whether NewSymbols is the same as before, and if it is, they apply their changes. That all happens atomically because #==, #ifTrue: and assignments are executed by the VM without suspension points, hence atomically. If NewSymbols is different, the methods are repeated until they succeed.
+ !
- ATTENTION!! To ensure consistency and thread safety without using a mutex, the two WeakSets which make up the symbol table are treated as if they were immutable. Removing from them without creating a copy just breaks that contract.!
Item was changed:
----- Method: Symbol class>>allSymbolTablesDo: (in category 'class initialization') -----
allSymbolTablesDo: aBlock
+ "See the class comment for details about the usage of the class variables before changing this method"
+
-
| originalNewSymbols originalSymbolTable |
originalNewSymbols := NewSymbols.
originalSymbolTable := SymbolTable.
originalNewSymbols do: aBlock.
originalSymbolTable do: aBlock.!
Item was changed:
----- Method: Symbol class>>allSymbolTablesDo:after: (in category 'class initialization') -----
allSymbolTablesDo: aBlock after: aSymbol
+ "See the class comment for details about the usage of the class variables before changing this method"
| originalNewSymbols originalSymbolTable |
originalNewSymbols := NewSymbols.
originalSymbolTable := SymbolTable.
(originalNewSymbols includes: aSymbol)
ifTrue: [
originalNewSymbols do: aBlock after: aSymbol.
originalSymbolTable do: aBlock after: aSymbol ]
ifFalse: [
originalSymbolTable do: aBlock after: aSymbol.
originalNewSymbols do: aBlock after: aSymbol ]
!
Item was changed:
----- Method: Symbol class>>allSymbols (in category 'accessing') -----
allSymbols
"Answer all interned symbols"
+ "See the class comment for details about the usage of the class variables before changing this method"
+
-
| originalNewSymbols originalSymbolTable |
originalNewSymbols := NewSymbols.
originalSymbolTable := SymbolTable.
^Array
new: originalNewSymbols slowSize + originalSymbolTable slowSize
+ streamContents: [ :stream |
- streamContents:[ :stream |
stream
nextPutAll: originalNewSymbols;
nextPutAll: originalSymbolTable ]
!
Item was changed:
----- Method: Symbol class>>condenseNewSymbols (in category 'private') -----
condenseNewSymbols
+ "Move all symbols from NewSymbols to SymbolTable, and compact SymbolTable if needed."
+ "See the class comment for details about the usage of the class variables before changing this method."
- "Move all symbols from NewSymbols to SymbolTable, and compact SymbolTable."
| originalNewSymbols originalSymbolTable newNewSymbols newSymbolTable |
+ [
+ originalNewSymbols := NewSymbols.
+ originalSymbolTable := SymbolTable.
+ (originalNewSymbols isEmpty and: [ originalSymbolTable isCompact ]) ifTrue: [
+ "Only recreate the sets if necessary"
+ ^self ].
+ (newNewSymbols := WeakSet new)
+ beReadOnlyObject.
+ (newSymbolTable := WeakSet new: originalNewSymbols slowSize + originalSymbolTable slowSize)
+ addAll: originalSymbolTable;
+ addAll: originalNewSymbols;
+ beReadOnlyObject.
+ originalNewSymbols == NewSymbols ifTrue: [
+ NewSymbols := newNewSymbols.
+ SymbolTable := newSymbolTable.
+ ^self ].
+ "Some other process has modified the symbol table. Try again." ] repeat!
- originalNewSymbols := NewSymbols.
- originalSymbolTable := SymbolTable.
- newNewSymbols := WeakSet new.
- newSymbolTable := originalSymbolTable copy
- addAll: originalNewSymbols;
- compact;
- yourself.
- originalNewSymbols == NewSymbols ifFalse: [
- "Some other process has modified the symbols. Try again."
- ^self condenseNewSymbols ].
- NewSymbols := newNewSymbols.
- SymbolTable := newSymbolTable!
Item was changed:
----- Method: Symbol class>>intern: (in category 'instance creation') -----
intern: aStringOrSymbol
"Answer the unique Symbol formed with given String.
If it does not exist yet, create it and intern it in the NewSymbols.
Interning a Symbol should return the Symbol itself, no Symbol should be duplicated"
+ "See the class comment for details about the usage of the class variables before changing this method"
| originalNewSymbols |
originalNewSymbols := NewSymbols.
^(self lookup: aStringOrSymbol) ifNil:[
| aSymbol newNewSymbols |
aStringOrSymbol isSymbol ifTrue:[
aSymbol := aStringOrSymbol.
] ifFalse:[
aSymbol := (aStringOrSymbol isOctetString ifTrue:[ByteSymbol] ifFalse:[WideSymbol])
new: aStringOrSymbol size.
aSymbol
copyFrom: aStringOrSymbol;
beReadOnlyObject.
].
newNewSymbols := originalNewSymbols copyWith: aSymbol.
+ newNewSymbols beReadOnlyObject.
originalNewSymbols == NewSymbols
ifTrue: [
NewSymbols := newNewSymbols.
newNewSymbols size > 1000 ifTrue: [ self condenseNewSymbols ].
aSymbol ]
ifFalse: [
"Some other process has modified the symbols. Try again."
self intern: aStringOrSymbol ] ]!
Item was changed:
----- Method: Symbol class>>lookup: (in category 'instance creation') -----
lookup: aStringOrSymbol
"Answer the unique Symbol formed with given String, if it exists.
Answer nil if no such Symbol does exist yet.
Looking up a Symbol should return the Symbol itself
- no Symbol should be duplicated
- every Symbol should be registered in one of the two Symbol tables"
+ "See the class comment for details about the usage of the class variables before changing this method"
| originalNewSymbols originalSymbolTable |
originalNewSymbols := NewSymbols.
originalSymbolTable := SymbolTable.
+ ^(originalSymbolTable like: aStringOrSymbol) ifNil: [ "Most symbols are in originalSymbolTable, so look for existing symbols in there first"
- ^(originalSymbolTable like: aStringOrSymbol) ifNil: [
originalNewSymbols like: aStringOrSymbol ]!
Item was changed:
----- Method: Symbol class>>possibleSelectorsFor: (in category 'private') -----
possibleSelectorsFor: misspelled
"Answer an ordered collection of possible corrections
for the misspelled selector in order of likelyhood"
| numArgs candidates lookupString best binary short long first |
lookupString := misspelled asLowercase. "correct uppercase selectors to lowercase"
numArgs := lookupString numArgs.
(numArgs < 0 or: [lookupString size < 2]) ifTrue: [^ OrderedCollection new: 0].
first := lookupString first.
short := lookupString size - (lookupString size // 4 max: 3) max: 2.
long := lookupString size + (lookupString size // 4 max: 3).
"First assemble candidates for detailed scoring"
candidates := OrderedCollection new.
self allSymbolTablesDo: [:s | | ss |
(((ss := s size) >= short "not too short"
and: [ss <= long "not too long"
or: [(s at: 1) = first]]) "well, any length OK if starts w/same letter"
and: [s numArgs = numArgs]) "and numArgs is the same"
ifTrue: [candidates add: s]].
"Then further prune these by correctAgainst:"
best := lookupString correctAgainst: candidates.
((misspelled last ~~ $:) and: [misspelled size > 1]) ifTrue: [
binary := misspelled, ':'. "try for missing colon"
+ (self lookup: binary) ifNotNil: [:him | best addFirst: him]].
- Symbol hasInterned: binary ifTrue: [:him | best addFirst: him]].
^ best!
Item was changed:
----- Method: Symbol class>>rehash (in category 'private') -----
rehash
"Rebuild the hash table, reclaiming unreferenced Symbols. This method will intern all symbols. You're probably looking for #condenseNewSymbols instead."
+ "See the class comment for details about the usage of the class variables before changing this method"
| originalNewSymbols originalSymbolTable newNewSymbols newSymbolTable |
+ [
+ originalNewSymbols := NewSymbols.
+ originalSymbolTable := SymbolTable.
+ newNewSymbols := WeakSet new.
+ newSymbolTable := WeakSet withAll: self allSubInstances.
+ newNewSymbols beReadOnlyObject.
+ newSymbolTable beReadOnlyObject.
+ originalNewSymbols == NewSymbols ifTrue: [
+ NewSymbols := newNewSymbols.
+ SymbolTable := newSymbolTable.
+ ^self ].
+ "Some other process has modified the symbol table. Try again." ] repeat
+ !
- originalNewSymbols := NewSymbols.
- originalSymbolTable := SymbolTable.
- newNewSymbols := WeakSet new.
- newSymbolTable := WeakSet withAll: self allSubInstances.
- originalNewSymbols == NewSymbols ifFalse: [
- "Some other process has modified the symbols. Try again."
- ^self rehash ].
- NewSymbols := newNewSymbols.
- SymbolTable := newSymbolTable!
Item was changed:
----- Method: WeakIdentityDictionary>>removeKey:ifAbsent: (in category 'removing') -----
removeKey: key ifAbsent: aBlock
"Remove key (and its associated value) from the receiver. If key is not in
the receiver, answer the result of evaluating aBlock. Otherwise, answer
the value externally named by key."
| index association |
index := self scanFor: key.
(association := (array at: index)) == vacuum ifTrue: [ ^aBlock value ].
+ tally := tally - 1. "Update tally first, so that read-only hashed collections raise an error before modifying array."
array at: index put: vacuum.
- tally := tally - 1.
self fixCollisionsFrom: index.
^association value!
Item was changed:
----- Method: WeakSet>>postCopy (in category 'copying') -----
postCopy
+
| oldFlag |
super postCopy.
oldFlag := flag.
flag := Object new.
+ 1 to: array size do: [ :index |
+ (array at: index) == oldFlag ifTrue: [
+ array at: index put: flag ] ]!
- array replaceAll: oldFlag with: flag.!
Item was changed:
----- Method: WeakSet>>remove:ifAbsent: (in category 'removing') -----
remove: oldObject ifAbsent: aBlock
| index |
index := self scanFor: oldObject.
(array at: index) == flag ifTrue: [ ^ aBlock value ].
+ tally := tally - 1. "Update tally first, so that read-only hashed collections raise an error before modifying array."
array at: index put: flag.
- tally := tally - 1.
self fixCollisionsFrom: index.
^oldObject!
Item was changed:
+ (PackageInfo named: 'Collections') postscript: '"Make sure the symbol table consists of immutable sets"
+ #(SymbolTable NewSymbols) do: [ :variableName |
+ (Symbol classPool at: variableName) beReadOnlyObject ]'!
- (PackageInfo named: 'Collections') postscript: '"Definition of separators has been updated, update CharacterSet caches too"
- CharacterSet cleanUp: false.'!
Chris Muller uploaded a new version of Morphic to project The Inbox:
http://source.squeak.org/inbox/Morphic-cmm.1981.mcz
==================== Summary ====================
Name: Morphic-cmm.1981
Author: cmm
Time: 4 May 2022, 7:20:05.197201 pm
UUID: 8cb761bf-5c12-4462-8376-3001637544f0
Ancestors: Morphic-cmm.1979
Testing saving while updating another image. wildelete
=============== Diff against Morphic-ct.1978 ===============
Item was added:
+ ----- Method: Morph>>test (in category 'testing') -----
+ test!
Item was added:
+ ----- Method: Morph>>test2 (in category 'submorphs - testing') -----
+ test2!
Chris Muller uploaded a new version of Morphic to project The Inbox:
http://source.squeak.org/inbox/Morphic-cmm.1980.mcz
==================== Summary ====================
Name: Morphic-cmm.1980
Author: cmm
Time: 4 May 2022, 7:06:51.358475 pm
UUID: 79069040-f9e8-4185-bc75-3af85adf48f4
Ancestors: Morphic-cmm.1979
Test. Delete me.
=============== Diff against Morphic-cmm.1979 ===============
Item was removed:
- ----- Method: Morph>>test (in category 'testing') -----
- test!
Chris Muller uploaded a new version of Morphic to project The Inbox:
http://source.squeak.org/inbox/Morphic-cmm.1979.mcz
==================== Summary ====================
Name: Morphic-cmm.1979
Author: cmm
Time: 4 May 2022, 6:15:06.896942 pm
UUID: 9cae8cac-58dc-4b16-b762-4ba85e9f570e
Ancestors: Morphic-ct.1978
Debugging SqueakSource. Ignore.
=============== Diff against Morphic-ct.1978 ===============
Item was added:
+ ----- Method: Morph>>test (in category 'testing') -----
+ test!
Christoph Thiede uploaded a new version of Collections to project The Trunk:
http://source.squeak.org/trunk/Collections-ct.999.mcz
==================== Summary ====================
Name: Collections-ct.999
Author: ct
Time: 28 March 2022, 10:08:58.886426 pm
UUID: d938acc0-5012-9649-960f-9b374a010b83
Ancestors: Collections-ct.998
Restores line breaks in Text codeSample that are required indeed for the FontImporterTool.
=============== Diff against Collections-ct.998 ===============
Item was changed:
----- Method: Text class>>codeSample (in category 'filler text') -----
codeSample
+ self flag: #linebreaks. "Samples are used in FontImporterTool and must contain manual linebreaks (visually stable example texts per line)."
^ 'exampleWithNumber: x
+ "A method that illustrates every part of Smalltalk method syntax
+ including primitives. It has unary, binary, and keyboard messages;
+ declares arguments and temporaries; accesses a global variable
+ (but not an instance variable); uses literals (array, nested array,
+ character, symbol, string, integer, float, scaled decimal, and byte
+ array); uses the pseudo variables nil, true, false, self, super, and
+ thisContext; shows that within a literal array nil, true, and false are
+ symbols not pseudo variables; and has sequence, assignment,
+ return, cascade, and tuple (array) creation. It has both zero
+ argument and one argument blocks, and has a block temporary."
- "A method that illustrates every part of Smalltalk method syntax including primitives. It has unary, binary, and keyboard messages; declares arguments and temporaries; accesses a global variable (but not an instance variable); uses literals (array, nested array, character, symbol, string, integer, float, scaled decimal, and byte array); uses the pseudo variables nil, true, false, self, super, and thisContext; shows that within a literal array nil, true, and false are symbols not pseudo variables; and has sequence, assignment, return, cascade, and tuple (array) creation. It has both zero argument and one argument blocks, and has a block temporary."
<primitive: ''primitiveCopyBits'' module: #BitBltPlugin error: ec>
| y |
true & false not & (nil isNil) ifFalse: [self halt].
y := self size + super size.
#($a #a ''a'' "a" (1 1.0 1.0s2) nil true false), { #[65]. thisContext. nil. true. false }
do: [ :each | | class |
class := each class.
Transcript
show: (class name);
show: '' ''].
^ x < y'!