An application: foreign function bindings
C
int puts(const char *s);
1/ 19
C in two minutes
object types
numeric types
int, char, float, . . .
pointers
int *, char *, int **, . . .
structures and unions
struct t { int x, char y };
arrays
int x[3] = { 1, 2, 3};
Operations
address, sizeof, read, write, . . .
function types
built from object types
n arguments, one return type
int(const char *);
char *(char *, char *);
Operations
call
2/ 19
Talking to C: two challenges
Conversions between value representations
Long_val, Val_long, caml_copy_double, . . .
Interactions with the garbage collector
Protect locals & parameters against disappearance & destruction
value puts_stub(value s)
{
CAMLparam1(s);
const char *p = String_val(s);
int n = puts(p);
CAMLreturn(Val_int(n));
}
3/ 19
Representing types
4/ 19
Representing object types
C object types
type ::=
int
char
type *
. . .
Representing C object types
type _ typ =
Int : int typ
| Char : char typ
| Ptr : ’a typ → ’a ptr typ
| . . .
| View : (’a → ’b)
* (’b → ’a)
* ’a typ → ’b typ
| . . .
let string : string typ =
View (ptr_of_string , string_of_ptr , Ptr Char)
5/ 19
Operations on object types
Low-level operations
val read : ’a typ → address → ’a
val write : ’a typ → ’a → address → unit
val sizeof : ’a typ → int
let read : type a. a typ → address → a =
fun typ addr → match typ with
| Int → read_int address
| Char → read_char address
| . . .
Higher-level operations
type ’a ptr (* = ’a typ * address *)
val (!@) : ’a ptr → ’a
val (@+) : ’a ptr → int → ’a ptr
6/ 19
Representing function types
C function types
ftype ::= type(type , type , . . ., type)
Representing C function types
type _ fn = Returns : ’a typ → ’a fn
| Function: ’a typ * ’b fn → (’a → ’b) fn
let (@→) a b = Function (a,b) and returning v = Returns v
Example
Ptr Char @→ Int @→ returning Int
represents
int(char *, int)
7/ 19
Operations on function types
val foreign : string → (’a → ’b) fn → (’a → ’b)
Example
let puts = foreign “puts” (string @→ returning int)
produces
val puts : string → int
8/ 19
Anatomy of a binding
let puts = foreign “puts” (str ing @→ returning int)
puts “Hello , world”
1. resolve the name
2. create a buffer with enough space
3. convert and write arguments
4. apply function
5. read results
9/ 19
Anatomy of a binding
let puts = foreign “puts” (str ing @→ returning int)
puts “Hello , world”
1. resolve the name “puts” ⇝ 0x7f0d1eebcf60
2. create a buffer with enough space
3. convert and write arguments
4. apply function
5. read results
9/ 19
Anatomy of a binding
let puts = foreign “puts” (str ing @→ returning int)
puts “Hello , world”
1. resolve the name “puts” ⇝ 0x7f0d1eebcf60
2. create a buffer with enough space
3. convert and write arguments
4. apply function
5. read results
9/ 19
Anatomy of a binding
let puts = foreign “puts” (str ing @→ returning int)
puts “Hello , world”
1. resolve the name “puts” ⇝ 0x7f0d1eebcf60
2. create a buffer with enough space
3. convert and write arguments ff ea 23 22
4. apply function
5. read results
9/ 19
Anatomy of a binding
let puts = foreign “puts” (str ing @→ returning int)
puts “Hello , world”
1. resolve the name “puts” ⇝ 0x7f0d1eebcf60
2. create a buffer with enough space
3. convert and write arguments ff ea 23 22
4. apply function ff ea 23 22 00 1b
5. read results
9/ 19
Anatomy of a binding
let puts = foreign “puts” (str ing @→ returning int)
puts “Hello , world”
1. resolve the name “puts” ⇝ 0x7f0d1eebcf60
2. create a buffer with enough space
3. convert and write arguments ff ea 23 22
4. apply function ff ea 23 22 00 1b
5. read results puts “Hello, world” ⇝ 13
9/ 19
More type interpretations
10/ 19
Drawbacks of dynamism
No type safety
Name lookup may fail dynamically
Interpretive overhead
Can’t use standard tools (nm, objdump, ldd, . . . )
11/ 19
Staged binding
module Bindings(F: FOREIGN) = struct
open F
let puts = foreign “puts” (string @→ returning int)
end
value puts_stub(value s) {
char *p = Ptr_val(s);
int n = puts(p);
return Val_int(n);
}
external puts_stub : address → int =
“puts_stub”
let foreign nm fn = match nm, fn with
| “puts”, Function (View . . .
Bindings(Generated_ML)
12/ 19
Staged binding
module Bindings(F: FOREIGN) = struct
open F
let puts = foreign “puts” (string @→ returning int)
end
value puts_stub(value s) {
char *p = Ptr_val(s);
int n = puts(p);
return Val_int(n);
}
external puts_stub : address → int =
“puts_stub”
let foreign nm fn = match nm, fn with
| “puts”, Function (View . . .
Bindings(Generated_ML)
12/ 19
Staged binding: abstracting the interpretation
module type FOREIGN = sig
type _ result
val foreign: string → (’a→’b) → (’a→’b) result
end
Example
module Bindings(F: FOREIGN) = struct
open F
let puts = foreign “puts” (string @→ returning int)
end
13/ 19
Staged binding: recovering the dynamic interpretation
module Foreign_dynamic = struct
type ’a result = ’a
let foreign = foreign (* i.e. implementation above *)
end
Example
Bindings(Foreign_dynamic)
produces
sig
val puts : string → int
end
14/ 19
Staged binding: generating C
val generate_C : string → ’a fn → unit
module Foreign_generate_C = struct
type ’a result = unit
let foreign = generate_C
end
Example
Bindings(Foreign_generate_C)
outputs
value puts_stub(value s) {
char *p = Ptr_val(s);
int n = puts(p);
return Val_int(n);
}
15/ 19
Staged binding: generating ML
val generate_ML : string → ’a fn → unit
module Foreign_generate_ML = struct
type ’a result = unit
let foreign = generate_ML
end
Example
Bindings(Foreign_generate_ML)
outputs
external puts_stub : address → int = “puts_stub”
let foreign nm fn = match nm , fn with
| “puts”, Function (View (_, froms , Ptr Char)) →
fun s → puts_stub (froms s)
| “puts”, fn → fail “type mismatch”
| name , _ → fail “unexpected name”
16/ 19
Staged binding: linking
module Bindings(F: FOREIGN) = struct
open F
let puts = foreign “puts” (string @→ returning int)
end
module Generated_ML : FOREIGN with type ’a result = ’a
= (* code generated on previous slide *)
Bindings(Generated_ML)
Type safe linking via type refinement!
17/ 19
(Some details omitted)
concurrency
C
remote calls
C
function pointers
void (*)(int , float);
more object types
struct s x[3];
determining object layout
struct t {
int x, y;
};
0:
1:
2:
3:
4:
5:
6:
7:
8:
x
y
inverted bindings
C
18/ 19
Next time: overloading
val (=) : {E:EQ} → E.t → E.t → bool
19/ 19