On 25 November 2012 21:59, Stéphane Ducasse <[email protected]> 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.

Reply via email to