SimpleExternalLibraryFunction.st
author Claus Gittinger <cg@exept.de>
Tue, 09 Jul 2019 20:55:17 +0200
changeset 24417 03b083548da2
parent 21623 0fd2de531f9a
permissions -rw-r--r--
#REFACTORING by exept class: Smalltalk class changed: #recursiveInstallAutoloadedClassesFrom:rememberIn:maxLevels:noAutoload:packageTop:showSplashInLevels: Transcript showCR:(... bindWith:...) -> Transcript showCR:... with:...

"{ Package: 'stx:libbasic' }"

"{ NameSpace: Smalltalk }"

ExternalLibraryFunction subclass:#SimpleExternalLibraryFunction
	instanceVariableNames:''
	classVariableNames:''
	poolDictionaries:''
	category:'System-Support'
!

!SimpleExternalLibraryFunction class methodsFor:'documentation'!

documentation
"
    instances of me are used for very simple functions, with all integer or
    equivalent arguments.
    These avoid the realively expensive ffi- arg setup, and jump directly to the
    target function.
    Can be used for a subset of all external functions and only on some machines.
    Only for tuning; the superclass must provide a fallback for all calls
"
! !

!SimpleExternalLibraryFunction methodsFor:'private invoking'!

invokeFFIwithArguments:argumentsOrNil forCPPInstance:aReceiverOrNil
    "the caller must have already checked, if instances of me are appropriate.
     May only be used for up to 10 args, with INT-sized non-float, non-struct arguments,
     and int-sized, non-float, non-struct return value.
     Now, all I do is convert the arguments and transfer directly; without the expensive ffi..."

    |argTypeSymbols returnTypeSymbol failureCode failureArgNr failureInfo returnValue stClass vtOffset
     virtual objectiveC async unlimitedStack callTypeNumber returnValueClass argValueClass
     oldReturnType oldArgumentTypes|

    virtual := self isVirtualCPP.
    objectiveC := self isObjectiveC.
    (virtual "or:[self isNonVirtualCPP]") ifTrue:[
	aReceiverOrNil isNil ifTrue:[
	    "/ must have a c++ object instance
	    self primitiveFailed.
	].

	"/ and it must be a kind of ExternalStructure !!
	(aReceiverOrNil isExternalStructure) ifFalse:[
	    self primitiveFailed.
	].
	virtual ifTrue:[
	    vtOffset := name.
	].
    ] ifFalse:[
	objectiveC ifTrue:[
	    aReceiverOrNil isNil ifTrue:[
		"/ must have an objective-c object instance
		self primitiveFailed.
	    ].
	    (aReceiverOrNil isObjectiveCObject) ifFalse:[
		self primitiveFailed
	    ]
	] ifFalse:[
	    aReceiverOrNil notNil ifTrue:[
		"/ must NOT have a c++/objectiveC object instance
		self primitiveFailed.
	    ]
	].
    ].

%{  /* STACK: 100000 */

#define VERBOSE
#define MAX_ARGS 10

    int __numArgs, __numArgsIncludingThis;
    static INT null = 0;
    INT __args[MAX_ARGS+1];
    INT retVal;
    int i = -1;
    int argIdx = 0;
    INTLFUNC codeAddress = (voidFUNC)__INST(code_);
    int __numArgsWanted;

#   define __FAIL__(fcode) \
    { \
	failureCode = fcode; failureArgNr = __mkSmallInteger(i+1); goto getOutOfHere; \
    }

    if (argumentsOrNil == nil) {
	__numArgs = 0;
    } else if (__isArrayLike(argumentsOrNil)) {
	__numArgs = __arraySize(argumentsOrNil);
    } else {
	__FAIL__(@symbol(BadArgumentVector))
    }
    if (__numArgs != __numArgsWanted) {
	__FAIL__(@symbol(ArgumentCountMismatch))
    }
    if (__numArgs > MAX_ARGS) {
	__FAIL__(@symbol(TooManyArguments))
    }

    /*
     * validate the c++ object
     */
    if (aReceiverOrNil != nil) {
	struct cPlusPlusInstance {
	    void **vTable;
	};
	struct cPlusPlusInstance *inst;

	if (__isExternalAddressLike(aReceiverOrNil)) {
	    inst = (void *)(__externalAddressVal(aReceiverOrNil));
	} else if (__isExternalBytesLike(aReceiverOrNil)) {
	    inst = (void *)(__externalBytesVal(aReceiverOrNil));
	} else {
	    __FAIL__(@symbol(InvalidInstance))
	}
	__args[0] = (INT)inst;
	__numArgsIncludingThis = __numArgs + 1;
	argIdx = 1;

	if (virtual == true) {
	    if (! __isSmallInteger(vtOffset)) {
		__FAIL__(@symbol(InvalidVTableIndex))
	    }
	    codeAddress = inst->vTable[__intVal(vtOffset)];
# ifdef VERBOSE
	    if (@global(Verbose) == true) {
		printf("virtual %d codeAddress: %"_lx_"\n", __intVal(vtOffset), (INT)codeAddress);
	    }
# endif
	}
    } else {
	__numArgsIncludingThis = __numArgs;
# ifdef VERBOSE
	if (@global(Verbose) == true) {
	    printf("codeAddress: %"_lx_"\n", (INT)codeAddress);
	}
# endif
    }

    /*
     * validate all arg types, map each to an ffi_type, and setup arg-buffers
     */
    for (i=0; i<__numArgs; i++, argIdx++) {
	OBJ arg;

	failureInfo = __mkSmallInteger(i+1);   /* in case there is one */

	arg = __ArrayInstPtr(argumentsOrNil)->a_element[i];

	if (__isSmallInteger(arg)) {
	    __args[argIdx] = __intVal(arg);
	} else {
	   INT iv = __signedLongIntVal(arg);
	   if (iv != 0) {
		__args[argIdx]  = iv;
	    } else {
		unsigned INT iv = __unsignedLongIntVal(arg);
		if (iv != 0) {
		    __args[argIdx] = iv;
		} else {
		    if (__isStringLike(arg)) {
			__args[argIdx] = (INT)(__stringVal(arg));
		    } else {
			if (__isBytes(arg)) {
			    __args[argIdx] = (INT)(__byteArrayVal(arg));
			    if (arg == NULL) {
				__args[argIdx] = (INT)0;
			    } else {
				if (__isExternalAddressLike(arg)) {
				    __args[argIdx] = (INT)(__externalAddressVal(arg));
				} else {
				    if (__isExternalBytesLike(arg)) {
					__args[argIdx] = (INT)(__externalBytesVal(arg));
				    } else {
					__FAIL__(@symbol(InvalidArgument))
				    }
				}
			    }
			}
		    }
		}
	    }
	}
    }
    failureInfo = nil;

    retVal = (*codeAddress)(__args[0], __args[1], __args[2], __args[3], __args[4], __args[5], __args[6],
			    __args[7], __args[8], __args[9], __args[10]);

# ifdef VERBOSE
    if (@global(Verbose) == true) {
	printf("retval is %"_ld_" (0x%"_lx_")\n", retVal, retVal);
    }
# endif

    if ((returnTypeSymbol == @symbol(int))
     || (returnTypeSymbol == @symbol(sint))
     || (returnTypeSymbol == @symbol(sint8))
     || (returnTypeSymbol == @symbol(sint16))
     || (returnTypeSymbol == @symbol(sint32))) {
# ifdef VERBOSE
	if (@global(Verbose) == true) {
	    printf("return int: %x\n", retVal);
	}
# endif
	RETURN ( __MKINT(retVal) );
    }
    if ((returnTypeSymbol == @symbol(uint))
     || (returnTypeSymbol == @symbol(uint8))
     || (returnTypeSymbol == @symbol(uint16))
     || (returnTypeSymbol == @symbol(uint32))) {
# ifdef VERBOSE
	if (@global(Verbose) == true) {
	    printf("return uint: %x\n", retVal);
	}
# endif
	RETURN ( __MKUINT(retVal) );
    }
    if (returnTypeSymbol == @symbol(bool)) {
	RETURN ( retVal ? true : false );
    }
    if (returnTypeSymbol == @symbol(void)) {
	RETURN ( nil );
    }
    if (returnTypeSymbol == @symbol(char)) {
	RETURN ( __MKCHARACTER(retVal & 0xFF) );
    }
    if (returnTypeSymbol == @symbol(wchar)) {
	RETURN ( __MKUCHARACTER(retVal & 0xFFFF) );
    }

# ifdef VERBOSE
    if (@global(Verbose) == true) {
	printf("return pointer: %"_lx_"\n", (INT)(retVal));
    }
# endif
    if (returnTypeSymbol == @symbol(handle)) {
	returnValue = __MKEXTERNALADDRESS(retVal);
    } else if (returnTypeSymbol == @symbol(pointer)) {
	returnValue = __MKEXTERNALBYTES(retVal);
    } else if (returnTypeSymbol == @symbol(bytePointer)) {
	returnValue = __MKEXTERNALBYTES(retVal);
    } else if (returnTypeSymbol == @symbol(charPointer)) {
	returnValue = __MKSTRING(retVal);
    } else if (returnTypeSymbol == @symbol(wcharPointer)) {
	returnValue = __MKU16STRING(retVal);
    } else {
	__FAIL__(@symbol(UnknownReturnType2))
    }
getOutOfHere: ;
%}.
    failureCode notNil ifTrue:[
	(failureCode == #UnknownReturnType or:[ failureCode == #UnknownArgumentType ]) ifTrue:[
	    oldReturnType := returnType.
	    oldArgumentTypes := argumentTypes.
	    self adjustTypes.
	    ((oldReturnType ~= returnType) or:[oldArgumentTypes ~= argumentTypes]) ifTrue:[
		thisContext restart
	    ].
	].
	(failureCode == #BadArgForAsyncCall) ifTrue:[
	    ^ self tryAgainWithAsyncSafeArguments:argumentsOrNil forCPPInstance:aReceiverOrNil
	].
	(failureCode == #FFINotSupported) ifTrue:[
	    self primitiveFailed:'FFI support missing in this build'.
	].

	self primitiveFailed.   "see failureCode and failureInfo for details"
	^ nil
    ].

    returnType isSymbol ifTrue:[
	returnValueClass notNil ifTrue:[
	    self isConstReturnValue ifTrue:[
		returnValue changeClassTo:returnValueClass.
		^ returnValue
	    ].
	    ^ returnValueClass fromExternalAddress:returnValue.
	].
    ] ifFalse:[
	returnType isCPointer ifTrue:[
	    returnType baseType isCStruct ifTrue:[
		stClass := Smalltalk classNamed:returnType baseType name.
		stClass notNil ifTrue:[
		    self isConstReturnValue ifTrue:[
			returnValue changeClassTo:returnValueClass.
			^ returnValue
		    ].
		    ^ stClass fromExternalAddress:returnValue.
		].
	    ].
	    returnType baseType isCChar ifTrue:[
		^ returnValue stringAt:1
	    ].
	].
    ].

    ^ returnValue

    "Created: / 01-08-2006 / 13:56:23 / cg"
    "Modified: / 31-03-2016 / 00:03:03 / cg"
! !

!SimpleExternalLibraryFunction class methodsFor:'documentation'!

version
    ^ '$Header$'
!

version_CVS
    ^ '$Header$'
! !