App Dev Guide
App Dev Guide
P46-0101-10
Copyright © 1993–2004 by Cincom Systems, Inc.
All rights reserved.
This product contains copyrighted third-party software.
Trademark acknowledgments:
CINCOM, CINCOM SYSTEMS, and the Cincom logo are registered trademarks of
Cincom Systems, Inc. ParcPlace and VisualWorks are trademarks of Cincom Systems,
Inc., its subsidiaries, or successors and are registered in the United States and other
countries. ObjectLens, ObjectSupport, ParcPlace Smalltalk, Database Connect, DLL & C
Connect, and COM Connect are trademarks of Cincom Systems, Inc., its subsidiaries, or
successors. ENVY is a registered trademark of Object Technology International, Inc. All
other products or services mentioned herein are trademarks of their respective
companies. Specifications subject to change without notice.
4 VisualWorks
Contents
Chapter 3 Syntax 74
Literals ....................................................................................................................... 74
Numbers ....................................................................................................... 74
Integers ........................................................................................... 74
Floating Point Numbers ................................................................... 75
Fixed-Point Numbers ....................................................................... 75
Nondecimal Numbers ...................................................................... 75
Numbers in Scientific Notation ........................................................ 75
Characters .................................................................................................... 76
Strings .......................................................................................................... 76
Symbols ....................................................................................................... 76
Byte Arrays ................................................................................................... 76
Arrays ........................................................................................................... 76
Booleans ...................................................................................................... 77
nil .................................................................................................................. 77
Variables ................................................................................................................... 78
Variable Types .............................................................................................. 78
Variable Names and Conventions ................................................................ 78
Private Variables .......................................................................................... 79
Temporary Variables ....................................................................... 79
Argument Variables ......................................................................... 80
Instance Variables ........................................................................... 81
Class Instance Variables ................................................................. 83
Shared Variables .......................................................................................... 83
Class Variables ................................................................................ 84
Pool Variables ................................................................................. 85
As Global Variables ......................................................................... 87
Class and Name Spaces Names .................................................... 88
Constant and Variable Bindings ...................................................... 88
Public and Private Shared Variables ............................................... 89
6 VisualWorks
Contents
8 VisualWorks
Contents
10 VisualWorks
Contents
12 VisualWorks
Contents
14 VisualWorks
Contents
16 VisualWorks
Contents
18 VisualWorks
Contents
20 VisualWorks
Contents
22 VisualWorks
Contents
24 VisualWorks
Contents
Index 579
Audience
This guide assumes that you have at least a beginning familiarity with
object-oriented programming. The description of VisualWorks begins at
an elementary level, with an overview of the system tools and facilities,
and a description of Smalltalk syntax, but does not attempt to be a
tutorial.
For additional help, a large number of books and tutorials are available
from commercial book sellers and on the world-wide web. In addition,
Cincom and some of its partners provide VisualWorks training classes.
Conventions
We have followed a variety of conventions, which are standard in the
VisualWorks documentation.
Typographic Conventions
The following fonts are used to indicate special terms:
Example Description
template Indicates new terms where they are defined,
emphasized words, book titles, and words as words.
[Link] Indicates filenames, pathnames, commands, and
other constructs to be entered outside VisualWorks
(for example, at a command line).
[Link] Indicates a variable element for which you must
substitute a value.
26 VisualWorks
Conventions
Example Description
windowSpec Indicates Smalltalk constructs; it also indicates any
other information that you enter through the
VisualWorks graphical user interface.
Edit menu Indicates VisualWorks user-interface labels for menu
names, dialog-box fields, and buttons; it also
indicates emphasis in Smalltalk code samples.
Special Symbols
This book uses the following symbols to designate certain items or
relationships:
Examples Description
File New Indicates the name of an item (New) on a menu
(File).
<Return> key Indicates the name of a keyboard key or mouse
<Select> button button; it also indicates the pop-up menu that is
displayed by pressing the mouse button of the
<Operate> menu same name.
<Control>-<g> Indicates two keys that must be pressed
simultaneously.
<Escape> <c> Indicates two keys that must be pressed
sequentially.
Integer>>asCharacter Indicates an instance method defined in a class.
Float class>>pi Indicates a class method defined in a class.
Getting Help
There are many sources of technical help available to users of
VisualWorks. Cincom technical support options are available to users
who have purchased a commercial license. Public support options are
available to both commercial and non-commercial license holders.
Commercial Licensees
If, after reading the documentation, you find that you need additional help,
you can contact Cincom Technical Support. Cincom provides all
customers with help on product installation. For other problems there are
several service plans available. For more information, send email to
supportweb@[Link].
Before Contacting Technical Support
When you need to contact a technical support representative, please be
prepared to provide the following information:
• The version id, which indicates the version of the product you are
using. Choose Help About VisualWorks in the VisualWorks main
window. The version number can be found in the resulting dialog
under Version Id:.
• Any modifications (patch files) distributed by Cincom that you have
imported into the standard image. Choose Help About VisualWorks in
the VisualWorks main window. All installed patches can be found in
the resulting dialog under Patches:.
28 VisualWorks
Getting Help
• The complete error message and stack trace, if an error notifier is the
symptom of the problem. To do so, select copy stack in the error notifier
window (or in the stack view of the spawned Debugger). Then paste
the text into a file that you can send to technical support.
Contacting Technical Support
Cincom Technical Support provides assistance by:
Electronic Mail
To get technical assistance on VisualWorks products, send email to
supportweb@[Link].
Web
In addition to product and company information, technical support
information is available on the Cincom website:
[Link]
Telephone
Within North America, you can call Cincom Technical Support at
(800) 727-3525. Operating hours are Monday through Friday from
8:30 a.m. to 5:00 p.m., Eastern time.
Outside North America, you must contact the local authorized
reseller of Cincom products to find out the telephone numbers and
hours for technical support.
Non-Commercial Licensees
VisualWorks Non-Commercial is provided “as is,” without any technical
support from Cincom. There are, however, on-line sources of help
available on VisualWorks and its add-on components. Be assured, you
are not alone. Many of these resources are valuable to commercial
licensees as well.
The University of Illinois at Urbana-Champaign very kindly provides
several resources on VisualWorks and Smalltalk:
• A mailing list for users of VisualWorks Non-Commercial, which
serves a growing community of VisualWorks Non-Commercial users.
To subscribe or unsubscribe, send a message to:
vwnc-request@[Link]
with the SUBJECT of "subscribe" or "unsubscribe".
• An excellent Smalltalk archive is maintained by faculty and students
at UIUC, who are long-time Smalltalk users and leading lights in the
Smalltalk community, at:
[Link]
• A Wiki (a user-editable web site) for discussing any and all things
VisualWorks related at:
[Link]
• A variety of tutorials and other materials specifically on VisualWorks
at:
[Link]
The Usenet Smalltalk news group, [Link], carries on active
discussions about Smalltalk and VisualWorks, and is a good source for
advice.
Smalltalk Tutorial
A new VisualWorks Smalltalk tutorial is available online at:
[Link]
The tutorial information is growing, so revisit this site.
Online Help
VisualWorks includes an online help system. To display the online
documentation browser, open the Help pull-down menu from the
VisualWorks main menu bar and select one of the help options.
VisualWorks FAQ
An accumulating set of answers to frequently asked questions about
VisualWorks is being compiled in the VisualWorks FAQ, which
accompanies this release and is available from the Cincom Smalltalk
documentation site.
30 VisualWorks
Smalltalk Tutorial
News Groups
The Smalltalk community is actively present on the internet, and willing to
offer helpful advice. A common meeting place is the [Link]
news group. Discussion of VisualWorks and solutions to programming
issues are common.
VisualWorks Wiki
A wiki server for VisualWorks is running and can be accessed at:
[Link]
This is becoming an active place for exchanges of information about
VisualWorks. You can ask questions and, in most cases, get a reply in a
couple of days.
Commercial Publications
Smalltalk in general, and VisualWorks in particular, is supported by a
large library of documents published by major publishing houses. Check
your favorite technical bookstore or online book seller.
Examples
There are a number of examples in file-in format in the examples
subdirectory, under the VisualWorks install directory. In addition, several
example listings in the document, especially those in Chapter 18,
“Working With Graphics and Colors”, invoke an Examples Browser. This
browser is provided in a parcel, also in the examples directory.
Running VisualWorks
VisualWorks runs as an virtual machine processing the data in a
Smalltalk image. The virtual machine is an executable file that interprets
and executes the Smalltalk byte-code stored in the image. There is a
separate virtual machine for each operating system supported by
VisualWorks.
The image is in a platform-independent file format, allowing a single
image file to run under the VisualWorks virtual machine running on any
supported operating system.
As you work with VisualWorks, the primary way of saving your work is by
saving the image, either periodically while working or when exiting
VisualWorks. You will probably accumulate several images over time. To
start a specific image, simply specify that image on the startup command
line.
32 VisualWorks
Running VisualWorks
Starting VisualWorks
To start VisualWorks, you run the virtual machine with the image file
passed as the argument:
virtual_machine image_file
For development work, we recommend using the engines named
vw<plat> (for example, [Link] for Microsoft Windows systems or
vwlinux86 for Linux systems). These engines include debug symbols
which can be helpful if you suffer engine crashes.
The virtual machines named [Link] on Windows systems and
visual on Unix and MacOS systems, are stripped versions of these
engines, and are suitable for deployed applications. See the Appendix
“Virtual Machines” on page 560 for more information about development
and deployment options.
By default, the virtual machine is installed in the bin/<platform>/
subdirectory of the root VisualWorks installation directory.
If no image file is specified, the virtual machine looks for an image with
the same name as the engine. For example, if you execute visual (or
[Link]) without an image name, it will look for [Link],.
Typically, you will start by changing to the image subdirectory, and
execute the object engine with the image as argument. For example:
> cd c:\visual\image
> ..\bin\win\[Link] [Link]
If you use a file manager to start VisualWorks, you may need to specify
full paths for both the object engine and the image.
If both the virtual machine and the image file are in the same directory, no
path information is required at all.
34 VisualWorks
Running VisualWorks
36 VisualWorks
Exiting VisualWorks
The sources file holds source code for the original VisualWorks system
image before you made changes. By default it is named [Link]
which is the original image name with a .sou extension.
The changes file, which typically has the same name as the image file but
with .cha as its extension, contains source code for changes you have
made to the system, specifically for any application code you have
created. Changes are recorded to this file every time you accept an edit,
whether or not you save the image, so you always have a history of work.
The changes file can become very large, and so should occasionally be
condensed using Changes Condense Changes from the Launcher’s System
menu. This removes all but the latest version of each system change.
You can change the name of the sources file and of the changes file on
the Source Files page of the Settings Tool (to open this tool, select System
Settings in the Launcher window).
Exiting VisualWorks
To end a VisualWorks development session, select File Exit VisualWorks
in the Launcher. A dialog prompts you to save the image before exiting. If
you choose to save the image, you may provide a new filename.
Selecting Cancel continues your session in the VisualWorks development
environment.
Note that closing the Launcher window, for example by clicking the
window’s close icon, allows you to either exit VisualWorks, or simply
close the Launcher window itself.
Emergency Exit
If VisualWorks stops responding to inputs such as mouse movements,
there are a few options.
You can press <Control>-\ to open the Process Monitor, which lists all
running VisualWorks processes. All UI processes are paused, as can be
seen by examining the listings. You can select a process and debug it to
find the problem.
Pressing <Control>-Y opens a debugger directly on the current process, by-
passing the Process Monitor.
If that doesn’t work, you can use the Emergency Evaluator. To open an
Emergency Evaluator, type <Shift>-<Control>-Y. An Emergency Evaluator
window will appear, with instructions to type a Smalltalk expression
terminated by <Escape>. Enter:
ObjectMemory quit
in the window, then press <Escape>. The system will shut down, after which
you can restart it.
To save the image before quitting, send:
ObjectMemory saveAs: 'filename' thenQuit: true
Then press <Escape>.
38 VisualWorks
Workspace
Workspace
A Workspace is a window in which you can test Smalltalk code. To open a
Workspace, choose Tools Workspace or click on the Workspace icon in
the VisualWorks Launcher.
Workspace Variables
Temporary variables used in a workspace have the workspace as their
scope, and exist as long as the workspace does, or until they are
explicitly cleared. These variables, called workspace variables, are
created when first assigned a value. That assignment then persists and
can be referenced by subsequently evaluated expressions in that
workspace. The variable and its assignment are saved with the image,
and so are available when reloading the saved image.
40 VisualWorks
Workspace
Performing Operations
While working in VisualWorks, you will need to perform a variety of
operations, such as opening tools and evaluating (executing) Smalltalk
expressions. The Launcher’s menus and buttons provide some number of
these operations, and many more are available in pop-up menus
throughout the system.
VisualWorks, like all Smalltalk systems, requires a mouse as a pointing
device. There are three primary operations performed using the mouse,
corresponding to three “buttons”:
• The <Select> button selects objects and text.
• The <Operate> button opens the <Operate> menu, which contains
commands appropriate to the current view. This context-sensitive
menu changes based on the current window and selection.
• The <Window> button opens the <Window> menu, which contains
commands that operate on the current window.
Since VisualWorks supports systems with one, two, and three-button
mouses, there are different methods for invoking operations on the
different systems.
42 VisualWorks
Add-in Components
Now select the entire text (using the <Select> button), then click and hold
the <Operate> button with the cursor in the workspace. Move the cursor
to select Do it, and release the mouse button. The text will display in the
Transcript.
Add-in Components
With the increased use of components in VisualWorks, many features of
the development environment are now distributed as add-ins to the base
image. These add-in components are distributed as parcel files. When
loaded into the image, the components are represented as packages and
bundles. When Store is installed, packages and bundles can be loaded
directly from a repository (refer to Chapter 7, “Managing Smalltalk Code”
below and the Source Code Management Guide).
For example, the UI Painter is a standard tool for developing the GUI in a
VisualWorks application, and used to be part of the standard base image.
It is now an add-in component that must be loaded for GUI development.
However, it does not need to be loaded to run an application with a GUI
built using the UIPainter. The runtime support is in the base image.
Partitioning and the environment in this way keeps the image smaller, and
simplifies application delivery, by allowing you to load only those
components required for your application and development environment.
Parcel Manager
Parcels and their descriptions may be viewed, loaded, and unloaded
using the Parcel Manager. To open a Parcel Manager, choose
System Parcel Manager or click on the corresponding icon in the Launcher
window.
The Parcel Manager enables you to view all parcels on the parcel path
that is associated with the working image. Use the tab control over the
left-hand list to control the presentation of the parcel path.
Select the Suggestions tab to see a pre-defined set of recommended
parcels. Each category under Suggestions contains parcels that have been
identified as key add-in features for VisualWorks. By selecting a particular
44 VisualWorks
System Browser
Icon Description
Use the tab controls on the parcel list view to view parcels sorted in
alphabetical order, or a hierarchical presentation ordered by parcel
prerequisties. The parcel details view (lower-right corner) shows
comments and properties associated with the selected parcel.
System Browser
The principal programming tool in VisualWorks is the System Browser.
You use it for “browsing” the code library, editing, compiling, and printing
any selected portion of source code. The browser also provides special-
purpose tools for refactoring, rewriting, checking and testing code.
To open a browser, choose Browse System or click on the Browser icon
in the VisualWorks Launcher window.
The System Browser allows you to select either a Package (the default)
or Parcel view of the system components. From either of these views, you
can also select a Hierarchy view, either of the selected component or of
the entire system. To change the primary view for a browser, select the
view from the Browser menu. To change default view for a new browser,
use the Browser page of the Settings Tool (System Settings in the
Launcher window, and set the default to either Package or Parcel.
Depending on the current view, the lists show different items. It will take
some experimentation and experience to get comfortable with the
browser, but the following comments will guide your learning.
Package / Bundle Class / Namespace Protocol Method / Variable
List List List List
Navigator
Code Tool
46 VisualWorks
System Browser
Navigator
Code Tool
Browser Navigator
The different parts of the browser’s navigator provide different views of
the system. Here is a brief summary of their function and use:
Package View
The VisualWorks library is organized into packages and bundles. Each
code definition is contained in a package, and can be viewed by selecting
the package. Packages can also be grouped into bundles and the
contained definitions browsed. The browser displays packages when
Package tab is selected in the Browser.
When Store is loaded, packages and bundles support code revisioning
and related mechanisms to assist in source code management. For
information about working with packages, refer to the VisualWorks
Source Code Management Guide.
Note that the use of packages has now replaced the use of class
categories and parcels as units of organization, as they were used in
previous versions.
Parcel View
Parcels are intended as a deployment artifact, providing an external file
container for the code defined in packages (and bundles). At this time, it
is still useful to be able to browse code in terms of the parcel that
contains it, which you do using the Parcel view (select Browser Parcel in
the system browser).
Hierarchy View
Occasionally it is useful to explore a class in terms of the other classes
from which it inherits behavior, or that inherit behavior from it. The
navigator allows you to do this by displaying the hierarchy of the selected
class.
To vew the entire class hierarchy, start by selecting class Object. You can
then find and browse a class by navigating through the hierarchy to it.
Although this is seldom very useful, it can be instructive.
Class / Name Space View
Classes and name spaces are defined in packages, so the contents of
the Class / Name space view depend upon the selected Package.
In addition to having a superclass, each class is defined in a name space,
which identifies the s. A name space is a name resolution scope for name
space, class, and shared variable names. Typically, you create your own
48 VisualWorks
System Browser
name space and then create your applications within that name space.
(Refer to “Working with Name Spaces” in Chapter 6 for more
information.)
When the class hierarchy view is selected, this view shows the containing
package for the selected item.
Instance, Class, and Variable Views
The Instance, Class, Shared Variable and Instance Variable tabs toggle the
contents of the method category and method/variable views, selecting
whether the categories and defintions of instance methods, class
methods, shared or instance variables are shown. In some situations,
such as when a namespace is selected that has only shared variables
defined in it, only one of the buttons, in this case Shared Variables, is shown.
Usually, any of the buttons can be selected, even though there may be no
entries for that view.
Icons in the Navigator
The browser’s navigator uses a number of special icons to distinguish
code components, special system classes, as well as the condition of
individual methods. The following table offers a brief summary:
Icon Description
Package
Bundle
Name space
Subclass of Model
Subclass of ApplicationModel
Subclass of Collection
Subclass of Exception
50 VisualWorks
System Browser
Searching
The navigator tool bar includes an entry field to do a quick search by
name for classes, variables, or methods:
To find a class, simply enter its name and select Accept from the
<Operate> menu, or press the <Return> key. To find a method, enter its
name, preceded by the # (pound) character. Wildcard searches are
possible using the * (asterisk) character.
Drag and Drop
To reorganize code, you can drag and drop methods on classes or
protocols; protocols on other classes or on protocols; classes on other
categories; and categories on other categories.
Controlling Visibility of Methods
By default, the browser’s method list only displays those methods
belonging to the currently selected class and protocol. Several options
are provided for controlling and expanding the visibility of methods.
When a class is selected, the browser may optionally be set to show all
methods in the class when no protocol is selected. To enable this option,
select Show all Methods when No Protocols Selected on the Browser page of the
Settings Tool.
Just as it is often useful to see class inheritance using the Hierarchy view,
so too it is often useful to see inherited methods. To expand the visibility
of the Method List to include inherited methods located in a superclass,
select the name of the superclass from the Method Visibility menu. This
setting remains active until you navigate to another class.
To fix the initial visibility setting so that it remains active while viewing
different classes, select Show All Inherited or Show All Inherited Except for Object.
To disable the expanded visibility, choose Show No Inherited.
Using Multiple Views
The System Browser can have with multiple active “views” on a method.
For example, while editing one method, you can switch to a new view to
look up some value in another method, and then return back to your
edited method without opening a new browser.
52 VisualWorks
File Browser
File Browser
The File Browser allows you to navigate the local file system, listing and
selecting directories and files. It is commonly used to find Smalltalk
source files to file-in (.st files), and for editing simple text files.
To open a File Browser, choose File File Browser or click on the
corresponding icon in the Launcher window.
Volumes and directories are shown on the right, files and their contents
on the left. When a file is selected in the upper-right view, its contents are
displayed in the lower-right view.
Special structured viewers are included for displaying VisualWorks
source files (.st), parcels, parcel source files, and XML source files. Use
the tab controls on lower-right view to select the desired view.
See the VisualWorks Tools topic in the online Help for more information
about the File Browser (select Help Topics).
Inspectors
An inspector allows you to examine objects by exploring their constituent
objects, the values of the object’s instance variables. The VisualWorks
inspector incorporates a number of additional editing tools that greatly
enhance the control you have over live objects.
The inspector has a variety of options, and you can use it to perform a
number of operations that otherwise might require several tools. We
describe a few features here, but you should explore and experiment
further.
Inspecting an Object
At the core of the inspector are two views, with the object’s variables
listed in the left-hand view. When you select a variable, its value appears
in the right-hand view.
For example, to inspect a point, enter this expression in a Workspace,
select it, and then select Inspect it from the <Operate> menu:
50@30
Alternatively, evaluate this expression using Do it:
(50@30) inspect
The resulting Point has two variables, x and y.
To view the value of a variable, select it. The value is shown in the right-
hand view. You can also do a multi-select of values, to see the values of
the selected variables all at the same time.
54 VisualWorks
Inspectors
You can inspect the component objects also, by selecting the object in the
left view and selecting dive in the inspector’s <Operate> menu, which then
shows the selected object in the current inspector. To back out of a diving
inspector, select Back in its <Operate> menu. To open a new inspector on
the object, select Inspect from the Object menu.
For some objects, the Basic view may include extra parts which are not its
instance variables. -self, for example, is a part that is always there even
though it is not an instance variable (these aspects of the object are
distinguished with a leading hyphen “-” character). For a further example,
have a look at a compiled method. Evaluate:
(Object compiledMethodAt: #printString) inspect
The basic view includes -bytecode and -source. These are not really parts of
the receiver, as instance variables are, but they are included in the basic
view as "virtual" attributes, just like -self, the object itself. For more
examples, inspect an Integer or a Character.
Drag-and-drop operations can be performed on the elements. If you
select a variable and drag it on top of another, its value will be assigned to
the target variable.
Inspecting Collections
Specialized inspectors for dictionaries and other collections provide
extended inspecting capabilities. For example, evalutate:
(OrderedCollection with: 1 with: 2 with: 3 with: 4) inspect
The resulting special inspector opens on the elements of the collection.
Modifying Objects
The right-hand view is a code view, in which you can type and execute
Smalltalk expressions. In this respect, it is like a workspace. Variables are
resolved within the scope of the code view.
Occasionally it is useful to set the value of a variable. You can do this by
entering an expression in the code view, and then selecting Accept in the
view’s <Operate> menu. This evaluates the expression and assigns the
return value to the variable under inspection.
For example, in the OrderedCollection inspector shown above, select the
first element. It’s current value is 1. In the code view, enter:
1+1
Select it and pick Accept in the view’s <Operate> menu. The expression is
evaluated to 2, which is then assigned to the variable.
Evaluating Expressions
While you can evaluate an expression in a code view, you lose that
expression as soon as you select another variable. A convenience feature
is a code evaluator view that can preserve expressions entered in it.
56 VisualWorks
Inspectors
The pane works much like the workspace. However, the evaluation
context is the object under inspection. Accordingly, you can use self to
refer to the object itself, and can perform operations on the object.
You can also “save” the contents of the evaluation pane, making the
same contents available to all inspectors. The contents are stored in the
inspector’s class variable, and so is shared by all instances. To write the
contents to the variable, select Accept on the pane’s <Operate> menu.
UI Painter
The “Visual” in VisualWorks emphasizes the graphical approach to
Graphical User Interface (GUI) design and development. This is provided
by the UI Painter.
The UIPainter is initially unloaded from the base image. To load it, use the
Parcel Manager (choose System Parcel Manager in the Launcher window;
within the Parcel Manager, the UI Painter can be found in the Essentials
category).
The Painter tool is in three parts:
• Canvas (lower right) - represents a single window, on which you
place widgets, the graphical components of the GUI.
• Palette (top right) - presents a collection of widgets that are
commonly used in a GUI, and some widget arrangement buttons.
• GUI Painter Tool (left) - provides a collection of menu commands and
buttons for performing formatting and other operations on the canvas,
a hierarchical view of the widgets on the current canvas, and the
properties of the selected widget.
58 VisualWorks
Configuring the VisualWorks Environment
The Palette has one button for each type of widget. To add a component,
for example an input field, to your canvas, you simply click on the Input
Field icon in the Palette to select it, and then click in the canvas to place
the widget.
For a fuller description of this and related GUI building tools, as well as a
detailed description of GUI building in VisualWorks, refer to the GUI
Developer’s Guide.
Settings
VisualWorks includes a Settings Manager that allows you to control a
variety of global parameters, such as the appearance of windows (Look
and Feel), source file name (Source), default font for text (Text), and so on.
Groups of customizable features are organized as a tree on the left hand
side of the Settings manager. Select a group in the tree to see and
change its settings in the right hand side of the window
To open the Settings Manager, choose System Settings in the
VisualWorks Launcher window, or click the corresponding button.
Settings are organized into pages. Settings on the same page are usually
related and affect the same area of application functionality. Each page
has a context-sensitive Help button that displays additional information to
guide you in the proper setting of each parameter.
Press the OK button to apply all unapplied changes on all pages and close
the window. It is not necessary to apply changes made to a page before
switching to another page. Use the Apply button to apply all changes,
leaving the window open.
The <Operate> menu of the settings tree includes allows you to
manipulate and modify settings. To save all settings on all pages in a file,
select Save... and specify the name of the file. Use Load... to read all
settings from a previously saved settings file. The values are accepted
immediately. To immediately restore the values of all settings to default,
select Reset to Default.
To load, save, or restore the settings of the current page, select Load
Page..., Save Page..., or Restore Page to Default. The values that are loaded or
restored are displayed, but not applied until either the OK or Apply button is
pressed.
60 VisualWorks
2
Object Orientation
A
A B
A C
A
Recompile
Expand postal code Expand postal code validation routines
62 VisualWorks
Objects and Methods
It turns out that the procedures cluster very naturally around the data.
The procedure for validating a postal code is something that only the
postal code object needs to know. Likewise, only the address object
needs to know what its valid inputs are. So if we can make each data
object smart enough to perform the useful operations on itself, we no
longer need separate procedures at all.
A
A B
A C
C
Expand postal code Recompile
Postal code
object
Composite Objects
Most objects are composite objects, being composed of several other
objects. For example, a customer object would contain identifying objects
such as customer number, name, address, city, state, postal code, and
telephone number. Why have a customer object at all? Because some
procedures have to be performed for a customer rather than a postal
code or a telephone number.
Account object
Customer object
Hierarchy of Objects
64 VisualWorks
Variables and Methods
An object containing
variables and methods
Variables Methods
zip getZip
setZipTo:
isValid
The two parts of an object are also known as state and behavior. The
values held by an object’s variables define its state. Its methods—what it
knows how to do—define behavior.
For example, a postal code object might have a variable called zip to hold
the postal code string. It needs at least two methods to be a civilized
object, as listed in the following table.
Method Names
The method name is used by other objects to select the operation defined
in a method. The method name is used when sending a message to
specify the requested operation. Accordingly, it is also called method
selector, a message selector, or simply a selector.
A message is sent by specifying a selector plus any argument values. We
frequently refer to, for example, “a getZip message,” meaning a message
selector plus arguments, if any.
The fundamental unit of any Smalltalk expression is an object reference
followed by a message, as in postalCode getZip. This expression asks the
postalCode object to return the value stored in its zip code variable.
Method names may contain letters, numbers, and underscores, but may
not begin with a number. When two or more words are combined to form
a name, as in this case, second and later initials are capitalized to
improve readability. This convention applies to all names in the system:
objects, variables and methods. All method names begin with a lower-
case letter.
Method Categories
It is not uncommon for an object to have dozens of methods. From class
to class, methods tend to cluster in recurring groups—for example,
objects that have data also have a set of methods for accessing the data.
Collectively, such methods are known as accessing methods. You may
encounter the phrase “accessing protocol,” which refers to the set of
methods for accessing data within an object.
Variables Methods
zip getZip
accessing protocol
setZipTo:
isValid testing protocol
66 VisualWorks
Classes and Instances
Class Variables
A class can also have its own state values, which serve as system
constants. These states are stored in shared variables. For example, the
class Date has a shared variable called MonthNames, which stores an Array
containing names for the 12 months. Our ZipCode class might have a
shared variable called Formats, to store a collection of known formats. In
either of these examples, it would be wasteful to store a new copy of the
variable in every instance that is cloned from it because the value is
constant for all instances.
Like class names, shared variable names begin with a capital letter.
ZipCode (class)
Class Class
variables methods
Formats formats
aZipCode (instance)
Instance
Template Instance variables
for methods zip
instance getZip
variables setZipTo:
zip isUSZip
isCanadianZip
68 VisualWorks
Class Inheritance
Class Inheritance
The class library is organized in a hierarchy of specialization, very much
like the taxonomy applied to the animal kingdom. At the root of the tree is
class Object. One kind of Object is a class called Magnitude. If you dig down
through a few more levels of specialization within the Magnitude
subhierarchy, you come to a class called SmallInteger. An instance of class
SmallInteger is an integer such as 3.
If you execute the expression 3 raisedTo: 4, the correct result (81) will be
returned. A raisedTo: message with an argument of 4 is being sent to 3,
which is an instance of SmallInteger. From the prior discussion about
instance methods, one would assume that the class SmallInteger has an
instance method called raisedTo:, but that is not the case.
Object
Magnitude
ArithmeticValue
Number
Integer
SmallInteger
Looking up a Method
Smalltalk provides a method-lookup mechanism that starts its search for
a given method in the obvious place—the class of the object to which the
message was sent. If no such method exists there, the method finder
climbs up through the hierarchy, stopping at each level to look for the
method. In our example, the method finder has to go up two levels, past
the Integer class to its parent, Number. There it finds the raisedTo: method.
SmallInteger is a subclass of Number, because it provides specialized
variables and/or methods. Number is a superclass of SmallInteger, as is the
class that sits between them in the hierarchy, Integer. Class Object is the
top-level superclass of all other objects.
The method finder has two ladders at its disposal, one for finding class
methods and the other for locating instance methods. As it climbs upward
through the superclasses, it uses only one ladder or the other, but not
both. Its choice of ladder is determined by the message recipient. If the
message is sent to an instance (3, in our example), only instance
Object
Magnitude
ArithmeticValue
Number
raisedTo:
Integer
SmallInteger
instance class
70 VisualWorks
Class Inheritance
values in the Customer object, the Customer inherits methods for keeping
that View in sync with the data changes. We don’t have to write any code
for such dependency coordination.
Abstract Classes
Some classes are designed only to provide inheritable features, and are
never meant to be instantiated. For example, the class Object, the ultimate
superclass of all other classes, has an empty template for instance
variables. This may seem odd considering that instance variables hold
the actual data. What would an instance of class Object hold as its nugget
of data? The answer is that Object is not intended to have instances. Its
behavior is inherited and used by its subclasses and their instances.
When a class is not intended to be used to create concrete instances, it is
called an abstract class. An abstract class is frequently useful as a
repository for variables and methods that are useful to two or more
classes, none of which is a logical subclass of the other. Another way of
looking at it is that the similarities shared by a group of objects are
squeezed up from their separate locations into a common superclass.
The postal code can serve as an example once again. Until now, we have
been trying to make a single ZipCode class handle two very different
postal code formats. Presumably, as the customer base expands, more
methods would have to be added to handle other postal systems.
Eventually, a plain old United States numeric zip code would have to be
stored in a class that had more irrelevant methods than relevant ones—
and that’s the sort of awkwardness this object-oriented technology is
supposed to avoid.
Let’s make ZipCode an abstract superclass, with two new subclasses:
USZip and CanadianZip. They can both inherit the zip variable and the
accessing methods (getZip and setZipTo:) as well as any class variables
and class methods. The isValid method must be re-implemented in each
of the subclasses, to handle their specific formats. The ZipCode class’s
version of isValid can then hand off the validation request to the
appropriate subclass. To Customer, Supplier and any other objects that
interact with ZipCode, the mechanism for finding out whether a zip code is
valid has not changed.
A subclass of an abstract class can be abstract itself. One might make
USZip abstract, for example, and create one subclass representing the
five-digit format (OldUSZip) and another for the hyphenated-nine-digit
format (SlowToBeAdoptedUSZip).
Choosing a Superclass
When you create a new class, choosing its superclass is an important
design decision. The choice is made easier when you employ an
architecture that has been proven in many diverse applications.
Object
The key to this architecture is to divide your application into two parts.
First develop the data structure and the attendant processing, then invent
the user interface. The user interface is further subdivided into input and
output modules. The data-and-processing module is referred to as the
model. The output module usually consists of the screen displaying
mechanisms—it’s called the view. The input module is called the
controller because it enables the user to control the sequence of events
by entering data and commands.
Not surprisingly, Smalltalk provides an abstract class as the intended
starting point for each of these three modules: Model, View and Controller.
Thus, the architecture is known as model-view-controller, or MVC,
programming. For detailed information about MVC design, see Chapter 8,
“Application Framework.”
We use the term “application” broadly here—an object as lowly as a
postal code can be regarded as a self-contained model that can have an
associated view (a box on the screen in which the postal code is
displayed) and controller (for accepting keyboard input to the model in the
form of data entry). This implies that an MVC application can be a
72 VisualWorks
Class Inheritance
Literals
A literal is a Smalltalk expression that always refers to the same object.
This reference cannot change.
There are several kinds of literals in VisualWorks, including numbers,
characters, strings, symbols, arrays, byte array literals, and three special
literals: nil, true and false.
Note that literals are strongly typed, meaning that each is a full-blooded
object, an instance of a class, and so respond to the full protocol of their
class.
Numbers
Numbers are represented in the usual way, using a preceding minus sign
and embedded decimal point as required.
Integers
Integers are expressed as numeric literals such as 101, or as the result of
arithmetic operations involving one or more integers such as 55 + 46.
74 VisualWorks
Literals
Octal Decimal
8r377 255
8r34.1 28.125
8r-37 -31
When the base is greater than ten, the capital letters starting with “A” are
used for digits greater than nine. For example, the hexadecimal
equivalent of the decimal number 255 is 16rFF.
Numbers in Scientific Notation
Numbers can also be expressed in scientific notation by including a suffix
composed of e (for exponent) or d (for double-precision) plus the
exponent in decimal. Note that you can also use the letter q instead of d.
The q (quad-precision) is available for portability to other Smalltalk
systems, but in VisualWorks, q has the same effect as d.
The base is raised to the power specified by the exponent and then
multiplied by the number. For example:
Characters
A character literal is always prefixed by a dollar sign. For example:
$a
$M
$-
$$
$1
Strings
A string literal is enclosed in single quotes (double quotes are used to
delimit a comment). Any character can be included in a literal string. If a
single quote is to be included, it must be preceded by a single quote, as
in:
'I won''t fail'
Symbols
A symbol is a label that conveys the name of a unique object such as a
class name. There is only one instance of each symbol in the system. A
symbol literal is preceded by a number sign, and optionally enclosed in
single quotes. For example, #Float and #'5%' are legal symbols. If a
symbol is enclosed in an array, it must still be preceded by a number sign.
Byte Arrays
A literal byte array is enclosed in square brackets and preceded by a
number sign. Elements of the array must be integers between 0 and 255.
They are separated by one or more spaces. The result, as in the following
example, is an instance of class ByteArray:
#[255 0 0 7]
Arrays
An array literal is enclosed in parentheses and preceded by a number
sign. Elements of the array are separated by one or more spaces (extra
spaces are ignored). An array literal embedded in another array must still
76 VisualWorks
Literals
Booleans
The boolean constant true is the sole instance of class True, and the
constant false is the sole instance of class False, both of which are
subclasses of Boolean. Unlike most instances, the values of true and false
are hard-wired in the compiler, which qualifies them as constants.
Even though they are constants, their behavior is defined in the instance
methods of the classes True and False, which implement boolean tests
and operations, such as ifTrue:, ifFales:, and:, or:, and not.
A Boolean value is seldom used directly, but is the return value of
comparison operations, and then used in branching control structures.
Refer to “Branching” in Chapter 5, “Control Structures” for more
information.
nil
The nil object is the sole instance of class UndefinedObject. As the class
name implies, nil is the null value given to variable slots that have not yet
been assigned a more interesting value. Like the booleans, nil is hard-
wired in the compiler. Its behavior is defined in UndefinedObject—for
example, it overrides the isNil method implemented by Object (answering
true instead of false).
It is expected that there is only one instance of nil in the system. Do not
create additional instances, even though this is possible using basicNew,
because this will cause VisualWorks to crash.
Variables
Objects are referred to by their names. Except in the case of literals,
objects are named by being assigned to a variable.
Variables are of two types, depending on their reference scope. Private
variables can be referenced only by a single object; they are private to
that object. Shared variables are accessible by multiple objects.
Variable Types
Unlike some other object-oriented environments, Smalltalk variables are
untyped, meaning that any variable can hold an object of any type.
Another way to say this, and perhaps better, is that Smalltalk variables
are dynamically typed. What makes this a better way to think of it is that
Smalltalk itself is strongly typed; everything in Smalltalk is a full-blooded
object, an instance of a class. There are no “primitive” types
78 VisualWorks
Variables
Capitalization Conventions
In conformance with the ANSI standard, VisualWorks does not allow the
use of periods in identifiers. VisualWorks does, however, employ a
notational extension for referencing bindings (the primary referents of
shared variable, class, and name space names) that does use periods.
This notation provides a way for referencing a binding in terms of the
name space and/or class and/or shared variable in which it is defined.
Refer to “Binding References” on page 131 for more information.
Private Variables
A variables is an association between a name and a changeable value.
The variable’s name is used to reference its value within the variable’s
name resolution scope. VisualWorks Smalltalk has several kinds of
variables for various naming scopes. The following variables are “private,”
in the sense that they are accessible only to specific objects. Shared
variables are discussed later (see “Shared Variables” below).
Temporary Variables
A temporary variable is most often encountered in a method, where it
provides temporary storage for an argument or a calculated value. Its
lifetime begins when it is declaration is evaluated, within the method or a
block expression within the method, and ends when the block or method
finishes processing and returns control to the calling object. The naming
scope of the variable is the method or block in which it is declared, and is
inaccessible outside of that scope.
A temporary variable is declared by enclosing its name between vertical
bars. The declaration must follow the message definition, and usually
follows a comment explaining the method, but is otherwise the first part of
the method definition.
80 VisualWorks
Variables
82 VisualWorks
Variables
Instance variables are inherited, so an instance has its own copy of the
instance variables declared by all of its superclasses. For example, the
class SystemDictionary is a subclass of Set, so it does not need to declare
its own tally variable because it can use the tally that is declared in its
superclass.
Class Instance Variables
A class instance variable stores data that varies with each subclass in a
hierarchy. It is declared as part of the class definition, and can only be
accessed by a class method.
For example, suppose you have an abstract LanguageDictionary class that
has methods for looking up words to verify spelling, etc. You give
LanguageDictionary a class instance variable named wordCollection. Now
you create a series of subclasses corresponding to the English language,
the Polish language, and so on. The EnglishLanguage class can initialize
wordCollection to hold English words. The other subclasses can initialize it
differently. Then when an instance of any subclass asks for wordCollection,
it gets the appropriate language-specific version.
LanguageDictionary class
wordCollection ()
EnglishDictionary class
wordCollection (‘aardvark’ ... )
PolishDictionary class
wordCollection (‘abak’ ... )
The advantages of this approach are that you still only have to initialize
the wordCollection once for each subclass (unlike instance variables) and
all subclasses can reuse methods that employ a common variable name
(unlike class variables).
Shared Variables
A shared variable is a variable that can be shared, or referenced, by
multiple objects. In previous releases of VisualWorks, shared variables
included class variables, pool variables, and global variables. These
various variable types are unified as a single type, called simply a “shared
variable.”
84 VisualWorks
Variables
2 Select, or add and select, a category for the new shared variable, in
the methods/shared variables list pane. The shared variable definition
template is displayed in the code pane:
[Link] defineShared: #NameOfBinding
private: false
constant: false
category: 'category description'
initializer: 'Array new: 5'
3 In the template:
• Replace #NameOfBinding with a symbol specifying the shared
variable name, such as #MySharedObject.
• Set the private: field to true to make the variable private; otherwise,
leave it as false. (Refer to “Public and Private Shared Variables”
below.)
• Set the constant: field to true if the variable’s value should not be
changed; otherwise, leave it as false. (Refer to “Constant and
Variable Bindings” below.)
• Enter an initialization expression, as a String, in the initializer: field,
or enter nil. (Refer to “Initializing Shared Variables” below.)
4 Select Accept from the browser’s <Operate> menu to save the
definition and create the shared variable.
Your new shared variable is added to the list. It can be viewed in any
class browser by selecting the Shared Variables tab and its category.
Pool Variables
Shared variables can also be defined directly in name spaces (non-class
name spaces). For example, in the Graphics name space are defined a lot
of classes, and two further name spaces: SymbolicPaintConstants and
TextConstants. These name spaces exist solely as the name scopes for
collections of shared variables.
Each shared variable is defined directly in the name space. Initialization
values for the variables are provided either on the definition’s initializer:
line, as is done for most of the TextConstant variables, or in an appropriate
class initialization method, as is done for the SymbolicPaintConstants
variables.
For these variables to be accessed within a name space other than its
defining name space, the variable must be imported, usually by a general
import of its name space. (Refer to “Importing Bindings” on page 133 for
more information.)
You can define a pool by creating a name space, which is the pool, and
then adding shared variables to it using a series of at:put: messages.
Browse SymbolicPaint class method initializeConstantPool for an example.
A better approach is to define the pool name space, and then add shared
variables to it:
1 In the System Browser class/name space list, select the name space
that will contain the pool.
Select the most local name space that makes sense for the breadth
of availability appropriate for this shared variable.
2 Select Add Name space from the browser’s Class menu. The name
space definition template is displayed in the code pane.
3 Complete the template, specifying the name of your pool as the name
space name. (Refer to “Creating Name Spaces” on page 126 for
completing this template.)
4 Select the pool name space, then pick Add Shared Variable from the
browser’s Class menu. The shared variable definition template is
displayed in the code pane:
Smalltalk defineSharedVariable: #NameOfBinding
private: false
constant: false
category: 'As yet unclassified'
initializer: 'Array new: 5'
5 In the template:
• Replace #NameOfBinding with a symbol specifying the shared
(pool) variable name, such as #MySharedObject.
• Set the private: field to true to make the variable private; otherwise,
leave it as false. (Refer to “Public and Private Shared Variables”
below.)
• Set the constant: field to true if the variable’s value should not be
changed; otherwise, leave it as false. (Refer to “Constant and
Variable Bindings” below.)
• Provide an appropriate category: string.
• Enter an initialization expression, as a String, in the initializer:
field, or enter nil. (Refer to “Initializing Shared Variables” below.)
6 Select Edit Accept in the browser to save the definition and create
the shared variable.
86 VisualWorks
Variables
At this point the pool variables are all defined and initialized. You may
which to edit the definitions, however, to make the variables private or
constant, or to change.
To see your new shared variables, open a System Browser, select the
Shared Variables tab, select the pool’s super-name space in the name
space list, select the pool name space in the class/name space list, and
select a category.
As Global Variables
Globals are seldom used in VisualWorks, having been largely replaced
by pool variables. Even before VisualWorks 5i, only a few “system
globals” such as Transcript and Processor have remained in the system. In
general, they are a bad practice in object-oriented programming, because
they break encapsulation, and so are to be avoided.
Instead of globals, these remaining system objects are defined as shared
variables in a name space that is almost certainly accessible to all name
spaces. Transcript, for example, is defined as a shared variable in the
[Link] name space.
To browse these definitions, examine the Smalltalk name space in the
System Browser, and then select Core in the class/name space list, and
browse the shared variables. You can do a search for Transcript using the
browser’s built-in search mechanism (upper-right corner of the tool).
The resulting shared variables aren’t truly “global” to the system, since it
is easy to define a name space that doesn’t import Core.
To define a shared variable:
1 In the System Browser, select a name space in the class/name space
list to be the super-name space.
Select the most local name space that makes sense for the breadth
of availability appropriate for this shared variable. For the widest
availability, select the Smalltalk name space.
2 Select Add Shared Variable from the browser’s Class menu. The
shared variable definition template is displayed in the code pane:
Smalltalk defineSharedVariable: #NameOfBinding
private: false
constant: false
category: 'As yet unclassified'
initializer: 'Array new: 5'
3 In the template:
88 VisualWorks
Variables
Note that you can change a shared variable’s definition, and so change it
from being variable to being constant. If you do so, be aware that
methods that set the variable will now fail.
Public and Private Shared Variables
Most Smalltalk dialects lack an enforceable distinction between public
and private classes and methods. Variables have traditionally been either
private (instance, class, and class instance variables) or public (global
and pool variables), depending on the kind of variable.
VisualWorks uses name spaces and shared variables provide a way to fill
some of this lack, by allowing you to control imports at two levels:
definition and import.
At either its creation or when imported, a shared variable can be declared
to be either public or private.
• If a binding is public, it is available for import by a name space or
class.
• If a binding is private, it is not available for import by a name space or
class.
Refer to “Importing Bindings” on page 133 for more information on
importing.
Defining a Binding as Private or Public
At one level, in its definition, each individual class, name space, and
shared variable is declared as either public or private by setting the
Boolean argument to the private: field. When set to false the binding is
public, and so can be imported. When set to true the binding is private,
and cannot be imported. At this level, privacy or publicity is set for the
object itself, and so is absolute.
So, for example, a shared variable that is defined in MyNameSpace and
declared as private is accessible only in the scope of MyNameSpace, and
cannot be imported by any name space or class. It is hidden from
anything that imports MyNameSpace.
Name spaces and classes are usually defined as public, since they
should be imported by name spaces that need to access them. Pool
variables also should be defined as public, since they also are meant to
be imported. Class variables, shared variables that are defined within the
scope of a class, are also usually defined as public, so they can be
accessed by the class’s subclasses, and their instances.
Defining a name space, class, or general shared variable as private is the
exception, but an option if appropriate.
90 VisualWorks
Variables
Special Variables
For three special variables, the value changes according to the execution
context but cannot be changed by assignment: self, super, and thisContext.
The most prevalent of these special variables is self, which holds a
reference to the object that is executing the current message.
In the simplest case, self merely allows the programmer to direct a new
message to the specific instance that is executing the current method. In
effect, an object can execute another of its own methods. A hypothetical
doSomething method could use a computeX method to calculate a number,
for example, with the expression self computeX.
A more complicated case arises when inheritance is involved. Suppose
the doSomething method is located in the superclass of the object that
received the doSomething message. But computeX is implemented by the
subclass. How do we send the method finder back to the bottom of the
ladder to search for computeX, rather than just starting from its superclass
location?
doSomething method
aSuperclass
self computeX
anObject doSomething
The special variable self is a pointer to the object (in this case, anObject)
that received the message being executed (doSomething)
The surprising but pleasing answer is that the expression self computeX
still works. The new message (computeX) is directed at self, which refers to
the object that received the previous message (doSomething).
It’s important to remember that self does not necessarily point to an
instance of the class whose method is being executed. In our example,
self is used in the parent’s method but it refers to the child. Thus, using
self in a method automatically provides for downward growth in the
hierarchy.
The super variable is very similar to self, except super tells the method
finder to begin its search one level above the executing method in the
class hierarchy. The receiver is the same as for self, namely the sending
object. This is useful when a subclass wants to add operations to its
parent’s method without having to duplicate the parent’s code. Note that
super is in the nature of a qualifier applied to the method finder, so it
cannot be assigned to a variable (as self can).
getX method
anObject
super computeX
anObject getX
Undeclared Variables
When a variable is deleted while references to it still exist, or a reference
to a variable is loaded (by a parcel or package) but never declared, its
name is entered in the Undeclared name space. This name space is
maintained by the system and need not concern you under normal
circumstances— but it can provide useful clues to certain kinds of
program errors.
To inspect the contents of Undeclared, select in the Launcher Browse
Global, and enter undeclared in the promptor. This opens a Namespace
Inspector on the name space.
92 VisualWorks
Message Expressions
Message Expressions
A message expression is the fundamental unit of programming in
Smalltalk. It has three kinds of components: a receiver, a method name,
and zero or more arguments. In 9 raisedTo: 2, the receiver is 9, the method
name is raisedTo:, and the argument is 2. The term message technically
refers to the method selector and arguments, while a message
expression includes the receiver.
Every message returns an object to the message sender. In the example
just given, the raisedTo: method returns an instance of SmallInteger—
specifically, 81. There are three ways to denote the object to be returned
from a method:
• By default, the message receiver (self) is returned to the sender.
• A return operator (^, entered as <Shift-6> on most keyboards)
preceding a variable name causes that object to be returned. For
example, the expression ^anObject causes anObject to be returned.
• A return operator preceding a message expression returns the value
of that expression. For example, the expression ^3 + 4 causes the
object 7 to be returned.
A period is used to separate message expressions. No period is
necessary after the final expression in a series.
There are three types of message: unary, binary, and keyword
expressions. In addition, two or more messages can be joined in
sequence. Each of these constructs is described below.
Unary Messages
A unary expression has a receiver and a method name but no argument.
The following are all unary expressions:
1.0 sin. "Returns the sine of 1.0."
Random new."Returns a random number generator."
Date today. "Returns today's date."
Binary Messages
A binary expression uses a special character, such as a plus sign ($+), as
its method name and takes one argument. Some binary selectors are
combinations of two special characters, such as the comparison selector
>= (greater than or equal to). The characters that allowed in a binary
selector and the construction rules for a binary selector are specified
precisely in Appendix A, “VisualWorks Smalltalk Syntax Description”.
94 VisualWorks
Message Expressions
Keyword Messages
A keyword expression has a receiver, one or more argument descriptors
(keywords), and one argument for each keyword. Each keyword ends in a
colon. The following are valid keyword expressions:
aDate addDays: 5 "Add five days to aDate."
anArray copyFrom: startIndex to: stopIndex
"Return a copy of that portion of anArray
that begins at startIndex and ends at stopIndex."
When there is more than one keyword, the method name is formed by
concatenating the keywords. In the second example above, the method
name is copyFrom:to: (formally pronounced “copyFrom colon to colon”).
There is no limit on the number of keywords in a method name.
Pragmas
Pragmas are a special method syntax that simplify specifying some
operations. They are specified with a syntax:
< pragmaKeyword: ... >
You may see these as you browse code.
One set of pragmas you may find useful are for defining menu items.
Refer to the GUI Developer’s Guide for information on these.
Messages in Sequence
Frequently, the receiver of a message is the object returned by the
previous message expression. To avoid creating a temporary variable to
store the returned object, you can create a sequence of messages. For
example, the first and second expressions below can be compressed into
the form of the third expression:
interest := principal * interestRate.
principal := principal + interest.
Cascading Messages
When two or more messages are to be sent to the same object, a
semicolon can be used to cascade the messages. This avoids having to
repeat the name of the receiver, though frequently at the expense of
readability. For example, the first set of expressions below has the same
effect as the final expression, in which the messages are cascaded:
Transcript show: 'This is line one.'.
Transcript cr. "Carriage return."
Transcript show: 'This is line two.'.
Transcript cr.
96 VisualWorks
Message Expressions
Block Expressions
A block expression represents a deferred sequence of operations. Blocks
are used in several contexts, including control structures, exception
handling, and finalization. The syntactic characteristics of block
expressions are described here.
A block expression is enclosed in square brackets, as in:
[index := index + 1.
anArray at: index put: 0]
The messages inside the block are not sent until the block object receives
the unary message value. The following expressions have the same
effect:
index := index + 1.
[index := index + 1] value.
Up to 255 separate arguments can be passed to a block. Argument
names must be listed just inside the opening bracket. Each argument
name must be preceded by a colon. The final argument name must be
followed by a vertical bar. For example:
[:counter | counter := counter + 1]
The argument variables are private to the block. The values of the
arguments are passed by using variants of the value message. There are
four variants, to be used depending on the number of arguments:
value: anObject
value: anObject value: anObject
value: anObject value: anObject value: anObject
valueWithArguments: anArray
Passing an argument to the example above would be arranged thus:
[:counter | counter := counter + 1] value: 3
Temporary variables can also be declared within a block. They must be
enclosed in vertical bars and placed after the vertical bar that separates
argument variables. They are local to the block.
The full syntax for a block is as follows:
[ :arg1 :arg2 |
| temp1 temp2 |
statement1.
statement2.
...]
98 VisualWorks
Formatting Conventions
Formatting Conventions
The compiler ignores tabs, carriage returns, and extra spaces. Formatting
conventions vary but readability favors the following guidelines:
1. Start the message definition at the left margin and indent all other
contents of the method by one level.
2. Leave a blank line beneath the method comment and as a separator
between sections of a long method.
3. Follow each period that ends an expression by a carriage return.
4. Indent as needed to visually identify each subordinate section of
code.
The code browser provided with VisualWorks Smalltalk provides a format
command for automatically applying these rules.
Defining a Class
A class is defined in a name space, as the value of a shared variable in
that name space. The variable is defined as “constant,” so the name of
the class cannot easily be changed.
100 VisualWorks
Defining a Class
The class definition properties are on two pages: Basic and Advanced. A
“Caution” icon (yellow triangle with an exclamation point) is displayed next
to any required field that lacks legal value.
The Basic properties are:
Package
The name of the package in which to create the class. The package
must already exist in the system. To define the class unpackaged,
select (none).
Name Space
The name space in which to create the class. The name space
determines the referential scope of the class name.
Name
The name for the class being created. There is no default. The name
must be new and unique in the specified name space, and must
begin with an uppercase letter.
Superclass
The name of the superclass, in literal binding reference (dotted
name) notation, as shown (see “Binding References” in Chapter 6,
“Name Spaces”).
Instance Variables
A space separated list of instance variable names.
Create Methods
Three check boxes specify which, if any, stub methods are created in
the class authomatically when the class is created. The methods
generally need to be edited to provide the desired behavior.
Accessors, if checked, creates get and set accessor methods for each
instance variable specified.
Initializer, if checked, creates an initializer method with lines setting the
initial values of each instance variable specified.
Subclass responsibilities, if checked and if any of the superclasses define
methods marked as #subclassResponsibility, creates stub methods in
the new class for all of those methods. Initially, the stubs will signal an
error when evaluated, so you need to replace their bodies with
appropriate implementations.
The Advanced properties are:
Private
If checked, makes the class unavailable for import by another class or
namespace (see “Public and Private Shared Variables” in Chapter 3,
“Syntax”).
Indexed Type
This field specifies the class type, and particularly the type of value
that can be held by its indexed variables. See “Class Types” below for
descriptions of the types.
Class Instance Variables
A space separated list of instance variable names (see “Class
Instance Variables” in Chapter 4, “Classes and Instances”).
Imports
A list bindings to import (see “Importing Bindings” in Chapter 6,
“Name Spaces”).
When the dialog values are set, click OK to define the class and any
specified methods.
Note that class variables are not declared in the class definition, but are
created as shared variables in the class name space. Refer to “Class
Variables” in Chapter 3, “Syntax” for more information.
102 VisualWorks
Defining a Class
Class Types
Classes are of different types, determined by the value of the Indexed
Type in the definition. The permissible types are as follow:
#none
A class with zero or more named instance variables (possibly
inherited) and no indexed variables (e.g., True, Point). Can have any
kind of subclass.
#objects
A class of indexable object with zero or more named instance
variables and whose indexed variables hold arbitrary objects (e.g.,
Array, OrderedCollection). Subclasses can be either #objects or #weak),
since subclasses must also be object-indexable.
#bytes
A class of byte indexable object with no named instance variables
and whose indexed variables hold only byte objects (e.g., ByteString).
Indexed variable contents are defined by the at: and at:put: primitive
methods defined in the class defines, providing one and two-byte
character strings, byte and word arrays, etc. A #bytes class cannot
inherit named or indexed instance variables, because the instances
contain only raw binary data. Consequently a #bytes class can only
104 VisualWorks
Locating a Class by Name
fullyQualifiedReference
When sent to a class or name space, returns a fully quallified name
computed compute from a binding reference (see “Binding
References” in Chapter 6).
asQualifiedReference
When sent to a String or Symbol, returns a binding reference.
For example:
| bindingReference |
bindingReference := stringOrSymbol asQualifiedReference.
bindingReference
ifDefinedDo: [:theClass| theClass ...statements... ]
elseDo: [self error: 'no class named ' , stringOrSymbol].
And, instead of:
Smalltalk at: stringOrSymbol
use:
stringOrSymbol asQualifiedReference value
Destroying an Instance
Garbage Collecting
In general, there is no reason to explicitly destroy an instance, because
Smalltalk employs garbage collection. When an object no longer has any
other object pointing to it (e.g., holding it in a variable), the system detects
that it is no longer needed, and automatically destroys the instance,
reclaiming the memory and resources.
106 VisualWorks
Working with Instances
Finalization
In some cases, such as if an object uses external resources, garbage
collection is not sufficient. In these cases, use the VisualWorks
finalization features (refer to Chapter 13, “Weak Reference and
Finalization”).
Lingering Instances
It is also possible to have “memory leaks,” caused by an instance that is
not fully released, and so cannot be garbage collected. To find these, look
for unusual memory usage on a per-class basis. Load the AT System
Analysis Parcel, and open the Class Reporter by selecting Tools
Advanced Class reports in the Launcher. On the Space page, select the
suspect class, click the Instance size radio button, and click Run. Run this
both against your image and a clean image to identify classes with
possible garbage. Then, send allInstances to the class and inspect them.
Use the Inspector's Utils Reference path command to trace back to a root
holding onto the object. Potential roots are:
• Object classPool at: #DependentsFields
• Object classPool at: #EventHandlers
• ObjectMemory dependents
• sysOopRegistry
Immutable objects
Several objects are “immutable,” meaning that their internal state cannot
be changed. Instances of SmallInteger, Character, and Symbol have always
been immutable in Smalltalk.
VisualWorks, beginning with version 7, extends the notion of immutability.
In addition to the objects listed above, all literals and general instances of
Number are now immutable. Also, facilities have been added to make
individual objects immutable. Except for instances of SmallInteger,
Character, and Symbol, objects which are immutable may be made
mutable.
This change provides several advantages:
• additional language safety by making literals immutable
• debugging aid, by catching where an object is assigned-to
• for persistence, where attempts to modify are caught, retried and the
updated objects written to persistent storage
108 VisualWorks
Methods
Methods
Methods define the behavior of classes and their instances. This is where
the real “programming” takes place in Smalltalk. Methods are the same
as what are often called “functions” in other environments, such as Java
and C++.
You create methods using the System Browser, and completing the
method definition template. You can also use an existing method as your
template.
There are two kinds of methods: instance methods and class methods.
Instance methods specify behavior for messages sent to instances, and
class methods specify behavior for messages sent to the class itself.
Class methods are most often used for creating an instance of the class
and for initializing and accessing class variables.
To promote reusability, keep Smalltalk methods short. For example, you
can usually break a long method into smaller methods to isolate
individual services that other clients may want to use. Similarly, when a
subset of the code is repeated in a large method with only minor
variations, you can usually make that subset into a separate method.
Method names may contain letters, numbers, and underscores, but may
not begin with a number. The first letter should be lowercase.
Creating a Method
1 In a System Browser, select either the instance or class radio button.
2 Select the class for this method.
3 Select the message category or add a new one.
4 Fill in the method template.
You must provide a method name, which is the message selector and
argument names, in the first line of the definition. Next, you should
include a comment briefly describing what the method returns. Then,
enter a sequence of Smalltalk expressions (see Message
Expressions) specifying the processing behavior of the method.
5 Select Accept command in the code view <Operate> menu to save the
method. The method is then compiled.
110 VisualWorks
Methods
| id |
id := self accountID.
^id isEmpty
ifTrue: [String new]
ifFalse: [id
copyFrom: 1
to: 4].
Branching
The Boolean classes True and False implement methods for performing
conditional selection (if statements).
Boolean Values
Many classes implement methods that test an object for a condition or
compare an object with another, and return a Boolean value—either true or
false.
The most basic tests, implemented in Object, are equality (=) and identity
(==), return true if two objects are equal or identical, respectively, and
return false otherwise.
9=9 “returns true”
9 == 9 “returns true”
9 = 'nine' “returns false”
9 == (5 + 4) “returns true, the same SmallInteger”
'this is a test' = 'this is a test' “returns true”
‘this is a test’ == ‘this is a test “returns false; equal but different”
Array new = Array new “returns true”
Array new == Array new “returns false”
112 VisualWorks
Branching
Similarly, numbers, strings, and a few other objects return a Boolean to >,
<, >=, and <= messages according to how the objects compare in size or
order.
There are also methods defined throughout the system, often named in
the form “isSomething”, where “Something” is the name of a kind of
object or a property, for testing whether an object is that kind, and
returning a Boolean response. For example, isString returns true if the
receiver is a String object, isNil returns true if the receiver has the value
nil, and isReadOnly returns true if the receiver has its “read only” property
set, and otherwise they return false.
anObject isString.
Using testing messages like these are useful in defining specific handling
of objects, based on condition of passing (true) or failing (false) the test, as
described in the next section.
Conditional Tests
Given an expression that evaluates to a Boolean, you can branch the
processing based on that value. The conditional test messages are
ifTrue: aBlock
Evaluates aBlock if the receiver is true.
ifFalse: aBlock
Evaluates aBlock if the receiver is false.
ifTrue: aBlock ifFalse: anotherBlock
Evaluates aBlock if the receiver is true, or anotherBlock if the receiver is
false.
ifFalse: aBlock ifTrue: anotherBlock
Evaluates aBlock if the receiver is false, or anotherBlock if the receiver is
true.
All of these messages must be sent to a Boolean, so of the last two, one of
the blocks in guaranteed to be evaluated.
ifTrue:ifFalse: is the Smalltalk version of common if-then-else construct. In
the following example, a prompt string is selected depending on whether
the application user is a managerial employee:
(userType == #Manager)
ifTrue: [prompt := 'Enter your password']
ifFalse: [prompt := 'Access denied—sorry']
The blocks can be left empty when no action is required. This is so often
the case that ifTrue: and ifFalse: are provided as separate methods. In the
example above, if no password were required, the ifTrue: portion of the
expression could be dropped entirely.
Unless the block does a return (^), which exits the block and its
containing method, processing continues with the next expression.
Note that Smalltalk has no equivalent of the case statement provided in
many languages, because case statements tend not to be object-
oriented.
Compound Conditions
Compound conditions are formed by “and,” “or,” and “not” operations,
producing a Boolean value from one or more other Boolean values. The
following messages are available for performing these operations
and: aBlock
Returns true if the receiver is true and aBlock evaluates to true;
otherwise returns false. aBlock is evaluated only if the receiver is true.
& aBoolean
Returns true if the receiver and aBoolean are both true; otherwise
returns false.
or: aBlock
Returns true if either the receiver is true or aBlock evaluates to true, or
both; otherwise returns false. aBlock is evaluated only if the receiver is
false.
| aBoolean
Returns true if either the receiver or aBoolean is true, or both; otherwise
returns false.
not
Returns false if the receiver is true, or true if the receiver is false.
As suggested in the descriptions above, the alternate forms for the “and”
and “or” operations provide for different processing control. The & and |
binary messages always evaluate both the receiver and aBoolean
expressions when evaluating the value of the compound statement. The
and: and or: keyword messages, on the other hand, only evaluate aBlock if
the value of the compound cannot be determined from the receiver alone.
If the receiver of and: is false, then the value of the compound must be
false regardless of the value of aBlock. Similarly, if the value of the receiver
of or: is true, the value of the compound must be true regardless of the
value of aBlock.
114 VisualWorks
Looping
For example, in this example using the | binary message, both conditions
are evaluated, and an unhandled exception (subscript out of range)
occurs:
| aCollection |
aCollection := #( 'one' 'two' 'three' ).
aCollection notNil | ((aCollection at: 5) = 'five')
ifTrue: [Transcript cr; show: 'true'].
However, since the first condition is true, the complex condition should
evaluate to true. Using the and: keyword message instead defers
evaluating the block until it is needed, which it is not in this case, and the
message goes through as intended.
| aCollection |
aCollection := #( 'one' 'two' 'three' ).
(aCollection notNil or: [(aCollection at: 5) = 'five'])
ifTrue: [Transcript cr; show: 'true'].
This difference can be valuable in writing efficient methods.
Looping
Three types of iterative operation are available: conditional, number, and
collection looping. This section discusses the three types of looping.
Conditional Looping
Conditional looping involves a conditional test that determines whether to
repeat the loop.
whileTrue: and whileFalse:
In the previous example, the expression (userType == #Manager) is
evaluated just once. By contrast, the condition that drives a while loop
has to be evaluated multiple times. In Smalltalk, it is enclosed in the
square brackets that identify it as a block (an instance of class
BlockClosure). The whileTrue: message causes that block to receive a value
message, which triggers execution of the block’s contents. If the
expressions in the receiver block return a true, the argument block is
executed. Then value is again sent to the receiver block to see if it is still
true, repeating the cycle.
The following example might be used in a game that ends when there is
only one player (the winner) left in the game:
[players > 1] whileTrue:
[nextPlayer takeTurn.
(nextPlayer outOfGame) ifTrue: [players := players - 1] ]
To reverse the logic of the test, use whileFalse:. For example, to process a
stream of objects until the endpoint is encountered:
[self atEnd] whileFalse: [aBlock value: (self next) ]
For situations in which no argument block is needed, the unary messages
whileTrue and whileFalse are available.
repeat
When a block of statements contains its own (reliable!) test for returning
from the loop, the simple message repeat can be sent to the block.
Number Iteration
Number looping corresponds to the traditional for loop, and is
implemented via messages to numbers.
timesRepeat:
To repeat a block of expressions a specific number of times, send a
timesRepeat: message to a number and provide the repeatable block as an
argument. For example, to send the string ‘Testing!’ to the Transcript
anInteger times:
anInteger timesRepeat: [Transcript show: 'Testing!']
to:by:do:
A more elaborate sort of for loop comes in the form of the to:by:do:
method, which lets you specify a starting integer, a stopping integer, the
step increment, and the block to be repeated. For example, to print
something like a word processor’s tab-setting ruler on the Transcript:
10 to: 65 by: 5 do: [ :marker |
Transcript show: marker printString.
Transcript show: '---'].
Here’s a translation: Count by fives from 10 to 65. Pass each such value
to the block, which converts it to a string and outputs it to the Transcript,
followed by three hyphens. The output looks like this:
10---15---20---25---30---35---40---45---50---55---60---65---
Notice that, unlike timesRepeat:, the to:by:do: method automatically passes
the value of the counter to the block (picked up by the argument named
marker in this case). The block must declare an argument variable to
catch the passed value.
116 VisualWorks
Looping
to:do:
When the counting increment is 1, you can use the simpler to:do:. The
following example prints the ASCII equivalents of the numbers 65 through
122 in the Transcript.
65 to: 122 do: [ :asciiNbr |
Transcript show: asciiNbr asCharacter printString]
Collection Iteration
Collection looping supports scanning, counting, and other operations
involving one repetition for each member of a collection. It is frequently
useful to repeat a series of operations for each element in a collection of
objects (collections are discussed further in Chapter 17, “Collections”).
The integer iteration discussed above is a special case, dealing
exclusively with numeric intervals—i.e., collections of integers. The
iteration methods discussed in this section apply to other kinds of
collections as well. All are implemented by the Collection class, which is
the superclass of dictionaries, arrays, sets, strings, etc.
do:
The simplest method, do:, evaluates the block for each member of the
collection. For example, to capture the contents of an array during
program execution, we might want to convert each member to a printable
string and output it to the Transcript:
anArray do: [ :anElement |
Transcript show: (anElement printString); cr ]
select:
To filter a collection and wind up with a desired subset, use select:. Each
member of the collection that satisfies the conditions in the block is stored
in a new collection of the same type, which is returned by the method.
The following example counts the number of question marks in a string by
gathering the question marks into a new collection and then finding the
size of that collection:
(aString select: [ :eachChar | eachChar == $? ] ) size
reject:
The reject: method is the opposite of select:. It gathers the members of the
original collection that fail the test rather than those that pass it.
Substituted for select: in the example above, it would create a collection of
non-question-marks, which would then be sized.
detect:
The detect: method, like select:, tests each element of the collection. But
instead of returning a subcollection of those elements that pass the test,
it returns the first such instance (and stops testing at that point). The
following example locates the first instance of the integer 8 in anArray:
anArray detect: [ :each | each == 8 ]
collect:
The collect: method performs a transformation on each element of the
collection and returns a new collection containing the transformed
objects. For example, to get an uppercase version of aString:
aString collect: [ :each | each asUppercase ]
inject:into:
The inject:into: method enables you to pass an explicit argument to the
block in addition to the collection’s elements. This explicit argument (the
inject: part of inject:into:) is used to initialize a counter for a cumulative
operation such as summing. For example, to add the numbers in aSet:
aSet inject: 0 into: [ :subtotal :nextNbr | subtotal + nextNbr]
The following table summarizes the branching and looping methods
discussed in this chapter.
Control Structure Methods
Method name Description
ifTrue: If the prior expression is true, execute the
argument block.
ifFalse: If the prior expression returns false, execute the
argument block.
ifTrue:ifFalse: If the prior expression is true, execute the first
block; otherwise do the second block.
ifFalse:ifTrue: Checks for a false condition first.
whileTrue: Repeat the argument block until the receiver
block is no longer true.
whileFalse: Repeat the argument block until the receiver
block is no longer false.
whileTrue Repeat the receiver block until it no longer
returns true.
whileFalse Repeat the receiver block until it no longer
returns false.
118 VisualWorks
Looping
120 VisualWorks
Multiple Name Spaces
Getting Started
You can gain experience with name spaces in stages, increasing the
extent of use as you become more comfortable with them. It is possible,
for instance, to define all of your classes in the Smalltalk name space,
and proceed largely as if multiple name spaces don’t exist. For
instructions on how to do this, see to “Taming Name Spaces,” in the
doc/TechNotes directory and from the Cincom Smalltalk
Documentation web page: [Link]
122 VisualWorks
Name Spaces and Their Contents
124 VisualWorks
Working with Name Spaces
In the leftmost pane, with the Packages tab selected, the navigator shows
the bundles and packages in the system. The second pane lists classes
and name spaces, with the name spaces distinguished by a special icon.
For example, in the screen above, the Core name space has been
selected in the class/name space view, and its definition appears in the
code tool (below).
126 VisualWorks
Working with Name Spaces
Private
Check if this name space is to be private, i.e., not available for import.
Imports
A list of imports, either specific or general, separated by whitespace,
and including “private” if appropropriate. For example, enter:
private Smalltalk.*
XML.*
See “Importing Bindings” for more information.
Then, click OK to define the name space.
Alternatively, select a package only to display a definition template, or a
name space to display a name space definition. Edit the definition giving
at least a new name space name, and Accept the changes, to create a
new name space.
128 VisualWorks
Referencing Objects in Name Spaces
using its full dotted name (except when specifying imports), in order to
understand the other referencing methods you need to know about dotted
names.
The full path a dotted name begins with the Root name space, continuing
through the hierarchy to the target binding. For example, the full reference
to the ButtonHilite constant (in its native name space) is:
[Link]
However, the VisualWorks system, when parsing a compound dotted-
name, assumes the [Link] initial segment. So, in practice, the
above reference is shortened to:
[Link]
This is the form of reference used in import statements, providing the
path starting immediately after Smalltalk.
If a binding is imported, the dotted name can specify the importing name
space path, instead of its native name space path. So, for example, if
[Link] imports ButtonHilite, the dotted name
[Link] would also be a legitimate dotted name, and
would reach the variable; it is not necessary to reference ButtonHilite
through its native name space, SymbolicPaintConstants.
Because a dotted name introduces a path starting immediately after
Smalltalk, dotted names do not follow the relative path rules familiar from
file systems. You can, however, reference a binding relative to the current
name space context by beginning the path expression with “_.”
(underscore, dot). Using this notation, if a name space (MyNamespace1)
imports another name space (MyNamespace2), and MyNamespace2 has a
class (Foo) with a class variable (Bar), an instance of any object defined in
MyNamespace1 can reference Bar with:
_.[Link]
Using dotted names in code to reference variables that are neither
defined in nor imported into the current name space, is permitted but
discouraged, because this use breaks encapsulation. There are,
however, occasions when they are needed. In source code, it is
sometimes necessary to refer to a variable that is not visible from the
current name space. For example, if a developer is adding a method to a
class that he does not own, and he may not have the freedom to add a
new import to the class's environment. In future releases we intend to
provide a better mechanism for extending classes, allowing extensions to
use variables not normally visible to the class, but they are not currently
available.
130 VisualWorks
Referencing Objects in Name Spaces
Binding References
In an environment with name spaces, we need a way to reference a
shared variable that makes no assumptions about which name space
contains its definition. A binding reference provides this facility.
A binding reference is a named object that holds a starting point and a list
of names. It can identify an arbitrary shared variable relative to an
arbitrary name space, by identifying a navigation path from the name
space to the shared variable.
Most of the protocol for binding references is defined in the class
GenericBindingReference, with more specific protocol defined in
BindingReference and LiteralBindingReference. The common protocol
includes useful questions such as:
isDefined
Does the variable exist in the system?
binding
Answer the VariableBinding for the shared variable, or raise an error if it
doesn't exist.
bindingOrNil
Answer the VariableBinding for the shared variable, or nil if it doesn't
exist.
value
Answer the value of the shared variable, or raise an error if it doesn't
exist.
valueOrDo: aBlock
Answer the value of the shared variable, or the value of aBlock if it
doesn't exist.
A binding reference, when asked for its binding, iterates through its list of
names. For each name, it asks the current name space for the variable of
that name. If the name is the last in the list, it answers the shared
variable. If the name is not last, it uses the value of the variable as the
new current name space, and repeats the process with the next name in
the list.
132 VisualWorks
Referencing Objects in Name Spaces
Importing Bindings
While it would be possible to require that you reference each object by
explicitly describing the name space path from Root to the target object,
that would be inconvenient, and would violate the object-orientation
134 VisualWorks
Referencing Objects in Name Spaces
136 VisualWorks
Referencing Objects in Name Spaces
138 VisualWorks
7
Managing Smalltalk Code
140 VisualWorks
File-Out Format Files
Filing In Code
Filing in source code from a file-out file is most commonly done using the
File Browser tool (File File Browser, or the corresponding icon in the
Launcher window). Enter the name of the file, or select it in the list pane
after displaying its directory. With the file selected, select File In... from the
<Operate> menu.
Note that if you file in a file from a pre-5i version of VisualWorks, the code
is loaded into the Smalltalk.* name space.
You can also use the Change List tool to view a file-out file method by
method, and replay selected definitions. See “Using the Change List”
below.
142 VisualWorks
Packages and Bundles
is loaded, and may require specific versions of those. Pre-load and post-
load actions may execute custom initialization and clean-up code to
manipulate the system as necessary, and restore it.
A bundle is a collection of packages and other bundles, providing a way
to assemble a body of code from smaller units. Each bundle is defined by
a specification that lists the names and versions of each package or
bundle that it contains, in a specific load order.
During development, a bundle can be used to load a set of packages, as
a convenience mechanism. For deployment, a bundle can be the unit that
defines a parcel. These uses are discussed in more detail later. A
package may belong to more than one bundle.
Like packages, bundles may have prerequisites, and initialization and
clean-up actions, which are used in addition to actions defined for their
contained packages and bundles. When deploying a bundle as a parcel,
however, only prerequisites and actions defined for the bundle become
prerequisites and actions for the parcel.
Creating packages
To create a new package choose Package New... in a system browser,
and specify a name for the new package.
The new package is added to the Packages list in the browser. The new
package is represented in the image, and so is saved with the image. It is
also recorded in the Change List.
Definition to Package...
Prompt for a target package, and move the currently selected class
into it.
Selection to Package...
Prompt for a target package, and move the currently selected class
into it. Only the parts of the class that are within the current package
are moved. If multiple packages are selected, all parts of the class
within all selected packages are moved to the package. If the
navigator is set to view categories, the complete class (methods and
shared variables) are moved.
All to Package...
Prompt for a target package, and move the currently selected class
into it. The complete definition, including shared variables, is moved.
For protocols, methods, and shared variables, select Move to Package...,
and select the package. All definitions in the selection are moved to the
target package.
You can reassign items to a different package using the same menu
commands.
Specifying prerequisites
Prerequisites are parcels, packages, or bundles that must be in the
system before the code unit is loaded. Before loading, a package or
bundle verifies that it’s prerequisites are loaded and, if not, loads them.
Packages and bundle prerequisites can be specified for either
development or deployment.
Deployment prerequisites are parcels, and are turned into parcel
prerequisites when the package or bundle is published as a parcel.
Development prerequisites may be either parcels, packages or bundles,
and are used when loading code from the Store repository. Often,
development prerequisites are a superset of package/bundle correlate of
the deployment prerequisites, including additional items such as
development tools and tests.
To specify prerequisites:
1 Select the bundle in the Refactory Browser package list and select
the Properties tab.
2 Select either the Development Prerequisites or Deployment Prerequisites
property.
144 VisualWorks
Packages and Bundles
Suppress warnings
A package’s (or bundle’s, or parcel’s) warning suppression action is a
one-argument block, where the argument is the name of a prerequisite.
The block suppresses the absent class warnings, that is, the a warning
about an attempt to add code to a non-existent class. It does so on a per
prerequisite basis, so you can suppress warnings for selected
prerequisites.
The block must return true for any prerequisite for which warnings should
be suppressed. For example, to suppress only warnings for MyPrereq, you
could enter:
[ :prerequisiteName |
prerequisiteName = ‘MyPrereq’ ifTrue: [ true ] ]
To suppress warnings for additional prerequisites, simply add them to the
test.
The warning suppression block is run before any of the package code is
loaded. Consequently it should not mention any code in the package.
146 VisualWorks
Packages and Bundles
Specify prerequisites
Prerequisites are parcels or packages that must be loaded before the
bundle is loaded. Prerequisites can be specified for development and for
deployment. Often, development prerequisites are a superset of
deployment prerequisites, including additional items such as
development tools and tests.
Deployment prerequisites are converted to parcel prerequisites if a parcel
is published from the bundle. Otherwise they are ignored.
To specify prerequisites:
1 Select the bundle in the Refactory Browser package list and select
the Properties tab.
2 Select either the Development Prerequisites or Deployment Prerequisites
property.
3 (Optional) Click Compute Prereqs to have the system make an initial
guess at the necessary prerequisites.
4 Select a parcel or package (packages are only available for
development prerequisites) and click >> to move it to the Prerequisites
list.
The item must be loaded in the system to use this method, but it
usually is if you have been doing development. To add a prerequisite
that is not currently loaded, enter its name in the entry field below the
parcels/packages list and click Add. You may also specify a version
number for the parcel/package.
5 To remove items from the prerequisite list, select the item and clicking
<<.
6 When done adjusting prerequisites, click Apply.
Suppress warnings
A package’s warning suppression action must be a block taking one
argument, the name of a prerequisite parcel being loaded. The block
should answer true if any warnings caused by loading the prerequisite
should be suppressed.
The warning suppression block is run before any of the parcel's code is
loaded. Consequently it cannot and should not mention any code in the
parcel.
148 VisualWorks
Parcels
Parcels
Parcels are the component deployment technology for VisualWorks,
providing a fast object loading mechanism especially suited to deploying
Smalltalk code. All standard VisualWorks add-in components are
provided as parcels. The System Browser provides the facilities for
browsing, creating, modifying, maintaining, and loading parcels.
Parcels provide the following features:
Source code storage
Parcels are saved in two files. Parcel files containing compiled code
in a binary format have a .pcl extension, and files containing the
corresponding source code have a .pst extension. (Note: Despite
the superficial resemblance between .pst source files and .st file-
out format files, .pst files do not “file-in” properly. They are strictly
source files for the .pcl binaries. They can, however, be browsed in
the Change List for comparison with a loaded parcel by viewing
differences between the system and an opened file.)
Partial loading
Partial loading is a powerful new technique for structuring code as
logical components rather than physical components which other
Smalltalk loading technologies impose. See “Partial Loading” on
page 161 for more information.
150 VisualWorks
Parcels
Normally, click yes to all, to continue loading the parcel. This is normal,
and has to do with the partial loading facility of parcels (see “Partial
Loading” below for more information).
Briefly, by clicking yes or yes to all, you permit code that cannot
currently be loaded to be placed in the Undeclared name space until
the prerequisites for its loading are satisfied. Usually these are
satisfied by later code in the same parcel or in another parcel you are
loading. When the prerequisites are satisfied, the code is loaded
automatically.
The parcel then loads.
As a short cut for loading parcels that are not in a defined parcel path,
you may select File Load Parcel File... . A standard file dialog prompts for
the location of the parcel file.
To unload a parcel, highlight its name in the parcel list and select Unload
from the <Operate> menu.
Using the System Browser
When viewing parcels in the System Browser, you may use Load... on the
navigator’s <Operate> menu. Note that this menu item appears only
when the browser is viewing parcels.
If no parcels are selected in the browser, you are prompted for the name
of a parcel to load.
Follow the remaining steps as described in the preceding section.
To unload a parcel, select the parcel in the browser, and select Unload
from its <Operate> menu.
152 VisualWorks
Parcels
To add a path to the list, enter it in the space provided and click Add. The
$(VISUALWORKS) prefix matches the VisualWorks home directory. You
can also specify a full directory path.
To change the search order, select an entry and drag it up or down in the
list. Directories are searched from top to bottom.
To delete a directory from the search path, select it and click Delete.
To edit an entry, select it, edit it in the entry field, and click Change.
When you are finished making changes to the parcel path list, click Accept.
Note: The parcel path is saved with the image. To make the path
permanent, you must save your image.
Managing Parcels
Loading and unloading parcels is generally done with the Parcel Manager
tool (for details on the Parcel Manager, see “Parcel Manager” on
page 44). Virtually all management of code inside parcels is performed
using the System Browser.
To view the loaded parcels within the Browser, select Browser Parcel.
The browser’s menus change to activate parcel maintenance activities.
The browser’s navigator can be used to show only the definitions that are
in the current parcel. Use the <Operate> menu in the parcels list.
Parcel Condition Indicators
Text formats are used in the Browser to indicate various states of code
with respect to parcels.
154 VisualWorks
Parcels
In the parcel list, parcels have characters following their name indicating
the parcel’s state:
Name spaces are added separately from other definitions. If you parcel
code that is defined in your own name space, make sure the name space
is parcelled as well. Select the name space in the class/name space list.
Then choose Move to Parcel... from the class/name space list
<Operate> menu. Adding a name space to a parcel adds only the name
space definition, not the shared variables defined in it.
If a definition is already contained in one parcel, but you want to move it
to another, select the definition and then select Move to Parcel... in its
<Operate> menu. Then select the destination parcel from the list of
currently loaded parcels.
To remove a definition from a parcel, select it and choose
Move to Parcel... . Then, move the definition to Unparceled.
Saving a Parcel
When you have made changes to a parcel, it is considered dirty until you
save it. Saving a parcel writes all definitions and objects in the parcel to
the external files described earlier and clears the “dirty” indicator.
To save a parcel, select Save from the parcel list’s <Operate> menu. The
Parcel Save dialog is displayed:
156 VisualWorks
Parcels
In the entry field, enter the name for your parcel. The parcel file is created
if necessary, and given the name you specified with a .pcl extension
appended. Long file names are supported, including spaces and periods
in the name. Do not specify the extension, because VisualWorks will
append another.
If the save source file box is checked, sources are saved in a [Link] file,
where name is the name you specified for the parcel.
The hide source on load option has no effect in the current environment, but
will cause source code to be hidden when this parcel is loaded into an
image. This is a deployment option, and should not be used during
development.
The pad source option is needed only for huge parcel files, for efficiency of
the storage mechanism. Since huge parcels are generally a bad idea,
leave this unchecked unless you know you need it. (See the
SourceFileManager class comment for details.)
If you save with sources to a parcel that is currently loaded, you need to
check republish, or save to a different file name. When a parcel is loaded
with sources, the sources are added to the system sources. Writing to
this file would corrupt the sources file. Republishing effectively creates a
new (temporary) parcel and sources file, unloads the old parcel, replaces
the old parcel with the new one, and reloads the parcel. This keeps the
source file and the image consistent. Republishing does not hide
sources, even if you have hide source on load selected.
On republish the system also checks to see if there are any methods that
have their source in the .pst file but have been moved out of the parcel.
It then rescues this source by writing it to the changes file. Source rescue
also happens on unload and empty. This helps prevent you from losing
any source code.
After saving the parcel, you will be prompted to save the image to ensure
that the source file loaded into the current image is the same as the file
on disk. This is not necessary if you load the parcel each time you work
with it, rather than saving the image with it attached.
158 VisualWorks
Parcels
Since most of the methods in the system are not defined in parcels (such
as base system methods), a list of all unparcelled methods would not be
helpful. These lists filter the collection of unparcelled methods to just
those probably of interest to your parcels.
It is possible that your parcel is the only user of some unparcelled method
that is defined in the base system or saved in the image, so don’t
automatically add any reported method to your parcel. The utilities
display any candidate methods in a method browser. Examine the
methods carefully before deciding to add them to your parcels.
Restrictions on Parcel Contents
A class’s instance and class side definitions must be contained in the
same parcel; they cannot be broken apart. Loading a class definition in a
parcel does not overwrite a class definition already in the image.
(To change a class definition, use a pre-load action.)
Named objects have the following restrictions:
• Named objects cannot be instances of the following classes:
160 VisualWorks
Parcels
Partial Loading
Parcels, like other Smalltalk code delivery mechanisms, add classes and
methods to the system. Existing mechanisms, such as file-in, require that
the superclasses of any installed classes, and the classes of any installed
methods are present in the system. This frequently forces one to
decompose a logical component that spans a number of other
components into sub-components.
Parcels eliminate this problem by allowing partially installing definitions
from a parcel. If a parcel contains a class that requires a superclass
which is not present in the system, or a method that requires a class
which is not present in the system, the class or method is not installed.
Instead, these classes and methods are added to either the
uninstalledClasses or uninstallededMethods set for the parcel.
162 VisualWorks
Parcels
164 VisualWorks
Parcels
Definition Overrides
A parcel imports classes to add methods to them. These methods we call
extension methods. Writing the class definitions to the file allows one to
check for shape changing compatibilities at the source level.
A parcel remembers any methods it overrides on load, and replaces them
on unload. This allows you to override arbitrary methods without the risk
of crashing the system when unloading the parcel that overrode some
vital method.
Parcels with overriding method extensions appear in the parcel list with a
+ after their name.
Parcels with overridden method definitions appear in the parcel list with a
- (minus) after their name.
If you try to save a parcel that some other parcel has overridden, the
overriding method would be saved, rather than the original overridden
definition. If you attempt to save a parcel with overridden definitions, a
notifier warns you of the situation, and the operation is cancelled when
you close the notifier. (Refer to “Restoring an Overridden Definition” and
“Removing an Overridden Definition” for clearing the override conditions.)
Note that packages behave differently, and can now save the overridden
definition.
To browse a parcel’s overridden extensions, select Browse Overridden
extensions in the parcel list <Operate> menu. This opens both a Change
List in conflicts mode, displaying the previous versions, and a Method List
Browser displaying the current versions. Overridden extensions are also
listed in a Parcel’s summary.
166 VisualWorks
Parcels
parcel itself or the association between the parcel and its classes and
methods. Parcel contents that have been filed out can be filed in using
the standard file-in mechanisms.
To file in a set of definitions and associate them with a parcel, first create
a new, empty parcel and select it in the browser’s parcel list. Then choose
Build Add File-In from the <Operate> menu and specify the .st file
containing the definitions to add. All of the definitions within it will be
added to the parcel and loaded into the system.
168 VisualWorks
Working with Change Sets
Unlike the previous project mechanism, you do not get a new visual
environment, but continue working with the same browsers and
workspaces. You do not have to enter or exit projects to switch change
sets.
Exploring Changes
Having made changes which you have assigned to a change set, or
project, it’s convenient to be able to review them. Three menu options in
the Change Set Browser ChangeSet (or <Operate>) menu to allow you to
review your change set’s contents.
Unlike the change list, change sets do not track, and these tools do not
show, the evolution of those changes. Instead, the record and report the
affected parts of the system, so you can use the file out as command to
store your work in a set of disk files.
Also, in the spirit of summarization, the change sets do not separately
report changes to methods in a class that has been added in this change
set. When you file out the new class, its methods will be included anyway.
However, if you empty the change set or “forget” the class addition,
successive method changes are recorded.
Browse
This menu pick opens a message browser on messages changed and
recorded in the change set.
Edit
This menu pick opens an editor browser on the current change set. You
can change the selected change set in the Change Set List, and the
editor will update to show the changes for that change set.
170 VisualWorks
Working with Change Sets
The top left pane lists classes that have changed in this change set. The
check boxes at the bottom of the window indicate the kind of change
recorded.
The top center pane lists methods that have changed for the selected
class, if any are recorded. Methods for classes whose definitions are in
the change set are not included, since a file-out will include them anyway.
The radio buttons at the bottom indicate the kind of change recorded.
You can edit the definitions either in this editor, or select browse or spawn in
the <Operate> menus to open a browser on the item. To remove just this
one item from the change set, select forget in its <Operate> menu.
Inspect
This menu option opens an inspector on the change set. Here you can
perform the usual inspector options.
Updating the Changes Display
To update an open Change Set browser after making a change to the
system, select update in the <Operate> menu for a browser pane.
Saving Changes
Change sets are typically used to identify sets of changes that can then
be distributed as file-out format files. Change sets are saved in source
code format, and so can be browsed in the Changes List.
To write out all the changes in a change set, select the change set and
select ChangeSets File out as... . You will be prompted for a file name.
As a shortcut, to file out all save sets, select ChangeSets File out all... .
You will be prompted for a directory name. The directory will be created, if
necessary, and a separate file-out file for each change set is written to it.
You can file out a single method by selecting it in the Change Set Editor
(ChangeSet Edit), then selecting File out as... in the <Operate> menu.
Note that, when filing out a change set that includes defining a class, all
subsequent changes made to methods in that class are also (implicitly)
assigned to the change set. This is true even if a different change set is
“current” when those method changes are made. A file-out the first
change set will include the method definitions.
172 VisualWorks
Using the Change List
To work with the changes file, VisualWorks has a Change List Tool that
allows you to build and manipulate a change list, which is based on the
contents of the changes file, change sets, and other file-in format files
containing descriptions of changes.
The Change List window has three views. The view at top left displays a
list of the changes. Entries in the Change List generally identify the
affected object and the nature of the change, such as NotifierController menu
(add). When you select an entry, the affected class or method displays in
the text view as it existed after the change.
The top right-hand view provides on/off switches for filtering the contents
of the change list. Any combination of filter switches can be selected. The
switches filter the list based on the currently selected list item. The filters
have no effect if no item is selected, and so cannot be selected.
For example, to display only changes that affect the same class as the
one affected by the currently selected change list entry, click on the class
switch. To further restrict the listing to identical entries, such as
NotifierController menu, click on the same switch.
Several operations using the Change List are described in this section.
For descriptions of menu items not covered here, refer to the online
VisualWorks Tools help.
174 VisualWorks
Using the Change List
The differences between the two texts are high-lighted. This gives you a
quick, graphic indication or what would be changed by filing-in a specific
change.
Turning off the show conflicts filter hides the conflict text view and returns
the change list view to its usual appearance.
Managing Conflicts
Several items on the Conflicts menu allow adding or otherwise processing
conflicts between the changes list and the system. The full set of menu
items are briefly described in the VisualWorks Tools Help. Here we
comment on a few of the more interesting options.
Add system conflicts
For each displayed change that has a version in the system with
which it conflicts, this option adds the corresponding system version
of the change to the change list.
176 VisualWorks
Using the Change List
Add originals
This option scans the system's source files (excluding the current
changes file) and, for each displayed change for which a
corresponding change exists in the sources file, add the sources file
version to the change list. This is useful comparing your changes
against the original sources.
Add to change set/Remove from change set
These options update the current change set to include or exclude
the changes in the change list, without filing in the changes. This is
useful when you have an old file-in representing a component that
you wish to extract from the system. This can be used together with
the System Browser's Parcel Build Add Changes and
Parcel Build Remove Changes options.
178 VisualWorks
Using the Change List
9 Select Replay All from the top, to restore all of your changes.
10 Save the resulting image.
At this point you have recovered your changes into the new image. Test it,
and if you are satisfied that it is stable, you may save it to the original
image name. Note that the old changes file will then be overwritten, so
you will not be able to repeat the process using it.
It frequently takes a few tries to get exactly what you want into the
restored image, so repeat the procedure until you have just what you
want.
180 VisualWorks
Managing Overrides
For example, to remove all doIts, begin by selecting any doIt. Then turn
on the type switch so all of the doIts are listed. Select Remove all in the
<Operate> menu to mark them for deletion, then Forget to erase them.
Then turn off the type switch to see the remaining entries.
When the displayed list of changes is the desired set, select Write file in
the <Operate> menu and supply the name of a file in which to store the
code. That file can then be loaded into another image via the File in
command in a File Editor or File List.
Only the displayed changes are included in a Write file operation, so if it is
possible to define the minimum set of changes by using the filter switches
alone, it is not necessary to Remove and Forget the nondisplayed entries.
Managing Overrides
Code overrides occur when code loaded into the image from a parcel or a
package defines one or more items already defined in the image. In this
case, the newly loaded code overrides the existing code, and becomes
the code currently in the system.
The Override List tool provides a view on overrides very much like the
view the Change List provides on changes. Most of the operations
supported by the Override List are equivalent, and so will not be repeated
here (refer to “Using the Change List” above). There are a couple of
differences, though, in command behavior that we will cover.
overrides currently in the system. Open Override List opens an empty list to
which you can selectively add parcels and/or packages containing
overridden definitions.
To compare the overridden and overriding definitions, select the parcel or
package to check in a browser and select:
• Parcel (or Package) Browse Overridden extensions, to browse
extension methods definitions that have been overridden, or
• Parcel (or Package) Browse Methods overridden by others, to browse
any methods defined in the parcel/package that have been
overridden by another parcel or package.
These options open comparison browser versions of the Override List,
putting the overridden and overriding definitions side-by-side for easy
comparison.
182 VisualWorks
Managing Overrides
Selecting Overrides
You can select which overrides are displayed, by selecting the relevant
parcels and packages. To list overrides related to a specific parcel or
package, select File Display Parcel... or File Display Package... in the
Override List. Then select the parcel or package to display in the
displayed list and click OK.
To list all overrides in the system, select File Display System Overrides. All
definitions overridden by a parcel of package are then included in the list.
The check boxes at the right provide filters on the list, to help focus on
specific sets of conflicts. With all boxes unchecked, all conflicts are
shown. When any boxes are checked, only the items checked are shown.
To show conflicts, select Show Show Conflicts. The different versions of
the selected item are then shown in separate panes, with conflicting code
shown in red.
184 VisualWorks
8
Application Framework
The user interface (UI) is the part of the application that presents data
and application status to the user, and accepts input from the user by
mouse and keyboard actions. The UI display is generally graphical (so
called a GUI), consisting of one or more window containing widgets,
graphical controls such as buttons, input fields and lists.
Separating the domain model from the UI makes the application easier to
maintain, and also promotes reusability of the application components. If
the domain model provides generic services rather than services that rely
on special knowledge about a particular UI, it is easier to substitute a
different interface later as UI technology and user needs evolve.
Separation also makes it easier to provide multiple UIs for a single
domain model, perhaps one for a novice user and another for an expert
user.
186 VisualWorks
Separating the Domain and the User Interface
User interface
Application model
Domain model
User interface
widgets
Application model
value
models
Domain model
attributes
User interface
Builder
Application model
188 VisualWorks
Dependencies Between Objects
Note that the sketching widget is not a dependent of the list widget.
Rather, it is a dependent of the value model that holds the list of
sketches. The list widget is the primary dependent of the value model,
and receives notifications much as its sibling widget does.
VisualWorks provides three layers of support for dependent notification:
• Notifications from a value model to an application model. Many
applications rely on this partially automated layer exclusively because
it is the easiest to implement and handles the common cases.
• Notifications from any object to any object. This is the foundation
layer upon which the first layer is built, and which provides broader
functionality for situations involving arbitrary types of objects.
• Event-based notifications for objects of any type. This is actually an
alternative to the second-layer architecture, provided for compatibility
with VisualWorks Smalltalk.
190 VisualWorks
Dependencies Between Objects
Dependency transformer
Application Model
192 VisualWorks
Dependencies Between Objects
Dependents
update: #aspectSymbol
Any object
Removing Dependents
The Object class provides a central dictionary for keeping track of any
object’s dependents. An application that adds a dependent is also
responsible for removing it (using removeDependent:), to avoid having the
dictionary hold onto obsolete dependents and waste increasing amounts
of memory.
The Model class provides an instance variable for storing dependents
locally, avoiding the use of the central dictionary. Thus, instances of
subclasses of Model (including the value model hierarchy) automatically
release their dependents when they expire. Because value models are
the targets of the vast majority of dependencies, this takes care of most
situations.
Circular Dependencies
Because dependencies involve indirect communications, the hazard of
circular message-passing becomes more likely. The most common
situation in which circularity arises involves two mutually dependent
widgets.
For example, in a document display window, the “page number” display
field and “table of contents” treeview widget may be mutually dependent.
That is, changing the page number updates the selection in the treeview,
and changing the selection in the treeview updates the page number.
Selecting an Interface
An application is typically started by sending an open message to the
appropriate subclass of ApplicationModel. This assumes that the primary
canvas was saved with the default name, windowSpec.
If the primary canvas has a different name, or if you want to open a
different canvas, you can send openWithSpec: to the class, with the spec
name as the argument.
The application model class creates a new instance of itself to run the
interface. If you want to use an existing application model instance, you
can send open or openInterface: to that instance. This is useful when you
want to reuse an instance rather than create a new one, or when you
want to initialize the application specially.
194 VisualWorks
Application Startup and Shutdown
Prebuild Intervention
After an instance of UIBuilder has been created, but before it has been
given a set of specs with which to construct a UI, the application model is
sent a preBuildWith: message. The argument is the newly created
UIBuilder.
Most applications do not need to intervene at this stage. Those that do,
typically take the opportunity to load the builder with custom bindings that
can only be derived at runtime.
Postbuild Intervention
The application model creates a hierarchy of spec objects from the spec
method, and hands the root spec to the builder. The builder then creates
a window and populates it with the appropriate widgets. The builder does
not yet open the window, however.
At this stage, the application model receives a postBuildWith: message,
with the builder as argument. The application model can use the builder
to access the window and any named widgets within the window—that is,
widgets that were given an ID property.
Applications commonly use postBuildWith: to hide or disable widgets as
needed by the runtime conditions.
Postopen Intervention
The builder opens the fully-assembled interface. At this stage, the
application model is sent a postOpenWith: message, again with the builder
as argument. As with postBuildWith:, the application can use the builder to
access the window and its widgets. This time, however, those objects
have been mapped to the screen, which makes a difference for some
kinds of configuration.
For example, the FileBrowser model that drives the File List interface uses
postOpenWith: to insert the default path in the window’s title bar—
something it could not do until after the window had been opened.
Application Cleanup
An application model often needs to take certain actions when the
application is closed. For example, a word-processing application might
need to ask the user whether edits that have been made to the currently
displayed text should be saved or discarded.
196 VisualWorks
User Settings Framework
Settings
Each individual setting on a page that appears in the Settings Manager is
defined as a method belonging to the class side of VisualWorksSettings.
The Settings Manager dynamically generates the user interface and the
page layout, so there are no window specifications or subcanvases for
the developer to worry about.
The settings framework requires only a method for each setting that
appears on a page, plus one method defining the page itself (for details
on the latter, see “Settings Pages” on page 200).
Each method used to define an invidual setting has two parts: a pragma
expression, which marks it as a setting definition, and the method body
that answers a setting model. For example:
toolsTranscriptLimit
<setting: #(tools transcriptLimit)>
^(IntegerSetting on: Transcript aspect: #characterLimit)
label: 'Transcript limit'
The pragma expression indicates both that the method is a setting, and
defines its ID. The ID is an array of symbols — in the example above,
#(tools transcriptLimit) — which must be unique to each particular setting.
The ID declares that a particular setting belongs to a specific page. For
example, all settings on the Tools page have IDs of the form #(tools
<aSymbol>) — in other words, their IDs all begin with the same
subsequence of symbols and only differ in the last symbol. Think of a
prefix as a "directory name", identifying the group a setting belongs to,
while the last element of an ID is a "file name" within the group.
The body of the method should return a setting model: an object that
knows how to get and set the value of the setting. In the example above,
an IntegerSetting on the characterLimit aspect of Transcript, i.e., that the
value of the setting will be obtained by sending characterLimit to the
Transcript, and set by sending the message characterLimit:.
Declaring the setting an IntegerSetting affects how it’s presented in the
settings tool: e.g., the setting is shown as an input field into which the
user can type an integer value. Typing anything else is not allowed, and
the settings framework performs simple input validation.
Finally, the label: message sent to the setting model defines its label. This
is a short string used to label the widget displaying this setting. The user
interface for the setting is dynamically generated using the information
provided in this method.
Defining a Setting
During application development, new settings and settings pages may be
defined simply by adding methods to class VisualWorksSettings.
As an example, we might want to add a setting to specify the number of
characters that can be written to the System Transcript before it starts
discarding the old output.
The following steps illustrate how to define a setting that manipulates the
Transcript object:
1 Open a browser on VisualWorksSettings, and examine its class-side
protocol.
For this example, we add a method in the protocol settings-tools.
2 Add a new method with the following body to the class side of
VisualWorksSettings:
toolsTranscriptLimit
<setting: #(tools transcriptLimit)>
^(IntegerSetting on: Transcript aspect: #characterLimit)
label: 'Transcript limit'
To see the new setting, open the Settings Manager and select the
Tools page.
The Transcript is an instance of class TextCollector, which includes two
methods — characterLimit and characterLimit: (in the private protocol) — for
controlling how many characters can be written to the Transcript. These
are used by the setting model (an instance of IntegerSetting) to manipulate
the Transcript object.
Note that the setting model also performs some minimal input validation.
In this case, the IntegerSetting only allows integers, as we would expect.
198 VisualWorks
User Settings Framework
Settings Pages
Each page of settings in the Settings Manager is defined in a manner
analogous to the individual settings on that page: using a single method
belonging to the class side of VisualWorksSettings.
Just as in the setting definition methods described previously, the method
that defines a settings page has a pragma expression and a method body
that answers a model for the settings page. For example:
transcriptPage
<settingsPage: #(tools transcript)>
^ModularSettingsPage new
label: 'Transcript';
icon: (ListIconLibrary visualFor: #tools);
settings: (self settingsWithPrefix: #(tools transcript))
The pragma is maked with the selector settingsPage:, which takes an array
argument to specify the page ID. This ID is used to define the hierarchical
relation between the various pages.
200 VisualWorks
User Settings Framework
3 Select the new protocol, and add a new method with the following
body to VisualWorksSettings:
transcriptPage
<settingsPage: #(tools transcript)>
^ModularSettingsPage new
label: 'Transcript';
icon: (ListIconLibrary visualFor: #tools);
settings: (self settingsWithPrefix: #(tools transcript))
4 Add the following method to the settings-transcript protocol:
toolsTranscriptLimit
<setting: #(tools transcript characterLimit)>
Setting Types
As noted above, a setting model does not actually contain the value of
the setting. The actual value is stored in the domain model, which the
setting model knows how to access.
Since the setting model is only a passive, transitive object, it is created by
using a setting type. For example, the following code:
IntegerSetting on: Transcript aspect: #characterLimit
returns a setting model that knows how to access the Transcript. Here,
class IntegerSetting specifies the type of setting that is instantiated.
In addition to class IntegerSetting, the settings framework supports a
number of different setting types.
The currently supported types are:
BooleanSetting
The setting value should be a true or false object. In the Settings
Manager, this type of setting is displayed as a checkbox.
ColorValueSetting
The value is an instance of ColorValue. In the Settings Manager, it is
displayed as a color swatch with a button that opens a color picker
dialog to pick a different color.
202 VisualWorks
User Settings Framework
EnumerationSetting
The value is one of a list of arbitrary objects. In the most general
case, the setting is initialized with three "parallel" sequences: a list of
objects that can be the value of the setting, a list of keys (Symbols)
that are used to represent the objects when the setting is saved in a
file, and a list of labels used to identify the choices in the Settings
Manager. This setting is displayed by default as a drop-down list of
choices, but can also be displayed as a group of radio buttons.
FilenameSetting
The value is a Filename identifying a file. The setting is represented as
an input field with the name of the file. The name can be changed
using the field, or (on Windows) by using the Browse button to pick a
file using the standard file selection dialog.
DirectorySetting
The value is a Filename identifying a directory. Unlike the
FilenameSetting, the Browse button is available on all platforms and
opens a directory selection dialog.
NumberSetting
The value is a Number. An upper and lower bound can be provided.
The setting is represented as an input field displaying the number.
IntegerSetting
Similar to the NumberSetting, but the value is required to be an Integer.
StringSetting
The value is a String. It is represented as an input field. Additionally,
an instance can be created as StringSetting forNameOfFile or
StringSetting forNameOfDirectory. Such StringSettings are represented
just as FilenameSetting and DirectorySetting, but the value of such a
setting is still a String rather than a Filename.
SequenceSetting
Its value is a sequenceable collection of values. The type of the
values is defined when the SequenceSetting type is created (it is
created using the of: message, with the type of the element passed
as the argument). The element type can be any of the types listed
above. These settings cannot be displayed by ModularSettingsPages,
each requiring a page of their own (a SequenceSettingPage).
It should be noted that a setting type and a setting model are not the
same. The setting model is responsible for data access: it knows how to
get and set the value of the setting, and also things like the label and the
help text. The setting type knows what values a setting can take.
204 VisualWorks
User Settings Framework
This example returns a setting model for the old preference model named
#showWorkspaceToolbar that is stored in the dictionary of preference
models in class [Link].
For example:
lookAndFeelPage
^ModularSettingsPage new
label: #LookAndFeel << #labels >> 'Look and Feel';
icon: (ListIconLibrary visualFor: #window);
addAllSettings:
(self settingsWithPrefix: #(lookAndFeel)
except: #(windowPlacement mouseButtonOrder));
useRadioButtonsForEnumerations;
addSetting:
(self settingWithId: #(lookAndFeel windowPlacement));
addSetting:
(self settingWithId: #(lookAndFeel mouseButtonOrder))
In this method, we use addAllSettings:except: to indicate that the setting
definition methods for window placement and mouse button order are
given different treatment. Note that these two settings are identified by
the last symbol in their respective IDs (each being an array of symbols).
By default, an EnumerationSetting is displayed as a drop-down list. In the
example code shown above, the message useRadioButtonsForEnumerations
is sent to indicate that these two settings should be shown as radio
buttons. Subsequently, any settings added to the page are displayed
using radio buttons. The remainder of the method adds the two settings
that were previous excluded from the page.
206 VisualWorks
User Settings Framework
208 VisualWorks
Responding to System Events
default generator, but it is most useful if it is well seeded, and the seed is
updated between image startups. To manage this we can define a
UserApplication subclass, DefaultRandomSystem, and implement two
methods.
setUp
DSSRandom resetDefaultFrom: 'seed' asFilename readStream binary
tearDown
'seed' asFilename writeStream binary;
nextPutAll: (DSSRandom default next changeClassTo: ByteArray);
close
The tearDown method records a seed value by writing it to a file just
before the system shuts down. The setUp method then reads that value
upon system start up, and reseeds the default generator with it. In this
case there is no application to launch.
As another example, we can implement main to launch an application,
such as RandomNumberPicker from the VisualWorks Walk Through. To do
this, we define a subclass of UserApplication, such as RandomPickerSystem,
and implement a main method. Minimally, it might be:
main
[Link] open
(By importing the WalkThru namespace into RandomPickerSystem, the
expression above can be simplified, and is the preferred practice.)
This example also indicates the reason for the main method, which is not
really needed (everything could be done in setUp). Programmers coming
from other development environmes often look for the method that starts
an application, and particularly for a method named “main.” This provides
that method.
210 VisualWorks
Responding to System Events
loadSettings: fileNameStream
"This handles loading settings from the command line."
<option: '-settings'>
| settingNames |
self class allowSettings ifFalse: [^self].
settingNames := CommandLineInterest argumentsFrom: fileNameStream.
settingNames do: [:each |
self loadSettingsFrom: each asFilename].
The option: pragma keyword identifies this as defining a command line
option, and the String argument identifies the particular option being
defined. The method selector takes an argument, fileNameStream, which
causes the next item on the command line to be handed to the method as
that argument. If the option does not require an argument value, the
method selector would be unary.
The rest of the method defines the processing of the argument. The
whole command line stream is handed into the method in the argument,
fileNameStream. The interesting expression is:
CommandLineInterest argumentsFrom: fileNameStream
which extracts just the argument relevant to the setting being defined; in
this case, the argument following “-setting” on the command line.
To define a new command line option relevant only to your application,
you can define it in your application’s subsystem class. For example, we
have already shown how to launch an application using its subsystem.
Perhaps you want to include an option to prevent launching the
application. Here is one way to do that, modifying the RandomPickerSystem
defined earlier.
First, in the class definition for RandomPickerSystem add an instance
variable, such as launchApp, which will hold a flag:
[Link] defineClass: #RandomPickerSystem
superclass: #{[Link]}
indexedType: #none
private: false
instanceVariableNames: 'launchApp '
classInstanceVariableNames: ''
imports: ''
category: 'System-Subsystems'
Then, implement a method to define the option and its handling:
noLaunchOption
<option: '-nolaunch'>
launchApp := 'nolaunch'.
The handling here is simple, simply setting the flag in the variable, which
we then use to decide whether or not to launch the application. Modifying
the main method to use the value, we might have:
main
Activating a Subsystem
Once a subsystem has been defined, as described in the preceding
section, it needs to be activated.
Normally a subsystem is activated upon system startup, by successfully
executing its setUp method. So, to activate a new subsystem you can save
the image, then shut down and relaunch the image. This is also a good
test of the set up operation.
To activate a new system without shutting down and relaunching, set an
activate message to the subsystem. For example:
RandomPickerSystem activate
As long as the setUp method completes successfully, the subsystem is
activated. In this example, the application will also launch.
212 VisualWorks
Responding to System Events
214 VisualWorks
Triggering Events
Triggering Events
Any object can trigger any event. Accordingly, there is generally no need
to specify the events an object will trigger, though for some purposes this
can be defined in a constructEventsTriggered message (refer to “Defining
Event Sets” later in this chapter).
To trigger an event, an object simply sends a variant of triggerEvent: to
itself, with the event name as the argument:
self triggerEvent: #foo
Variants are described below.
216 VisualWorks
Registering an Event Handler
218 VisualWorks
Removing Event Handlers
RemoveAction messages
removeAction: anAction forEvent: anEventNameSymbol
Remove the first occurrence of anAction from the list of actions for the
event named anEventNameSymbol.
removeActionsForEvent: anEventNameSymbol
Remove all actions for the event named anEventNameSymbol.
removeActionsSatisfying: aBlock forEvent: anEventNameSymbol
Remove all actions for the event anEventNameSymbol that satisfy
aBlock.
removeActionsWithReceiver: anObject forEvent: anEventNameSymbol
Remove all actions for the event named anEventNameSymbol in the
receiver's event table which have anObject as their receiver.
removeAllActionsWithReceiver: anObject
Remove all actions for all events in the receiver's event table that
have anObject as their receiver.
220 VisualWorks
How Handlers are Registered
Event classes
Several special event classes are defined, as subclasses of Event. In
general, there is no need create such classes, as explained above. These
classes exist as interfaces for operating system events coming in through
the virtual machine.
222 VisualWorks
Trigger Event System Support Methods
actionListForEvent: anEventNameSymbol
Answers an editable list of actions that are evaluated when the event
named anEventNameSymbol is triggered. The actions may be blocks or
messages.
canTriggerEvent: anEventNameSymbol
Answer a Boolean indicating whether the receiver can trigger an event
named anEventNameSymbol.
eventsHandled
Answers a collection of the events name symbols for which there are
actions registered in the receiver's event table.
hasActionForEvent: anEventNameSymbol
Answer a Boolean with regard to if the receiver has an action
registered for the event named anEventNameSymbol.
wrapperAt: aSymbol
Answer the value of the named component at aSymbol. Typically gets
a SpecWrapper or nil. In the case of a toolbar, it gets the actual ToolBar
instance.
controllerAt: aSymbol
Answers the controller for the component associated with aSymbol.
The answer may be nil or a Controller. In the case of a toolbar, it will
be nil.
widgetAt: aSymbol
Answer the widget associated with aSymbol. Typically answers a kind
of VisualPart, which may be nil.
mainWindow
Answer the main window associated with this ApplicationModel
instances. Typically answers a ApplicationWindow. May be nil if the
window is not created yet.
windowMenuBar
Answers the instance of MenuBar associated with the main window.
May be nil if the window is not mapped and opened, or if there is no
menu bar associated with the main window.
224 VisualWorks
10
Debugging Techniques
Software Probes
Software probes provide a mechanism analogous to hardware probes
used in troubleshooting electronic components, providing a way to check
the state of the system at a specific point. An electronic probe does not
change the design of an electronic circuit but, when used, it may change
the circuit's characteristics slightly. Similarly, using a software probe does
not change the source code design, but will affect the timing of the
program execution. In regards to a Smalltalk program, this means that the
source code is unchanged, so insertion and removal of a probe is not
logged, but program timing will be slightly changed. Usually, this is not a
problem.
A probe can be inserted before or after any message send, assignment
operation, or upon referencing a variable reference. Inserting a probe
actually inserts a message send to the probe object. Because a probe is
inserted by modifying the compiled method instead of source code, it is
possible to perform actions that are cumbersome to do within the
Smalltalk syntax.
There are two basic types: breakpoint and watchpoint. Every probe has a
conditional expression and an action. If the conditional expression returns
true, then the action is performed. In the case of an breakpoint, the
expression simply returns true. The action performed is determined by the
probe type.
Breakpoint
A breakpoint, which is the simplest kind of probe, immediately opens the
system debugger, skipping the notifier stage, when it is triggered. The top
method in the stack is the method containing the breakpoint. The current
message send depends on placement of the breakpoint. A breakpoint is
a better alternative to inserting self halt in code to invoke a debugger,
because it does not require a change in the source code.
A conditional expression may be used with a breakpoint, allowing you to
test for specific conditions and selectively trigger the breakpoint. The
expression can include any arbitrary operation, such as data collection.
However, it must return a Boolean upon completion. The debugger window
opens if the value is true, and does not open if the value is false.
226 VisualWorks
Software Probes
Watchpoint
Watchpoints display a string message in a watchpoint window, without
interrupting program execution.
The string provides information about the state of some part of the
program when the watchpoint is triggered. In general, the string is a
representation of an object.
There are four watchpoint types, which you select when creating the
watchpoint:
Top of Stack
Displays the value of the variable currently at the top of the argument
stack, which may be an argument or the last message result.
Instance Variable
Displays the value of the specified instance variable.
Temp Variable
Displays the value of the specified temporary variable.
Expression Watch
Displays the result of a Smalltalk expression, which must evaluate to
a String. This probe enables the user to properly display complex
information or to format a string in a more meaningful manner.
The String representing the object displayed by the Top of Stack,
Instance Variable, and Temp Variable probes is obtained by sending the
object the debugString message. This method is defined in class Object as
^self printString. It provides flexibility in representing an object.
Setting Probes
Inserting a probe into source code is done by selecting a menu command
in a browser and, in most cases, providing additional information in a few
probe editors.
Setting a breakpoint
A simple breakpoint is set in a method definition by placing the cursor at
the point at which you want to interrupt processing, and then selecting
Insert Breakpoint from the <Operate> menu. The character at the cursor
location is highlighted, indicating the breakpoint.
A breakpoint can also be made conditional. To do this, select Insert Probe
instead of Insert Breakpoint. Then, in the Select Probe Type dialog, select the
Breakpoint radio button and check the Conditional check box. Refer to
“Making a probe conditional” below for further information.
228 VisualWorks
Setting Probes
The three buttons allow you to specify what variable to watch. You can
only specify one. Top of Stack will display in the watch window the value on
the top of the argument stack, which is either a message argument or
response. The Instance Variable and Temp Variable buttons pop up a list of
available variables (Temp Variable is active only if the method has
temporaries).
When you have selected the variable to watch, click Done. The probe is
set, and the watch window opens the first time the probe is triggered.
230 VisualWorks
Setting Probes
Removing probes
You can remove probes either selectively or from an entire method.
To remove a single probe, select its highlighted character and select
Remove Selected Probe from the <Operate> menu.
To remove all probes from the method, select Remove All Probes from the
<Operate> menu.
Leave the Breakpoint radio button selected, and check the Conditional
check box. Click Done. The Conditional Text Expression probe editor opens.
Initially the expression is simply false, which will prevent the breakpoint
from triggering. You need to replace this with an expression that will
evaluate to true in just those cases where you want the breakpoint to
trigger, allowing you to further investigate the state.
For example, in RandomNumberPicker from the VisualWorks Walk
Through, you could insert a breakpoint in the nextRandom message, and
set the conditional expression to:
currentRandomValue value < 0.5
to break only when the random value is smaller than .5.
When you have entered the expression, accept the change (Accept on
the <Operate> menu), and click Done. The probe highlight is placed and
the probe is set.
A probe expression is a normal Smalltalk expression, except that it has
additional variable scoping. This scoping permits the expression to
reference variables in the probed method context and instance variables
of its receiver. Additionally, each probe may refer to its own local debug
variables and to global debug variables. Menu commands are available in
the condition editor pane to define new variables.
There are two predefined variables that you can reference to access
information in the context where the probe activation occurs:
DOITCONTEXT and TopOFStack. The variable DOITCONTEXT holds the
context itself, and TopOFStack is the object on the top of the context stack.
An additional predefined variable, ThisProbe, holds probe state
232 VisualWorks
Setting Probes
Either enter the numeric ID of a watch window in the entry field, or click
Select ID and choose a window from the list.
The Open Window button opens the window immediately, rather than
waiting for the probe to be triggered.
The Change Label button allows you to enter a more descriptive window
label string. You can then use the label later to identify this window for
other watch probes.
When the window parameters have been set, click Done.
Modifying a probe
Watch probes and conditional breakpoint probes can be modified. The
variable of a variable probe cannot be changed, but the conditional test,
watch expression, and window parameters all can be changed.
234 VisualWorks
Setting Probes
Probe location
When a probe is present in a method, its position in the source code is
indicated by highlighting the character at that position. Permanent probes
are indicated by underlining the character and coloring it red. Temporary
probes, which are only available in the debugger, are indicated by
underlining the character and coloring it yellow.
Because the highlighting is done with a text emphasis, operations that
change the text may remove the probe highlight without actually removing
the probe.
The meaning of the highlights in various situations are as follows;
Message selector
• First or only character - probe activation occurs before message
send.
• Last character or following space - probe activation occurs after
message send.
• Last character of first component of keyword - probe activation
occurs after message send.
Variable name
• First character - probe activation occurs before variable access
(usually an assignment).
• Last character - probe activation occurs after variable access
(usually a read).
Examples of probe highlighting are shown later in the paragraph
discussing probe insertion within the browser.
Limitations
Probe highlights
Performing a format operation in a browser causes the probe highlight to
be lost. If you then accept the change, probe is lost as well. Because
probes are maintained by their position in the source code, reformatting
accepting loses that position, so the cannot be reinserted.
Inserting probes at returns
The VisualWorks compiler compiles the following code:
^condition
ifTrue: [ expression1 ]
ifFalse: [ expression2 ]
as though it were written:
condition
ifTrue: [ ^expression1 ]
ifFalse: [ ^expression2 ]
That is, with two returns, one for each expression, rather than just one.
Because probes are added according to the parse tree, if you attempt to
probe the return value by inserting a probe at the return caret in the first
example, the result is as though one probe were inserted at the return
caret for only one of the expressions. The work-around is to insert two
probes, one at the end of expression1 and another at the end of
expression2.
The same situation occurs for the following code block:
[statements...
condition
ifTrue: [ expression1 ]
ifFalse: [ expression2 ] ] value
which is compiled as though it were written:
236 VisualWorks
Class Probes
[statements...
condition
ifTrue: [ expression1 blockReturn ]
ifFalse: [ expression2 blockReturn ] ] value
If a probe is placed at the condition, expecting to reflect the value
returned by one of the expressions, it would actually only capture one of
the expressions. This only occurs when the conditional statement is the
last statement in the block. Again, the proper work-around is to insert a
probe at the end of both expression1 and expression2.
Class Probes
Three commands on the Class menu in browsers provides for managing
probes at a class level: Add Class Probe, Remove Class Probe, and Browse Probed
Methods. These commands are aids to managing probes in bulk rather
than individually.
The panel displays a filtered list of methods that reference the selected
instance variable. Select a variable from the Selected Instance Variable drop-
down list. Then, select filtering criteria by the using the On Read, On Write,
and Include Subclasses check boxes. The type of reference is shown to the
immediate left of the method string. In the list, select the methods into
which you want to support the probe.
The proble action to perform on the selected methods can be one of the
following:
Breakpoint
Inserts a breakpoint at the variable reference point in each selected
method.
Smart Watch
Inserts an expression watch probe at the variable reference point in
each method. The expression returns a string containing the class
name, method selector and character position of the probe in the
method. When triggered, this string is recorded in the watch window.
Next the debugString message is sent to the object on top of the stack.
The resulting string is then recorded in the watch window on the line
following the method identification string.
238 VisualWorks
Class Probes
Simple Watch
Inserts an expression watch probe at the variable reference point in
each method. When triggered, the debugString message is sent to the
object on top of the stack and the returned string is recorded in the
watch window.
N Simple Watches
Inserts an expression watch probe at the variable reference point in
each method. When triggered, the debugString message is sent to the
object on top of the stack and the returned string is recorded in a
watch window. This differs from the "simple watch" in that each probe
has its own watch window.
Expression Watch
Inserts an expression watch probe, with a user defined expression, at
the variable reference point in each method. When triggered, the
expression is executed and the returned string is recorded in a watch
window. After the setup panel closes, an expression editor is opened
for you to define the expression (refer to “Setting an expression
watchpoint” above for an explanation of the editor).
Conditional
After the setup panel closes, a conditional expression editor is
opened for you to define the expression (refer to “Making a probe
conditional” above for an explanation of the editor).
Window parameters
After the setup panel closes, the "Window parameter" panel opens
for you to specify the watch window (refer to “Select a watch window”
for an explanation of the selector).
Generate report
Cause a report to be generated when the setup panel is closed. The
report lists all of the methods that have been selected to have a
probe inserted.
On Message Receipt...
The On Message Receipt... command allows you to insert a probe at the
beginning of each method in a selected group of methods. When you
select this command, the setup panel opens.
The panel displays a list of methods defined in the class and, optionally,
its subclasses. The type of action performed on the selected methods
can be one of the following:
Breakpoint
Inserts a breakpoint before the first statement in each selected
method.
Simple Msg Trace
Inserts an expression watch probe before the first statement in each
method. When triggered, the probes record the method receiver's
class and method selector in the watch window.
Ivar Watch
Inserts an instance variable watch probe into each selected method.
When triggered, the probes record a representation variable’s value
in the watch window. When this button is selected, the Select Variable
menu button is enabled, permitting you to select an instance variable.
240 VisualWorks
Class Probes
Expression Watch
Inserts an expression watch probe in each method. When triggered,
the probes cause the expression to be evaluated and the resulting
string to be recorded in the watch window. After the probe selection
panel closes, the expression editor opens (see “Setting an
expression watchpoint” for an explanation of the editor).
Conditional
After the setup panel closes, a conditional expression editor is
opened for you to define the expression (refer to “Making a probe
conditional” above for an explanation of the editor).
Window parameters
After the setup panel closes, the "Window parameter" panel opens
for you to specify the watch window (refer to “Select a watch window”
for an explanation of the selector).
Generate report
Cause a report to be generated when the setup panel is closed. The
report lists all of the methods that have been selected to have a
probe inserted.
Debugger
Walkback Notifier
When a program error occurs, a notifier window appears. This notifier
displays the last five message-sends in the context stack. The context
stack lists message-sends that were waiting for a return when the
breakdown occurred.
Sometimes that listing of the context stack is sufficient for you to identify
the problem and correct it. If so, click the Terminate button to close the
notifier and abort the program.
When the error is not so serious as to prevent proceeding with the
program (that is, it is a warning), you can click Proceed to close the notifier
and continue executing the program.
Debugger Window
When you need to examine the conditions that led to the failure more
closely, click Debug. The notifier is replaced by a debugger, which enables
you to trace the program flow leading to the error, proceed with execution
step by step, and examine the operative method and the values of the
variables at each stage of execution.
The VisualWorks debugger enables you to look at the methods that are
waiting for a return value when a program interrupt occurs, examine the
values of variables in each context, dynamically change a value or a
method, insert breakpoints, and restart execution at a chosen location
with the new values and logic experimentally in place.
242 VisualWorks
Debugger
At the top are three stack panes. On the left is the stack view, which lists
the message-sends that were waiting for a return at the time of the error.
The right two panes are the stack inspector, which allows inspection of
the selected expression’s intermediate stack values (see “Inspecting the
Stack” below for more information).
The code view is similar to the System Browser’s code view. When a
message-send is highlighted in the stack view, the corresponding method
is displayed in the code view. Within the method, the current point of
execution is automatically highlighted by the debugger.
At the bottom of the window are the instance-variable inspector, to the
left, and the temporary-variable inspector, to the right, which allow you to
examine the values of the variables. The variables and their values are
updated each time you choose a different position in the execution stack
with the stack view.
244 VisualWorks
Debugger
246 VisualWorks
Debugger
The Debugger’s inspectors let you change the value of a variable and
then restart the program. Simply edit the value, changing 'two' to a legal
value such as the integer 2. Then select accept in the <Operate> menu.
You can then select Execute Restart, and then Execute Run to resume
execution.
In practice, the value 'two' normally would be supplied by another method
rather than a Workspace expression. Having traced the problem to this
value, you can correct its parent method. To do so, edit and Accept the
revised method in any code view such as the one in the Debugger or the
one in the System Browser.
Stack menu
Copy Stack Report
Copies the context list to the clipboard so it can be pasted into a
document or workspace.
Show More Stack
This command adds more contexts to the context list. Under normal
conditions the debugger opens with the stack size set to 500, so this
command is seldom needed.
Filter Stack
Enables stack filtering, as specified in Settings tool, on the Debugger
page. The editor allows one to specify coloring of the context items
according to matching rules. For more information view the editor
help.
Use Short Class Names
When selected, displays only the class names, without the dotted-
name prefix for classes that are not visible outside the Smalltalk name
space.
Select Home Context
Searches the stack and selects the home context of the currently
selected context. If the home context is not on the stack, a dialog will
inform the user of the situation.
Inspect Context
Opens an inspector on the method context.
Bookmark Context
Highlights the stack item (context) and adds it as an item on the
Stack Bookmark menu, for easy access to this context.
Clear Bookmark
Clears the bookmark for this context.
Method menu
Most of these menu items are the same as in the System Browser. The
only exception is:
Recompile with Full Blocks
Recompiles the method so that all the blocks are full blocks. This also
has the effect of causing the method to be reentered, i.e., the
execution state of the method is reset. An Accept command also
causes the method to be reentered. This method is a temporary
method and disappears when a method return is executed.
248 VisualWorks
Debugger
Execute menu
Step Into
The most detailed stepping operation. When a message send is
selected, it sends the message and displays the resulting context.
Otherwise, it steps through the method, stepping into blocks along
the way.
Step
Steps through the method, stepping into blocks along the way.
Step Over
Steps through the method, stepping over blocks as they occur.
Restart
Initializes the selected context and restarts execution at beginning of
its method, as if the debugger had just stepped into it. The method
may be either a CompiledMethod or a CompiledBlock.
Return
Allows the selected method or block to discontinue further execution
and return immediately to its sender.
Run to caret
Advances to the caret, either into or out of a block closure. This is
limited to full block closures. If a return is encountered within the
selected context before the caret is reached, execution will stop
before executing the return. However, if the return is within a block
closure the method may return, at which point execution will stop.
Jump to caret
Jumps over code to the next caret, without execution. It causes the
execution point to be positioned at the beginning of the statement
containing the caret. A jump to caret into or out of a block closure
cannot be performed. However, it is possible to jump into and out of
conditional blocks, because they have been optimized by the
compiler and are not real block closures. Also, it is not possible to
jump into a loop, even if it has been optimized by the compiler.
Run
Continues execution from the current location.
Run with Break on Return
This command and the next are useful for debugging loops. This
command is similar to Run, except that an implicit breakpoint is set to
be triggered upon return from the current context. Also, the debugger
remains open. Execution stops either upon return from the context, or
if another breakpoint is encountered before then. Execution is
guaranteed to stop, so runaway loops can be interrupted.
Temporary Probes
Temporary probes are probes that disappear when the method returns,
as they only apply to the method context and its blocks. They appear in
the text view as yellow highlights, instead of red like permanent probes.
250 VisualWorks
Inserting Probes in the Debugger
The Default to Temp probes or Default to Perm probes command on the Visual
Launcher Probes menu sets whether the Temporary button is initially set or
cleared. Additionally, the command determines if the "insert breakpoint"
command will insert a temporary breakpoint or a permanent breakpoint.
3. Probes inserted into or removed from a method will only affect the
home context, block closures, and block contexts that are
descendants of the home context. Contexts and closures that are a
result of a different message send, but the same method, will not
have the probe operation performed on them.
Refer to the following section on Debugging tips for assistance with
problems with inserting probes into blocks.
Debugging Tips
Inserting probes into blocks
When a probe is inserted into a method, the compiled method is replaced
with a probed compiled method. If the probe was inserted via the browser
all the blocks are recompiled as full blocks. If the probe was inserted via
the debugger then the block structure is not changed. The importance of
this is that in order to insert a probe in a block via the debugger the block
must be a full block. This also affects the operation of the debugger
Skip to caret command, which operates by inserting a temporary breakpoint
in the method, continuing execution, and then removing the breakpoint
when it is encountered.
If you wish to insert a probe into a block that is not a full block you can
use the debugger Make full blocks menu command, or you can insert a
probe into the method using the browser before the method is executed.
If the method of interest is a method that cannot be halted with a
breakpoint, you can disable it by inserting a conditional breakpoint and
have the conditional expression return false. When the method is
subsequently entered, in the debugger, all its blocks will be full blocks
which will permit temporary breakpoints to be inserted in a block as well
as using skip-to-caret into or out of blocks. The "Implementation Limits"
document, in the doc/TechNotes directory, has a more complete
description of blocks.
Iteration debugging
Frequently, one would like to continue execution in the debugger for the
next iteration for some iterator construct. The Execute Return with Break on
Return and Return with Break Again commands provide this capability.
These two commands are especially useful for debugging loops. You can
set a breakpoint inside a loop, and then use Run with Break on Return to start
execution. It will stop either on the breakpoint inside the loop or, if the
252 VisualWorks
Debugging Tips
loop did not iterate, upon return from the method. When stopped inside
the loop, you can use Run with Break Again to do the next iteration, with a
protection against "running away" in case there is no next iteration.
The following steps illustrate how to do this.
1 Insert a temporary breakpoint in the loop code where you want
control to be returned to the debugger, or in some message that is
sent from the loop.
2 Select the either the home method context or a context between the
block context and the home context.
3 Issue the Run with Break on Return command.
4 When the process stops inside the loop, perform successive
iterations by issuing the Run with Break Again command. It does not
matter what context is selected when the command is reissued.
5 If you want to reset the guard context, select the desired context and
issue the Run with Break on Return command.
Interrupting a Program
In addition to inserting breakpoint probes, you can manually stop a
Smalltalk program by typing a user interrupt key sequence or by inserting
a halt message in the program.
<Control>-y invokes the user interrupt function. Enter this key sequence
when you want to freeze a program that is looping endlessly, or to capture
its state at a specific observable stage.
<Control>-\ freezes all user processes and opens a process monitor,
allowing you to explore them individually.
Inserting the expression self halt in a method at the location where you
want execution to be interrupted, used to be normal practice. In the
presence of breakpoints, this is seldom necessary, but is an option. When
a self halt is encountered, the Debugger is opened immediately, by-
passing the initial walkback.
Probe library
The probe library feature stores collections of probes in external files.
This allows you to use a standard set of probes to employ in debugging a
new image. For information about the file format, read
pdp/[Link].
254 VisualWorks
Global Probe Management
The following commands, on the Probes menu in the Visual Launcher, are
used to save and load the probe library files.
Probe Library Load
This command will save all the probes in the image to the specified
file.
Probe Library Save
This command will read the specified file and load the described
probes into the image.
Expression libraries
Two expression libraries are provided to assist in using common probe
expressions. One library is for test expressions ([Link]) and the
other is for watch expressions ([Link]). Both libraries are in the
pdp/ directory.
The following commands for managing the libraries are on the Debug
Watch Library and Test Library submenus in the Visual Launcher. Each
command has two submenus which select either the test expression
library or the watch expression library.
Load
This command prompts for the name of a file containing an
expression library. The entries contained in the file will be added to
the entries already in the library.
Save
This command prompts for the name of a file into which to save the
expression library.
Inspect
This command opens an inspector on the library.
Clear
This command clears the expression library of all entries.
256 VisualWorks
11
Exception and Error Handling
Exceptions are unusual or undesired events that can occur during the
execution of a VisualWorks application. While not all exceptions are
errors, errors are among the most important exceptions that your
application needs to handle.
When an exception occurs, an application might need to take some
special action. For example, if an application is reading data from a file
and unexpectedly encounters an end-of-file, it might stop processing and
display an error message. Using the exception handling features in
VisualWorks, the application can trap the exception and invoke the
special processing.
258 VisualWorks
Exception Classes
Exception Classes
Exceptions are represented as instances of classes, with Exception at the
top of the class hierarchy. It has several direct subclasses, two of the
most important being Error and Notification. Subclasses of all these define
more specific kinds of exceptions which can be trapped by your
application. Your application can define its own Exception subclasses for
special exceptions and errors.
Each exception class either defines or inherits a defaultAction message,
which is invoked when that exception occurs unless a handler is defined
for it. The table below lists some common exception classes with the
exceptional event represented by the class and the default action it
performs.
Exception Classes and Their Default Actions
Exception Class Exceptional Event Default Action
ArithmeticError Any error evaluating an Inherited from Error
arithmetic operator
Error Any program error Open a notifier
MessageNotUnderstood A message was sent to Inherited from Error
an object that did not
define a corresponding
method
Notification Any unusual event that Do nothing,
does not impair continuing executing
continued execution of
the program
Warning An unusual event that Display a Yes/No
the user should be question dialog and
informed about return a Boolean
value to the signaler
ZeroDivide An attempt to divide by Inherited from
zero ArithmeticError
Your application can have its own exception conditions,which are distinct
from those provided with VisualWorks. To identify the exception, create a
subclass of Exception or Error, as appropriate. If special handling is
required for the exception, you must define a handler for it, as explained
in the following sections.
The occurrence of an exception normally causes VisualWorks to discard
the work in progress. Sometimes a method does something that requires
a subsequent action, regardless of whether or not an exception occurs. In
that case, use the unwind mechanism described at the end of this
chapter.
Handling Exceptions
The default action for most exceptions is to display a notifier. For
development this is useful, allowing the developer to seek out the cause
and repair it. However, for an application, a notifier is not appropriate, and
the exception needs to be handled by the application itself. To handle
exceptions in an application you define an exception handler.
An exception handler has two parts: the class of exception for which it
watches, and the block of code (the handler block) to be executed when
such an exception occurs. The handler block must be a one-argument
block.
Exception handlers are defined using the on:do: message. For example,
the following expression defines an exception handler for an attempt to
divide by zero, and specifies that a message be printed in the Transcript:
| xy|
x := 7.
y := 0.
[x / y]
on: ZeroDivide
do: [ :ex | Transcript show: 'zero divide detected'; cr.]
If a zero divide error occurs while evaluating [x / y], the handler block (the
argument to do:) is evaluated, causing the message to be written to the
transcript.
When creating exception handlers for your application, be as specific as
makes sense in naming the exception to which the handler responds. For
example, it might be reasonable in some contexts to trap any error,
without being any more specific than calling it an Error. In this case, an
expression like the following makes sense:
260 VisualWorks
Handling Exceptions
[x / y]
on: Exception
do: [:theException |
Transcript show: theException description.
^'uncomputable'].
The instance of the class of exception that occurred is passed as the
argument to the handler block. In the above example, the exception
object could be an instance of ZeroDivide, ArithmeticError, or Exception.
Exception Sets
Occasionally it is necessary to establish an exception handler to handle
several exceptions that are not necessarily related in a hierarchy. This
can be accomplished by using an ExceptionSet. If any exception in the set
occurs, or any subclass of a listed exception, the handler block is
activated.
You can implicitly create an exception set by specifying a list of
exceptions in a handler. For example:
[do some work]
on: ZeroDivide, Warning
do: [ :theException | whatever]
Sending the , (comma) message to an exception class with another
exception creates an instance of ExceptionSet.
If you need to reuse the same set of exceptions, you can also create an
exception set explicitly and assign it to a variable:
specialExceptions := ExceptionSet with: ZeroDivide with: Warning
The exception set can then be used as the argument to on: in an
exception handler.
Signaling Exceptions
Most of the exceptions that your application needs to handle are detected
by code within the standard VisualWorks class library. Occasionally,
however, you may need to write a new method to signal the occurrence of
an exception, particularly if you have also created a new class of
exceptions.
An exception is signaled by sending the message raiseSignal or raiseSignal:
to the class that defines the exception. For example:
Error raiseSignal
262 VisualWorks
Exception Environment
Exception Environment
Each VisualWorks process has a distinct exception environment, which is
an ordered list of active handlers. When a new process begins, the list is
empty. When the receiver block of an on:do: statement is executed, its
exception handler is added to the beginning of the list, and its entry is the
on:do: statement. If another exception handler is defined within the
receiver block, it is added to the beginning of the exception environment
list for the process.
[ block 1 stuff
[ block 2 stuff
[ block 3 stuff
[ block 4 stuff ]
on: ColorError
do: [ handler code for 4 ] ]
on: Warning
do: [ handler code for 3 ] ]
on: Error
do: [handler code for 2] ]
on: ZeroDivide
do: [ handler code for 1 ]
newest or
innermost
ColorError
direction of search
Warning
Error
ZeroDivide
oldest or
outmost
264 VisualWorks
Exiting Handlers Explicitly
The messages exit:, resume:, and return: return their argument as the
return value, instead of the value of the final statement of the handler
block.
The message exit is provided by VisualWorks for conditionally exiting a
complex handler block. For resumable exceptions, it sends a resume
message, which restores the environment in which the exception
occurred and continues processing. For nonresumable exceptions, it
sends a return message, which trims the exception environment to the
active handler’s exception environment.
For example:
[Error raiseSignal]
on: Error
do: [:exception |
exception isResumable
ifTrue: [exception exit: 5].
Dialog warn: 'Nonresumable exception']
Because Error is a nonresumable exception, the warning dialog is
displayed. Replacing the protected block with [ Notification raiseSignal ] and
testing for Notification instead will exit (resume) with a return value of 5.
If the argument of a handler block is a resumable exception, the message
resume can be used instead of exit, which behaves in exactly the same
manner as exit for resumable exceptions. Attempting to resume a non-
resumable exception causes an “attempt to proceed” error.
To terminate and return from the block that triggered the exception, send
a return message. When sent to a resumable exception, return forces
control to return from the protected block instead of returning to the
message that triggered the exception. Thus, return can simulate the effect
of a nonresumable exception when an exception is in fact resumable. The
message return trims the exception environment to the active handler’s
exception environment.
Another way to exit a handler block is with the retry message. This
message terminates the handler block and tries again to evaluate the
receiver of the on:do: block. Any cleanup blocks created using the unwind
mechanism are executed before retrying, whether they were created by
the original evaluation of the receiver block or by the handler block.
For example, the following method tries again after a division-by-zero
error:
[^ x / y]
on: ZeroDivide
do:
[:exception|
"make the divisor very small but > 0"
y := 0.00000001.
exception retry]
The message retry therefore trims the exception environment to the active
handler’s exception environment when it retries execution.
The message retryUsing: does a retry, but evaluating the block passed as
argument instead. For example:
266 VisualWorks
Resumable and Nonresumable Exceptions
[self doTaskQuickly]
on: LowMemory
do: [:exception|
exception retryUsing: [self doTaskEfficiently]]
The message retryUsing: also trims the exception environment to the
active handler’s exception environment when it retries execution.
The message pass can be used inside a handler block to terminate the
handler block and execute any enclosing handler blocks for the current
exception. For example:
[n / m]
on: ZeroDivide
do:
[:exception|
"0/0 = 1; otherwise raiseSignal a ZeroDivide exception"
exception dividend ~= 0
ifTrue: [exception pass]
ifFalse: [exception return: 1]
The message pass sets the exception environment to the environment of
the handler to which it passes control.
In this example, the programmer decided to handle the case of 0 / 0
specially. If the dividend is anything other than zero, however, control
passes to the ZeroDivide exception. Control never returns to the sender of
a pass message.
268 VisualWorks
Translating Exceptions
Translating Exceptions
Occasionally, an exception handler might need to translate one exception
into another exception. This is usually done to provide more information,
or to consolidate low level exceptions to a higher level one. For example,
a low-level operating system error exception might need to be translated
into a higher level user exception.
Care is required to avoid executing the wrong handler. The reason is that
the exception environment within the handler signalling the low-level
exception is not necessarily the same as the exception environment
signalling the high-level exception. This problem is solved by using the
message resignalAs: instead of raiseSignal within the handler block. For
example:
[low-level I/O]
on: OperatingSystemException
do: [ex|
ex errorCode = -213
ifTrue: [ex resignalAs: EndOfFile new]
ifFalse: [ex resignalAs:
(Error new messageText: 'OS Error']]
The message resignalAs: aborts the current exception handler, restoring
the exception and execution environments to the states they were in
when the exception that is the receiver of resignalAs: was originally
signaled. (Note that this can cause the execution of unwind blocks). After
the environments are restored, the exception that is the argument to
resignalAs: is signaled. This causes the argument exception to function as
if it had been originally signaled in place of the receiver.
Unwind Protection
When a block of expressions contains opportunities for a premature
return, a means of cleaning up the mess may be required.
Providing such a mechanism is a kind of exception handling, though it is
accomplished with a variant of the value message that initiates a block.
Use ifCurtailed:, with the cleanup expressions as the argument block. The
cleanup block is used if the execution stack is cut back because of a
signal, if a return is used to exit from the block, or if the process is
terminated.
270 VisualWorks
Using a Signal to Handle an Error
methods. For example, browse class Palette which defines two signals,
PaintNotFoundSignal and PixelNotFoundSignal, and provides accessors in
two class methods, paintNotFoundSignal and pixelNotFoundSignal.
Classes for which error handling has been updated to use the class-
based mechanism still provide class-side accessor methods, but return a
class instead of a Signal instance. For example, Object errorSignal returns
the class Error rather than an instance of Signal.
Proceedabiltiy
A Signal has a proceedability attribute, which indicates whether the error
is harmless enough to permit the process to proceed from that point
onward. By default, a new signal inherits the proceedability setting of its
parent signal. To establish a specific proceedability in a new signal, use
newSignalMayProceed:, as in the following expression:
divSignal := (Number errorSignal) newSignalMayProceed: false
Creating an Exception
In the Signal mechanism, an Exception object is created by sending a raise
message to the appropriate signal. This object then travels back along
the message stack looking for its matching signal (or an ancestor),
triggering the intended handle: block.
For example, a paint program recognizing an error in the paint selection,
would signal that error by sending a raise message to PaintNotFoundSignal,
which raises the exception. This exception then traverses the chain of
calling objects until it finds a handler.
Because such Signal instances are not guaranteed to exist is future
versions, it is safer to use the accessor methods to access a signal. This
accessor method is updated to reference the class instead of the signal,
and using only the accessor method makes this transparent to the
application. So, it would be better to send:
Palette paintNotFoundSignal raise
The raise message effectively transfers control from the method in which
the error was perceived to the handle: block in the calling method. A
variant of raise permits control to proceed from the point of error (usually
after the handle: block warns the user or corrects the cause, or both). To
create a proceedable exception, use raiseRequest (the exception requests
that control be returned to it). A proceedable exception can only be
successfully addressed to a proceedable signal; a nonproceedable
exception can be addressed to either type of signal. Thus, the exception
largely determines its own proceedability.
Setting Parameters
An exception can carry an argument object back to the handler block,
such as a value that can be used to diagnose the breakdown, an array of
such values, or a block of remedial operations. The default is nil. To set
that value, send a parameter: message to the exception, with the object as
argument.
For situations in which the signal’s notifier string needs to be replaced or
augmented, send errorString: to the exception, with the replacement string
as argument. If the first character of the argument string is a space, the
argument is appended to the signal’s notifier string. Otherwise, the
argument string is used instead of the signal’s string.
By default, an Exception begins its search for a handler in the context that
sent the raise message. To substitute a different starting place, send a
searchFrom: message to the Exception, with the starting-point context as
argument.
Because more than one instance of the same Signal can exist, as
implemented by different methods (with different handlers, possibly), an
Exception can get fielded by the wrong handler unless it has a way to
identify its originator. To do so, send originator to the Exception, with the
object that originated the raise message as argument. To equip the
handler with the originator, so it can spot the matching Exception, send a
handle:from:do: message, supplying the originator as the argument to the
from: keyword.
272 VisualWorks
Using a Signal to Handle an Error
To return control to the point at which the error occurred, use proceed. To
pass an argument to be used as the value of the signal message, use
proceedWith:. To proceed by raising a new exception—in effect, to
substitute a different signal in place of the original error creator—use
proceedDoing: and raise the new exception in the argument block.
To restart the do: block, use restart. To substitute another block of
expressions for the original block, use restartDo:, as in the expression
theException restartDo: aBlock.
If a handler does not choose one of the four options described here, it
has the same effect as theException returnWith: the value of the block.
Raising a signal within its own handler does not restart the handler.
However, raising a signal within a proceedDoing: or restartDo: block does
invoke the signal’s handle block again.
Returning to the calculator example, let’s fill in the handler code:
ArithmeticValue divisionByZeroSignal
handle: [:theException |
Transcript cr; show: 'Enter a nonzero divisor'.
theException restart]
do: [the division operation]
274 VisualWorks
12
Process Control
UI Processes
Due to process management limitation in the VisualWorks UI architecture
prior to version 7.1, it was not safe to include code that affects the UI in a
process other than the UI process. If a forked process had to affect a UI
process, it was necessary to load the ForkedUI parcel.
Beginning in 7.1, the VisualWorks UI architecture fully supports multiple
processes. The new architecture provides for multiple window managers,
each of which may manage events for either a single or multiple windows.
Managing windows events is described in the GUI Developer’s Guide.
Refer to chapter 3, “Controlling the GUI Programmatically.”
Creating a Process
A Smalltalk process is a light-weight process that is non-preemptive of
other processes of the same or lower priority. It represents a sequence of
actions being performed by the computer. Frequently, two or more such
processes need to be running simultaneously. For example, you might
wish to assemble an index in the background at the same time as your
application user is performing an unrelated activity such as entering data.
In that case, the computer’s attention must be divided between the two
activities—in effect, we want to place a fork in the path so the processor
will progress down both paths at the same time.
Scheduling a Process
Processor is the lone, pre-fabricated instance of class ProcessorScheduler,
in the same way that Smalltalk is the unique instance of class
SystemDictionary. Both are global variables. Processor is responsible for
deciding which instruction to execute next, choosing among the next
actions in all of the current processes. It has to be made aware of a
process first—the process has to be scheduled.
The fork message, described above, automatically schedules its newly
created process. To schedule a suspended process (including a process
created with a newProcess message), use resume, as in the expression
aProcess resume.
To temporarily prevent execution of a process’s instructions, use suspend.
Thus, resume and suspend are complementary methods. A resumed
process starts up where it left off when it was suspended.
276 VisualWorks
Setting the Priority Level
A newly created process inherits the priority level of the process that
created it.
To assign a new priority to a process, use an expression of the form
aProcess priority: (Processor userInterruptPriority). Notice that the priority:
method expects an integer argument, but the sender asks the Processor
for the integer by name.
You can also specify the priority level at process creation time, using
forkAt: with the requisite priority level integer.
The Processor gives control to the process having the highest priority.
When the highest priority is held by multiple processes, the active
process can be moved to the back of the line with the expression
Processor yield—otherwise it will run until it is suspended or terminated
before giving up the processor. A process that is yielded will regain
control before a process of lower priority.
Synchronizing Processes
Sometimes one process has to wait for another process to mature before
it can take a particular action. For example, a printer might be tied up for
the next 20 minutes printing someone else’s job. Does that mean your
printing job should just tie up the Processor and refuse to yield until the
printer is available?
Semaphore
The Semaphore class provides a simple mechanism for resolving such
problems. In the printer example, an instance of Semaphore would be
created to keep an eye on the printer:
printerSemaphore := Semaphore new.
The process that funnels output to the printer, which we’ll call
printerProcess, sends the message printerSemaphore signal each time it
becomes available for more input. The waiting process, which has been
suspended so it won’t lock up the processor, is then resumed.
How did the waiting process get suspended in the first place? Instead of
just sending its contents and assuming it will be caught by printerProcess,
the waiting process sends the message printerSemaphore wait. Because
printerSemaphore has not yet received a signal message from
printerProcess, the waiting process was suspended. If the printerProcess
had already sent a signal message that was not consumed by another
process, printerSemaphore would have done nothing, permitting the waiting
process to send its data.
278 VisualWorks
Synchronizing Processes
wait
resume
raiseSignal
Using a Delay
The Delay class answers the common need for a means of postponing a
process for a specific amount of time. To create a Delay, use forSeconds:,
as in Delay forSeconds: 30. Or use forMilliseconds: if you require a finer
quantification of time.
To create a Delay that continues until the system’s millisecond counter
reaches a particular value, use untilMilliseconds:. To find out the current
value of the counter, use the expression Delay millisecondClockValue.
Merely creating a Delay has no impact on the current process. The
process must send the wait message to the instance of Delay. Thus, the
following expression in a method would suspend the current process for
30 seconds: (Delay forSeconds: 30) wait.
280 VisualWorks
13
Weak Reference and Finalization
Weak Arrays
A WeakArray is similar to an ordinary Array, the prime difference being that
a WeakArray references its elements weakly.
When an element of a WeakArray is no longer referenced by any object
other than another WeakArray, then that element is eligible for reclamation
by the garbage collector. During reclamation, the reference to that
element is removed from the WeakArray and replaced by zero.
Only the indexed variables of the WeakArray class are weak references.
The named instance variable, dependents, is strong. Further, this is the
only class whose references can be weak. Even subclasses of this class
can contain only strong references.
It is possible, however, to add named instance variables to this class, if
you are willing to redefine the class. As stated above, such variables will
be strong. The fact that this is the only class that can have weak
references may seem to be a substantial restriction, but you can easily
construct more complicated objects with a mix of strong and weak
references by using a WeakArray as a subcomponent.
Finalization
WeakArray also provides the system with a way of performing a final set of
actions when an object expires. For example, an application might need
to release some external resource when the objects using that resource
have all been garbage collected. Finalization provides the system the
ability to notify the application that the objects using the external resource
have all expired, thus letting the application know that it is safe to loosen
its hold on the external resource.
The mechanism involves sending a changed message to any WeakArray
that has had one of its elements zeroed out as described above. This
notification is then propagated to each of the dependents of that
WeakArray, allowing them to take the actions necessitated by the death of
the WeakArray’s element.
Of course, any such dependent will need to store whatever information it
needs prior to receiving such notification, because the object that was
once an element of the WeakArray will already have been destroyed. The
dependent must also ensure that it can subsequently locate that
information based solely on the dead element’s index in the WeakArray
(the dependent can find the index of a WeakArray’s dead element by
invoking the indexOf:replaceWith:startingAt:stoppingAt: primitive).
282 VisualWorks
Finalization
284 VisualWorks
WeakDictionary
WeakDictionary
A WeakDictionary is a dictionary whose valueArray is a WeakArray. Such a
dictionary is fully protocol-compatible with IdentityDictionary. The lookup is
done using == rather than =.
For finalization, WeakDictionary also stores an array of executors for its
elements. The default executor for each element is a shallow copy of the
element. An element’s executor is responsible for finalization after the
element has been reclaimed. An element with special finalization
requirements should implement the finalize message, which is sent to the
executor to actually perform the finalization. The default implementation
of finalize in the Object class performs no finalization.
HandleRegistry
A HandleRegistry is a WeakDictionary whose values all respond to a key
message. The elements of a HandleRegistry are registered using their
response to the key message as the dictionary key and using the element
as the value. Access functions are all implemented as critical regions so
that multiple processes can operate on an instance at the same time.
Finalization Example
To illustrate the finalization mechanism outlined above, we provide an
example in the form of code for an Executor class. An Executor is an object
that executes the last will and testament of a familyMember. To try it, enter
the code into the system, then evaluate the expression in the class
comment.
Class definition:
[Link] defineClass: #Executor
superclass: #{[Link]}
indexedType: #none
private: false
instanceVariableNames: 'familyMembers familyWills'
classInstanceVariableNames: ''
imports: ''
category: 'Finalization-Example'
Class comment:
The Executor class is a simple example of how finalization can be
achieved by using WeakArrays. After entering the code into the
system, evaluate the expression: “Executor example inspect”.
Instance Variables:
familyMembers
<WeakArray> containing the name string of each family member.
familyWills
<Array> of blocks that will print the last will and testament of the
corresponding person in the familyMembers array on the Transcript.
Instance methods for finalization:
readLastWillAndTestamentOfTheDeparted
"Read the will of each family member who has died."
286 VisualWorks
Ephemerons
Ephemerons
Ephemerons are special forms of Association that are used to attach
properties to objects without preventing those objects from being garbage
collected. They are like an Association whose key is weak, but they are
more sophisticated in that references back to the key from the transitive
closure of an ephemeron's other fields do not contribute to the key being
counted as “alive” for garbage collection purposes.
Both the DependentsFields dictionary, which associates dependents with
instances that don't inherit from Model, and the EventHandlers dictionary,
which associates event handlers with all objects, are implemented to use
EphemeronDictionary to use ephemerons. The result is that adding
dependents or event handlers to an object does prevent that object from
being garbage collected, and means that application code does not need
to handle that clean-up itself.
Ephemerons also support instance-based finalization, in that simply
attaching an ephemeron to an object is a way of arranging that the
ephemeron will be notified when there exist no other references to an
object than from ephemerons.
When you have finished developing your application, you need to extract
it from the VisualWorks development environment and prepare it to run
as a stand-alone application. This process is called deploying an
application.
The basic activities in deploying an application are:
• Preparing the application to run stand-alone, by removing
dependencies on development environment
• Organizing code into deployment parcels
• Building the deployment image
To simplify the process of preparing an image and installing it on a
customer’s system, VisualWorks includes:
• Runtime Packager, a utility for creating a deployment image from a
development image.
• An application installation framework is provided as a goodie. This
framework was used in the VisualWorks installation program. To
examine the framework, load the VisualWorks Installer parcel.
288 VisualWorks
Choosing a Delivery Strategy
Parcels
Parcels, files that contain application objects, can be rapidly loaded into
an image without the use of a compiler. This makes parcels
advantageous in large, complex applications. Parcels allow you to:
• Deliver a very small base image
• Incrementally update your application without supplying a new image
• Customize your application at run time
• Tailor the memory footprint of your running application
Combined Deployment
Even though loading parcels is fast, loading the image is faster. Loading
your entire application from parcels into a minimal image might not be
optimal for a variety of reasons.
A combined use of the image and parcels might have the core application
code saved in the image, at least up to the first window. From that
window, additional code can be loaded from parcels as needed. Seldom
used code might never be loaded by some users.
VisualWorks Installer
As an aid to installation, you can use the VisualWorks installation
framework, which is provided as a goodie. Load and browse the
VWInstallerFramework and VWInstallerConfiguration parcels to use this
option.
290 VisualWorks
Running a Deployed Image
When a virtual machine executes, the default is to read an image file with
the same name. So, if you rename the executable to myApp (or
[Link]) and the image file to [Link], you can simply execute:
> myApp
with any required options.
292 VisualWorks
Preparing an Image for Deployment
294 VisualWorks
Preparing an Image for Deployment
The Transcript
The Transcript object is preserved in a deployment image, but is not
displayed as in the development image. Messages sent to Transcript
continue to process without errors but do not display themselves unless
you define a window to show the state of the Transcript.
Handling Errors
Your application is expected to catch all anticipated errors and to handle
them. Refer to Chapter 11, “Exception and Error Handling” for information
about error handling in VisualWorks.
For unhandled errors, Runtime Packager replaces calls to open a
NotifierView with calls to a RuntimeEmergencyNotifier. This simplified notifier
excludes tool support, such as the debugger, and simply notifies the user
that an unhandled exception has occurred, with a brief description of hte
error. It also writes a summary of the error and its stack to an error log file
(by default called [Link]).
Both the error handling class and the error log name are specified on the
Exceptions page of the Options step. You can create your own handling
procedures for unhandled exceptions and specify it on this page.
296 VisualWorks
Preparing an Image for Deployment
SystemEventInterest
atSystemEvent: #returnFromSnapshot
send: #start
to: anObject
To unregister the dependency that will send #start to anObject upon event
#returnFromSnapshot, use this one:
SystemEventInterest
removeDependencyOnSystemEvent: #returnFromSnapshot
selector: #start
receiver: anObject
To unregister all message-based system event dependencies for anObject,
for any selector or event, send:
SystemEventInterest removeAllDependenciesFor: anObject
298 VisualWorks
Preparing an Image for Deployment
300 VisualWorks
Preparing an Image for Deployment
CommandLineInterest
removeDependencyOnSystemEvent: #earlySystemInstallation
selector: #hookup
receiver: self
commandLineOption: '-hookup'
To unregister interest in -port, send this message:
CommandLineInterest
removeDependencyOnSystemEvent: #returnFromSnapshot
selector: #port:
receiver: anHTTPServer
commandLineOption: '-port'
To clear all message-based dependencies for anObject, send:
CommandLineInterest removeAllDependenciesFor: anObject
302 VisualWorks
Creating the Deployment Image
parcels
unparcelled code
development tools
Smalltalk base
304 VisualWorks
Creating the Deployment Image
A Short-cut Procedure
The basic procedure can be rather slow, and you don’t always need to
perform every step. The menu command File Package Runtime Image
creates a runtime image in one operation by automatically executing the
Scan for unreferenced items, Save loadable parcels, and the final Strip and save
steps.
You still need to set options appropriately, especially specifying how to
handle parcels. But, once you understand the whole process, know what
you do and do not need to do for your image, or have a parameters file
that specifies various features, this short-cut procedure can be a great
convenience.
Examples
The following short examples use the RuntimeExample application that is
loaded as part of the Runtime Packager parcel.
Building a Stand-alone Image
For simple applications you build the deployment image with all of the
application code directly in the image.
This is the simplest procedure. The only options that need to be set are
the Startup Class and Startup Method. Runtime Packager begins with the
resulting startup message and analyzes code the image to which code
must remain and which may be deleted.
1 Load and start Runtime Packager in the usual way, into a clean
image.
2 Do the Clean up image step. Undeclared and DependentFields should both
be clean.
3 Do the Set common options step. On the Basics page set the following:
Startup Class: [Link]
306 VisualWorks
Creating the Deployment Image
Clean Up Image
Objects can accumulate in a development image that are not needed for
runtime execution and would occupy storage needlessly. This step scans
for global objects that commonly arise in the development process.
The scan for referenced and unreferenced items detects unreferenced
globals appearing in the system name spaces.
Unreferenced globals that are either undeclared variables or non-Model
objects that have dependents cannot be detected in the scan of
referenced items. When this step is performed, inspectors are opened on
the contents of Undeclared and DependentsFields. If no suspicious contents
are found, you will be notified and no inspectors are opened.
If entries exist in the Undeclared dictionary, you will be prompted to remove
any items that are apparently unreferenced and which are also currently
bound to nil before opening the inspector. These entries can be left
behind when classes are removed from the system, for example.
Removing these entries should normally be harmless and will greatly
simplify analyzing the Undeclared items. However, there is no provision for
restoring entries deleted by this process, so the image should first be
saved if you are not sure that the entries are extraneous.
In most cases, undeclared variables represent some type of problem in
the development process and each entry should be investigated to
ensure that no problems are lurking in the application. If no references to
an entry can be found, the entry can be eliminated. Be especially careful
when removing items from DependentsFields if you do not understand why
they are there.
308 VisualWorks
Runtime Packager Process Details
If parcels are loaded that contain facilities used only in the development
image, such as the UIPainter and Store parcels, they should be unloaded
before beginning the packaging process. If you are unable to account for
entries in Undeclared after unloading these parcels, close the Runtime
Packager window, invoke garbage collection, and open Runtime
Packager again.
This step can be skipped if you do not want to eliminate the types of
global objects detected here.
Config files contain a list of parcel files to be loaded and also may
contain other command line options. If a file of the form
[Link] is found in the current directory, it is processed
as a config file before other options are processed.
Build headless image
Create an image that does not access the display. Refer to
Chapter 15, “Creating an Application without a GUI” for more
information.
Clear parcel search path
Clears the list of directories to be searched when loading parcels. If
this is not selected, the Settings values are preserved in the runtime
image.
Use three-step procedure
A three-step procedure is recommended for optimal runtime images.
The procedure will be used if this box is checked. Because of the
extra time required for three saves, the default is to create a slightly
less optimal image in a single step.
The three step save process does the following:
a Do Perm Save Image As..., then exit and restart.
b Do Collect All Garbage, snapshot, exit, restart (removes transient
objects in PermSpace).
c Snapshot one more time (compacts objects in PermSpace).
Skip default scan for unreferenced items
The menu item File Package Runtime Image normally performs a scan
for unreferenced items as part of the packaging process. To skip
doing the scan, select this otion.
310 VisualWorks
Runtime Packager Process Details
312 VisualWorks
Runtime Packager Process Details
Stripping Page
This page allows specification of options that control the final stripping
step. These options are:
Remove system organization
Remove the system organization and categories. This option will
reduce the size of the runtime image, but may conflict with some
services that require categories to be present. Defined categories are
replaced with empty category objects so that functions which expect
such objects to be present can operate without raising exceptions.
Package external interfaces
Prior to creating the stripped image, evaluate each instance of
CMacroDefinition and replace it with the resulting value.
314 VisualWorks
Runtime Packager Process Details
Prestrip Method
Prestrip Method names the method to which the pre-strip message is to
be sent. This method must be one the prestrip class can respond to.
If no message is to be sent, Prestrip Method and Prestrip Class should be
blank.
Name spaces do not appear with the list of classes and globals. There is
no provision for keeping or deleting a name space, and by default all
name spaces are kept.
The status of classes and methods can be changed by pressing the
buttons between the selection boxes. The meaning of the buttons is
mnemonic:
>> means move all selected items from the left to the right.
<< means move all selected items from the right to the left.
After items are moved, they become the selected items in the box to
which they are moved. Hence, you can easily undo an erroneous button
press by pressing the button for movement in the opposite direction.
Pop-up menus are provided in each selection box. These can be used to
select all items currently appearing in the box, clear all selections, look at
specific items, and scan for references. Two types of reference scan are
provided. The standard reference scan is provided by the Browser
classes and may miss some references that will be detected during the
more complete scan for referenced items in the next step. The extended
reference scan is more inclusive. It also allows you to filter out the items
that are not being kept in the runtime image, which is especially useful
after the scan for referenced items has been completed in the following
step.
When ENVY/Developer is present in the image, the top right selection list
box in the window can be toggled between VisualWorks categories,
ENVY/Developer applications, and ENVY/Developer subapplications
using the pop-up menu. When applications are shown in this selection
box, all classes in the application or its subapplications are shown below.
Otherwise, only classes immediately contained in the selected categories
or subapplications appear.
Classes that are dynamically loaded through the use of parcels should be
indicated as contingent or kept in the runtime image.
A menu option is provided for resetting classes and methods to their
default settings. Only classes and methods in currently selected
categories (or applications) will be affected. This permits a more selective
way to reset to default values than would be achieved by pressing TAKE
DEFAULTS in the main window.
316 VisualWorks
Runtime Packager Process Details
Pop-up Menus
The following pop-up menus are used to perform actions with respect to
applications, categories, classes, and methods shown in this step. These
pop-up menus can also be selected from the window's main menu.
Classes Menu
This menu is available as a pop-up menu for Deleted, Contingent, and
Kept classes. This menu can also be selected via the Classes entry in
the window's menu bar.
• Select all - Select all classes in the related selection list
• Clear all - Clear all selections in the related selection list
• Browse - Open a browser on the selected class
• References - Use the Extended References Browser to located
references to the selected class
Methods Menu
This menu is available as a pop-up menu for Deleted, Contingent, and
Kept methods. This menu can also be selected via the Methods entry in
the window's menu bar.
• Select all - select all methods in the selection list
• Select category - select methods in a chosen category
• Clear all - clear all selections in the selection list
• Browse - open a method browser on selected methods
• Implementors - browse all implementors of the chosen selector
• References - Use the Extended References Browser to located
references to the chosen selector
Defaults
By default, some classes are kept. These are kernel classes and they are
almost certainly needed to make a runtime image. For a complete list,
see the method defaultClassesKeptVW in class RuntimeBuilderItems.
Class RuntimeManager within Runtime Packager is needed for image start-
up and is by default also a kept class. RuntimeManagerStripper is a special
subclass of RuntimeManager used to complete the stripping operation and
is required. It is eliminated in the final runtime image.
Global objects are not kept by default, but the major system globals are
referenced in numerous places will be detected as referenced.
Classes that are not generally used in the runtime image are deleted by
default. These classes come from the Tools name space and related Tools
categories. For a complete list, see the method defaultClassesDeletedVW in
class RuntimeBuilderItems.
The EtTools application and its classes are deleted by default when
ENVY/Developer is present. Similarly, subapplication
EmLibraryManagement and its classes are also deleted by default. Both
EtTools and EmLibraryManagement are removed from the image by the
standard ENVY/Developer method for removing ENVY/Developer.
Indicating that their classes are deleted prevents spurious references
during the scan for unreferenced items.
When an image starts, ObjectMemory sends update:with:from: messages to
all its dependents. By default, the classes of all dependents of
ObjectMemory are kept. If you know that a dependent is not needed in the
runtime image, you can specify the class as deleted.
318 VisualWorks
Runtime Packager Process Details
itemsReferencedByClassSelector: aSymbol
Answer the collection of literals including symbols, variable bindings,
and classes referenced in the class method named by aSymbol. In
the scan, these literals replace entirely those found in the method
itself.
When these selectors are implemented as class methods, the answers
provided by them are used during the scan to include classes, methods,
and globals to be considered referenced and thus kept in the runtime
image. If an improper answer is returned by these selectors, a dialog is
used to alert you to the error.
To start scanning, click the Do This Step button. A window will open to show
you progress reports. Scanning a large image might take some time.
When the scan is complete, a dialog box opens summarizing the results
of the scan. You can see more detailed information by proceeding to the
next step.
If you choose to bypass this step, only classes, methods, and globals that
you have explicitly indicated as deleted will be removed from the runtime
image.
320 VisualWorks
Runtime Packager Process Details
To start this step, click Do This Step. The names of parcels saved are
written to the Transcript as the saves proceed. If a file is about to be
overwritten, you will be prompted for permission to overwrite the file
before proceeding.
If there are no runtime loadable parcels, this step can be skipped. If no
parcels have been defined as runtime loadable, you will be notified if this
step is attempted.
322 VisualWorks
Runtime Packager Process Details
Note that image startup and shutdown processing is not included within
the scope of a test. Deleting classes or methods used in startup or
shutdown is a common source of difficulty in creating a viable runtime
image. In most cases, dumps written to the error log are the best way to
debug startup or shutdown problems.
If you have extra windows open, for example a browser, you should close
or minimize them before beginning an application test. References to
classes and methods from all open windows are considered part of the
application being tested. Having extra windows open tends to result in
extraneous references and may cause you to include unneeded classes
and methods in the runtime image.
Buttons along the top of the window control activities during the
application test. These buttons are:
Save Image
In spite of all precautions to the contrary, it is possible that the image
could be corrupted during the test. If you don’t have a recently saved
image, click this button to save it now. Once the test starts, you do not
want to save the image.
Begin Test
This button begins the test process. All deleted classes and methods
are altered to allow the detection of any references to them. When
that process has been completed, the startup message, if any, is sent
and the application starts its execution in the normal way.
End Test
When you have completed application testing, pressing this button
will restore the image to its status prior to the beginning of the test. If
debugger windows are opened during the test, you might want to
press this button before debugging the problem.
Accept Dynamic References
As deleted classes and methods are referenced, they are reported in
the scrollable text area below. If you want to accept all such classes
and methods as items to be kept in the runtime image, press this
button.
Ignore Dynamic References
In some cases you may see references to items that are clearly not
part of your application. For example, you may see references to the
debugger if an error occurs. To ignore the dynamic references
appearing in the text area below, press this button. All dynamic
references displayed will be ignored. This button is active both during
and after the test. If pressed during the test, displayed dynamic
references revert to the status they had at the beginning of the test. If
pressed after the test, displayed dynamic references are simply
ignored.
References to classes and methods that would have been deleted in the
runtime image are shown in the scrollable text area as they occur. Only
the first reference to each item is shown. Once the references are
accepted or are ignored, the text area is cleared.
After the test ends, press OK to have all accepted dynamic references
included as kept items. You might want to go back and rerun the scan
step at this time to pick up other classes and methods that are now
reachable but just did not happen to get used in your test. If you press
Cancel, no changes are made to the kept items and the window is simply
closed.
Classes and methods that are potentially loadable through parcels do not
get special treatment in this step. The assumption is that loadable parcels
will be loaded into the image through command processing at start up or
through some equivalent process. Do not allow parcels to be loaded
during the test or you not be able to recover the source for methods
contained in such parcels, as would be case if runtime parcels into any
development image.
Policy Values
• Growth Increment - how much additional memory to allocate at a time
when growing memory
• Retry Decrement - how much less to ask for if memory is not available
when an attempt is made to grow memory
324 VisualWorks
Runtime Packager Process Details
in your runtime image, but you might want to use instances of the
launcher in your application. If so, you should close the launchers
manually and just say No here instead of closing all launchers
automatically.
3. You will be given one last chance to change your mind.
4. Parcels that are loadable at runtime are removed from the image.
Only the definitions are removed. Classes and methods defined in
these parcels are removed in a later step of the stripping process.
5. Subclasses of ExternalInterface are packaged for the runtime
environment. C macros are fully expanded.
6. The emergency notifier is installed. If you are using the default notifier
class RuntimeEmergencyNotifier, any errors after this point will cause a
dump file to be written. If the debuggers are stripped out this could be
the only way to debug a problem in this step.
7. ENVY/Developer is removed if it is present in the image.
8. Sources are discarded. That is, the image will no longer look for
sources or create changes entries. If ENVY/Developer is present, the
connection with the manager library is terminated.
9. A new emergency evaluator is installed. The evaluator is invoked
when you press Ctrl-Shift-Y. The replacement evaluator is a dialog
confirming that you want to quit now.
10. A series of mundane clean-ups are done. One of these is clearing the
Transcript. The transcript is written to the dump file on errors, and you
might want to place application error messages there even if the
transcript is not shown to the user.
11. If you requested deletion of the compiler, the default pop-up menu for
text fields that could contain code is replaced with the menu for
straight text (the compiler is needed to evaluate anything). A few
other menus that reference the compiler are not altered. If you select
DoIt from these menus, the request is ignored.
12. System and method categories are discarded if the remove system
organization and categories option was selected.
13. Methods, classes, and globals are deleted from the image. Copyright
notices in the method named copyright are always retained.
14. If selected, literals are merged based on value.
15. If selected, multiple instances of the same byte code string are
consolidated into a single instance referenced from multiple methods.
326 VisualWorks
Debugging a Deployed Image
328 VisualWorks
Customizing Detected References
RuntimeDumperFramework
RuntimeShortImageDumper
RuntimeImageDumper
itemsReferencedByClassSelector: aSymbol
Answer the collection of literals including symbols, variable bindings,
and classes referenced in the class method named by aSymbol. In the
scan, these literals replace entirely those found in the method itself.
If nil is answered by these methods, then the normal inferred references
are used.
Examples of the general pattern used by these customizing methods can
be found in class RuntimeManager.
Trouble Shooting
Workspace or Browser is Opened with the Application
Typically this is because you left the window open before stripping.
Perhaps it was minimized. Make sure all windows are closed and restrip
the image.
330 VisualWorks
Trouble Shooting
Key Concepts
The headlessness of an image is controlled by the sole instance of the
class HeadlessImage. This instance (HeadlessImage default) enables you to
create new images by saving them either in headless mode (with the
display system deactivated) or in “headful” mode (with an activated
display system). You typically develop your application in a headful
image, test it in a headless image, and then debug it in a headful image
that is created from the headless image. The HeadlessImage instance
records the image’s mode and can be queried for it.
The basic way to provide input to a headless image is through a startup
file. A startup file is a file that contains Smalltalk expressions in file-in
format. When a headless image is started, it reads the file and evaluates
the expressions. You typically use a startup file to start your application in
the headless image. Applications can also accept input through sockets,
file I/O, TTY interaction, and so on.
By default, output that would normally be displayed in the System
Transcript is saved to disk in a transcript file.
332 VisualWorks
Setting Up a Headless Image
the following:
HeadlessImage default transcriptFilename: '[Link]'
The default name is returned by the defaultTranscriptFilename class
method.
6 Create a headless image by selecting File Save Headless As... or by
evaluating an expression such as the following:
HeadlessImage default saveHeadless: 'headlessImageName'
This creates a new image named [Link] in which
HeadlessImage’s state is set to headless. Creating a headless image has
no effect on the current image.
Starting on Unix/Linux
Most of the Unix platforms have a headless engine. These engines
exclude the GUI and window management primitives, dynamically
loading them as required from a shared library. (The all-in-one, “headful”
engines are still provided.)
The headless engines are named in the vw<platform> format, as
usual. The GUI inclusive engines are named vw<platform>gui. To
start a headless image using a headless vm, simply invoke the virtual
machine with the image as usual, for example:
vwlinux86 [Link]
plus any necessary options.
Starting on Windows
On Windows platforms, you will want to suppress the splash screen and
sound, however, so use the -noherald command line option:
visual -noherald [Link]
On Windows systems, there are two console engines available:
[Link] and [Link]. Use the appropriate engine
to launch the headless image instead of [Link].
334 VisualWorks
Running an Application in Headless Mode
336 VisualWorks
Tips for Programming a Headless Application
printf: aString
<C: int printf(void _oopref *aString)>
^self externalAccessFailedWith: _errorCode
printfArgs: argArray
<C: int printf(...)>
^self externalAccessFailedWith: _errorCode
338 VisualWorks
Delivering a Headless Application
Numbers
VisualWorks provides several number types, each defined in its own
class. The basic types are:
Integer
The Integer class is an abstract superclass with two subclasses:
SmallInteger and LargeInteger. LargeInteger further has subclasses
LargePositiveInteger and LargeNegativeInteger. A SmallInteger is any
integer in the range 229-1 (536,870,911) to -229, inclusive. Large
integers are limited only by available memory. The system coerces
integers into the proper subclass transparently, so you rarely need to
pay attention to this issue.
Floating Point
The Float class creates instances of single-precision floating point
numbers between plus and minus 1038, with eight or nine digits of
precision. The Double class creates double-precision floating point
numbers between plus and minus 10307, with 14 to 15 digits of
precision. A floating-point number has a decimal point, at least one
digit before the decimal, and at least one digit after the decimal.
Because of the imprecise way floating point numbers are represented
in computer memory, mathematically equivalent representations of
floating point numbers may not turn out to be equivalent in
comparisons. So, for comparing numbers, avoid Float, and consider
using instances of Fraction or FixedPoint instead.
340 VisualWorks
Numbers
Fraction
An instance of Fraction is a number with an integral numerator and
denominator, separated by a division slash, as in 3/4. Fractions are
always reduced to lowest terms.
Fixed Point
A fixed-point number (an instance of FixedPoint) is useful for business
applications in which a fixed number of decimal places is required.
Their literal representation appends the character $s to the number
(e.g., 5.2s).
Three related classes, Random, Date, and Time, are described later in this
chapter.
Creating a Number
Numbers are created either by a literal numerical expression or by an
arithmetic operation. The kind (or class) of a number resulting from an
arithmetical operation depends on the numbers involved and the
operation.
The following are literal expressions for numbers:
3/5 fraction
^3 + 8 integer
Arithmetic Operations
Arithmetic operators are defined as messages for each class of number,
but each number class defines the standard operations and many more.
Use the system browser to examine the messages in the arithmetic
protocol for each number class for details:
+ addition
– subtraction
* multiplication
/ division
Comparing Numbers
Numeric comparison operators are defined as messages for each class
of number, but each number class defines the standard operations and
many more. These tests all return a Boolean value:
= equality
~= inequality
~~ non-identity
342 VisualWorks
Numbers
Note that, when comparing floating point numbers (class Float), certain
comparisons may give incorrect results. For example, equality and
identity (= and == ) may fail between two representations that are
mathematically equal. This is due to the way floating points are
represented by computers, and has nothing specific to do with Smalltak
or VisualWorks. For such comparisons, consider representing these
numbers as Fraction or FixedPoint numbers instead.
Mathematical Functions
VisualWorks number classes support a large number of advanced
mathematical functions. Browse the number classes for details about
available functions.
Factoring
Three messages are defined for Integer, providing factoring operations:
factorial factorial
Trigonometric Functions
Trigonmetrical functions are defined to either operate on or return the
value for an angle expressed in radians.
To convert an angle expressed in degrees to radians, send the
degreesToRadians message to the number:
344 VisualWorks
Numbers
|x|
x := 45 degreesToRadians.
^x sin
Conversely, to convert a result angle expressed in radians to degrees,
send the radiansToDegrees message:
|xy|
x := 45 degreesToRadians sin.
y := x arcSin radiansToDegrees.
^y
The functions supported are:
sin sine
cos cosine
tan tangent
arcSin ArcSine
arcCos ArcCosine
arcTan ArcTangent
Logarithmic Functions
Send the following unary messages to a number to perform logarithmic
functions:
Numeric Constants
There are three numeric constants defined in VisualWorks: zero, unity,
and pi. All three are returned by class methods for various numeric
classes.
Zero
The zero message is defined for all numeric classes, and returns the
appropriate value to ensure additive identity. The type of the zero value
varies; for example, Float returns 0.0 and Integer returns 0.
To get a zero of the same class as an existing number, first get the class
of that number by sending a class message to it and then send zero to the
resulting object.
|xyz|
x := Float zero.
y := Integer zero.
z := x class zero.
^x + y + z
Unity
The unity message is defined for all numeric classes, and returns the
appropriate value to ensure multiplicative identity. The type of one
returned varies; for example, Float returns 1.0 and Integer returns 1.
To get a one of the same class as an existing number, first get the class
of that number and then send unity to the resulting object.
|xyz|
x := Float unity.
y := Integer unity.
z := x class unity.
^x + y + z
Pi
The pi message is defined for Float or Double. Float returns a single-
precision version while Double returns a double-precision version.
To get a pi of the same class as an existing number, first get the class of
that number and then send pi to the resulting object.
|xyz|
x := Float pi.
y := Double pi.
z := x class pi.
^x + y + z
346 VisualWorks
Random Numbers
Random Numbers
A pseudo-random number can be generated by an instance of Random.
This object is a kind of stream, so the next message gets the next number
in the sequence.
A random stream returns a Double value, generally between 0 and 1 but
dependent on the seed value.
| randomStream x |
randomStream := Random new.
x := randomStream next.
^x
The seed: message changes the seed value, allowing you to force a
specific sequence. This message is sent to an instance of Random, and
restarts the sequence:
| randomStream x |
randomStream := Random new seed: 4.
x := randomStream next.
^x
The new message invokes the DefaultRandomGenerator, which is set to
MinimumStandardRandom by default. For backward compatibility, the
previous creation protocol and behavior is supported by FastRandom. You
can easily subclass Random to implement your own generator, and make
it the default if you wish.
Dates
Dates are supported in VisualWorks as instances of the class Date.
Creating a Date
There are a variety of messages for creating a date. Browse the class
methods defined instance creation protocol of Date for the complete list. We
will describe a few methods here.
To create a date for today’s date, send a today message to the Date class.
| date |
date := Date today.
^date
It is often useful to create a date from a string, which can be done by
sending a readFromString: message to Date. The argument is a string
containing the month, day, and year in any of several formats. The year is
always last. The month can be either a number (1 through 12) or the
unique first letters of the name (case is irrelevant). The month, day, and
year can be separated by a space, comma, hyphen, slash, period, or
nothing:
Date readFromString: 'January 31, 1994'
Date readFromString: '31 January 1994'
Date readFromString: '1/31/94'
Date readFromString: '1.31.1994'
Date readFromString: '1-31-1994'
Date readFromString: '31JAN94'
You can create a date by specifying the day, month and year. To specify
each by a number, send a newDay:monthNumber:year: message to the Date
class. Alternatively, specify the month by name, send a
newDay:month:year: message to Date. The month argument is the unique
first letters of a month name expressed as a Symbol:
| date1 date2 |
date1 := Date
newDay: 31
monthNumber: 1
year: 1994.
date2 := Date
newDay: 31
month: #Jan
year: 1994.
^date1 = date2
Note that if a two-digit year is specified, the year is given in the current
century, so
Date newDay: 2 month: 'jan' year: 52
Returns 1952 before the year 2000, and 2057 after 2000. To create a Date
for a year prior to 1000, use newDay:year:, for example:
Date newDay: 136 year: 52
in which the number of days is specified from the start of the year.
348 VisualWorks
Dates
Comparing Dates
The usual numerical comparison operations can be performed on dates:
= equality
~= inequality
Formatting a Date
A date can describe itself in a string having a variety of formats. The
printFormat: message takes as its argument an array containing six
elements. The six elements are interpreted as follows:
• Day’s position in the string (1, 2, or 3)
• Month’s position in the string (1, 2, or 3)
• Year’s position in the string (1, 2, or 3)
• The separator character
• Month’s format: 1 (numeric), 2 (abbreviation), or 3 (full name)
• Year’s format: 1 (with century) or 2 (without century)
To format a date string, send a printFormat: message to the date with a
six-element array as argument specifying the formats:
| date |
date := Date today.
^date printFormat: #(2 1 3 $- 3 1)
350 VisualWorks
Times
Times
VisualWorks provides the class Time to represent times. A Time consists
of some number of hours, minutes, and seconds, specified relative to
midnight. Time calculation is based on a microsecond clock in the virtual
machine.
Note: On windows, the default time access function, while very fast
and high-resolution, tends to drift from wall time over a period of
hours. For an application that must run for long periods, and is very
time sensitive, this can cause problems. In this situation, start
VisualWorks using the -walltime command line option (refer to
“Virtual Machine Command Line Options” for a description).
Creating a Time
There are several methods for creating instances of Time. Browse the
class methods in the Time instance creation protocol for details and the
complete set.
To create a time to represent the current time, send a now message to the
Time class:
| time |
time := Time now.
^time
You can create a time from a string representation by sending a
readFromString: message to Time. The argument is a string containing the
hours, minutes, and seconds, separated by colons. The minutes and/or
seconds can be omitted. The “am/pm” designation can be omitted (“am”
is the default) and can be in upper- or lowercase.
| times |
times := OrderedCollection new.
times
add: (Time readFromString: '3:47:26 pm');
add: (Time readFromString: '03:47');
add: (Time readFromString: '::26 PM').
^times
In computations involving times on different dates, it is sometimes useful
to represent each time as a number of seconds since midnight. At the
end of the computation, you can convert the number of seconds back into
352 VisualWorks
Times
TimeZone setDefaultTimeZone:
(TimeZone timeDifference: -5
DST: 1
start: 2
end: 2
from: 97 "on April 7"
to: 304 "until October 31"
startDay: #Sunday).
By default, the time zone is set for the Pacific time zone with daylight
savings time. You need to set these to appropriate values for your
location. The Time Zones page of the System Settings dialog (System
Settings) provides a set of sample expressions for various regions.
When properly set, the reference time zone returns the actual time zone,
and so should be used by application code that needs to know the time
zone:
timeZone := TimeZone reference.
For backwards compatibility, TimeZone keeps both a default time zone and
a reference time zone in the class variables DefaultTimeZone and
ReferenceTimeZone, respectively. There is no longer a distinction between
these.
354 VisualWorks
17
Collections
Keyed
Y N
Duplicates
Integer key allowed
Y N Y N
Sorted
Array
Y N
OrderedCollection
LinkedList
SortedCollection
Set
A Set is about as close to a generic collection as you can get. No index.
No sorting. It does discard duplicates, which is often useful. The fact that
an instance of Set has only one special capability should not distract you
from the fact that the generic behavior it inherits, as described in later
sections of this chapter, includes powerful mechanisms for manipulating
elements of a data set.
An IdentitySet is identical in all respects, except that it uses == for
comparisons instead of =.
356 VisualWorks
Choosing the Appropriate Class
Bag
An instance of Bag is like a Set, except that it counts the duplicate. For
each element in a Bag there is also a tally of the occurrences of that
object. If each character in the word collection were an element in a Bag,
for example, the tally for the element $c would be 2. Bag does not create a
new element for a duplicate, but increments the counter the item.
Array
Array allows you to maintain relative positions of elements, via an integer
index. In our collection example, $e can be identified by its external key,
the integer 5. (In a Set or a Bag, by contrast, the position of $e is
unpredictable.) As another example, if a customer name were to be
stored as a collection of three elements—first, middle, and last names—
it would make sense to use an Array rather than a Set because the
relative positions of the elements must be preserved.
A RunArray provides efficient storage for situations in which a value is
repeated consecutively over long stretches of an array. For example, the
font information for a block of text is a likely candidate—a roman font
would be used for many sequences of elements in the array (letters in the
text), with occasional bursts of italic, bold, etc. Although RunArray
responds to the same messages as Array, its internal representation
avoids waste by storing an element only if it differs from the preceding
element, along with a tally of that element’s repetitions.
A ByteArray provides space-efficient storage for bytes. Its elements are
restricted to the set of SmallIntegers from 0 to 255. WordArray is for
manipulating 16-bit words; its elements can be integers from 0 to 65535.
Interval
An Interval is a finite arithmetic progression, such as the series 2 4 6 8. It is
typically used to control an iterative loop, as described in Chapter 5,
“Control Structures.”
OrderedCollection
An OrderedCollection, like an Array, has an integer index and accepts any
object as an element. Unlike Array, however, an OrderedCollection permits
elements to be added and removed freely. It is frequently used as a stack
(the last element in is the first one removed) or a queue (first in, first out).
However, its uses extend farther because there are so many situations in
which ordering must be preserved as an arbitrary number of elements
are added.
SortedCollection
When elements are not added in the desired order, sorting is required.
SortedCollection provides that extra capability. By default, elements are
sorted in ascending order. You can override this default by specifying an
alternative sort algorithm enclosed in a block. For example, the
expression:
SortedCollection sortBlock: [:x :y | x >= y]
creates a new collection whose elements will be sorted in descending
order.
LinkedList
As its name suggests, a LinkedList is a collection in which each element
points to the next element. An OrderedCollection can accomplish the same
thing, but is less efficient in circumstances involving large numbers of
additions and deletions. For example, the ProcessorScheduler class makes
use of LinkedList to track the highly dynamic list of processes. LinkedList
achieves its efficiency in a way that prohibits its elements from belonging
to other collections at the same time.
Dictionary
The Dictionary class, instead of imposing an integer index on each
element, permits any object to be the external key. The result, as in the
familiar Webster’s dictionary, is a collection of key-value pairs. For
example, an element might consist of the word ‘object’ with the associated
definition ‘something solid that can be seen or touched’. Thus, each
element in a Dictionary is typically an instance of Association, which is a
key-value pair. The nil object is specifically excluded as a valid element.
An IdentityDictionary is similar, except that it uses == for comparisons
instead of =. That is, the values in an IdentityDictionary are expected to be
literals or other unique objects that can be compared with the more
efficient identity operator ( == )
358 VisualWorks
Creating a Collection
Creating a Collection
Typically, you create an empty collection, and then add elements to it. All
collections respond to the new message, as shown here for List.
| list |
list := List new.
| array1 array2 |
array1 := #( 'Leonardo' 'Michelangelo' 'Donatello' 'Raphael' ).
array2 := #( 1 2 3 4 )
Notice the use of # to indicate that a literal is being created.
Sometimes a new collection needs to be created from an existing
collection. For example, a nongrowing array might need to be expanded
to accommodate more elements. Or a dictionary’s keys might be placed
in a list for sorting.
Send a withAll: message to the desired collection class, with an
expression yielding the elements of the old collection, for example:
List withAll: Smalltalk keys
Adding Elements
Different kinds of collections add elements in different ways. Most
collections will add an element when sent an add: message with an
element to add. Arrays are the exception, since they are restricted to the
number of elements with which they are created. A Dictionary always
adds a key-value pair.
Because the elements of a Set are each unique, adding an element that
already exists in the set results in no change; duplicates are omitted. A
Bag, on the other hand, adds duplicates without limit.
By default, a List adds new elements to the end of the collection. You can
also position the additional element at the beginning of the collection,
before a particular element, or before a particular index. (A Set and a
Dictionary do not keep their elements in an externally visible order, so the
notion of inserting a new element does not apply.)
360 VisualWorks
Adding Elements
| list |
list := List new.
| list |
list := List new.
totalElements := 0.
sizes do: [ :sz | totalElements := totalElements + sz].
^totalElements
Expanding an Array
Although an Array can contain only the number of elements with which it
was created, you can expand an array by creating a copy that has a new
element appended to it. The copy can then be substituted for the original.
To create the copy, send a copyWith: message to the Array. The argument
is the object that is to be appended to the end of the new array.
362 VisualWorks
Removing Elements
| array copy |
array := #( 1 2 3 4 5 6 7 8 9 ).
Removing Elements
The basic method for removing an object from a collection is to send a
remove: message to the collection, with the object to be removed as
argument:
| list |
list := List withAll: ColorValue constantNames.
Removing a Subcollection
The removeAll: message allows you to remove all memebers of one
collection from a target collection. Send removeAll: to the collection from
which you want elements removed. The argument is a collection
containing the elements to be removed.
| list |
list := List withAll: ColorValue constantNames.
| list |
list := List new: 25.
1 to: 25 do: [ :i | list add: i].
364 VisualWorks
Removing Elements
| list |
list := List withAll: ColorValue constantNames.
Replacing Elements
Replacing elements in a collection is useful when the collection has
sufficient structure so that its elements have a position. Indexed
collections, such as List and Array, have the right structure, as do keyed
collections, such as Dictionary. Unordered collections, such as a Set, do
not support replacing of elements, because there is no corresponding
notion of a location at which to make the replacement.
list atAllPut: 0.
^list
366 VisualWorks
Replacing Elements
Replacing a Subcollection
Sequenced collections, such as List, Array, and OrderedCollection, allow
replacing an interval of objects with objects from another sequenced
collection by sending a replaceFrom:to:with:startingAt: message to the
collection. The first and second arguments are index numbers identifying
the replacement range. The with: argument is a collection containing the
new elements. The startingAt: argument is the index number in the new
collection at which to begin copying the replacement elements.
| mainList replacements |
mainList := #( 1 2 3 4 5 6 7 8 9 ).
replacements := #( 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 ).
mainList
replaceFrom: 1
to: mainList size
with: replacements
startingAt: 7.
^mainList
Copying Elements
A collection, like any other object, can provide a copy of itself in response
to being sent a copy message. The result is a new object which is a
complete copy of the original.
| dict1 dict2 |
dict1 := Dictionary new.
dict1 at: #Leader put: 'Leonardo';
at: #Member1 put: 'Michelangelo';
at: #Member2 put: 'Donatello';
at: #Member3 put: 'Raphael'.
Copying a Subcollection
For sequenced collections, such as List, Array, and OrderedCollection, send
a copyFrom:to: message to copy a segment of the collection. The first
argument is the starting index of the range you want to copy, and the
second argument is the ending index.
368 VisualWorks
Testing Collections
| list copy |
list := List new.
1 to: 10 do: [ :number | list add: number ].
^set1 - set2
Testing Collections
It is useful to be able to test collections for a variety of properties. The
following sections describe a number of useful tests. For others, browse
the collection classes.
This example shows that a copy is equal, but a copy with one changed
element is not equal.
| list1 list2 test1 test2 |
list1 := List withAll: ColorValue constantNames.
list2 := list1 copy.
list isEmpty
ifFalse: [^list first]
370 VisualWorks
Retrieving Elements
Retrieving Elements
Indexed and keyed collections are useful for storing objects that can then
be retrieved by index or key. The following sections describe methods for
retrieving objects from a collection.
^list at: 1
To search backward from the end, send a lastIndexOf: message. The index
of the last occurrence is returned, or zero if none exists. The returned
index is relative to the beginning of the collection.
| list found1 found2 found3 |
list := List withAll: #( #red #green #blue #red #yellow #blue).
372 VisualWorks
Retrieving Elements
Sorting a Collection
Sorted collections can rearrange themselves either in ascending order or
according to a specified sort criterion. A List has a simplified form of the
sorting messages.
The sort messages assume that the elements respond to < and =
messages, which are used to compare elements during the sorting.
Sort criteria are specified in a block containing the test for determining
whether one element comes before another. The block is given two
elements to compare, and is expected to answer true when the first
element should precede the second element.
Arbitrary collections are sorted by first being converted to an instance of
SortedCollection.
374 VisualWorks
Converting Collection Types
The following are a few of the conversion methods. Browse the Collection
class converting protocol for additional methods.
376 VisualWorks
Looping through the Elements (Iterating)
To loop on the index or key, send a keysDo: message to the collection. The
argument is a block that performs a series of operations on each
element. The block is expected to declare one argument variable to hold
the element to be processed.
To loop on the collection and process using both the key or index and the
value, send a keysAndValuesDo: message to the collection. The argument
is a two-argument block that performs a series of operations on the key
and associated value for each element.
| dict randomGenerator gc randomX randomY colorValue |
randomGenerator := Random new.
gc := (ExamplesBrowser prepareScratchWindowOfSize: 300@400)
graphicsContext.
^dict
378 VisualWorks
18
Working With Graphics and Colors
Examples Browser
Many examples in this chapter use the Examples Browser, which is
provided in the ExamplesBrowser parcel in the examples/ directory. It is
also loaded with the Help system.
Coordinate System
Graphics in VisualWorks are represented as points in a two-dimensional
rectangular coordinate system, with x coordinates increasing from left to
right on the graphic plane and y coordinates increasing from top to
bottom. Numbering starts from zero.
0 1 2 3 4
x
1
2
3
y
Coordinate system
Points
An x-y coordinate pair is represented as an instance of Point. The
@ message creates a Point, as in this example which creates a point with
an x-value of 100 and a y-value of 250. The spaces before and after the
binary selector (@) are optional:
100 @ 250
You can also create a point by specifying polar coordinates. The following
example creates a Point whose coordinates lie on a circle of radius 100 at
45 degrees:
Point r: 100 theta: 45 degreesToRadians
Two constants are available: Point zero returns 0@0, and Point unity returns
1@1.
380 VisualWorks
The VisualWorks Graphics Environment
A Point can perform comparison and arithmetic functions. So, you can
test for equality, and for less than and greater than relations. You can add
two points, and add (or subtract) a scalar value to a Point, to increase
(decrease) both x and y by scalar amount. For other operations, browse
the Point class using the System Browser.
Graphical Objects
Graphical objects are drawn and positioned by specifying points in the
coordinate system. VisualWorks uses several generic geometric shapes
as well as bitmap images for displaying graphics.
The graphic objects supported by the imaging model are texts, lines,
polylines, splines, Bezier curves, arcs, circles, rectangles and graphical
images. Display surfaces themselves are graphic objects, so you can
display one window in another.
Text Objects
Texts are treated as graphical objects in many contexts, particularly
instances of Text and ComposedText. These are described in detail in
Chapter 19, “Working with Text.”
Lines, Polygons, and Curves
A line segment connects two points, named start and end. A polyline
connects three or more points (its collection of vertices) as a series of line
segments, and is closed between the start and end points. A polygon is a
polyline that is filled rather than stroked. (A point is inside the polyline if
an infinite ray originating from the point crosses the polyline an odd
number of times. If the polyline is not closed, it is implicitly closed before
the even/odd rule is applied.) Rectangles are treated specially.
An arc is a curved line defined by three elements of information:
• The smallest rectangle that can contain the ellipse of which the arc is
a segment (adjusted for line width).
• The angle at which the arc begins, measured in degrees clockwise
from the 3 o’clock position (or counterclockwise for negative values).
• The angle traversed by the arc, known as the sweep angle. The
sweep angle is measured from the starting angle (not necessarily the
3 o’clock position) and proceeds clockwise for positive values and
counterclockwise for negative values.
Bitmap Images
An Image is a graphic object composed of a rectangular array of pixels.
Image employs a bitmap to represent its pixel colors or coverages.
A very simple Image can be constructed by manipulating the bits in the
map directly, but this is unwieldy for complicated pictures. More typically,
a scanner or a drawing tool is used to create the desired arrangement of
pixels. An Image is then captured from the on-screen representation or
from the bitmap.
An Image is stored in Smalltalk memory, so it is saved with the Smalltalk
image. For that reason, a graphical image can be used as a storage
device for display objects like Pixmaps and Masks.
An Image can be either color-based or coverage-based, depending on its
palette.
Common uses of images in an application are for cursors and icons.
Display Surfaces
Graphic operations in Smalltalk display graphics on two-dimensional
graphic media. All current graphic media are subclasses of the abstract
class DisplaySurface, which represents host graphic media related to video
display screens.
There are three types of display surface: Window, Pixmap, and Mask. While
a Window is used to display graphic objects on-screen, Pixmaps and Masks
are used for manipulating graphics. All three types of display surface
employ a GraphicsContext as an intermediary between the surface and the
objects to be displayed.
Graphics Context
Every display surface uses an instance of GraphicsContext to manage
graphic parameters such as line width, tiling phase, and default font.
Displaying operations are performed not by the display surface directly,
but by its GraphicsContext.
Similarly, messages for modifying graphic parameters such as line width
must be addressed to the appropriate GraphicsContext. That object applies
the relevant parameters and then displays the object on the surface.
A display surface does not store a graphics context, so it cannot be
accessed by an accessor. Instead, you need to get a display surface’s
graphic context any time a change is made. To get the graphics context,
send the message graphicsContext to the display surface. This is done
repeatedly in the examples.
382 VisualWorks
The VisualWorks Graphics Environment
Cursors
The displayed cursor is controlled by class Cursor. Use this class to create
a new cursor from an image and a mask, and to substitute cursors.
Cursors can be bitmaps up to 32x32 bits (except of Mac OS 9.x which is
restricted to 16x16).
For example, the example class CursorExample defines images to create a
town crier cursor. To create the cursor, evaluate:
townCrier := Cursor image: CursorExample townCrierForCursor
mask: CursorExample townCrier
hotSpot: 1@1 name: #myCursor.
To get the current cursor, send a currentCursor message to Cursor. To set
the cursor, send currentCursor: with the cursor as argument. Typically you
change the cursor while some process is occuring, and then set it back:
Cursor currentCursor: newCursor.
(Delay forSeconds: 10 ) wait.
Cursor currentCursor: heldCursor
For a better example, see CustomViewExample.
On Windows platforms, the VM substitutes platform cursors for the origin,
top left, bottom right, corner, execute, and wait cursors if not others, even
if your application cursors are more appropriate. You can turn off this
substituion by evaluating:
ScheduledControllers useHostCursors: false.
To restore substitution of host cursors, set this to true.
384 VisualWorks
Displaying a Geometric Object
messages are available for other shapes, such as arcs, polygons, and
rectangles. Browse the GraphicsContext class displaying protocol instance
methods to see the complete set.
| gc |
gc := ([Link]
prepareScratchWindow) graphicsContext.
| gc line scaleFactor |
gc := ([Link]
prepareScratchWindow) graphicsContext.
scaleFactor := 10@1.
"Black rectangle"
rect1 := 100@100 extent: 200@200.
rect1 asFiller displayOn: gc.
"Gray rectangle"
border := 3.
rect2 := (rect1 origin + border) corner: (rect1 corner - border).
rect2 asFiller displayOn: (gc paint: ColorValue gray).
Shifting Graphics
Sometimes it’s convenient to draw a polyline, polygon or rectangle as if its
origin point were at 0@0, then position the object elsewhere relative to
the display surface. For that reason, a variant of the displaying messages
allows you to specify the point at which the object’s origin is to be
positioned.
The variants are as follows:
gc displayPolyline: pointCollection at: aPoint
gc displayRectangle: aRectangle at: aPoint
gc displayRectangularBorder: aRectangle at: aPoint
gc displayPolygon: pointCollection at: aPoint
386 VisualWorks
Displaying a Geometric Object
displayOn: aGraphicsContext
self model isNil ifTrue: [^self].
388 VisualWorks
Displaying a Geometric Object
Drawing Objects
A line segment connects two points, named start and end. A polyline
connects three or more points (its collection of vertices) as a series of line
segments, and is closed between the start and end points. A polygon is a
polyline that is filled rather than stroked. (A point is inside the polyline if
an infinite ray originating from the point crosses the polyline an odd
number of times. If the polyline is not closed, it is implicitly closed before
the even/odd rule is applied.) Rectangles are treated specially.
390 VisualWorks
Drawing Objects
Lines
| gc line scaleFactor |
gc := ([Link]
prepareScratchWindow) graphicsContext.
scaleFactor := 10@1.
392 VisualWorks
Drawing Objects
Rectangles
Rectangles are used in a variety of graphic operations, from setting the
size of a window to specifying the bounding box of an ellipse, as well as
simply to create a rectangular graphic.
Creating a Rectangle
There are several ways to create a rectangle, accommodating a variety of
contexts. The most common methods are to send an extent: or corner:
message to an origin (top left) point. Both of the following expressions
create a rectangle 100 pixels wide, 250 pixels high, with its origin at
50@50:
50@50 extent: 100@250
50@50 corner: 150@300
The extent: message specifies the rectangle by its size, setting the x and y
distance from the starting point. The corner: message, on the other hand,
specifies the absolute corner position.
100
50@50
250
150@300
Creating a rectangle
394 VisualWorks
Drawing Objects
scaledBy: aScalarOrPoint
expandedBy: aScalarPointOrRectangle
insetBy: aScalarPointOrRectangle
insetOriginBy: point1 cornerBy: point2
intersect: aRectangle
merge: aRectangle
areasOutside: aRectangle
moveBy: aPoint
moveTo: aPoint
translatedBy: aScalarOrPoint
align: point1 with: point2
amountToTranslateWithin: aRectangle
• The smallest rectangle that can contain the ellipse of which the arc is
a segment (adjusted for line width).
• The angle at which the arc begins, measured in degrees clockwise
from the 3 o’clock position (or counterclockwise for negative values).
• The angle traversed by the arc, known as the sweep angle. The
sweep angle is measured from the starting angle (not necessarily the
3 o’clock position) and proceeds clockwise for positive values and
counterclockwise for negative values.
A complete ellipse is an arc with a sweep angle of 360 degrees. A circle
is an arc with a square bounding box and a sweep angle of 360 degrees.
A wedge is a filled arc (or circle or ellipse). If the arc does not describe a
closed ellipse, the ends of the arc are connected to the center of the
ellipse to define the filled region. The common case of a filled circle is
referred to as a dot, and is defined by a diameter.
Drawing Circles
A circle is created by specifying its center point and radius.
| gc circle |
gc := ([Link]
prepareScratchWindow) graphicsContext.
396 VisualWorks
Drawing Objects
| gc random points |
gc := ([Link]
prepareScratchWindow) graphicsContext.
random := Random new.
points := OrderedCollection new.
| gc ellipse |
gc := ([Link]
prepareScratchWindow) graphicsContext.
"Red ellipse"
ellipse := EllipticalArc
boundingBox: (150@175 extent: 100@50)
startAngle: 0
sweepAngle: 360.
ellipse asFiller displayOn: (gc paint: ColorValue red)
Drawing Arcs
An arc is created in the same way as a full ellipse, except that the
beginning and sweep angles specify only a portion of the full 360
degrees.
Use the same technique as for displaying a full ellipse, but the startAngle
argument is the angle at which the arc or wedge begins, measured in
degrees clockwise from the 3 o’clock position. The sweepAngle argument
is the number of degrees spanned by the arc, measured clockwise from
the starting angle.
| gc arc box |
gc := ([Link]
prepareScratchWindow) graphicsContext.
box := 150@100 extent: 100@200.
398 VisualWorks
Drawing Objects
"Red arc"
arc := EllipticalArc
boundingBox: box
startAngle: 270
sweepAngle: 90.
arc asFiller displayOn: (gc paint: ColorValue red)
Curved Lines
bezier := Bezier
start: (points at: 1)
end: (points at: 2)
controlPoint1: (points at: 3)
controlPoint2: (points at: 4).
bezier asStroker displayOn: gc.
Splines and Bezier curves support comparison, intersection testing,
scaling, and transforming. A Spline can also be asked whether it folds
back on itself (isCyclic).
400 VisualWorks
Drawing Objects
Line Properties
Line properties, such as thickness and endcaps, are controlled by the
graphics context rather than by the graphical object itself.
Changing the Line Thickness
By default, lines, arcs, and polygons are drawn with a one-pixel line width.
You can increase the thickness of a line by setting the thickness in pixels.
Extra thickness is spread evenly on both sides of the actual line, so a
horizontal line that is 20 pixels thick has 10 pixels above the line and 10
pixels below.
To set the line width, send a lineWidth: message to the graphics context of
the display surface. The argument is an integer indicating the number of
pixels of thickness.
| gc rect |
gc := ([Link]
prepareScratchWindow) graphicsContext.
rect := 10@10 extent: 30@30.
capButt
capProjecting
capRound
By default, lines and arcs are drawn with butt ends, which means each
end stops abruptly at the specified endpoint. When two thick lines share
an endpoint, butt ends produce a notched joint. Changing the cap style to
projecting fixes this by extending each end of the line by half of its
thickness. Another solution is to use round ends, which extend the ends
in a semicircle.
To change the endcap, send a capStyle: message to the graphics context.
The argument is derived by sending a capButt, capProjecting, or capRound
message to the GraphicsContext class.
| gc |
gc := ([Link]
prepareScratchWindow) graphicsContext.
gc lineWidth: 20.
402 VisualWorks
Graphical Images
joinMiter
joinBevel
joinRound
"Bevel joins"
gc joinStyle: GraphicsContext joinBevel.
gc displayPolyline: (Array with: 100@300 with: 200@150 with: 300@300).
"Round joins"
gc joinStyle: GraphicsContext joinRound.
gc displayPolyline: (Array with: 100@400 with: 200@250 with: 300@400).
Graphical Images
An Image is a graphic object composed of a rectangular array of pixels. It
is similar to a Pixmap and a Mask in many respects, the main differences
being:
• An Image is stored in Smalltalk memory, so it is saved with the
Smalltalk image. For that reason, a graphical image can be used as a
storage device for Pixmaps and Masks.
• An Image is not a display surface, so you can’t display other graphic
objects on it as a means of assembling the desired picture.
101
000
101
bitmap pixels
404 VisualWorks
Graphical Images
Paint the desired image in the scrollable pixel grid. The controls are pretty
standard for simple paint programs.
To make the graphic available to your application, click the Install button,
then specify your application class as the class into which to install the
graphic, and a method name for the graphic. This installs the graphic as a
resource, which you can access in the resources browser. The method is
installed as a class method in a resource protocol of a selected class.
Reading an Image from a File
To creating an Image from an external source, such as a file, send a
fromFile: message to the ImageReader class, with the name of the file as a
String. The result is an instance of the ImageReader subclass appropriate
for the image format, such as GIFImageReader. To get the image from the
image reader, send an image message to it. For example:
image := ( ImageReader fromFile: '..\bin\win\[Link]' ) image
This returns an Image instance.
It is often useful to store the image in a resource method. To do so, send
an imageFromFile:toClass:selector: message, with the file name, the target
class name, and the resource selector name as arguments:
ImageReader
imageFromFile: '[Link]'
toClass: DummyTree
selector: #herald
Capturing an Image from the Screen
You can also capture a graphic image from the screen, whether the
image is in a VisualWorks window or another program’s window.
The Image Editor allows you to select a relatively small area of the
screen. To use its capability, open an Image Editor and choose the
ImageCapture command. The cursor changes to a cross-hair. Move the
cursor to the top left of the selection area, press the mouse button, drag
the cursor to the lower-right corner, and release the mouse button. You
can then edit or install the resulting image.
To capture a larger area, or to invoke the screen capture capability from
your application, send a fromUser message to the Image class. The cursor
changes to a cross-hair, and you can select the area as above. You will
need to capture the image in a variable and process it as needed. This
example simply displays it in a scratch window:
| gc capturedImage |
gc := ([Link]
prepareScratchWindow) graphicsContext.
capturedImage := Image fromUser.
capturedImage displayOn: gc.
406 VisualWorks
Graphical Images
Displaying an Image
As with other visual objects, an image can display itself on a graphics
context. The image’s palette must match that of the graphics context:
coverage-based to display a Mask, color-based to display on a Window or
Pixmap.
To display an image positioned at the origin (0@0), send a displayOn:
message to the image with the graphics context as argument. To specify
a display position other than the default 0@0, send a displayOn:at:
message to the image with a Point as the second argument:
| gc logo |
gc := ([Link]
prepareScratchWindow) graphicsContext.
logo := LogoExample logo.
logo convertForGraphicsDevice: Screen default.
Caching an Image
A display surface such as a Pixmap usually can be displayed on another
display surface (such as a window) more quickly than an equivalent
Image. However, an Image has greater longevity because it does not
require a resource from the operating system, so it can survive when you
quit and restart VisualWorks.
A CachedImage combines the longevity of an Image with the displaying
speed of a display surface. Whenever its display surface is unavailable,
as when it has been destroyed by a save-and-restart operation, it is
recreated from the image automatically. This relieves your application
from having to recreate such display surfaces manually.
A CachedImage must be treated like a display surface, not an image. For
example, you cannot rotate a CachedImage.
Create a CachedImage by sending an on: message to the CachedImage
class, with the image as argument:
| gc logo |
gc := ([Link]
prepareScratchWindow) graphicsContext.
408 VisualWorks
Graphical Images
logo displayOn: gc
Changing Color by Numeric Value
To get the current color number of a pixel, send an atPoint: message to
the image. The argument is a Point indicating the coordinates of the pixel
in the image. The number that identifies the pixel color in the image’s
palette is returned.
To change the color of a pixel, send an atPoint:put: message to the image.
The first argument is the location of the pixel and the second argument is
a color number that exists in the image’s palette.
logo displayOn: gc
Masking an Image
You can mask out
a rectangular portion
of an image . . .
410 VisualWorks
Graphical Images
| ovalMask |
ovalMask := Mask extent: 66@66.
ovalMask graphicsContext
displayWedgeBoundedBy: ovalMask bounds
startAngle: 0
sweepAngle: 360.
^ ovalMask
You can also create a mask from an image by changing the palette of the
image to a coverage palette. Send a convertToCoverageWithOpaquePixel:
message to the image. The argument is an integer specifying the position
in the image palette of the color to make opaque, to allow the image to
show through.
Masking a Rectangular Area
For masking an image to a rectangular area, you do not need to create a
mask. Instead, you can simply specify the rectangle in a
completeContentsOfArea: message that you send to the display surface.
1 Create a display surface (Pixmap) containing the image by sending an
asRetainedMedium message to the image.
2 Send a completeContentsOfArea: message to the display surface, with a
rectangle as argument.
The copied portion is returned as an image, which can then be
displayed on the graphics context.
| gc logo subImage pixmap copyRect |
gc := ([Link]
prepareScratchWindow) graphicsContext.
logo := LogoExample logo magnifiedBy: 2@2.
gc copyArea: ovalMask
from: pixmap graphicsContext
sourceOffset: 0@0
destinationOffset: 10@10.
Modifying an Image
There are a variety of modifications you can make to images using
facilities provided in VisualWorks, such as rotating and expanding.
Expanding or Shrinking an Image
You can get a copy of an image that has been magnified or shrunken in
either the x dimension, the y dimension, or both.
To get an expanded copy of an image, send a magnifiedBy: message to
the image. The argument is a Point whose x value is multiplied by the
width of the image to derive the width of the expanded version; similarly,
the y value controls the height of the expanded version.
412 VisualWorks
Graphical Images
helpImage
displayOn: gc at: 10@10.
helpImage reflectedInX
displayOn: gc at: 60@10.
helpImage reflectedInY
displayOn: gc at: 10@60.
helpImage reflectedInX reflectedInY
displayOn: gc at: 60@60.
Rotating an Image
You can rotate an image about the z axis in 90-degree increments by
sending a rotatedByQuadrants: message to the image. The argument is an
integer indicating how many 90-degree rotations you want. A rotated copy
of the image is returned.
| gc helpImage rotatedImage |
gc := ([Link]
prepareScratchWindow) graphicsContext.
helpImage := VisualLauncher helpIcon image.
helpImage
displayOn: gc at: 10@10.
rotatedImage
displayOn: gc at: 60@10.
Each rotated copy uses time and memory resources. For a series of
rotations, you can reduce the resources required by reusing the same
scratch image for each subsequent copy, as shown in the variant. The
scratch image must be of the same size as the unrotated image, so this
technique works only when all images in the series are the same size.
Create a scratch image the same size as the image that is to be rotated
by sending a copyEmpty message to the original image. Then send a
rotateByQuadrants:to: message to the image to be copied. The first
argument is the number of quadrants to rotate the image. The second
argument is the scratch image.
| gc helpImage scratchImage |
gc := ([Link]
prepareScratchWindow) graphicsContext.
helpImage := ToolbarIconLibrary help20x20 image.
414 VisualWorks
Graphical Images
Overlaying Images
416 VisualWorks
Colors and Patterns
Pixel Coverage
A CoverageValue identifies the fraction of a pixel that is covered. Since a
pixel, by its nature, must be displayed in its entirety, only the values 0 and
1 are typically used. Fractional coverages can be specified, however, as
explained in the discussion of coverage palettes on page 416.
CoverageValue is the paint basis for Masks. An Image can also be coverage-
based, typically when it is used as a storage medium for a Mask, which
does not survive after the system is shut down.
A CoverageValue can be created by name or by value:
CoverageValue transparent
CoverageValue coverage: 0
CoverageValue opaque
CoverageValue coverage: 1
Creating a Color
ColorValue class methods provide simple protocol for creating instances
by either color constant name, RGB values, or HSB values.
Create by Color Name
Several color constants are defined by class method selectors for each
color name. To create a color, send the appropriate color message to
ColorValue class. For example, to create an instance of cyan, send the
cyan message to the ColorValue class:
| gc color |
gc := ([Link]
prepareScratchWindow) graphicsContext.
gc paint: color.
gc displayDotOfDiameter: 400 at: 200@200.
The following example displays all the predefined colors in a ray chart.
| gc endPoint colors |
gc := ([Link]
prepareScratchWindow) graphicsContext.
gc lineWidth: 7.
endPoint := 350@0.
colors do: [ :c |
endPoint := endPoint + (-10@12).
gc paint: (ColorValue perform: c).
gc displayLineFrom: 0@0 to: endPoint.
gc paint: ColorValue black.
c asString displayOn: gc at: endPoint + (0@8)]
418 VisualWorks
Colors and Patterns
| gc r x y |
gc := ([Link]
prepareScratchWindow) graphicsContext.
r := 50.
gc lineWidth: 2.
gc translation: 150@150.
0 to: 1 by: 0.005 do: [ :i |
x := (i * Float pi) cos * r.
y := (i * Float pi) sin * r / 2.
gc paint: (ColorValue hue: 0.0 saturation: 0.5 brightness: i).
gc displayLineFrom: x@y to: 0@-100 ].
gc translation: 200@200.
0 to: 1 by: 0.005 do: [ :i |
x := (i * Float pi) cos * r.
y := (i * Float pi) sin * r / 2.
gc paint: (ColorValue hue: 0.0 saturation: 0.75 brightness: i).
gc displayLineFrom: x@y to: 0@-100 ].
gc translation: 250@250.
0 to: 1 by: 0.005 do: [ :i |
x := (i * Float pi) cos * r.
y := (i * Float pi) sin * r / 2.
gc paint: (ColorValue hue: 0.0 saturation: 1.0 brightness: i).
gc displayLineFrom: x@y to: 0@-100 ]
Coloring a Graphical Object
420 VisualWorks
Colors and Patterns
| gc circle colors |
gc := ([Link]
prepareScratchWindow) graphicsContext.
circle := Circle center: 200@200 radius: 200.
colors := ColorValue constantNames.
Creating a Pattern
A Pattern is created by filling a space with a single graphic image that is
repeated in tiles. A pattern can be used in any situation that you can use
a solid color.
To create a pattern, send an asPattern message to the graphic image to
serve as the tile:
| gc tile |
gc := ([Link]
prepareScratchWindow) graphicsContext.
gc paint: tile.
gc displayRectangle: (50@50 extent: 300@300).
The graphic image is typically an Image subclass instance, but can also
be a window, Pixmap, or Mask.
Applying a Pattern
Patterns are applied in the same way as colors. Send a paint: message to
the graphics context of the display surface on which the object is to be
displayed. The argument is a pattern, or in the case of a Mask, a
coverage.
| gc tile |
tile := Pixmap extent: 10@10.
gc := tile graphicsContext.
"Tile background"
gc paint: ColorValue chartreuse.
gc displayRectangle: (0@0 extent: 10@10).
"Tile foreground"
gc paint: ColorValue red.
gc displayDotOfDiameter: 10 at: 4@4.
"Patterned circle"
gc := ([Link]
prepareScratchWindow) graphicsContext.
gc paint: tile asPattern.
gc displayDotOfDiameter: 400 at: 200@200.
Adjusting a Pattern’s Tile Phase
For some patterns, the placement of that first tile can be critical to the
pattern. By default, the first tile is placed with its upper left corner at the
origin of the display surface’s GraphicsContext.
To adjust the start location, send a tilePhase: message to the graphics
context of the display surface on which the patterned object is to be
displayed. The argument is a point that defines the origin of the first tile in
the pattern.
In the example, the tile phase is the same as the origin of the painted
object, which aligns the tiles with the top and left edges of the object.
| gc tile |
gc := ([Link]
prepareScratchWindow) graphicsContext.
tile := Image CincomLogo shrunkenBy: 4@4.
tile := tile asPattern.
gc paint: tile.
gc tilePhase: 50@50.
422 VisualWorks
Image Color Palettes
Coverage Palettes
A CoveragePalette is used by Masks and masking images, to specify levels
of transparency. It has a maxPixelValue, which determines the number of
levels of transparency. Usually, maxPixelValue is set to 1, because a pixel
can only be fully transparent (pixel value 0) or fully opaque (1).
However, you may want to allow for intermediate levels of translucence.
By specifying the maxPixelValue, you can create an image having any
number of coverage levels (currently, masks are restricted to two levels).
Color Palettes
A color palette can have either of two representations: fixed or mapped. A
FixedPalette breaks a pixel value into red, green, and blue fields, each of
which controls the intensity of that primary color. A MappedPalette stores a
table of colors, so each numeric pixel value can be associated with an
arbitrary color. A MonoMappedPalette is a MappedPalette that is specialized
for the case in which the palette contains only black and white.
Mapped palettes are most appropriate for images on color-mapped
display screens and for images that use a small number of colors. Fixed
palettes support true-color display screens that don’t use a hardware
color map. Such true-color screens typically support a large number of
colors. A mapped palette for a typical true-color screen, which has a
depth of 24, requires a color mapping table with more than 16 million
elements.
Creating a Color Palette
Different types of palettes are created in different ways.
To create a mapped palette, send a withColors: message to MappedPalette,
specifying an array of colors used to initialize the palette.
A fixed palette uses RGB values. Depending on the depth of the image,
one set of RGB values might occupy 8 bits, 24 bits, or 32 bits (or even
something in between). When you create a fixed palette, you must arm it
with the means to locate the red bits, the green bits, and the blue bits.
You do so by indicating the number of the bit that begins each RGB
0 0 1 0 1 0 1 1
424 VisualWorks
Image Color Palettes
426 VisualWorks
Color Rendering Policies
NearestPaint
NearestPaint simply chooses the nearest available paint from the screen’s
palette. On color screens, NearestPaint usually produces satisfactory
results and always gives the best performance of the three renderers.
On a limited palette, such as on a monochrome screen, the results can
be disappointing. For example, a magenta image on a chartreuse
background will result in a white rectangle, because both colors are
luminous enough to be converted to white by NearestPaint.
OrderedDither
OrderedDither employs a threshold array to synthesize unrecognized
colors by blending neighboring colors from the screen’s palette. This has
the effect of smoothing the transition from one palette color to the next in
a continuous tone. While the result is often more pleasing than with
NearestPaint, you pay a price in performance.
ErrorDiffusion
An ErrorDiffusion uses a more sophisticated blending algorithm. When it
makes a choice from the screen’s palette, it keeps track of how far off that
choice was from the requested color. When this error accumulates
sufficiently, the renderer uses the color on the other side of the threshold.
For example, suppose that a region of the image uses a red-brown color,
but the screen’s palette has only red and brown. An ErrorDiffusion may
supply red at first, but keeps track of the numeric difference between red
428 VisualWorks
Color Rendering Policies
430 VisualWorks
19
Working with Text
Characters
Character objects are instances of the class Character. As with all objects
in Smalltalk, and unlike many languages, characters are full blooded
objects, not primitive data types.
Creating Characters
Many characters can be represented by printable keyboard characters.
Instances of these characters can be created by preceding the desired
character with a dollar sign:
char := $C
432 VisualWorks
Strings
Comparing Characters
Characters can be compared using the usual binary comparison
operators defined for numbers: =, ==, <, >, ~=, and so on. Comparison is
performed based on the integer values of the characters, so
$C < $D
evaluates as True, but
$c < $D
evaluates as False.
Strings
A String in Smalltalk is a collection, or more specifically an array, of
characters. While protocol is defined at the level of the String class, a
string is actually represented as a platform-specific subclass of String.
Strings are the foundation of all text operations in VisualWorks, including
the text formatting and display operations described later in this chapter.
Creating a String
Most frequently a string is created by enclosing the desired characters in
single quotes:
| string |
string := 'This is a string.'.
^string
You can create an empty string by sending a new message to the String
class. This is equivalent to enclosing nothing between single quotes.
| emptyString |
emptyString := String new.
^emptyString
434 VisualWorks
Strings
^string
Some character sets contain single lowercase characters that become
multiple characters in their uppercase form. If you are working with such a
character set, your code should handle the results of asUppercase
accordingly.
Combining Strings
There are a variety of ways in which two or more strings can be combined
to form longer strings, or to perform replacements within a string.
The simplest operation is concatenation, which is performed by putting a
comma between the two string expressions, for example:
| firstName lastName fullName space |
firstName := 'Bill'.
lastName := 'Clinton'.
space := String with: Character space.
^formalList contents
436 VisualWorks
Strings
Comparing Strings
Unlike characters, strings are not compared by numerical value of their
characters. When comparing strings, case is ignored and alphabetical
order is used, unless the two strings have exactly the same letters in the
same order. In this latter case, numerical values are used to differentiate
uppercase and lowercase letters.
Testing for Equality and Identity
Two strings are equal when both have the same number of characters,
and both have the same characters in the same order.
To test for equality or inequality, send an = or ~= (not equal) message to
one string with another string as argument:
| str1 str2 |
str1 := 'abc'.
str2 := 'ABC'.
^str1 = str2
To compare based on identity, send an == or ~~ (not identical) message to
the object. Two different strings cannot be identical, though two variables
that refer to the same string are identical.
| str1 str2 str3 |
str1 := 'Excellent'.
str2 := 'Excellent'.
str3 := str1.
^Array
with: (str1 == str2)
with: (str1 == str3)
The sameAs: message compares the equality of strings while ignoring
case:
| str1 str2 str3 |
str1 := 'north'.
str2 := 'North'.
str3 := 'northwest'.
^Array
with: (str1 sameAs: str2)
with: (str1 sameAs: str3)
with: (str2 sameAs: str3)
Searching
The ability to find a specific character or substring is essential in
applications that parse strings. Often a special character or series of
characters identifies a field within a string, especially when the string
represents the contents of a structured text file.
438 VisualWorks
Strings
interval := classComment
findString: searchString
startingAt: 1
ignoreCase: true
useWildcards: true.
^classComment
copyFrom: interval first
to: interval last
Substring Operations
When a string contains two or more parts, getting the parts as separate
strings is a common requirement. For example, you might need to extract
the first and last names from a string containing a full name. You can
copy a portion of a string, using the starting and stopping character
locations.
In certain situations, the only part of a string that you need is a prefix that
ends at a specific character. You can copy the characters that precede a
specific endpoint character.
Copying a Substring
Send a copyFrom:to: message to the string. The first argument is the
starting index and the second argument is the ending index of the desired
substring.
| fullName firstName lastName spaceIndex |
fullName := 'Mahatma Gandhi'.
spaceIndex := fullName indexOf: Character space.
firstName := fullName
copyFrom: 1
to: spaceIndex - 1.
lastName := fullName
copyFrom: spaceIndex + 1
to: fullName size.
440 VisualWorks
Strings
Copying a Prefix
Send a copyUpTo: message to the string. The argument is the character
that marks the end of the prefix (but is not included in it).
| fullName firstName |
fullName := 'Boris Yeltsin'.
442 VisualWorks
Strings
Abbreviating a String
Abbreviations are rarely as comprehensible as the full form of a string,
and automatically derived abbreviations tend to be even less readable. In
some situations, however, an abbreviation is useful, and VisualWorks
provides a few useful abbreviation messages. Here are two methods.
Browse the String class and its superclasses for others.
Contracting a String
Send a contractTo: message to the string. The argument is the number of
characters in the abbreviation, including three for the ellipsis. Half of the
abbreviation will be taken from the beginning of the string and the other
half from the end.
| string contractedString |
string := 'North American Free Trade Agreement'.
444 VisualWorks
Formatted Text and Fonts
txt := ComposedText
withText: txt
style: textStyle.
446 VisualWorks
Formatted Text and Fonts
| txt gc |
gc := (ExamplesBrowser prepareScratchWindow) graphicsContext.
txt := ComposedText comment asComposedText.
txt compositionWidth: 380.
txt rightFlush.
448 VisualWorks
Formatted Text and Fonts
| txt gc |
gc := (ExamplesBrowser prepareScratchWindow) graphicsContext.
txt := 'Line 1\Line 2\Line 3\Line 4'
withCRs asComposedText.
txt compositionWidth: 380.
txt hardcopy.
450 VisualWorks
Formatted Text and Fonts
Counting Characters
Text objects support a size message which returns the number of
characters in the text.
| composedText plainText |
composedText := Object comment asComposedText.
^txt
findString: 'Var*:'
startingAt: 1
ignoreCase: true
useWildcards: true.
Replacing Text
Replacing part of a ComposedText is very much as with a string. The
substitution text can be either a Text or a String. If the replacement text is
a Text, it can have boldfacing or other emphasis properties.
To replace text, send a replaceFrom:to:with: message to the composed text.
This method is defined in the ComposedText class, so you don’t need to
extract the text or string.
As with string replacements, the first and second arguments are integers
indicating the range of text to be replaced. The third argument is the
replacement text, which can be either a string or a text.
| txt gc |
gc := (ExamplesBrowser prepareScratchWindow) graphicsContext.
txt := 'Red Green Blue' asComposedText.
txt compositionWidth: 300.
452 VisualWorks
Character Formatting
Character Formatting
Character formatting for composed text is primarily controlled by two
objects contained in a composed text object: a Text object and a
TextAttributes object.
The Text object has two parts: a String and an array of modifiers that
indicate how each character in the string is formatted. Modifiers, which
are called emphases because the modifiers are often used to emphasize
a portion of a text, specify features such as bold, italic, color, and, to a
limited extent, character size.
More complete control over character formatting, including font selection,
is handled by the TextAttributes object for the composed text. VisualWorks
provides a default TextAttributes, which is used by composed text unless
an alternate is specified. The effect of modifiers applied to the Text object
are defined by the text attributes assigned to the composed text.
454 VisualWorks
Character Formatting
456 VisualWorks
Character Formatting
| txt gc ca ta |
gc := (ExamplesBrowser prepareScratchWindow) graphicsContext.
txt := Object comment asComposedText.
txt compositionWidth: 300.
458 VisualWorks
Character Formatting
| txt gc |
gc := (ExamplesBrowser prepareScratchWindow) graphicsContext.
txt := 'normal courier times helvetica terminal foobar' asText.
460 VisualWorks
Character Formatting
| txt gc ca ta fd |
gc := (ExamplesBrowser prepareScratchWindow) graphicsContext.
txt := Object comment asComposedText.
"Copy the default font description and set its pixel size to 22"
fd := txt textStyle defaultFont copy.
fd pixelSize: 22.
ta := TextAttributes defaultFontQuery: fd.
ta gridForFont: nil
withLead: 2.
txt textStyle: ta.
462 VisualWorks
Character Formatting
2 To scale the text style either larger or smaller than the preferred pixel
size send the message scalingFactor: to the text style from step 1.
The argument is a ratio of the desired pixel size to the preferred pixel
size. For example, if the preferred pixel size is 16 a scaling of 1.5
displays the font at a pixel size of 24.
3 Install the text style in the composed text by sending a textStyle:
message to the composed text. The argument is the text style from
step 2.
464 VisualWorks
Character Formatting
Note: The text style must have been defined previously and installed
in the fonts menu.
To refresh any open windows to use the new font, send a resetViews
message to the TextAttributes class.
TextAttributes setDefaultTo: #default.
TextAttributes resetViews.
466 VisualWorks
Character Formatting
File Names
The Filename class supports operations involving disk files and directories.
Filename is an abstract class, and directs the creation message to the
appropriate subclass. This keeps your file-creating code general enough
to run on any of the supported platforms.
Filenames themselves are a platform problem, due largely to platform
specific separator characters in path names and disk volume specifiers.
LogicalFilename and its subclass PortableFilename provide mechanisms for
storing absolute and relative pathnames in a platform neutral form.
468 VisualWorks
File Names
Creating a Filename
To create a simple file or directory name object, send asFilename to a
string identifying the desired file or directory:
| name filename |
name := '[Link]'.
filename := name asFilename.
^filename
In this case the filename includes no directory information, and so the
named file is relative to whatever the current directory is. You can specify
path information in the string as well, for example:
‘mydirectory\[Link]’ asFilename
‘c:\mydirectory\[Link]’ asFilename
‘/usr/tmp/[Link]’ asFilename
The disk file or directory is not affected by the mere creation of a Filename
object. No link exists to the disk file or directory, so you do not need to
release an external resource at this point.
For example, VisualWorks assumes that its home directory is set in the
VISUALWORKS system variable. In the Settings Tool there are several
references to directories using this variable, for example in specifying
parcel paths, such as $(VISUALWORKS)/parcels. Depending on the path,
the result is either an instance of LogicalFilename or PortableFilename:
• If the path is absolute, starting with root, or with a machine or disk
specification, the system reders it as a LogicalFilename. The result is
not generally portable.
• If the path is relative, or begins with a system variable, the system
renders it as a PortableFilename. The result is generally portable.
To maximize portability, use only constructs that produce a
PortableFilename. Use system variables to ensure a portable root path
segment.
470 VisualWorks
Getting File Information
^newFile fileSize.
472 VisualWorks
Getting File Information
| dir parentDir |
dir := Filename defaultDirectory.
^'
PATH: ', pathString, '
DIRECTORY: ', dirString, '
FILE: ', fileString
#statusChanged
The time of the most recent change in external attributes of the file,
such as ownership and permissions.
If the operating system does not support the requested type of
information, nil is returned; otherwise, an array containing a date and a
time is returned.
| newFile stream datesDict modifyDates modifyDate modifyTime |
newFile := 'testFile' asFilename.
stream := newFile writeStream.
stream nextPutAll: Object comment.
stream close.
datesDict := newFile dates.
modifyDates := datesDict at: #modified.
modifyDates isNil
ifFalse: [
modifyDate := modifyDates first.
modifyTime := modifyDates last].
^'
MODIFIED: ', modifyDate printString, ' at ', modifyTime printString
474 VisualWorks
System Variables
| workingDir contents |
workingDir := Filename defaultDirectory.
System Variables
Operating systems use system variables for a variety of purposes,
typically related to the directory path locations of required resourses.
VisualWorks relies on one system variable, $(VISUALWORKS), as the
directory whose subdirectories contain its resources. If you installed the
working directory option during installation, there is also a $(WORK_VW).
A common system variable is PATH, which holds a list of directory paths
to executable programs.
Within VisualWorks, system variables are written as above, with the
variable name enclosed in parentheses, and preceeded by $.
System variables are generally used to specify a directory path relative to
the value held in the variable. To create a Filename instance from a String
containing a system variable, send an expandEnvrionmentIn: message to
Filename:
Filename expandEnvironmentIn: '$(VISUALWORKS)\bin'
This returns a ByteString. To get a Filename instance, send asFilename to the
ByteString:
( Filename expandEnvironmentIn: '$(VISUALWORKS)\bin' ) asFilename
^newFile contentsOfEntireFile
"Appending"
stream := filename appendStream.
stream nextPutAll: ' -- SECOND STRING'.
stream close.
476 VisualWorks
Copying or Moving a File
newFile delete.
posttest := newFile exists.
^'
EXISTS BEFORE DELETION: ', pretest printString, '
EXISTS AFTER DELETION: ', posttest printString.
On operating systems such as UNIX that support multiple pathnames for
the same physical disk file or directory, deleting as shown here removes
the reference that is identified by the pathname, but it does not delete the
physical file or directory if another reference exists.
Moving a File
To move a file to another directory, send a moveTo: message to the
Filename. The argument is a string containing the new pathname, which
can include a different directory. If the Filename represents a directory or a
nonexistent disk file, an error results.
| newFile stream |
newFile := 'testFile' asFilename.
stream := newFile writeStream.
stream nextPutAll: Object comment.
stream close.
Renaming a File
To rename a file send a renameTo: message to the Filename. The argument
is a string containing the new pathname, which can include a different
directory. If the Filename represents a directory or a nonexistent disk file,
an error results.
Renaming a file is more efficient than moving the file.
| newFile stream |
newFile := 'testFile' asFilename.
stream := newFile writeStream.
stream nextPutAll: Object comment.
stream close.
478 VisualWorks
Comparing Two Files or Directories
pathsAreEqual := (
file1 = file2).
contentsAreEqual := (
file1 contentsOfEntireFile = file2 contentsOfEntireFile).
^'
PATHS ARE EQUAL: ', pathsAreEqual printString, '
CONTENTS ARE EQUAL: ', contentsAreEqual printString.
pathsAreEqual := (
dir1 = dir2).
contentsAreEqual := (
dir1 directoryContents = dir2 directoryContents).
^'
PATHS ARE EQUAL: ', pathsAreEqual printString, '
CONTENTS ARE EQUAL: ', contentsAreEqual printString.
Printing a File
Some operating systems support printing a text file directly, and others
require that it first be converted to PostScript or another printer-specific
format. VisualWorks supports several approaches for printing files. Only
basic text printing is covered here.
newFile printTextFile
480 VisualWorks
Printing a File
This example shows how to create and edit a data file that contains
comma-delimited fields.
To build the records and fields, create a block in which, for each field of
data, a nextPutAll: message is sent to the stream with the data string as
argument, followed by a nextPut: message with the separator character as
argument.
Send a valueNowOrOnUnwindDo: message to the data-writing block. The
argument is another block that closes the stream by sending a close
message to it.
| dataFile stream separator writingBlock |
dataFile := 'dataFile' asFilename.
separator := $,."comma"
"Write data"
stream := dataFile writeStream.
writingBlock := [
ColorValue constantNames do: [ :color |
stream nextPutAll: color.
stream nextPut: separator]].
writingBlock valueNowOrOnUnwindDo: [stream close].
"Read data"
stream := dataFile readStream.
colorNames := OrderedCollection new.
readingBlock := [
[stream atEnd] whileFalse: [
colorNames add: (stream upTo: separator)]].
readingBlock valueNowOrOnUnwindDo: [stream close].
^colorNames
482 VisualWorks
Setting File Permissions
"Print it"
| newFile stream removed restored |
newFile := 'testFile' asFilename.
stream := newFile writeStream.
stream nextPutAll: Object comment.
stream close.
newFile makeUnwritable.
removed := newFile canBeWritten.
newFile makeWritable.
restored := newFile canBeWritten.
^'
PERMISSION REMOVED: ', removed printString, '
PERMISSION RESTORED: ', restored printString.
484 VisualWorks
Storing Objects in a BOSS File
486 VisualWorks
Getting Objects from a BOSS File
^array
"Search sequentially."
foundObject := nil.
bos := BinaryObjectStorage
onOldNoScan: 'points.b' asFilename readStream.
[[bos atEnd]
whileFalse: [
nextObject := bos next.
(nextObject z > 45)
ifTrue: [
foundObject := nextObject.
bos setToEnd]]]
ensure: [bos close].
^foundObject
488 VisualWorks
Storing and Getting a Class
^foundObject
^file fileSize
490 VisualWorks
Converting Data After Changing a Class
^2
492 VisualWorks
Customizing the Storage Representation
representBinaryOn: bos
"Represent a PointExample by its x, y and z coordinates
plus the message and receiver for creating an instance from
those coordinates."
^MessageSend
receiver: self class
selector: #x:y:z:
arguments: (Array with: x with: y with: z).
494 VisualWorks
Overview
Overview
The VisualWorks browser provides over two-dozen distinct refactoring
operations for manipulating classes, methods, and individual statements
within a method. Refactoring operations are thus class-, method-, or
statement-oriented.
Class-oriented refactorings
These operate on classes, instance variables, and class variables
and are available on the browser’s Class menu (for details, see
“Refactoring Classes” on page 502).
Class-oriented Refactorings
Create a Subclass
Add a Variable
Method-oriented refactorings
These operate on methods, and are available on the Method menu
(for details, see “Refactoring Methods” on page 504).
Method-oriented Refactorings
Move a Definition to Another Component
Statement-oriented refactorings
These operate on individual statements in a method and are
available through the context sensitive menus in the code tool
(for details, see “Refactoring Portions of a Method” on page 505).
Statement-oriented Refactorings
Extract a Method
Inline a Temporary Variable
Remove a Parameter
Inline a Parameter
Extract to a Temporary
Inline a Message
496 VisualWorks
Refactoring for Abstraction
498 VisualWorks
Refactoring for Abstraction
Since the same instance variables are defined by the sibling class
ClearancePurchase, this refactoring operation also removes them from the
sibling class.
Consolidating Common Code
In the example framework, the method purchaseItemFor: is similar in both
classes CatalogPurchase and ClearancePurchase. We can make a further
refactoring to consolidate this code in a single method in the Purchase
superclass.
To separate the common code:
1 Open a browser on the method CatalogPurchase>>purchaseItemFor:,
and highlight the lines of code that are unique:
| price |
price := catalog costForItem: item.
2 Select Refactor Extract Method from the <Operate> menu.
a A dialog prompts to ask whether you want to extract the
assignment of price. Answer No.
3 A new dialog appears, prompting for the name of a new method to
contain the extracted code. Enter: computePrice.
The refactoring operation creates a new method using the extracted
code:
computePrice
^catalog costForItem: item
4 Select the method ClearancePurchase>>purchaseItemFor: and highlight
the unique code:
| price discountedPrice |
price := catalog costForItem: item.
discountedPrice := price - (price * discount).
5 Select Extract Method from the <Operate> menu.
a A dialog prompts to ask whether you want to extract the
assignment of price. Answer No.
6 A new dialog appears, prompting for the name of a new method to
contain the extracted code. Enter: computePrice.
Inlining Methods
It is often desirable or necessary to inline the functionality contained in a
method by moving it to a different, more appropriate, class.
For example, suppose an application class defines the following method:
copyDictionary: aDictionary
| newDictionary |
newDictionary := Dictionary new: aDictionary size.
aDictionary
keysAndValuesDo: [:key :value | newDictionary at: key put: value].
^newDictionary
Since this method works entirely with its parameter, aDictionary, it would
simplify the overall design of the application if this functionality were
relocated in class Dictionary, i.e.:
Dictionary>>copyWithAssociations
| newDictionary |
newDictionary := Dictionary new: self size.
self keysAndValuesDo:
[:key :value | newDictionary at: key put: value].
^newDictionary
By placing the functionality in class Dictionary, we can replace indirect
sends such as self copyDictionary: someDictionary with direct, inline sends
to the Dictionary object.
500 VisualWorks
Refactoring for Abstraction
Individual Refactorings
The VisualWorks browser provides over two-dozen distinct refactoring
operations for manipulating classes, methods, and individual statements
within a method.
Refactoring Classes
Creating a Subclass
To insert a new class into the middle of an existing hierarchy, use the
browser’s navigator to choose the superclass for the new class and then
select Class Create Subclass.... A dialog prompts for the name of the new
subclass(es).
This refactoring operation may be used to insert a new class between an
abstract superclass and all of its subclasses.
Renaming a Class and Its References
To rename a class and every reference to it in the image, select
Class Rename....
This refactoring operation checks for symbols with the same name as the
class, and these, too, are renamed (this catches the use of expressions
like Smalltalk at: ...).
Note that in the case of class names constructed by sending the asSymbol
message, the strings containing the class name will not be changed.
Safely Removing a Class
To remove a class, first checking for any references to it, select
Class Safe Remove....
Note that if the class is referenced using constructed symbols or Smalltalk
at: ..., this refactoring may remove the class even though code still uses it.
Changing a Class to a Sibling
To insert a new superclass into an existing hierarchy, use the browser’s
navigator to choose the subclass for the new class and then select
Class Refactor Convert to Sibling.
When requested, enter the name of the class to be created. If the
selected class has subclasses, a class selection dialog opens, for you to
select classes to make as siblings of the selected class, under the new
superclass. The new class will be a superclass of the class selected in
the browser’s navigator, and the other selected classes are moved to be
siblings of the selected class under the new class. It also pushes up
502 VisualWorks
Individual Refactorings
Refactoring Methods
Moving a Definition to Another Component
To move a method, an argument or an instance variable to another
component, select Move to Component... from the Method menu.
This operation can be used to move the body of a method to another
component, leaving a forwarder and thereby not changing the external
interface of the class that contains the original method.
Renaming a Method and its References
To rename all implementors of a method, all senders, and all symbols
references, select Rename... from the Method menu.
In addition to strict renaming, this refactoring operation also enables you
to rearrange the method’s parameters. However, when rearranging the
parameters, any symbols that are performed cannot be permuted.
504 VisualWorks
Individual Refactorings
Inlining a Parameter
To remove a parameter from the method, adding a corresponding
assignment at the beginning of the method, highlight the parameter and
select Refactor Inline Parameter from the <Operate> menu.
This operation is only performed if all senders of the method have the
same value for the parameter.
Renaming a Temporary
To rename a temporary variable in the body of the method, highlight it
and select Refactor Rename... from the <Operate> menu.
Moving a Temporary to an Inner Scope
To move a temporary variable definition into the tightest scope that
contains both the variable assignment and references, highlight it and
select Refactor Move to Inner Scope from the <Operate> menu.
This operations is useful for improving code performance by converting
unoptimized blocks into optimized ones.
Extracting to a Temporary
To extract a message into an assignment statement, highlight the
statement and select Refactor Extract to Temporary from the <Operate>
menu.
506 VisualWorks
Individual Refactorings
Code Critic
The VisualWorks 7 browser includes a Code Critic tool that may be used
to screen application code for over 60 common types of bugs.
508 VisualWorks
Code Critic
Filtering Results
The Code Critic includes a mechanism for creating and applying special
rule filters. These enable you to ignore a particular rule for a particular
class or method.
For example, during a session with the Critic, once problems associated
with certain rules have been fixed, we may want to ignore those rules
during subsequent checks with the Code Critic.
Working with Rule Filters
To filter an item visible in the Critic’s results list, select it and choose
Add Filter from the <Operate> menu.
To save the current filter set, or load another one, select Save Filters... or
Load Filters... from the browser’s Edit menu. To reset the filters used by the
Critic, select Clear Filters.
510 VisualWorks
Code Rewriting Editor
Search pattern
Replace pattern
The rewrite editor is available whenever you select the Rewrite tab of the
browser’s code tool. Specify a search pattern in the upper input area of
the tool, and a replacement pattern in the lower area.
Use the Search... button to locate all occurrences of the search pattern in
the method, protocol, or classes selected in the browser navigator.
Results are displayed in a new browser. No code is changed.
Use the Replace... button to locate all occurrences of the search pattern in
the specified code, and then open a transformation editor on all matching
methods. The transformation editor allows you to apply the rewrite rule.
The rewrite editor enables you to write your own transformation rules.
A set of pre-defined transformation rules are also available, but as part of
the Code Critic; for details, see “Applying Transformation Rules” on
page 510.
Transformation Rules
A transformation rule is specified using a pattern that is iteratively applied
to each expression in each method this is selected for rewriting.
Pattern-matching is performed against each node in the method’s parse
tree. When the rewrite editor finds a node in the method’s parse tree that
matches the node specified in the parse tree generated from the search
pattern, it applies a transformation.
You may also specify a pattern for a whole method, rather than just a
single node (for details, see “Replacing Whole Methods” on page 514).
When the rewrite tool scans for parse nodes that match the search
pattern, it first converts the pattern into a collection of meta-variables.
Each meta-variable is identified in the search pattern using a ` character.
Meta-variables allow pattern matching without having to specify specific
variable names. In general, a meta-variable is specified by one or more
special characters followed by a valid variable name. For example:
`receiver printOn: `variable
specifies a pattern with two meta-variables named receiver and variable.
This pattern would match the expression:
super printOn: aStream
Using Meta-variables and Modifiers
The ` character for specifying a meta-variable may be accompanied by
other special characters called “modifiers” that are used to specify the
type of node that the meta-variable can match. Modifiers are entered
immediately after the ` character.
For example, since it is often impractical to specify every possible match
exactly, the modifer @ may be added after the ` character to specify a
match for any type of node in the method’s parse-tree.
Suppose, to sustain the example, we want to replace every occurrance of
the message printOn: with the message print:. Using @, we can specify a
general pattern:
`@receiver printOn: `@variable
would now match the expression:
self name printOn: aStream
Slight variations of this pattern can be used to replace keyword
messages with several arguments. Messages taking block arguments
can be easily manipulated in this manner.
512 VisualWorks
Code Rewriting Editor
Rewriting Methods
To rewrite methods using a transformation rule:
1 Select the method or methods you wish to rewrite in the browser’s
navigator and then open the rewrite editor by clicking on the code
tool’s Rewrite tab.
2 Enter a search pattern in the upper input field of the rewrite editor.
3 Enter a replacement pattern in the lower input field.
4 To browse a list of methods that match the search pattern, click on
the Search... button.
5 Open a list of methods that are ready to be transformed, by clicking
on the Replace.. button. A transformation editor appears, showing a
list of methods and highlighting the code that will be transformed.
6 To actually transform the method(s) that match the search pattern,
select Execute or Execute All from the <Operate> menu in the
transformation editor.
514 VisualWorks
Unit Testing
Unit Testing
The VisualWorks 7 browser includes a testing tool for using the SUnit
framework. SUnit (short for Smalltalk Unit Testing Framework) is the
de facto industry standard for developers to build test suites and attach
them to their application classes.
SUnit supports the XP Unit Test testing practices. It is currently available
for all Smalltalk dialects and is maintained by Camp Smalltalk. The SUnit
distributed with VisualWorks 7 is the most recent Camp Smalltalk version.
Additional information can be found at the following sites:
• SUnit Camp Smalltalk
[Link]
• Simple Smalltalk Testing: With Patterns
[Link]
Kent Beck’s original paper on SUnit.
• Various documents and papers are available at:
[Link]
The SUnit testing framework makes two general structures available to
developers:
Test Case
A test case is a collection of unit tests that are generally applied to a
single class. the smallest unit of testing available, checking for
specific conditions. Individual test cases are constructed by creating
concrete subclasses of the abstract framework class TestCase.
Test Resource
A test resource represents a resource needed to perform a
repeatable unit test (e.g., a temporary file or database instance).
Test resources are associated with specific test cases, and may need
to be prepared or initialized before testing can begin.
The unit testing methodology involves a novel approach to code
development: test cases are defined first, then the application code is
written and tested until the test passes.
The test case defines what the code should do or be like (the invariants of
the program execution), thereby integrating test-writing into the heart of
the development process.
To gather all tests in the selected class into a suite and then run them,
click Run. The test results are displayed by the testing interface:
You may run selected tests, by selecting only a single method, protocol,
or a combination of protocols or methods. Similarly, you can gather a
number of test cases into a single suite by selecting several classes
(using the <Shift> key) before clicking on Run.
Although the unit test framework includes the notion of a test suite,
developers using the VisualWorks testing tool don’t need to worry about
suites. The testing tool automatically bundles all selected test cases into
a test suite before running the suite.
516 VisualWorks
Unit Testing
assert: anExpression
To pass the test, anExpression should answer true. This is a
convenience method the behaves like should:.
deny: anExpression
To pass the test, anExpression should answer false. This is a
convenience method the behaves like shouldnt:.
Using should: and shouldnt:
Test cases may use the methods should: and shouldnt: to test for specific
conditions allowing a test to pass.
The method should: takes a block parameter and allows the test to pass if
the block returns true. For example:
testSomeCondition
self should: [someCondition == true].
Alternately, you may test using the method shouldnt:, which passes if the
block returns false.
For example, a test that uses shouldnt:raise: (a variant of shouldnt:) might
look like this:
testCreation
self shouldnt: [MyNewClass new] raise: Error
In place of should: and shouldnt:, you may also use the two convenience
methods assert: and deny:. For example:
self should: [empty include: 5]
is equivalent to:
self assert: (empty includes: 5)
Defining Test Resources
Individual test cases will vary in their complexity. A test case may check
against a simple set of predictions (e.g., “Should this condition be true?”),
or it may involve a test resource that is used during testing.
A resource may be a simple data structure, or something as complicated
as a database instance or a server connection. A single test case may
require a number of different resources, which may either be created and
destroyed for each individual test, or used for a series of tests.
Resources that are created and destroyed for each test are represented
using instance variables in the test case class. In practice, an instance
variable is added for each condition predicted by the resource.
518 VisualWorks
Unit Testing
Resources that are created and then used for a number of tests are
represented using subclasses of TestResource. These must be initialized
once before any test is run, and destroyed after all tests have been run.
As an example of a resource used for a single test, we might test the
behavior of a Set using two resources, one to represent the “empty”
condition, and one to represent the “full” condition. Thus, we would add
two instance variables, empty and full, to our test case.
To initialize these two resources, we override the method setUp defined in
class TestCase:
setUp
empty := Set new.
full := Set with: 5 with: #abc
Each time a test case is run, it begins by sending setUp and ends by
sending tearDown. To ensure that tests are repeatable and predictable,
setUp and tearDown are run once for each test. Subclasses of TestCase
should override these two methods to initialize and release resources.
An example method that would use a resource to test the add: behavior of
a Set might look like this:
testAdd
empty add: 5.
self should: [empty includes: 5]
This test adds the number 5 to the empty resource, and then uses should:
to test that the empty resource now include the number 5.
Subclasses of TestCase are instantiated once for each test that is run, and
any resources are initialized, used once, and then discarded.
520 VisualWorks
24
XML Framework
522 VisualWorks
Working with XML documents
This example does its work and returns nil, unless errors occur. For
syntax checking, you still need to provide handlers for syntax errors, as
described in “XML Error Handling” below.
Browse the SAXDriver hierarchy to see what drivers are available. In
general the classes provide superclasses for your own drivers.
For advanced users, it is possible to specify handlers for different aspects
of a document. Browse the XMLParser contentHandler:, dtdHandler:,
entityResolver: and errorHandler: methods for this option.
524 VisualWorks
Accessing XML Document elements
elementsNamed: aNodeTag
Returns an OrderedCollection of child elements named aNodeTag.
anyElementNamed: aNodeTag
Same as elementNamed:, except that the search is recursive from the
receiver, so the receiver, its children, grandchildren, etc., are included
in the search. An error is raised if there is not exactly one.
anyElementsNamed: aNodeTag
Same as elementsNamed:, except that the search is recursive from the
receiver, so the receiver, its children, grandchildren, etc., are included
in the search.
attributes
Returns a OrderedCollection of Attribute objects in the receiving Element.
selectNodes: aBlock
Returns an OrderedCollection of Node objects satisfying the selection
criteria specified in aBlock.
The following sections will use these messages to explore a Document.
Selecting elements
An XML document is structured as a hierarchy of elements with a single
root element. Depending on the individual document, the structure may
be very shallow, as in the case of a well-formed but unstructured
document, or quite deep. To make use of the XML document involves
traversing and digging through this element hierarchy.
The children message returns an OrderedCollection of elements contained
immediately in the receiving element.
parser := XMLParser new.
pdoc := parser parse:
'[Link] asURI.
elementCollection := pdoc root children.
The contents of the resulting collection may not all be elements as such.
For example, elementCollection in the above code contains (at the time of
this writing) some [Link] nodes as well as Element nodes. This can be
important when working down through the hierarchy because a Text does
not respond to children.
The isElement message returns a Boolean indicating whether the receiver
is an Element or not. You can use it to collect just those nodes that are
elements, for example:
elementCollection := pdoc root children select: [ :el | el isElement ]
The elements of this collection now all respond to children, and you can
continue digging into the hierarchy.
It is also frequently desirable to select only those elements with a
particular tag, or name. For example, when dealing with a specific node,
you may want to deal only with elements tagged “partNum”. To collect all
these elements in a node (aNode), send a elementsNamed: message with a
NodeTag or String argument:
partNumElements := aNode elementsNamed: 'partNum'.
The String format shown here only works if the element is in the default
XML namespace; otherwise the argument must be an instance of
NodeTag. You may retrieve a NodeTag from an element by sending a tag
message to the Element, and then use that tag to identify other elements
with the same tag. This can be useful for retrieving all other elements with
the same tag as one you already have:
subjTag := someElement tag.
tagGroup := newDoc root elementsNamed: subjTag.
Alternatively, you can create a NodeTag by sending a qualifier:ns:type:
message to a new instance:
526 VisualWorks
Accessing XML Document elements
Selecting attributes
Elements often have attributes, specifying special features of the
element. The attributes message, sent to an Element (anElement), returns
an OrderedCollection of an element’s attributes.
attrs := anElement attributes.
Attributes are essentially key/value pairs, where the key is the attribute
name, and the value is a String. The messages for accessing these are:
tag
Returns the Attribute name, as a NodeTag.
value
Returns the Attribute value.
To make use of an Attribute, you will need to search through the collection
of attributes until you find one you are interested in, and then get its
value. For example, if you need to process an “href” attribute for an
element, you will search for that attribute and return the value. For
example:
( attrs detect: [ :attr | attr tag type = 'href' ] ifNone: [ ] ) value.
Since attributes are already key/value pairs, it may be worth setting them
into a Dictionary, especially for repeated access:
attrDict := Dictionary withAll: (aCollection collect:
[ :each | Association key: each tag type value: each value ]) .
Building a document
Besides handling XML documents that your application receives, for
conducting web-based commerce it is also necessary to build XML
documents. You can do this simply by assembling a long string and
transmitting that over the transport, but this places all of the responsibility
for building proper XML on your application.
VisualWorks provides facilities for building an XML DOM tree that
alleviates some of the responsibility for building a syntactically correct
XML document.
Not all aspects of a document are supported, however, so you may need
to provide some other mechanism for adding these aspects to the
document. For example, the XML prolog and DTD declarations are not
supported by the XML framework. If required in your application, these
need to be written onto output stream before any document elements,
and so are not handled as part of the document itself. (See “Writing the
XML Document” at the end of this section).
This section describes how to build an XML document using the
VisualWorks XML support, and noting where methods not included in the
XML framework are required. The general procedure is to create an
[Link] instance and add nodes.
Node ordering
The most straight-forward method for adding nodes is by sending
addNode: to an existing node, with the new node as argument. This is the
method we will use in the following discussion.
However, addNode: adds the new node to the end of the receiver’s
collection of nodes. Accordingly, you need to be careful to add nodes in
order, from the start of the XML document to the end.
528 VisualWorks
Building a document
If you must insert a node someplace other than at the end, realize that
you can add it using OrderedCollection messages. This may be useful, for
example, to ensure that processing instructions are added early in the
document, prior to the root element.
XML
heading1
heading2
body
send messages like this:
newDoc := [Link] new.
newDoc addNode: ( [Link] name: 'xml' text: 'version="1.0" ' ).
newDoc addNode: ( [Link] tag: 'XML' ).
newDoc root addNode: ( ( [Link] tag: 'heading1' )
addNode: ( ( [Link] tag: 'heading2' )
addNode: ( [Link] tag: 'body' ) ) ).
The nodes can, of course, be constructed individually and added to the
containing node in other ways.
The PI element defines a processing instruction. Refer to “Add processing
instructions” for more information.
If you assemble a collection of nodes, you can add them as subnodes as
a group when creating their parent, using the tag:elements: instance
creation method. For example, to add a node structure to newDoc, do:
nodeGroup := Array with: ([Link] tag: 'body')
with: ( ([Link] tag: 'heading2')
addNode: ([Link] tag: 'body' ) ).
newDoc root addNode: ( [Link] tag: 'heading1' elements: nodeGroup ).
Adding element attributes
An element may have attributes, which are additional labels identifying
the contents of an element. For example, an image element may include
alignment and source information:
<IMG ALIGN="left" SRC="[Link]
Attributes are instances of [Link], which is a subclass of Node. To
add attributes, create the Attribute instances and add them as a collection
by sending a tag:attributes:elements: instance creation message to Element,
sending an addNode: message to the containing element as usual. The
argument to the elements: keyword can be provided as a collection of
elements or as nil.
attrGroup := Array
with: ([Link] name: 'ALIGN' value: 'left' )
with: ([Link] name: 'SRC' value:
'[Link]
530 VisualWorks
Building a document
532 VisualWorks
Using XML Namespaces
Declare namespaces
A Document can specify one or more namespaces for resolving element
or attribute names within the document. A root element often specifies a
namespace, such as this, from [Link]
<html xmlns="[Link]
A document can also have multiple namespaces, one of which may have
no prefix, as in the above. All additional namespaces must have a prefix.
To specify two XML namespaces, one without and the other with a prefix,
the XML is specified like this:
< html xmlns="[Link]
xmlns:foo="[Link] >
To declare these namespace specifications in an XML Document in
VisualWorks, create a Dictionary containing these namespaces, and then
add the Dictionary to the document root element by sending it a
namespaces: message. The Dictionary contains associations between a
prefix string and the URI string. A namespace without a prefix is
associated with an empty prefix.
534 VisualWorks
Using XML Namespaces
attrGroup := Array
with: ([Link]
name:
(NodeTag new qualifier: 'foo' ns: (nsDict1 at: 'foo') type: 'ALIGN')
value: 'left' )
with: ([Link]
name: (NodeTag new qualifier: '' ns: (nsDict1 at: '') type: 'SRC')
value: '[Link]
536 VisualWorks
Building a SAX driver
538 VisualWorks
Building a SAX driver
startDocument
Transcript show: 'Start of Document'; cr.
endDocument
Transcript show: 'End of Doc';cr.
540 VisualWorks
Building a SAX driver
showNamespaceDeclarations: aBoolean
Sets the Boolean value of the namespace-prefixes feature
(SAXNamespacePrefixes).
Setting the validating feature using the validate: message was illustrated
above, to parse a document without a DTD (see “Parsing an XML
document”). Using the more general messages, turning off validation can
be done like this:
parser := XMLParser new.
parser atFeature: SAXValidate put: false.
The feature can also be identified by an URI, in which case the above
could be:
parser := XMLParser new.
parser atFeature: '[Link] put: false.
For setting or getting SAX feature and property values, you should trap
SAXNotRecognizedException and SAXNotSupportedException.
parser := XMLParser new.
featureStr := '[Link] .
[ [ parser atFeature: featureStr ]
on: SAXNotRecognizedException
do: [ :e | Dialog warn: 'Feature ', featureStr, ' is not recognized.' ] ]
on: SAXNotSupportedException
do: [ :e | Dialog warn: 'Feature ', featureStr, ' is not supported.' ]
Document Fragments
When using XML to exchange data, it is frequently inconvenient, or
inefficient, to have to parse an entire document up to the element that
one is actually interested in. For example, if you are only interested in one
chapter (e.g., chapter 23), or one paragraph, of a book, it would be
inefficient to have to parse all of the book up to that element.
Document fragments provide a way to represent a part of a document.
The challenge for using fragments is to have enough context to be able to
parse the fragment correctly.
The VisualWorks XML framework supports document fragments in the
[Link] class. The main difference between a Document
and a DocumentFragment is that a DocumentFragment does not require a
single top-level element, but may have a sequence of elements at its top
level. It may also have character data outside of an element. So, for
example, a document fragment could include:
Building a fragment
To build the above fragment, send the appropriate addNode: messages to
an instance of DocumentFragment.
docFrag := [Link] new.
docFrag addNode: ( ( [Link] tag: 'body' )
addNode: ( [Link] text: 'Some introductory text.' ) ).
docFrag addNode: ( ( ( [Link] tag: 'heading2' )
addNode: ([Link] text: 'Some heading' ) )
addNode: ( ([Link] tag: 'body')
addNode: ( [Link] text: 'Discussion of this topic.') ) ).
docFrag addNode: ( ( ( [Link] tag: 'heading2' )
addNode: ([Link] text: 'Some heading' ) )
addNode: ( ([Link] tag: 'body')
addNode: ( [Link] text: 'Discussion of this topic.') ) ).
Attempting this construct with a Document instance would result in errors
due to the multiple top-level nodes, but it is acceptable as a
DocumentFragment.
Parsing a fragment
If an XML document references a fragment as an entity, you can parse
the entire document as usual. The fragment is simply included in the
document as if it were physically present within the XML.
A fragment-aware application, however, will want to deal with fragments it
may receive from a data source. The application will have to be able to
provide the context necessary for including the fragment in a document.
In the case of the above fragment, the context may be simply:
542 VisualWorks
XSL stylesheet processing
<document>
<heading1>
Title
</heading1>
***insert fragment here***
</heading1>
</document>
The XML framework provides no specific support for providing this
context. Recommendations are available from the World Wide Web
Consortium (see [Link] but it is the
responsibility of your application to implement a strategy.
<?xml version="1.0"?>
<?xml-stylesheet type="text/xml" href="[Link]"?>
<PERIODIC_TABLE>
<ATOM STATE="GAS">
<NAME>Hydrogen</NAME>
<SYMBOL>H</SYMBOL>
<ATOMIC_NUMBER>1</ATOMIC_NUMBER>
<ATOMIC_WEIGHT>1.00794</ATOMIC_WEIGHT>
<BOILING_POINT UNITS="Kelvin">20.28</BOILING_POINT>
<MELTING_POINT UNITS="Kelvin">13.81</MELTING_POINT>
<DENSITY UNITS="grams/cubic centimeter">
<!-- At 300K, 1 atm -->
0.0000899
</DENSITY>
</ATOM>
<ATOM STATE="GAS">
<NAME>Helium</NAME>
<SYMBOL>He</SYMBOL>
<ATOMIC_NUMBER>2</ATOMIC_NUMBER>
<ATOMIC_WEIGHT>4.0026</ATOMIC_WEIGHT>
<BOILING_POINT UNITS="Kelvin">4.216</BOILING_POINT>
<MELTING_POINT UNITS="Kelvin">0.95</MELTING_POINT>
<DENSITY UNITS="grams/cubic centimeter"><!-- At 300K -->
0.0001785
</DENSITY>
</ATOM>
</PERIODIC_TABLE>
and an XSL document ([Link]) to transform the document into
HTML:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="[Link]
<xsl:template match="PERIODIC_TABLE">
<html>
<xsl:apply-templates/>
</html>
</xsl:template>
<xsl:template match="ATOM">
<P>
<xsl:apply-templates/>
</P>
</xsl:template>
</xsl:stylesheet>
First generate the rules database, then parse the document and apply the
rules as follows:
544 VisualWorks
Using XPath
Using XPath
XPath is a language for addressing parts of an XML document. XPath
models a document as a tree structure, allowing elements to be accessed
by specifying a path to those elements, like a filesystem path.
Expression Selection
/AAA The root node AAA
/AAA/BBB/CCC All elements tagged CCC that are children of BBB
that are children of root AAA.
//CCC All elements CCC in the document
//BBB/CCC All elements CCC that are children of BBB
//BBB/* All elements that are children of BBB
/*/*/* All elements with two ancestors
//BBB/CCC[2] Each second instance of element BBB that is a child
of BBB
//CCC[text()] All text elements in any CCC
//BBB | //CCC All elements BBB and CCC
/AAA/BBB/descendant::*i All elements that are descendents of /AAA/BBB
//@id All attributes id
//BBB[@id] Al elements BBB with an id attribute
546 VisualWorks
Using XPath
exprString := '//foo:CCC[text()]'.
expr := [Link] new
xmlNode: ( myDoc root) ;
parse: exprString as: #expression.
The element’s sole purpose is to map "foo" to an URL, but could be, for
example, the document root node, as done above. If you don't use
namespaces in the path, the XML Element is optional.
548 VisualWorks
A
VisualWorks Smalltalk Syntax Description
In the sections that follow, the syntax of the Smalltalk language is formally
defined with the aid of Backus-Naur form. The following characters have
special meanings unless they are enclosed in quotation marks.
Character Description
= expands to
‘’ terminal (single quotes surround an atomic literal)
“” comment (double quotes surround a comment)
| or
+ one or more
* zero or more
[] zero or one
\ excluding the following
... through
() grouping
<> keyboard key
Lexical Primitives
The lexical syntax is formally ambiguous, in that, for example, the string
abc: can be parsed either as an identifier followed by a non-quote-
character, or as a keyword. We resolve this ambiguity in all cases in favor
of the longest token that can be formed starting at a given point in the
source text. Thus abc: is always considered to be a keyword, if the a is
the beginning of the token.
Character Classes
The definition of token is not used anywhere else in the syntax; it is
supplied only for exposition.
Numbers
digits = digit+
big-digits = (digit | letter)+ “as appropriate for radix”
number = digits (‘r’ [‘-’] big-digits | optional-fraction-and-exponent)
optional-fraction-and-exponent = [‘.’ digits] [(‘e’ | ‘d’ | ‘s’) [‘-’] digits]
550 VisualWorks
Atomic Terms
Atomic Terms
Note that “binding” here is used in a more general sense than elsewhere
in this document, to include variables and bindings.
We originally intended that the definition of array-literal be the following:
array-literal = ‘#’ ‘(’ literal* ‘)’
This would have simplified the syntax, eliminating the need for array-
literal-body and byte-array-literal-body as separate constructs. However,
this definition is not backward-compatible with previous versions of the
Smalltalk-80 language. Specifically, it requires symbols and arrays
appearing within an array literal to be quoted with #. Because of this, we
adopted the more complex definition.
In order to keep lexical analysis and parsing separate, but still allow
constructs like x:=3 (without a space, making it look like a keyword, x:), we
have had to introduce the alternative
keyword ‘=’ expression
for assignment. This should really be read as though it were
binding-name ‘:=’ assignment
552 VisualWorks
Methods
Methods
554 VisualWorks
Composed Characters
Composed Characters
The following table lists the special characters in the left column. The two
characters that make up the body of the compose sequence are shown in
the second column. The hexadecimal equivalents of these two columns
are displayed in the right-hand columns. A description is shown in the
middle column.
× xx 78 78 cross 00D7
Ø /O 2F 4F O slash 00D8
|O 7C 4F capital thorn 00DE
ß ss 73 73 German double-s 00DF
æ ae 61 65 ae diphthong 00E6
+d 2B 64 small eth 00F0
÷ -: 2D 3A divide 00F7
ø /o 2F 6F o slash 00F8
|o 7C 6F small thorn 00FE
Ð -D 2D 44 D with stroke 0110
d -d 2D 64 d with stroke 0111
H -H 2D 48 H with stroke 0126
h -h 2D 68 h with stroke 0127
i .i 2E 69 dotless i 0131
556 VisualWorks
Composed Characters
€ =C 3D 43 euro 20AC
™ TM 54 4D trademark 2122
Ω om 6F 6D omega 2126
1/ 18 31 38 one eighth 215B
8
3/
8 38 33 38 three eighths 215C
5
/8 58 35 38 five eighths 215D
7
/8 78 37 38 seven eighths 215E
← -< 2D 3C arrow left 2190
↑ |^ 7C 5E arrow up 2191
→ -> 2D 3E arrow right 2192
Diacritical Marks
A diacritical mark, such as a circumflex ( ^ ), is combined with a character
in a similar fashion. The compose key (<Control>-k by default) comes
first, then a character representing the diacritical mark (taken from the
table below) and finally the base character. For example, to get ñ, you
would type <Control>-k, followed by a tilde ( ~ ) and the letter ‘n’.
Programmatically, add the diacritical by sending, for example:
| baseChar diacrit composedChar |
baseChar := $a.
diacrit := 16r0300 asCharacter.
composedChar := baseChar composeDiacritical: diacrit.
Transcript cr; nextPut: composedChar; flush
You can also identify the diacritical by name:
diacrit := Character diacriticalNamed: #grave.
The names are specified in the diacriticalNamed: method definition.
In the following table, the diacritical mark is shown in position relative to a
broken circle representing the base character.
` 60 grave 0300
’ 27 acute 0301
^ 5E circumflex 0302
~ 7E tilde 0303
- 2D macron 0304
u 75 breve 0306
558 VisualWorks
Diacritical Marks
, 2C cedilla 0327
; 3B ogonek 0328
_ 5F underline 0332
Production Engines
The production engines are called visual or [Link]. These are
the standard engines, which are stripped of all debug symbols, and are
suitable for deploying VisualWorks applications because of their relatively
small size.
There are also "unstripped" versions of the production engines, which
can be useful in your own development. These include debug symbols,
and so, if you encounter a crash (e.g. by calling external C or COM code
incorrectly), you may be able to use your platform's debugger to
investigate the problem. They are named vwPlatformName, for example
vwlinux86 or [Link], to distinguish them from the standard
engines.
560 VisualWorks
VisualWorks Virtual Machines
Debug Engines
Debug engines include debug symbols and have assertion-checking
code compiled throughout. They are considerably slower than their
production counterparts, but are suitable for debugging object engine
crashes. They are named vwPlatformNamedbg, for example
vwlinux86dbg or [Link], and are located in the debug/
subdirectory for each platform engine. All engines contain debug
functions that can be used to examine the state of the system, trace the
Smalltalk (engine) stack, and so on, using platform debuggers.
Assert Engines
These engines are fully-optimized, but with asserts compiled-in and
enabled. They run at least 50% of the speed of the fully-optimized
production engine, even though they check engine asserts. For for
normal development, this provides perfectly acceptable performance
while checking the engine during normal use. They are named
vwPlatformNameast, for example vwlinux86ast or [Link],
and are located in the asser/ subdirectory for each platform engine.
562 VisualWorks
Virtual Machine Command Line Options
-logo bmpFile
Displays the specified bitmap file as the startup splash screen. On
Windows, prepend .\ to a relative path name.
-nologo
Suppress the splash screen on startup
-sound wavFile
Play wavFile on image startup.
-nosound
Suppress playing the startup sound
Windows platforms
On MS Windows systems, this switch is supported:
-walltime
Use the lower-resolution GetSystemTimeAsFileTime instead of
QueryPerformanceCounter to drive the microsecond clock. This is
useful for long-running applications that are very time sensitive, since
the default QueryPerformanceCounter, while higher-resolution, tends
to drift from the wall clock over a period of hours.
Unix/Linux platforms
These headless engines support two new command-line switches:
-gui:
Load default GUI subsystem shared library on startup.
-guilib [Link]:
Load specified GUI subsystem shared library on startup.
The headless GUI engines currently do not support Input Management
when used with their GUI library. This is a bug we will fix in a subsequent
release. For now I18N users who want to use headful applications should
use the all-in-one engines.
Note: All UNIX and Linux VMs now write all herald information to
/dev/tty, instead of stdout, so VisualWorks can be used on a
pipe.
St80a*background: moccasin
St80a*selectionForeground: White
St80a*selectionBackground: RoyalBlue
St80a*inactiveSelectionForeground: LightGrey
St80a*inactiveSelectionBackground: RoyalBlue3
St80a*menuBackground: LightCyan
Note that there are two schemes in the file, one for key St80 and one for
key St80a. The vm's default lookup key in Xrdb is St80. One can change
the key the vm uses using the -className className switch when
starting the X11 engines. For example:
$VISUALWORKS/bin/linux86/vwlinux86gui -className
St80a [Link]
564 VisualWorks
D
Code Critic Rules
This appendix summarizes the rules used by the browser’s Code Critic
tool. The rules are organized in five groups:
• Bugs
• Possible Bugs
• Unnecessary Code
• Intention Revealing
• Miscellaneous
Additionally, the Code Critic provides a set of predefined transformations
to be used as simple method-level code refactorings:
• Code Transformations
Bugs
Rule Description
Messages sent but not implemented Checks for messages that are sent by a
method, but no class in the system
implements such a message. These will
certainly cause a doesNotUnderstand:
message when they are executed.
Self/Super sends not implemented Similar to the "Message sent but not
implemented" check, but only checks
messages sent to self or super since these
can be statically typed.
Overrides a "special" message Checks that a class does not override a
message that is essential to the base system
(e.g., Object>>class).
References an undeclared variable Checks for references to a variable in the
Undeclared dictionary. If you remove a
variable from a class that is accessed by a
method, you will create an undeclared
variable reference for those methods that
accessed the variable.
Subclass responsibility not defined Checks that all subclassResponsibility
methods are defined in all leaf classes.
Uses A | B = C instead of A | (B = C) Checks precedence ordering of & and | with
equality operators. Since | and & have the
same precedence as =, there are common
mistakes where parenthesis are missing
around equality operators.
Uses True/False instead of true/false Checks for uses of the classes True and False
instead of the objects true and false.
Variable used, but not defined anywhere Similar to the "References an undeclared
variable" check, but looks for variables that
are not defined in the class or in the
Undeclared dictionary.
566 VisualWorks
Possible Bugs
Possible Bugs
Rule Description
Assignment inside unwind blocks Checks assignment to a variable that is the
should be outside first statement inside a value block that is
also used in an unwind block.
Defines = but not hash Checks that all classes that define = also
define hash. If hash is not defined then the
instances of the class might not be able to
be used in sets since elements that are
equal must have the same hash.
Has class instance variable but no Checks that all classes that have class
initialize method instance variables also have an initialize
method. This ensures that all class instance
variables are initialized properly when the
class is filed-in to a new image.
Instance variable overridden by Checks for methods with block temporary
temporary variable variables that override an instance variable.
This causes problems when using the
instance variable inside the method.
Missing super sends Checks that some methods contain a send
to super. For example, the postCopy method
should always contain super postCopy.
Modifies collection while iterating Checks for sends to remove: from inside of
collection iteration methods such as do:.
These can cause the do: method to break
since it will iterate beyond the end of the
collection. The common fix for this problem
is to copy the collection before iterating over
it.
More addDependent: messages then Check that the number of addDependent:
removeDependent: message sends in a class is less than or
equal to the number of removeDependent:
messages. If there are more addDependent:
sends, it is possible that some dependents
are not being released, which may lead to
memory leaks.
Possible missing "; yourself" Checks for missing "; yourself" cascaded
message send for cascaded messages.
This helps locate common coding mistakes
such as:
anArray := (Array new: 2) at: 1 put: 1; at: 2 put: 2.
I.e., anArray would be assigned the value 2
rather than the array object.
Possible three element point; Checks arithmetic statements for possible
E.g., x @ y + q @ r three element points (i.e., a point that has
another point in its x or y part).
Rule Description
References an abstract class Checks for references to classes that have
subclassResponsibility methods. Such
references might create instances of the
abstract class or might be used as the
argument to an isKindOf: message (the latter
is considered bad style).
Returns a boolean and non boolean Checks for methods that return a boolean
value (true or false) and return some other
value such as (nil or self).
If the method is supposed to return a
boolean, this suggests there is a path
through the method that might return a non-
boolean.
If the method doesn't need to return a
boolean, you should probably rewrite it to
return some non-boolean value since other
programmers reading your method might
assume that it returns a boolean.
Returns value of ifTrue:/ifFalse: without Check for methods returning the value of an
ifFalse:/ifTrue: block ifTrue: or ifFalse: message. These statements
return nil when the block is not executed. For
example, the following code will return nil
when aBoolean is false:
methodName
^aBoolean ifTrue: [0]
If the code should return nil when aBoolean is
false, then it should probably be written as:
methodName
^aBoolean
ifTrue: [0]
ifFalse: [nil]
Sends different super message Checks for methods whose source sends a
different super message. A common example
of this is in creation methods. You might
define a method such as:
createInstance
^super new initialize
If the new method is not defined in the class,
you should probably rewrite this to use self
instead. Also, if the new method is defined,
you might question why you need to use the
superclass' new method instead of new
method defined in the class.
Subclass of class that has instance Checks that all subclasses of Collection
variable but doesn't define copyEmpty classes that add an instance variable also
redefine the copyEmpty method. This method
is used when growing the collection. It
copies over the necessary instance variables
to the new, larger collection.
568 VisualWorks
Unnecessary Code
Rule Description
Temporaries read before written Checks that all temporaries are assigned
before they are used. This can help find
possible paths through the code where a
variable might still be unassigned when it is
used.
Uses the result of an add: message Check for possible uses of the result
returned by the add: or addAll: messages.
These messages return their arguments not
the receiver. As a result, many uses of the
results are wrong.
Unnecessary Code
Rule Description
Block immediately evaluated Check for blocks that are immediately
evaluated. Since the block is immediately
evaluated, there is no need for the
statements to be in a block.
Check for same statements at end of Checks for ifTrue:ifFalse: blocks that have the
ifTrue:ifFalse: blocks same code at the beginning or end. Instead
of having the same code in two places, it
should be moved outside the blocks.
Class not referenced Check if a class is referenced either directly
or indirectly by a symbol. If a class is not
referenced, it can be removed.
Instance variables not read and written Checks that all instance variables are both
read and written. This check does not work
for data model classes since they use the
instVarAt:put: messages to set instance
variables.
Method just sends super message Check for methods that forward the message
to its superclass. These methods can be
removed.
Methods equivalently defined in Check for methods that are equivalent to
superclass their superclass methods. Such methods
don't add anything to the computation and
can be removed, since the superclass's
method will work just fine.
Methods implemented but not sent Check for methods that are never sent.
If a method is not sent, it can be removed.
Rule Description
Unnecessary = true Check for an =, ==, ~=, or ~~ message being
sent to true/false or with true/false as the
argument. Many times these can be
eliminated since their receivers are already
booleans.
For example, anObject isFoo == false could be
replaced with anObject isFoo not if isFoo
always returns a boolean. Sometimes
variables might refer to true, false, and
something else, but this is considered bad
style since the variable has multiple types.
Variable referenced in only one method Checks for instance variables that might
and always assigned first better be defined as temporary variables. If
an instance variable is only used in one
method and it is always assigned before it is
used, then that method could define that
variable as a temporary variable of the
method instead (assuming that the method
is not recursive).
Variables not referenced Check for variables not referenced.
If a variable isn’t used in a class, it should be
deleted.
570 VisualWorks
Intention Revealing
Intention Revealing
Rule Description
Assignment to same variable at the end Checks for ifTrue:ifFalse: blocks that assign
of ifTrue:ifFalse: blocks the same variable at the end of the block.
Instead of having the assignment being in
both blocks, we can instead assign the
variable the result of the ifTrue:ifFalse:
message. For example, this code:
aBoolean
ifTrue: [foo := true]
ifFalse: [foo := anotherBoolean]
could be rewritten as:
foo := aBoolean
ifTrue: [true]
ifFalse: [anotherBoolean]
Once we have simplified the expression by
pulling the assignment out of the blocks,
then we could see that the code is equivalent
to:
foo := aBoolean or: [anotherBoolean]
Guarding clauses Checks for ifTrue: or ifFalse: conditions at the
end of methods with two or more statements
inside their blocks. Such methods might be
more comprehensible if they returned self
instead. For example, the following code:
someMethod
a isNil
ifFalse:
[self doSomething.
self doAnotherThing]
might be better represented as:
someMethod
a isNil ifTrue: [^self].
self doSomething.
self doAnotherThing
In the first method, a not being nil looks like
the exception, but most likely a being nil is
the exception which is more obvious in the
second method.
ifTrue:/ifFalse: returns instead of Checks for common ifTrue: returns that could
and:/or:'s be simplified. For example,
foo
aCondition ifTrue: [^false].
^true
can be simplified as:
foo
^aCondition not
Rule Description
Method defined in all subclasses, but Checks classes for methods that are defined
not in superclass in all subclasses, but not defined in self. Such
methods should most likely be defined as
subclassResponsibility methods to help
document the class. Furthermore, this check
helps to find similar code that might be
occurring in all the subclasses that should be
pulled up into the superclass.
Sends add:/remove: to external Checks for methods that appear to be
collection modifying a collection that is owned by
another object. Such modifications can
cause problems especially if other variables
are modified when the collection is modified.
For example, CompositePart must set the
containers of all its parts when adding a new
component.
Unnecessary size check Check for code that checks that a collection
is non-empty before sending it an iteration
message (e.g., do:, collect:, etc.). Since the
collection iteration messages work for empty
collections, the method does not need to be
cluttered with the extra size check.
Uses "size = 0" or "= nil" instead of Checks for methods using equality tests
"isEmpty" or "isNil" instead of the message sends. Since the
code aCollection size = 0 works for all objects,
it is more difficult for someone reading such
code to determine that aCollection is a
collection. Whereas, in the expression
aCollection isEmpty, it is clear that aCollection
must be a collection since isEmpty is only
defined for collections.
Uses at:ifAbsent: instead of Checks for uses of at:ifAbsent: in place of the
at:ifAbsentPut: shorter at:ifAbsentPut: message. For example:
aDictionary
at: aKey
ifAbsent: [aDictionary at: aKey put: anObject]
should be rewritten as:
aDictionary
at: aKey ifAbsentPut: [anObject].
You may also use one of the Code Critic’s
transformation rules to convert these
methods.
Uses detect:ifNone: instead of contains: Checks for the common code fragment:
(aCollection detect: [:each | 'some condition']
ifNone: [nil]) ~= nil
which can be simplified and clarified as:
aCollection contains: [:each | 'some condition'].
572 VisualWorks
Intention Revealing
Rule Description
Uses do: instead of collect: or select:'s Checks for methods using do: instead of
collect: or select:. The collect: and select:
variants are preferred for clearly expressing
intention.
Uses do: instead of contains: or detect: Checks for methods using do: instead of
using contains: or detect:.
Uses ifTrue:/ifFalse: instead of min: or Checks for uses of ifTrue:/ifFalse: when it
max: could use min: or max:. For example:
a < b ifTrue: [a] ifFalse: [b]
may be rewritten as:
a min: b
Uses to:do: instead of do:, with:do:, or Checks for methods using to:do: when a do:,
timesRepeat: with:do: or timesRepeat: should be used.
Uses whileTrue: instead of to:do: Checks for methods using whileTrue: when
the shorter to:do: would work. For example,
this common C-like code:
i := 1.
[i <= size]
whileTrue:
["self do something with i".
i := i + 1]
can be written as:
1 to: size do: [:i | "self do something with i"]
Miscellaneous
Rule Description
Doesn't use the result of a yourself Check for methods sending the yourself
message message unnecessarily. For example, the
following statement doesn't need yourself,
since it is not used:
aCollection addAll: #(a b c); yourself
If this statement were assigned to a variable,
then the cascade with yourself would be
needed to get the value of aCollection.
Inspect instances of "A + B * C" might Checks for methods that might have
be "A + (B * C)" precedence problems. Developers who are
used to other languages often make
mistakes when writing Smalltalk code since
in Smalltalk all binary operations are
performed left-to-right.
Instance variables defined in all Checks classes for instance variables that
subclasses are defined in all subclasses. It is often
better style to move the instance variable up
into the class so that all the subclasses don’t
have to define it.
Long methods Checks for methods that have more 10
statements (this check counts statements,
not lines.)
Methods with full blocks Checks for methods that contain full blocks
or create a context with the thisContext
keyword. These methods are a place where
inefficiencies can creep in. For example, a
common reason why a full block is created is
because a block assigns a temporary
variable that is not defined inside the block. If
the temporary variable is only used inside
the block, then the definition of the
temporary should be moved inside the block.
The "move to inner scope" refactoring can be
used to correct this.
Non-blocks in ifTrue:/ifFalse: messages Checks for methods that don’t use blocks in
the ifTrue:ifFalse: messages. Developers new
to Smalltalk may write code such as:
aBoolean ifTrue: (self doSomething).
instead of the correct version:
aBoolean ifTrue: [self doSomething].
Even if such expressions are correct, they
cannot be optimized by the compiler.
574 VisualWorks
Miscellaneous
Rule Description
Redundant class name in selector Checks for the class name in a selector, e.g.:
openHierarchyBrowserFrom:, which is a
redundant name for HierarchyBrowser.
Refers to class name instead of "self Checks for classes that have their class
class" name directly in the source instead of self
class. Using self class allows you to create
subclasses without needing to redefine the
method.
Sends "questionable" message Check for methods that send messages
which perform low level actions. For
example, using become: throughout an
application should be avoided. Also,
messages such as isKindOf: suggest a lack of
polymorphism.
String concatenation instead of streams Check for methods that use string
concatenation inside an iteration message.
Since string concatenation is O(n^2), it is
better to use streaming since it is O(n) -
assuming that n is large enough.
Unnecessary assignment or return in Checks valueNowOrOnUnwindDo:,
block valueOnUnwindDo:, ensure:, and showWhile:
blocks for assignments or returns that are
the last statement in the block. These
assignments or returns should be moved
outside the block since they return the value
of the block.
For example, the code:
someMethod
| bos |
bos := BinaryObjectStorage
onOld: 'test' asFilename readStream.
[^bos next]
valueNowOrOnUnwindDo: [bos close]
can be rewritten as:
someMethod
| bos |
bos := BinaryObjectStorage
onOld: 'test' asFilename readStream.
^[bos next]
valueNowOrOnUnwindDo: [bos close]
Having the assignment or return inside the
block runs much slower than copying or
optimizing blocks.
Rule Description
Utility method Check for methods that have one or more
arguments and do not refer to self or an
instance variable. These methods might be
better defined in some other class or as
class methods.
Variable is only assigned a single literal If a variable is only assigned a single literal
value value then that variable is either nil or that
literal value. If the variable is always
initialized with that literal value, then each
variable reference could be replaced with a
message send to get the value. If the
variable can also be nil, then it might be
better to replace that variable with another
that stores true or false, depending on
whether the old variable had been assigned.
576 VisualWorks
Code Transformations
Code Transformations
The following predefined code transformations are provided by the Code
Critic (the patterns are defined in class ParseTreeTransformationRule):
Transformation Description
"a >= b and: [a <= c]" -> "a between: b Transform:
and: c" a >= b and: [a <= c]
to:
a between: b and: c
= nil -> isNil AND ~= nil -> notNil Transform = nil to isNil
and transform ~= nil to notNil.
at:ifAbsent: -> at:ifAbsentPut: Transform:
aDictionary
at: aKey
ifAbsent: [aDictionary at: aKey put: anObject]
to:
aDictionary
at: aKey ifAbsentPut: [anObject].
detect:ifNone: -> contains: Transform:
(foo detect: [:a| a test] ifNone: [nil]) isNil
to:
(foo anySatisfy: [:a | a test])
Eliminate guarding clauses Transform methods ending with an ifTrue: or
ifFalse: that have multiple statements inside
the block, replacing them with ifFalse: [^self].
followed by straight-line code that was inside
the block. For example:
someMethod
a isNil
ifFalse:
[self doSomething.
self doAnotherThing]
is transformed to:
someMethod
a isNil ifTrue: [^self].
self doSomething.
self doAnotherThing
Eliminate unnecessary not Transform:
aTest not ifTrue:
to:
aTest ifFalse:
Transformation Description
Move assignment out of showWhile: Transform:
blocks Cursor busy
showWhile: [x := self someLongCalc].
to:
x := Cursor busy
showWhile: [self someLongCalc]
This eliminates a full block.
Move assignment out of ensure: blocks Transform:
[x := self aCalc]
ensure: [self close]
to:
x := [self aCalc]
ensure: [self close]
This eliminates a full block.
Includes the ifCurtailed: variation.
Move variable assignment outside of Transform:
single statement ifTrue:ifFalse: blocks aTest ifTrue: [x:=1] ifFalse: [x:=2]
to:
x := (aTest ifTrue: [1] ifFalse: [2])
Rewrite ifTrue:ifFalse: using min:/max: Transform:
a < b ifTrue: [a] ifFalse: [b]
to:
a max: b
(includes many variations)
Rewrite super messages to self Transform:
messages when both refer to same Singleton class>>default
method ^super new initialize
to:
Singleton class>>default
^self new initialize
if Singleton class does not define new.
Use cascaded nextPutAll: instead of #, Transform:
in #nextPutAll: aStream nextPutAll: 'any ', 'time ', 'now '.
to:
aStrean nextPutAll: 'any ';
nextPutAll: 'time ';
nextPutAll: 'now '.
578 VisualWorks
Index
button 42 Application settings, defined 196
button 42 arc function 381, 395
Symbols argument variable 80
arithmetic operations 342
ArithmeticError class 259
$ 431 array
* (multiplication) 342 defined 76
** (power function) 342 expanding 362
- (minus) removing an element 365
collection subtraction 369 size 359, 362
numeric subtraction 342 Array class 357
+ (plus) assigning variable values 90
numeric addition 342 association
.st files 141 in a dictionary 358
/ (division) 342 removing from dictionary 365
// (integer division) 342 Association class 358
<Operate> button 27
<Select> button 27 B
<Window> button 27 Bag class 357
\ (division remainder) 342 baseline in text 464
^ 110 behavior, defined 65
’ (single quote) 433 Bezier class 399
A Bezier curve, defined 399
binary file
A 271 See also BOSS
abbreviating a string 444 BinaryObjectStorage 484
aboutToQuit 296 bitmap, defined 404
aboutToQuit event 296 block expression 98
aboutToSnapshot 296 BlockClosure class 112
absolute value function 342 bold text emphasis 454
abstract class 71 boolean
access date of a file 473 values 77
activate 208 Boolean class 112
adding BOSS
class definition 50 retrieving contents of a file 486
elements to a collection 360 retrieving specific objects 488
method definition 50 searching for an Object 487
aligning sequential access 487
text 448 skipping the initial scan 486
appending storing a class 489
a string 436 storing objects 484
text to a file 476 storing objects in a file 484
application stream positioning 485
framework 185 using custom storage formats 492
model 185 versioning 491
580 VisualWorks
Index
582 VisualWorks
Index
584 VisualWorks
Index
586 VisualWorks
Index
text V
adding emphasis 454 variable
aligning 448 argument 80
boldfacing 454 assignment 90
changing case 454 defined 65
color 465 instance 81
comparing 452 system 469
creating 445 temporary 79
displaying 446 variables
font family 458 workspace 40
font size 455 VariableSizeTextAttributes class 461
indents and tabs 448 version
line spacing 464 of a BOSS file 491
printing 450 version control 172–180
replacing a subtext 451 virtual machine 32
searching 450 command line switches 562
size 435, 451 debugging and deployment 560
storing in file 475 VisualComponent class 220
string 445
TextAttributes 446, 461, 465, 466 W
See also text style Warning class 259, 268
thickness of a line 401 weak array 281
tile finalization 282
pattern 416 WeakArray class 281
phase 422 WeakDictionary class 285
time wedge 396
creating 351 while loop 115
zones 353 width
time stamp 352 of a string 435
Timestamp class 352 Window class 382
tools WordArray class 357
Debugger 242 working directory 472
Settings Manager 59 Workspace 39
Workspace 39 workspace variables 40
transcript 39 World Wide Web 29
translating exceptions 269 write stream 480
translation protocol 386 WriteStream 436
true 77 X
truncating a string 444
typographic conventions 26 XML
accessing elements 524
U add attribute 530, 543
unary message 93 add element 529
underline emphasis 454 add text 531
uninstalled code 161 attributes 527
unloaded code 161 build document 528
unwind protection 269 children 526
user interrupt 253 document fragment 541
User settings, application-specific 196 DOM 521
User settings, defined 196 DTD 522
error handling 548
parser drivers 523
parsing 522
processing instruction 531
root 525
SAX 537
SAX event handler 537
SAX2 539
Schema 523
select elements 526
stylesheet 543
validating 523
XML support 521
XMLParser class 522, 524
XSL, See XML
Z
ZeroDivide class 259
588 VisualWorks
Method Index
Symbols B
= 112 baseline: 457, 464
== 112 beCurrentDirectory 472
A binary 93
binaryReaderBlockForVersion:format: 491
abs 342 boundingBox:startAngle:sweepAngle: 397
actionForEvent: 219, 222
activate 212 C
add: 359, 360 canBeWritten 482
add:before: 361 canTriggerEvent: 223
add:beforeIndex: 361 centered 448
addAll: 362 changed:with: 388
addAll:beforeIndex: 362 characterAttributes: 457, 464
addAllFirst: 362 characters: 538
addDays: 349 children 524
addFirst: 361 close 476
addTime: 352 collect: 377
after: 373 colorPalette 426
allBold 454 colors 425
ambivalentEventChecking 220 completeContentsOfArea: 411
anyElementNamed: 525 composeDiacritical: 432
anyElementsNamed: 525 compositionWidth: 447
appendStream 476 constructEventsTriggered 221
asComposedText 445, 480 contentHandler: 524
asDays 349 contents 486
asDouble 344 contentsOfEntireFile 474, 479
asFilename 469 contractTo: 444
asFiller 392 convertForGraphicsDevice: 407
asFixedPoint: 344 convertForGraphicsDevice:renderedBy: 428
asLowercase 434 convertToPalette: 425
asPattern 421 convertToPalette:renderedBy: 429
asRational 344 copy:from:in:rule: 415
asRetainedMedium 408, 411 copyArea:from:sourceOffset:destinationOffs
at: 371, 372 et: 412
at:ifAbsent: 372 copyEmpty 414
at:put 366 copyFrom:to: 368, 440
at:put: 361 copyReplaceAll:with: 442
atAllPut: 366 copyReplaceFrom:to:with: 441
atEnd 487 copyTo: 477
atFeature: 539 copyUpTo: 441
atPoint: 409 copyWith: 362
atPoint:put: 409 copyWithout: 365
atProperty: 539 coveragePalette 426
attributes 525
D forgetInterval: 488
dates 473 fork 276
dayOfMonth 349 from:to: 391
daysInMonth 349 fromFile: 406
daysInYear 349 fromSeconds: 352
defaultAction 265 fromUser 406
defaultDirectory 472 G
degreesToRadians 344 gridForFont:withLead: 459, 464
delete 477
detect:ifNone: 373 H
directoryContents 474, 479 halt 253
displayArcBoundedBy:startAngle:sweepAngl handlers: 523
e: 397 hasActionForEvent: 223
displayDotOfDiameter:at: 396 hue:saturation:brightness: 419
displayLineFrom:to: 391
displayOn: 446 I
displayOn:at: 407 ignorableWhitespace: 538
displayPolygon: 392 image 406
displayPolyline: 392 imageFromFile:toClass:selector: 406
displayWedgeBoundedBy:startAngle:sweep includesAssociation: 371
Angle: 397 includesKey: 371
do: 376 indexOf: 371, 439
document 524 indexOfSubCollection:startingAt: 372
dropFinalVowels 444 isDirectory 473
dtdHandler: 524 isEmpty 370
dumpFailedMsg 328 isInteger 343
isNil 77
E isResumable 268
elementNamed: 524 isZero 343
elementsNamed: 525
emergencyAbortText 328 J
emphasizeAllWith: 454 justified 448
emphasizeFrom:to:with: 454, 455 K
endDocument 537
keyAtValue: 372
endElement: 538 keysAndValuesDo: 377
endPrefixMapping: 538
ensure: 485 keysDo: 377
entityResolver: 524 L
errorHandler: 524 lastIndexOf: 372
errorOccurredMsg 328 leftFlush 448
even 343 lineGrid: 457, 464
eventsHandled 223 lineWidth: 401
exists 471 ln 345
exp 345 log 345
F M
family: 459 magnifiedBy: 412
fileSize 471 main 209
findDefaultDirectory 472 makeDirectory 471
findString:ignoreCase:useWildcards: 439 makeUnwritable 482
findString:startingAt:ifAbsent: 439 makeWritable 482
firstIndent: 448 model: 389
590 VisualWorks
Method Index
signalWith: 263
size 370, 435
skippedEntity: 538
sort 375
sortWith: 375
sourceMode: 490
spellAgainst: 438
sqrt 342
startDocument 537
startElement: 538
startPrefixMapping: 538
strictlyPositive 344
styleNamed: 446, 456
styleNamed:put: 465
subtractDate: 349
T
tearDown 209
textStyle: 449, 456, 457, 464
thisContext 91
tilePhase: 422
today 347
triggerEvent: (and variants) 215
U
update:with: 388
userInterruptMsg 328
useTabs: 449
V
validate: 522
valueAtPoint: 409
valueAtPoint:put: 409
vertices: 392
W
wait 278
weekday 349
when:send:to: 216
widget:when:do: (and variants) 223
withAll: 360
withColors: 425
withCRs 445
withText:style: 446
wordWrap: 447, 453
writeStream 476
Y
yourself 359
592 VisualWorks
Reader Comment Sheet
Name:
Job title/function:
Company name:
Address:
How often do you use this product? # Daily # Weekly # Monthly # Less
How long have you been using this product? # Months # Years
FAX
Can you find the information you need?
Please comment.
# Yes # No
IT!
Is the information adequate to perform your task? # Yes # No
Please comment.
General comment:
P46-0101-10