eXept Software AG Logo

Smalltalk/X Webserver

Documentation of class 'ExternalLibraryFunction':

Home

Documentation
www.exept.de
Everywhere
for:
[back]

Class: ExternalLibraryFunction


Inheritance:

   Object
   |
   +--ExecutableFunction
      |
      +--ExternalFunction
         |
         +--ExternalLibraryFunction

Package:
stx:libbasic
Category:
System-Support-External Memory
Version:
rev: 1.218 date: 2024/04/23 19:08:13
user: cg
file: ExternalLibraryFunction.st directory: libbasic
module: stx stc-classLibrary: libbasic

Description:


instances of me are used to interface to external library functions (as found in a dll/shared object).
Foreign function calls are based on the FFI library if available.
A limited fallback implementation is provided for systems with no libffi.
(this may have limitations on the supported argument types; for example,
 the x86_64 fallback does not support float/double arguments).
Therefore the fallback should be considered a temporary workaround,
until libffi has been ported.

Inside a method, when a special external-call pragma such as:
    <api: bool MessageBeep(uint)>

is encountered by the parser, the compiler generates a call via
    <correspondingExternalLibraryFunctionObject> invokeWithArguments: argumentArray.
and the correspondingExternalLibraryFunctionObject is kept in the literal array.

In the invoke method, the library is checked to be loaded (and loaded if not already),
the arguments are converted to C and pushed onto the C-stack, the function is called,
and finally, the return value is converted back from C to a smalltalk object.

The parser supports the call-syntax of various other smalltalk dialects:
    Squeak / ST-X:
        <cdecl:   [async] [virtual|nonVirtual][const] returnType functionNameStringOrIndex ( argType1..argTypeN ) module: moduleName >
        <apicall: [async] [virtual|nonVirtual][const] returnType functionNameStringOrIndex ( argType1..argTypeN ) module: moduleName >

    Dolphin:
        <stdcall: [virtual|nonVirtual][const] returnType functionNameStringOrIndex argType1..argTypeN>
        <cdecl:   [virtual|nonVirtual][const] returnType functionNameStringOrIndex argType1..argTypeN>

    ST/V:
        <api: functionName argType1 .. argTypeN returnType>
        <ccall: functionName argType1 .. argTypeN returnType>
        <ole: vFunctionIndex argType1 .. argTypeN returnType>

    VisualWorks:
        <c: ...>
        <c: #define NAME value>

It is also possible (although less user friendly), to create an instance 'by hand',
via my instance creation protocol (name:module:returnType:argumentTypes:),
and keeping the instance wherever feasable, and calling it via the invoke protocol.

copyright

COPYRIGHT (c) 2004 by eXept Software AG All Rights Reserved This software is furnished under a license and may be used only in accordance with the terms of that license and with the inclusion of the above copyright notice. This software may not be provided or otherwise made available to, or used by, any other person. No title to or ownership of the software is hereby transferred.

example

[exBegin] |f| f := ExternalLibraryFunction new. f beCallTypeWINAPI. f name:'MessageBeep' module:'user32.dll' returnType:#boolean argumentTypes:#(uint). f invokeWith:1. [exEnd] Synchronous vs. Asynchronous calls: by default, foreign function calls are synchronous, effectively blocking the whole ST/X system (that is by purpose,´because most C-code is not prepared for being interrupted, and also, normal code is not prepared for a garbage collector to move objects around, while another C thread might access the data...). Therefore, the following will block all ST/X activity for 10 seconds (try interacting with the launcher while the Sleep is performing): [exBegin] |f| f := ExternalLibraryFunction new. f beCallTypeWINAPI. f name:'Sleep' module:'kernel32.dll' returnType:#void argumentTypes:#(uint). f invokeWith:10000. [exEnd] if you know what you do and you do not pass any possibly moving objects (such as strings) as argument, the call can be made asynchronous. In that case, ONLY the calling thread will be blocked; all other smalltalk threads will continue to execute. (try interacting now with the launcher while the Sleep is performing): [exBegin] |f| f := ExternalLibraryFunction new. f beCallTypeWINAPI. f beAsync. f name:'Sleep' module:'kernel32.dll' returnType:#void argumentTypes:#(uint). f invokeWith:10000. [exEnd]

Class protocol:

class initialization
o  flushModuleHandlesForLibrary: aLibraryName
self flushModuleHandlesForLibrary:'ole32.dll'

o  flushModuleHandlesForWhich: aBlock
self flushModuleHandlesForLibrary:'ole32.dll'

o  initialize
using inline access to corresponding c--defines to avoid duplicate places of knowledge

Usage example(s):

DLLPATH := nil.
     self initialize

constants
o  callTypeAPI

o  callTypeC

o  callTypeCDecl

o  callTypeMASK

o  callTypeOLE

o  callTypeUNIX64

o  invokeSelectors

debugging
o  verbose: aBoolean
turn on/off tracing of calls

Usage example(s):

     ExternalLibraryFunction verbose:true

dll mapping
o  addToDllPath: aDirectoryPathName
can be used during initialization, to add more places for dll-loading

Usage example(s):

     self addToDllPath:'c:\matrix\API\dll'

o  defaultDLLPath
return a default dll-path, as per operating system

Usage example(s):

     self defaultDLLPath

o  dllMapping
allows for dll's to be replaced,
for example, if you want to use the mozilla sqlite dll
C:\Program Files\Mozilla Firefox\mozsqlite3.dll
for the sqlite3, execute:
ExternalLibraryFunction
dllMapping at:'sqlite3'
put: 'C:\Program Files\Mozilla Firefox\mozsqlite3.dll'
for mingw:
ExternalLibraryFunction
dllMapping at:'sqlite3'
put:'C:\mingw64\opt\bin\libsqlite3-0.dll'

o  dllMapping: aDictionary
allows for dll's to be replaced,
for example, if you want to use the mozilla sqlite dll
C:\Program Files\Mozilla Firefox\mozsqlite3.dll
for the sqlite3, execute:
ExternalLibraryFunction
dllMapping at:'sqlite3'
put: 'C:\Program Files\Mozilla Firefox\mozsqlite3.dll'
for mingw:
ExternalLibraryFunction
dllMapping at:'sqlite3'
put:'C:\mingw64\opt\bin\libsqlite3-0.dll'

o  dllMappingAt: baseLibname put: aNameOrPath
allows for dll's to be replaced,
for example, if you want to use the mozilla sqlite dll
C:\Program Files\Mozilla Firefox\mozsqlite3.dll
for the sqlite3, execute:
ExternalLibraryFunction
dllMappingAt:'sqlite3'
put: 'C:\Program Files\Mozilla Firefox\mozsqlite3.dll'
for mingw:
ExternalLibraryFunction
dllMappingAt:'sqlite3'
put:'C:\mingw64\opt\bin\libsqlite3-0.dll'

o  dllPath
provide a default dllPath, where external libraries are searched for

Usage example(s):

     ExternalLibraryFunction dllPath

o  dllPath: aCollectionOfDirectoryPathNames
provide a default dllPath, where external libraries are searched for

o  removeFromDllPath: aDirectoryPathName
remove added places from dll-loading

Usage example(s):

     |prevDllPath|

     prevDllPath := self dllPath.
     self removeFromDllPath:'C:\aaa\bbb'.
     self addToDllPath:'C:\aaa\bbb'.
     self assert:self dllPath size = (prevDllPath size + 1).
     self removeFromDllPath:'C:\aaa\bbb'.
     self assert:self dllPath size = (prevDllPath size).

instance creation
o  name: functionName module: moduleName callType: callTypeSymbol returnType: returnType argumentTypes: argTypes

o  name: functionName module: moduleName returnType: returnType argumentTypes: argTypes

type name mapping
o  adjustAllTypes
because adjustTypes is invoked only once (when the ext-call is first created),
any change in the type mapping (ffiTypeSymbolForType: or mapType) will not
affect existing lib-calls.
Use this, to adjust all those, after a change in the type mapping

o  ffiTypeSymbolForType: aType
map aType to one of the ffi-supported ones:
sint8, sint16, sint32, sint64 - explicitly sized
uint8, uint16, uint32, uint64 - explicitly sized
long ulong int uint - native CPU sized
bool float double void pointer handle

a number of wellknown types is handled by an explicit switch here;
additional custom types may be entered into a typeMap which is held in a class var
of the containing class (see mapType:to:) or a typeMap here (classVar).

o  mapType: aTypeSymbol toFFI: mappedType
additional user defined type map:
eg. self mapType:#INT8 toFFI:#int8

allows use of INT8 in external function api declarations.
mappedType should be one of the ffi-supported ones:
sint8, sint16, sint32, sint64
uint8, uint16, uint32, uint64
long ulong int uint
bool float double void pointer handle


Instance protocol:

accessing
o  argumentTypes

o  argumentTypesString

o  beAsync
let this execute in a separate thread, in par with the other execution thread(s).
Ignored under unix/linux (until those support multiple threads too).

o  beCallTypeAPI

o  beCallTypeC

o  beCallTypeOLE

o  beCallTypeUNIX64

o  beCallTypeV8

o  beCallTypeV9

o  beCallTypeWINAPI

o  beConstReturnValue
specify that a pointer return value is not to be finalized
(i.e. points to static data or data which is freed by c)

o  beMustFreeReturnValue
specify that a pointer return value is to be freed explicitly by st/x
(i.e. points to mallocd data which is not freed by c)

o  beNonVirtualCPP
specify this as a non-virtual c++-function

o  beObjectiveC
specify this as an objective-c message send

o  beUnlimitedStack
let this execute on the c-stack (as opposed to the thread-stack)
for unlimited auto-sized-stack under unix/linux.
Ignored under windows.

o  beVirtualCPP
specify this as a virtual c++-function

o  callType: aSymbol

o  callTypeNumber

o  isAsync
is this executed in a separate thread, in par with the other execution thread(s) ?

o  isCPPFunction
is this a virtual or non-virtual c++-function ?

o  isCallTypeAPI
is this a windows API-call linkage call.
Attention: this uses a different call API (callee unwinds the stack),
and MUST be declared as such for many Kernel functions.
The calltype API is one of the worst historic garbage kept by MS...

o  isCallTypeC
is this a regular C-call (attention: on windows, there are two kinds of calls)

o  isCallTypeOLE
is this an OLE-object call ? (eg. a virtual c++ call; same as isCallTypeCPP)

o  isConstReturnValue
is the pointer return value not to be finalized
(i.e. points to static data or data which is freed by c)

o  isNonVirtualCPP
is this a non-virtual c++-function ?

o  isObjectiveC
is this an objective-C message?

o  isUnlimitedStack
will this execute on the c-stack (as opposed to the thread-stack)
for unlimited auto-sized-stack under unix/linux.
Ignored under windows.

o  isVirtualCPP
is this a virtual c++-function (same as isCallTypeOLE) ?

o  moduleName

o  mustFreeReturnValue
answer true, if a pointer to some C-datum is returned, which must be freed by ST/X.
(i.e. points to malloc'd data which is NOT freed by c)

o  returnType

o  vtableIndex

inspecting
o  inspectorExtraAttributes
( an extension from the stx:libtool package )
extra (pseudo instvar) entries to be shown in an inspector.

invoking
o  invoke
call the function with no arguments

o  invokeCPPVirtualOn: anInstance
call a CPP virtual function with no arguments

o  invokeCPPVirtualOn: anInstance with: arg
call a CPP virtual function with 1 argument

o  invokeCPPVirtualOn: anInstance with: arg1 with: arg2
call a CPP virtual function with 2 arguments

o  invokeCPPVirtualOn: anInstance with: arg1 with: arg2 with: arg3
call a CPP virtual function with 3 arguments

o  invokeCPPVirtualOn: anInstance with: arg1 with: arg2 with: arg3 with: arg4
call a CPP virtual function with 4 arguments

o  invokeCPPVirtualOn: anInstance withArguments: args
call a CPP virtual function with up to maxArgs (15) arguments

o  invokeObjCOn: anInstance
call an ObjC method with no arguments

o  invokeObjCOn: anInstance withArguments: args
call an ObjC method with up to maxArgs (15) arguments

o  invokeWith: arg
call the function with 1 argument

o  invokeWith: arg1 with: arg2
call the function with 2 arguments

o  invokeWith: arg1 with: arg2 with: arg3
call the function with 3 arguments

o  invokeWith: arg1 with: arg2 with: arg3 with: arg4
call the function with 4 arguments

o  invokeWithArguments: argArray
call the function with up to maxArgs (15) arguments

printing
o  printOn: aStream
(comment from inherited method)
append a printed representation of the receiver to aStream

private
o  adjustTypes
map all those existing type names to a small number of definite ffi type names.
This is needed, because there are so many different C-type names found in code imported
from various Smalltalk dialects' library function call declarations.
For example: all of word, WORD, unsignedShort, ushort, uShort etc. will map to uint16.
Also, this deals with pointer size differences.

o  convertArgument: arg
a chance to convert arguments to one of the primitive (native) types.

o  linkToModule
link this function to the external module.
I.e. retrieve the module handle and the code pointer.
The name of the DLL is retrieved either from the primitive declaration or
the owning instance or its class

o  loadLibrary: dllName
load a dll.
Returns a handle or nil.
Notice the dllMapping mechanism, which can be used to silently load different dlls.
This is useful, if some code has a hardcoded dll-name in it, which needs to be changed,
but you do not want or cannot recompile the methods (i.e. no source avail)

o  prepareInvoke
called before invoked.
When called the very first time, moduleHandle is nil,
and we ensure that the dll is loaded, the function address is extracted

o  prepareObjCInvoke
called before invoked

private-accessing
o  name: functionNameOrVirtualIndex module: aModuleName returnType: aReturnType argumentTypes: argTypes

o  owningClass

o  owningClass: aClass

o  setModuleName: aModuleName

private-invoking
o  invokeCPPVirtualFFIOn: instance withArguments: arguments

o  invokeFFIWithArguments: arguments

o  invokeFFIwithArguments: argumentsOrNilArg forInstance: aReceiverOrNil
basic invoke mechanism. Calls the function represented by the receiver with argumentsOrNil.
For cplusplus, aReceiverOrNil is required to be an externalStructure like object;
for objectiveC, it must be an ObjectiveC object

o  tryAgainWithAsyncSafeArguments: argumentsOrNil forInstance: aReceiverOrNil
invoked by the call primitive, iff GC-unsave arguments where passed to the call.
Here, allocate non-movable blocks of memory and copy the arguments into them,
then try the call again, copy changed values back, and release the memeory.

testing
o  isExternalLibraryFunction
return true, if the receiver is some kind of externalLibrary function;
true is returned here



ST/X 7.7.0.0; WebServer 1.702 at 20f6060372b9.unknown:8081; Wed, 24 Jul 2024 14:33:16 GMT