SISC Scheme on Android's Dalvik VM


JRuby and Scheme

The JRuby guys seem to be having all the fun when it comes to getting a dynamic language to run on Google's Android OS. As James Nutter put it on this blog post,

"I realized very recently that JRuby is just about the only mainstream JVM languge that can create *new* code while running on the device, which opens up a whole host of possibilities. It is not possible to implement an interactive shell in Groovy or Scala or Clojure, for example, since they all must first compile code to JVM bytecode, and JVM bytecode can't run on the Dalvik VM directly."

While not a 'major' JVM language, I wanted to see if I could get Scheme to perform the same feat on the Dalvik VM. I started exploring Java implementations of Scheme. There are two prominent ones:

Kawa is promising, and it's author already managed to get it to work on Android. Kawa Scheme code, though, must be compiled to Java bytecode to run on the JVM, and so too for the Dalvik VM.

To get Scheme to perform like JRuby, I turned to SISC, which does not require Scheme to be byte compiled to run on the JVM. Like JRuby, SISC offers a fully interactive REPL which can be used to instantiate Java classes 'on the fly.'

I managed to bring up SISC Scheme's REPL on an Android emulator by following the troubleshooting hints I found in these two posts, the first on JRuby and the second on groovy.

Here's what I did:

1. Installation

  1. Downloaded and compiled SISC source code (probably unnecessary, since I only needed the jars)

  2. Converted SISC's jars ( sisc-opt.jar, sisc.jar, sisc-lib.jar, sisc-heap.jar) to Dalvik bytecode. I used the dx tool in platforms/android-2.0.1/tools because I wanted SISC to run on the lastest Android release.

    dx --dex --output ~/sisc.jar <path to sisc source>/sisc.jar

  3. Created a 2.0.1 emulator image using the android tool and called the image 'sisc'

  4. Started up the emulator:

    emulator -avd sisc

  5. Added all the converted SISC jars into the 'data' folder under the root directory: /data. For example,

    adb push sisc.jar data/sisc.jar

  6. Invoked the remote Android shell using adb:

    adb shell

First I attempted to see if SISC would return its version number by using the following command, gleaned from the sisc.sh shell script that comes with SISC's jar/source.

dalvikvm -classpath /data/sisc-opt.jar:/data/sisc.jar:/data/sisc-lib.jar:/data/sisc-heap.jar -Dsisc.home=/data sisc.REPL -v

This was successful. It reported:


"SISC - The Second Interpreter of Scheme Code - 1.17.0-alpha"

Next I attempted to invoke the SISC REPL by leaving off the -v flag.

dalvikvm -classpath /data/sisc-opt.jar:/data/sisc.jar:/data/sisc-lib.jar:/data/sisc-heap.jar -Dsisc.home=/data sisc.REPL

That failed.

In the shell, I looked through the log using logcat. I discovered that SISC needed the java.beans.Introspector class. This led me to the next steps.

  1. Following the groovy blog post, I extracted the java.beans classes from Sun's rt.jar, and then created a jar of them using the jar command line tool.

    jar cvf javabeans.jar java/beans

  2. Converted the classes in this jar to Dalvik bytecode. Since Android ships with a subset of the java.beans classes, dx emits a serious (and amusing message) about converting core libraries into Dalvik bytecode. I quote: "the path you are on will ultimately lead to pain, suffering, grief, and lamentation." Sounds good to me. I overrode the error message by using the --core-library flag.

    dx --dex --core-library -output ~/javabeans.jar javabeans.jar

  3. Pushed this jar into the data directory of the android emulator, using adb push

When I included this jar in the class path, sisc.REPL failed again. Checking logcat and following the chain of dependencies, I did the following:

  1. Added Sun's reflection classes (under sun/reflect) by combining them into a jar file, converting the class files in this jar to Dalvik bytecode with dx, and saving it into a jar called: sunreflect.jar.

  2. Added Sun's awt classes (under sun/awt), following the method in the previous step and producing the file called: sunawt.jar.

  3. Pushed them into the emulator's data directory.

I tried to bring up SISC's REPL with the following command (yes, I know it could be shorter):

dalvikvm -classpath /data/javabeans.jar:/data/sunreflect.jar:/data/sunawt.jar:/data/sisc-opt.jar:/data/sisc.jar:/data/sisc-lib.jar:/data/sisc-heap.jar -Dsisc.home=/data sisc.REPL

Success! After some warning message about the BufferReader constructor, I got the REPL:

      Feb 6, 2010 2:51:56 PM java.io.BufferedReader <init>
      INFO: Default buffer size used in BufferedReader constructor. It would be better to be explicit if an 8k-char buffer is required.
      SISC (1.17.0-alpha)
      #;>
    

2. Tests

2.1 Simple Scheme

Now I decided to test if the REPL actually worked. I tried (+ 3 1), and the REPL returned 4. So it works as a calculator. I also created a few procedures and these worked too.

2.2 Not so simple Scheme - Fibonnaci

I decided to throw something a little more complicated at SISC - generating Fibonnanci numbers using recursion

(define (fib n)
  (if (< n 2)
      n
      (+ (fib (- n 1)) (fib (- n 2)))))

This didn't work out so well. I was greeted with several compiler warnings:

{warning: compiler detected application of non-procedure '0'.)
{warning: compiler detected application of non-procedure '0'.)

And when I tried to call this procedure, I got this:

  Error: attempt to apply non-procedure '0'.
  console:10:24: <indeterminate call>
  console:10:24: <from call to/argument of fib>
  console:10:19: <indeterminate call>
  ---------------------------
  To enable more detailed stack tracing, set the dynamic parameter max-stack-trace-depth to a non-zero value, e.g. 16.
  ---------------------------
  Some stack trace entries may have been suppressed. To see all entries set the dynamic parameter suppressed-stack-trace-source-kinds to '().

Not good.

2.3 Java class access

The real question, though, is can the REPL interact with Android's Java classes? Initially, I suspected that SISC couldn't. One of SISC Scheme's dependencies is the ASM Java bytecode library, and Android uses Dalvik bytecode.

But I decided to give a try it anyway. To my surprise, I got it to partly work. I started up the REPL, and tried it on the benign class org.json.JSONArray which ships with Android:

(import s2j)
(define-java-class <jjson-array> |org.json.JSONArray|)
(java-class? <jjson-array>)

What I have done here is import the SISC module (s2j) that allows me to interact with Java in Scheme. I create a 'scheme-name' - <jjson-array> - and map it to the JSONArray class. I finally ask if <jjson-array> is indeed a Java class. The REPL returns #t, SISC's Scheme representation of the true value.

2.4 Object instantiation

Next I tried to instantiate it.

(define json-array (java-new <jjson-array>))
(java-object? json-array)

Here I instantiate a <jjson-array> and assign it the json-array identifier. I attempt to determine if it is indeed a Java object by applying the java-object? procedure to it. The REPL returns #t.

To my surprise, I just instantiated a Java object on Android.

2.5 Method invocation

But does this object really behave like the JSONArray object? To test this, I had to apply a procedure to this object, one which was mapped to a method on the JSONArray object.

(define-generic-java-method length |length|)
(length json-array)

Here, I define a procedure called 'length', that, if the object has a length method, will invoke that method. JSONArray has a length method, so I apply it. The REPL returns <java int 0>, or the java integer value for zero. It says that the array has no items in it.

Like JRuby I have just instantiated a Java object and invoked a method on it, and retrieved the correct value. I tried to manipulate it as well (add strings to it), and that worked too.

2.6 Android Java Classes

Things start to fail when I try to grab a hold of any of the android.* classes. For SISC Scheme to instantiate those classes, I think SISC needs to be embedded in a standalone app: an app similar to Charles Nutter's Ruboto IRB ( http://github.com/headius/ruboto-irb).

3. Conclusion

While I might be able to pull off creating something like Ruboto IRB for SISC Scheme, a fully functional SISC REPL on the Dalvik VM is far from complete. SISC's Scheme dependency on the ASM bytecode library is a major obstacle to overcome. It's dependency on several Java beans not included in the Android SDK would also need to be addressed. Finally, there's those Sun dependencies to work around too (the reflection classes and the awt library).