And don't forget to make the primitive declaration automatic, nobody can remember that cryptic string :) On 2012-11-25, at 18:20, Igor Stasenko <siguctua@gmail.com> wrote:
On 25 November 2012 21:59, Stéphane Ducasse <stephane.ducasse@inria.fr> wrote:
Igor your proposal looks nice.
it's not a proposal anymore.. i already made the changes :)
Stef
On Nov 22, 2012, at 2:47 PM, Igor Stasenko wrote:
Hi,
i am thinking about redesign of FFI top-level api, because it not very good.
First thing is that NBFFICallout referenced in too many places (187 in my image). So, from one side, we should encourage using a convenience syntax, using
self nbCall: .. instead of NBFFICallout ...
but there's one more thing: - a code generator has many different ways of use, and i actually going to add another one, but i see that it will explode the protocol(s) without make it convenient to use.
Imagine that you want to generate a callout to function, which address cannot be obtained directly from external library. This is actually a real issue, which Esteban met when implementing bindings to Chipmunk library: - some functions are not exposed directly by dynamic library, but indirectly: the symbol(s) which library exports is not direct pointer(s) to functions but instead a pointers to variable, where the function pointer is stored. So, in order to get function pointer , one needs first to get a pointer to symbol, and then read the pointer value at that address.
So, i thought that i would simply add new protocol to NBFFICallout:
call: anonFunctionSignature convention: callConvention functionAddress: aFunctionAddressBlock
Then, one could use it like:
myMethod <primitive: #primitiveNativeCall module: #NativeBoostPlugin error: errorCode >
^ NBFFICallout call: #( int () ) convention: #cdecl functionAddress: [ self getPointerTo: 'functionname' ]
but it is too elaborate.. you can imagine that if one would like to implement a more convenient form, he could just do:
myMethod <primitive: #primitiveNativeCall module: #NativeBoostPlugin error: errorCode >
^ self indirectCall: #( int () ) name: 'functionname'
or even:
myMethod <primitive: #primitiveNativeCall module: #NativeBoostPlugin error: errorCode >
^ self indirectCall: #( int functionname () )
now the problem is that you cannot wrap the NBFFICallout method in order to implement convenience method, because of use of thisContext.
I also concerned about exploding NBFFICallout public API.. which imo doesn't makes much sense, because we should encourage the use of convenience methods.
So, i looking for improving public API:
- First thing is getting rid of direct references to NBFFICallout.. - second thing is avoid tying public API with thisContext (so you can create own convenience methods without much hassle).
So, lets imagine what and how we can do it:
foo <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'>
^ NBFFICallout cdecl: #( int foo() ) module: 'bar'
1. getting rid of global:
foo <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'>
^ self nbCallout cdecl: #( int foo() ) module: 'bar'
2. separating the call convention from signature:
foo <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'>
^ self nbCallout cdecl call: #( int foo() ) module: 'bar'
3. as a bonus, since cdecl is most used, we can assume it is default:
foo <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'>
^ self nbCallout call: #( int foo() ) module: 'bar'
4. avoid hardcoding module:
foo <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'>
^ self nbCallout call: #( int foo() ) module: self module
5. pass code generation options as a separate message, instead of additional keyword:
foo <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'>
^ self nbCallout options: #( +optFoo - optBar); call: #( int foo() ) module: self module
6. finally users would want to do it in most convenient way:
foo <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'>
^ self call: #( int foo() )
Which means that #call: is convenience method which they implement by themselves. Here how i think it should look like:
call: fnSpec
^ (self nbCalloutIn: thisContext sender) cdecl; options: #(+optFoo - optBar); call: fnSpec module: self module.
If you look at current implementation of Object>>nbCall:
nbCall: fnSpec " you can override this method if you need to"
| sender | sender := thisContext sender.
^ NBFFICallout handleFailureIn: sender nativeCode: [ :gen | gen sender: sender; callType: self nbCallingConvention; generateCall: fnSpec module: self nbLibraryNameOrHandle]
it is almost the same, except from cryptic use of blocks, and private internal NBFFICallout methods which doing the job.
So, basicly, i want to get rid of NBFFICallout class >> [cdecl/stdcall]: module: [options:] [cdecl/stdcall]: emitCall: [options:]
and replace them with #call:module: and #call: emit: and also add new one: #call:address:
which can be used for passing a function pointer directly.
And any future extensions to public API will be a single method. Not 2,3 or more, like today..
-- Best regards, Igor Stasenko.
-- Best regards, Igor Stasenko.