Igor your proposal looks nice.

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.
> 


Reply via email to