Académique Documents
Professionnel Documents
Culture Documents
0
Ole Agesen Lars Bak Craig Chambers Bay-Wei Chang Urs Hlzle John Maloney Randall B. Smith David Ungar Mario Wolczko Welcome to SELF 3.0. This manual is designed to take you through the steps needed to install and start using SELF. It has three parts: Getting Started Exploring SELF Programming and Debugging We hope you enjoy using SELF. Good luck! The SELF Group
Copyright (c) 1993, Sun Microsystems, Inc. and Stanford University. All Rights Reserved. Sun Microsystems, Inc 2550 Garcia Avenue Mountain View, CA 94043 USA RESTRICTED RIGHTS LEGEND: Use, duplication, or disclosure by the government is subject to restrictions as set forth in subparagraph (c) (1) (ii) of the Rights in Technical Data and Computer Software Clause at DFARS 252.227-7013 (Oct. 1988) and FAR 52.227-19(c) (June 1987). SOFTWARE LICENSE: The software described in this manual may be used internally, modied, copied and distributed to third parties, provided each copy of the software contains both the copyright notice set forth above and the disclaimer below. DISCLAIMER: Sun Microsystems, Inc. makes no representations about the suitability of this software for any purpose. It is provided to you "AS IS", without express or implied warranties of any kind. Sun Microsystems, Inc. disclaims all implied warranties of merchantability, tness for a particular purpose and non-infringement of third party rights. Sun Microsystems, Inc.'s liability for claims relating to the software shall be limited to the amount, if any of the fees paid by you for the software. In no event will Sun Microsystems, Inc. be liable for any special, indirect, incidental, consequential or punitive damages in connection with or arising out of this license (including loss of prots, use, data, or other economic advantage), however it arises, whether for breach of warranty or in tort, even if Sun Microsystems, Inc. has been advised of the possibility of such damage. 1
costs. Archive le
README Install Self.tar.gz Optional.Snapshot.tar.gz
Contents Describes how to install the system (similar to this chapter). Shell script to perform the installation. Archive containing the basic SELF system: the SELF virtual machine and SELF source les describing the SELF world. Contains a snapshot of the SELF world after reading in medium.self. An equivalent snapshot can be generated from the SELF source les in Self.tar.gz. Contains source code for the SELF virtual machine, roughly 115,000 lines of C++. An installation of GNU g++ version 2.4.5 is required to compile this code. This le is only necessary if you want to study or change the virtual machine or use the glue (see section 25 of the Reference Manual).
Optional.VM.tar.gz
Create a temporary directory to hold the archive les and retrieve README, Install, and
2
Self.tar.gz into this directory. The other three les, Optional.Snapshot.tar.gz, Optional.Glue.tar.gz, and Optional.VM.tar.gz are optional.
The archive les can be deleted when the site installation is complete.
The available number is the amount of swap space available for SELF. 1. Now, subtract that number from the amount of swap space that the congure script said you need for SELF. The difference is the amount of swap space you must add. 2. Find a disk with enough free space to hold the additional swap le. (You can use the UNIX df command for this.) 3. Become superuser. 4. Create a swap le of the desired size:
# mkfile 12m /u1/mySwapFile
(The example creates a 12MB le named mySwapFile on the disk /u1. Any name may be chosen for the swap le. Remember to put this le on the disk you found in step 3.) 5. Under SunOS 4.x, edit /etc/fstab, adding the following line:
/u1/mySwapFile swap swap rw,noquota 0 0 (Assuming that your swap le is named /u1/mySwapFile.)
Under Solaris 2.x skip this item. 6. Make the new swap le available to the system. Under SunOS 4.x type:
# swapon -a
% Self Self Virtual Machine Version 3.0, Feb 30 1993 08:50:12 Copyright 1989-93: The Self Group (type _Credits for credits) reading ~/.selfrc VM#
Upon entering SELF, you are greeted by the prompt VM#, indicating that the system is ready to accept input. You can now enter a SELF expression. This expression will be evaluated in the context of an object called the lobby, i.e., any message with an implicit receiver will be sent to the lobby. SELF will evaluate the expression and print the result. The bare SELF world has only a very few predened objectsthe ones that are absolutely necessary for bootstrapping the system, like true and the prototypical string object (see section 23 of the Reference Manual). Thus, even basic methods like integer + are not dened:
VM# 3 + 4 A lookup error happened while sending the message "+" to <1>. The receiver also did not understand the lookup error message "undefinedSelector:Type:Delegatee:MethodHolder:Arguments:" which was subsequently sent to it. -- Self VM #0 (<error>:1): + = ( | _ self* = 3. _ :arg1 = 4. | "undefined selector error; this method automatically generated by the VM." ) #1 (<stdin:1) <top level expr> = ( | _ self* = lobby. | 3 + 4) VM#
Ignore the details of the error message for the moment; they will be described in a later section. You only need to understand that the + message is not dened at this point. However, there are numerous primitives dened by the system, such as _IntAdd: for integer addition:
VM# 3 _IntAdd: 4 7: ( | ^ parent* = <0>. | )
The lobby objects special status as the receiver of messages evaluated in the root context makes it desirable to put the lobby in a central place in the SELF inheritance graph, and in fact, most objects in the default world inherit from the lobby. See the SELF World manual. 5
Normally, you will not work with the system in this crude, initial state; rather, youll use more comfortable worlds created by reading in scripts or snapshots. Later sections explain how to do these things.
VM activity
VM memory
object memory
code cache
The indicators in the left part of the display correspond to various internal activities and events. The bars on the right show how memory is being used. To understand how to interpret this display, see Appendix G of the Reference Manual. The spy can be useful simply to reassure you that SELF is still running during the longish pauses that sometimes occur when the garbage collector runs. We suggest that you start the spy now.
By default, the spy displays on $DISPLAY,the default display for graphical output. If you wish to specify a different display, use the _SpyXDisplay: message to name another screen, such as obj:0.1.
Type
Control-Z, then kill -9 % to the UNIX shell _Quit _Quit
Table 1 summarizes the various ways to exit the SELF virtual machine. The rst way forcefully exits the virtual machine no matter what it is currently doing and returns to the UNIX shell.
In this example, the loop was interrupted by typing Control-C, and the process menu was used to abort the process. If the user had typed q to quit the scheduler, all current processes would have been aborted along with the scheduler itself:
... -----------------------------------------Select a process (or q to quit scheduler): q Scheduler shut down. -----------------------------------------prompt VM#
The scheduler has been stopped, returning the user to the VM# prompt. The command prompt start restarts the scheduler:
VM# prompt start Self 11>
Although the VM# prompt can be used to evaluate expressions directly, the scheduler supports much nicer error messages and debugging, so it is usually best to run the scheduler. (The scheduler is started automatically when the default world is created.)
4.3 Summary
Table 2 Interrupting and Exiting SELF Control-C
Interrupts current SELF process via SELF scheduler The current prompt is Self 17> (the SELF scheduler is running) The current prompt is VM# (the SELF scheduler is not running) At prompt Running SELF code At prompt Running SELF code process menu process menu no effect no effect
Control-\
Interrupts current SELF process directly process menu process menu no effect returns to VM# prompt
Control-D
Signals end-of-le when reading standard input exits scheduler (goes to VM# prompt) no effect no effect no effect
_Quit
VM quit primitive
Certain virtual machine operations like garbage collection, reading a snapshot, and compilation cannot be interrupted; interrupts during these operations will be deferred until the operation is complete. As a last resort (e.g., if the system appears to be hung), you can force an abort by pressing Control-\ ve times in a row.
2. Check the directory path that SELF will use to nd source les:
VM# _DirPath '.:~/self/self'
The result string is a list of directories, separated by colons. A zero-length directory name is an abbreviation for the current directory. In particular, if _DirPath is the empty string, then the path includes just the current directory. Paths may also include the tilde character (~), which is expanded as it is in the UNIX C-shell. 3. If the directory containing your source les is not in the path, then change the path:
VM# _DirPath: 'myDirectory'
You can define a default value for _DirPath by including a line like _DirPath: '.:/tmp:~smith/self' in a file named .selfrc in your login directory.
4. (Optional, but recommended.) Start the spy so you can watch the world ll up with objects:
VM# _Spy: true
5. Read in the default world. To do this, ask SELF to read expressions from the le all.sm:
VM# 'all.sm' _RunScript
Unless you have asked SELF not to print script names, you should see something like:
6. After all the les have been read in, SELF will start the process scheduler, initialize its cache of path names of well-known objects, and print:
refilling path cache...done. 1705 entries Self 0>
That last line is the SELF prompt indicating that the virtual machine is ready to read and evaluate expressions. Congratulations! You are now ready to explore the world of SELF objects. But, rst you should save the world.
The next time you start SELF, you can just read in this snapshot (Snapshot _ReadSnapshot) and start working; you will not need to rebuild the default world.
10
Now that you have built yourself a default SELF world, you are ready to read in your snapshot and explore this world using either text-based interactions or the graphical user interface.
When the Self 0> prompt appears, you are ready to type in SELF expressions and explore the SELF world. The snapshot le name can be a relative or absolute path name, and tildes will be expanded as they are in the UNIX C-shell. If the rst component in the le name is not the root, tilde, or the current directory, then SELF will search the sequence of directories set by _DirPath: for a le of the given name.
As a shortcut, you can just type the name of the snapshot to the UNIX shell as if it were a command; UNIX will run whatever version of the SELF virtual machine it nds in your search path and then read the snapshot.
11
_DirPath: '~/mySelfDirectory' _SnapshotCode: true shell _AddSlotsIfAbsent: ( | spyOn = ( _Spy: true ). spyOff = ( _Spy: false ). gc = ( _GarbageCollect ). quit = ( _Quit ). | )
set the VMs search path VM option to save compiled code with snapshot Command shortcuts added to the shell object: turn spy on turn spy off run garbage collector quit
The .selfrc le is executed immediately after SELF starts up. If you are starting up a snapshot, the .selfrc le is executed before the snapshot is read in. Thus, any command shortcuts or other changes to the SELF world made by the .selfrc le are overwritten when the snapshot is read in. However, virtual machine settings like _DirPath: and _SnapshotCode: survive.
Note that the system prints a prompt string such as Self 0> to indicate that it is ready to accept an expression. The number in the prompt string is an index into the command history (discussed in the next section). The value of the evaluated expression is always printed, even if the expression performs explicit output. For example, in the following example, the nal 10 printed is the value of the do: expression:
Self 2> 10 do: [| :i | (i * 2) print. print] 0 2 4 6 8 10 12 14 16 18 10
Multi-line expressions can also be entered. If the read-eval-print loop discovers that the expression typed so far has open (unbalanced) parentheses or brackets, it provides a continuation prompt (>>), allowing the expression to be continued on the next line. One can enclose a sequence of expressions in an extra set of parentheses to force the parser to read input lines until the final closing parenthesis. This handling of multiple line expressions is also used by the parser when evaluating expressions in script les (see section 16). For example:
12
The rst line of the error explains the cause of the error. In this case, the message snort was not understood by the implicit receiver shell (the shell object is discussed in the next section). Section 13 describes all the kinds of errors that can arise in SELF programs.
13
The lobby is a common parent of many objects in the system and therefore provides a kind of linguistic common ground for the language. In particular, many useful objects such as true and all
parents of the lobby.
In the expressions you type in, a message sent to self gets sent to the shell object, and method lookup passes on from there through parent links.
lobby
shell
...
the prototype objects are accessible from the lobby. When the virtual machine evaluates the expressions you type, messages implicitly sent to self get looked up rst in the shell object, then in the lobby, then in the lobbys parents, and so on.
The actual details of the read-eval-print loop are as follows: an expression is read from the standard input. The input is treated as if it were dening the body of a method in the doIt slot of the lobby. If the input is syntactically correct, the system installs the generated bytecodes as the doIt method and creates a new process which sends printIt to the lobby. printIt is a SELF method (modiable by the user) which calls doIt and then prints the result.
Expressions read from a le by the _RunScript primitive, however, are handled slightly differently: they are evaluated in the context of the lobby itself, not the shell object. This arrangement keeps the lobby free of temporary slots added by the user, preventing accidental name clashes. The programmer can thus safely augment the shell object with slots to store temporary results and shortcuts to reduce typing (i.e., methods playing the role of macros). For example:
Self 7> _AddSlots: ( | p | ) shell Self 8> p: preferences shell Self 9> p xDisplay: :0.1 <preferences><object 12>
Adds the slot p to the shell. Puts the preferecnces into the s slot. Tell p to change the display used for the GUI.
14
You can use the history object to get the result of a command or to redo it:
Self 6> history getResult: 4 traits smallInt Self 7> history execute: 0 2432902008176640000 Self 8> history getPrefix: '20 fac' [0] 20 factorial Self 9> history executePrefix: '17 par' traits smallInt
These are verbose expressions: you will probably want to dene shortcuts for the history operations you use most frequently. You can add these shortcuts to your .selfrc le so that they are automatically added to shell object each time you start SELF. Some suggested shortcuts for history operations are given in history.sm.
We recommend you do your programming through the graphical user interface, in which you can skip this section. 15
The slot list is printed in normal SELF syntax. Asterisks following the slot name indicate parent slots. A private slot name is preceded by _ and a public slot by ^. A slot name followed by the symbol <- indicates that there is a corresponding assignment slot. In this example, the x and y slots have associated x: and y: slots. Note that the x and y slot names are preceded by the symbol ^_, indicating that the accessing slots x and y are public but the corresponding assignment slots x: and y: are private. Here is a somewhat longer example:
Self 1> inspect: traits point ( | _ parent* = traits pair. ^ # pt = ( rectangle from: self To: pt). ^ ## sz = ( rectangle from: self To: + sz). ^ alignToGrid: s = ( ((x / s x) * s x) @ ((y / s y) * s y)). ^ asRectangle = ( (0@0) # self). ^ copyX: x Y: y = ( (clone x: x) y: y). ^ normalize = ( asFloat / r). ^ r = ( (x square + y square) squareRoot). ^ restrictTo: rect = ( | xr <- nil. yr <- nil. | xr: x. yr: y. x < rect left ifTrue: [ xr: rect left ]. x > rect right ifTrue: [ xr: rect right ]. y < rect top ifTrue: [ yr: rect top ]. y > rect bottom ifTrue: [ yr: rect bottom ]. xr @ yr). ^ separator = '@'. ^ translateBy: pt = ( + pt). ^ xAxisReflect = ( copy y: y negate). ^ yAxisReflect = ( copy x: x negate). _ storeStringWorks = true. { 'Category: ui1BackwardsCompatibility' ^ #! pt = ( oldStyleRectangle from: self To: pt)
Again, this section may be safely skipped if you are planning to use the graphical user interface. 16
}. { 'Category: ui1BackwardsCompatibility' ^ ##! sz = ( oldStyleRectangle from: self To: + sz maxPoint) { 'Category: ui1BackwardsCompatibility' ^ maxPoint = ( x predecessor @ y predecessor) }. { 'Category: ui1BackwardsCompatibility' ^ rect = ( (0@0) #! maxPoint) }. | ) traits point result printed by read-eval-print loop
The message printString (and variations thereof) is used to get an objects printString without printing it:
Self 18> (3 @ 4) printString '3@4' The result is a string object, printed by the read-eval-print loop. Self 19> (3 @ 4) printString size 3 The printString is 3 characters long.
The read-eval-print loop. As previously mentioned, SELF sends printIt to the lobby when an expression is typed at the prompt, after rst installing the expression into the doIt slot in the shell. printIt attempts to print the result of doIt in an intelligent manner. It does this by invoking the name inferencer, a SELF-level mechanism that asks objects to print themselves if they know how, or tries to infer a name based on the objects location in the world otherwise. (This is not the case in the bare world, the minimal collection of objects you get when you start the SELF VM without reading in a snapshot or script les. In this case, printIt simply sends the _Print primitive, described in the next section, to the result of doIt.) The name inferencer uses the path cache, a SELF object that caches paths from the lobby to objects in the world. Objects that are reachable from the lobby (and thus appear in the path cache) are considered well-known. Here are some examples illustrating the name inferencer in the read-eval-print loop:
Self 1> 3 @ 4 3@4
The name inferencer uses the printString method for points.
17
The name inferencer named this object based on its location in the world of objects.
The name inferencer often generates a name that is actually a sequence of message sends that evaluates to the named object. For example:
Self 3> traits point traits point
This name is an expression you can evaluate to get the object.
The name traits point is a short version of the full path from the lobby to the traits for point objects, traits graphics point. The name for well-known objects that also respond intelligently to printString is a combination of the path name and the printString. For example, the prototype point object has both a location part and a name part:
Self 4> point <point>0@0
This name has both a location part (point) and a printString part (0@0).
In this case, the name part is short for prototypes graphics point.
is a small integer provided by the virtual machine as an object handle. One could use memory addresses
for this but addresses can be large numbers that are cumbersome to remember and type. Besides, the address of an object may change after a memory scavenge or garbage collection; in contrast, object reference numbers remain invariant. Object reference numbers are printed as an integer surrounded by angle brackets, for example
<14>. The user can turn a reference number (an integer) into an object by sending it the _AsObject primitive. For example:
Self 5> 6 _AsObject 3@4
The object reference number 6 refers to the point 3@4.
The virtual machine only keeps track of the last n objects assigned reference numbers, where n is a configuration parameter controlled by the _NumObjectIDs option (default: _NumObjectIDs: 1000). A given reference number is valid as long as it is within the last n reference numbers assigned. After this, it is forgotten and sending it the _AsObject primitive will fail. However, reference numbers are not recycled during a SELF session, so an invalid history number will not later be assigned to some other object; doing that could be very confusing!
18
10.3.2 The _Print Primitive The _Print primitive is a basic object printing function implemented entirely in the virtual machine. _Print prints its receiver and returns nil as the result. The output of the _Print primitive always lists at least the object reference number and slots of the receiver. It may also contain additional information for certain kinds of objects. The following sections describe the output of the _Print primitive in greater detail. Plain objects. Ordinary objects respond to _Print by printing their reference number and a list of their slots. _Print is a shallow operation: it does not recursively send _Print to the contents of its slots. Instead, the contents of a slot is printed as follows: its literal form if the slot contains an integer, a float, or a string, true, false, nil, or lobby if the slot contains one of these objects, <- if the slot contains the assignment primitive, <a method> if the slot contains an object that contains code, or the reference number of the referenced object if it is none of the above. The slot list is printed in normal SELF syntax. A private slot name is preceded by _ and a public slot by ^. Asterisks are appended to the slot name to indicate parent slots. For example, sending _Print to 3@4 gives:
Self 0> (3 @ 4) _Print <19>: ( | _ parent* = <18>. _ thisObjectPrints = true. ^ x = 3. _ x: = <-. ^ y = 4. _ y: = <-. | ) nil The result of _Print is always nil.
The output shows that the object 3@4 has object reference number 19. Its parent slot contains an object with reference number 18. The slots thisObjectPrints, x, and y contain the boolean object true, and the integers 3 and 4 respectively. The slots x: and y: contain the assignment primitive. Here is another example:
Self 1> (5 @ 7) _Print <20>: ( | _ parent* = <18>. _ thisObjectPrints = true. ^ x = 5. _ x: = <-. ^ y = 7. _ y: = <-. | ) nil
It can be seen that both 3@4 and 5@7 share the same parent (the point traits object) because both objects contain the object reference number 18 in their parent slots. To look at this object, we convert the reference number 18 into an object using the _AsObject primitive and print it:
Self 2> 18 _AsObject _Print <27>: ( | _ parent* = <29>. ^ # = <a method>. ^ ## = <a method>. ^ alignToGrid: = <a method>. ^ asRectangle = <a method>. ^ copyX:Y: = <a method>. ^ normalize = <a method>. ^ r = <a method>. ^ restrictTo: = <a method>. ^ separator = '@'. ^ translateBy: = <a method>. ^ xAxisReflect = <a method>. ^ yAxisReflect = <a method>. _ storeStringWorks = true. ^ #! = <a method>. ^ ##! = <a method>. ^ maxPoint = <a method>. ^ rect = <a method>. | ) nil
Vectors. Object vectors and byte vectors display their array elements after their slots. The elements are printed in the same manner as the contents of slots. The _VectorPrintLimit option controls how many elements of a vector are printed. (The default is _VectorPrintLimit: 20.)
Self 3> someVector _Print <49>: ( | ^ parent* = <29>. | object array: {true, <50>, 2.45} ) nil
Integers and oats. Integers and oats are not assigned history numbers since their values can be directly printed and typed so easily.
Self 4> 1.3 _Print 1.3: ( | ^ parent* = <0>. | ) nil Self 5> 16r2f _Print 47: ( | ^ parent* = <1>. | ) nil
The output of _Print. The float 1.3 has one parent slot. A hexadecimal number (2F radix 16). Integers always _Print in decimal.
Strings, special objects, processes, and mirrors. Strings and the true, false, nil, and lobby objects print their conventional form before their reference number and slot lists. Process objects print the special keyword process before their reference number and slot lists. Mirror objects (see the SELF World manual) print the short form of their reectee in addition to the reference number and slots of the mirror.
Self 6> 'tree' _Print 'tree' <2>: ( | ^ parent* = <3>. | byte array: {116, 114, 101, 101} ) Strings are surrounded by single quotes. nil Self 7> true _Print true <39>: ( | _ parent* = <40>. ^ asInteger = 1. ^ ifTrue:False: = <a method>. ^ not = false. ^ printString = 'true'. ^ storeStringIfFail: = <a method>. ^ && = <a method>. ^ || = <a method>. ^ ^^ = <a method>. | ) nil
20
Self 8> (reflect: 2@3) _Print Create a mirror on 2@3 and sends _Print to it. mirror <reflectee = <67>> <68>: ( | ^ parent* = <48>. _ thisObjec tPrints = true. | ) nil
11.1 Requirements
The ui requires an 8-bit color framebuffer. (It may work with deeper framebuffers but this has not been tested.) A GX accelerator is recommended for color operation; the ui may have poor performance on less powerful color frame buffers. The ui can run under OpenWindows or with any X11 server, locally or remotely.
21
Function
grabbing (color changing) removing
Examples
sprouting contents, moving objects, popping up menu (color version only; shift key must be down to initiate) remove boxes from screen, hide slots, desprout arrows
of an object are depicted as wide strips on the face of the object, with the name in the left part of the strip, and the contents in the right part. At the bottom is a hidden-slots bar which can be used, via a menu, to hide or reveal slots.
Menus do not reect the philosophy of concreteness we espouse for the ui (see [CU90]). They have been added to the ui as an expediency, to make this prototype more practical in the interim before a full-edged user interface is constructed. 22
The relationship between objects and the slots that contain (i.e., refer to) them is shown by arrows pointing from the slots to the objects. Objects can be moved about on the screen, the contents of
their slots can be examined, and they can be removed from the screen. Each object provides menus to manipulate the object. The menus are accessed by pressing down the left mouse button over a designated area. Each object has a menu area at the top left (the small rectangular area), one menu per visible slot (at the left of the slot), and a hidden-slots bar at the bottom. Moving objects. Objects can be picked up and moved about on the screen by pointing to the face of the object (or the face of one of its slots), pressing down and holding the left mouse button, and moving the mouse. Letting go of the left mouse button drops the object. Examining slots. Clicking the left mouse button on a slots sprout button (the small, circular button at the right hand end of the slot) causes the object referenced by the slot to display itself on the screen (sprout), connected to the slot by an arrow. The arrow can be removed by clicking the right mouse button over the sprout button. Navigating in the ui is performed by traversing slots to reach the desired object (the lobby object is a good starting point). Removing objects from the screen. Clicking the right mouse button on the menu box at the top left of an object removes that object from the screen. Hiding and showing slots. Clicking the right mouse button on a slots menu box (at the left of the slot) will hide that slot. Hidden slots do not show on the face of the object. When some of an objects slots are hidden, the hidden-slots bar displays how many slots are hidden. Pressing the left mouse button on the hidden-slots bar pops up a menu that lists all the hidden slots (or their annotations). Selecting an item from this menu makes the slot appear in the object. If an annotation is displayed, then the slots with that annotation are accessible via a sub-menu. Selecting the annota-
23
tion reveals all the slots with that annotation. In addition to hidden slots and annotations, this menu may include some of the special entries described in the table below. Table 2 Special items in the hidden slots menu Menu item
All Parents Public Indexable Well-known indexable
Effect
Shows all slots Shows only parent slots Shows only public slots Shows only the elements of the vector or byte vector part Shows only those elements of the vector part that are well-known (i.e., recorded in the path cache)
Except for the All item, which is always present in the hidden slots menu, special menu items appear only when there is at least one slot (or pseudo-slot) in that object matching that category. Hiding all slots (iconication). Clicking the right mouse button on the hidden-slots bar on the bottom of the box will hide all slots, effectively iconifying the object. Pseudo-slots. The ui permits access to certain parts of vectors, methods, and mirrors by way of pseudo-slots. For example, object vectors and byte vectors show their elements as pseudo-slots, in addition to showing their regular slots. Pseudo-slots have names enclosed in angle brackets. Table 3 Objects that have pseudo-slots in the ui Kind of object
process mirror vector byte vector method block generic activation method activation block activation process stack the object that the mirror reects (its reectee) vector elements (the element indices are the names of the pseudo-slots) byte vector elements (the element indices are the names of the pseudo-slots) byte code array, literal array, le name, le line number, source string lexical parent receiver, expression stack, byte code position, plus all method pseudo-slots selector, method holder, sender, plus all generic activation pseudo-slots selector, lexical parent, sender, plus all generic activation pseudo-slots
Pseudo-slots
24
The "object" menu. At the top left of each object is a rectangular area, which, when the left mouse is pressed, reveals a menu. The menu items are: Add slot allows you to add a new slot to an object. A typein window will appear in which you should type the name of the slot you wish to add. Selecting the apply button will add the slot; cancel will dismiss the typein. Add typein opens a typein window on the object. An expression may be entered into the typein, and evaluated. Evaluation takes place in the context of the object. All typeins support some Emacs-like text editing operations. Arrow keys (or ^b, ^f, ^p and ^n) move the Ibeam around. You can move to the beginning or end of a line with ^a or ^e. You can delete a line with ^k. ^s initiates a search; ^r initiates a reverse search. The character after the Ibeam can be deleted with ^d, the character before with BackSpace or Delete. In addition to these features, you can also select text by dragging through it with the cursorwith the left mouse button depressed, and cut, copy and paste text using the keys of those names (or paste with ^y). Also, you can evaluate the text using Meta-Return. Drop dismissed the object from the ui. References summons an object which refers (in its indexable slots) to all the objects that refer to the original object. Children summons an object which refers (in its indexable slots) to all the objects which have the original object as a parent. Add method slot adds a new method slot called new_method_slot initialized to the method (self). Use the edit slot option (see below) to change the name and definition of the method. Add variable slot adds a new variable slot called new_variable_slot, initialized to nil. se the edit slot option (see below) to change the name and definition of the slot. Add constant slot adds a new constant slot called new_constant_slot, initialized to nil. se the edit slot option (see below) to change the name and definition of the slot. The slot menu. Each slot has a menu area at its left which, with the left button, can be used to manipulate the slot. Different kinds of slots have different options, drawn from this list: Edit slot allows you to edit the complete definition of the slot (including its name). Hide hides the slot. Implementors of slot, slot: summon an object which can be followed to all the implementors of messages with the same name.
Want to nd the implementors of a slot called foo but cant nd a sender or another implementor? Using a typein create an object with a slot called foo, thus: (|foo|), then summon the implementors. 25
Senders summons an object which refers to all the methods which send the message with the same name as the slot. Messages sent by summons an object whose slots have the same names as the messages sent from the selected method. In conjunction with the implementors and senders options you can navigate the code in the system. Edit code allows you to edit the code of a method (but not its name or arguments). Remove slot, slot: removes the corresponding slot. The send menu. Objects may appear in the ui with a button at the lower right (next to the hidden slots bar) that, when depressed, raises a menu which can be used to send unary messages to that object. (See below for how to add these menus.) Selecting a menu item sends the corresponding message to the object. The object returned by the message send will appear in the ui.
Changing the contents of objects by grabbing arrows. [This is potentially a hazardous operation.] By setting allowArrowGrabbing to true in the preferences object (see below), the ui will allow the contents of a slot to be changed by grabbing the head of its outgoing arrow with the left mouse button and dropping it over another object. When the arrowhead is grabbed, it changes into a square, indicating that it is waiting for a target. Dropping the square onto another object makes the arrow to point to that object, thereby changing the contents of the slot that the arrow is coming from. (Be sure to drop the square onto the target object, not merely near it.) Some arrow redirections are not currently permitted by the ui. These include moving an arrow for a pseudo-slot and moving an arrow to a method object that takes a different number of arguments than the original contents of the slot. In these cases, a warning is printed in the SELF shell and no change is made. Dropping an arrow over empty space aborts the operation. Be careful! Indiscriminately changing objects on which the rest of the system depends can break parts of the system, and redirecting arrows in the ui is tantamount to altering the running system.
26
If the ui and other parts of the SELF system begin behaving strangely (many errors and stack dumps), it may be because a crucial part of the system was changed unintentionally. In this case, quit SELF and start anew. Function key operations. Objects in the ui and the ui itself also respond to certain key press events from the keyboard. The relevant object is the object under the cursor at the time of the key press. If the cursor is on the background (i.e., not on any object), the key press generates a message to the ui itself. Table 4 Function key operations Key
Stop (L1) Props (L3) Cut (L10) Copy (L6) Paste (L8) F1 On the background, quits the ui. Adds a mirror on the object into the ui. Removes the object from the ui, but retains a reference to the object in a hidden clipboard. Puts a reference to the object into the clipboard. On the background, puts a copy of the object in the clipboard into the ui. On an object, adds the slots of the object in the clipboard to the given object. Adds to the ui the display object to which you are pointing. For example, if you point to the senders button on an object and hit F1, the sendersBox object that implements the button will appear in the ui. Finds the nontrivial parents of an object. Point to an object and press F2 to nd any parents of the object which themselves have parents. Traces the ancestry of an object. Point to an object, (move it to the lower lefthand corner of the screen), and press F3 to see all the objects ancestors which themselves contain parent slots. (Ancestors without parent slots are ignored in order to avoid cluttering things up with mere category objects.) F3 stops at the lobby, but you can go on by pointing to the lobby and pressing F3. Summons all the well-known children of the object being pointed to. If there are more than 20, only 20 are shown.
Effect
F2 F3
F4
It is fairly easy to extend this functionality: the ui sends keyPress: keyCode to the object under the cursor. User interface objects inherit keyPress: methods.
27
makes the given object obj appear in the ui. To add the object to the ui with a send menu item for the unary message foo, use:
ui add: obj With: foo
The ui also responds to add:With:With:, add:With:With:With:, and add:WithMessages: (which takes a list of strings) to add the object with multiple send items. Nearly all the operations that can be carried out with the mouse in the ui can also be achieved by sending messages to ui. For details, see the requests client category in traits ui.
28
able arrow grabbing, evaluate preferences allowArrowGrabbing: true. Some of the slots for preferences are summarized in the following table; inspect the object for a complete list: Table 5 UI preferences Slot Name
allowArrowGrabbing blurArrows blurBodies uiColorFile uiIconFile
Description
If true, allow arrowheads to be grabbed and dropped on other objects. Color version only. If true, enable experimental motion blur for arrows. Color version only. If true, enable experimental motion blur for bodies. Color version only. File name to use for initial color scheme. Color version only. File name to use for the ui icon. File should be in X bitmap format.
Default
false false true ui.colors ui.icon
Box blueprints. The ui has a facility to customize the way an object rst appears on the screen which slots are showing and what send pseudo-slots are shown. It does this by consulting a dictionary of blueprints for objects, keyed on the structure of the object (the names of all its slots). If a blueprint is found, the ui uses that to determine which slots are shown and which are hidden, and what send pseudo-slots to add to the object. If no blueprint is found, a default policy is used. You can add your own blueprints to this dictionary; see the global boxBlueprint.
Color space allows the colors of the ui to be changed interactively. Whatever the cursor is pointing at when color space is entered is whats color will be changed. (This includes, for example, the background, the boxes, the text on the boxes, and the arrows. In the case of arrows, point to the round tail of the arrow to change its color.) Entering color space transports the cursor to the absolute location of the current color in the ui window. In color space, the horizontal axis measures hue and the vertical axis measures brightness. Pressing the left button down in while in color space (while keeping the middle button down) switches the vertical axis from brightness to saturation. Moving the cursor in color space interactively changes the color of the target elements. Releasing the middle button leaves color space. The color scheme for the ui is automatically saved after color changing. A le is created to hold the color information. This les name is dened by the preferences uiColorFile slot (the
29
default is ui.colors). The ui checks for this le when it starts up and, if it nds it, uses the given colors. Otherwise, it uses the default colors.
30
Finding Objects
Here is the heart of the matter: writing and debugging your own SELF programs.
12 Finding Objects
The browse object implements an enumeration facility that allows you to nd all the objects in the system with specic properties. The result of any browsing operation is a vector of mirrors. Example: nd all objects having the selector upLex:
31
are:
_AddSlots: and _RemoveSlot:
mirror on (<shell><object 5>) Self 1> inspect: x ( | | ) <object 6> Self 2> >> >> >> >> >> >> >> x _AddSlots: ( | Use the _AddSlots: primitive to add... a <- 17.8. An assignable slot initialized to 17.8. b. An assignable slot initialized to nil. c = 3. A constant slot. h = ( history printRecords ). A method slot. q = ( | a. b <- 'foo' | ). An object with its own slots. ^_ p <- nil. A public data slot with private assignment slot | ) (all other slots in this example are
undeclared and have public semantics).
<object 6> Self 3> inspect: x ( | a <- 17.8. b <- nil. c = 3. h = ( history printRecords ). q = <an object>. ^_ p <- nil. | ) <object 6>
There must be periods between slot denitions. For example, if the period after slot h is omitted, SELF will send the q message to the parenthetical expression ( history printRecords ), resulting in an undened selector error. When a slot is redened, its contents are replaced with a reference to the new object, but other references to the original object are left unchanged. If the old slot was assignable and the new slot isnt, the assignment slot is removed:
33
Self 4> x _AddSlots: ( | a = 'new a' | )A change to x from previous example. <an object> Self 5> x _Print <37>: ( | b = nil. c = 3. h = <a method>. q = <38>. ^ p = nil. b: = <-. _ p: = <-. a = 'new a'. | ) The a: slot is gone. nil
Note that expressions used to construct an object literal are always evaluated in the context of either the lobby (in the case of reading in a script le) or the shell object (in the case of evaluating an expression typed by the user), not in the context of the object being constructed.
Remove ps y: slot.
Note that all the object manipulation primitives in SELF always manipulate individual objects the above example did not remove the y: slot from all points, only from the point p. Of course, if traits point, the object containing the shared behavior for all points, were modied then all points would feel the change via inheritance, even though only a single object was changed.
er slots of the object. These primitives incrementally modify objects. On the other hand, script les must be able to completely redene an object (e.g., to completely replace the behavior of a traits object), so SELF provides the _Define: primitive as a convenience (it could be built out of _AddSlots: and _RemoveSlot:). _Define: rst removes all slots from the receiver, then adds
34
to the receiver all the slots found in its argument object. Thus, after performing the _Define: primitive, the receiver object contains a set of slots with exactly the same names, attributes, and contents as those specied in the _Define: primitives argument object. Like _AddSlots: and _RemoveSlot:, _Define: modies the receiver in place and changes only that one object.
Self 12> x _Print Object x from the preceding example. <37>: ( | b = nil. c = 3. hello = <a method>. q = <4>. ^ p = nil. b: = <-. _ p: = <-. a = new a. | ) nil Self 13> x _Define: ( | a = 12. j <- 2. | ) <an object> Self 14> x _Print <37>: ( | a = 12. j = 2. j: = <-. | ) nil
Note that xs object reference number is still the same, but its slots have changed.
35
We can test out the object by sending it some messages (such as + 7 and - 5) in a typein:
We can now proceed to add more methods to provide further functionality. This is most easily accomplished by using the add slot menu item, in which case a complete method denition should
36
be entered. Here, we have added methods to do multiplication and division, to clear the result, and to negate the result:
Note that the precise division operator is /=. Of course, if we try to divide by zero we get an error notication:
37
In order to reduce the screen space, we can hide all the objects slots except the result slot (by clicking on the hidden slots bar at the bottom of the object with the right button, then using the left button menu on that bar to expose the result slot):
However, it may be more convenient if the object displayed the result in its title bar, so that we could hide the slots completely. To do this we add a private slot called thisObjectPrints (set to true), and a printString method:
Note that the title updates. At this point we may consider the calculator nished, but for better organization it may be useful to break out the shareable behavior into a separate traits object. To do this we will create a separate object with just a result slot that inherits from our existing object, delete the result slot from our current calculator, and then install the resulting objects in the globals and traits namespaces. The simplest way to do this is to make a copy of the current object, and delete some slots from the original object and the other slots from the copy.
38
Next we delete the appropriate slots from each object (using the remove menu item on each slot),
namely +, -, *, /, clear, negate and printString from one, and result and thisObjectPrints from the other.:
39
After that, we sprout the parent slot of the smaller object and redirect the arrow to the larger object:
Finally, we add constant slots called calculator to traits applications and global applications and hook these up to our prototypical calculator and to the calculator traits. The nal picture should look something like this:
40
We can now summon a calculator from, say, the lobby, using calculator copy. We might want to add some menu items (such as clear and negate) thus:
We hope this has given you some ideas as to how to extend and improve this program. Happy hacking! The remainder of Section 14 describes the use of script les. If you stay entirely within the ui, you are unlikely to need any of this material, and can skip to the next section.
41
'hello world' printLine "This is a comment" '10 factorial is ' print 10 factorial printLine
clone
...
pointProto
p1
parent* x y 0 0
parent* x y 5 10
Now, suppose the denition of the point traits in the le point.self is changed to:
_AddSlotsIfAbsent: ( | ^ pointTraits = () | ) pointTraits _Define: ( | Redefined point traits. ^ clone = ( _Clone ). ^ print = ( x print. @ print. y print. ). ^ + p = ( (pointProto clone x: x + p x) y: y + p y ). | ) _AddSlotsIfAbsent: ( | ^ pointProto = () | ) pointProto _Define: ( | _ parent* = pointTraits. ^ x <- 0. ^ y <- 0. | )
Define the prototype.
clone print +
pointProto
p1
parent* x y 0 0
parent* x y 5 10
Reading in the edited script le modied the original pointTraits object in place, instead of creating a new pointTraits object. This allows the new behavior to apply to existing clones of the point prototype such as p1. We can use _Print: to verify this:
Self 7> pointTraits _Print <5>: ( | ^ clone = <a method>. ^ print = <a method>. ^ + = <a method>. | ) nil Self 8> p1 _Print <7>: ( | _ parent* = <5>. ^ x = 5. ^ x: = <-. ^ y = 10. ^ y: = <-. | ) Points to <5>, the newly modified point traits object. nil
44
oddballs applications _AddSlotsIfAbsent: ( | notebook = () | ) notebook _Define: ( | _ parent* = traits oddball. _ notes <- list copy. ^ addNote: n = ( notes add: n. self ). ^ printNotes = ( notes do: [| :n | n printLine ]. self ). | )
The notebook object contains both state (notes) and behavior (e.g., printNotes). It inherits some of its behavior from traits oddball. After the notebook object has been created, it can be used to record unstructured notes in the form of strings:
Self 9> notebook addNote: 'This is a short note.' <an object>
Note that addNote: returns the notebook object. This initially prints as <an object> because the notebook object is not in the pathCache. This problem can be corrected by updating the pathCache:
Self 10> pathCache refill refilling path cache...done. '1642 entries' Self 11> notebook addNote: 'SELF is fun!' notebook Self 12> notebook addNote: 'This is a longer note. It goes on for several sentences and may print as multiple lines. Long notes make it difficult to quickly scan a list of notes.' notebook
In the last example, the string was typed without carriage returns and allowed to wrap around. If a carriage return were typed in the middle of this string, the parser would consider the expression complete and complain that the string literal is missing its trailing quote mark. The contents of the notebook can be displayed at any time:
Self 13> notebook printNotes This is a short note. SELF is fun! This is a longer note. It goes on for several sentences and may print as multiple lines. Long notes make it difficult to quickly scan a list of notes. notebook
The notebook can be improved by having it store structured note objects rather than strings. In particular, note objects can be given short titles to be printed when the user wants to quickly scan the contents of the notebook. Here are the appropriate object denitions:
45
traits applications _AddSlotsIfAbsent: ( | note = () | ) traits note _Define: ( | parent* = traits clonable. printString = ( '*** ', title, ' ***\n', contents, '\n' ). | ) prototypes applications _AddSlotsIfAbsent: ( | note = () | ) note _Define: ( | _ parent* = traits note. _ thisObjectPrints = true. ^ title <- '<title>'. ^ contents <- '<note contents>'. | )
The behavior shared by note objects is dened in a separate traits object to allow the behavior of all the note objects to be changed by modifying the traits object. Initially, the traits object just denes a method for printing the object, but later it can be extended as the program evolves. Some of the behavior of note objects is inherited from traits clonable (e.g., copy and printLine). The prototype note object contains the data slots for its title and contents and a parent slot pointing to the note traits object. The constant slot thisObjectPrints tells the read-eval-print loop to print note objects by sending them the printString method (see section 10.2). Finally, to make it convenient to add structured notes and to allow the user to print a list of note titles, the behavior of the notebook object is extended with two additional methods:
notebook _AddSlots: ( | ^ addTitle: t Contents: c = ( notes add: ((note copy title: t) Contents: c). self ). ^ printTitles = ( notes do: [| :n | n title printLine ]. self ). | )
Note that _AddSlots: modies the existing notebook object in place, rather than creating an entirely new one, so the previous notes stored in the notebook are retained. The new notebook can now be used:
Self 14> notebook addTitle: 'Structured Note' Contents: 'this is a structured note' notebook
46
Self 15> notebook printNotes This is a short note. SELF is fun! This is a longer note. It goes on for several sentences and may print as multiple lines. Long notes make it difficult to quickly scan a list of notes. *** Structured Note *** this is a structured note notebook Self 16> notebook printTitles No 'title' slot found in 'This is a short note.'. Sending method holder is notebook <7>. ## Stack trace for process <15> ...
The last expression, which was meant to print a list of note titles, produced an error because the notebook still contains some old, unstructured notesthat is, simple strings that do not understand the message title. These notes (the rst three added to the notebook) can be removed by manipulating the notebook objects note list directly. The notebook object is rst entered to make its notes slot visible to expressions typed at the prompt (see section 10.2):
Self 17> enter: notebook shell Self 18> notes removeFirst 'This is a short note.' Self 19> notes removeFirst 'SELF is fun!' Self 20> notes removeFirst 'This is a longer note. It goes on for several sentences and may print as multiple lines. Long notes make it difficult to quickly scan a list of notes.'
Self 54> notebook printNotes *** Structured Note *** this is a structured note *** Feature: searching *** allow the user to find all notes containing a given substring *** Feature: deleting *** allow the user to delete a note or a set of notes notebook Self 55> notebook printTitles Structured Note Feature: searching Feature: deleting notebook
You might want to view the structure of this program graphically. To do so, type:
Self 56> ui start initializing ui...creating colormap series . . . . . . . . . done. done. uiX Self 57> ui add: notebook uiX Self 58> ui add: note copy uiX
The previous commands start the graphical user interface and make the notebook and a note object appear in it. Position the cursor over one of the these objects and press the function key F3. This will cause the inheritance hierarchy for the given object to be sprouted and arranged on the screen, as described in section 11. The last two notes in the notebook suggest some possible ways to extend and improve this program. You will probably think of many more. Happy hacking!
48
By default, errors are handled by a set of methods dened in errorHandling.sm. For all errors except non-recoverable and fatal VM errors, an object can handle errors in its own way by dening its own error handling methods. If the object in which an error occurs neither inherits nor denes error handling behavior, the VM prints out a low-level error message and a stack trace. The system will also resort to this low-level message and trace if an error is encountered while trying to handle an error.
15.1 An Example
Here is an expression that produces an error in the current system:
Self 7> 100000 factorial The stack has grown too big. (Self 3.0 limits stack sizes, and cannot resume processes with stack overflows.) ## Stack trace for process <9> ##___Rcvr__Selector/Block__________File:line______Method-holder/location____ #0 <10> asSmallInteger smallInt:132 traits smallInt coercions #1 <10> <= smallInt:86 traits smallInt comparing #2 <10> factorial integer:32 traits integer functions { #3 <11> [* predecessor fact...integer:32 [] in factorial #4 <12> ifTrue:False: boolean:47 false #5 <13> factorial integer:32 traits integer functions }*309 #930 <0> <top level expr> <the prompt>:1 shell
The error arose because the recursive method factorial exceeded the size allocated for the process stack which resulted in a stack overow. The virtual machine currently allocates a fixed-size stack to each process and does not extend the stack on demand.
The rst two lines of the output describe the error. These lines are followed by a stack trace of the erroneous process. Each line in the stack trace denotes a method or block activation and is divided into several elds: #0 the number of the stack frame. #0 is the most recent frame, i.e. the one that caused the stack overflow. The number of the last frame (#930) indicates the stack depth at the time of the error. the receiver of the message in this frame has object reference number 10. You can type inspect: 10 _AsObject to examine the receiver object.
<10>
asSmallInteger the methods selector name. If the frame is a block activation frame, the source code is shown in this field (as shown in frame #3 of this example). smallInt:132 the name and line number of the source file where this method is defined.
49
traits smallInt coercions the method holder of the method being executed. If the frame is a block activation, this is the frame method in which the block is defined (e.g., the block in frame #3 is defined in the method factorial). A sequence of recursive calls is presented as a folded structure. In the above example the notation { ... }* 309 represent 309 occurrences of a sequence of activations with the same structure as activations #3, #4, and #5. One can explore the stack in greater detail using the debugger, as discussed in section 12.
uous. The error message also says where the matching slots were found. Ambiguities can often be solved by changing parent priorities.
No fish delegatee slot was found in <a child of lobby> <12>. The lookup found no parent slot fish, which was explicitly specified as the dele-
Use the selectors error: and error:Arguments: to raise a programmer dened error.
50
The selector 12 could not be sent to shell because it is not a string. The primitive _Perform expects a string as its first argument. The selector add: could not be sent to shell <0> because it does not take 2 arguments. The primitive _Perform received the wrong number of arguments.
There are many other kinds of possible primitive errors (see Appendix H of the Reference Manual).
fixed size stack for each process, and the stack cannot be expanded.
Self 3.0 cannot run a block after its enclosing method has returned. (Self cannot resume this process, either.)
This error occurs if a block is executed after its lexically enclosing method has returned. This is call a non-LIFO block. Non-LIFO blocks are not supported by the current version of SELF.
The rst line helps the SELF implementors locate the problem. Printing the SELF stack may provide more information about the problem but does not always work. Returning to the SELF prompt is usually successful, but the system integrity may have been compromised as a result of the error. Execute the primitive _Verify to verify the virtual machines integrity; see Table 22 in Appendix H of the Reference Manual).
51
Since fatal errors usually arise from a bug in the virtual machine, please send the SELF group a bug report as described in section I-2.5, and include a copy of the error message if possible. If the error is reproducible please describe how to reproduce it.
The error message alone many not be sufcient to nd the bug. To explore further, you can attach the process to the debugger:
Self 5> attach attaches the process that most recently encountered an error Process <15> has been attached to the debugger. No i slot found in <a child of traits block> <13>. Sending method holder is shell <14>. shell <14> <top level expr> = (| i <- 0. | 10 do: [ i: i succ ] i print) Process <15>, suspended with error, activation: 6 in [0,6]. File: <the prompt> <shell> <object 14>
52
Attaching a process with an error sets the debuggers current activation to the bottommost interesting activation. In this case, the current activation is number 6. Activations 0 to 5 are omitted by the debugger since they are part of the error handling mechanism itself. By issuing the commands up and down the current activation can be changed to expose other stack activations. The current byte code position of the activation is highlighted by underlining part of the source code. The highlighting shows the message being sent when the error occurred. Both the receiver and the selector are underlined. If your terminal does not support underlining, highlighting is shown by surrounding the expression with curly brackets ({}). In this example, the highlighting shows that the message i is being sent to a block object. This explains the problem: the expression needed a period after the block to separate the expression into two statements.
Process <26>, suspended, activation: 5 in [0,5]. File: <the prompt> <shell> <object 14> Self 5> step single step shell <14> <top level expr> = (halt. 6 printLine) Process <26>, suspended, activation:0 in [0,0]. File: <the prompt> <shell> <object 14> ... etc ...
As shown, breakpoints are explicitly inserted in the source code. Setting a breakpoint in a method can be done through the ui using the edit slot or edit code slot menu options.. Caution: inserting halt in a method used by the core system can result in unexpected behavior (e.g., the debugger itself might break). This problem can be solved by using conditional break-
53
points to restrict breakpoints to specic processes. The method break is an example of a conditional breakpoint that breaks the process only if the process is currently attached to a debugger.
break = (| env = lobby. | env process this isDebugged ifTrue: [halt])
Typing control-C interrupts the scheduler and presents a list of all processes:
^C ----------------Interrupt----------------Ready: <0> scheduling process{ | i <- 0| [ i: i succ ] loop } Waiting: <1> waiting process{ inputLoop } -----------------------------------------Select a process (or q to quit scheduler): 0 select the loop process Select | [a]bort, | | [b]ackground, | | [s]uspend, | | [r]esume, | | [p]rint stack, | or | [n]o action | for process 0: s Process 0 suspended. ------------------------------------------
The runaway process is now suspended and can be attached to the debugger:
Self 12> attach: 0 <a child of t... <9> loop = ( value. _Restart. ) Process <0>, suspended, activation: 0 in [0,1]. File: ~/self/self/block:16 shell Self 13> up shell <10> doIt = (| i <- 1630863. | [ i: i succ ] loop ) Process <7>, suspended, activation: 1 in [0,1]. File: <the prompt> shell
54
Description
attach the process that most recently generated an error attach the process with object reference number n detach the debugged process execute (n) non trivial bytecodesa execute (n) bytecodes execute (n) non trivial bytecodes in the current activation execute (n) bytecodes in the current activation nish executing the current activation continue execution print out a stack trace of the process display the current activation go to and display the nth activation on the stack display the status of the debugged process go up (n) activation(s) go up to the lexical enclosing scope of this activation go down (n) activation(s)
lookup: <name> lookup the given name in the context of the current activation a. A bytecode is trivial if it is a push of a literal or a send to a slot residing in the lexical scope of the current activation.
55
tion to be useful (it was developed primarily for virtual machine debugging). It is described here for those rare occasions when such raw information is needed. Tracing prints a line for every non-inlined, non-inline-cached message send (that is, for every message send that actually requires a lookup at run-time). Tracing is activated and deactivated using the debugging primitive _Trace:. Because tracing generates a lot of output, you may want to insert _Trace: true and _Trace: false statements only around the relevant portions of your program. To see more message sends, turn off inline caching during tracing. Send _InlineCache: false to turn it off, followed by _FlushInlineCache to ush all entries that have already been cached. To see every message send, turn off inlining with _Inline: false followed by _Flush to ush the code cache, eliminating previously inlined code. This will generate a lot of output! Caution: _Trace is not very useful when the scheduler is running (the usual case) since tracing is not restricted to a single SELF process. Tracing when the scheduler is running results in mixed trace output from all currently executing SELF processes.
responding transporter fileIn because the les created are intended to be read by a bare Virtual Machine in order to support bootstrapping.) The transporter does its work in two steps: First it processes (cooks and checks) all the module information (described in the next section). This processing includes sanity checks, consistency checks, finding all the objects that belong to each module but are not explicitly claimed by it, and detecting any other errors that might interfere with the next step. This step takes about 2 1/2 minutes (on a SparcStation-10) and the results of this step are saved for potential reuse in the SELF heap. If it finds an inconsistency it warns the user but keeps running to the end of the step, at which point it stops. Second, the transporter actually writes out one or more modules (a.k.a. source files). In order to avoid destroying the contents of existing files, each overwritten file is renamed with a .OLD suffix. This step takes a few seconds for each file, or about 6 minutes for the whole SELF world. There are three ways to invoke the transporter: The easiest way to run the transporter is to just ask it to write out every file, by saying transporter fileOut visitAll, but this takes about 10 minutes. If you just want to save the contents of one module, such as the file point.sm, you can say transporter fileOut visitOne: point, where the argument is the name of the module (as named in modules raw; see later). This incantation still cooks and checks all modules, but only writes out one, so it only takes about 3 minutes. Finally, if you have previously done either visitOne or visitAll, the processed module information is still in the heap and you can save the module in a few seconds by saying transporter fileOut revisitOne: point. This incantation reprocesses the one module specified, and so will even work if new objects or slots have been added to the module since the last visitAll or visitOne. But, sometimes it can get confused if other modules have changed, or if changes to the module in question alter the boundaries between it and the other modules. It it gets confused it will issue spurious warnings. In that case, just run visitOne to recompute all of the cooked module information.
57
If you are using the user interface, you can create a new, constant slot containing an empty object by summoning the modules raw object, selecting add slot from the menu button on the top left of the object, and typing in your_module_name = () to the text editor box. 17.2.1 Basic Module Information Revision: First, each raw module object should contain a revision slot. The contents of this slot is a string that is prepended to the source le and is used to contain an RCS revision string. For example, for a new module you would create the slot with the expression: revision = $Revision:$
Comment: If you want to comment the module, add a slot called comment and (for historical reasons) put a method in it with a comment. For example:
comment = ( Put the comment for the module here. It can extend over multiple lines ).
(In the near future, I expect we will change over to strings instead of methods.) Parent: Each raw module object should have a parent slot (in case you ever add a postFileIn method to it). For now, we use the lobby as the parent, so you can dening this slot by typing:
parent* = lobby
Contents: Suppose your module contains two objects, traits point, and point. You need to tell the transporter that this module adds two slots to the system and that the objects contained in these slots are part of this module. Furthermore, you need to give the transporter the full sequence of links to each object. So even though you can refer to the point object by merely sending point to the lobby, since the point slot is really in a grandparent of the lobby, you need to spell it out for the transporter: prototypes graphics point and traits graphics point. (To nd out an objects full path you can type in (pathCache at: reflect: point) fullName. ) So, the contents of the contents slot would be a string containing two lines (one for each slot):
contents = prototypes graphics point traits graphics point .
References: Sometimes a module contains a slot that refers to another modules object. For example, the annotation module creates a slot in the defaultBehavior object called isSlotAnnotation that merely points to false, which is an object that belongs to the boolean module.
58
When a module denes such slots that merely refer to other modules objects, put them in the references slot using the same format as the contents slot:
references = defaultBehavior isSlotAnnotation defaultBehavior isObjectAnnotation .
SlotsToRemove: Occasionally, the module needs to remove an existing slot when it is read in. To indicate this to the transporter, add a slot called slotsToRemove containing a list of slots in the same syntax as contents. PostFileIn: Sometimes a module needs to run some initialization code whenever it is read in. To do this, invoke the initialization code from a method called postFileIn in the raw module object. (If you create the le with a text editor, you will have to manually add a line to the end of the le to invoke the postFileIn method when you read in the le. For example, the string module needs to run the initializeAscii method, so you would add a slot to modules raw string by typing:
postFileIn = ( initializeAscii)
Subparts: Sometimes one source le needs to read in other source les. For example the le tests.sm nishes by recursively reading in the les programmingTests.sm, debugTests.sm, lowLevelTests.sm, numberTests.sm, and deltaBlue.sm. In order to inform the transporter, the raw module information object for tests contains
subparts = programmingTests debugTests lowLevelTests numberTests deltablue .
Status: You can attach a status string to a module by putting it in this slot in the raw module information object. Its contents are not used by the transporter, but it is led out. Example:
status = not ready for prime-time.
17.2.2 Copy-Down Objects Suppose you want to dene a coloredPoint object to contain all the slots that are in a point plus a color slot. Since SELF has no inheritance of structure, you would write this in a source le by saying:
point copy _AddSlots: ( | color | )
59
Of course, in the user interface, you would just send copy to point and then add a slot to it. Some day the environment will remember how objects were made, but for now you have to explicitly (and redundantly) tell the transporter about it. We call an object that is created by copying another and then adding slots, a copied-down object, and there is a way to express this to the transporter, so it can create source les appropriately. In the raw module object, create a slot named copyDowns, containing one slot for each copied-down object. In each of those slots goes an object describing each copied-down object, and this copydown description object itself has four slots: from: This slot contains a list of objects (same format as contents) that will be copied and merged to form the copied-down object. For our example it would be
from = prototypes graphics point.
to: This slot tells the transporter where to install the copied-down object, for example:
to = prototypes graphics coloredPoint.
It is similar to what would be in a contents slot in that it also tells the transporter that this module denes a slot and its contents, and so the coloredPoint module would not need to repeat this in a contents slot. slotsToChange: This optional slot lists the names of slots that are contained in the from objects but that nevertheless need to be dened in the source le, typically because their contents differ. For example, if we want our colored point to inherit from its own traits instead of the regular point traits, we would say:
slotsToChange = parent
to force the transporter to output a denition for this slot in the le coloredPoint.sm. This would replace the copied-down parent slot from point with one containing whatever was in the parent slot of coloredPoint. More than one slot name can appear in slotsToChange, separated by white space. copyMessage: This optional slot gives the message to be used to copy the from objects. If absent, it defaults to copy. 17.2.3 Summary of Copy-Downs Here is the module information for coloredPoint:
60
modules raw _AddSlots: ( | coloredPoint = ( | revision = $Revision:$. contents = traits graphics coloredPoint. parent* = lobby. copyDowns = ( | slot_0 = ( | from = prototypes graphics point. to = prototypes graphics coloredPoint. slotsToChange = parent. | ). | ) | ) | )
17.2.4 Advanced Module Information Occasionally, you will have to resort to one of these (hacks) to get your le to le out cleanly. collectionsToEmpty: Suppose your module denes an object containing a collection in a constant slot and that, although the collection may accumulate contents, you want the collection to be empty in the source le. You can tell this to the transporter by adding a slot called collectionsToEmpty (same syntax as contents) and listing all the slots containing such collections. The transporter will send each one the copyRemoveAll message and le out the results instead of the real collection object. vectorsToNotInitialize: Suppose your module denes an object containing a vector in a slot and, although the vector accumulates contents, you want the source le to just create the vector with the correct size, but not to initialize its contents. Just include a slot called vectorsToNotInitialize, with the same syntax as the contents slot. Counterfactual Initialization: Suppose your module denes an object that includes a counter or some other state (such as process queues), but you want the source le to contain an initial value that differs from the real, current value of the slot. In order to write out the object as it would be if it were initialized, you can add a method that your object inherits called copyForFilingOut that copies your object and resets its state. (Your object must contain a slot called thisObjectPrints in order for the transporter to send it copyForFilingOut). Here is an example: suppose you want to dene a global counter object, but do not want to le out its actual value:
61
prototypes _AddSlots: ( | counter = ( | _ parent* = traits clonable. "to support copy" ^ copyForFilingOut = (copy count: 0). ^ count <- 0. _ thisObjectPrints = (). | )
Now, no matter what the count slot contains, the source le will always initialize it to zero. By the way, if any object includes the thisObjectPrints slot, the SELF world assumes that the object can safely respond to printString and other messages dened in the concreteObject protocol in mirrors. storeStringIfFail: Some kinds of objects have simple textual creation and initialization expressions: for example a point can be created and initialized by typing: x_value @ y_value, and a list can be created and initialized by typing: (element1 & element2 & ...) asList. Such expressions are much more readable in source les than the fully general creation and initialization expressions would be. In order to tell the transporter that an object has such a representation, the object must include a thisObjectPrints slot and must inherit storeStringIfFail: failBlock method that either returns the textual expression or invokes the failBlock with an error string. defaultBehavior contains the slot
storeStringIfFail: failBlock = ( failBlock value: no storeString for me)
17.3 Conclusions
The transporter remains in ux; the version described herein is just a snapshot intended to allow users to experiment with this release. Good luck, and please let me know how to improve it.
62