|
Class: ExternalLibraryFunction
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
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.
copyrightCOPYRIGHT (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 initialization
-
flushModuleHandlesForLibrary: aLibraryName
-
self flushModuleHandlesForLibrary:'ole32.dll'
-
flushModuleHandlesForWhich: aBlock
-
self flushModuleHandlesForLibrary:'ole32.dll'
-
initialize
-
using inline access to corresponding c--defines to avoid duplicate places of knowledge
Usage example(s):
DLLPATH := nil.
self initialize
|
constants
-
callTypeAPI
-
-
callTypeC
-
-
callTypeCDecl
-
-
callTypeMASK
-
-
callTypeOLE
-
-
callTypeUNIX64
-
-
invokeSelectors
-
debugging
-
verbose: aBoolean
-
turn on/off tracing of calls
Usage example(s):
ExternalLibraryFunction verbose:true
|
dll mapping
-
addToDllPath: aDirectoryPathName
-
can be used during initialization, to add more places for dll-loading
Usage example(s):
self addToDllPath:'c:\matrix\API\dll'
|
-
defaultDLLPath
-
return a default dll-path, as per operating system
Usage example(s):
-
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'
-
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'
-
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'
-
dllPath
-
provide a default dllPath, where external libraries are searched for
Usage example(s):
ExternalLibraryFunction dllPath
|
-
dllPath: aCollectionOfDirectoryPathNames
-
provide a default dllPath, where external libraries are searched for
-
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
-
name: functionName module: moduleName callType: callTypeSymbol returnType: returnType argumentTypes: argTypes
-
-
name: functionName module: moduleName returnType: returnType argumentTypes: argTypes
-
type name mapping
-
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
-
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).
-
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
accessing
-
argumentTypes
-
-
argumentTypesString
-
-
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).
-
beCallTypeAPI
-
-
beCallTypeC
-
-
beCallTypeOLE
-
-
beCallTypeUNIX64
-
-
beCallTypeV8
-
-
beCallTypeV9
-
-
beCallTypeWINAPI
-
-
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)
-
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)
-
beNonVirtualCPP
-
specify this as a non-virtual c++-function
-
beObjectiveC
-
specify this as an objective-c message send
-
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.
-
beVirtualCPP
-
specify this as a virtual c++-function
-
callType: aSymbol
-
-
callTypeNumber
-
-
isAsync
-
is this executed in a separate thread, in par with the other execution thread(s) ?
-
isCPPFunction
-
is this a virtual or non-virtual c++-function ?
-
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...
-
isCallTypeC
-
is this a regular C-call (attention: on windows, there are two kinds of calls)
-
isCallTypeOLE
-
is this an OLE-object call ? (eg. a virtual c++ call; same as isCallTypeCPP)
-
isConstReturnValue
-
is the pointer return value not to be finalized
(i.e. points to static data or data which is freed by c)
-
isNonVirtualCPP
-
is this a non-virtual c++-function ?
-
isObjectiveC
-
is this an objective-C message?
-
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.
-
isVirtualCPP
-
is this a virtual c++-function (same as isCallTypeOLE) ?
-
moduleName
-
-
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)
-
returnType
-
-
vtableIndex
-
inspecting
-
inspectorExtraAttributes
( an extension from the stx:libtool package )
-
extra (pseudo instvar) entries to be shown in an inspector.
invoking
-
invoke
-
call the function with no arguments
-
invokeCPPVirtualOn: anInstance
-
call a CPP virtual function with no arguments
-
invokeCPPVirtualOn: anInstance with: arg
-
call a CPP virtual function with 1 argument
-
invokeCPPVirtualOn: anInstance with: arg1 with: arg2
-
call a CPP virtual function with 2 arguments
-
invokeCPPVirtualOn: anInstance with: arg1 with: arg2 with: arg3
-
call a CPP virtual function with 3 arguments
-
invokeCPPVirtualOn: anInstance with: arg1 with: arg2 with: arg3 with: arg4
-
call a CPP virtual function with 4 arguments
-
invokeCPPVirtualOn: anInstance withArguments: args
-
call a CPP virtual function with up to maxArgs (15) arguments
-
invokeObjCOn: anInstance
-
call an ObjC method with no arguments
-
invokeObjCOn: anInstance withArguments: args
-
call an ObjC method with up to maxArgs (15) arguments
-
invokeWith: arg
-
call the function with 1 argument
-
invokeWith: arg1 with: arg2
-
call the function with 2 arguments
-
invokeWith: arg1 with: arg2 with: arg3
-
call the function with 3 arguments
-
invokeWith: arg1 with: arg2 with: arg3 with: arg4
-
call the function with 4 arguments
-
invokeWithArguments: argArray
-
call the function with up to maxArgs (15) arguments
printing
-
printOn: aStream
-
(comment from inherited method)
append a printed representation of the receiver to aStream
private
-
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.
-
convertArgument: arg
-
a chance to convert arguments to one of the primitive (native) types.
-
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
-
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)
-
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
-
prepareObjCInvoke
-
called before invoked
private-accessing
-
name: functionNameOrVirtualIndex module: aModuleName returnType: aReturnType argumentTypes: argTypes
-
-
owningClass
-
-
owningClass: aClass
-
-
setModuleName: aModuleName
-
private-invoking
-
invokeCPPVirtualFFIOn: instance withArguments: arguments
-
-
invokeFFIWithArguments: arguments
-
-
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
-
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
-
isExternalLibraryFunction
-
return true, if the receiver is some kind of externalLibrary function;
true is returned here
|