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:
Downloaded and compiled SISC source code (probably unnecessary, since I only needed the jars)
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
Created a 2.0.1 emulator image using the android tool and
called the image 'sisc'
Started up the emulator:
emulator -avd sisc
Added all the converted SISC jars into the 'data'
folder under the root directory:
/data. For
example,
adb push sisc.jar data/sisc.jar
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.
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
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
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:
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.
Added Sun's awt classes (under sun/awt), following the
method in the previous step and producing the file called:
sunawt.jar.
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.REPLSuccess! 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)
#;>
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.
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.
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.
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.
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.
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).
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).