Skip to content

Commit

Permalink
Code to allow runtime compilation on Android
Browse files Browse the repository at this point in the history
  • Loading branch information
GeorgeJahad committed Jun 16, 2009
1 parent 7fa2346 commit 9caf291
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 6 deletions.
10 changes: 8 additions & 2 deletions build.xml
Expand Up @@ -12,6 +12,7 @@
<property name="jsrc" location="${src}/jvm"/>
<property name="cljsrc" location="${src}/clj"/>
<property name="build" location="classes"/>
<property name="sdk-location" location="${src}/../../sdk"/>

<!-- version related properties -->
<property file="${cljsrc}/clojure/version.properties"/>
Expand Down Expand Up @@ -63,6 +64,7 @@
<tstamp/>
<mkdir dir="${build}"/>
<antcall target="init-version"/>
<antcall target="copy-dx-files"/>
</target>

<target name="init-version">
Expand All @@ -75,16 +77,20 @@
<chmod file="pom.xml" perm="ugo-w"/>
</target>

<target name="copy-dx-files">
<unjar src="${sdk-location}/platforms/android-1.5/tools/lib/dx.jar" dest="${build}"/>
</target>

<target name="compile-java" depends="init"
description="Compile Java sources.">
<javac srcdir="${jsrc}" destdir="${build}" includeJavaRuntime="yes"
<javac srcdir="${jsrc}" destdir="${build}" includeJavaRuntime="yes" classpath="${sdk-location}/platforms/android-1.5/android.jar:${build}"
debug="true" target="1.5"/>
</target>

<target name="compile-clojure" depends="compile-java"
description="Compile Clojure sources.">
<java classname="clojure.lang.Compile"
classpath="${build}:${cljsrc}">
classpath="${build}:${cljsrc}:${sdk-location}/platforms/android-1.5/android.jar">
<sysproperty key="clojure.compile.path" value="${build}"/>
<arg value="clojure.core"/>
<arg value="clojure.main"/>
Expand Down
4 changes: 3 additions & 1 deletion src/clj/clojure/main.clj
Expand Up @@ -25,7 +25,9 @@
*print-meta* *print-meta*
*print-length* *print-length*
*print-level* *print-level*
*compile-path* (System/getProperty "clojure.compile.path" "classes")
*compile-path* (System/getProperty
"clojure.compile.path"
(if *android* "/data/clojure/classes" "classes"))
*command-line-args* *command-line-args*
*1 nil
*2 nil
Expand Down
102 changes: 99 additions & 3 deletions src/jvm/clojure/lang/Compiler.java
Expand Up @@ -17,6 +17,18 @@
import clojure.asm.*;
import clojure.asm.commons.Method;
import clojure.asm.commons.GeneratorAdapter;

// Needed for Android compilation
import dalvik.system.DexClassLoader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import android.util.Log;


//*/
/*
Expand Down Expand Up @@ -186,8 +198,15 @@ public class Compiler implements Opcodes{
static final public Var COMPILE_PATH = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")),
Symbol.create("*compile-path*"), null);
//boolean

//boolean True if running on Android
static final public Var ANDROID = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")),
Symbol.create("*android*"),
System.getProperty("java.vm.vendor").equals("The Android Project")? Boolean.TRUE:Boolean.FALSE);

//boolean True if running on Android
static final public Var COMPILE_FILES = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")),
Symbol.create("*compile-files*"), Boolean.FALSE);
Symbol.create("*compile-files*"), RT.booleanCast(ANDROID.deref()));

//Integer
static final public Var LINE = Var.create(0);
Expand Down Expand Up @@ -2912,6 +2931,12 @@ public final int constantsID(){
private DynamicClassLoader loader;
private byte[] bytecode;

static final int MAX_DL_ARRAY_SIZE = 100;
static DexClassLoader[] dlArray =
new DexClassLoader[MAX_DL_ARRAY_SIZE];
static int dlOffset = 0;
static Class[] clArray= new Class[MAX_DL_ARRAY_SIZE];

public FnExpr(Object tag){
this.tag = tag;
}
Expand Down Expand Up @@ -3193,11 +3218,55 @@ else if(isVariadic()) //RestFn ctor takes reqArity arg

bytecode = cw.toByteArray();
if(RT.booleanCast(COMPILE_FILES.deref()))
{
writeClassFile(internalName, bytecode);
if(RT.booleanCast(ANDROID.deref()))
handleAndroid();
}
// else
// getCompiledClass();
}
private void handleAndroid() throws Exception{

Log.d( "Clojure", "compiledex start");
String strings[] = {"--dex",
"--output=/data/clojure/classes.dex",
"/data/clojure/classes"};
com.android.dx.command.Main.main(strings);
Log.d( "Clojure", "compiledex end");

//Zip up the file because the DexClassLoader requires it
try {
String dexSource = "/data/clojure/classes.dex";
String target = "/data/clojure/classes/"
+ internalName.replace('.', '/') + ".zip";
Log.d( "Clojure", "zip target is " + target );

ZipOutputStream zos = new
ZipOutputStream(new FileOutputStream(target));
FileInputStream fis = new FileInputStream(dexSource);

zos.putNextEntry(new ZipEntry("classes.dex"));

int size = 0;
byte[] buffer = new byte[1000];

while ((size = fis.read(buffer, 0, buffer.length))
> 0) {
zos.write(buffer, 0, size);
}

zos.closeEntry();
fis.close();

zos.close();
} catch (IOException e) {
e.printStackTrace();
}

Log.d( "Clojure", "Zipdex end");

}
void emitListAsObjectArray(Object value, GeneratorAdapter gen){
gen.push(((List) value).size());
gen.newArray(OBJECT_TYPE);
Expand Down Expand Up @@ -3409,8 +3478,10 @@ synchronized Class getCompiledClass(){
if(compiledClass == null)
try
{
if(RT.booleanCast(COMPILE_FILES.deref()))
compiledClass = RT.classForName(name);//loader.defineClass(name, bytecode);
if(RT.booleanCast(ANDROID.deref()))
handleAndroidLoad();
else if(RT.booleanCast(COMPILE_FILES.deref()))
compiledClass = RT.classForName(name);//loader.defineClass(name, bytecode);
else
{
loader = (DynamicClassLoader) LOADER.deref();
Expand All @@ -3424,6 +3495,27 @@ synchronized Class getCompiledClass(){
return compiledClass;
}

private void handleAndroidLoad() throws Exception{
String target = "/data/clojure/classes/" +
internalName.replace('.', '/') + ".zip";
Log.d("Clojure", "load target is " + target);

//Store class and loader refs in arrays so the can't be
// garbage collected
dlArray[dlOffset] = new DexClassLoader(target,
"/data/clojure", null,this.getClass().getClassLoader());
clArray[dlOffset] = dlArray[dlOffset].loadClass(name);

if (clArray[dlOffset] == null)
Log.d("Clojure", "loaded class is null");
else
Log.d("Clojure", "loaded class is " +
clArray[dlOffset].toString());

compiledClass = clArray[dlOffset];
dlOffset++;
}

public Object eval() throws Exception{
return getCompiledClass().newInstance();
}
Expand Down Expand Up @@ -4528,7 +4620,11 @@ public static Object eval(Object form) throws Exception{
&& ((Symbol) RT.first(form)).name.startsWith("def")))
{
FnExpr fexpr = (FnExpr) analyze(C.EXPRESSION, RT.list(FN, PersistentVector.EMPTY, form), "eval");
if(RT.booleanCast(ANDROID.deref()))
Log.d("Clojure", "starting to load");
IFn fn = (IFn) fexpr.eval();
if(RT.booleanCast(ANDROID.deref()))
Log.d("Clojure", "starting invoke " + fn);
return fn.invoke();
}
else
Expand Down

0 comments on commit 9caf291

Please sign in to comment.