Squeak
  links to this page:    
View this PageEdit this Page (locked)Uploads to this PageHistory of this PageTop of the SwikiRecent ChangesSearch the SwikiHelp Guide
Old Ideas/Projects of Anthony Hannan
Last updated at 3:04 pm UTC on 12 July 2005
See Anthony Hannan for current projects.

Secure Layered Environments - Nov 2002

Environments (inspired by Us, PIE, and PerspectiveS) provide global namespaces, version control, and access control. An environment is just a behavior (with multiple inheritance) whose methods return other behaviors and global objects. Each process runs with respect to an environment instance. Global variables, class pointers, and superclass pointers are just selectors sent to the process's environment, giving each environment a chance to override behaviors and global objects with different versions.

The VM prevents updates to objects reachable from inherited environments, so a process can only update objects in its own environment. One environment may inherit (and therefore see objects) from another environment only if it has permission. Likewise, a process may be started in an environment only by methods/users that have permission.

Secure Environments Example: Money
Requirements
A MoneyAccount object would contain a balance and an owner, and have behavior to transfer money from one account to another. A transfer should only be allowed if the sender is equal to the account owner. No other manipulation should be allowed.

Solution using Secure Environments
The Money environment would contain the publicly accessible MoneyAcount behaviors: 'newFor: owner' and 'transfer: amount to: account'. User environments that wished to use and exchange money would inherit from this Money environment. The #transfer:to: method would check that the process's environment is the same as the receiver's owner (remember top-level environments represent users). It is impossible for a user to run a process with respect to another user's environment, so it can't be an imposter. After the #transfer:to: method confirms that the the owner matches the environment, it sends 'run: [self privTransfer: amount to: account]' to the PrivateMoney environment. PrivateMoney, which also inherit from the Money environment, checks that the sender is the particular #transfer:to: method, then executes the block with respect to itself. This environment would contain the #balance: method so they can be changed. No other senders would be allowed to execute a block in this environment, and no environments would be allowed to inherit from it.

Of course, low-level security has to be implemented. In particular, the actual storage of balances in memory and on disk has to encrypted, or held remotely on a trusted server and communicated with via distributed objects. Also, meta behavior like #instVarAt:put: has to reside in protected environments or be eliminated altogether.

Morphic Inspectors

Kind of like MathMorph's MorphicWrappers but more like a cross between the Self object interface and visual programming. Another way to look at it is as a command line interface to objects or an object spreadsheet. I picture it replacing the static tools we use today such as the browser which usually takes up valuable space displaying more than you need. It is probably along the lines of the omni-user interface envisioned for Squeakland, and I hope to meet with those guys soon. Anyway, I have some various versions of it coded but I am always revising it and still not close to where I envision it with environments/contexts, zooming interfaces as opposed to overlapping windows, automatic conversion of visual contexts to objects, etc.

Squeak Socket

SqueakSocket passes objects between images quickly using young-only image segments and maintain remote proxies for objects left behind. A desired object and all otherwise-free young objects that it points to are transfered to the other image. Non-free and old objects it points to stay behind while remote proxies for them are transfered in their place. SqueakSocket manages these remote proxies and even deletes them when garbage collected on either end. This implementation is mostly complete. The only unresolved issue is when to replace a remote proxy refering to an environment object such as a class, with a reference to the local copy of that environment object. Are they really the same object, one if one changes? This leads to the following module discussion.

Distributed Squeak and Modules

Module issues arise when trying to execute a process across mulitple images. Should we just use the environment (classes) of the local image. What if the object moves or is copied to another image, should it adopt the local class or bring its class with it? Or should a module be associated with a process and have the process lookup all classes in that module. The module would have to follow the process around and loaded dynamically into images that don't already have it. I am still struggling over these issues and am looking forward to Henrik's modules although I don't think he is addressing distributed processing.

Integrated C and Smalltalk Modules with Transactions - Oct 2001

After reviewing the VM plugins and keeping up with the discussion on modules, I have come up with some different points of view for modules that I would like to share with you. In my analysis, I first looked at how C modules work and how they can be improved, and then extended this to Smalltalk modules. In this approach, I am attempting to build a modular structure that contains C modules at the base and Smalltalk object modules on top of them.

C Modules
Currently, the module interface of a C object file (.o file in unix) is its symbol table containing names of functions and variables that it is available to other modules (exporting) and names of functions and variables it is using from other modules (importing). All names in all modules are in one global name space, and any module can define named function/variables to be imported. If more than one module defines the same name, the linker will choose the one it sees first.

Adding Imported Modules
A C module does not know who will fulfill its imported names, ie. it has no-sub modules or imported modules. The imported modules are specified in the makefile. This is flexible but in reality the module programmer usually knows which modules s/he will be importing. It would be cleaner if this importing knowledge was kept with the module instead of spreading it to the makefile. We can avoid loosing the module freedom by still allowing any dependent module to override a named definition of a module it imports. For example, if MyModule imports the StandardMath module that defines factorial, MyModule or any module that uses MyModule may redefine factorial causing even StandardMath functions that use factorial to use the new definition. The graph of imported modules eliminates the need for a makefile, and a particular "program" configuration becomes just a single module (of course including its ancestory of imported modules).

Single Relative Name Space and Overriding Names
All names in the entire ancestory of imported modules are visible to a particular module, creating a single name space. However, two independent modules can define the same name, and they can even be imported into the same third module as long as the third module does not use the duplicated name directly. For example, an AlternativeMath module may define its own factorial function and MyModule may import both the AlternativeMath module and StandardMath module. There is only an ambiguity if MyModule calls factorial directly (which one is it calling?). But it is ok for it to call it indirectly via a function in AlternativeMath or StandardMath; the appropriate factorial function will be used depending on which module is calling it (like Self Language's sender path tie breaker rule). Also, MyModule may redefine factorial overriding the factorial functions in both AlternativeMath and StandardMath modules, so functions in either module that use factorial will now use the new factorial function.

Adding Super References and Resolving Ambiguities
When redefining functions/objects we sometimes want to use the old definition in our new definition. To do this we reference the old definition by prefixing the name with the module name where it resides. For example, we can call the old factorial function from inside our new factorial function by writing "StandardMath.factorial". This prefixing technique can also be used to resolve ambiguities in a name space (see above).

Smalltalk Modules
A Smalltalk module is structured the same as the enhanced C module described above. It will name and define a set of objects, and these objects can reference and/or override named objects in imported modules. For example, a HTTPModule may import StringModule and override String to add a asUrl method to it. All dependent and ancestor modules of HTTPModule will see the new class when referencing String, while other modules (programs) will not.

Self-like Module Behavior
A new class of objects called Module will hold Smalltalk modules and C modules. A module contains a named set of objects (a dictionary) and a set of imported modules. A module behaves like a Self Language object; its named objects are directly accessible by sending names as messages to it, and if the accessed object is a method the method will execute. Also, like in Self, if the name is not found locally, its parents (imported modules) will be searched, and so on. This Self like behavior is equivalent to C behavior after the modules are linked, except that in C you can only send message to yourself or your imported modules (ie. all functions are in the same name space). So in the Slang, we will limit calls to 'self' only (like we do today). Even with this limitation we will have more flexibility than we currently do because of the multiple inheritance (multiple imported modules). (Another difference between Self and C is the sender path tie breaker behavior. We can implement this in C utilizing mangled names and/or private (static) functions).
Smalltalk modules will have the same Self-like behavior, but most of their named objects will probably be classes and other globals rather than methods. A new pseudo-variable named 'environment' will return the current "program" module of the current process. The compiler will lookup globals in the defining module which will always be an ancestor of the current module (environment). When a message is sent to a Module by name instead of via 'self' or 'environment', the named module becomes the new environment (new name space) for the current process until the call returns.

Loading
We want to be able to run any C or Smalltalk module and have it automatically load what it needs. The executable 'squeak' will take a C or Smalltalk module as its first argument (no image file anymore) and a C or Smalltalk expression as it second argument to be executed once loaded. The expression may be omitted in which case main() will be called if the first arg is a C module or 'Processor activeProcess' will be continued if the first arg is a Smalltalk module.
C modules are stored as regular C object files (.o files). To make them work with 'squeak' a second .i file that lists imported modules must assist every .o file (with the same first name). 'squeak' will have an additional command line option specifying the path of directories to search for modules. If the option is not specified then the environment variable SQUEAKMODULES will be used as the search path. If the given module is a C module, 'squeak' will find all imported modules, link together an executable, and then execute it.
Smalltalk module files will have the .s suffix and are stored in image segment format. The named objects will be the roots, and references to named objects in imported modules will be the out pointers.
'squeak' starts a .s module by starting the C module called SmalltalkModuleLoader.o with 'loadThenExecute( <.s file>, )' as its initial expression. This will initialize ObjectMemory, load the Kernel Smalltalk module, load the specified .s file, create a new Smalltalk process for the specified smalltalk expression or fetch the last activeProcess, and finally call interpret(). Out pointers to objects in modules not loaded yet will point to proxies that will load the module on demand (like current ImageSegmentRootStubs), if its a C module it will load like a dll. The Kernel Smalltalk module will consist of all the objects that hang off of the specialObjectsArray minus the Smalltalk dictionary.

Changing
Like a transaction, changes are made only to the current module (environment). Any changes made to objects in imported modules are treated as overrides by the current module. This is accomplished by catching stores to original objects (old objects excluding recently tenured objects) and remembering the slots old value before replacing it with its new value. For example, adding a morph to World (assuming that the named object, World, resides in an imported/ancestor module of the current module) will add the World object as a key in the current module's overideDictionary with the changed slot number and old value as its value. When the process changes modules or a new process starts running under a different module, the current module swaps out its new values and puts back the old values then the new module swaps in its new values.

Saving
Saving the current module (environment) will save its local named objects plus its overrides to ancestor modules. It will store the latter as associations of FieldPaths to values for every overriden slot. A FieldPath is a sequence of fields to follow from a global to an object, in this case, specifying the path to the changed slot. Also, any other pointers from objects in the current module to objects in imported modules will be converted to FieldPaths before saving. So for example, saving a current module that added a morph to World will save the association, #(World submorphs) -> {newMorph. #(World submorphs 1). #(World submorphs 2) ...}, in its list of overrides. (The new submorphs array has references to original morphs in the old submorphs array).

Committing/Migrating
Rather than saving changes to the current module, changes can be migrated to its imported modules, making the current module behaves like a transaction. The current modules override dictionary can be handed down to one of its imported modules (or split up among them), also its new named objects can be handed down as well.

Versions
Version numbers can be added to module names as in Module.4.12.1, its filename would be Module.4.12.1.s or Module.4.12.1.o. When the version number is not specified the module (file) with the highest 2-digit version number is used. In other words, "Module" would retrieve Module.4.12 before Module.4.11 but never Module.4.12.1. The third digit is reserved for work-in-progress versions.

Simulation, Compilation and Binding
A C module can either be in real or simulated mode. When a C function is called from Smalltalk and the C module is in real mode, the compiler generates a special bytecode that will call the native C function. If the C module is changed to simulated mode, callers of its functions are re-compiled to do a normal Smalltalk call, and the C module behaves like a Smalltalk module.
Because overrides are changed at their original locations (see Changing above), we can bind at compile time. For example, a reference to World will hold the World's association in the compiled method, and a reference to a C function will hold the native function address in the compiled method (of course this address will be converted to a FieldPath when saving it to disk).
Any C object file can be a Squeak C module, as long as it's self contained like a library or has an associated .i file to find other linked files needed. To make it run in simulation mode we have to have an associated .s file, or .c file (and a C to Slang translator).

Conclusion
With modules structured as layers of objects we automatically get transaction behavior, changeset behavior, name spaces, and component/project behavior, in addition to loadable code libraries. Objects, not classes and methods, are the modules' elements, enabling modules to be components/projects as well as source code groupings. The Self behavior gives modules Environment behavior but with multiple inheritance. And the close resemblance between C and Smalltalk modules will allow easier VM maintainance, and, in fact, blurs the lines between VM and image, making Squeak one big modular system written in two different languages: C/Slang and Smalltalk.
Of course, this framework is not much different then Henrik's (we're all talking about modules), but the advantages I see with this one is:
Only one kind of module (no Delta modules);
Only one kind of imports (no sub modules and parameter modules);
All objects are modularized, not just classes/methods;
VM code is included in same modular framework (no separate plugin framework).

Sytem Modules - July 2001

A SystemModule is a set of environment variables (classes) and methods with a globally unique id. It inherits from one or more other SystemModules. The resulting directed graph is searched in topological sort order when looking for a variable or method. Ambiguities are resolved at module creation time by renaming and rewriting its senders to use the new name.

SystemModules can be transferred between images or loaded from files. Inherited modules (prerequisites) are also transfer/loaded if not already in the image.

Changing or adding a method or global to a module requires creating a new module, which may be a copy of the original or inherit from it. A new special variable called 'predecessor' refers to self but looks up the message sent to it starting in the inherited modules of the module that defines the current method. It is analogous to super but for modules. 'thisProcess' and 'thisEnvironment' are also new special variables that refer to the current process (same as Processor activeProcess) and the current process's environment (a module plus all inherited modules make up an environment)

A module and all of its elements are immutable, so they are consistent across images. Immutability is enforced by putting immutable object in the front of old space below endOfReadOnlyMemory. primitiveAtPut and storeReceiverVariableBytecode will check the address before storing. New immutable objects are added to readOnly portion of memory by swapping it with one or more old object after endOfReadOnlyMemory and incrementing it accordingly. Immutable objects can never be made mutable again.

A process runs relative to a module and looks up message selectors and global variables in it and its ancestors (inherited modules). For efficiency, when the process is first created, the module and its ancestors are flattened into a single RuntimeEnvironment. In the RuntimeEnvironment, method literal selectors are converted to SelectorBehaviors that point directly to the set of methods that implement the selector keyed by method class (inverse of current Behaviors). Each SelectorBehavior contain visible methods from all inherited modules for that selector. So method lookup just involves a hash lookup on the receiver class and its supers until the right method is found.

In fact, the Interpreter's method cache can be replaced by a single recent method cache in each SelectorBehavior. The first slot can contain the last receiver class and the second its method. This cache would be even faster then the current method cache (one comparison versus two and no hash misses).

Distributed Squeak
Two Squeak images can connect to each other and share the same RuntimeEnvironment. This is done by swapping needed modules and then creating a RuntimeEnvironment in each image that share the same volatile objects. An image can point to an object in a remote image by using a RemoteProxy. With both images "sharing" the same RuntimeEnvironment blocks and objects can move between them and execute exactly the same.

When a message is sent to a RemoteProxy, the message's image segment is extracted and forwarded to the remote image where it is executed. The result's image segment is sent back. Image segments are used so all free components of an object or message can be transferred. Also, if an object is garbage collected that is still referenced remotely then the object's image segment is migrated to the remote image.

Image segments are extracted by a primitive that marks all objects except those shadowed by the roots and then traces the roots. This is time consuming for retrieving the full segment. But if we just mark young objects we can extract a partial segment of young only objects, quickly. (Its like doing an incrementalGC vs. a fullGC).

If we wanted to make full segments quick then we would have to group objects together in memory by segment (like the previously proposed Persistent Segments, below).

Previous Persistent Segments proposal of February 2001

Objects are grouped into segments, and segments are grouped into databases. A segment can be protected from read or write using security permissions. The traditional image is partioned into segments and stored in a database. The new image swaps segments in and out of a database(s) while running. A Segment (aka. ImageSegment) consists of an array of roots, internal objects (only reachable from roots), and a set of OutPointer proxies that refer to roots of other segments. An OutPointer contains the database address, segment number, and root index of the object it is standing in for. An OutPointer exists only when one or more objects in the segment point to an object outside the segment. A roots exists only when one or more objects outside the segment point to it. Every object must belong to exactly one segment. New objects are added to the first segment that points to it, and objects can move segments.

When an OutPointer proxy receives a message, and the segment it refers to is not yet loaded, it forwards the message to the database and waits. The database, another running Squeak, with all its segments virtually loaded into its image, will execute the message on the client's behalf as long as it does not change any object in any segment. If a change is attempted, execution is aborted and the segment of the original object is returned to the client where the message is re-executed. If no change is made (ie. the message is just a query) the execution result is returned to the client. If the result is an old object its whole segment is returned, otherwise, the new result is extracted (using ImageSegment), converting old object references to OutPointer proxies, and returned. During execution, any messages sent to segment roots that are already loaded at the client are forwarded back to the client in case the client has changed the segment in a way that may affect execution. Also, any messages sent to OutPointers that refer to another database are forwarded to that database. Like before, if execution attempts to modify any established object, even on the client, execution is aborted. This is necessary in case the same execution thread later attempts to make a change back on the database, in which case the whole execution needs to be rolled back. Allowing the database to execute "queries" avoids unecessary loading of segments in clients while still allowing messages like 'Smalltalk allImplementorsOf:' to be used.

When a new segment is loaded into the client an incrementalGC is executed, surviving young objects are move up by the size of the new segment, the segment is loaded at the end of old space, and its internal pointers are adjusted for the new memory location. While adjusting pointers, pointers to OutPointer proxies that refer to loaded segments are immediately replaced by the roots they refers to. All other OutPointer proxies in the image that refer to the new segment's roots will become one with them (if this is too expensive then this can be put off into a low priority process while OutPointer proxies forward messages to their roots).

When a new object is first referenced by an established (old) object, the new object and all virgin objects reachable from it will point to the established object's segment via its segment header, a new header word that only new objects will have. A virgin object is one with no segment yet, and an established object is one with a segment, young or old. Objects loaded with the original segment don't need the extra segment header because its segment can be determined from its address in memory. The orginal segment takes up a contiguous block of memory, and the first object in that block is always a SegmentHeader that knows the segment size and points to its roots and outPointers. A new specialObjects member, called loadedSegments, points to all the loaded segment headers and is (binary) searched to find the segment of an old object. If a matching segment is not found then we can safely assume the object was once young and find its segment in its segment header.

Upon commit all segments are decoupled and sent to the database(s) as a single transaction. The details of the commit are as follows: A fullGC is executed. Memory is scanned from beginning to end converting pointers that cross segment boundaries to OutPointer proxies (decoupling). Each segment (including all its new objects) is copied and compacted, freeing the original segment. They are copied to young space unless there is enough room in old space as a result of previously freed segments. All segments are then compacted together to the beginning of memory and sent to the database. Segments can then be coupled back together as if they were just loaded (or put off to a low priority process as mentioned before).

A new segment can be created by specifying a set of roots and extracting out all objects only reachable by those roots (ie. current ImageSegments). This creates a block of new objects at the end of old memory with a new SegmentHeader in front. All the original roots become one with the new roots, leaving the original roots and its free children to be garbage collected.

A special OutPointer proxy called a ClassOutPointer is used for refering to classes. This is needed so a client can use his own customized methods of a well known class on an imported object. A ClassOutPointer contains the location of its original class plus its name and all instance variables. When a ClassOutPointer is converted to its class, it first looks in the Smalltalk dictionary for a class of that name with those instance variables. If there's no match the orginal class's segment is loaded and used. Compact classes and its instances are always assumed to have the well known structure.

A segment has two identifiers: its original database address (hostName and port) and its segment number. For efficiency, mirrors of well known segments will reside in most databases. If a mirror is changed, the change is sent to the original and all mirrors are updated (remember, you must have permission to change a segment, even a mirror). Typical well known segments would include all the classes in a default image partitioned by system category, and a core segment holding all the constant objects that reside in the specialObjectsArray (like nil, true, false, compact classes, etc). Typically, these well known segments will be write protected (writeable only by SqueakCentral) and you would make changes to copies of these segments and submit change sets as usual. Of course, you can also make your database directly accessible to the public with your new segments on it.

Your commited "image" would be shrunk down to a start segment containing the Smalltalk dictionary with the Processor holding the suspended active Process and the World containing the saved screen work; most of the class vars would contain OutPointers. However, you will still be able to take a snapshot that will save your work-in-progress image to a file without commiting any changes to the database.

I really think Squeak needs persistence so we can start building a world wide web of objects, and I don't think the above will slow things down too much.

Squeak-Thor - Oct 2000

Thor, developed at MIT, is a distributed, object database system consisting of two kinds of processes: client Front Ends (FE's), and server Object Repositories (OR's). You can think of an FE as a Squeak VM and an OR as a Squeak image. However, multiple FE's can acceess multiple OR's, concurrently, and objects in an OR can reference objects in other OR's. This allows a world wide web of objects that can be accessed concurrently and consistently by a world of users. When a user wants to let the world see what changes he has made he commits his transaction (like saving an image).

No special code is needed for fetching or saving objects. When an object pointer is followed in the code and that object has not yet been loaded into the FE, a fetch is automatically sent to the OR where the object resides. The OR returns the disk page that the object resides on to the FE. The FE caches each object on the returned page and updates object pointers. A whole disk page is returned because it is cheap to fetch and send a whole page and it is likely that objects on the same page will be needed in the future. Like virtual memory, objects that are no longer used (and not changed) are dropped from the cache to make room for new objects coming in. For object-oriented applications like a CAD program, MIT benchmarks have shown that Thor is faster than C++ accessing data in files (even memory-mapped files).

The objects in Thor are implemented in Theta, a programming language similar to Java, and designed in conjunction with Thor at MIT. Every variable and method must have a type. Types are just interfaces and classes implement those interfaces. Unlike, Java, Theta supports parametric polymorphism and basic types are real objects. To optimize message sends, the Theta compiler creates a method dispatch vector for each type a class implements. The runtime system also does further customization when it discovers that there is only one implementation of a message. It changes the message send to a direct procedure call or directly inlines the procedure.

I've tried replacing the ObjectMemory in the VM with an interface to Thor, but it was too slow. For every slot that was read from memory in Squeak a method was invoke in Theta to retrieve the slot from the Thor object that represented the Squeak object. I had 5 classes of objects to represent Squeak objects: PointerObject, WordObject, ByteObject, PointerThenByteObject (for CompiledMethods), and SmallIntegerObject. Instead of having this extra layer between Squeak and Thor. I want to tightly integrate Squeak and Thor.

The Theta compiler highly optimizes the code to make up for the slow down of using the object level persistence. I would like to do the same with Squeak. But to do significant optimization we will need to add type information to Squeak. I'm thinking of adding optional types and type inference, and optimize where we can.

NYC Meeting - March 2002

Time: Monday, March 25 2002, 4 pm
Place: Starbuck, corner of 29th & Park Ave South
Who: Luciano Notarfrancesco (lnotarfrancesco@yahoo.com), Larry Kellogg (larryfromdc@yahoo.com), Michael Cole (cole@nimiqinc.com), David Siegel (dsiegel@acm.org), Anthony Hannan (ajh18@cornell.edu)

Below is a tentative list of topics to discuss with short descriptions. Please add to this list. What we end up with by Monday will be an outline for our meeting.

Layers

ajh - An image can be broken down into changeset/layers. Many layers are the same between images and can be shared. Most layers are immutable except for the top level "user" layers. Each layer specifies a sequence of other layers to load before loading itself. A layer loads new objects plus makes changes to existing objects in previous layers. A layer's changes are a sequence of message sends where the receiver and args are object references. An object reference consist of the global id of the layer that introduces that object and the position of that object in the layer's segment. When a layer is loaded its segment of new objects is kept on disk until a message is sent to one of them then the whole segment is loaded. Changes to objects are associated with the current user layer (.image file). The user can change his image at anytime causing layers to be reloaded.
We need a way to distribute and organize layers over the Internet. Some sort of distributed database. It can be simple because there is only one type of query (get layer with id) and most layers will be immutable. The only ones that won't be immutable are the user layers that will be updated to point to new layers.

How do these considerations relate to the already implemented ImageSegments. What are their strenghts, what are their drawbacks today? What about the ".pr" files (see Project)?

ajh - ImageSegments don't refer to other ImageSegments that it depends on. Also, ImageSegments can not specify changes it only specifies new objects.

dms - When do layers become immutable? From the description, it sounds like this occurs on first save. If I'm working on a project, it sounds like I must organize it as an array of layers, with the first layer representing the first version of the project, and each successive layer representing modifications to the project. If layers are immutable, though, how can I ever remove anything from my project? I must be misinterpreting your intent.

ajh - A layer becomes immutable when it is published. All layers that it depends on most also be immutable. "Open" layers (top-level "user" layers or "images") have to be treated specially because they can change but they are also need to be shared. You can change open layers, but to change a published layer you have to override it with another layer and add that layer to the user/image layers you want affected.


Functionality Modules

ajh - A functionality is a namespace for globals and "global" selectors. A "global" selector is one that can be sent from any method. "Local" selectors can only be sent from methods of the same class.

There are already private/local selectors. They just need a special prefix which is not well known

ajh - I think it would still be nice to have selector namespaces, plus it helps organize them better than ad hoc method categories.

ajh - A class implements both "global" and "local" selectors. Functionalities thus provide a small well-defined interface for clients to use. Note: a single functionality defines the entire interface for all players in that function, not just for one class of players (a functionality is the sum of many "class" interfaces). Functionalities will be nested in a hierarchy. Selector and global references will be disambiguated if necessary by providing a relative path from the defining method's functionality to the target selector/global's functionality. A method's functionality is defined as the functionality that holds its selector, or if the selector is "local" then the class's functionality is used. A class's functionality is the functionality that holds the class as a global.

ajh - VM C modules (instead of using a separate plugin framework) could be incorporated into the same Functionality module framework, intergrating Smalltalk code with C code. C modules in the Kernel layer will make up the base VM, while C modules in other layers would be loaded dynamically as libraries.

Jitter

ajh - For the (obsolete) VI4 I'm thinking of implementing my own simple Jitter that does not use the C stack but just uses the same code as the bytecode expressions. I'm also interested in discussing when JIT translation should be done (versus just interpreting it) and how to cache it. I've looked at Ian's Jitter code but all that C code is confusing without a "class browser".

len - I like this project. It's a shame that Jitter was implemented mostly in C (and the last time I looked I saw also a lot of C++). It's not that hard, actually, to implement a pure Squeak Jitter. I know how to do it for X86 at least, and I could probably do it for SPARC too. Very interesting ....write more...

One could consider as well directly to compile to machine language without using byte codes. The assembler languages of todays microcomputers is really powerful given the wealth of 32bit and 64 bit commands. Couldn't it be that considerable speed gains are feasible? One could start with a subset like Slang or - a more application oriented example - the subset of Smalltalk used to do turtle graphics in StarSqueak.

Context Views

ajh - Instead of a Workspace, use visual programming to show expressions and intermediate values. Drill-down to see called contexts. Drilling-down on an object shows its #showElements context which will list all of its vars in a loop. Each iteration in a loop shows a new context thus displaying a table of context views, one for each var.

Suggestion (hh) A warming up exercise (XP style): implement a workspace (a SystemWindow) with three panes and post it to the list


len - This seems in synch with Morphic Wrappers. I'd like to discuss this more deeply.

hh - I can not attend but attended the Berne / Switzerland meeting about which I wrote a report. Francisco (Pancho) Garau, the current maintainer of MorphicWrappers attended there as well. I would be interested in knowing your opinions on the conclusiions there.

MorphicWrappers: think of it as well as a general morphic direct manipulation package which can replace everything (browsers, inspectors, workspaces).




Micro Squeak

len - I'm interested in building very small images and VMs. Striping out all io primitives I got a 64k VM statically linked in linux. About the image, I think we're going to get pretty small images once the image is well partitioned in Modules. I guess the minimal image will essentially consist of the classes defined in the ANSI Smalltalk standard. Other tricks to reduce the image size could be: replacing MethodDictionaries by Arrays and do linear search, coalesceing literals, replacing selectors by SmallIntegers or 16-bit integers included in the CompiledMethod after the byte code.

Multiple memory spaces

len - Changing the current ObjectMemory model to handle multiple memory spaces would allow us to make images that are partially stored in ROM.

ajh - I also think memory spaces would be good for layer segments above.

dms - Shared memory spaces could provide a basis for a high performance object database.

len - Another idea: how about making all pointers be aligned to 8 bytes (I'm not sure how big is the smallest header, 12 bytes pehaps?) and shift them to use those 3 bits for something else?