System-Object Storage

DataStream
This is the save-to-disk facility. A DataStream can store one or more objects in a persistent form.
To handle objects with sharing and cycles, you must use a
ReferenceStream instead of a DataStream. (Or SmartRefStream.) ReferenceStream is typically
faster and produces smaller files because it doesn't repeatedly write the same Symbols.
Here is the way to use DataStream and ReferenceStream:
rr _ ReferenceStream fileNamed: 'test.obj'.
rr nextPut: <your object>.
rr close.
To get it back:
rr _ ReferenceStream fileNamed: 'test.obj'.
<your object> _ rr next.
rr close.
Each object to be stored has two opportunities to control what gets stored. On the high level, objectToStoreOnDataStream allows you to substitute another object on the way out. The low level hook is storeDataOn:. The read-in counterparts to these messages are comeFullyUpOnReload and (class) readDataFrom:size:. See these methods, and the class DiskProxy, for more information about externalizing and internalizing.
NOTE: A DataStream should be treated as a write-stream for writing. It is a read-stream for reading. It is not a ReadWriteStream.
atEnd
Answer true if the stream is at the end.
beginInstance:size:
This is for use by storeDataOn: methods.
Cf. Object>>storeDataOn:.
beginReference:
We're starting to read anObject. Remember it and its reference
position (if we care; ReferenceStream cares). Answer the
reference position.
byteStream
close
Close the stream.
contents
Answer all of the contents of the receiver.
errorWriteReference:
PRIVATE -- Raise an error because this case of nextPut:'s perform:
shouldn't be called. -- 11/15/92 jhm
example
exampleWithPictures
fileNamed:
flush
Guarantee that any writes to me are actually recorded on disk. -- 11/17/92 jhm
getCurrentReference
PRIVATE -- Return the currentReference posn.
Overridden by ReferenceStream.
initialize
Subclasses should redefine this method to perform initializations on instance creation
maybeBeginReference:
Do nothing. See ReferenceStream|maybeBeginReference:
new
newFileNamed:
next
Answer the next object in the stream.
next:
Answer an Array of the next anInteger objects in the stream.
nextAndClose
Speedy way to grab one object. Only use when we are inside an object binary file. Do not use for the start of a SmartRefStream mixed code-and-object file.
nextPut:
Write anObject to the receiver stream. Answer anObject.
nextPutAll:
Write each of the objects in aCollection to the
receiver stream. Answer aCollection.
noteCurrentReference:
PRIVATE -- If we support references for type typeID, remember
the current byteStream position so we can add the next object to
the 'objects' dictionary, and return true. Else return false.
This method is here to be overridden by ReferenceStream
objectAt:
PRIVATE -- Read & return the object at a given stream position. 08:18 tk anInteger is a relative file position.
objectIfBlocked:
We don't do any blocking
oldFileNamed:
on:
outputReference:
PRIVATE -- Output a reference to the object at integer stream position referencePosn (relative to basePos). To output a weak reference to an object not yet written, supply (self vacantRef) for referencePosn.
project
readArray
PRIVATE -- Read the contents of an Array.
We must do beginReference: here after instantiating the Array
but before reading its contents, in case the contents reference
the Array. beginReference: will be sent again when we return to
next, but that's ok as long as we save and restore the current
reference position over recursive calls to next.
readBitmap
PRIVATE -- Read the contents of a Bitmap.
readBoolean
PRIVATE -- Read the contents of a Boolean.
This is here only for compatibility with old data files.
readByteArray
PRIVATE -- Read the contents of a ByteArray.
readClass
Should never be executed because a DiskProxy, not a clas comes in.
readFalse
PRIVATE -- Read the contents of a False.
readFloat
PRIVATE -- Read the contents of a Float.
This is the fast way to read a Float.
We support 8-byte Floats here. Non-IEEE
readFloatString
PRIVATE -- Read the contents of a Float string.
This is the slow way to read a Float--via its string rep'n.
It's here for compatibility with old data files.
readInstance
PRIVATE -- Read the contents of an arbitrary instance.
ASSUMES: readDataFrom:size: sends me beginReference: after it
instantiates the new object but before reading nested objects.
NOTE: We must restore the current reference position after
recursive calls to next.
Let the instance, not the class read the data.
readInteger
PRIVATE -- Read the contents of a SmallInteger.
readMethod
PRIVATE -- Read the contents of an arbitrary instance.
ASSUMES: readDataFrom:size: sends me beginReference: after it
instantiates the new object but before reading nested objects.
NOTE: We must restore the current reference position after
recursive calls to next.
Let the instance, not the class read the data.
readNil
PRIVATE -- Read the contents of an UndefinedObject.
readRectangle
Read a compact Rectangle. Rectangles with values outside +/- 2047 were stored as normal objects (type=9). They will not come here. 17:22 tk
readReference
Read the contents of an object reference. (Cf. outputReference:) File is not now positioned at this object.
readShortInst
Read the contents of an arbitrary instance that has a short header.
ASSUMES: readDataFrom:size: sends me beginReference: after it
instantiates the new object but before reading nested objects.
NOTE: We must restore the current reference position after
recursive calls to next.
Let the instance, not the class read the data.
readShortRef
Read an object reference from two bytes only. Original object must be in first 65536 bytes of the file. Relative to start of data. vacantRef not a possibility.
readString
readStringOld
readSymbol
PRIVATE -- Read the contents of a Symbol.
readTrue
PRIVATE -- Read the contents of a True.
readUser
Reconstruct both the private class and the instance. Still used??
readWordArray
PRIVATE -- Read the contents of a WordArray.
readWordArrayForSegment
Read the contents of a WordArray ignoring endianness.
readWordLike
Can be used by any class that is bits and not bytes (WordArray, Bitmap, SoundBuffer, etc).
replace:with:
We may wish to remember that in some field, the original object is being replaced by the proxy. For the hybred scheme that collects with a DummyStream and writes an ImageSegment, it needs to hold onto the originals so they will appear in outPointers, and be replaced.
reset
Reset the stream.
rootObject
Return the object at the root of the tree we are filing out.
rootObject:
Return the object at the root of the tree we are filing out.
setCurrentReference:
PRIVATE -- Set currentReference to refPosn.
Noop here. Cf. ReferenceStream.
setStream:
PRIVATE -- Initialization method.
setStream:reading:
PRIVATE -- Initialization method.
size
Answer the stream's size.
streamedRepresentationOf:
testWith:
tryToPutReference:typeID:
PRIVATE -- If we support references for type typeID, and if
anObject already appears in my output stream, then put a
reference to the place where anObject already appears. If we
support references for typeID but didn't already put anObject,
then associate the current stream position with anObject in
case one wants to nextPut: it again.
Return true after putting a reference; false if the object still
needs to be put.
For DataStream this is trivial. ReferenceStream overrides this.
typeIDFor:
Return the typeID for anObject's class. This is where the tangle of objects is clipped to stop everything from going out.
Classes can control their instance variables by defining objectToStoreOnDataStream.
Any object in blockers is not written out. See ReferenceStream.objectIfBlocked: and DataStream nextPut:.
Morphs do not write their owners. See Morph.storeDataOn: Each morph tells itself to 'prepareToBeSaved' before writing out.
unStream:
vacantRef
Answer the magic 32-bit constant we use ***ON DISK*** as a stream 'reference
position' to identify a reference that's not yet filled in. This must be a
value that won't be used as an ordinary reference. Cf. outputReference: and
readReference. --
NOTE: We could use a different type ID for vacant-refs rather than writing
object-references with a magic value. (The type ID and value are
overwritten by ordinary object-references when weak refs are fullfilled.)
writeArray:
PRIVATE -- Write the contents of an Array.
writeBitmap:
PRIVATE -- Write the contents of a Bitmap.
writeBoolean:
PRIVATE -- Write the contents of a Boolean.
This method is now obsolete.
writeByteArray:
PRIVATE -- Write the contents of a ByteArray.
writeClass:
Write out a DiskProxy for the class. It will look up the class's name in Smalltalk in the new sustem. Never write classes or methodDictionaries as objects. For novel classes, front part of file is a fileIn of the new class.
writeFalse:
PRIVATE -- Write the contents of a False.
writeFloat:
PRIVATE -- Write the contents of a Float.
We support 8-byte Floats here.
writeFloatString:
PRIVATE -- Write the contents of a Float string.
This is the slow way to write a Float--via its string rep'n.
writeInstance:
PRIVATE -- Write the contents of an arbitrary instance.
writeInteger:
PRIVATE -- Write the contents of a SmallInteger.
writeNil:
PRIVATE -- Write the contents of an UndefinedObject.
writeRectangle:
Write the contents of a Rectangle. See if it can be a compact Rectangle (type=15). Rectangles with values outside +/- 2047 were stored as normal objects (type=9). 17:22 tk
writeString:
PRIVATE -- Write the contents of a String.
writeStringOld:
PRIVATE -- Write the contents of a String.
writeSymbol:
PRIVATE -- Write the contents of a Symbol.
writeTrue:
PRIVATE -- Write the contents of a True.
writeUser:
Write the contents of an arbitrary User instance (and its devoted class).
writeWordLike:
Note that we put the class name before the size.
DeepCopier
DeepCopier does a veryDeepCopy.
It is a complete tree copy using a dictionary. Any object that is in the tree twice is only copied once. All references to the object in the copy of the tree will point to the new copy. See Object|veryDeepCopy which calls (self veryDeepCopyWith: aDeepCopier).
When a tree of morphs points at a morph outside of itself, that morph should not be copied. Use our own kind of weak pointers for the 'potentially outside' morphs. Default is that any new class will have all of its fields deeply copied. If a field needs to be weakly copied, define veryDeepInner: and veryDeepFixupWith:.
veryDeepInner: has the loop that actually copies the fields. If a class defines its own copy of veryDeepInner: (to leave some fields out), then veryDeepFixupWith: will be called on that object at the end. veryDeepInner: can compute an alternate object to put in a field. (Object veryDeepCopyWith: discovers which superclasses did not define veryDeepInner:, and very deeply copies the variables defined in those classes).
To decide if a class needs veryDeepInner: and veryDeepFixupWith:, ask this about an instance: If I duplicate this object, does that mean that I also want to make duplicates of the things it holds onto? If yes, (i.e. a Paragraph does want a new copy of its Text) then do nothing. If no, (i.e. an undo command does not want to copy the objects it acts upon), then define veryDeepInner: and veryDeepFixupWith:.

Here is an analysis for the specific case of a morph being held by another morph.
Does field X contain a morph (or a Player whose costume is a morph)? If not, no action needed.
Is the morph in field X already a submorph of the object? Is it down lower in the submorph tree?
If so, no action needed.
Could the morph in field X every appear on the screen (be a submorph of some other morph)?
If not, no action needed.
If it could, you must write the methods veryDeepFixupWith: and veryDeepInner:, and in them, refrain from sending veryDeepCopyWith: to the contents of field X.
----- Things Ted is still considering -----
Rule: If a morph stores a uniClass class (Player 57) as an object in a field, the new uniClass will not be stored there. Each uniClass instance does have a new class created for it. (fix this by putting the old class in references and allow lookup? Wrong if encounter it before seeing an instance?)
Rule: If object A has object C in a field, and A says (^ C) for the copy, but object B has A in a normal field and it gets deepCopied, and A in encountered first, then there will be two copies of C. (just be aware of it)
Dependents are now fixed up. Suppose a model has a dependent view. In the DependentFields dictionary, model -> (view ...).
If only the model is copied, no dependents are created (no one knows about the new model).
If only the view is copied, it is inserted into DependentFields on the right side. model -> (view copiedView ...).
If both are copied, the new model has the new view as its dependent.
If additional things depend on a model that is copied, the caller must add them to its dependents.
checkBasicClasses
Check that no indexes of instance vars have changed in certain classes. If you get an error in this method, an implementation of veryDeepCopyWith: needs to be updated. The idea is to catch a change while it is still in the system of the programmer who made it.
DeepCopier new checkVariables
checkClass:
Check that no indexes of instance vars have changed in certain classes. If you get an error in this method, an implementation of veryDeepCopyWith: needs to be updated. The idea is to catch a change while it is still in the system of the programmer who made it.
checkDeep
Write exceptions in the Transcript. Every class that implements veryDeepInner: must copy all its inst vars. Danger is that a user will add a new instance variable and forget to copy it. This check is only run by hand once in a while to make sure nothing was forgotten.
(Please do not remove this method.)
DeepCopier new checkDeep
checkVariables
Check that no indexes of instance vars have changed in certain classes. If you get an error in this method, an implementation of veryDeepCopyWith: needs to be updated. The idea is to catch a change while it is still in the system of the programmer who made it.
DeepCopier new checkVariables
fixDependents
They are not used much, but need to be right
initialize
Subclasses should redefine this method to perform initializations on instance creation
initialize:
intervalForChecks
set delay interval for checking for new instance variables to 10 minutes. hg 11/23/1999
isItTimeToCheckVariables
objInMemory:
Test if this global is in memory and return it if so.
references
warnIverNotCopiedIn:sel:
Warn the user to update veryDeepCopyWith: or veryDeepInner:
DiskProxy
A DiskProxy is an externalized form of an object to write on a
DataStream. It contains a "constructor" message to regenerate
the object, in context, when sent a comeFullyUpOnReload message
(i.e. "internalize").
We are now using DiskProxy for shared system objects like StrikeFonts.
The idea is to define, for each kind of object that needs special
externalization, a class method that will internalize the object by
reconstructing it from its defining state. We call this a
"constructor" method. Then externalize such an object as a frozen
message that invokes this method--a DiskProxy.
(Here is the old comment:
Constructing a new object is good for any object that (1) can not be
externalized simply by snapshotting and reloading its instance
variables (like a CompiledMethod or a Picture), or (2) wants to be
free to evolve its internal representation without making stored
instances obsolete (and dangerous). Snapshotting and reloading an
object"s instance variables is a dangerous breach of encapsulation.
The internal structure of the class is then free to evolve. All
externalized instances will be useful as long as the
constructor methods are maintained with the same semantics.
There may be several constructor methods for a particular class. This
is useful for (1) instances with characteristically different
defining state, and (2) newer, evolved forms of an object and its
constructors, with the old constructor methods kept around so old
data can still be properly loaded.)
Create one like this example from class Picture
DiskProxy global: #Picture
selector: #fromByteArray:
args: (Array with: self storage asByteArray)
* See also subclass DiskProxyQ that will construct an object in
the above manner and then send it a sequence of messages. This may save
creating a wide variety of constructor methods. It is also useful because
the newly read-in DiskProxyQ can catch messages like #objectContainedIn:
(via #doesNotUnderstand:) and add them to the queue of messages to
send to the new object.
* We may also want a subclass of DiskProxy that evaluates a string
expression to compute the receiver of the constructor message.
My instance variables:
* globalObjectName -- the Symbol name of a global object in the
System dictionary (usually a class).
* constructorSelector -- the constructor message selector Symbol to
send to the global object (perform:withArguments:), typically a
variation on newFrom:.
* constructorArgs -- the Array of arguments to pass in the
constructor message.
-- 11/9/92 Jerry Morrison
comeFullyUpOnReload:
Internalize myself into a fully alive object after raw loading from a
DataStream. (See my class comment.) DataStream will substitute the
object from this eval for the DiskProxy.
constructorArgs
constructorSelector
enter
Enter the new project
global:preSelector:selector:args:
Initialize self as a DiskProxy constructor with the given
globalNameSymbol, selectorSymbol, and argument Array.
I will internalize by looking up the global object name in the
SystemDictionary (Smalltalk) and sending it this message with
these arguments.
global:selector:args:
Initialize self as a DiskProxy constructor with the given
globalNameSymbol, selectorSymbol, and argument Array.
I will internalize by looking up the global object name in the
SystemDictionary (Smalltalk) and sending it this message with
these arguments.
globalObjectName
loadFromServer
In support of check for newer version in ProjectViewMorph menu
preSelector
preSelector:
printOn:
Try to report the name of the project
simpleGlobalOrNil
Return the object I refer to if it is a simple global in Smalltalk.
storeDataOn:
Besides just storing, get me inserted into references, so structures will know about class DiskProxy.
DummyStream
The purpose of this class is to absorb all steam messages and do nothing. This is so ReferenceStream can pretend to write on it while traversing all objects it would normally write. We need to know what those object are. 8/17/96 tk
nextInt32Put:
do nothing
nextNumber:put:
do nothing
nextPut:
do nothing
nextPutAll:
do nothing
nextStringPut:
do nothing
on:
originalContents
position
Return any random number. Here is where the real lying begins. We are a DummyStream afterall. 8/17/96 tk
position:
Pretend to position wherever the caller says!
skip:
Do nothing.
subclassResponsibility
Do nothing. Most messages to class Stream are defined as subclassResponsibility. Just accept them. 8/17/96 tk
ImageSegment
I represent a segment of Squeak address space. I am created from an
array of root objects. After storing, my segment contains a binary
encoding of every object accessible from my roots but not otherwise
accessible from anywhere else in the system. My segment contains
outward pointers that are indices into my table of outPointers.
The main use of ImageSegments is to store Projects. A dummy
version of SmartRefStream traverses the Project. Everything it finds
is classified as either an object that is owned by the project (only
pointed to inside the project), or an object outside the project that
is pointed to from inside the project. The objects that are
completely owned by the project are compressed into pure binary form
in an ImageSegment. The outside objects are put in the 'outPointers'
array. The entire ImageSegment (binary part plus outPointers) is
encoded in a SmartRefStream, and saved on the disk. (aProject
exportSegmentWithChangeSet:fileName:directory:) calls (anImageSegment
writeForExportWithSources:inDirectory:changeSet:).
Note that every object inside the project is put into the
segment's arrayOfRoots. This is because a dummy SmartRefStream to
scan the project, in order to make intelligent decisions about what
belongs in the project.
See Project's class comment for what messages are sent to
objects as they are unpacked in a new image.

---- Older Details ------

The primary kind of image segment is an Export Segment. It
can be saved on a server and read into a completely different Squeak
image.
Old way to create one:
(ImageSegment new copyFromRootsForExport: (Array with: Baz with: Baz class))
writeForExport: 'myFile.extSeg'.
Old way to create one for a project:
(Project named: 'Play With Me - 3') exportSegment.
To read it into another image: Select 'myFile.extSeg' in a FileList,
Menu 'load as project'. It will install its classes automatically.
If you need to see the roots array, it is temporarily stored in
(SmartRefStream scannedObject).

Most of 'states' of an ImageSegment are not used to export a project,
and have been abandoned.

When a segment is written out onto a file, it goes in a
folder called <image name>_segs. If your image is called
"Squeak2.6.image", the folder "Squeak2.6_segs" must accompany the
image whenever your move, copy, or rename it.
Whenever a Class is in arrayOfRoots, its class (aClass class)
must also be in the arrayOfRoots.
There are two kinds of image segments. Normal image segments
are a piece of a specific Squeak image, and can only be read back
into that image. The image holds the array of outPointers that are
necessary to turn the bits in the file into objects.
To put out a normal segment that holds a Project (not the
current project), execute (Project named: 'xxx') storeSegment.


arrayOfRoots The objects that head the tree we will trace.
segment The WordArray of raw bits of all objects in the tree.
outPointers Oops of all objects outside the segment
pointed to from inside.
state (see below)
segmentName Its basic name. Often the name of a Project.
fileName The local name of the file. 'Foo-23.seg'
endMarker An object located in memory somewhere after a
segment that has
just been brought in. To enumerate the objects in
the segment, start at
the segment and go to this object.
userRootCnt number of roots submitted by caller. Extras
are added in preparation for saving.

state that an ImageSegment may exist in...

#activeCopy (has been copied, with the intent to
become active)
arrayOfRoots, segment, and outPointers have been created by
copyFromRoots:. The tree of objects has been encoded in the segment,
but those objects are still present in the Squeak system.

#active (segment is actively holding objects)
The segment is now the only holder of tree of objects. Each of the
original roots has been transmuted into an ImageSegmentRootStub that
refers back to this image segment. The original objects in the
segment will all be garbageCollected.

#onFile
The segment has been written out to a file and replaced by a file
pointer. Only ImageSegmentRootStubs and the array of outPointers
remains in the image. To get this far:
(ImageSegment new copyFromRoots: (Array with: Baz with: Baz class))
writeToFile: 'myFile.seg'.

#inactive
The segment has been brought back into memory and turned back into
objects. rootsArray is set, but the segment is invalid.

#onFileWithSymbols
The segment has been written out to a file, along with the text of
all the symbols in the outPointers array, and replaced by a file
pointer. This reduces the size of the outPointers array, and also
allows the system to reclaim any symbols that are not referred to
from elsewhere in the image. The specific format used is that of a
literal array as follows:
#(symbol1 symbol2 # symbol3 symbol4 'symbolWithSpaces' # symbol5).
In this case, the original outPointers array was 8 long, but the
compacted table of outPointers retains only two entries. These get
inserted in place of the #'s in the array of symbols after it is read
back in. Symbols with embedded spaces or other strange characters
are written as strings, and converted back to symbols when read back
in. The symbol # is never written out.
NOTE: All IdentitySets or dictionaries must be rehashed when
being read back from this format. The symbols are effectively
internal. (No, not if read back into same image. If a different
image, then use #imported. -tk)

#imported
The segment is on an external file or just read in from one. The
segment and outPointers are meant to be read into a foreign image.
In this form, the image segment can be read from a URL, and
installed. A copy of the original array of root objects is
constructed, with former outPointers bound to existing objects in the
host system.
(Any Class inside the segment MUST be in the arrayOfRoots.
This is so its association can be inserted into Smalltalk. The
class's metaclass must be in roots also. Methods that are in
outPointers because blocks point at them, were found and added to the
roots.
All IdentitySets and dictionaries are rehashed when being
read back from exported segments.)


To discover why only some of the objects in a project are being
written out, try this (***Destructive Test***). This breaks lots of
backpointers in the target project, and puts up an array of
suspicious objects, a list of the classes of the outPointers, and a
debugger.
"Close any transcripts in the target project"
World currentHand objectToPaste ifNotNil: [
self inform: 'Hand is holding a Morph in its paste buffer:\' withCRs,
World currentHand objectToPaste printString].
PV _ Project named: 'xxxx'.
(IS _ ImageSegment new) findRogueRootsImSeg:
(Array with: PV world presenter with: PV world).
IS findOwnersOutPtrs. "Optionally: write a file with owner chains"
"Quit and DO NOT save"

When an export image segment is brought into an image, it is like an
image starting up. Certain startUp messages need to be run. These
are byte and word reversals for nonPointer data that comes from a
machine of the opposite endianness. #startUpProc passes over all
objects in the segment, and:
The first time an instance of class X is encountered, (msg _
X startUpFrom: anImageSegment) is sent. If msg is nil, the usual
case, it means that instances of X do not need special work. X is
included in the IdentitySet, noStartUpNeeded. If msg is not nil,
store it in the dictionary, startUps (aClass -> aMessage).
When a later instance of X is encountered, if X is in
noStartUpNeeded, do nothing. If X is in startUps, send the message
to the instance. Typically this is a message like #swapShortObjects.
Every class that implements #startUp, should see if it needs
a parallel implementation of #startUpFrom:.
aComment
Compact classes are a potential problem because a pointer to the class would not ordinarily show up in the outPointers. We add the classes of all compact classes to outPointers, both for local and export segments.
Compact classes are never allowed as roots. No compact class may be in an Environment that is written out to disk. (In local segments, the compact classes array should never have an ImageSegmentRootStub in it. For export, fileIn the class first, then load a segment with instances of it. The fileIn code can be pasted onto the front of the .extSeg file)
For local segments, a class may become compact while its instances are out on the disk. Or it may become un-compact. A compact class may change shape while some of its instances are on disk. All three cases go through (ClassDescription updateInstancesFrom:). If it can't rule out an instance being in the segment, it reads it in to fix the instances.
See Behavior.becomeCompact for the rules on Compact classes. Indexes may not be reused. This is so that an incoming export segment has its index available. (Changes may be needed in the way indexes are assigned.)
For export segments, a compact class may have a different shape. The normal class reshape mechanism will catch this. During the installation of the segment, objects will have the wrong version of their class momentarily. We will change them back before we get caught.
For export segments, the last two items in outPointers are the number 1717 and an array of the compact classes used in this segment. (The classes in the array are converted from DiskProxies by SmartRefStream.) If that class is not compact in the new image, the instances are recopied.
acceptSingleMethodSource:
activeClasses
activeClassesByCategory
allInstancesOf:do:
Bring me in, locate instances of aClass and submit them to the block. Write me out again.
allObjectsDo:
Enumerate all objects that came from this segment. NOTE this assumes that the segment was created (and extracted). After the segment has been installed (install), this method allows you to enumerate its objects.
arrayOfRoots
arrayOfRoots:
cc:new:current:fake:refStrm:
Sort out all the cases and decide what to do. Every Fake class is uncompacted before having insts converted. As the segment is installed, instances of reshaped compact classes will have the wrong class. Trouble cases:
1) Existing class is compact in the segment and not compact here. Make that compact, (error if that slot is used), load the segment. If an class was just filed in, it is an existing class as far as we are concerned.
2) A compact class has a different shape. We created a Fake class. Load the segment, with instances in the seg having the Wrong Class!! Find the bad instancees, and copy them over to being the real class.
3) An existing class is not compact in the segment, but is in the image. Just let the new instance be uncompact. That is OK, and never reaches this code.
A class that is a root in this segment cannot be compact. That is not allowed.
classNameAt:
comeFullyUpOnReload:
fix up the objects in the segment that changed size. An
object in the segment is the wrong size for the modern version of the
class. Construct a fake class that is the old size. Replace the
modern class with the old one in outPointers. Load the segment.
Traverse the instances, making new instances by copying fields, and
running conversion messages. Keep the new instances. Bulk forward
become the old to the new. Let go of the fake objects and classes.
After the install (below), arrayOfRoots is filled in.
Globalize new classes. Caller may want to do some special install on
certain objects in arrayOfRoots.
May want to write the segment out to disk in its new form.
compactClassesArray
A copy of the real compactClassesArray, but with only the classes actually used in the segment. Slow, but OK for export.
compactIndexAt:
Look in this header word in the segment and find it's compact class index. *** Warning: When class ObjectMemory change, be sure to change it here. ***
compressedFileExtension
copyFromRoots:sizeHint:
Copy a tree of objects into a WordArray segment. The copied objects in the segment are not in the normal Squeak space. If this method yields a very small segment, it is because objects just below the roots are pointed at from the outside. (See findRogueRootsImSeg: for a *destructive* diagnostic of who is pointing in.)
Caller must hold onto Symbols.
To go faster, make sure objects are not repeated in aRootArray and other method directly, with true.
copyFromRoots:sizeHint:areUnique:
Copy a tree of objects into a WordArray segment. The copied objects in the segment are not in the normal Squeak space.
[1] For exporting a project. Objects were enumerated by ReferenceStream and aRootArray has them all.
[2] For exporting some classes. See copyFromRootsForExport:. (Caller must hold Symbols, or they will not get registered in the target system.)
[3] For 'local segments'. outPointers are kept in the image.
If this method yields a very small segment, it is because objects just below the roots are pointed at from the outside. (See findRogueRootsImSeg: for a *destructive* diagnostic of who is pointing in.)
copyFromRootsForExport:
When possible, use copySmartRootsExport:. This way may not copy a complete tree of objects. Add to roots: all of the methods pointed to from the outside by blocks.
copyFromRootsLocalFileFor:sizeHint:
If the roots include a World, add its Player classes to the roots.
copySmartRootsExport:
Use SmartRefStream to find the object. Make them all roots. Create the segment in memory. Project should be in first five objects in rootArray.
declare:
The class just arrived in this segment. How fit it into the Smalltalk dictionary? If it had an association, that was installed with associationDeclareAt:.
declareAndPossiblyRename:
The class just arrived in this segment. How fit it into the Smalltalk dictionary? If it had an association, that was installed with associationDeclareAt:.
deepCopyTest:
ImageSegment new deepCopyTest: Morph withAllSubclasses asArray
dependentsCancel:
Erase the place we temporarily held the dependents of things in this project. So we don't carry them around forever.
dependentsRestore:
Retrieve the list of dependents from the exporting system, hook them up, and erase the place we stored them.
dependentsSave:
Object that have dependents are supposed to be instances of subclasses of Model. But, class Objects still provides 'Global Dependents', and some people still use them. When both the model and the dependent are in a project that is being saved, remember them, so we can hook them up when this project is loaded in.
discoverActiveClasses
doSpaceAnalysis
Capture statistics about the IS and print the number of instances per class and space usage
endianness
Return which endian kind the incoming segment came from
errorWrongState
extract
This operation replaces (using become:) all the original roots of a segment with segmentRootStubs. Thus the original objects will be reclaimed, and the root stubs will remain to bring the segment back in if it is needed.
extractThenInstall
For testing only
fileExtension
findInOut:
Take an array of references to a morph, and try to classify them: in the segment, in outPointers, or other.
findOwnerMap:
Construct a string that has a printout of the owner chain for every morph in the list. Need it as a string so not hold onto them.
findOwnersOutPtrs
findRogueRootsAllMorphs:
This is a tool to track down unwanted pointers into the segment. If we don't deal with these pointers, the segment turns out much smaller than it should. These pointers keep a subtree of objects out of the segment.
1) assemble all objects should be in seg: morph tree, presenter, scripts, metaclasses. Put in a Set.
2) Remove the roots from this list. Ask for senders of each. Of the senders, forget the ones that are in the segment already. Keep others. The list is now all the 'incorrect' pointers into the segment.
findRogueRootsImSeg:
This is a tool to track down unwanted pointers into the segment. If we don't deal with these pointers, the segment turns out much smaller than it should. These pointers keep a subtree of objects out of the segment.
1) Break all owner pointers in submorphs and all scripts.
2) Create the segment and look at outPointers.
3) Remove those we expect.
4) Remember to quit without saving -- the owner pointers are smashed.
findRogueRootsPrep
Part of the tool to track down unwanted pointers into the segment. Break all owner pointers in submorphs, scripts, and viewers in flaps.
findRogueRootsRefStrm:
This is a tool to track down unwanted pointers into the segment. If we don't deal with these pointers, the segment turns out much smaller than it should. These pointers keep a subtree of objects out of the segment.
1) assemble all objects that should be in the segment by using SmartReference Stream and a dummyReference Stream. Put in a Set.
2) Remove the roots from this list. Ask for senders of each. Of the senders, forget the ones that are in the segment already. Keep others. The list is now all the 'incorrect' pointers into the segment.
fixCapitalizationOfSymbols
MultiString>>capitalized was not implemented correctly.
Fix eventual accessors and mutators here.
folder
ifOutPointer:thenAllObjectsDo:
If I point out to anObject, bring me in, Submit all my objects to the block. Write me out again.
install
This operation retrieves the segment if necessary from file storage, installs it in memory, and replaces (using become:) all the root stubs with the reconstructed roots of the segment.
isIntegerObject:
isOnFile
loadSegmentFrom:outPointers:
This primitive will install a binary image segment and return as its value the array of roots of the tree of objects represented. Upon successful completion, the wordArray will have been transmuted into an object of zero length. If this primitive should fail, it will have destroyed the contents of the segment wordArray.
localName
Return the current file name for this segment, a local name in the segments directory.
numberOfFieldsOf:
objectAfter:
Return the object or free chunk immediately following the given object or free chunk in the segment. *** Warning: When class ObjectMemory change, be sure to change it here. ***
objectJunksDo:
originalRoots
Return only the roots that the user submitted, not the ones we had to add.
outPointers
prepareToBeSaved
Prepare objects in outPointers to be written on the disk. They must be able to match up with existing objects in their new system. outPointers is already a copy.
Classes are already converted to a DiskProxy.
Associations in outPointers:
1) in Smalltalk.
2) in a classPool.
3) in a shared pool.
4) A pool dict pointed at directly
printSpaceAnalysisOn:
Capture statistics about the IS and print the number of instances per class and space usage
printTypeOf:
oop is a field pointer, which either is an int or a pointer into the seg or into outPointers
readFromFile
Read in a simple segment. Use folder of this image, even if remembered as previous location of this image
reclaimObsoleteSegmentFiles
rehashSets
I have just been brought in and converted to live objects. Find all Sets and Dictionaries in the newly created objects and rehash them. Segment is near then end of memory, since is was newly brought in (and a new object created for it).
Also, collect all classes of receivers of blocks. Return them. Caller will check if they have been reshaped.
remapCompactClasses:refStrm:
See if our compact classes are compatible with this system. Convert to what the system already has. If we are adding a new class, it has already been filed in. A compact class may not be a root.
reshapeClasses:refStream:
restoreEndianness
Fix endianness (byte order) of any objects not already fixed. Do this by discovering classes that need a startUp message sent to each instance, and sending it.
I have just been brought in and converted to live objects. Find all Sets and Dictionaries in the newly created objects and rehash them. Segment is near then end of memory, since is was newly brought in (and a new object created for it).
Also, collect all classes of receivers of blocks which refer to instance variables. Return them. Caller will check if they have been reshaped.
revert
Pretend this segment was never brought in. Check that it has a fileName. Replace (using become:) all the original roots of a segment with segmentRootStubs. Thus the original objects will be reclaimed, and the root stubs will remain to bring the segment back in if it is needed.
How to use revert: In the project, choose 'save for reverting'.
ReEnter the project. Make changes.
Either exit normally, and change will be kept, or
Choose 'Revert to saved version'.
rootsIncludingBlockMethods
Return a new roots array with more objects. (Caller should store into rootArray.) Any CompiledMethods that create blocks will be in outPointers if the block is held outside of this segment. Put such methods into the roots list. Then ask for the segment again.
rootsIncludingBlocks
For export segments only. Return a new roots array with more objects. (Caller should store into rootArray.) Collect Blocks and external methods pointed to by them. Put them into the roots list. Then ask for the segment again.
rootsIncludingPlayers
Players have been removed from Morphs, this method could now more accurately be renamed rootsIncludingMorphs
savePlayerReferences:
Save our associations we own in the shared References table. They will be installed when the segment is imported.
scanFrom:
Move source code from a fileIn to the changes file for classes in an ImageSegment. Do not compile the methods. They already came in via the image segment. After the ImageSegment in the file, !ImageSegment new! captures control, and scanFrom: is called.
segUpdateInstancesOf:toBe:isMeta:
Bring me in, locate instances of oldClass and get them converted. Write me out again.
segment
segmentCopy
This operation will install a copy of the segment in memory, and return a copy of the array of roots. The effect is to perform a deep copy of the original structure. Note that installation destroys the segment, so it must be copied before doing the operation.
segmentDirectory
segmentName
Return the local file name for this segment.
segmentName:
Local file name for this segment.
smartFillRoots:
Put all traced objects into my arrayOfRoots. Remove some
that want to be in outPointers. Return blockers, an
IdentityDictionary of objects to replace in outPointers.
startUp
state
storeDataOn:
Don't wrote the array of Roots. Also remember the structures of the classes of objects inside the segment.
storeSegmentFor:into:outPointers:
This primitive will store a binary image segment (in the same format as the Squeak image file) of the receiver and every object in its proper tree of subParts (ie, that is not refered to from anywhere else outside the tree). Note: all elements of the reciever are treated as roots indetermining the extent of the tree. All pointers from within the tree to objects outside the tree will be copied into the array of outpointers. In their place in the image segment will be an oop equal to the offset in the outpointer array (the first would be 4). but with the high bit set.
swapOutInactiveClasses
swapOutProjects
testClassFaultOn:
uniqueFileNameFor:
verify:matches:knowing:
verifyCopy
writeForExport:
Write the segment on the disk with all info needed to reconstruct it in a new image. For export. Out pointers are encoded as normal objects on the disk.
writeForExportOn:
Write the segment on the disk with all info needed to reconstruct it in a new image. For export. Out pointers are encoded as normal objects on the disk.
writeForExportWithSources:inDirectory:
Write the segment on the disk with all info needed to reconstruct it in a new image. For export. Out pointers are encoded as normal objects on the disk. Append the source code of any classes in roots. Target system will quickly transfer the sources to its changes file.
writeForExportWithSources:inDirectory:changeSet:
Write the segment on the disk with all info needed to
reconstruct it in a new image. For export. Out pointers are encoded
as normal objects on the disk. Append the source code of any classes
in roots. Target system will quickly transfer the sources to its
changes file.
writeForExportWithSourcesGZ:inDirectory:
Write the segment on the disk with all info needed to reconstruct it in a new image. For export. Out pointers are encoded as normal objects on the disk. Append the source code of any classes in roots. Target system will quickly transfer the sources to its changes file.
writeToFile
writeToFile:
The short name can't have any fileDelimiter characters in it. It is remembered in case the segment must be brought in and then sent out again (see ClassDescription updateInstancesFrom:).
writeToFileWithSymbols
writeToFileWithSymbols:
ImageSegmentRootStub
An ImageSegmentRootStub is a stub that replaces one of the root of an ImageSegment that has been extracted from the Squeak ObjectMemory. It has two very simple roles:
1. If any message is sent to one of these objects, it will be caught by doesNotUnderstand:, and bring about a reinstallation of the missing segment. This exception is caused by the fact that no other messages are defined in this class, and neither does it inherit any from above, since its superclass is nil. When the reinstallation has been accomplished, the message will be resent as though nothing was amiss.
2. If one of these objects is a class, and a message is sent to one of its instances, it will cause a similar fault which will be caught by cannotInterpret:. This exception is caused by a somewhat more subtle condition: the primitive operations of the virtual machine do not have time to check whether classes are resident or not -- they assume that all classes are resident. However every non-cached message lookup does test for a nil in the methodDictionary slot. If a rootStub replaces a class (or any behavior), it masquerades as the class, but it will have a nil in the slot where the method Dictionary is expected. This will cause the VM to send cannotInterpret:, eventually leading to the same process for reinstalling the missing segment and resending the message as above.
Just to be on the safe side, a rootStub that replaces a Behavior also carries a copy of both the superclass and format fields from the original class. This insures that, even if some operations of the VM require these values, things will continue to operate properly when the segment is absent.
doLogFaults
doesNotUnderstand:
Any normal message sent to this object is really intended for another object that is in a non-resident imageSegment. Reinstall the segment and resend the message.
dontLogFaults
faultLogs
isInMemory
We are a place holder for an object that is out.
startLoggingFaults
stopLoggingFaults
xxSuperclass:format:segment:
Set up fields like a class but with null methodDict
xxxClass
Primitive. Answer the object which is the receiver's class. Essential. See
Object documentation whatIsAPrimitive.
xxxSegment
ObjectScanner
An instance of this class is the compiler's context for filing in a SmartRefStream containing instance-specific classes. When the old name of a new object's class conflicts with an existing class name, install a class var in me. It has the old name but points at the new class. The compiler uses it when compiling the code in the fileIn. Fill the SmartRefStream's renamed class dictionary.
An object fileout:
!ObjectScanner new initialize! "allow me to take control with scanFrom:"
Player subclass: Player23 instanceVariableNames: 'foo' classVariableNames: ''
poolDictionaries: nil category: 'Instance Specific'!
"I prescan this and (self rename: #Player23 toBe: #Player30)"
!Player23 methodsFor: 'all' stamp: 'tk 3/9/98 18:58'! "actually sent to Player30"
foo
^ foo! !
!self smartRefStream!<binary representation of the objects>!
clear
remove all old class vars. They were UniClasses being remapped to aviod a name conflict.
initialize
remove all old class vars that are not instance-specific classes being renamed
lookAhead:
See if this chunk is a class Definition, and if the new class name already exists and is instance-specific. Modify the chunk, and record the rename in the SmartRefStream and in me.
rename:toBe:
See if there is a conflict between what the fileIn wants to call the new UniClass (Player23) and what already exists for another unique instance. If conflict, make a class variable to intercept the existingName and direct it to class newName.
scanFrom:
Sieze control of the fileIn. Put myself in as the context. If any UniClasses (for just one instance) are defined, they will do it through me, and I will look for conflicting class names. If so, install the old name as a class var of me, so the compile will work. Tell my SmartRefStream about renaming the class.
smartRefStream
ReferenceStream
This is a way of serializing a tree of objects into disk file. A ReferenceStream can store
one or more objects in a persistent form, including sharing and cycles.
Here is the way to use DataStream and ReferenceStream:
rr _ ReferenceStream fileNamed: 'test.obj'.
rr nextPut: <your object>.
rr close.
To get it back:
rr _ ReferenceStream fileNamed: 'test.obj'.
<your object> _ rr next.
rr close.
ReferenceStreams can now write "weak" references. nextPutWeak:
writes a "weak" reference to an object, which refers to that object
*if* it also gets written to the stream by a normal nextPut:.
A ReferenceStream should be treated as a read-stream *or* as a write-stream, *not* as a read/write-stream. The reference-remembering mechanism would probably do bad things if you tried to read and write from the same ReferenceStream.
[TBD] Should we override "close" to do (self forgetReferences)?
Instance variables
references -- an IdentityDictionary mapping objects already written
to their byteStream positions. If asked to write any object a
second time, we just write a reference to its stream position.
This handles shared objects and reference cycles between objects.
To implement "weak references" (for Aliases), the references
dictionary also maps objects not (yet?) written to a Collection
of byteStream positions with hopeful weak-references to it. If
asked to definitely write one of these objects, we'll fixup those
weak references.
objects -- an IdentityDictionary mapping relative byte stream positions to
objects already read in. If asked to follow a reference, we
return the object already read.
This handles shared objects and reference cycles between objects.
currentReference -- the current reference position. Positon relative to the
start of object data in this file. (Allows user to cut and paste smalltalk
code from the front of the file without effecting the reference values.)
This variable is used to help install each new object in "objects" as soon
as it's created, **before** we start reading its contents, in
case any of its content objects reference it.
fwdRefEnds -- A weak reference can be a forward reference, which
requires advance-reading the referrent. When we later come to the
object, we must get its value from "objects" and not re-read it so
refs to it don't become refs to copies. fwdRefEnds remembers the
ending byte stream position of advance-read objects.
skipping -- true if <what?>
insideASegment -- true if we are being used to collect objects that will be
included in an ImageSegment. If so, UniClasses must be noted and traced.
If the object is referenced before it is done being created, it might get created twice. Just store the object the moment it is created in the 'objects' dictionary. If at the end, comeFullyUpOnReload returns a different object, some refs will have the temporary object (this is an unlikely case). At the moment, no implementor of comeFullyUpOnReload returns a different object except DiskProxy, and that is OK.
beginInstance:size:
This is for use by storeDataOn: methods. Cf. Object>>storeDataOn:.
beginReference:
Remember anObject as the object we read at the position recorded by
noteCurrentReference:. This must be done after instantiating anObject but
before reading any of its contents that might (directly or indirectly) refer to
it. (It's ok to do this redundantly, which is convenient for #next.)
Answer the reference position.
blockers
blockers:
maps objects -> nil if they should not be written. object -> anotherObject if they need substitution.
example2
getCurrentReference
PRIVATE -- Return the currentReference posn. Always a relative position. So user can cut and paste the Smalltalk source code at the beginning of the file.
insideASegment
insideASegment:
isAReferenceType:
Return true iff typeID is one of the classes that can be written as a reference to an instance elsewhere in the stream.
maybeBeginReference:
See if need to record a reference. In case in the file twice
next
Answer the next object in the stream. If this object was already read, don't re-read it. File is positioned just before the object.
nextPutWeak:
Write a weak reference to anObject to the receiver stream. Answer anObject.
If anObject is not a reference type of object, then just put it normally.
A 'weak' reference means: If anObject gets written this stream via nextPut:,
then its weak references will become normal references. Otherwise they'll
read back as nil. --
noteCurrentReference:
PRIVATE -- If we support references for type typeID, remember
the current byteStream position so beginReference: can add the
next object to the 'objects' dictionary of reference positions,
then return true. Else return false.
objectAt:
PRIVATE -- Read & return the object at a given stream position.
If we already read it, just get it from the objects dictionary.
(Reading it again wouldn't work with cycles or sharing.)
If not, go read it and put it in the objects dictionary.
NOTE: This resolves a cross-reference in the ReferenceStream:
1. A backward reference to an object already read (the normal case).
2. A forward reference which is a sated weak reference (we record where
the object ends so when we get to it normally we can fetch it from
'objects' and skip over it).
3. A backward reference to a 'non-reference type' per the long NOTE in
nextPut: (we compensate here--seek back to re-read it and add the object
to 'objects' to avoid seeking back to read it any more times).
4. While reading a foward weak reference (case 2), we may recursively hit an
ordinary backward reference to an object that we haven't yet read because
we temporarily skipped ahead. Such a reference is forward in time so we
treat it much like case 2.
11/16-24/92 jhm: Handle forward refs. Cf. class comment and above NOTE.
08:57 tk anInteger is a relative position
objectIfBlocked:
See if this object is blocked -- not written out and another object substituted.
on:
project
Return the project we are writing or nil
projectChangeSet
The changeSet of the project we are writing
refTypes:
references
replace:with:
We may wish to remember that in some field, the original object is being replaced by the proxy. For the hybred scheme that collects with a DummyStream and writes an ImageSegment, it needs to hold onto the originals so they will appear in outPointers, and be replaced.
reset
PRIVATE -- Reset my internal state.
11/15-17/92 jhm: Added transients and fwdRefEnds.
7/11/93 sw: Give substantial initial sizes to avoid huge time spent growing.
9/3/93 sw: monster version for Sasha
setCurrentReference:
PRIVATE -- Set currentReference to refPosn. Always a relative position.
setStream:
PRIVATE -- Initialization method.
setStream:reading:
PRIVATE -- Initialization method.
statisticsOfRefs
Analyze the information in references, the objects being written out
tryToPutReference:typeID:
PRIVATE -- If we support references for type typeID, and if
anObject already appears in my output stream, then put a
reference to the place where anObject already appears. If we
support references for typeID but didn't already put anObject,
then associate the current stream position with anObject in
case one wants to nextPut: it again.
Return true after putting a reference; false if the object still
needs to be put.
: Added support for weak refs. Split out outputReference:.
08:42 tk references stores relative file positions.
versionCode
SmartRefStream
Ordinary ReferenceStreams assume that the names and order of instance variables is exactly the same when an object file is written and read.
SmartRefStream allows object files to be read even after instance variables have changed or the entire class has been renamed.
When an object file is written, no one knows how the classes will change in the future. Therefore, all conversion must be done when the file is read. The key is to store enough information in the file about the names of the instance variables of all outgoing classes.
SmartRefStream works best with only one tree of objects per file. You can nextPut: more than once, but each object tree gets its own class structure description, which is big.
Conversion of old objects is done by a method in each class called (convertToCurrentVersion: varDict refStream: smartRefStrm). At fileOut time, ChangeSet>>checkForConversionMethods creates a prototype of this method (if Preference #conversionMethodsAtFileOut is true). The programmer must edit this method to (1) test if the incoming object needs conversion, (2) put non-nil values into any new inst vars that need them, and (3) save the data of any inst vars that are being deleted.
Determining which old version is represented by the incoming object can be done in several ways: noticing that a current inst var is nil when it should have data, noticing that there is an older inst var name in the variable dictionary (varDict), checking kinds of objects in one or more inst vars, or retrieving the classVersion of the incoming object from the ref stream.
If a class is renamed, a method goes into SmartRefStream telling the new name. The conversion method of the new class must be prepared to accept instances of the old class also. If no inst var names have changed, the conversion method does nothing.
An example:
Suppose we change the representation of class Rectangle from ('origin' 'corner') to ('origin' 'extent'). Suppose lots of Rectangle instances are already out on files (in .pr project files, especially).
The programmer changes the class definition, modifies all the methods, and filesOut. A series of dialogs appear, asking if instances Rectangle might be in an object file, if 'extent' needs to be non-nil (yes), and if the info in 'corner' needs to be preserved (yes). This method appears:
Rectangle >> convertToCurrentVersion: varDict refStream: smartRefStrm
"These variables are automatically stored into the new instance: #('origin').
Test for this particular conversion. Get values using expressions like (varDict at: 'foo')."
"New variables: #('extent'). If a non-nil value is needed, please assign it."
"These are going away #('corner'). Possibly store their info in some other variable?"
"Move your code above the ^ super... Delete extra comments."
^ super convertToCurrentVersion: varDict refStream: smartRefStrm
The programmer modifies it to be:
Rectangle >> convertToCurrentVersion: varDict refStream: smartRefStrm
(varDict includesKey: 'extent') ifFalse: ["old version!"
"Create the new extent, and preserve the info from the old corner"
extent _ (varDict at: 'corner') - origin.
].
^ super convertToCurrentVersion: varDict refStream: smartRefStrm
This conversion method stays in the system and is ready to convert the old format of Rectangle whenever one is encountered in an object file. Note that the subclasses of Rectangle, (B3DViewport, CharacterBlock, and Quadrangle) do not need conversion methods. Their instances will be converted by the code in Rectangle.
Files written by SmartRefStream are in standard fileout format. You can mix raw objects with code to be filed in. The file starts out in the normal fileOut format. Definitions of new classes on the front.
structures Dictionary of (#Rectangle -> #(<classVersionInteger> 'origin' 'corner')). Inst
var names are strings.
steady Set of Classes who have the same structure now as on the incoming file.
Includes classes with same inst vars except for new ones added on the end.
reshaped Dictionary of Classes who have a different structure now from the incoming file.
Includes those with same inst vars but new version number.
(old class name -> method selector to fill in data for version to version)
renamed Dictionary of Classes who have a different name. Make an instance of the new
class, and send it the conversion call.
(old class name symbol -> new class name).
renamedConv Dictionary of conversion selector for Classes who have a different name.
(old class name symbol -> conversion selector).
topCall Tells if next or nextPut: are working on the top object in the tree.
nil if outside, the top object if deep inside.
See DataStream.typeIDFor: for where the tangle of objects is clipped, so the whole system will not be written on the file.
No object that is written on the file is ever a class. All class definitions are filed in. A class may be stored inside an ImageSegment that itself is stored in a SmartRefStream.
UniClasses are classes for the instance specific behavior of just one instance. Subclasses of Player are an example. When a UniClass is read in, and a class of the same name already exists, the incoming one is renamed. ObjectScanner converts the filed-in code.
Values in instance variables of UniClasses are stored in the array that tells the class structure. It is the fourth of the four top level objects. #(version (class-structure) the-object ((#Player25 scripts slotInfo costumeDictionary) (#Player26 scripts slotInfo costumeDictionary))).
There is a separate subclass for doing veryDeepCopy (in memory). Currently, any object for which objectToStoreOnDataStream return an object other than self, does this: The new object (a DiskProxy) is traced. When it comes time to go through the fields of the old object, they are not found as keys in references (DiskProxies are there instead). So the old field value is left in the new object. That is OK for StrikeFont, Class, MetaClass, DisplayScreen. But the DiskProxies are evaluated, which takes a lot of time.
Some metaclasses are put into the structures table. This is for when a block has a receiver that is a class. See checkFatalReshape:.
ImageSegments:
A ReferenceStream is used to enumerate objects to put inside an ImageSegment. If an instance of a UniClass is seen, the class is put in also.
A SmartRefStream is used to store the ImageSegment. Roots are nil, and the segment is a wordArray. We are encoding the outPointers. Structures contains all classes from both places. Must filter out UniClasses for some things, and do include them for putting source code at end of file. Do not write any class inst vars in file.
--Ted Kaehler and Bob Arning.
abstractStringx0
appendClassDefns
Make this a fileOut format file. For each UniClass mentioned, prepend its source code to the file. Class name conflicts during reading will be resolved then. Assume instVarInfo: has already been done.
applyConversionMethodsTo:className:varMap:
Modify the object's instance vars to have the proper values
for its new shape. Mostly, fill in defaut values of new inst vars.
Can substitute an object of a different class. (Beware: if
substituted, varMap will not be correct when the new object is asked
to convert.)
catalogValues:size:
Create a dictionary of (name -> value) for the inst vars of this reshaped object. Indexed vars as (1 -> val) etc.
checkCrLf
Watch for a file that has had all of its Cr's converted to CrLf's. Some unpacking programs like Stuffit 5.0 do this by default!
checkFatalReshape:
Inform the user if any of these classes were reshaped. A block has a method from the old system whose receiver is of this class. The method's inst var references might be wrong. OK if inst vars were only added.
cleanUpCategories
clippingMorphbosfcep0
conversionMethodsFor:
Each of these needs a conversion method. Hard part is the comment in it. Return a MessageSet.
convert1:to:allVarMaps:
Go through the normal instance conversion process and return a modern object.
convert2:allVarMaps:
Go through the normal instance conversion process and return a modern object.
dropShadowMorphbosfces0
initKnownRenames
initShapeDicts
Initialize me.
instVarInfo:
Return the object to write on the outgoing file that contains the structure of each class we are about to write out. Must be an Array whose first element is 'class structure'. Its second element is a Dictionary of pairs of the form #Rectangle -> #(<classVersion> 'origin' 'corner').
layoutMorphbosfcepbbochvimol0
layoutMorphbosfcepcbbochvimol0
mapClass:
See if the old class named nm exists. If so, return it. If not, map it to a new class, and save the mapping in renamed.
mapClass:origName:
See if instances changed shape. If so, make a fake class for the old shape and return it. Remember the original class name.
moreObjects
Return true if there appears to be another object following this one on the file.
morphicEventtcbks0
morphicSoundEventtcbkss0
multiStringx0
multiSymbolx0
myMorphbosfce0
newMorphicEventts0
next
Really write three objects: (version, class structure, object). But only when called from the outside.
nextAndClose
Speedy way to grab one object. Only use when we are inside an object binary file. If used for the start of a SmartRefStream mixed code-and-object file, tell the user and then do the right thing.
nextPut:
Really write three objects: (version, class structure, object). But only when called from the outside. If any instance-specific classes are present, prepend their source code. byteStream will be in fileOut format.
You can see an analysis of which objects are written out by doing:
(SmartRefStream statsOfSubObjects: anObject)
(SmartRefStream tallyOfSubObjects: anObject)
(SmartRefStream subObjects: anObject ofClass: aClass)
nextPutObjOnly:
Really write three objects: (version, class structure, object). But only when called from the outside. Not in fileOut format. No class definitions will be written for instance-specific classes. Error if find one. (Use nextPut: instead)
noHeader
Signal that we've already dealt with the version and structure array, and are now reading objects.
objectFromStreamedRepresentation:
read:withClasses:
readInstance
Read the contents of an arbitrary instance.
ASSUMES: readDataFrom:size: sends me beginReference: after it
instantiates the new object but before reading nested objects.
NOTE: We must restore the current reference position after
recursive calls to next.
Three cases for files from older versions of the system:
1) Class has not changed shape, read it straight.
2) Class has changed instance variables (or needs fixup). Call a particular method to do it.
3) There is a new class instead. Find it, call a particular method to read.
All classes used to construct the structures dictionary *itself* need to be in 'steady' and they must not change! See setStream:
readInstanceSize:clsname:refPosn:
The common code to read the contents of an arbitrary instance.
ASSUMES: readDataFrom:size: sends me beginReference: after it
instantiates the new object but before reading nested objects.
NOTE: We must restore the current reference position after
recursive calls to next.
Three cases for files from older versions of the system:
1) Class has not changed shape, read it straight.
2) Class has changed instance variables (or needs fixup). Call a particular method to do it.
3) There is a new class instead. Find it, call a particular method to read.
All classes used to construct the structures dictionary *itself* need to be in 'steady' and they must not change! See setStream:
readShortInst
Instance has just one byte of size. Class symbol is encoded in two bytes of file position. See readInstance.
readWordLike
Can be used by any class that is bits and not bytes (WordArray, Bitmap, SoundBuffer, etc).
recordImageSegment:
Besides the objects being written out, record the structure of instances inside the image segment we are writing out.
renamed
renamedConv
reshapedClassesIn:
Look for classes in the outPointer array that have changed shape. Make a fake class for the old shape. Return a dictionary mapping Fake classes to Real classes. Substitute fake classes for real ones in outPointers.
saveClassInstVars
Install the values of the instance variables of UniClasses.
classInstVars is an array of arrays (#Player3 (Player3 class's inst var
scripts) (Player3 class's inst var slotInfo) ...)
scanFrom:
During a code fileIn, we need to read in an object, and stash it in ScannedObject.
scannedObject
scannedObject:
setStream:
Initialize me.
setStream:reading:
Initialize me.
statsOfSubObjects:
storeInstVarsIn:from:
For instance variables with the same names, store them in the new instance. Values in variable-length part also. This is NOT the normal inst var transfer! See Object.readDataFrom:size:. This is for when inst var names have changed and some additional conversion is needed. Here we handle the unchanged vars.
streamedRepresentationOf:
structures
structures:
subObjects:ofClass:
superclasses
superclasses:
tallyOfSubObjects:
transparentColorrcc0
uniClasesDo:
Examine structures and execute the block with each instance-specific class
uniClassInstVarsRefs:
If some of the objects seen so far are instances UniClasses, check the UniClasses for extra class inst vars, and send them to the steam also. The new objects get added to (dummy references), where they will be noticed by the caller. They will wind up in the structures array and will be written on the disk by class.
Return all classes seen.
verifyStructure
Compare the incoming inst var name lists with the existing classes. Prepare tables that will help to restructure those who need it (renamed, reshaped, steady). If all superclasses are recorded in the file, only compare inst vars of this class, not of superclasses. They will get their turn.
versionSymbol:
Create the symbolic code (like a version number) for this class in some older version. First initials of all the inst vars, followed by the class version number. Returns a string, caller makes it into a compound selector.
worldMorphbosfcebbfgccpmcpbttloiairfidcuwhavcdsll0
writeClassRename:was:
Write a method that tells which modern class to map instances to.
writeClassRenameMethod:was:fromInstVars:
The class coming is unknown. Ask the user for the existing class it maps to. If got one, write a method, and restart the obj fileIn. If none, write a dummy method and get the user to complete it later.
writeConversionMethod:class:was:fromInstVars:to:
The method convertToCurrentVersion:refStream: was not found in newClass. Write a default conversion method for the author to modify.
writeConversionMethodIn:fromInstVars:to:renamedFrom:
The method convertToCurrentVersion:refStream: was not found in newClass. Write a default conversion method for the author to modify. If method exists, append new info into the end.