Tuesday, October 11, 2011

Slitaz Linux

Lately I've been looking for a distribution of Linux for the sole purpose of running Squeak / Pharo. Essentially it would be a single-application installation of Linux; only the Squeak VM would be running and nothing else is really needed.

Slitaz Linux is a Linux distro which is in total 30MB, which includes the LXDE desktop environment (my favourite) and a credible number of applications. I find that downright impressive. It runs entirely in RAM meaning that it is *very* responsive.

My Laptop is a Dell C640 from the 18th century; I replaced the hard disk with a CompactFlash card and installed Slitaz on it. I customized Slitaz by removing applications I wouldn't use, added Xorg for my hardware and generated a 20MB image. I also installed a "big" version of Slitaz as well which mounts the CF card for the root filesystem so I can run gcc etc.

Now I have a Squeak development machine with a 30 second boot time and insane reactivity.

There were issues. I needed to rebuild the Squeak VM without OpenGL support (because Slitaz doesn't include those libraries, and Squeak won't start without them). For future reference, I needed to install (from memory) gcc, binutils, linux-headers, cmake and xorg-dev. I also needed to modify the VM source to properly include dirent.h somewhere.

Sunday, August 28, 2011

Class format

I want to add more instance variables to Class. How difficult is this? Pretty. I suspect that for vanilla Squeak or Pharo, you'd need to carefully remake every single Metaclass instance and Class instance. For me, my Packages architecture means I only need to recompile my own private Metaclasses and all its instances which can be done safely.

Before I started though, I wanted to see what happened if I did something wrong: what happens if I add the extra variables to Class, but instances and subclasses of Class (i.e. every single class and class class) were not updated?

Classes in the Squeak VM have three variables very important variables which are referenced directly by the VM: superclass, format and methodDictionary. The format describes the nature of instances: are they normal/variable/words/bytes/weak, and how many instance variables does each instance have. "format" is a bit-field, so you need to wrangle bits to modify it.

What I'm going to test is what the VM does if I'm not careful and forget to update an instance of Class somewhere to be big enough to store my extra instance variables:
  1. Make a class, e.g. "TestVMCrash", with three instance variables, and a method, e.g. >>third, which sets the third instance variable.
  2. Get the format of that class: "TestVMCrash format", which returns 136 for me (which means "normal class, 3 instvars").
  3. Reduce the number of instance variables in TestVMCrash to 2.
  4. Make an instance and try to set the third, now non-existent, instance variable.
So after making the class, I reduce the number of instance variables:

ClassBuilder new computeFormat: #normal instSize: 2 forSuper: Object ccIndex: 0
" returns 134 "
TestVMCrash setFormat: 134. "The class now has only two instvars"


o := TestVMCrash new.
" o will have 2 instance varables, but here we try to set an non-existant third: "
o third.

This causes the VM to behave badly. The first time I tried, it crashed with a SEGFAULT. The second time, it froze. The VM barged ahead and corrupted the image, so there are no checks on invalid instance variable accesses.

In conclusion, I need to be very careful when adding instance variables to Class, and back up my image often.

Sunday, May 15, 2011

Now, where was I?

A year has passed since I last worked on SecureSqueak. Babies do that to you. So now, I ask myself: what was I doing!?

It appears I was trying to set up a completely independent class hierarchy / object graph with no dependencies on Squeak's classes. In other words, my own Class, Metaclass, Object and so forth, all in a Namespaced environment rather than in the SystemDictionary (known colloquially as the "Smalltalk" global object).

This is trickier than it seems. All of the above need to live "in" a Package object. As an object, it is therefore an instance of the Package class, which has Class, Object and so forth as superclasses. So these (Class, Object, etc) all need to be made first... but then these classes in turn need to be in a Package (called "Kernel"). Thus, we have a chicken-and-egg problem. We need an instance of Package, which is an instance of a class inside itself (and further, compiled by a compiler which is in another instance of Package).

My solution is to file out the core packages using my current tools: Kernel, SourceCode, Compiler, Namespaces, NamespaceTools, etc. Then I do this:
  1. File in (but do not compile) the Kernel package, using the existing compiler and a custom filing code.
  2. Make the new Metaclass singularity by hand.
  3. Compile the Kernel package using whatever compiler is available, using the new Metaclasses.
  4. File in and compile all other dependencies using the resulting Kernel. These will all be discarded once the second kernel is built.
  5. File in the Kernel source code again into a separate PackageSource, this time using the new Kernel (above) for the Object, Class etc classes.
  6. Now, do a whole lot of "special case" handling on the new second Kernel, using and sacrificing bits from the first, to make the new Kernel completely self-contained. This is quite a complex step so I'll spare the details.
Thus is born a set of objects with no links whatsoever to any Class in the SystemDictionary.