1) On 32-bit OS X with debugging turned on, the tests fail to compile - I get a segfault in llc. With debugging disabled, everything compiles fine.
2) On 32-bit OS X, Array.copyElements() crashes - specifically the call to llvm.memmove segfaults. I don't know why.
Neither of these bugs manifests on my 64-bit linux box. Unfortunately, those are the only two machines I have, so I don't know if it's OS X related or 32-bit related. Both bugs are very difficult to diagnose, I think I've been struggling with the second one for almost 6 months now. Honestly, if I could pay someone a thousand bucks to solve these for me it would be worth it - I've spent far more of my time than that on these two.
In the mean time - I checked in the Path class and the tests for it, so we now have file path manipulation functions.
Also, I've been thinking a bit about FFI - that is, foreign function interface. It has always been my plan that Tart should be capable of calling directly to C functions without the need to write a separate wrapper library. The main idea is that you have a bunch of different annotations (package tart.ffi.*) that tells the compiler how transform the function parameters and return value. So for example:
@CMallocString
@Extern("getcwd")
def currentDir -> String;
The idea here is that the compiler generates a wrapper function which calls the underlying getcwd() provided by the operating system. The @CMallocString annotation tells the compiler that the result of getcwd() is a C-style string that has been malloc'd, and which can be freed. The compiler should, in this case, generate the code to convert the C-style string into a Tart String object.
Some other possible annotations might be:
CString - function returns a char * pointer.
CWideString - function returns a wchar_t * pointer.
CMallocString - function returns a char * pointer which can be free()'d.
CMallocWideString - function returns a wchar_t * pointer which can be free()'d.
CArray - parameter annotation indicating that the function requires a C-style array pointer.
In addition, we need some way to tell the compiler that certain operations, such as i/o may not return immediately. The reason for this is due to the way garbage collection works - if we run out of memory while some thread is waiting for i/o, we would have to wait for the i/o to finish before doing the collection, because we don't know that it's safe to do a collection until we rendezvous with the thread. The way around this is to have the thread that is doing the i/o signal in advance that it's OK to do a collection while it's waiting. I use the term "suspended" to describe threads in this state.
So what we'd need is something like this:
with Suspension() {
// do i/o
}
The "Suspension" class is an object that supports the "scoped object" protocol, meaning that it has two methods, enter() and exit(), which are called at the beginning and end of the 'with' block, regardless of how the 'with' block is exited (such as throwing an exception.) In the case of Suspension, the 'enter' method puts the current thread into the suspended state, and the 'exit' method restores it to the normal state. While the thread is suspended, any object references may change their address without warning, since the thread is no longer synchronized to the collector. In general you want to do as little as possible in this state, and you should only used 'pinned' objects (which never get moved around as long as they are in the pinned state.)
So in actuality, a typical i/o operation will look like this:
var buffer = ubyte[](128);
with pbuf = pin(buffer) {
with Suspension() {
fread(pbuf, pbuf.size);
}
}
Of course, all of this stuff will get buried in the i/o classes and never actually be seen by most users.