Christoph Thiede uploaded a new version of Protocols to project The Trunk:
http://source.squeak.org/trunk/Protocols-ct.87.mcz
==================== Summary ====================
Name: Protocols-ct.87
Author: ct
Time: 19 June 2023, 4:39:48.046379 pm
UUID: e694c77b-331f-3e46-ac97-5face4998266
Ancestors: Protocols-ct.86
Makes home icon in lexicon tools scale-factor-aware.
=============== Diff against Protocols-ct.86 ===============
Item was changed:
----- Method: Lexicon>>addSpecialButtonsTo:with: (in category 'toolbuilder') -----
addSpecialButtonsTo: buttonPanelSpec with: builder
| homeCatBtnSpec menuBtnSpec mostGenericBtnSpec |
homeCatBtnSpec := builder pluggableButtonSpec new
model: self;
action: #showHomeCategory;
+ label: MenuIcons smallHomeIcon scaleIconToDisplay;
- label: MenuIcons smallHomeIcon;
help: 'show this method''s home category';
yourself.
menuBtnSpec := builder pluggableButtonSpec new
model: self;
action: #offerMenu;
label: (ScriptingSystem formAtKey: #TinyMenu) asMorph;
help: 'click here to get a menu with further options';
yourself.
mostGenericBtnSpec :=builder pluggableButtonSpec new
model: self;
action: #chooseLimitClass;
label: #limitClassString;
help: 'Governs which classes'' methods should be shown. If this is the same as the viewed class, then only methods implemented in that class will be shown. If it is ProtoObject, then methods of all classes in the vocabulary will be shown.'.
buttonPanelSpec children
add: homeCatBtnSpec;
addFirst: mostGenericBtnSpec;
addFirst: menuBtnSpec.!
Marcel Taeumel uploaded a new version of FFI-Kernel to project FFI:
http://source.squeak.org/FFI/FFI-Kernel-mt.231.mcz
==================== Summary ====================
Name: FFI-Kernel-mt.231
Author: mt
Time: 16 June 2023, 4:39:58.761813 pm
UUID: 321ca604-4a55-a64b-bcd6-2643c59bf71a
Ancestors: FFI-Kernel-mt.230
Revise FFI-Kernel-eem.227 by using a simple mutex to make #structTypeNamed: thread-safe.
Note that there is still the preference #useTypePool to speed up calls to #externalType. This is only slightly related to type-safety as it introduces strong references even for types not used in any call-out signature.
Also note that it is not problematic if two subsequent calls à la "MyStruct new" work with different temporary type objects as long as their information (e.g., byteAlignment, compiledSpec) is the same.
=============== Diff against FFI-Kernel-mt.230 ===============
Item was changed:
ExternalObject subclass: #ExternalStructure
instanceVariableNames: ''
classVariableNames: 'LogAccessorSourceCode'
poolDictionaries: 'ExternalTypePool FFIConstants'
category: 'FFI-Kernel'!
ExternalStructure class
+ instanceVariableNames: 'compiledSpec byteAlignment'!
- instanceVariableNames: 'compiledSpec byteAlignment externalType'!
!ExternalStructure commentStamp: 'eem 6/26/2019 15:26' prior: 0!
An ExternalStructure is for representing external data that is
- either a structure composed of different fields (a struct of C language)
- or an alias for another type (like a typedef of C language)
It reserves enough bytes of data for representing all the fields.
The data is stored into the handle instance variable which can be of two different types:
- ExternalAddress
If the handle is an external address then the object described does not reside in the Smalltalk object memory.
- ByteArray
If the handle is a byte array then the object described resides in Smalltalk memory.
Instance Variables (class side)
byteAlignment: <Integer>
compiledSpec: <WordArray>
byteAlignment
- the required alignment for the structure
compiledSpec
- the bit-field definiton of the structure in the ExternalType encoding understood by the VM's FFI call marshalling machinery.
A specific structure is defined by subclassing ExternalStructure and specifying its #fields via a class side method.
For example if we define a subclass:
ExternalStructure subclass: #StructExample
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'garbage'.
Then declare the fields like this:
StructExample class compile: 'fields ^#( (name ''char*'') (color ''ulong'') )' classified: 'garbage'.
It means that this type is composed of two different fields:
- a string (accessed thru the field #name)
- and an unsigned 32bit integer (accessed thru the field #color).
It represents the following C type:
struct StructExample {char *name; uint32_t color; };
The accessors for those fields can be generated automatically like this:
StructExample defineFields.
As can be verified in a Browser:
StructExample browse.
We see that name and color fields are stored sequentially in different zones of data.
The total size of the structure can be verified with:
StructExample byteSize = (Smalltalk wordSize + 4).
An ExternalStructure can also be used for defining an alias.
The fields definition must contain only 2 elements: an eventual accessor (or nil) and the type.
For example, We can define a machine dependent 'unsigned long' like this:
ExternalStructure subclass: #UnsignedLong
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'garbage'.
Then set the fields like this:
UnsignedLong class compile: 'fields ^(Smalltalk wordSize=4 or: [Smalltalk platformName=''Win64''])
ifTrue: [#(nil ''ulong'')] ifFalse: [#(nil ''ulonglong'')]' classified: 'garbage'.
And verify the size on current platform:
UnsignedLong byteSize.
Then, the class names 'UnsignedLong' and 'StructExamples' acts as a type specification.
They can be used for composing other types, and for defining prototype of external functions:
LibraryExample>>initMyStruct: aStructExample name: name color: anInteger
<cdecl: void 'init_my_struct'( StructExample * char * UnsignedLong )>
self externalCallFailed
!
ExternalStructure class
+ instanceVariableNames: 'compiledSpec byteAlignment'!
- instanceVariableNames: 'compiledSpec byteAlignment externalType'!
Item was removed:
- ----- Method: ExternalStructure class>>addSelectorSilently:withMethod: (in category 'accessing method dictionary') -----
- addSelectorSilently: selector withMethod: compiledMethod
- "Override to void the strong reference to the externalType"
- self voidExternalType.
- ^super addSelectorSilently: selector withMethod: compiledMethod!
Item was changed:
----- Method: ExternalStructure class>>externalType (in category 'external type') -----
externalType
+ "Return an external type describing the receiver as a structure"
+ ^ExternalType structTypeNamed: self name!
- "Answer an external type describing the receiver as a structure.
- Keep a strong reference to it so that it doesn't get garbage collected
- from StructTypes immediately."
- ^externalType := ExternalType structTypeNamed: self name!
Item was removed:
- ----- Method: ExternalStructure class>>removeSelector: (in category 'accessing method dictionary') -----
- removeSelector: selector
- "Override to void the strong reference to the externalType"
- self voidExternalType.
- ^super removeSelector: selector!
Item was removed:
- ----- Method: ExternalStructure class>>voidExternalType (in category 'private') -----
- voidExternalType
- externalType := nil.
- ExternalType voidStructTypeFor: self!
Item was changed:
Object subclass: #ExternalType
instanceVariableNames: 'compiledSpec referentClass referencedType byteAlignment arrayClass'
+ classVariableNames: 'ArrayTypes AtomicTypeCodes AtomicTypes ExtraTypeChecks StructTypes StructTypesLock UseArrayClasses UseTypePool'
- classVariableNames: 'ArrayTypes AtomicTypeCodes AtomicTypes ExtraTypeChecks StructTypes UseArrayClasses UseTypePool'
poolDictionaries: 'ExternalTypePool FFIConstants'
category: 'FFI-Kernel'!
!ExternalType commentStamp: 'mt 6/5/2020 18:25' prior: 0!
An external type represents the type of external objects.
Instance variables:
compiledSpec <WordArray> Compiled specification of the external type
referentClass <Behavior | nil> Class type of argument required
referencedType <ExternalType> Associated (non)pointer type with the receiver
byteAlignment <Integer | nil> The desired alignment for a field of the external type within a structure. If nil it has yet to be computed.
Compiled Spec:
The compiled spec defines the type in terms which are understood by the VM. Each word is defined as:
bits 0...15 - byte size of the entity
bit 16 - structure flag (FFIFlagStructure)
This flag is set if the following words define a structure
bit 17 - pointer flag (FFIFlagPointer)
This flag is set if the entity represents a pointer to another object
bit 18 - atomic flag (FFIFlagAtomic)
This flag is set if the entity represents an atomic type.
If the flag is set the atomic type bits are valid.
bits 19...23 - unused
bits 24...27 - atomic type (FFITypeVoid ... FFITypeDoubleFloat)
bits 28...31 - unused
Note that all combinations of the flags FFIFlagPointer, FFIFlagAtomic, and FFIFlagStructure are invalid, EXCEPT from the following:
FFIFlagPointer + FFIFlagAtomic:
This defines a pointer to an atomic type (e.g., 'char*', 'int*').
The actual atomic type is represented in the atomic type bits.
FFIFlagPointer + FFIFlagStructure:
This defines a structure which is a typedef of a pointer type as in
typedef void* VoidPointer;
typedef Pixmap* PixmapPtr;
It requires a byte size of four or eight (e.g. a 32-bit or 64-bit pointer) to work correctly.
[Note: Other combinations may be allowed in the future]
!
Item was changed:
----- Method: ExternalType class>>newTypeNamed: (in category 'instance creation') -----
newTypeNamed: aTypeName
+ "Create a new struct type or array type. Not needed for atomic types; see #initializeAtomicTypes."
- "Create a new struct type or array type. Not needed for atomic types; see #initializeDefaultTypes."
| structClass arraySpec |
self
assert: [aTypeName last ~~ $*]
description: 'Pointer type will be created automatically'.
self
assert: [aTypeName first ~~ $*]
description: 'Non-pointer type for alias-to-pointer types will be created automatically'.
aTypeName last == $] ifTrue: [ "array type, e.g., char[50]"
arraySpec := self parseArrayTypeName: aTypeName.
arraySpec second ifNil: [arraySpec at: 2 put: (self newTypeNamed: arraySpec first)].
^ self
newTypeForContentType: arraySpec second
size: arraySpec third].
structClass := (self environment classNamed: aTypeName)
ifNotNil: [:class | (class includesBehavior: ExternalStructure) ifTrue: [class]].
^ (structClass isNil or: [structClass isSkipped "i.e., not yet ready, see ExternalTypeAlias"])
ifTrue: [self newTypeForUnknownNamed: aTypeName]
ifFalse: [self newTypeForStructureClass: structClass]!
Item was changed:
----- Method: ExternalType class>>structTypeNamed: (in category 'instance lookup') -----
structTypeNamed: typeName
+ "Thread-safe. Answers the external type for the struct named typeName. If there is no type yet, create a new one but only if typeName can be matched to an existing class in the system already. If you still need a type even if there is no such class present, use #newTypeNamed: to create a type with an unknown referent class."
- "Answers the external type for the struct named typeName. If there is no type yet, create a new one but only if typeName can be matched to an existing class in the system already. If you still need a type even if there is no such class present, use #newTypeNamed: to create a type with an unknown referent class."
+ ^ (StructTypes at: typeName ifAbsent: [nil]) ifNil: [
+ (StructTypesLock ifNil: [StructTypesLock := Mutex new])
+ critical: [ "Make temporary type creation thread-safe for operations such as 'MyStruct new' called from different processes. Note that if you need better performance and/or strong type references, consider using that type either in a call-out signature (see ExternalLibraryFunction) or enable the preference #useTypePool."
+ (StructTypes at: typeName ifAbsent: [nil]) ifNil: [
+ "Create struct types for existing struct classes on-the-fly."
+ StructTypes removeKey: typeName ifAbsent: [].
+ (self environment classNamed: typeName)
+ ifNotNil: [:cls | (cls includesBehavior: ExternalStructure) ifTrue: [
+ self newTypeNamed: typeName]]] ]]!
- ^ (StructTypes at: typeName ifAbsent: [nil])
- ifNil: [ "Create struct types for existing struct classes on-the-fly."
- StructTypes removeKey: typeName ifAbsent: [].
- (self environment classNamed: typeName)
- ifNotNil: [:cls | (cls includesBehavior: ExternalStructure) ifTrue: [
- self newTypeNamed: typeName]]]!
Item was changed:
----- Method: ExternalType class>>typeNamed: (in category 'instance lookup') -----
typeNamed: typeName
+ "Thread-safe. Answer a type object for the given typeName or nil if that name cannot be a type (yet). See commentary in #structTypeNamed:.
+ Supports pointer-type lookup for both atomic and structure types.
- "Supports pointer-type lookup for both atomic and structure types.
Examples: 'long', 'long*', 'long *' or 'MyStruct', 'MyStruct*', 'MyStruct *', 'IntPtr', '*IntPtr' "
| isPointerType isNonPointerType isArrayType actualTypeName type |
isArrayType := false. isNonPointerType := false.
actualTypeName := typeName copyWithoutAll: ' '.
(isPointerType := actualTypeName last == $*) "e.g. MyStruct*"
ifTrue: [actualTypeName := actualTypeName allButLast].
actualTypeName last == $) "e.g. (char[])* -- pointer type for array type"
ifTrue: [actualTypeName := (actualTypeName copyFrom: 2 to: actualTypeName size - 1)].
(isNonPointerType := actualTypeName first == $*) "e.g. *DoublePtr"
ifTrue: [actualTypeName := actualTypeName allButFirst].
(isArrayType := actualTypeName last == $])
ifTrue: [ type := self arrayTypeNamed: actualTypeName ]
ifFalse: [
(Symbol lookup: actualTypeName)
ifNotNil: [:sym | actualTypeName := sym].
type := (self atomicTypeNamed: actualTypeName)
ifNil: [self structTypeNamed: actualTypeName]].
^ type ifNotNil: [
isPointerType
ifTrue: [type asPointerType "e.g. int* MyStruct* "]
ifFalse: [isNonPointerType
ifTrue: [type asNonPointerType "e.g. *IntPtr *MyStructPtr "]
ifFalse: [type "e.g. int IntPtr MyStruct MyStructPtr "]]]!
Item was removed:
- ----- Method: ExternalType class>>voidStructTypeFor: (in category 'housekeeping') -----
- voidStructTypeFor: anExternalStructureClass
- (StructTypes associationAt: anExternalStructureClass name ifAbsent: [^self]) value: nil!
Marcel Taeumel uploaded a new version of FFI-Kernel to project FFI:
http://source.squeak.org/FFI/FFI-Kernel-mt.230.mcz
==================== Summary ====================
Name: FFI-Kernel-mt.230
Author: mt
Time: 16 June 2023, 1:53:15.667813 pm
UUID: a4c0f4c0-a674-2c48-8ac0-f73251d7aa69
Ancestors: FFI-Kernel-mt.229
Revise FFI-Kernel-eem.228 by just updating the check in ExternalType >> #platformChangedFrom:to:. Reverts the extra update logic that was introduced by FFI-Kernel-eem.228.
=============== Diff against FFI-Kernel-mt.229 ===============
Item was changed:
ExternalFunction subclass: #ExternalLibraryFunction
instanceVariableNames: 'name module errorCodeName'
+ classVariableNames: ''
- classVariableNames: 'Instances'
poolDictionaries: ''
category: 'FFI-Kernel'!
!ExternalLibraryFunction commentStamp: '' prior: 0!
An ExternalLibraryFunction specifies a fully qualified function from an external library.
Instance variables:
name <String | Integer> name or ordinal of function
module <String | nil> name of module (nil if bound in the VM).
errorCodeName <String | nil> name of temp receiving error code, if any!
Item was removed:
- ----- Method: ExternalLibraryFunction class>>instances (in category 'private') -----
- instances
- "Cache our instances for faster reinitialization."
- ^Instances ifNil: [Instances := WeakSet withAll: self allInstances]!
Item was removed:
- ----- Method: ExternalLibraryFunction class>>new (in category 'instance creation') -----
- new
- ^self instances add: super new!
Item was removed:
- ----- Method: ExternalLibraryFunction class>>platformChangedFrom:to: (in category 'instance initialization') -----
- platformChangedFrom: lastPlatform to: currentPlatform
- "Byte size or byte alignment for atomic types might be different on the new platform."
-
- "Make sure that the cache of all instances is up-to-date before using it..."
- Instances ifNil: [self new].
-
- "Get all instances to update their compiledSpecs so that FFI calls work."
- self updateArgTypeSpecs!
Item was removed:
- ----- Method: ExternalLibraryFunction class>>updateArgTypeSpecs (in category 'instance initialization') -----
- updateArgTypeSpecs
- self instances do: [:elf| elf updateArgTypeSpecs]!
Item was removed:
- ----- Method: ExternalLibraryFunction>>updateArgTypeSpecs (in category 'instance initialization') -----
- updateArgTypeSpecs
- argTypes ifNotNil:
- [:typeArray| typeArray do: [:argType| argType updateFromReferentClass]]!
Item was changed:
----- Method: ExternalType class>>platformChangedFrom:to: (in category 'system startup') -----
platformChangedFrom: lastPlatform to: currentPlatform
"Byte size or byte alignment for atomic types might be different on the new platform."
lastPlatform pluginVersion ~= currentPlatform pluginVersion
ifTrue: ["Type codes might have changed. Re-init thoroughly. Preserve type identity."
self initialize "Slower but necessary for new type codes"]
+ ifFalse: ["Byte alignment of double and int64_t might have changed. Re-init quickly. Preserve type identity. Also updates all struct types and thus all library function calls."
+ self flag: #performance. "mt: Maybe cache ExternalAtomicType atomicTypeSpecs and compare it to actually detect changes? Well, #initializeFast is even faster if nothing changed for the atomic types, so..."
+ self initializeFast].
- ifFalse: ["Byte alignment of double and int64_t might have changed. Re-init quickly. Preserve type identity."
- currentPlatform wordSize = 4 ifTrue: [self initializeFast]].
self flag: #todo. "mt: Update all critical aliases for atomic types, i.e., intptr_t, uintptr_t. But what about 'c_long' between 64-bit platforms?!!"
"lastPlatform wordSize ~= currentPlatform wordSize
ifTrue: [self recompileAllLibraryFunctions]."!
Item was removed:
- ----- Method: ExternalType>>updateFromReferentClass (in category 'instance initialization') -----
- updateFromReferentClass
- "If I have a referentClass update my compiledSpec to reflect the new state of the system, e.g. after a platform change."
-
- referentClass ifNotNil: [:refClass| self newReferentClass: refClass]!
Item was changed:
----- Method: FFIPlatformDescription class>>startUp: (in category 'system startup') -----
startUp: resuming
"Notify all FFI classes about platform changes."
resuming ifTrue: [
LastPlatform in: [:lastPlatform | self newCurrent in: [:currentPlatform |
lastPlatform = currentPlatform
ifTrue: [
self flag: #discuss. "mt: Maybe add #platformResuming?"
ExternalAddress allBeNull ]
ifFalse: [
LastPlatform := currentPlatform. "Update now. See #current."
+ { ExternalAddress. ExternalType. ExternalPool. ExternalLibrary }
- { ExternalAddress. ExternalType. ExternalPool. ExternalLibrary. ExternalLibraryFunction }
do: [:cls | cls
platformChangedFrom: lastPlatform
to: currentPlatform] ]]].
+ self checkFFIOnStartUp ifTrue: [self checkFFI]].!
- self checkFFIOnStartUp ifTrue: [self checkFFI]]!
Marcel Taeumel uploaded a new version of FFI-Kernel to project FFI:
http://source.squeak.org/FFI/FFI-Kernel-mt.229.mcz
==================== Summary ====================
Name: FFI-Kernel-mt.229
Author: mt
Time: 16 June 2023, 10:02:47.730285 am
UUID: ea58cc5d-3288-9345-8f5d-2b8455dd53ac
Ancestors: FFI-Kernel-eem.228
Improve commentary for FFI-Kernel-eem.226 changes.
=============== Diff against FFI-Kernel-eem.228 ===============
Item was changed:
----- Method: ExternalUnknownType>>typeName (in category 'accessing') -----
typeName
+ "Overwritten to support intermediate state where referentClass is just that class' name at the moment. See #newTypeForStructureClass:. Both cases are important since #becomeUnknownType exists."
+
+ ^ referentClass isSymbol
+ ifTrue: [referentClass]
+ ifFalse: [referentClass name]!
- referentClass isBehavior ifTrue:
- [^referentClass name].
- self assert: [referentClass isSymbol].
- ^ referentClass "Usually just the name of the class."!
Marcel Taeumel uploaded a new version of System to project The Trunk:
http://source.squeak.org/trunk/System-mt.1416.mcz
==================== Summary ====================
Name: System-mt.1416
Author: mt
Time: 16 June 2023, 9:14:18.416484 am
UUID: 9bdd0613-8cb2-ff41-bb40-a6c1a1d83ceb
Ancestors: System-eem.1415
Fixes regression from System-eem.1415. We must clear the PlatformNameCache directly before snapshotting.
Here, Cursor class >> #currentCursor: needs #platformName again but is called after the shutdown code. Might happen elsewhere. This fix documents the issue and brings system-attribute cache-invalidation closer to the snapshot primitive. A VM-based cache might even be better for certain values of primitive 149.
=============== Diff against System-eem.1415 ===============
Item was added:
+ ----- Method: SmalltalkImage>>cleanUpSystemAttributeCaches (in category 'housekeeping') -----
+ cleanUpSystemAttributeCaches
+ "Reset platform-specific values. This MUST BE called directly before the image is snapshotted. Note that this can't safely be done on start-up because Smalltalk is too late in the start-up sequence. See commentary in #getSystemAttribute:."
+
+ self flag: #discuss. "mt: Such caches might better be managed by the VM itself. A primitive-based cache protocol could help. On a fresh (VM) start-up, those values would then be nil (or uninitialized) automatically."
+
+ EndianCache := PlatformNameCache := nil.!
Item was changed:
----- Method: SmalltalkImage>>getSystemAttribute: (in category 'private') -----
getSystemAttribute: attributeID
"Optional. Answer the string for the system attribute with the given
integer ID. Answer nil if the given attribute is not defined on this
platform. On platforms that support invoking programs from command
lines (e.g., Unix), this mechanism can be used to pass command line
arguments to programs written in Squeak.
+
+ NOTE THAT the answered string needs to be allocated. It might thus
+ be useful to cache it for the current session to not stress the scavenger
+ too much. For example, see #platformName.
By convention, the first command line argument that is not a VM
configuration option is considered a 'document' to be filed in. Such a
document can add methods and classes, can contain a serialized object,
can include code to be executed, or any combination of these.
Currently defined attributes include:
-1000 1000th command line argument that specify VM options
...
-1 first command line argument that specify VM options
0 the full path name for currently executing VM
(or, on some platforms, just the path name of the VM's directory)
1 full path name of this image (better use primImageName instead)
2 first command-line argument for Squeak programs.
+ Note: if Preferences readDocumentAtStartup is set, this first
+ argument is treated as a URL to a Squeak document to open.
- Note: if Preferences readDocumentAtStartup is set, this first argument is treated as a URL to a Squeak document to open.
3 second command-line argument for Squeak programs
...
1000 1000th command line argument for Squeak programs
1001 this platform's operating system 'Mac OS', 'Win32', 'unix', ...
1002 operating system version
1003 this platform's processor type
1004 vm version
1005 window system name
1006 vm build id
1007 Interpreter class (Cog VM only)
1008 Cogit class (Cog VM only)
1009 Platform source version (Cog VM only?)
1201 max filename length (Mac OS only)
1202 file last error (Mac OS only)
10001 hardware details (Win32 only)
10002 operating system details (Win32 only)
10003 graphics hardware details (Win32 only)
"
<primitive: 149>
^ nil!
Item was changed:
----- Method: SmalltalkImage>>processShutDownList: (in category 'snapshot and quit') -----
processShutDownList: quitting
+ "Send #shutDown to each class that needs to wrap up before a snapshot."
- "Send #shutDown to each class that needs to wrap up before a snapshot.
- Also void the various caches; this can't safely be done on start-up because
- Smalltalk is too late in the start-up sequence."
+ self send: #shutDown: toClassesNamedIn: ShutDownList with: quitting.!
- self send: #shutDown: toClassesNamedIn: ShutDownList with: quitting.
- EndianCache := PlatformNameCache := nil!
Item was changed:
----- Method: SmalltalkImage>>snapshot:andQuit:withExitCode:embedded: (in category 'snapshot and quit') -----
snapshot: save andQuit: quit withExitCode: exitCode embedded: embeddedFlag
"Mark the changes file and close all files as part of #processShutdownList.
If save is true, save the current state of this Smalltalk in the image file.
If quit is true, then exit to the outer OS shell.
If exitCode is not nil, then use it as exit code.
The latter part of this method runs when resuming a previously saved image. This resume logic checks for a document file to process when starting up."
| resuming msg |
Object flushDependents.
Object flushEvents.
(SourceFiles at: 2) ifNotNil:[
msg := String streamContents: [ :s |
s nextPutAll: '----';
nextPutAll: (save ifTrue: [ quit ifTrue: [ 'QUIT' ] ifFalse: [ 'SNAPSHOT' ] ]
ifFalse: [quit ifTrue: [ 'QUIT/NOSAVE' ] ifFalse: [ 'NOP' ]]);
nextPutAll: '----';
print: Date dateAndTimeNow; space;
nextPutAll: (FileDirectory default localNameFor: self imageName);
nextPutAll: ' priorSource: ';
print: LastQuitLogPosition ].
self assureStartupStampLogged.
save ifTrue: [ LastQuitLogPosition := (SourceFiles at: 2) setToEnd; position ].
self logChange: msg.
Transcript cr; show: msg].
self processShutDownList: quit.
Cursor write show.
+ save ifTrue: [
+ self cleanUpSystemAttributeCaches.
+ resuming := embeddedFlag
+ ifTrue: [self snapshotEmbeddedPrimitive]
+ ifFalse: [self snapshotPrimitive]] "<-- PC frozen here on image file"
- save ifTrue: [resuming := embeddedFlag
- ifTrue: [self snapshotEmbeddedPrimitive]
- ifFalse: [self snapshotPrimitive]] "<-- PC frozen here on image file"
ifFalse: [resuming := false].
(quit and: [resuming == false]) ifTrue:
[exitCode
ifNil: [ self quitPrimitive ]
ifNotNil: [ self quitPrimitive: exitCode ] ].
Cursor normal show.
self startUpPostSnapshot: resuming == true.
Project current wakeUpTopWindow.
"Now it's time to raise an error"
resuming == nil ifTrue: [self error:'Failed to write image file (disk full?)'].
^ resuming!