Pharo foreign function interface (FFI) by example by Esteban Lorenzano

FASTPresentations 1,127 views 25 slides Nov 22, 2017
Slide 1
Slide 1 of 25
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25

About This Presentation

Since Pharo 5 we have been developing a new framework for communicate with external libraries called UnifiedFFI (or UFFI) that has opened the doors to perform a huge step in our support for mainstream necessities. In this talk I will talk about the intrinsic problems of developing such frameworks an...


Slide Content

UFFI
by Example

About me
Pharo architect since 2012
Owned a company to develop in
Pharo back in 2008
Java senior architect for 7 years (and
15 years overall Java experience)
Web, microprocessors, etc., etc., etc.
JavaScript, C++, ObjC, C#, Delphi,
ASM and lots of languages no longer
exist or have been long-time
forgotten
24 years (!) programming experience
(yes… I’m becoming old)

Why FFI*?
Why we want it or even need it?
*FFI: Foreign Function Interface

–Dan Ingalls
“an operating system is a collection of things that
don't fit inside a language; there shouldn't be one”

–John Lennon
“Life is what happens to you while you're busy
making other plans..”

FFI in Pharo
FFI Plugin
Alien FFI
NativeBoost FFI

FFI plugin
Compiler build (no extensions available).
Basic C types and structures
No callbacks
Not very easy to understand
void pango_cairo_update_layout (cairo_t *cr, PangoLayout *layout)
CairoCanvas >> pangoUpdateCairo: anAddress1 layout: anAddress2
<cdecl: void 'pango_cairo_update_layout' (void* void*) module: '/opt/
local/lib/libpangocairo-1.0.0.dylib'>

AlienFFI plugin
Object per method (“aliens”)
Very low level
Callbacks!
Not very easy to understand
void pango_cairo_update_layout (cairo_t *cr, PangoLayout *layout)
CairoCanvas >> pangoUpdateCairo: anAlien1 layout: anAlien2
^ (Alien
lookup: 'pango_cairo_update_layout'
inLibrary: '/opt/local/lib/libpangocairo-1.0.0.dylib')
primFFICallResult: (result := Alien newGC: 4)
with: anAlien1
with: anAlien2

NativeBoost FFI
Runtime build
Easy to create OO models with it (look Athens)
Very easy to understand
Very low level and hard to modify
void pango_cairo_update_layout (cairo_t *cr, PangoLayout *layout)
CairoCanvas >> pangoUpdateLayout: layout
^ self
nb Call: #(void pango_cairo_update_layout (self, PangoLayout *layout))
module: '/opt/local/lib/libpangocairo-1.0.0.dylib'

Why we removed
NativeBoost?
It was not working on Spur (or in 64bit)
It was hard to maintain (and we need a different
implementation for each architecture)
Since we were using NB exclusively for FFI, we decided
to replace it with a different one, using the VM plugin

What is UFFI?
A front-end to express FFI calls in Pharo.
Uses ThreadedFFIPlugin, but should be able to plug
others in the future.
It shares same philosophy as NativeBoost:
‣keep as much as possible in the image
‣no need to modify VM to add functionality
But it is not ASM: Just plain Smalltalk.

Goals
Keep NativeBoost syntax
‣Because is cool :)
‣Provide backward compatibility for most use-cases
Enhance documentation and self-documentation
Be the unified base for future FFI backends
implementations

Meta-goals
Expand the “universe of possible”:
-Be able to use existing libraries our there instead
needing to reproduce them (run away of the “not
invented here” syndrome).
-Do not need plugins to expand functionality
Provide an easy and efficient way to interact with the
outside world.

How does a call looks like?
char *getenv(const char *)
getEnv: variable
^ self
ffiCall: #( String getenv( String variable ))
module: LibC
(People who know NativeBoost will find this very familiar….)

How does a call looks like?
char *getenv(const char *)
getEnv: variable
^ self
ffiCall: #( String getenv( String variable ))
module: LibC
A regular Pharo method with one argument

How does a call looks like?
char *getenv(const char *)
getEnv: variable
^ self
ffiCall: #( String getenv( String variable ))
module: LibC
A literal array to represent C function

How does a call looks like?
char *getenv(const char *)
getEnv: variable
^ self
ffiCall: #( String getenv( String variable ))
module: LibC
Types annotation used to generate marshalling code

How does a call looks like?
char *getenv(const char *)
getEnv: variable
^ self
ffiCall: #( String getenv( String variable ))
module: LibC
The value to be passed when calling out

How does a call looks like?
char *getenv(const char *)
getEnv: variable
^ self
ffiCall: #( String getenv( String variable ))
module: LibC
The library to lookup C function

FFILibrary
A very simple abstraction to define module names that
can be different each platform.
Can be used also as a place to store C function
definitions (like a real library :) ).

Insights to UnifiedFFI
getEnv: variable
^ self
ffiCall: #(void pango_cairo_update_layout (self, PangoLayout *layout))
module: PangoLibrary
1. Generate bytecodes for marshalling
2. Re-send the method execution
void pango_cairo_update_layout (cairo_t *cr, PangoLayout *layout)

How does a call looks like?
49 <20> pushConstant: <cdecl: void 'pango_cairo_update_layout' (void*
void*) module: '/opt/local/lib/libpangocairo-1.0.0.dylib'>
50 <05> pushRcvr: 5
51 <10> pushTemp: 0
52 <76> pushConstant: 1
53 <E1> send: instVarAt:
54 <8A 82> pop 2 into (Array new: 2)
56 <E2> send: invokeWithArguments:
57 <87> pop
58 <78> returnSelf
void pango_cairo_update_layout (cairo_t *cr, PangoLayout *layout)

Types
Support for standard C types: int, float, etc.
Support for type aliases (map a name to one of the defined types)
Complex types:
-FFIExternalObject: External addresses (objects)
-FFIOpaqueObject: Opaque C types/structures
-FFIExternalStructure
-FFIExternalArray, FFITypeArray
-FFIExternalEnumeration
-FFIExternalValueHolder: Buffers (to pass referenced data, e.g. “double *”)
-FFIConstantHandle: Windows HANDLE (constant addresses)

So… and the example?
Demo time :)

Thanks!
Smalltalk quitSession.