Description
Hi,
This is my first post here, so hello and thanks to all the developers of VOC. I have been trying VOC with a few C libraries and have a few suggestions for improvements. I may attempt to impelement some of these, so any comments or technical suggestions would be welcome.
A few system flags are already implemented in VOC.
Assigning a system flag [1]
to an ARRAY
causes it to be not copied when passed as a value parameter. This avoids making useless copies of strings which improves the performance of text I/O. This has the same effect as the NO_COPY
flag in OOC. As far as I can see this is only used in Files.WriteString, but there are probably many other places that could benefit.
Assigning a system flag [1]
to a RECORD
or POINTER
causes the object to be untraced by the GC. That is, the pointer (or any pointers in the RECORD
are not recorded as heap pointers in the generated code and type descriptors. Clearly this is required for pointers to any objects allocated outside the Oberon heap. The GC assumes that it can use a record's type descriptor for marking the heap object, and for enumerating embedded pointers, so omitting this flag on a C-allocated object could corrupt the C heap and/or crash the garbage collector. This flag is I think equivalent to the UNTRACED
flag in Component Pascal.
OOC defines some useful flags for its C interface:
http://ooc.sourceforge.net/OOCref/OOCref_16.html#SEC150
The important flags are:
-
NO_DESCRIPTOR
declares that a record type has no type descriptor. This means it cannot be used in type tests and type guards, or the NEW procedure, and cannot be passed as a formal parameter that requires a type tag. -
NO_LENGTH_INFO
declares that an open array type has no length information. This means that LEN cannot be used, and it cannot be passed as a formal parameter that requires a length. -
UNION
declares that aRECORD
is to be translated to a C "union" instead of "struct". These don't occur very often but need to be implemented properly, especially if the variable is allocated by the (Oberon) client code.
The current approach to calling external functions is a bit cumbersome, and requires one to hand-code a "macro" that will translate the Oberon call into a C call. For example:
PROCEDURE -CreateWindow*(title : ARRAY OF CHAR; x, y, w, h, flags : C.int) : Window
"(LibSDL_Window)SDL_CreateWindow((const char *)title, x, y, w, h, flags)";
Since this is a macro (or "code procedure"), it is necessary to explicitly cast between Oberon and C types in order to keep the compiler happy. Also, since the Oberon parameter list contains extra information (array length, type descriptors) the C and Oberon declarations don't always match (eg. the hidden title__len parameter in the above). If the types were properly declared (eg. with NO_DESCRIPTOR
and NO_LENGTH_INFO
) then it should be possible to declare matching parameter lists, and simply call the functions via C externs. The need to explicitly code the type conversions makes it more difficult to automatically generate such interface declarations (eg. using a tool like H2O).
One existing problem is the representation of open arrays. If you declare the following, as in oocC.string:
string = POINTER TO ARRAY OF CHAR;
The corresponding C code is:
typedef
struct {
ADDRESS len[1];
CHAR data[1];
} *oocC_string;
With the current representation of the open array, POINTER TO ARRAY
points to the length field rather than the data, so you can't simply cast (char *) as (oocC.string) without losing a few bytes of the array. At the moment, the only way to properly access a C array is to delcare an array with static length (eg. POINTER TO ARRAY 1 OF CHAR), and then disable run-time bounds checking. The NO_LENGTH_INFO
flag would allow this to be done properly. With RECORDs this is less of a problem, as the type information is stored at a negative offset relative to the data.
One last addition that would really help is to declare link libraries for C interface modules. For example, something like this, as can be done in OOC:
MODULE X11 [ LINK LIB "X11" ];
This means that whenever the X11 module is included the correct library dependency (-lX11
) will be added to the link command. Currently it looks like the best option for specifying link libraries is via the CFLAGS
, but this adds messy dependencies into the makefiles. In some cases one must write a separate link step because gcc does not always allow link libraries to be declared before the object files that depend on them.
I think this should be fairly easy to implement. Currently, each module contains a complete list of all modules that are directly or indirectly imported. The linker uses OPT.Links to enumerate the required object files, and this list is extended via OPT.InLinks for every IMPORT statement, and is saved via OPT.OutLinks when the symbol file is created. Link libraries could be added to this list (maybe with a special flag for the linker), or a separate parallel list could be maintained.
Any comments or suggestions?