ExternalFunctionCallback.st
changeset 10609 fa629d528330
parent 10607 9f42b83e653a
child 10610 44dcb48a04c7
--- 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 $'
+! !