Recent Changes - Search:

Software

Wiki Docs

FFI

(redirected from LambdaVM.FFINotes)

Syntax

Foreign imports and exports work just like they do in C. You use the same syntax with "jvm" as the calling convention.

Foreign imports

The "external entity" as the FFI spec defines it (the stuff between the quotes) for a jvm foreign import should be of one of 5 forms:

  • "methodname" for a non-static method (the type will be used to determine the class name)
  • "full.package.to.Class.methodname" for a static method
  • "&fieldname" for a non-static field (again, the type will determine the class name)
  • "&full.path.to.Class.fieldname" for a static field
  • "<init>" for a constructor (again, type determines class name)

For all non-static imports (excluding "new") the first argument must be the class the method/field is in (followed by the function arguments). Example:

foreign import jvm "charAt" read :: JString -> Int -> IO Word16

For "<init>" imports the result type must be a java class. Example:

foreign import jvm "<init>" newString :: UArray Int Word16 -> IO JString

Field imports must have the form of either a setter (single arg of the field type, void return) or a getter (no args, return type of the field)

foreign import jvm "&myField" set_MyField :: MyClass -> Int -> IO ()
foreign import jvm "&myField" get_MyField :: MyClass -> IO Int

"&" is used to prefix field imports for no particular reason other than it is used to import global variables with the C FFI and it is easy to type. Suggestions are welcome.

As with normal FFI stuff you can omit the IO on the return type. This is just like doing unsafePerformIO and requires all the same precautions.

Foreign exports

The "external entity" as the FFI spec defines it (the stuff between the quotes) for a jvm foreign export is the name of the method you want to export your function as. A foreign export declaration will create a static method in a class with the same name as your module with the name you specify.

foreign export jvm "add" (+) :: Int -> Int -> Int

will create a java method with the signature

int add(int x, int y);

You can export multiple function with the same name as long as their types are different (normal java overloading applies). Duplicate exports with the same type aren't check and will result in an invalid classfile though.

Types

Primitive Types

Java's primitive types map directly to Haskell's. You can safely assume Haskell's Int and java's int are the same. Although you may wish to define type JInt = Int, type JLong = Int64, etc for clarity. (This should probably be in Foreign.Java).

Primitive Type Mappings
Java TypeHaskell TypeUnboxed TypeNotes
booleanBoolBool# 
byteInt8Int8# 
byteWord8Word8#Not unsigned in java
shortInt16Int16# 
charWord16Word16#Can't be Char in HS because HS Chars are unicode code points (0-0x10ffff)
intIntInt# 
intInt32Int32# 
intWordWord#Not unsigned in java
intWord32Word32#Not unsigned in java
intCharChar#Must be in the range 0-0x10ffff, a valid unicode code point
longInt64Int64# 
longWord64Word64#Not unsigned in Java
floatFloatFloat# 
doubleDoubleDouble# 

Java Objects

Java objects are represented as unlifted/unboxed Haskell values (i.e. String#). You use a "foreign type" declaration to bring a java type into scope. Unlifted objects have a few limitation (like not being able to be used to instansiate a polymorphic type) so you probably want to create a boxed version of each java type. The basic boilerplate for a java type would be this:

foreign import jvm type "java.io.InputStream" InputStream#
data InputStream = InputStream InputStream#
foreign import jvm safe "read" read :: InputStream -> IOUArray Int Int8 -> Int -> Int -> IO Int

This could definitely be machine generated but I haven't written a utility to do it yet.

Java Arrays

Java arrays are represented as unboxed arrays (UArray#*, MutableUArray#*, UArray, STUArray, IOUArray). The boxed versions of these arrays must be parameterized with Int as their index to use them in foreign import/exports. This is so we can easily reconstruct the lower and upper bounds stored in the box when we pass an array from Java to Haskell. You must be very careful with immutable arrays in Java. Java doesn't have const-correctness like C++ so immutable arrays will appear as normal mutable arrays in Java. If you modify one behind Haskell's back you'll break referential transparency and likely cause subtle hard to find bugs. If performance isn't an issue and you are unsure of the how the array will be used in Java (maybe you are calling a 3rd party library) you may be better off using freeze/thaw to make a mutable copy of your array in Haskell first.

UArray/STUArray/IOUArray instances are only defined for primitive types. The underlying UArray# is perfectly capable of being parameterized over a primitive java object type though. Unfortunately the UArray instances require a good deal of boilerplate code for each instance. This could probably be generated by a utility (the same one that should generate the foreign import declarations). In the mean time you can either write the instances by hand (see Data/Array/Base.hs) or the unboxed array operations rather than those in the Data.Array hierarchy. It would be nice to replace Data.Array with a saner implementation.

*UArray# is the new ByteArray#. The all encompassing ByteArray# doesn't work for Java so I replaced it with UArray# which is parameterized over the primitive type of its elements. (This certainly needs more explanation.)**

** And this will soon be incorrect as I'm merging Array# and UArray#

Edit - History - Print - Recent Changes - Search
Page last modified on May 04, 2007, at 07:50 PM EDT