-
Notifications
You must be signed in to change notification settings - Fork 89
primitiveMaker
primitiveMaker is a Self application used for adding primitives to Self. It reads a templates file and produces 'glue' code, C++ headers, stubs, and static data that can be linked into the Self VM and called via a primitive.
Examples of use are in the objects/glue directory. The files suffixed with Templates.Self contain the primitiveMaker template. A template file is directly loaded as a script into Self to produce the glue code. For example:
"Self 1" 'glue/xlibTemplates.self' _RunScript
reading glue/xlibTemplates.self...
Processing:
...
a primitiveMaker reader
The result of this is the file glue/xlib_wrappers.self and glue/xlib.primMaker.hh.
There are some built in examples that can be run with primitiveMaker reader copy staticLinking test and primitiveMaker reader copy dynamicLinking test.
The template file needs to be of the form:
primitiveMaker reader copy [staticLinking|dynamicLinking]
create: 'fileNamePrefix' From: '
template1
template2
template3a \
template3b
...
templateN
'
The staticLinking symbol is used for linking directly into the SelfVM. dynamicLinking is used for creating a shared library that can be loaded into a running VM.
Within the template file lines starting with // are comments, and those with -- are inserted as comments in the output file.
Each template section in the template file contains the information for the generated wrapper. There are a few special templates.
A traits: <self-path> template specifies the self traits object that will be the target of the _AddSlots for the wrappers. For example:
traits: traits xlib display
Slot definitions after this will be added to the traits xlib display trait.
A visibility: privateSlot|publicSlot should come after a traits template and defines whether the following slots have public or private visibility.
A macroName: <macro-name> template specifies the base name of the macro that will be defined to hold all the lines of glue or primitive entries. For the xlib template this is:
macroName: xlib
This results in an xlib_glue C #define in the xlib.primMaker.hh file.
A glueLibraryName: <glue-library-name> template specifies the library name of any glue code that needs to be linked in. The xlib code has an xlib_glue.cpp file that contains additional routines that need to be linked in to implement some of the xlib extension. This is noted in the template file with:
glueLibraryName: xlib_glue.o
The primitiveMaker documentation states that this is only needed if using dynamic linking so I'm not sure why it's defined in the xlib file.
The remaining templates define what slots are generated for the previously defined trait. They are of the syntax [_|^] <wrapper-spec> = <resultType> <type-of-prim> <c-name> <primTableInfo>. An example from xlib is:
void xDisplayName: string = string call XDisplayName canAWS
<wrapper-spec> gives the name of the Self-wrapper, and the argument type conversion specs. It is a sequence of keywords, interspersed with type conversion specs. The first spec may be void to force the wrapper to discard the receiver.
The <resultType> is the result of the C routine being wrapped.
<type-of-prim> can be:
get-
getMember- Return the named field of the C structure set-
setMember- Set the named field of the C structure -
call- call a C function. The receiver is not passed to the function. callMember-
new- Create an object using operator new -
delete- Delete an object using operator delete
The <cname> is the name of the C/C++ routine being called. It is omitted if the <type-of-prim> is new or delete.
primTableInfo can be one of:
-
canAWS- the C primitive can abort, walk the stack, or scavenge. -
cannotFail- the C routine cannot fail. -
passFailHandle- pass a fail handler as the last argument.
The argument type conversion specs used in templates define how Self arguments are converted to C/C++ types. The definitions for these are in glueDefs.hh.
The argument simple type conversion specs are:
char, signed_char, unsigned_char,
short, signed_short, unsigned_short,
int, signed_int, unsigned_int,
long, signed_long, unsigned_long,
long_long, signed_long_long, unsigned_long_long,
float, double, long_double,
bool, any_oop,
smi, unsigned_smi,
String conversions copy the string:
string, string_len, string_null, string_len_null,
string_len and string_len_null pass the string and the string length as arguments to the C function. Given the following template:
Foo withString: string_len_null = void call bar
The calling Self code takes a single argument, a Self String. This calls a C function that has two arguments:
void bar(char* s, int len);
The length is automatically computed and passed to the C function.
Some type conversion specs take additional data. The following expect a C pointer type to follow the spec name:
bv, bv_len, bv_null, bv_len_null,
cbv, cbv_len, cbv_null, cbv_len_null,
bv is short for byte vector and cbv for constant byte vector. These conversions do not copy the data. Do not use cbv if the underlying C routine writes to it. The _len suffixed routines operate like the string_len example given previously.
The following expects a pointer type and a type seal to follow the spec name:
proxy, proxy_null,
The following expects an oop subclass to fllow the spec name (eg. byteVector, foreign, proxy, etc):
oop,
The following expects the name of any C type which is big enough to store one of an int, float or pointer.
any
The type conversion spec can also specify a class name that maps to a C/C++ structure or class. This is similar to using proxy. For example:
Display xftFontOpenOnScreen: int \
Name: string \
= XftFont {xlib xftFont deadCopy} \
call XftFontOpenName canAWS
and:
= proxy XftFont* XftFont_seal \
{xlib xftFont deadCopy} \
call XftFontOpenName canAWS
The first specifies the class name, XftFont, whereas the second uses proxy which provides the pointer type and seal. They are equivalent. The proxy version can pass by value because you can explicitly state the type.
The return type conversion specs define how a C/C++ value is converted into a Self object. The simple return type conversion specs are:
char, signed_char, unsigned_char,
short, signed_short, unsigned_short,
int, signed_int, unsigned_int,
long, signed_long, unsigned_long,
long_long, signed_long_long, unsigned_long_long,
float, double, long_double,
void, bool, oop,
smi, string,
The following takes an argument giving the return value that signals an error:
int_or_errno
The following require a C pointer type and type seal to after the spec name:
proxy, proxy_null, proxy_or_errno
proxy_or_errno takes an additional argument giving the return value that signals an error.
The following require a C pointer type, type seal and argument count to follow the spec name:
fct_proxy, fct_proxy_null,
Both proxy and fct_proxy cause the glue code to take an additional argument, following the other arguments, that contains a dead proxy or dead function proxy to receive the new value and type seal.
The following expects the callee to return an int.
specific_failure_int
specific_success_int
A specific int is given as an argument for these. specific_failure_int fails the primitive if the result matches the second argument. specific_success_int fails the primitive if it does not match the second argument.
The xlib template has the following:
traits: traits xlib xVisualInfo
visibility: publicSlot
void new = XVisualInfo {xlib xVisualInfo deadCopy} new
XVisualInfo delete = void delete
This maps an XVisualInfo structure to Self code. Calling xlib xVisualInfo new creates a new instance, allocated from the C++ side using operator new. The delete member of the object calls the C++ operator delete.
XVisualInfo visual = Visual {xlib visual deadCopy} getMember visual
The C structure for XVisualInfo has a visual field of type Visual*. The template above defines a visual method on the Self object that gets the structure field (getMember visual does this). It is represented in Self as a xlib visual.
XVisualInfo visualID = int getMember visualid
Creates a visualID method on the trait that returns the visualid field which is a member of the XVisualInfo C structure of type int.
XVisualInfo visualID: int = void setMember visualid
Creates a visualID: method on the trait which sets the visualid field of the C struct.
An example of bv type conversion is reading data from a file using unix primitives:
void basicReadFile: proxy_null int UnixFile_seal \
Into: bv_len char* \
Offset: int \
Count: int \
= int_or_errno -1 call read_wrap passFailHandle
This adds a basicReadFile:Into:Offset:Count: method to whatever trait was defined before it. The Into: argument is a bv_len char*. This is a byte vector that can be written too and is mapped to C as a char*. Because it is a bv_len the C routine is expected to take both a char* and int length arguments. This is the C definition from the wrapper file:
int read_wrap(int fd, char *buf, int buf_len,
int offset, int nbytes, void *FH)
Note the buf and buf_len arguments. This maps to the Info: argument in the Self call. The result is defined as an int but if the actual result is -1' then this counts as a failure. The passFailHandlecauses a last argument for a fail handler to be required that will get called when the result is-1`. Two Self methods are created, one that takes the fail handler and one that doesn't. The first calls the second like:
basicReadFile: t0 Into: t1 Offset: t2 Count: t3 IfFail: [|:e| ^error: e].