25.36.1. DECLARESUB Details

[<<<] [>>>]

DEVELOPER DETAILS!

This command is used to start an external function defined in a dynamic load library.

The dynamically loaded modules are implemented in ScriptBasic via an ordinary command that has the syntax:

'declare' 'sub' * function 'alias' string 'lib' string nl

For the compiler it generates a user defined function, which is defined on the line that contains the declare statement. The execution system will call itself recursively to execute lines starting at the line where the declare statement is. The command implemented in this file is executed and unlike the FUNCTION or SUB it immediately tells the execution system to return after the line was executed.

This command first checks if the line was already executed. On the first execution it loads the module and gets the address of the function named in the alias string. This entry point is stored in a struct and next time the function is called by the basic pointer it does not need to search for the function and for the module. If a function of an already loaded module is called the program does not reload the module. The program maintains a linked list with the names of the loaded modules and loads modules only when they are first referenced.

The modules are loaded using the operating system dll loading function dlopen or LoadLibrary. These functions search several locations for libraries in case the library is specified without absolute path name. The module loader can be fouled if the same library is defined with full path and with single name in the basic code.

For example, if the commands

declare sub fun2 alias "mefun" lib "libobas"
declare sub fun3 alias "youfun" lib "/usr/lib/scriba/libobas.so"

refer to the same module, the code implemented here thinks that they are different libraries.

When the module is loaded the code tries to get the function named versmodu and calls it. This function gets three arguments. The first argument is the interface version that ScriptBasic supports for the external modules. The second argument is a pointer to a ZCHAR terminated string that contains the variation of the calling interpreter. Note that this has to be an 8-character (+1 ZCHAR) string. The third argument is a pointer to a NULL initialized void * pointer, which is the module pointer. This pointer is stored by ScriptBasic, and it is guaranteed not been modified. The modules can store "global" variable information using this pointer. Usually this pointer is not used in this function, especially because there are no "safe" memory allocation functions available at this point of execution.

The module should examine the version it gets and it should decide if the interface version is OK for the module. If the interface version is not known the function should return 0 and ScriptBasic will interpret this value as a failure to load the module. If the module does not know if the interface version is good or not it can return the interface version that it can handle. In this case it is the duty of the interpreter to decide if the interface version can be provided or not. ScriptBasic will examine the version and in case it can not handle the version it will generate an error.

This methodology will allow either ScriptBasic to revert its functionality to earlier interface versions in case the higher version interface not only extends the lower version but is incompatible with the former version. On the other hand a module designed for a higher version may be loaded and executed by a lower version of ScriptBasic in case the module is ready to use the lower interface version.

If the function versmodu can not be found in the DLL then ScriptBasic assumes that the module is ready to accept the current interface version. However such a module should not generally be written.

Note that the version we are talking about here is neither the version of ScriptBasic nor the version of the module. This is the version of the interface between the module and ScriptBasic.

After the version negotiation has been successfully done ScriptBasic tries to get the address of the function named bootmodu. If this function exists it is started. This function can perform all the initializations needed for the module. This function gets all the parameters that a usual module implemented function except that there are no parameters and the function should not try to set a result value, because both of these arguments are NULL.

A module function (including bootmodu) is called with four arguments. The four arguments are four pointers.

int my_module_function(pSupportTable pSt,
                       void **ppModuleInternal,
                       pFixSizeMemoryObject pParameters,   // NULL for bootmodu
                       pFixSizeMemoryObject *pReturnValue) // NULL for bootmodu

The parameter pSt points to a struct that holds the function pointers that the function can call to reach ScriptBasic functionality. These functions are needed to access the arguments and to return a value.

The parameter ppModuleInternal is a pointer pointing to a NULL initialized pointer. This pointer belongs to the module. ScriptBasic guarantees that the value is not modified during the execution (unless the module itself modifies it). This pointer can be used to remember the address space allocated by the module for itself. To store permanent values to remember the state of the module you can either use static variables or this pointer. However using this pointer and allocating memory to store the values is the preferred method. The reason for preferring this method is that global variables are global in the whole process. On the other hand ScriptBasic may run in several threads in a single process executing several different basic programs. This ppModuleInternal pointer will be unique for each thread.

Here is the point to discuss a bit the role of bootmodu. Why not to use DllMain under Windows NT? The reason is in the possibility of threaded execution. DllMain is executed when the process loads the dll. bootmodu is executed when the basic executor loads the module. If there are more threads running then DllMain is not started again. Use DllMain or a similar function under UNIX if you want to initialize some process level data. Use bootmodu to initialize basic program specific data.

The parameter pParameters is a ScriptBasic array containing the values of the arguments. There is no run-time checking about the number of arguments. This is up to the module function.

The last parameter is a pointer to a ScriptBasic variable. The initial value of the pointed pointer is NULL, meaning undef return value. The final return value should be allocated using the macros defined in basext.h and this pointer should be set to point to the value. Note however that bootmodu and finimodu should not to try to dereference this variable, because for both of them the value is NULL.

For further information on how to write module extension functions read the on-line documentation of basext.c in this source documentation.

A module is unloaded when the basic program execution is finished. There is no basic code to unload a module. (Why?)

Before the module is unloaded calling one of the operating system functions dlclose or FreeLibrary the program calls the module function finimodu if it exists. This function gets all the four pointers (the last two are NULLs) and it can perform all the tasks that the module has to do clean up.

Note however that the module need not release the memory it allocated using the besALLOCATE macro defined in the file basext.h (which is generated using headerer.pl from basext.c). The memory is going to be released afterwards by ScriptBasic automatically.

You can have a look at the source code of modules provided by ScriptBasic, for example MySQL module, Berkeley DB module, HASH module, MT module and so on.


[<<<] [>>>]