#396 Java FFI

brian Tue 18 Nov 2008

I've "spiked" the Java FFI design and gotten the most basic case working end-to-end. Here is my simple test case which now works:

using [java] java.lang
class Foo
{
  static Void main()
  {
    echo(System.getProperty("java.home", "foo"))
    echo(System.nanoTime())
  }
}

The code has been pushed to hg if you wish to review.

brian Fri 21 Nov 2008

Update on my progress - I've got the following working:

  • calling overloaded methods
  • constructors using Fan ctor syntax such as Date()
  • calling methods with Java primitives such as int

Java primitives are mapped into Fan using the following qnames:

[java]::int
[java]::short
[java]::byte
[java]::float

The big outstanding issue is how to map arrays.

I'm thinking non-primitive arrays are mapped as Fan list types such as JavaObj[]. That requires some "boxing/unboxing" when calling b/w Fan and Java. The other alternative is to pass arrays around as a special type like:

class Array
{
  Int size
  Obj get(Int i)
  Void set(Int i)
}

The special type methods can be efficiently mapped into array opcodes - so we'd get native array performance with casting overhead (we have the casting overhead with lists too). The advantage is we don't need to "box/unbox". The disadvantage is that we loose static typing and convenience of working with a list.

Arrays of primitives are another story because boxing to a Fan list is way too much overhead to efficiently work with Java. I'm thinking primitive arrays need special interop wrappers such as:

class Int32Array
{
  Int size()
  Int get(Int)
  Void set(Int)
}

I'm not planning on doing anything with Java generics at this point.

Any comments?

jodastephen Fri 21 Nov 2008

Could the array question be one to improve the performance of arrays in Fan in general?

What I'm thinking is could an immutable list in Fan be implemented as an array in bytecode. Would the array be suitably safe for passing between threads at the bytecode level?

That way, there would be no need for a dedicated public Array class - there would be a hidden subclass of List instead. There would need to be a method on Fan's list class to determine if the length cannot be changed.

  • Fully mutable - list
  • Fully immutable - array
  • Mutable fixed length - array

brian Fri 21 Nov 2008

We really already have that functionality in List - handled by the toRO and toImmutable subclasses. However note the best we can do it read-only since we can't guarantee a Java class is immutable (at least not yet). We could make some special cases for things like String - maybe even load the immutable class list from a text file.

Since Array is already backed by an Object[] (since my rework last month), boxing an array is really just a single object allocation which would create a fan.sys.List under the covers backed by the original array.

So then the question becomes do box to a mutable or read-only array? It actually doesn't really hurt anything to box as mutable - the only change is that when I grow the array I'll use reflection to grow using the original type. That way unboxing is simply a field access and cast operation.

But I'd definitely be open to hear votes on whether a Java array is boxed as a mutable list or read-only list.

jodastephen Fri 21 Nov 2008

It actually doesn't really hurt anything to box as mutable - the only change is that when I grow the array I'll use reflection to grow using the original type.

If I've understood correctly, then I don't think this works.

Java defines an API in an interface that takes an array. The calling code passes an array in and expects the results to be available in the array that was passed in (yes, this is a hack, but it does happen). If the Fan code implementing this interface adds to the list, then Fan creates a new array, meaning that the calling code doesn't see the changes made in the Fan implementation method.

list = list.toFixedLength()

(BTW, shouldn't ro() and rw() be renamed to toRO() and toRW()? )

So I want to see a mutable, fixed-length implementation of a List in Fan, because that will allow foreign APIs to be implemented properly.

brian Fri 21 Nov 2008

(BTW, shouldn't ro() and rw() be renamed to toRO() and toRW()? )

I prefer ro myself, but for consistency maybe we should rename. I'd like to hear other opinions.

So I want to see a mutable, fixed-length implementation of a List in Fan, because that will allow foreign APIs to be implemented properly.

I can see that angle, but that is really just a tradeoff in flexibility versus trying to babysit the developer. As long as you treat the list like an array and just get/set then it works fine. Also I don't think that is a very common case, I don't think I've seen an API in my life which passes you an array to fill in. The 99% case is when a method returns an array like Class.getFields.

So I'm more included to say an array is boxed to a mutable list.

jodastephen Fri 21 Nov 2008

I don't think I've seen an API in my life which passes you an array to fill in.

Java's list class - toArray(array).

I agree that it could be left to trust the developer, but its quite a magical one to explain. Sometimes the code side effects the calling Java code, and sometimes it doesn't... yuck.

brian Fri 21 Nov 2008

Java's list class - toArray(array).

Good point (although I think that is more of a 1% case).

katox Fri 21 Nov 2008

I don't think I've seen an API in my life which passes you an array to fill in.

Not exactly an API but there is a a common hack how to avoid final restriction of local variable access in inner classes.

Example (codeguru.com):

//: DirList3.java
// Building the anonymous inner class "in-place"
import java.io.*;

public class DirList3 {
public static void main(final String[] args) {
  try {
    File path = new File(".");
    String[] list;
    if(args.length == 0)
      list = path.list();
    else 
      list = path.list(
        new FilenameFilter() {
          public boolean 
          accept(File dir, String n) {
            String f = new File(n).getName();
            return f.indexOf(args[0]) != -1;
          }
        });
    for(int i = 0; i < list.length; i++)
      System.out.println(list[i]);
  } catch(Exception e) {
    e.printStackTrace();
  }
}
} ///:~ 

JohnDG Fri 21 Nov 2008

The big outstanding issue is how to map arrays.

Why can't they be represented as native arrays in all cases, with methods delegating to a static class somewhere? As I recall, this is how you handle non-null primitives.

However, I should point out that arrays are not used much in day-to-day programming. On the Java side, the JCF is far more common.

If you can do VM-specific methods, then perhaps you could have a fromList static method accepting a JFC class, and a toList() method that converts a Fan array into a Java list. This would at least make interop more bearable.

On the Fan side, I'd like to see auto-conversion someday, which would eliminate the need to explicitly call the fromXXX, toXXX methods.

While I'm complaining, let me say I don't like the new home page. :-) The picture of the fan neighborhood is nice, but programmers don't care about where a language came from and aren't interested in reading lists of features. They want to see actual code samples for things they might like to do.

So if it's a choice between the picture and the code sample, please go back to the code sample. :-)

brian Fri 21 Nov 2008

Why can't they be represented as native arrays in all cases, with methods delegating to a static class somewhere? As I recall, this is how you handle non-null primitives.

Primitives are different because we have a closed set of types which are represented on a one-to-one basis with a Fan type. Arrays are a type system anomaly which we can't map into Fan's type system. That is why the two choices are to use a List (keep the type info) or use a generic Array class for all array types (and lose the type info):

Field[] fields := cls.getFields()  // option 1
Array fields := cls.getFields()    // option 2

The third alternative would be come up with real array syntax for Fan - but that doesn't sit well with me.

So if it's a choice between the picture and the code sample, please go back to the code sample. :-)

I think the issue is that any short sample can easily be an immediate turn-off. I've gotten all sorts of comments about the original code sample (more negative than positive). What is the right code sample for a home page?

katox Fri 21 Nov 2008

What is the right code sample for a home page?

A compilable one ;) but I prefer a code sample to the current picture too. Or maybe use a stock photo. Let's move that to another topic.

jodastephen Sat 22 Nov 2008

That is why the two choices are to use a List (keep the type info) or use a generic Array class for all array types (and lose the type info)

I definitely vote for something that keeps the types, and since an array is really just a length restricted list, the List class seems most sensible.

BTW, what is the strategy for alternate implementations of List/Map in Fan? With Java, much power is derived from user produced implementations, such as a list that filters for you, or is backed by a remote data source and so on. Can users create their own List/Map implementations? Is that intended/desirable?

brian Sat 22 Nov 2008

To get started I will map to a normal mutable List. We can play with it and decide how we like that mapping. I'd prefer not to create a new mode of lists which are mutable but not resizable (although we could do that under the covers).

Java array types will be represented in fcode as:

java.bar.Baz[]  =>  [java]foo.bar::[Baz

Although you can't use primitives and array types directly in Fan as variables - we do need a formal representation of the types for the JVM to generate the correct call signatures.

BTW, what is the strategy for alternate implementations of List/Map in Fan?

Right now to keep things simple I've made List and Map final. This avoids the whole problem of user generics. My personal design tastes are to model application domain collections in their own right versus as collection customizations:

class EmployeeList
{
  Employ get(Str id)
  EmployList set(Str id, Emply e) 
  Void each(|Employ e|) 
}

Things like that are duck typed replacements for Lists. But I know in the Java world that people love to create all sorts of various implementations and subclasses of Java collections - so I'd be open to discuss strategies.

alexlamsl Sun 23 Nov 2008

Even though the concurreny utilities in Java does not suit the Fan model well, the basic Java Collection Framework is what I missed most in Fan at the moment.

brian Sun 23 Nov 2008

Java Collection Framework is what I missed most in Fan at the moment.

OK - if others would like to discuss the Fan strategy for the collection framework, then let's create another topic.

brian Mon 24 Nov 2008

Does anyone have any compelling cases for supporting multi-dimensional arrays in phase 1 of the Java FFI?

They aren't used very heavily in the J2SE APIs - ResourceBundle, GridBagLayout, some of the java.awt.image classes.

jodastephen Mon 24 Nov 2008

I can't remember using multi-dimensional arrays, so they could probably be dropped in phase 1.

Login or Signup to reply.