--- a/ExternalFunctionCallback.st Wed Jun 13 22:23:17 2007 +0200
+++ b/ExternalFunctionCallback.st Wed Jun 13 23:13:25 2007 +0200
@@ -1,8 +1,19 @@
-'From Smalltalk/X, Version:5.3.5 on 12-06-2007 at 16:40:04' !
+"
+ COPYRIGHT (c) 2007 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.
+"
+"{ Package: 'stx:libbasic' }"
ExternalBytes subclass:#ExternalFunctionCallback
instanceVariableNames:'returnType argumentTypes action'
- classVariableNames:'CallBackRegistry'
+ classVariableNames:'CallBackRegistry Verbose'
poolDictionaries:''
category:'System-Support'
!
@@ -37,20 +48,168 @@
!ExternalFunctionCallback primitiveFunctions!
%{
-#define VERBOSE
+#define xxVERBOSE
void
-ExternalFunctionCallback__closure_test_fn(ffi_cif* cif, void* resp, void** args, void* userdata)
+ExternalFunctionCallback__closure_wrapper_fn(ffi_cif* cif, void* resp, void** args, void* userdata)
{
- int actionIndex = userdata;
+ int actionIndex = (int)userdata;
+ int i;
+ OBJ st_argVector = nil;
+ OBJ st_actionVector, st_callBack = nil, st_result;
+ OBJFUNC code;
+ ffi_type *retType;
+ INT sintResult;
+ unsigned INT uintResult;
+ float floatResult;
+ double doubleResult;
+
+ if (@global(ExternalFunctionCallback:Verbose) == true) {
+ fprintf(stderr, "ExternalFunctionCallback(wrapper): actionIndex=%d resp*=%x\n", actionIndex, resp);
+ fflush(stderr);
+ fprintf(stderr, "ExternalFunctionCallback(wrapper): nargs=%d\n", cif->nargs);
+ fflush(stderr);
+ }
+
+ st_argVector = __ARRAY_NEW_INT(cif->nargs);
+
+ for (i=0; i<cif->nargs; i++) {
+ ffi_type *argType;
+ OBJ st_arg = nil;
+
+ __PROTECT__(st_argVector);
+
+ argType = cif->arg_types[i];
+ if (argType == __get_ffi_type_sint()) {
+ st_arg = __MKINT( *(int *)(args[i]) );
+ } else if (argType == __get_ffi_type_uint()) {
+ st_arg = __MKUINT( *(unsigned int *)(args[i]) );
+ } else if (argType == __get_ffi_type_uint8()) {
+ st_arg = __MKSMALLINT( *(unsigned char *)(args[i]) );
+ } else if (argType == __get_ffi_type_sint8()) {
+ st_arg = __MKSMALLINT( *(char *)(args[i]) );
+ } else if (argType == __get_ffi_type_uint16()) {
+ st_arg = __MKSMALLINT( *(unsigned short *)(args[i]) );
+ } else if (argType == __get_ffi_type_sint16()) {
+ st_arg = __MKSMALLINT( *(short *)(args[i]) );
+ } else if (argType == __get_ffi_type_uint32()) {
+ st_arg = __MKUINT( *(unsigned int *)(args[i]) );
+ } else if (argType == __get_ffi_type_sint32()) {
+ st_arg = __MKINT( *(int *)(args[i]) );
+ } else if (argType == __get_ffi_type_float()) {
+ st_arg = __MKSFLOAT( *(float *)(args[i]) );
+ } else if (argType == __get_ffi_type_double()) {
+ st_arg = __MKFLOAT( *(double *)(args[i]) );
+ } else if (argType == __get_ffi_type_pointer()) {
+ st_arg = __MKEXTERNALADDRESS( *(void **)(args[i]) );
+ } else {
+ if (@global(ExternalFunctionCallback:Verbose) == true) {
+ fprintf(stderr, "ExternalFunctionCallback(wrapper): invalid argument type %d - arg %d\n", argType, i);
+ }
+ }
+
+ __UNPROTECT__(st_argVector);
+
+ if (@global(ExternalFunctionCallback:Verbose) == true) {
+ fprintf(stderr, "ExternalFunctionCallback(wrapper): st-arg for %x is %x\n", *(unsigned int *)(args[i]), st_arg);
+ }
+ __ArrayInstPtr(st_argVector)->a_element[i] = st_arg; __STORE(st_argVector, st_arg);
+ }
+
+ /* the action ... */
+ st_actionVector = @global(ExternalFunctionCallback:CallBackRegistry);
+ if (st_actionVector != nil) {
+ OBJ cls = __Class(st_actionVector);
+
+ if ((cls == Array) || (cls==WeakArray)) {
+ actionIndex += /* nInstVars */ __intVal(__ClassInstPtr(cls)->c_ninstvars);
- printf("closure_test_fn(userdata=%d resp*=%x)\n", userdata, resp);
- printf("closure_test_fn(arg[0]=%d)\n", *(int *)(args[0]));
- *(ffi_arg *)resp = 1;
+ if (__arraySize(st_actionVector) <= actionIndex) {
+ st_callBack = __ArrayInstPtr(st_actionVector)->a_element[actionIndex-1];
+ }
+ }
+ }
+ if (st_callBack == nil) {
+ if (@global(ExternalFunctionCallback:Verbose) == true) {
+ fprintf(stderr, "ExternalFunctionCallback(wrapper): ignored nil callback\n");
+ }
+ *(void **)resp = 0;
+ return;
+ }
+
+ {
+ static struct inlineCache value_snd = _DUMMYILC1;
+
+ if (@global(ExternalFunctionCallback:Verbose) == true) {
+ fprintf(stderr, "ExternalFunctionCallback(wrapper): sending value: to %x..\n", st_callBack);
+ }
+ st_result = _SEND1(st_callBack, @symbol(value:), nil, &value_snd, st_argVector);
+ if (@global(ExternalFunctionCallback:Verbose) == true) {
+ fprintf(stderr, "ExternalFunctionCallback(wrapper): result is %x\n", st_result);
+ }
+ }
+
+ retType = cif->rtype;
+
+ if (st_result == true) {
+ sintResult = uintResult = 1;
+ } else if (st_result == false) {
+ sintResult = uintResult = 0;
+ } else if (st_result == nil) {
+ sintResult = uintResult = 0;
+ } else {
+ sintResult = __signedLongIntVal(st_result);
+ uintResult = __unsignedLongIntVal(st_result);
+ }
+
+ if (retType == __get_ffi_type_sint()) {
+ *(int *)resp = sintResult;
+ } else if (retType == __get_ffi_type_uint()) {
+ *(int *)resp = uintResult;
+ } else if (retType == __get_ffi_type_uint8()) {
+ *(unsigned char *)resp = uintResult;
+ } else if (retType == __get_ffi_type_sint8()) {
+ *(char *)resp = sintResult;
+ } else if (retType == __get_ffi_type_uint16()) {
+ *(unsigned short *)resp = uintResult;
+ } else if (retType == __get_ffi_type_sint16()) {
+ *(short *)resp = sintResult;
+ } else if (retType == __get_ffi_type_uint32()) {
+ *(int *)resp = uintResult;
+ } else if (retType == __get_ffi_type_sint32()) {
+ *(int *)resp = sintResult;
+ } else if (retType == __get_ffi_type_float()) {
+ if (__isFloat(st_result)) {
+ *(float *)resp = (float)__floatVal(st_result);
+ } else {
+ if (__isShortFloat(st_result)) {
+ *(float *)resp = __shortFloatVal(st_result);
+ } else {
+ *(float *)resp = (float)sintResult;
+ }
+ }
+ } else if (retType == __get_ffi_type_double()) {
+ if (__isFloat(st_result)) {
+ *(double *)resp = __floatVal(st_result);
+ } else {
+ if (__isShortFloat(st_result)) {
+ *(double *)resp = (double)__shortFloatVal(st_result);
+ } else {
+ *(double *)resp = (double)sintResult;
+ }
+ }
+ } else if (retType == __get_ffi_type_pointer()) {
+ *(void **)resp = (void *)__externalAddressVal( st_result );
+ } else {
+ if (@global(ExternalFunctionCallback:Verbose) == true) {
+ fprintf(stderr, "ExternalFunctionCallback(wrapper): invalid result type %d\n", retType, i);
+ }
+ *(void **)resp = 0;
+ }
}
void
-ExternalFunctionCallback__doCall_closure(INTFUNC f)
+ExternalFunctionCallback__test_call_closure(INTFUNC f)
{
int result = 0;
@@ -62,7 +221,32 @@
%}
! !
-!ExternalFunctionCallback class methodsFor:'examples'!
+!ExternalFunctionCallback class methodsFor:'documentation'!
+
+copyright
+"
+ COPYRIGHT (c) 2007 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.
+"
+!
+
+documentation
+"
+ an ExternalFunctionCallback wraps a block into a C-callable function;
+ i.e. it creates a closure, which as seen from C-code looks like an ordinary
+ function pointer, but when invoked evaluates a smalltalk block.
+
+ [author:]
+ Claus Gittinger
+"
+!
examples
"
@@ -70,17 +254,56 @@
cb := ExternalFunctionCallback new.
cb returnType:#bool argumentTypes:#(uint).
+ cb action:[:args | Transcript showCR:args. true].
cb generateClosure.
- cb action:[:arg | Transcript showCR:arg].
- cb address.
+ cb address. 'can be passed to C'.
+
+ ExternalFunctionCallback testCall:cb withArgument:123.
+ cb release
"
! !
+!ExternalFunctionCallback class methodsFor:'helpers'!
+
+closureIndexFor:aCallBack
+ CallBackRegistry isNil ifTrue:[
+ CallBackRegistry := WeakArray with:aCallBack.
+ ] ifFalse:[
+ CallBackRegistry := CallBackRegistry copyWith:aCallBack.
+ ].
+ ^ CallBackRegistry size.
+!
+
+testCall:aCallback withArgument:arg
+%{
+ INTFUNC f = __externalAddressVal(aCallback);
+ INT result;
+
+ fprintf(stderr, "calling callBack %x(%d)\n", f, __intVal(arg));
+ result = (*f)(__intVal(arg));
+ fprintf(stderr, "result from callBack is %x\n", result);
+%}
+! !
+
!ExternalFunctionCallback methodsFor:'accessing'!
-action:something
- action := something.
+action:aOneArgBlock
+ action := aOneArgBlock.
+! !
+
+!ExternalFunctionCallback methodsFor:'callback'!
+
+value:argList
+ "because this is evaluated from C, we probably should not block or abort or do
+ any other things which confuse C
+ (its probably a good idea to write something into a queue here)"
+
+self halt.
+ action notNil ifTrue:[
+ ^ action valueWithArguments:argList
+ ].
+ ^ nil
! !
!ExternalFunctionCallback methodsFor:'generation'!
@@ -132,32 +355,47 @@
"Created: / 11-06-2007 / 15:52:01 / cg"
! !
+!ExternalFunctionCallback methodsFor:'private-debugging'!
+
+debugCall:args
+ self halt.
+ ^ nil
+! !
+
!ExternalFunctionCallback methodsFor:'private-generation'!
generateClosure
- |argTypeSymbols returnTypeSymbol failureCode failureInfo returnValue stClass vtOffset
- virtual async unlimitedStack callTypeNumber returnValueClass argValueClass|
+ |argTypeSymbols returnTypeSymbol failureCode failureInfo
+ callTypeNumber returnValueClass argValueClass callBackIndex|
argTypeSymbols := argumentTypes.
returnTypeSymbol := returnType.
-
-%{ /* STACK: 100000 */
-#if 1
+ callBackIndex := self class closureIndexFor:self.
+%{
#ifdef HAVE_FFI
- ffi_cif __cif;
- ffi_type *__argTypesIncludingThis[MAX_ARGS+1];
- ffi_type **__argTypes = __argTypesIncludingThis;
+ ffi_cif *pcif;
ffi_type *__returnType = NULL;
- void *__returnValuePointer;
static int null = 0;
int i;
ffi_abi __callType = FFI_DEFAULT_ABI;
int __numArgsWanted;
+ struct closurePlusCIF {
+ ffi_closure closure;
+ ffi_cif cif;
+ ffi_type *argTypes[MAX_ARGS];
+ } *closurePlusCIFp;
ffi_closure *pcl;
+ ffi_cif *cif;
+ ffi_type **argTypePtrs;
+
+ closurePlusCIFp = (struct closurePlusCIF *) malloc(sizeof(struct closurePlusCIF));
+ cif = &(closurePlusCIFp->cif);
+ argTypePtrs = closurePlusCIFp->argTypes;
+ pcl = &(closurePlusCIFp->closure);
#define __FAIL__(fcode) \
{ \
- failureCode = fcode; goto getOutOfHere; \
+ failureCode = fcode; free(closurePlusCIFp); goto getOutOfHere; \
}
if (argTypeSymbols == nil) {
@@ -346,7 +584,7 @@
__FAIL__(@symbol(UnknownArgumentType))
}
- __argTypes[i] = thisType;
+ closurePlusCIFp->argTypes[i] = thisType;
}
failureInfo = nil;
@@ -375,34 +613,39 @@
#endif
}
-# ifdef VERBOSE
- printf("prep_cif cif-ptr=%x\n", &__cif);
-# endif
- if (ffi_prep_cif(&__cif, __callType, __numArgsWanted, __returnType, __argTypesIncludingThis) != FFI_OK) {
+ if (@global(ExternalFunctionCallback:Verbose) == true) {
+ printf("prep_cif cif-ptr=%x\n", cif);
+ }
+
+ if (ffi_prep_cif(cif, __callType, __numArgsWanted, __returnType, argTypePtrs) != FFI_OK) {
__FAIL__(@symbol(FFIPrepareFailed))
}
- pcl = (ffi_closure *) malloc(sizeof(ffi_closure));
-# ifdef VERBOSE
- printf("closure is 0x%x (%d bytes)\n", pcl, sizeof(ffi_closure));
-# endif
- if (ffi_prep_closure(pcl, &__cif, ExternalFunctionCallback__closure_test_fn, (void *) 3 /* userdata */) != FFI_OK) {
+
+ if (@global(ExternalFunctionCallback:Verbose) == true) {
+ printf("closure is 0x%x (%d bytes)\n", pcl, sizeof(ffi_closure));
+ printf("index is %d\n", __intVal(callBackIndex));
+ }
+ if (ffi_prep_closure(pcl, cif, ExternalFunctionCallback__closure_wrapper_fn, (void *)(__intVal(callBackIndex)) /* userdata */) != FFI_OK) {
__FAIL__(@symbol(FFIPrepareClosureFailed))
}
-# ifdef VERBOSE
- printf("pcl->cif is 0x%x\n", pcl->cif);
- printf("pcl->fun is 0x%x\n", pcl->fun);
- printf("pcl code is %02x %02x %02x %02x\n", ((char *)pcl)[0],((char *)pcl)[1],((char *)pcl)[2],((char *)pcl)[3]);
- printf(" %02x %02x %02x %02x\n", ((char *)pcl)[4],((char *)pcl)[5],((char *)pcl)[6],((char *)pcl)[7]);
- printf(" %02x %02x %02x %02x\n", ((char *)pcl)[8],((char *)pcl)[9],((char *)pcl)[10],((char *)pcl)[11]);
- printf(" %02x %02x %02x %02x\n", ((char *)pcl)[12],((char *)pcl)[13],((char *)pcl)[14],((char *)pcl)[15]);
+ if (@global(ExternalFunctionCallback:Verbose) == true) {
+ printf("pcl->cif is 0x%x\n", pcl->cif);
+ printf("pcl->fun is 0x%x\n", pcl->fun);
+ printf("pcl code at %x is:\n", pcl);
+ printf(" %02x %02x %02x %02x\n", ((unsigned char *)pcl)[0],((unsigned char *)pcl)[1],((unsigned char *)pcl)[2],((unsigned char *)pcl)[3]);
+ printf(" %02x %02x %02x %02x\n", ((unsigned char *)pcl)[4],((unsigned char *)pcl)[5],((unsigned char *)pcl)[6],((unsigned char *)pcl)[7]);
+ printf(" %02x %02x %02x %02x\n", ((unsigned char *)pcl)[8],((unsigned char *)pcl)[9],((unsigned char *)pcl)[10],((unsigned char *)pcl)[11]);
+ printf(" %02x %02x %02x %02x\n", ((unsigned char *)pcl)[12],((unsigned char *)pcl)[13],((unsigned char *)pcl)[14],((unsigned char *)pcl)[15]);
+ }
+ __INST(address_) = pcl;
+
+#if 0
+ ExternalFunctionCallback__test_call_closure((INTFUNC)pcl);
#endif
- ExternalFunctionCallback__doCall_closure((INTFUNC)pcl);
- free(pcl);
#else /* no FFI support */
__FAIL__(@symbol(FFINotSupported));
#endif /* HAVE_FFI */
-#endif /* 0 */
getOutOfHere: ;
%}.
failureCode notNil ifTrue:[
@@ -412,3 +655,28 @@
"Created: / 11-06-2007 / 21:53:02 / cg"
! !
+
+!ExternalFunctionCallback methodsFor:'private-releasing'!
+
+release
+ |idx|
+
+ idx := CallBackRegistry identityIndexOf:self.
+ CallBackRegistry at:idx put:nil.
+%{
+ ffi_closure *pcl = (ffi_closure *)__INST(address_);
+
+ __INST(address_) = 0;
+ if (pcl) {
+ free(pcl);
+ }
+%}.
+ self invalidateReference.
+
+! !
+
+!ExternalFunctionCallback class methodsFor:'documentation'!
+
+version
+ ^ '$Header: /cvs/stx/stx/libbasic/ExternalFunctionCallback.st,v 1.2 2007-06-13 21:13:25 cg Exp $'
+! !